SDK Data Reference

EMF & Hand Tracking

EMF Poses

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

EMF poses are expressed in the emf_tx (back-of-hand transmitter coil) frame, with frame_id set to l_hand_emf_tx / r_hand_emf_tx. This differs from the wrist frame used by the hand tracking products — see Coordinate Frames for the transform between them.

EmfPose

Single-finger EMF pose.

FieldTypeDescription
posePosePose (position + orientation)
confidencefloatPositioning confidence (0.0~1.0, higher is more reliable. Values below 0.9 can be treated as unreliable)

EmfPoseArray

5-finger EMF pose array. Finger order: thumb, index, middle, ring, pinky.

FieldTypeDescription
headerFrameHeaderFrame header
posesList[EmfPose]5-finger pose list
{
  "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
    }
  ]
}

Hand Tracking Products

# Joint angles
sub = glove.hand_joint_angles().subscribe()
angles = await sub.recv_async()
for i, finger in enumerate(angles.fingers):
    print(f"Finger {i}: angles={finger.angles}, confidence={finger.confidence:.2f}")

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

Hand tracking products (joint angles hand_joint_angles, skeleton hand_skeleton, and fingertip poses tip_poses) are computed from EMF pose frames, and their timestamps match the source EMF frame — use this to align the products with their source data. See the SDK Time Synchronization page for full timestamp semantics.

For skeleton and fingertip poses, frame_id is the wrist frame (l_wrist / r_wrist).

The fingertip-pose seq is an independent stream counter that does not continue the source EMF seq.

FingertipPose

Single-finger fingertip pose.

FieldTypeDescription
posePosePose
confidencefloatConfidence

FingertipPoses

5-finger fingertip poses.

FieldTypeDescription
headerFrameHeaderFrame header
posesList[FingertipPose]5-finger fingertip poses
{
  "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 fingers total
  ]
}

FingerJointAngles

Single-finger joint angles.

FieldTypeDescription
anglesList[float]Fixed array of 5 joint-angle slots (radians), accessed by index. The thumb uses all 5 values. The other four fingers use only the first 4, with the 5th value fixed at 0 as padding
confidencefloatIK solver confidence

HandJointAngles

Full-hand 21 DoF joint angles, where 21 DoF refers to the degrees of freedom of the hand kinematic model used by the hand tracking algorithm.

Returned per finger. The thumb maps to CMC3 + MCP1 + IP1, so all 5 slots of angles are used. The other four fingers map to MCP2 + PIP1 + DIP1, so only the first 4 slots are used, with the 5th slot as padding. Total 21 DoF for the whole hand. To keep the data structure uniform, the SDK returns a fixed-length 5 angles array per finger, so fingers contains 5 × 5 slots in total.

FieldTypeDescription
headerFrameHeaderFrame header. frame_id carries over from the source EMF frame and has no meaning for joint angles
fingersList[FingerJointAngles]5-finger joint angles in thumb, index, middle, ring, pinky order
{
  "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 fingers total. For non-thumb fingers, the 5th value is 0 padding
  ]
}

SkeletonJoint

Hand skeleton keypoint.

FieldTypeDescription
namestrKeypoint name (such as index_finger_mcp)
posePosePose
confidencefloatConfidence

HandSkeleton

21-keypoint hand skeleton in the MediaPipe topology.

FieldTypeDescription
headerFrameHeaderFrame header
jointsList[SkeletonJoint]21 keypoints
{
  "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 keypoints total
  ]
}

Output Rate Tuning

emf_poses and its derived streams publish at the EMF input rate (120 Hz) by default. To cut IK/FK compute, call glove.emf_poses_rate_divider() to set divider N — the output rate becomes input rate / N.

Calling

glove.emf_poses_rate_divider().set(4)   # 120 Hz → 30 Hz
n = glove.emf_poses_rate_divider().get()

Values and Default

ValueBehavior
1 (default)No decimation. Output rate equals input rate
≥ 2 integerOutput rate = input rate / N, not restricted to divisors of 120 (for example, N=5 yields 24 Hz)
< 1, non-numeric, or unsetTreated as 1. No error, no stream interruption

The change takes effect within 10 input frames (≤ ~83 ms at 120 Hz).

Affected Streams

Follows dividerUnaffected
emf_posesimu_* (IMU streams)
tip_posestf, tf_static
hand_joint_anglestactile, tactile_zones
hand_skeletontactile_binary, tactile_residual
tactile_point_cloud

EMF solving still runs per input frame. Only the publish step decimates, so tracking continuity stays intact.

Interaction with Calibration

When you call glove.calibrate() or calibrate_blocking(), the SDK temporarily forces the divider to 1 for the capture phase and restores the prior value when capture ends — including error and cancellation paths. Pre-setting N=4 won't stretch the calibration runtime by 4×. See Calibration.