SDK 数据参考
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}")EmfPose
单指 EMF 位姿。
| 字段 | 类型 | 说明 |
|---|---|---|
pose | Pose | 位姿(位置 + 朝向) |
confidence | float | 定位置信度(0.0~1.0,越高越可靠,低于 0.9 时可认为不可信) |
EmfPoseArray
5 指 EMF 位姿数组。手指顺序:拇指、食指、中指、无名指、小指。
| 字段 | 类型 | 说明 |
|---|---|---|
header | FrameHeader | 帧头 |
poses | List[EmfPose] | 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
}
]
}手部追踪产物
# 关节角度
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}")FingertipPose
单指指尖位姿。
| 字段 | 类型 | 说明 |
|---|---|---|
pose | Pose | 位姿 |
confidence | float | 置信度 |
FingertipPoses
5 指指尖位姿。
| 字段 | 类型 | 说明 |
|---|---|---|
header | FrameHeader | 帧头 |
poses | List[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
单指关节角度。
| 字段 | 类型 | 说明 |
|---|---|---|
angles | List[float] | 固定 5 个槽位的关节角数组(弧度),通过索引访问。拇指 5 个值均有效,其余四指仅前 4 个值有效,第 5 个值固定为 0 占位 |
confidence | float | IK 求解置信度 |
HandJointAngles
全手 21 DoF 关节角度,其中 21 DoF 指手部追踪算法使用的手部运动学模型自由度。
按 5 指返回:拇指对应 CMC3 + MCP1 + IP1,因此 angles 的 5 个槽位均有效,其余四指对应 MCP2 + PIP1 + DIP1,仅前 4 个槽位有效,第 5 个槽位为占位。全手合计 21 DoF。为保持统一数据结构,SDK 对每个手指都返回一个固定长度为 5 的 angles 数组,因此 fingers 中实际是 5 × 5 槽位。
| 字段 | 类型 | 说明 |
|---|---|---|
header | FrameHeader | 帧头 |
fingers | List[FingerJointAngles] | 按拇指、食指、中指、无名指、小指顺序返回的 5 指关节角度 |
{
"header": {
"seq": 100,
"timestamp_us": 1709876543210,
"frame_id": ""
},
"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
手骨架关键点。
| 字段 | 类型 | 说明 |
|---|---|---|
name | str | 关键点名称(如 index_finger_mcp) |
pose | Pose | 位姿 |
confidence | float | 置信度 |
HandSkeleton
21 个 MediaPipe 关键点组成的手骨架。
| 字段 | 类型 | 说明 |
|---|---|---|
header | FrameHeader | 帧头 |
joints | List[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 算力时,调用 glove.emf_poses_rate_divider() 设置降频因数 N,输出率 = 输入率 / N。
调用方式
glove.emf_poses_rate_divider().set(4) # 120 Hz → 30 Hz
n = glove.emf_poses_rate_divider().get()取值与默认
| 取值 | 行为 |
|---|---|
1(默认) | 不降频,输出率 = 输入率 |
≥ 2 整数 | 输出率 = 输入率 / N,不限于 120 的约数(如 N=5 得 24 Hz) |
< 1、非数值、未设置 | 一律按 1 处理,不报错,不中断数据流 |
设置在 10 个输入帧内生效(120 Hz 输入下 ≤ ~83 ms)。
影响范围
| 跟随降频 | 不受影响 |
|---|---|
emf_poses | imu_*(IMU 系列) |
tip_poses | tf、tf_static |
hand_joint_angles | tactile、tactile_zones |
hand_skeleton | tactile_binary、tactile_residual |
tactile_point_cloud | — |
EMF 解算仍按输入率逐帧进行,仅在发布前抽帧,跟踪连续性不受影响。
与标定的关系
调用 glove.calibrate() 或 calibrate_blocking() 时,SDK 内部自动把降频因数临时强制为 1,采集结束(含错误与取消路径)自动恢复为标定前的值。即使预先设了 N=4,标定时间也不会被拉长 4 倍。详见 标定。