SDKPython SDK

读取 Retargeting 数据

Skeleton、灵巧手输出,以及一对一、一对多、多对多三种映射。

当前版本 · 1.4.0

读取 Retargeting 数据

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

Retargeting 数据由 SDK 调度产生,包括 Skeleton 和你配置的灵巧手输出。这些值不是设备直接产出:读它们需要先装算法包,并选择一种 运行方式。数据来源的完整说明见 数据来源与读取方式,设备原始读数见 读取原始数据。

配置 target

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

from qnbot_sdk import TargetAlgorithm, TargetConfig

target_config = TargetConfig(
    algorithms=(TargetAlgorithm("<package-id>"),),
)

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

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

Skeleton

设备的 pose() 提供手套采集的原始姿态读取;skeleton() 提供动态 OpenXR 骨骼数据读取。其中,joint_angles() 提供 25 个关节角的数据读取,pose() 提供包含 wrist 的 25 个骨骼节点数据读取。需要 OpenXR 骨骼数据时,直接从设备取得 skeleton() 并选择要读取的数据。完整关节顺序和名称见 Skeleton 固定关节。

skeleton() 是启动前配置:必须在 SDK 创建之后、start() 之前调用;启动前重复调用会返回同一个 Skeleton 对象;设备启动后再调用会报告生命周期错误。

from qnbot_sdk import Sdk
from qnbot_sdk.glove import GloveConfig

def main() -> None:
    sdk = Sdk(devices=(GloveConfig(),))
    glove = sdk.glove()
    try:
        skeleton = glove.device().skeleton()
        skeleton.joint_angles().subscribe(
            lambda sample: print(sample.sequence, sample.value.joints)
        )
        skeleton.pose().subscribe(
            lambda sample: print(
                sample.sequence,
                sample.value.names,
                sample.value.positions_local,
            )
        )
        glove.start()
        glove.run_forever()
    finally:
        glove.close()

if __name__ == "__main__":
    main()

按用途选择骨骼姿态的读取路径:

想做什么读取路径
确认节点坐标所在的参考系coordinate_frame
按索引对齐节点名称names
取节点位置做驱动或可视化positions_local
取节点四元数做朝向对齐orientations_local_xyzw

完整字段、类型和可空行为见 HandSkeletonPose。

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

Skeleton 可以与灵巧手输出同时使用。无论采用一对一、一对多还是多对多的灵巧手映射,每只手套仍通过自己的 device.skeleton() 读取 Skeleton 数据;这些映射只影响 device.output(name=...) 对应的 target。配置方式见 灵巧手输出。

Linux x86_64、Linux ARM64、Windows x86_64 和 macOS ARM64 均支持上述 skeleton 公开 API。

灵巧手输出

先在 Sdk(targets=...) 中配置 target。创建 SDK 并选择对应手套后,按名称取得输出,并在启动和 run_forever() 前订阅:

from qnbot_sdk import Sdk, TargetAlgorithm, TargetConfig
from qnbot_sdk.glove import GloveConfig

def main() -> None:
    sdk = Sdk(
        devices=(GloveConfig(),),
        targets=(
            TargetConfig(
                name="dexhand",
                algorithms=(TargetAlgorithm(id="<package-id>"),),
            ),
        ),
    )
    glove = sdk.glove()
    try:
        glove.device().output(name="dexhand").subscribe(
            lambda sample: print(sample.value.target, sample.value.joints)
        )
        glove.start()
        glove.run_forever()
    finally:
        glove.close()

if __name__ == "__main__":
    main()

output(name=...) 中的名称对应 TargetConfig.name。配置中只有一个属于该手套的 target 时可以省略名称;存在多个 target 时必须按名称选择,否则抛出 AmbiguousSelectionError。

按用途选择字段:

想做什么读取字段
分辨输出来自哪个 targettarget
取关节目标位置下发给硬件joints
与自己的控制周期对齐或记录时间timestamp

完整字段、类型和可空行为见 HandJointCommand。

每个 TargetConfig 只绑定一个 source;SDK 不会把多只手套融合成一个 target。下面只展示设备、target、输出选择和订阅;启动与运行方式仍按前文执行。

一对一

只有一只手套和一个 target 时,SDK 可以自动关联 source 与 target;该手套只有一个 target 输出时,调用 device.output() 可以省略 name 参数。

from qnbot_sdk import Sdk, TargetAlgorithm, TargetConfig
from qnbot_sdk.glove import GloveConfig

sdk = Sdk(
    devices=(GloveConfig(),),
    targets=(
        TargetConfig(
            algorithms=(TargetAlgorithm(id="<package-id>"),),
        ),
    ),
)

sdk.glove().device().output().subscribe(
    lambda sample: print(sample.value.joints)
)

一对多

一只手套驱动多个灵巧手时,为同一个 source 配置多个具有不同 name 的 target,并分别调用 device.output(name=...)。

from qnbot_sdk import DeviceSelector, Sdk, TargetAlgorithm, TargetConfig
from qnbot_sdk.glove import GloveConfig

glove_source = DeviceSelector(type="glove", name="right-glove")
sdk = Sdk(
    devices=(GloveConfig(name="right-glove"),),
    targets=(
        TargetConfig(
            name="primary-hand",
            source=glove_source,
            algorithms=(TargetAlgorithm(id="<package-id>"),),
        ),
        TargetConfig(
            name="backup-hand",
            source=glove_source,
            algorithms=(TargetAlgorithm(id="<package-id>"),),
        ),
    ),
)

device = sdk.glove().device(name="right-glove")
device.output(name="primary-hand").subscribe(
    lambda sample: print(sample.value.joints)
)
device.output(name="backup-hand").subscribe(
    lambda sample: print(sample.value.joints)
)

完整的配置、订阅和运行示例见 multiple_outputs.py。

多对多

多只手套驱动多个灵巧手时,在同一个 SDK 中配置多条彼此独立的 source 到 target 映射,先用 glove.device(name=...) 选择 source,再用 output(name=...) 选择该 source 的 target。

from qnbot_sdk import DeviceSelector, Side, Sdk, TargetAlgorithm, TargetConfig
from qnbot_sdk.glove import GloveConfig

sdk = Sdk(
    devices=(
        GloveConfig(name="left-glove", side=Side.LEFT),
        GloveConfig(name="right-glove", side=Side.RIGHT),
    ),
    targets=(
        TargetConfig(
            name="left-hand",
            side=Side.LEFT,
            source=DeviceSelector(type="glove", name="left-glove"),
            algorithms=(TargetAlgorithm(id="<package-id>"),),
        ),
        TargetConfig(
            name="right-hand",
            side=Side.RIGHT,
            source=DeviceSelector(type="glove", name="right-glove"),
            algorithms=(TargetAlgorithm(id="<package-id>"),),
        ),
    ),
)

glove = sdk.glove()
glove.device(name="left-glove").output(name="left-hand").subscribe(
    lambda sample: print(sample.value.joints)
)
glove.device(name="right-glove").output(name="right-hand").subscribe(
    lambda sample: print(sample.value.joints)
)