EMF 与手部追踪

EMF 位姿

sub = glove.emf_poses().subscribe()
poses = await sub.recv_async()
for pose in poses.poses:
    pos = pose.pose.position
    print(f"位置: [{pos[0]:+.3f}, {pos[1]:+.3f}, {pos[2]:+.3f}], 置信度: {pose.confidence:.2f}")

EMF 位姿在 emf_tx(手背发射线圈)坐标系下给出,frame_idl_hand_emf_tx / r_hand_emf_tx。这与手部追踪产物所在的手腕坐标系不同,两者的变换关系见坐标系约定

EmfPose

单指 EMF 位姿。

字段类型说明
posePose位姿(位置 + 朝向)
confidencefloat定位置信度(0.0~1.0,越高越可靠,低于 0.9 时可认为不可信)

EmfPoseArray

5 指 EMF 位姿数组。手指顺序:拇指、食指、中指、无名指、小指。

字段类型说明
headerFrameHeader帧头
posesList[EmfPose]5 指位姿列表
{
  "header": {
    "seq": 100,
    "timestamp_us": 1709876543210,
    "frame_id": "l_hand_emf_tx"
  },
  "poses": [
    {
      "pose": {
        "position": [0.041, 0.066, 0.022],
        "orientation": {
          "x": 0.0,
          "y": 0.0,
          "z": 0.0,
          "w": 1.0
        }
      },
      "confidence": 0.95
    }
  ]
}

手部追踪产物

# 关节角度
sub = glove.hand_joint_angles().subscribe()
angles = await sub.recv_async()
for i, finger in enumerate(angles.fingers):
    print(f"手指 {i}: 角度={finger.angles}, 置信度={finger.confidence:.2f}")

# 手骨架
sub = glove.hand_skeleton().subscribe()
skeleton = await sub.recv_async()
for joint in skeleton.joints:
    print(f"{joint.name}: 位置={joint.pose.position}")

手部追踪产物(关节角 hand_joint_angles、骨架 hand_skeleton、指尖位姿 tip_poses)由 EMF 位姿数据帧解算得出,其时间戳和对应的源 EMF 帧相同,可用于与源数据严格对齐。打戳时刻的完整语义见 SDK 时间同步与时间戳

骨架与指尖位姿的 frame_id 为手腕坐标系(l_wrist / r_wrist)。

指尖位姿的 seq 为独立流计数,不延续源 EMF 帧的 seq

FingertipPose

单指指尖位姿。

字段类型说明
posePose位姿
confidencefloat置信度

FingertipPoses

5 指指尖位姿。

字段类型说明
headerFrameHeader帧头
posesList[FingertipPose]5 指指尖位姿
{
  "header": {
    "seq": 100,
    "timestamp_us": 1709876543210,
    "frame_id": "l_wrist"
  },
  "poses": [
    {
      "pose": {
        "position": [0.041, 0.066, 0.022],
        "orientation": {
          "x": 0.0,
          "y": 0.0,
          "z": 0.0,
          "w": 1.0
        }
      },
      "confidence": 0.95
    },
    ... // 共 5 指
  ]
}

FingerJointAngles

单指关节角度。

字段类型说明
anglesList[float]固定 5 个槽位的关节角数组(弧度),通过索引访问。拇指 5 个值均有效,其余四指仅前 4 个值有效,第 5 个值固定为 0 占位
confidencefloatIK 求解置信度

HandJointAngles

全手 21 DoF 关节角度,其中 21 DoF 指手部追踪算法使用的手部运动学模型自由度。

按 5 指返回:拇指对应 CMC3 + MCP1 + IP1,因此 angles 的 5 个槽位均有效,其余四指对应 MCP2 + PIP1 + DIP1,仅前 4 个槽位有效,第 5 个槽位为占位。全手合计 21 DoF。为保持统一数据结构,SDK 对每个手指都返回一个固定长度为 5 的 angles 数组,因此 fingers 中实际是 5 × 5 槽位

字段类型说明
headerFrameHeader帧头。frame_id 沿用源 EMF 帧,对关节角无实际意义
fingersList[FingerJointAngles]按拇指、食指、中指、无名指、小指顺序返回的 5 指关节角度
{
  "header": {
    "seq": 100,
    "timestamp_us": 1709876543210,
    "frame_id": "l_hand_emf_tx"
  },
  "fingers": [
    {
      "angles": [0.12, 0.34, 0.56, 0.78, 0.91],
      "confidence": 0.92
    },
    {
      "angles": [0.11, 0.22, 0.33, 0.44, 0.0],
      "confidence": 0.88
    },
    ... // 共 5 指,非拇指第 5 个值为 0 占位
  ]
}

SkeletonJoint

手骨架关键点。

字段类型说明
namestr关键点名称(如 index_finger_mcp
posePose位姿
confidencefloat置信度

HandSkeleton

21 个 MediaPipe 关键点组成的手骨架。

字段类型说明
headerFrameHeader帧头
jointsList[SkeletonJoint]21 个关键点
{
  "header": {
    "seq": 100,
    "timestamp_us": 1709876543210,
    "frame_id": "l_wrist"
  },
  "joints": [
    {
      "name": "wrist",
      "pose": {
        "position": [0.0, 0.0, 0.0],
        "orientation": {
          "x": 0.0,
          "y": 0.0,
          "z": 0.0,
          "w": 1.0
        }
      },
      "confidence": 0.95
    },
    ... // 共 21 个 MediaPipe 关键点
  ]
}

输出率调节

emf_poses 默认按 EMF 输入率(120 Hz)发布。需要降低输出率以节省带宽和 IK/FK 算力时,在 emf_poses 的订阅句柄上调用 set_rate(hz) 请求目标输出率。使用前先把设备固件升级到 v0.11.4 及以上、Wuji SDK 升级到 v2026.8.17 及以上。

调用方式

sub = glove.emf_poses().subscribe()
actual = sub.set_rate(30)   # 请求 30 Hz,返回设备实际生效的输出率
sub.set_rate(0)             # 恢复默认输出率
  • 请求值会被设备量化为它支持的输出率,set_rate() 返回实际生效值。
  • 传入 0 恢复默认输出率。
  • 输出率属于设备流本身,对该流的所有订阅方生效。多个订阅方先后请求时,后写入的值生效。
  • 该流的订阅全部关闭或设备重连后,输出率自动恢复满速,设置不跨连接保留。
  • 调用 set_rate() 前需持有活跃订阅,无订阅时 SDK 在本地拒绝请求,不向设备发起通信。

影响范围

降低 emf_poses 输出率的同时,也降低了手部追踪所用的 EMF 输入率。衍生流 tip_poseshand_joint_angleshand_skeletontactile_point_cloud 跟随源流输出率同步变化,不能单独调频——在衍生流的订阅句柄上调用 set_rate() 会报错。

可调流共四组:EMF 位姿(emf_poses)、触觉帧(tactile)、触觉分区(tactile_zones)与 6 路原始 IMU(imu_palmimu_thumbimu_indeximu_middleimu_ringimu_pinky),各流独立调节、互不影响。tftf_static 等全局资源不支持调频。

与标定的关系

调用 glove.calibrate()calibrate_blocking() 时,SDK 在采集期间自动把 EMF 链路恢复满速,采集结束(成功、失败或取消)后恢复原输出率。预先降低 emf_poses 输出率不会拉长标定时间。详见 标定

订阅更新