> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wuji.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Hand Tracking

## EMF Poses

```python theme={null}
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](/en/docs/wuji-glove/coordinate-frames) for the transform between them.

### EmfPose

Single-finger EMF pose.

| Field | Type | Description |
| - | - | - |
| `pose` | [Pose](/en/docs/wuji-sdk/data-reference#pose) | Pose (position + orientation) |
| `confidence` | `float` | Positioning 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.

| Field | Type | Description |
| - | - | - |
| `header` | [FrameHeader](/en/docs/wuji-sdk/data-reference#frameheader) | Frame header |
| `poses` | `List[EmfPose]` | 5-finger pose list |

```json theme={null}
{
  "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

```python theme={null}
# 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](/en/docs/wuji-sdk/time-sync) 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.

| Field | Type | Description |
| - | - | - |
| `pose` | [Pose](/en/docs/wuji-sdk/data-reference#pose) | Pose |
| `confidence` | `float` | Confidence |

### FingertipPoses

5-finger fingertip poses.

| Field | Type | Description |
| - | - | - |
| `header` | [FrameHeader](/en/docs/wuji-sdk/data-reference#frameheader) | Frame header |
| `poses` | `List[FingertipPose]` | 5-finger fingertip poses |

```json theme={null}
{
  "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.

| Field | Type | Description |
| - | - | - |
| `angles` | `List[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 |
| `confidence` | `float` | IK 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.

| Field | Type | Description |
| - | - | - |
| `header` | [FrameHeader](/en/docs/wuji-sdk/data-reference#frameheader) | Frame header. `frame_id` carries over from the source EMF frame and has no meaning for joint angles |
| `fingers` | `List[FingerJointAngles]` | 5-finger joint angles in thumb, index, middle, ring, pinky order |

```json theme={null}
{
  "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.

| Field | Type | Description |
| - | - | - |
| `name` | `str` | Keypoint name (such as `index_finger_mcp`) |
| `pose` | [Pose](/en/docs/wuji-sdk/data-reference#pose) | Pose |
| `confidence` | `float` | Confidence |

### HandSkeleton

21-keypoint hand skeleton in the MediaPipe topology.

| Field | Type | Description |
| - | - | - |
| `header` | [FrameHeader](/en/docs/wuji-sdk/data-reference#frameheader) | Frame header |
| `joints` | `List[SkeletonJoint]` | 21 keypoints |

```json theme={null}
{
  "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` publishes at the EMF input rate (120 Hz) by default. To save bandwidth and IK/FK compute, call `set_rate(hz)` on an `emf_poses` subscription handle to request a lower output rate. Upgrade the device firmware to v0.11.4 or later and the Wuji SDK to v2026.8.17 or later first.

### Calling

```python theme={null}
sub = glove.emf_poses().subscribe()
actual = sub.set_rate(30)   # request 30 Hz, returns the rate the device applies
sub.set_rate(0)             # restore the default output rate
```

* The device quantizes the request to a rate it supports. `set_rate()` returns the rate in effect.
* Pass `0` to restore the default output rate.
* The rate belongs to the device stream and applies to every subscriber of that stream. When several subscribers send requests, the last write wins.
* The rate resets to full speed when all subscriptions to the stream close or the device reconnects. The setting doesn't persist across connections.
* Hold an active subscription before calling `set_rate()`. Without one, the SDK rejects the request locally and sends nothing to the device.

### Affected Streams

Lowering `emf_poses` also lowers the EMF input rate used for hand tracking. The derived streams `tip_poses`, `hand_joint_angles`, `hand_skeleton`, and `tactile_point_cloud` follow the source rate and can't be tuned on their own — calling `set_rate()` on a derived-stream handle raises an error.

Four groups of streams are tunable: EMF poses (`emf_poses`), the tactile frame (`tactile`), tactile zones (`tactile_zones`), and the six raw IMU streams (`imu_palm`, `imu_thumb`, `imu_index`, `imu_middle`, `imu_ring`, `imu_pinky`), each tuned independently. Global resources such as `tf` and `tf_static` don't support rate tuning.

### Interaction with Calibration

When you call `glove.calibrate()` or `calibrate_blocking()`, the SDK runs the EMF chain at full speed during capture and restores the previous output rate when capture ends — whether it succeeds, fails, or is canceled. Lowering the `emf_poses` rate beforehand doesn't stretch the calibration time. See [Calibration](/en/docs/wuji-glove/sdk-data-reference/calibration).

For how to subscribe to these streams and what the output looks like, see [SDK Usage Practices: Subscribing With Callbacks](/en/docs/wuji-glove/sdk-practice/subscribe-callback).

<Tabs>
  <Tab title="Python">
    Python: See the Python API examples earlier on this page.
  </Tab>

  <Tab title="C">
    The C SDK provides typed subscriptions for EMF poses and hand-tracking outputs.

    ```c theme={null}
    wuji_glove_subscribe_emf_poses(dev, on_emf_poses, user_data, &emf_sub);
    wuji_glove_subscribe_tip_poses(dev, on_tip_poses, user_data, &tip_sub);
    wuji_glove_subscribe_hand_joint_angles(dev, on_angles, user_data, &angles_sub);
    wuji_glove_subscribe_hand_skeleton(dev, on_skeleton, user_data, &skeleton_sub);
    ```

    Frame pointers are valid only during the callback. Call `wuji_sub_close` when the subscriptions are no longer needed.
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.