> ## 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 Retargeting

Hand retargeting maps human hand keypoints (21 MediaPipe-format landmarks) to a 20-value joint-angle vector for Wuji Hand 2 and Wuji Hand — ready to send to the device. The algorithm is built into the SDK: install with `pip install wuji-sdk numpy` — `numpy` is used for keypoint/qpos arrays, and nothing else is required. The C SDK provides an equivalent API — the function names are in the interface list below, and the memory ownership and status code conventions are in the [C API conventions](/en/docs/wuji-sdk/data-reference).

## Quick start

`RetargetSession.for_hand(model, side)` binds a session to a hand model with the built-in IK config. `session.step(keypoints)` accepts a `(21, 3)` float32 array and returns a `(20,)` float32 vector of joint angles — send the result to the hand as-is:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import numpy as np
    from wuji_sdk import Handedness, HandModel, RetargetSession

    session = RetargetSession.for_hand(HandModel.WujiHand2, side=Handedness.Right)

    # keypoints: 21 MediaPipe landmarks in meters
    keypoints = np.zeros((21, 3), dtype=np.float32)
    # ... fill keypoints from camera / MediaPipe / VR

    joint_angles = session.step(keypoints)   # numpy.ndarray (20,)
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    WujiRetargetSession *session = NULL;
    if (wuji_retarget_session_create(WUJI_HAND_MODEL_WUJI_HAND2,
                                     WUJI_HANDEDNESS_RIGHT, &session) != WUJI_STATUS_OK) {
        fprintf(stderr, "create failed: %s\n", wuji_last_error());
        return 1;
    }

    float keypoints[63] = {0};   // 21x3 row-major xyz, in meters
    float joint_angles[20];      // joint angles in firmware order (rad)
    // ... fill keypoints from camera / MediaPipe / VR

    if (wuji_retarget_session_step(session, keypoints, joint_angles) != WUJI_STATUS_OK) {
        // A degenerate or invalid frame returns WUJI_STATUS_ERR_ALGORITHM — drop it
        fprintf(stderr, "step failed: %s\n", wuji_last_error());
    }

    wuji_retarget_session_free(session);
    ```
  </Tab>
</Tabs>

Clear warm-start and low-pass-filter state when switching data sources (re-align timing, drop stale frames): call `session.reset()` in Python or `wuji_retarget_session_reset()` in C.

## API reference

### Create a session

Class method `RetargetSession.for_hand(hand_model, side)` builds a session bound to a hand model with the built-in config.

| Parameter | Type | Description |
| - | - | - |
| `hand_model` | `HandModel` | Target hand model: `HandModel.WujiHand` or `HandModel.WujiHand2` |
| `side` | `Handedness` | Handedness: `Handedness.Left` or `Handedness.Right` |

### Retarget a frame

`session.step(keypoints)` runs retargeting on a single keypoint frame.

| Parameter | Type | Description |
| - | - | - |
| `keypoints` | `np.ndarray` | Shape `(21, 3)` float32 array (or 63 elements row-major) in MediaPipe landmark order, in meters |

Returns `np.ndarray` of shape `(20,)` float32. Send to the hand directly.

### Reset state

`session.reset()` clears warm-start and low-pass-filter state, and `wuji_retarget_session_reset()` is the C equivalent. Call it when switching data sources or after a long pause before resuming.

For a measured single-frame run, degenerate input, and handling tracking gaps, see [Retargeting Sessions: Keypoints to Joint Angles](/en/docs/wuji-sdk/sdk-practice/retarget-session).

## Input format

`step` input follows the [MediaPipe Hands](https://ai.google.dev/edge/api/mediapipe/python/mp/tasks/vision/HandLandmarker) landmark convention — 21 fixed-order points (wrist + 5 fingers × 4 joints), coordinates in meters. Any source producing this format works: camera + MediaPipe, the Wuji Glove `hand_skeleton` subscription, VR hand tracking, and so on.

## Live teleoperation example

`RetargetSession` is a pure retargeting interface — teleoperation (driving a hand from a live keypoint stream) is application-level code built on top. For full examples, see [`examples/python/retargeting/`](https://github.com/wuji-technology/wuji-sdk/tree/main/examples/python/retargeting) and [`examples/c/retargeting/`](https://github.com/wuji-technology/wuji-sdk/tree/main/examples/c/retargeting) in the `wuji-sdk` repo (Wuji Glove input → retarget → drive Wuji Hand 2 / Wuji Hand).

With a queue-based subscription, `recv()` returns the oldest unread frame, so reading one frame per loop falls behind. Drain to the latest frame each round before feeding `step`. A callback-based subscription (as in the C example) overwrites a shared buffer from the callback, so the main loop always reads the latest frame.

### Choose the SDK User for Teleop

The hand model depends on the currently selected SDK user. Switch before or after connecting — either works. An already-connected glove picks up the new user's model on its own, with no reconnect:

* **Default user** — always runs on the SDK's built-in default hand URDF. Calibrating under the default user has no effect.
* **Named user** — runs on that user's calibrated hand model. When that user hasn't calibrated yet, it falls back to the built-in default URDF too.

The Python and C teleop examples (`1.teleop_real.py` and `1_teleop_real.c`) behave the same: both list every SDK user before connecting the glove and prompt for a choice. Press Enter to keep the current user, and the example restores the previously selected user on exit. The menu doesn't show whether a user is calibrated — no public API resolves a user's hand model before connecting.

The built-in default URDF is currently more reliable than a per-user calibrated hand model, so start with the default user. If the built-in URDF doesn't track your hand well, create a named SDK user, switch to it, then calibrate the glove under that user. After calibration, pick that user from the example menu. See [SDK User Management and Calibration](/en/docs/wuji-sdk/device-params#sdk-user-management) for details.

## Platform and limits

* Linux **x86\_64** and **aarch64** only. Precompiled wheels are not distributed for macOS, Windows, or other architectures.
* The algorithm is built into the SDK — no extra download. The only runtime dependency is `numpy` (for keypoint/qpos arrays).

## Interface List

| Operation | Python | C |
| - | - | - |
| Create a session | `RetargetSession.for_hand(hand_model, side)` | `wuji_retarget_session_create()` |
| Retarget one frame | `session.step(keypoints)` | `wuji_retarget_session_step()` |
| Reset internal state | `session.reset()` | `wuji_retarget_session_reset()` |
| Release the session | Released with the object | `wuji_retarget_session_free()`, a no-op on `NULL` |
| Hand model values | `HandModel.WujiHand`, `HandModel.WujiHand2` | `WUJI_HAND_MODEL_WUJI_HAND`, `WUJI_HAND_MODEL_WUJI_HAND2` |
| Handedness values | `Handedness.Left`, `Handedness.Right` | `WUJI_HANDEDNESS_LEFT`, `WUJI_HANDEDNESS_RIGHT` |

Both sides run the same algorithm with the same built-in IK config. Python takes and returns numpy arrays of shape `(21, 3)` and `(20,)`, while C takes and returns caller-allocated row-major arrays of 63 and 20 floats. A degenerate or invalid keypoint frame raises `WujiException` in Python and returns `WUJI_STATUS_ERR_ALGORITHM` in C. Drop the frame in either case.


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