示例程序
SDK 交付包的 examples/ 提供多个可直接编译运行的示例,按功能分为 5 类子目录;每个 demo 都是独立的 main,通常第一个参数为设备 IP。交付包还在 bin/<arch>/(x86_64 / aarch64)下提供了这些示例的预编译可执行文件,可直接运行。它们既是功能演示,也是可照抄的范式。
示例一览
| 子目录 | 示例 | 演示内容 |
|---|---|---|
connection/ | device_info_demo | 连接设备、查询设备状态与信息 |
record/ | record_demo | 录制启停 + 状态机演变(含可复用的 waitForState 轮询范式) |
subscribe/ | subscribe_demo | 订阅全量数据流 |
pose_demo | 位姿订阅 | |
raw_cloud_demo | 原始点云订阅并存 PCD | |
imu_demo | IMU 订阅 | |
image_demo | 相机 JPEG 图像订阅 | |
relocation_map/ | map_upload_demo | 上传重定位地图、激活、列出设备现有地图 |
reloc_demo | 重定位:注入初始位姿 | |
comprehensive/ | all_features_demo | 全功能串联演示 |
运行
# 直接运行预编译产物(<arch> 取 x86_64 或 aarch64)
./bin/<arch>/record_demo <设备IP> [录制秒数] # 录制启停 + 状态演变
./bin/<arch>/subscribe_demo <设备IP> # 数据订阅
./bin/<arch>/map_upload_demo <设备IP> <本机地图目录> <mapId> # 上传并激活地图
./bin/<arch>/reloc_demo <设备IP> # 注入定位初值并观察
./bin/<arch>/all_features_demo <设备IP> # 全功能演示
例如:
./bin/x86_64/record_demo 192.168.123.103 30
./bin/x86_64/map_upload_demo 192.168.123.103 /home/user/maps/site_A site_A
录制轮询范式(record_demo)
record_demo 里的 waitForState() 可以直接拿去用,要点有四条:
bool waitForState(xg::Device* device, xg::RecordState target,
long maxWaitMs, xg::RecordStatus* status) {
const auto t0 = std::chrono::steady_clock::now();
while (elapsedMs(t0) < maxWaitMs) {
std::this_thread::sleep_for(
std::chrono::milliseconds(XG_RECOMMENDED_POLL_INTERVAL_MS));
if (device->queryRecordStatus(status) != xg::Error::Ok) {
continue; // 要点 1: 查询失败不等于操作失败, 继续重试
}
if (status->state == target) {
return true;
}
if (status->state == xg::RecordState::Error) {
return false; // 要点 2: 故障态立即退出, 不必等满
}
// 要点 3: Idle 与 Starting 都表示仍在准备, 继续等
}
return false; // 要点 4: 等待上限到了不代表失败, 命令已下发, 勿重复下发
}
maxWaitMs 参考值(空载无地图,仅供参考、勿写死为上限):
| 操作 | 状态变化 | 从下发到确认 |
|---|---|---|
startRecord() | Idle → Starting → Recording | 约 8 秒 |
stopRecord() | Recording → Stopping → Idle | 约 5.5 秒 |
启动耗时随地图规模增长,大地图场景会明显更久。建议取实测值的数倍作为上限,并在等待期间向操作者显示进度。
订阅范式(subscribe_demo)
auto device = xg::Device::open(opts);
if (!device) return 1;
device->subscribePose([](const xg::Pose& p) {
std::printf("pose t=%.3f pos=(%.3f, %.3f, %.3f)\n",
p.timestampSec, p.posX, p.posY, p.posZ);
});
device->subscribeCloud([](const xg::CloudFrame& f) {
std::printf("cloud t=%.3f points=%zu\n", f.timestampSec, f.count);
});
std::this_thread::sleep_for(std::chrono::seconds(30));
// device 析构自动取消订阅
图像范式(image_demo)
device->subscribeImage(xg::Device::CameraId::Left,
[](const xg::Device::ImageFrame& f) {
// jpegData 仅回调内有效,存图须拷贝:
// std::vector<uint8_t> jpeg(f.jpegData, f.jpegData + f.jpegSize);
std::printf("jpeg: %zu bytes\n", f.jpegSize);
});
定位初值范式(reloc_demo)
xg::Device::InitPose init;
init.posX = 1.0f; init.posY = 2.0f; init.posZ = 0.0f;
init.quatW = 1.0f; // 无旋转;四元数模长须 > 0.1
xg::Error e = device->setLocInitPose(init);
// e == Ok 只表示设备收下了先验,不代表已重定位成功(本版无成功判据)
地图上传范式(map_upload_demo)
覆盖重定位地图的完整生命周期:连接 → 上传(带进度)→ 激活 → 列出确认。
# 用法:<设备IP> <本机地图目录> <mapId>
./map_upload_demo 192.168.123.103 /home/user/maps/site_A site_A
本机地图目录结构要求(缺一不可,上传前本地校验):
<地图目录>/
├── location_map.las
└── loc_database/
├── camera_param.proto
├── fpfh_feature.proto
├── keyframes.proto
└── vlad.proto
// 1) 上传地图(带进度回调,内部走分片 + NACK 重传)
xg::Device::MapUploadOptions up;
up.localMapDir = "/home/user/maps/site_A";
up.mapId = "site_A"; // 仅允许 [A-Za-z0-9_-]
xg::Error err = dev->uploadMap(up, [](float progress) {
std::printf("\r上传进度: %5.1f%%", progress * 100.0f);
std::fflush(stdout);
});
// InvalidArg 多为本机地图目录结构不全;Rejected 多为设备端磁盘不足/校验失败
// 2) 激活地图
dev->setActiveMap("site_A");
// 3) 列出设备上的地图确认
std::vector<xg::Device::MapInfo> maps;
if (dev->listMaps(&maps) == xg::Error::Ok) {
for (const auto& m : maps) {
std::printf("[%s] mapId=%s files=%zu total=%.2f MB\n",
m.isActive ? "*" : " ", m.mapId.c_str(),
m.files.size(), m.totalSize / (1024.0 * 1024.0));
}
}
激活后重定位还需注入初值(见 reloc_demo 的 setLocInitPose)才会开始全局初始化重定位。接口细节见 API 参考 · 地图管理接口。
编译单个示例
若要在自己工程外单独编译某示例:
# <arch> 取 x86_64 或 aarch64
g++ -std=c++17 examples/subscribe/subscribe_demo.cpp \
-I<sdk>/include -L<sdk>/lib/<arch> -llixel_sdk \
-Wl,-rpath,'$ORIGIN/lib/<arch>' -o subscribe_demo