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

# 关节状态与控制

整手 API 覆盖 20 个关节。单关节的读取与动作通过 `hand.joint(k)` / `hand.joints()` 返回的 `JointHandle` 进行，见[关节遍历](#关节遍历)。

## joint\_states（SUB）

**关节状态流**：订阅 20 关节的实时状态帧，原生输出率 1000 Hz。可通过订阅句柄调低推送频率：`sub.set_rate(hz)` 返回实际生效值，传 `0` 恢复原生输出率，完整用法见[调节订阅流输出率](/docs/wuji-sdk/data-subscription/)。本流与 `joint_diagnostics` 共用同一条设备流，调低其中一个的推送频率，另一个会同步降低。

<Info>
  推送频率调节适用于 `joint_states`、`joint_diagnostics` 与 `imu` 流。固件与 Wuji SDK 需同批升级到支持该能力的版本，版本要求见对应发布记录。
</Info>

```python theme={null}
sub = hand.joint_states().subscribe()   # → Subscription[JointStateFrame]
frame = sub.recv()                      # 同步非阻塞，None = 暂无数据
frame = await sub.recv_async()          # 异步等待
print(frame.header.seq, frame.header.timestamp_us)
for j in frame.joints:
    print(j.nid, j.position, j.velocity, j.effort)
sub.close()
```

回调模式：

```python theme={null}
def on_state(frame):
    print(frame.header.seq, [j.position for j in frame.joints[:4]])

cb = hand.joint_states().subscribe_with_callback(on_state)
# ...
cb.close()
```

<Info>
  `position` 为关节侧角度（rad），`velocity` 为关节侧角速度（rad/s），这是获取关节角与关节速度的唯一推荐路径。帧变长、仅含在线关节，用每条的 `nid` 定位关节。`joint_diagnostics` 流不包含关节角。
</Info>

## joint\_command（PUB）

**关节命令**：发布 20 关节的位置 / 速度 / 力矩前馈。每次发送恰好 20 个 `JointCommand`，每个携带该关节的 `position` / `velocity` / `effort`。

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

pub = hand.joint_command().publish()    # → JointCommandPublisher
pub.send([JointCommand(position=p, velocity=0.0, effort=0.0) for p in positions])
pub.close()
```

要把人手关键点转换成这里的 20 维关节命令，用 Wuji SDK 的[手部重定向](/docs/wuji-sdk/retargeting/)。

## online\_joints\_count（GET）

获取**在线关节数**（0–20）。

```python theme={null}
n = hand.online_joints_count().get()   # → int，0–20
```

## 关节遍历

`JointHandle` 提供 `label` / `index`（用于与 `effort_limit()` / `mit_params()` 等 20 元素返回数组做下标对应），以及单关节资源与动作。`FingerHandle` 用于按手指遍历关节。

| 方法 | 返回类型 | 说明 |
| - | - | - |
| `hand.joints()` | `list[JointHandle]` | 全部 20 个关节 |
| `hand.fingers()` | `list[FingerHandle]` | 全部 5 根手指 |

```python theme={null}
for joint in hand.joints():
    print(joint.label, joint.index)        # e.g. "thumb_S1" 0

for finger in hand.fingers():
    for joint in finger.joints():          # 每根手指 4 个关节，按 S1..S4 顺序
        print(joint.label)                 # "thumb_S1", "thumb_S2", ...
```

### JointHandle

**关节句柄**：提供 label / index、单关节资源与单关节动作。

| 成员 | 类型 | 说明 |
| - | - | - |
| `label` | `str` | 属性。关节标签，格式 `{finger}_S{1..4}`，如 `"thumb_S1"`、`"pinky_S4"` |
| `index` | `int` | 属性。全局索引（0–19） |
| `effort_limit()` | 资源（SET/GET） | 单关节力矩限幅（A） |
| `error_code()` | 资源（GET） | 单关节当前错误码，用 `describe_error()` 解码。返回整数，直接打印时自行按 `f"0x{code:04X}"` 格式化 |
| `status_word()` | 资源（GET） | 单关节状态字 |
| `enable()` / `disable()` | 动作 | 单关节使能 / 失能 |
| `clear_fault()` | 动作 | 清除该关节故障 |
| `set_origin()` / `clear_origin()` | 动作 | 设置 / 清除该关节用户零点 |

### FingerHandle

**手指句柄**：遍历该手指的 4 个关节。

| 方法 | 返回类型 | 说明 |
| - | - | - |
| `joints()` | `list[JointHandle]` | 该手指 4 个关节，按 S1..S4 顺序 |

## 关节编号

| 手指 | S1 | S2 | S3 | S4 |
| - | - | - | - | - |
| thumb | 0 | 1 | 2 | 3 |
| index | 4 | 5 | 6 | 7 |
| middle | 8 | 9 | 10 | 11 |
| ring | 12 | 13 | 14 | 15 |
| pinky | 16 | 17 | 18 | 19 |

标签格式：`{finger}_S{1..4}`（如 `thumb_S1`、`pinky_S4`）。含运动范围与模型命名的完整对照表见[控制指南](/docs/wuji-hand/latest/control-guide#关节表)。

## 数据类型

### JointStateFrame

**关节状态帧**：`hand.joint_states().subscribe().recv()` 返回值。变长，仅含在线关节。

| 属性 | 类型 | 说明 |
| - | - | - |
| `header` | `FrameHeader` | `seq` / `timestamp_us` / `frame_id` |
| `num_joints` | `int` | 本帧关节数量 |
| `joints` | `list[JointStateEntry]` | 关节状态数组 |

### JointStateEntry

**单关节状态**。

| 属性 | 类型 | 说明 |
| - | - | - |
| `nid` | `int` | 节点编号，跨帧定位关节 |
| `position` | `float` | 位置（rad，关节侧） |
| `velocity` | `float` | 速度（rad/s） |
| `effort` | `float` | 力矩 (A) |

### JointCommand

**关节命令**：`joint_command().publish().send()` 入参元素。

```python theme={null}
class JointCommand:
    position: float    # 位置 (rad)
    velocity: float    # 速度 (rad/s)
    effort: float      # 力矩前馈 (A)
```

<Tabs>
  <Tab title="Python">
    Python：本页前文已展示 Python 接口。
  </Tab>

  <Tab title="C">
    C 侧接口与本页 Python 接口语义一致，函数前缀 `wuji_hand_2_`。初始化约定、内存管理、错误码与工程构建见 [C 接口参考](/docs/wuji-sdk/data-reference/)。

    ### 订阅关节状态

    变长帧含 `FrameHeader`（`seq` + `timestamp_us` + `frame_id = "l_wrist" | "r_wrist"`），`joints` 数组按 `nid` 识别在线关节：

    ```c theme={null}
    void on_state(WujiFrameKind kind, const WujiJointStateFrame* frame, void* user) {
        if (kind != WUJI_FRAME_KIND_OK) return;
        printf("seq=%u frame=%s n=%u\n", frame->header.seq, frame->header.frame_id, frame->num_joints);
        for (size_t i = 0; i < frame->joints_len; i++) {
            const WujiJointStateEntry* j = &frame->joints[i];
            printf("  nid=%u pos=%+.3f vel=%+.3f eff=%+.3f\n", j->nid, j->position, j->velocity, j->effort);
        }
    }

    WujiSub* sub = NULL;
    wuji_hand_2_subscribe_joint_states(dev, on_state, NULL, &sub);
    // ... 关闭：wuji_sub_close(sub);
    ```

    `nid` 是固件流编号，不是 flat 0–19 关节索引。用 `wuji_hand_2_nid_to_joint_index` 换算，见下文[流 nid 与关节索引](#流-nid-与关节索引)。

    ### 流 nid 与关节索引

    `WujiJointStateEntry.nid` 与 `WujiJointDiagnosticsEntry.nid` 都是固件流编号，与逐关节数组和 `wuji_hand_2_joint_label` 使用的 flat 0–19 关节索引不是同一套编号。两个静态函数负责互相换算，都无需设备连接。

    ```c theme={null}
    WujiStatus wuji_hand_2_nid_to_joint_index(uint8_t nid, uint8_t *out);
    WujiStatus wuji_hand_2_joint_index_to_nid(uint8_t joint_index, uint8_t *out);
    ```

    两者都返回 `WujiStatus`，只有返回 `WUJI_STATUS_OK` 时 `*out` 才有效，失败时 `*out` 保持原值，使用结果前先检查状态。`wuji_hand_2_nid_to_joint_index` 在 `out` 为 `NULL` 或 `nid` 不是关节编号（ROOT 的 `nid` 0、触觉槽位或越界值）时返回 `WUJI_STATUS_ERR_INVALID_ARG`。`wuji_hand_2_joint_index_to_nid` 在 `out` 为 `NULL` 或索引超出 0–19 时返回 `WUJI_STATUS_ERR_INVALID_ARG`。失败时 `wuji_last_error()` 会给出被拒绝的值和有效的关节 `nid` 列表。

    ### 实时指令 publisher

    打开 publisher 后调用 `send`，传入恰好 20 个 `WujiJointCommand`（每项 `position / velocity / effort`），不需要前馈的字段填 0：

    ```c theme={null}
    WujiJointCommandPublisher* pub = NULL;
    wuji_hand_2_joint_command_publish(dev, &pub);

    WujiJointCommand cmds[20] = {0};
    // cmds[i].position = ...; cmds[i].velocity = 0; cmds[i].effort = 0;
    wuji_joint_command_publisher_send(pub, cmds);

    wuji_joint_command_publisher_close(pub);
    ```

    按固定频率循环调 `send` 维持持续控制（典型 200 Hz–1 kHz）。`close` 对 NULL 安全，但 publisher 句柄仍需调用 `wuji_joint_command_publisher_close` 释放，否则会泄漏资源。
  </Tab>
</Tabs>


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