原始数据转换

Wuji SDK 的原始数据转换让你在采集时只保存轻量的原始数据,事后再把它们复算成完整的后处理产物——指尖位姿、关节角度、手骨架、IMU 融合姿态。离线复算结果与在线实时计算完全一致。

何时使用

实时逆向运动学解算对 CPU 消耗较高。如果采集时只想录数据、之后再分析,全程跑解算是浪费。原始数据转换适合:

  • 采集时只订阅轻量的 emf_poses 和原始 IMU,降低 CPU 占用
  • 事后再决定需要哪些后处理产物
  • 用任意自选格式保存原始数据,不绑定特定文件格式

工作流

  1. 在线采集:只订阅 emf_poses 和原始 IMU 数据
  2. 保存:用任意格式保存原始数据帧
  3. 离线复算:用 WujiGlove.offline_pipeline() 把保存的帧复算成后处理产物

离线复算与在线实时计算结果一致:相同的输入加相同的标定,得到相同的输出。

创建离线 pipeline

WujiGlove.offline_pipeline() 创建一条不依赖真实设备连接的 pipeline。

from wuji_sdk import WujiGlove

pipeline = WujiGlove.offline_pipeline(
    sn="WujiGlove-12345",   # 必填,加载该设备的参数表
    hand_side="right",       # 必填,"left" 或 "right"
    urdf_path=None,          # 可选,指定标定手部模型文件
)
参数说明
sn设备序列号,用于加载该设备的参数表
hand_side手别,"left""right"。非法值抛 ValueError
urdf_path可选,显式指定 URDF 手部模型路径

pipeline 创建后可查询实际使用的标定来源:

print(pipeline.sn)               # "WujiGlove-12345"
print(pipeline.hand_side)        # "right"
print(pipeline.urdf_source)      # "override" | "calibration_file" | "builtin_default"
print(pipeline.urdf_source_path) # 实际 URDF 路径,或 None

URDF 查找顺序

手部产物依赖标定的手部模型。pipeline 按以下规则确定 URDF:

  1. 显式 urdf_path:传入时直接使用该文件,优先于其他所有来源。文件读不出来时直接抛错,不回退
  2. 托管模型:未传 urdf_path 时,看 calibration.hand_model_path 是否指向 SDK 托管目录之外的文件。是则用这个自定义文件,否则用当前 SDK 用户对应左右手的稳定文件 left_hand.urdf / right_hand.urdf。两者是二选一,不互为回退
  3. 内置默认模型:上一步选中的文件不存在或不可用时,回退到内置默认 URDF,并打印 warning

默认 SDK 用户跳过托管模型,未传 urdf_path 时始终使用内置默认模型。要复算出与在线一致的手部产物,先切换到采集这批数据时所用的具名用户,详见 标定

回退到内置默认模型(urdf_sourcebuiltin_default)时,手部产物可能不准确。IMU 融合产物不依赖 URDF,不受影响。

复算手部产物

手部链以 emf_poses 帧为输入,复算出三种产物。三者共享同一次解算,结果与在线一致。

for emf_poses in my_recording_reader():   # 自定义读取器,逐帧返回 EmfPoseArray
    angles   = pipeline.hand_joint_angles().compute(emf_poses)
    tips     = pipeline.tip_poses().compute(emf_poses)
    skeleton = pipeline.hand_skeleton().compute(emf_poses)
    save(angles, tips, skeleton)   # 自定义写入器
资源返回类型内容
pipeline.hand_joint_angles()HandJointAngles21 DoF 手部关节角
pipeline.tip_poses()FingertipPoses5 指指尖位姿
pipeline.hand_skeleton()HandSkeleton21 个 MediaPipe 关键点骨架

产物的完整字段定义见 SDK 数据参考

复算 IMU 融合

IMU 链以原始 IMU 帧为输入,复算出融合姿态。手套手背 IMU 用 imu_data_palm()

for raw_frame in my_imu_reader():   # 逐帧返回原始 ImuData
    fused = pipeline.imu_data_palm().compute(raw_frame)
    save(fused)   # 自定义写入器

原始 IMU 帧用 ImuData.raw() 构造,orientation_covariance[0] 自动标记为 -1(未融合)。IMU 融合产物不依赖标定 URDF。

构造输入帧

用 schema 构造器在内存中构造输入帧:

from wuji_sdk import (
    EmfPose, EmfPoseArray, FrameHeader, ImuData,
    Pose, Quaternion, Vector3F64,
)

emf = EmfPoseArray(
    header=FrameHeader(seq=1, timestamp_us=0, frame_id="r_hand_emf_tx"),
    poses=[
        EmfPose(
            pose=Pose(position=[0.05, 0.0, 0.0],
                      orientation=Quaternion(0.0, 0.0, 0.0, 1.0)),
            confidence=1.0,
        )
        for _ in range(5)
    ],
)

raw_imu = ImuData.raw(
    header=FrameHeader(seq=1, timestamp_us=0, frame_id="imu_raw/palm"),
    angular_velocity=Vector3F64(0.0, 0.0, 0.0),
    linear_acceleration=Vector3F64(0.0, 0.0, 9.8),
)

完整可运行示例见 SDK 安装包中的 examples/3.offline_pipeline.py