SDKC++ SDK

读取 Retargeting 数据

Skeleton、灵巧手输出,以及 target 和算法包配置。

当前版本 · 1.4.0

读取 Retargeting 数据

先读:数据来源与读取方式 · 读完你能:配好 target 并读到 Skeleton 或灵巧手输出

Retargeting 数据由 SDK 调度产生,包括 Skeleton 和你配置的灵巧手输出。需要持续得到这些输出时,应用必须选择一种运行方式。

配置 target

target 决定手套数据交给哪个算法包、输出几个结果。最少只要指定算法包 ID:

qnbot::TargetConfig target_config;
target_config.algorithms = {qnbot::TargetAlgorithm{"<package-id>"}};

type、name、side、source 都可以省略:SDK 会在结果唯一时自动补全,无法唯一确定时创建 Sdk 会直接失败。需要固定设备或自定义输出名时再显式填写,字段定义见 配置字段速查。

算法包必须先安装完成再创建 Sdk:用 Qnbot CLI 装到本机后,把 qnbot algorithm list 显示的 package_id 填进 TargetAlgorithm。安装、升级与卸载的步骤由 CLI 指南拥有,见 算法包管理。运行中的 Sdk 不会热更新包列表,装好后需要重新创建。

Skeleton

设备的 pose() 返回手套采集的原始姿态;skeleton() 返回动态 OpenXR 骨骼能力。其 joint_angles() 返回 25 个 active joints 角度,其中新增的 thumb_cmc_flexion 位于原生关节定义索引 1;pose() 返回包含 wrist 的 25 节点 HandSkeletonPose(wrist_local 坐标系)。

skeleton() 是启动前配置:必须在 SDK 创建之后、start() 之前调用;启动前重复调用会复用同一能力,设备已经启动后再调用会明确报生命周期错误。

auto skeleton = glove.device().skeleton();
auto angles_subscription = skeleton.joint_angles().subscribe(
    [](const qnbot::Sample<qnbot::HandJointCommand>& sample) {
        std::cout << sample.sequence << ' ' << sample.value.joints.size() << '\n';
    });
auto pose_subscription = skeleton.pose().subscribe(
    [](const qnbot::Sample<qnbot::HandSkeletonPose>& sample) {
        std::cout << sample.sequence << ' ' << sample.value.names.size() << '\n';
    });
glove.start();

标定如何被引导由交互模式决定:默认 auto_terminal 会在交互式终端自动引导并复用兼容标定,external 模式由应用接管,见 引导式采集交互 与 外部接管引导交互。使用 external 模式时用 qnbot::TargetName::skeleton 选择该能力。完整示例见 skeleton.cpp。

Linux x86_64、Linux ARM64、Windows x86_64 和 macOS ARM64 均支持上述 Skeleton API。返回字段和完整关节名称见 HandJointCommand 与 HandSkeletonPose。

灵巧手输出

配置 target 后,通过 device.output() 读取 HandJointCommand。只有一个 target 时可以省略名称;同一个 source 有多个 target 时必须按 TargetConfig::name 选择。

每个 TargetConfig 只绑定一个 source;SDK 不会把多只手套融合成一个 target。输出字段见 HandJointCommand。

一对一

一只手套对应一个灵巧手时,只配置一个 source 和一个 target;两者唯一时可以省略名称:

qnbot::TargetConfig target;
target.algorithms = {qnbot::TargetAlgorithm{"<package-id>"}};

qnbot::SdkConfig config;
config.devices = {qnbot::GloveConfig{}};
config.targets = {target};
qnbot::Sdk sdk(std::move(config));

auto device = sdk.glove().device();
auto output = device.output();
auto subscription = output.subscribe([](const auto& sample) {
    use_joint_command(sample.value);
});

一对多

一只手套驱动多个灵巧手时,为同一个 source 配置多个具名 target,再按名称读取输出:

qnbot::GloveConfig source;
source.name = "primary";

qnbot::TargetConfig dexhand;
dexhand.name = "dexhand";
dexhand.source = qnbot::DeviceSelector{"glove", "primary", std::nullopt};
dexhand.algorithms = {qnbot::TargetAlgorithm{"<dexhand-package-id>"}};

qnbot::TargetConfig backup = dexhand;
backup.name = "backup";
backup.algorithms = {qnbot::TargetAlgorithm{"<backup-package-id>"}};

qnbot::SdkConfig config;
config.devices = {source};
config.targets = {dexhand, backup};
qnbot::Sdk sdk(std::move(config));

auto device = sdk.glove().device("primary");
auto dexhand_output = device.output("dexhand");
auto backup_subscription = device.output("backup").subscribe(
    [](const auto& sample) { use_backup_command(sample.value); });

多对多

多只手套驱动多个灵巧手时,为 source 和 target 分别命名,并在每个 target 中明确选择 source:

qnbot::GloveConfig left;
left.name = "left-glove";
left.side = qnbot::Side::left;
qnbot::GloveConfig right;
right.name = "right-glove";
right.side = qnbot::Side::right;

qnbot::TargetConfig left_hand;
left_hand.name = "left-hand";
left_hand.source = qnbot::DeviceSelector{"glove", "left-glove", qnbot::Side::left};
left_hand.algorithms = {qnbot::TargetAlgorithm{"<package-id>"}};
qnbot::TargetConfig right_hand;
right_hand.name = "right-hand";
right_hand.source = qnbot::DeviceSelector{"glove", "right-glove", qnbot::Side::right};
right_hand.algorithms = {qnbot::TargetAlgorithm{"<package-id>"}};

qnbot::SdkConfig config;
config.devices = {left, right};
config.targets = {left_hand, right_hand};
qnbot::Sdk sdk(std::move(config));

auto glove = sdk.glove();
auto left_subscription = glove.device("left-glove").output("left-hand").subscribe(
    [](const auto& sample) { use_left_command(sample.value); });
auto right_output = glove.device("right-glove").output("right-hand");
auto right_subscription = right_output.subscribe(
    [](const auto& sample) { use_right_command(sample.value); });