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

# Common Data Types

This page lists all common data type field definitions in the Wuji SDK.

> [!NOTE]
>
> For device-specific data structures, see the [product-specific documentation listed below](#device-specific-types).

## Common Types

### FrameHeader

Header information for each data frame.

| Field | Type | Description |
| - | - | - |
| `seq` | `int` | Incrementing sequence number |
| `timestamp_us` | `int` | Device timestamp (microseconds). For the semantics, see [Data frame timestamps](https://docs.wuji.tech/docs/en/wuji-sdk/latest/time-sync.md#data-frame-timestamps) |
| `frame_id` | `str` | Coordinate frame ID (e.g., `"l_wrist"`), max 32 characters |

### Vector3 / Vector3F64

Three-dimensional vector. `Vector3` uses f32 precision, `Vector3F64` uses f64 precision (used for IMU data, ROS compatible).

| Field | Type | Description |
| - | - | - |
| `x` | `float` | X component |
| `y` | `float` | Y component |
| `z` | `float` | Z component |

### Quaternion

Quaternion representing 3D rotation. Python has a single `Quaternion`, while C splits it into `WujiQuaternionF32` and `WujiQuaternionF64` to match the precision each stream carries.

| Field | Type | Description |
| - | - | - |
| `x` | `float` | X component |
| `y` | `float` | Y component |
| `z` | `float` | Z component |
| `w` | `float` | W component |

### Pose

Position and orientation.

| Field | Type | Description |
| - | - | - |
| `position` | `List[float]` | Position \[x, y, z] (meters) |
| `orientation` | [Quaternion](#quaternion) | Rotation quaternion |

### Handedness

Device handedness enum, used for [connecting by handedness](https://docs.wuji.tech/docs/en/wuji-sdk/latest/device-connection.md). The C constants are `WUJI_HANDEDNESS_LEFT` (`0`) and `WUJI_HANDEDNESS_RIGHT` (`1`). The two Wuji Hand handedness getters return a raw `uint8_t` rather than this enum, and their encodings are opposites — see [Wuji Hand Data Subscription](https://docs.wuji.tech/docs/en/wuji-sdk/latest/data-subscription.md) for the convention.

| Value | Description |
| - | - |
| `Handedness.Left` | Left hand (fourth character of serial number is `J`) |
| `Handedness.Right` | Right hand (fourth character of serial number is `K`) |

### ImuData

IMU sensor data, following the ROS `sensor_msgs/Imu` convention.

| Field | Type | Description |
| - | - | - |
| `header` | [FrameHeader](#frameheader) | Frame header |
| `orientation` | [Quaternion](#quaternion) | Orientation quaternion |
| `orientation_covariance` | `list[float]` | Orientation covariance (length 9). A first element of `-1` marks the orientation as unavailable |
| `angular_velocity` | [Vector3F64](#vector3-/-vector3f64) | Angular velocity (rad/s) |
| `angular_velocity_covariance` | `list[float]` | Angular velocity covariance (length 9) |
| `linear_acceleration` | [Vector3F64](#vector3-/-vector3f64) | Linear acceleration (m/s²) |
| `linear_acceleration_covariance` | `list[float]` | Linear acceleration covariance (length 9) |

## Coordinate Transforms

### FrameTransform

Single coordinate transform.

| Field | Type | Description |
| - | - | - |
| `timestamp_us` | `int` | Timestamp (microseconds) |
| `parent_frame_id` | `str` | Parent frame |
| `child_frame_id` | `str` | Child frame |
| `translation` | `List[float]` | Translation \[x, y, z] (meters) |
| `rotation` | [Quaternion](#quaternion) | Rotation quaternion |

### FrameTransforms

Collection of coordinate transforms.

| Field | Type | Description |
| - | - | - |
| `transforms` | `List[FrameTransform]` | Transform list |

## Recording Types

For complete usage and code examples, see [Data Recording](https://docs.wuji.tech/docs/en/wuji-sdk/latest/data-recording.md).

### TopicRecorder

MCAP recording session configurator. Register channels, then call `start()` to begin recording.

| Method | Parameters | Description |
| - | - | - |
| `__init__()` | `compression: str = "lz4"`, `chunk_size: int = None` | Create a recorder. Supports `"lz4"`, `"zstd"`, `"none"` |
| `record()` | `sub: Subscription` | Register a subscription channel |
| `start()` | `output_path: str` | Start recording, returns `RecordingHandle` |

### RecordingHandle

Recording control handle returned by `TopicRecorder.start()`.

| Method | Returns | Description |
| - | - | - |
| `pause()` | — | Pause recording |
| `resume()` | — | Resume recording |
| `stop()` | `RecordingSummary` | Stop recording and return summary |
| `subscribe_metrics()` | `MetricsStream` | Subscribe to real-time quality metrics |
| `subscribe_status()` | `StatusStream` | Subscribe to recording status updates |
| `subscribe_alerts()` | `AlertStream` | Subscribe to quality alerts |

### RecordingSummary

Recording statistics returned by `handle.stop()`.

| Field | Type | Description |
| - | - | - |
| `total_frames` | `int` | Total frames recorded |
| `file_size` | `int` | MCAP file size in bytes |
| `duration_s` | `float` | Recording duration in seconds |
| `channels` | `list[ChannelRecordingSummary]` | Written frame counts by channel |
| `quality` | `QualitySummary` | Quality statistics summary |

`channels` is ordered by channel ID and includes registered channels with no messages. Each `ChannelRecordingSummary` contains `channel_id`, `channel_name`, and `frame_count`. `duration_s` includes pauses and buffer draining during stop. It excludes MCAP file finalization, flushing, and waiting for monitor callbacks to exit.

### QualityMetrics

Real-time quality metrics (5-second sliding window).

| Field | Type | Description |
| - | - | - |
| `frame_drop_rate` | `float` | Frame drop rate (0.0–1.0) |
| `frame_jitter_us` | `float` | Inter-frame jitter in microseconds |
| `sync_offset_ms` | `float` | Cross-channel sync offset in milliseconds |
| `sync_rate` | `float` | Sync success rate (0.0–1.0) |
| `timestamp_ns` | `int` | Nanosecond timestamp |
| `channels` | `list[ChannelHealth]` | Per-channel health details, see [ChannelHealth](#channelhealth) |

### ChannelHealth

Per-channel health details (elements of `channels` on [QualityMetrics](#qualitymetrics)).

| Field | Type | Description |
| - | - | - |
| `channel_name` | `str` | Channel name |
| `channel_id` | `int` | Channel ID |
| `actual_hz` | `float` | Measured frame rate (Hz) |
| `drop_rate` | `float` | Frame drop rate for this channel (0.0–1.0) |
| `last_frame_age_ms` | `int \| None` | Time since the last frame in milliseconds, `None` if no frame received yet |
| `jitter_us` | `float` | Inter-frame jitter in microseconds |
| `is_online` | `bool` | Whether the channel is currently receiving data |
| `last_downtime_ms` | `int \| None` | Duration of the last downtime in milliseconds, `None` if never dropped |

### QualitySummary

Aggregate recording quality statistics.

| Field | Type | Description |
| - | - | - |
| `total_frames` | `int` | Total frames received |
| `dropped_frames` | `int` | Total frames dropped |
| `frame_drop_rate` | `float` | Drop rate (0.0–1.0) |
| `avg_sync_offset_ms` | `float` | Average sync offset in milliseconds |
| `max_sync_offset_ms` | `float` | Maximum sync offset in milliseconds |
| `sync_rate` | `float` | Sync success rate (0.0–1.0) |
| `spc_alert_count` | `int` | Total SPC alerts triggered |
| `duration_s` | `float` | Recording duration in seconds |

### RecordingAlert

Quality alert.

| Field | Type | Description |
| - | - | - |
| `metric` | `str` | Name of the metric that triggered the alert |
| `current_value` | `float` | Current measured value |
| `threshold` | `float` | Alert threshold |
| `message` | `str` | Human-readable alert message |

### RecordingStatus

Recording runtime status.

| Field | Type | Description |
| - | - | - |
| `state` | `str` | Current state (`"recording"` or `"paused"`) |
| `frame_count` | `int` | Frames recorded so far |
| `duration_s` | `float` | Elapsed duration in seconds |

## Device-Specific Types

| Product | Type definitions |
| - | - |
| Wuji Hand 2.2 (Beta) | [Wuji Hand SDK Reference](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference.md#data-types) |
| Wuji Hand 2.1 (Beta) | [Wuji Hand SDK Reference](https://docs.wuji.tech/docs/en/wuji-hand/beta1/sdk-reference.md#data-types) |
| Wuji Glove | [Wuji Glove data streams overview](https://docs.wuji.tech/docs/en/wuji-glove/latest/sdk-data-reference/index.md) |

## Error Code Categories

The following three categories of error codes are unrelated. A value from one category means nothing in another:

| Category | Carried by | How to decode |
| - | - | - |
| Device fault code (`u16`) | `error_code_current` in Wuji Hand 2 joint diagnostics, the `warning` / `deferred_stop` / `immediate_stop` / `fatal` fields in `ErrorStatus`, `error_code` in the error history, and `system_error_code` in encoder calibration results | `WujiHand2.describe_error()`. See [Wuji Hand 2 Joint Diagnostics — Device Fault Codes](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference/diagnostics.md#device-fault-codes) |
| Encoder calibration code (`u8`) | `enc1_error_code` / `enc2_error_code` in the encoder calibration results | A calibration-only code that reports why the calibration step failed (enc1 is a small 0–9 enum, enc2 is segmented by calibration stage). Don't pass it to `describe_error()`. Read the human-readable name from the `enc1_error_name` / `enc2_error_name` read-only properties on the calibration result objects |
| First-generation Wuji Hand diagnostic bitfield (`u32`) | `WujiHandJointDiagnostics.error_code`, returned by `wuji_hand_get_all_diagnostics()` | Each bit represents a fault condition. This encoding is independent of Wuji Hand 2 device fault codes. Don't pass it to `WujiHand2.describe_error()` or `wuji_hand_2_describe_error()` |

**Use Code Values**

Device fault codes use the `0xSCNN` layout. Don't decode the hexadecimal digits yourself. Treat each complete code as an opaque identifier for logs, user displays, and bug reports.

**Read Decoded Results**

In Python, `describe_error()` returns a `dict`. Read values by key, such as `info["severity"]` and `info["clear_policy"]`.

The following fields contain text from the firmware's error catalog:

| Field | Meaning |
| - | - |
| `desc` | Error description |
| `cause` | Cause |
| `resolution` | Suggested action |

**Handle `None` Results**

When `describe_error(code)` returns `None`, check the original error code:

| Error Code | Meaning | Action |
| - | - | - |
| `0` | No active error. The catalog doesn't include this value | No action needed |
| Nonzero value | The firmware may contain an error code that the SDK catalog doesn't yet include | Keep the original value for logs and bug reports |

For the differences between SDK exceptions, C return codes, and device fault codes, see the [Error Code Index](https://docs.wuji.tech/docs/en/wuji-sdk/latest/appendix.md#error-code-index). For device fault-code lookup, see [Fault Logs and Error Codes](https://docs.wuji.tech/docs/en/wuji-sdk/latest/device-fault-logs.md#look-up-error-codes).

## C SDK conventions

This section covers C SDK initialization, error handling, memory lifetime, callback constraints, and CMake setup. Device-specific capabilities remain documented on the corresponding product pages.

## Initialization and conventions

**Initialization**

Call once at process start and release at shutdown:

```c theme={null}
#include "wuji_sdk.h"

if (wuji_init(NULL) != WUJI_STATUS_OK) {
    fprintf(stderr, "init failed: %s\n", wuji_last_error());
    return 1;
}

// ... your code

wuji_shutdown();
```

**Return value taxonomy**

* `WujiStatus` — most operations (`wuji_init` / `wuji_scan` / `wuji_connect` / opening subscriptions, etc.). `WUJI_STATUS_OK` means success. On failure, retrieve the per-thread error string via `wuji_last_error()`.
* `int32_t` — metadata readers (`wuji_dev_serial_number` / `wuji_dev_device_name`) return the number of bytes written.
* `uint8_t` — status query `wuji_dev_is_connected`.
* `void` — `wuji_shutdown` and resource cleanup functions.

**Resource cleanup functions**

* `wuji_discovered_free` — release the device list returned by `wuji_scan`
* `wuji_dev_release` — paired with `wuji_connect`, release the device handle after disconnecting
* `wuji_sub_close` — close the subscription handle

**User Info Struct**

`WujiUserInfo` holds seven fields: `user_id`, `display_name`, `description`, `external_id`, `is_default`, `created_at`, and `updated_at`. `wuji_update_user` takes a `WujiUserUpdate` carrying only the fields to change, and setting `clear_description` / `clear_external_id` clears the matching value. For the full user management API list, see [SDK Users and Calibration Data](https://docs.wuji.tech/docs/en/wuji-sdk/latest/device-params.md#sdk-user-management).

## Usage constraints

C SDK users must observe the following constraints to avoid use-after-free, deadlocks, and reading stale error strings.

* A typed callback's `frame` pointer is valid only for the duration of the callback. Heap fields are released after the callback returns. Copy whatever you need to retain inside the callback. Do not store the pointer.
* Never call `wuji_sub_close` from inside that subscription's own callback. `close` joins the worker thread and will deadlock if called from the callback itself. Close from a different thread.
* The return value of `wuji_last_error()` is only valid until the next `wuji_*` call on the same thread. Copy the message immediately if you need to retain it.
* `END` and `ERROR` are terminal frames — no further data will arrive after them, but you still need to call `wuji_sub_close` to release the subscription handle.
* String getters use a two-call query: first call with `buf=NULL, buf_len=0` to read `*needed` (required bytes including NUL), then allocate and call again to fill. Passing an undersized buffer returns `WUJI_STATUS_ERR_BUFFER_TOO_SMALL` and never truncates.
* Whole-hand batch reads (`get_all_effort_limit` / `get_all_mit_params`) return a flat-20 array plus an online bitmap. Use the `WUJI_JOINT_ONLINE(mask, i)` macro to check slot validity, otherwise you will read offline-joint placeholders (0 or NaN).
* The calibration feedback callback runs on the calibration worker thread, and the `feedback` pointer is valid only during the callback. The SDK guards the session calls made from inside it rather than deadlocking: `wuji_glove_calibration_wait` and `wuji_glove_calibration_try_finish` return `WUJI_STATUS_ERR_INVALID_ARG`, and `wuji_glove_calibration_session_free` records the reason in `wuji_last_error()` and skips the release — so the session stays alive and you must free it again after the callback returns. `wuji_glove_calibration_cancel` is safe to call from the callback. Free the session with `wuji_glove_calibration_session_free` and the result with `wuji_glove_calibration_result_free`.
* `WujiUserInfo` holds heap strings: free a single struct with `wuji_user_info_free`, and free an array returned by `wuji_list_users` with `wuji_user_info_array_free`.

## Python equivalents

The table below maps each C API to its Python counterpart by capability. For full usage of cross-device capabilities and per-device APIs, see the [C API Reference](https://docs.wuji.tech/docs/en/wuji-sdk/latest/c-sdk-reference.md).

<table>
  <thead>
    <tr><th>Capability</th><th>C</th><th>Python</th></tr>
  </thead>

  <tbody>
    <tr><td>Init</td><td>`wuji_init(NULL)`</td><td>`SdkManager.instance()`</td></tr>
    <tr><td>Scan</td><td>`wuji_scan(&devs, &count)`</td><td>`manager.scan()`</td></tr>
    <tr><td>Connect</td><td>`wuji_connect(&target, alias, &opts, &dev)`</td><td>`manager.connect(...)` / `manager.auto_connect(...)`</td></tr>
    <tr><td>Default connect options</td><td>`wuji_connect_options_default()`</td><td>`ConnectOptions()`</td></tr>
    <tr><td>Device subscribe</td><td>`wuji_glove_subscribe_tactile(dev, cb, user, &sub)`</td><td>`glove.tactile().subscribe_with_callback(...)`</td></tr>
    <tr><td>Global subscribe</td><td>`wuji_subscribe_tf(cb, user, &sub)`</td><td>`manager.tf().subscribe()`</td></tr>
    <tr><td>Stream output rate</td><td>`wuji_sub_set_rate(sub, hz, &actual)`</td><td>`sub.set_rate(hz)`</td></tr>
    <tr><td>Hand 2 control action</td><td>`wuji_hand_2_enable(dev, mask)`</td><td>`hand.enable(joints=mask)`</td></tr>
    <tr><td>Hand 2 realtime command</td><td>`wuji_hand_2_joint_command_publish` + `wuji_joint_command_publisher_send`</td><td>`hand.joint_command().publish().send([...])`</td></tr>
    <tr><td>Hand 2 joint state subscribe</td><td>`wuji_hand_2_subscribe_joint_states`</td><td>`hand.joint_states().subscribe()`</td></tr>
    <tr><td>Hand 2 joint diagnostics subscribe</td><td>`wuji_hand_2_subscribe_joint_diagnostics`</td><td>`hand.joint_diagnostics().subscribe()`</td></tr>
    <tr><td>Hand 2 IMU subscribe</td><td>`wuji_hand_2_subscribe_imu`</td><td>`hand.imu().subscribe()`</td></tr>
    <tr><td>Hand 2 nid and joint index conversion</td><td>`wuji_hand_2_nid_to_joint_index(nid, &index)` / `wuji_hand_2_joint_index_to_nid(joint_index, &nid)`</td><td>`WujiHand2.nid_to_joint_index(nid)` / `WujiHand2.joint_index_to_nid(joint_index)`</td></tr>
    <tr><td>Hand 2 error description</td><td>`wuji_hand_2_describe_error(code, &info)`</td><td>`WujiHand2.describe_error(code)`</td></tr>
    <tr><td>Hand 2 flash log export</td><td>`wuji_hand_2_export_flash_logs` + `wuji_hand_2_flash_log_export_free`</td><td>`hand.export_flash_logs()`</td></tr>
    <tr><td>Hand connect (USB SN)</td><td>`wuji_hand_connect_sn(sn, alias, &dev)`</td><td>`WujiHand.connect_sn(sn, alias)`</td></tr>
    <tr><td>Hand joint state subscribe</td><td>`wuji_hand_subscribe_joint_states`</td><td>`hand.joint_states().subscribe()`</td></tr>
    <tr><td>Hand realtime command</td><td>`wuji_hand_joint_command_publish` + `wuji_hand_joint_command_publisher_send`</td><td>`hand.joint_command().publish().send([...])`</td></tr>
    <tr><td>Hand realtime controller</td><td>`wuji_hand_realtime_controller_open` / `_set_target_position` / `get_actual*`</td><td>`hand.realtime_controller(LowPass(...))`</td></tr>
    <tr><td>Glove time sync</td><td>`wuji_glove_sync_time(dev, &tsr)`</td><td>`glove.sync_time()`</td></tr>
    <tr><td>Glove hand model path</td><td>`wuji_glove_get_hand_model_path` / `wuji_glove_set_hand_model_path`</td><td>`glove.hand_model_path().get()` / `.set(path)`</td></tr>
    <tr><td>SDK user management</td><td>`wuji_create_user` / `wuji_switch_user` / `wuji_list_users`</td><td>`manager.create_user()` / `switch_user()` / `list_users()`</td></tr>
    <tr><td>User data export and import</td><td>`wuji_user_data_export` / `wuji_user_data_preview` / `wuji_user_data_import`</td><td>`manager.export_user_data()` / `preview_user_data()` / `import_user_data()`</td></tr>
    <tr><td>Glove hand model calibration (async)</td><td>`wuji_glove_calibration_start` + `wuji_glove_calibration_try_finish`</td><td>`glove.calibrate()`</td></tr>
    <tr><td>Glove hand model calibration (sync)</td><td>`wuji_glove_calibrate`</td><td>`glove.calibrate_blocking()`</td></tr>
    <tr><td>Glove tactile contact calibration (sync)</td><td>`wuji_glove_calibrate_tactile_blocking`</td><td>`glove.calibrate_tactile_blocking()`</td></tr>
    <tr><td>Disconnect</td><td>`wuji_dev_disconnect` + `wuji_dev_release`</td><td>`device.disconnect()`</td></tr>
  </tbody>
</table>

## CMake project example

A complete runnable C project (with `CMakeLists.txt` plus a subscription callback demo) lives in the [`examples/c/` directory of the wuji-sdk repo](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/c/README.md), organized into `examples/c/wuji_glove/` (stream output rate control, custom hand model for online IK, SDK user management, and hand model calibration), `examples/c/wuji_hand_2/` (joint-state subscriptions, control actions, device info, [fingertip tactile data](https://docs.wuji.tech/docs/en/wuji-sdk/latest/wuji-hand-2-tactile.md), stream output rate control, flash-log export, opposition replay, and MIT sweep), and `examples/c/wuji_hand/` (subscriptions, publishing, grasp loops, and tactile status) subdirectories. See that directory's README for build instructions. Minimal link:

> [tab] Linux
>
> ```bash theme={null}
> cc my_app.c -I "${SDK}/include" -L "${SDK}/lib" -lwuji_sdk_c -o my_app
> LD_LIBRARY_PATH="${SDK}/lib" ./my_app
> ```

> [tab] macOS arm64
>
> ```bash theme={null}
> cc my_app.c -I "${SDK}/include" -L "${SDK}/lib" \
>   -lwuji_sdk_c -Wl,-rpath,"${SDK}/lib" -o my_app
> ./my_app
> ```

Use an absolute path for `${SDK}`, the extracted package directory. In CMake, set `WUJI_SDK_INCLUDE_DIR` to `${SDK}/include`, and set `WUJI_SDK_LIB` to `${SDK}/lib/libwuji_sdk_c.so` on Linux or `${SDK}/lib/libwuji_sdk_c.dylib` on macOS.

## Interface List

Python and C types map one to one with the same field semantics. Naming and memory layout follow each language's conventions:

| Type | Python | C |
| - | - | - |
| Frame header | `FrameHeader` | `WujiFrameHeader` |
| 3D vector (f32) | `Vector3` | No standalone struct — f32 triples are inlined as `float[3]`, such as `WujiPose.position` |
| 3D vector (f64) | `Vector3F64` | `WujiVector3F64` |
| Rotation quaternion | `Quaternion` | `WujiQuaternionF32`, `WujiQuaternionF64` |
| Pose | `Pose` | `WujiPose` |
| Device handedness | `Handedness.Left`, `Handedness.Right` | `WUJI_HANDEDNESS_LEFT`, `WUJI_HANDEDNESS_RIGHT` |
| IMU data | `ImuData` | `WujiImuData` |
| Single transform | `FrameTransform` | `WujiFrameTransform` |
| Transform collection | `FrameTransforms` | `WujiFrameTransforms` |
| Recorder and control handle | `TopicRecorder`, `RecordingHandle` | `WujiTopicRecorder`, `WujiRecordingHandle` |
| Recording summary and quality metrics | `RecordingSummary`, `QualitySummary`, `QualityMetrics`, `ChannelHealth`, `RecordingAlert`, `RecordingStatus` | `WujiRecordingSummary`, `WujiRecordingQualitySummary`, `WujiRecordingMetrics`, `WujiRecordingChannelHealth`, `WujiRecordingAlert`, `WujiRecordingStatus` |
| Per-channel recording counts | `ChannelRecordingSummary` | `WujiRecordingChannelSummary` |
| Wuji Hand joint state | `HandJointStates` | `WujiHandJointStates` |
| Wuji Hand joint command | `HandJointCommand` | No frame struct — send an array of 20 `WujiJointCommand` entries |
| Wuji Hand tactile pressure frame | `TactileGloveFrame` | `WujiTactileGloveFrame` |
| Decoded device fault code | The `dict` returned by `WujiHand2.describe_error()` | `WujiErrorInfo` |

Three representation differences stand out. A Python `str` field is a fixed-length `char` array in C, such as `frame_id[32]` and `parent_frame_id[32]`. A Python `list` field is a pointer plus a length in C, such as `transforms` and `transforms_len` on `WujiFrameTransforms`. Python has a single `Quaternion`, while C splits it into `WujiQuaternionF32` and `WujiQuaternionF64` to match the precision each stream carries. C frame pointers stay valid only for the duration of a subscription callback, so copy anything you need to keep. See the [C Interface Reference](https://docs.wuji.tech/docs/en/wuji-sdk/latest/data-reference.md) for the full constraints.


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