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

# Offline Pipeline

The [Wuji SDK](/en/docs/wuji-sdk/) offline pipeline lets you save only lightweight raw data during capture, then recompute the full post-processed products afterward — fingertip poses, joint angles, hand skeleton, and fused IMU orientation. Offline results are identical to real-time computation.

## When to use it

Real-time inverse kinematics solving is CPU-intensive. If you only want to record data during capture and analyze it later, running the solver the whole time is wasteful. The offline pipeline fits when you want to:

* Subscribe only to lightweight `emf_poses` and raw IMU during capture, lowering CPU usage
* Decide later which post-processed products you need
* Save raw data in any format of your choice, without binding to a specific file format

## Workflow

1. **Capture online**: subscribe only to `emf_poses` and raw IMU data
2. **Save**: store the raw data frames in any format
3. **Recompute offline**: use `WujiGlove.offline_pipeline()` to recompute post-processed products from the saved frames

<Info>
  Offline recomputation produces the same result as real-time computation: the same input plus the same calibration yields the same output.
</Info>

## Create an offline pipeline

`WujiGlove.offline_pipeline()` creates a pipeline that does not require a connection to a physical device.

```python theme={null}
from wuji_sdk import WujiGlove

pipeline = WujiGlove.offline_pipeline(
    sn="WujiGlove-12345",   # required, loads the parameter file for this device
    hand_side="right",       # required, "left" or "right"
    urdf_path=None,          # optional, specify a calibrated hand model file
)
```

| Parameter | Description |
| - | - |
| `sn` | Device serial number, used to load the parameter file for that device |
| `hand_side` | Hand side, `"left"` or `"right"`. An invalid value raises `ValueError` |
| `urdf_path` | Optional, an explicit path to a URDF hand model |

After creation, query the calibration source actually in use:

```python theme={null}
print(pipeline.sn)               # "WujiGlove-12345"
print(pipeline.hand_side)        # "right"
print(pipeline.urdf_source)      # "override" | "calibration_file" | "builtin_default"
print(pipeline.urdf_source_path) # actual URDF path, or None
```

### URDF Lookup Order

Hand products depend on a calibrated hand model. The pipeline picks the URDF like this:

1. **Explicit `urdf_path`**: when passed, that file is used directly and takes priority over every other source. An unreadable file raises an error instead of falling back
2. **Managed model**: without `urdf_path`, the pipeline checks whether `calibration.hand_model_path` points to a file outside the SDK-managed directory. If it does, that custom file is used. If it doesn't, the stable per-side file `left_hand.urdf` / `right_hand.urdf` for the current SDK user is used. The two are alternatives, not fallbacks for each other
3. **Built-in default model**: when the file picked above is missing or unusable, the pipeline falls back to the built-in default URDF and prints a warning

<Warning>
  The default SDK user skips the managed model and always uses the built-in default unless you pass `urdf_path`. To recompute hand products that match the online ones, switch to the named user that captured the data. See [Calibration](/en/docs/wuji-glove/sdk-data-reference/calibration).
</Warning>

<Warning>
  When the pipeline falls back to the built-in default model (`urdf_source` is `builtin_default`), hand products may be inaccurate. IMU fusion products do not depend on the URDF and are unaffected.
</Warning>

## Recompute hand products

The hand chain takes `emf_poses` frames as input and recomputes three products. The three share a single solve, so the results match the online ones.

```python theme={null}
for emf_poses in my_recording_reader():   # custom reader, yields EmfPoseArray frame by frame
    angles   = pipeline.hand_joint_angles().compute(emf_poses)
    tips     = pipeline.tip_poses().compute(emf_poses)
    skeleton = pipeline.hand_skeleton().compute(emf_poses)
    save(angles, tips, skeleton)   # custom writer
```

| Resource | Return type | Content |
| - | - | - |
| `pipeline.hand_joint_angles()` | `HandJointAngles` | 21 DoF hand joint angles |
| `pipeline.tip_poses()` | `FingertipPoses` | 5-finger fingertip poses |
| `pipeline.hand_skeleton()` | `HandSkeleton` | 21 MediaPipe keypoint skeleton |

For the full field definitions of these products, see [SDK Resources](/en/docs/wuji-glove/sdk-data-reference/).

## Recompute IMU fusion

The IMU chain takes raw IMU frames as input and recomputes fused orientation. Use `imu_data_palm()` for the glove's dorsum IMU.

```python theme={null}
for raw_frame in my_imu_reader():   # yields raw ImuData frame by frame
    fused = pipeline.imu_data_palm().compute(raw_frame)
    save(fused)   # custom writer
```

Construct raw IMU frames with `ImuData.raw()`. It automatically marks `orientation_covariance[0]` as -1 (not fused). IMU fusion products do not depend on a calibration URDF.

## Construct input frames

Use the schema constructors to build input frames in memory:

```python theme={null}
from wuji_sdk import (
    EmfPose, EmfPoseArray, FrameHeader, ImuData,
    Pose, Quaternion, Vector3F64,
)

emf = EmfPoseArray(
    header=FrameHeader(seq=1, timestamp_us=0, frame_id="r_hand_emf_tx"),
    poses=[
        EmfPose(
            pose=Pose(position=[0.05, 0.0, 0.0],
                      orientation=Quaternion(0.0, 0.0, 0.0, 1.0)),
            confidence=1.0,
        )
        for _ in range(5)
    ],
)

raw_imu = ImuData.raw(
    header=FrameHeader(seq=1, timestamp_us=0, frame_id="imu_raw/palm"),
    angular_velocity=Vector3F64(0.0, 0.0, 0.0),
    linear_acceleration=Vector3F64(0.0, 0.0, 9.8),
)
```

For a complete runnable example, see `examples/3.offline_pipeline.py` in the SDK package.

For the full workflow and a measured comparison between offline replay and online solving, see [SDK Usage Practices: Replaying Data Offline](/en/docs/wuji-glove/sdk-practice/offline-replay).


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