HumansXドキュメントセンター English公式サイトへ ↗
ナレッジベース › Wuji Hand 2 › マニュアル

Joint States and Control

SDK Reference

このページは現在英語版のみです。日本語でのご説明が必要な場合は HumansX までお問い合わせください。

The whole-hand API covers 20 joints. Single-joint reads and actions go through the JointHandle returned by hand.joint(k) / hand.joints() — see Joint Traversal.

joint_states (SUB)

Joint state stream: subscribe to the real-time state frames of the 20 joints, at a native output rate of 1000 Hz. Lower the push rate through the subscription handle: sub.set_rate(hz) returns the rate in effect, and 0 restores the native rate — see Adjusting the Stream Output Rate for full usage. This stream and joint_diagnostics ride the same device stream, so lowering the rate of one lowers both.

Push-rate tuning applies to the joint_states, joint_diagnostics, and imu streams. Upgrade the firmware and the Wuji SDK together to versions that support it — see the release notes for the version requirements.

sub = hand.joint_states().subscribe() # → Subscription[JointStateFrame]
frame = sub.recv() # synchronous, non-blocking; None = no data yet
frame = await sub.recv_async() # asynchronous wait
print(frame.header.seq, frame.header.timestamp_us)
for j in frame.joints:
    print(j.nid, j.position, j.velocity, j.effort)
sub.close()

Callback mode:

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()

position is the joint-side angle (rad) and velocity the joint-side angular velocity (rad/s)—this is the only recommended path for joint angle and speed. Frames are variable-length and contain only online joints — identify each entry by its nid. The joint_diagnostics stream doesn’t carry joint angles.

joint_command (PUB)

Joint command: publish positions / velocities / torque feedforward for the 20 joints. Each send takes exactly 20 JointCommand entries, one position / velocity / effort per joint.

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()

To turn human hand keypoints into these 20-joint commands, use Wuji SDK Hand Retargeting.

online_joints_count (GET)

Get the online joint count (0–20).

n = hand.online_joints_count().get() # → int, 0–20

Joint Traversal

JointHandle provides label / index (for indexing into 20-element return arrays such as effort_limit() / mit_params()), plus single-joint resources and actions. FingerHandle traverses joints by finger.

MethodReturn typeDescription
hand.joints()list[JointHandle]All 20 joints
hand.fingers()list[FingerHandle]All 5 fingers
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 joints per finger, in S1..S4 order
        print(joint.label)                 # "thumb_S1", "thumb_S2", ...

JointHandle

Joint handle: provides label / index, single-joint resources, and single-joint actions.

MemberTypeDescription
labelstrProperty. Joint label, format {finger}_S{1..4}, e.g. "thumb_S1", "pinky_S4"
indexintProperty. Global index (0–19)
effort_limit()Resource (SET/GET)Single-joint torque cap (A)
error_code()Resource (GET)The joint’s current error code — decode with describe_error(). Returns an integer, so format it as f"0x{code:04X}" if you print it directly
status_word()Resource (GET)The joint’s status word
enable() / disable()ActionEnable / disable this joint
clear_fault()ActionClear this joint’s faults
set_origin() / clear_origin()ActionSet / clear this joint’s user origin

FingerHandle

Finger handle: traverse the 4 joints of this finger.

MethodReturn typeDescription
joints()list[JointHandle]The 4 joints of this finger, in S1..S4 order

Joint Numbering

FingerS1S2S3S4
thumb0123
index4567
middle891011
ring12131415
pinky16171819

Label format: {finger}_S{1..4} (e.g. thumb_S1, pinky_S4). For the full table with motion ranges and model naming, see the Control Guide.

Data Types

JointStateFrame

Joint state frame: return value of hand.joint_states().subscribe().recv(). Variable-length, online joints only.

AttributeTypeDescription
headerFrameHeaderseq / timestamp_us / frame_id
num_jointsintJoint count in this frame
jointslist[JointStateEntry]Joint state array

JointStateEntry

Single-joint state.

AttributeTypeDescription
nidintNode ID — identifies the joint across frames
positionfloatPosition (rad, joint-side)
velocityfloatVelocity (rad/s)
effortfloatTorque (A)

JointCommand

Joint command: element of the joint_command().publish().send() argument.

class JointCommand:
    position: float # position (rad)
    velocity: float # velocity (rad/s)
    effort: float # torque feedforward (A)
  • Python

  • C

Python: See the Python API examples earlier on this page.

The C API matches the Python API on this page and uses the wuji_hand_2_ prefix. For initialization conventions, memory management, error codes, and build setup, see the C API Reference.

Subscribe to Joint States

Variable-length frames carry a FrameHeader (seq + timestamp_us + frame_id = "l_wrist" or "r_wrist"). The joints array identifies online joints by nid:

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);
// ... close with: wuji_sub_close(sub);

nid is the firmware stream numbering, not the flat 0–19 joint index. Convert it with wuji_hand_2_nid_to_joint_index (see Stream NID and Joint Index below).

Stream NID and Joint Index

Both WujiJointStateEntry.nid and WujiJointDiagnosticsEntry.nid carry the firmware stream numbering, not the flat 0–19 joint index used by per-joint arrays and wuji_hand_2_joint_label. Two static functions convert between the two. Neither requires a device connection.

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);

Both return WujiStatus. *out is valid only when the call returns WUJI_STATUS_OK and is left untouched otherwise, so check the status before using the result. wuji_hand_2_nid_to_joint_index returns WUJI_STATUS_ERR_INVALID_ARG for a NULL out or a non-joint nid (ROOT nid 0 or any other non-joint ID). wuji_hand_2_joint_index_to_nid returns WUJI_STATUS_ERR_INVALID_ARG for a NULL out or an index outside 0–19. On failure, wuji_last_error() names the rejected value and lists the valid joint nids.

Realtime Command Publisher

Open the publisher, then call send with exactly 20 WujiJointCommand entries (each position / velocity / effort). Pass 0 for fields you do not want to feed forward:

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);

Call send at a fixed rate (typically 200 Hz–1 kHz) to maintain continuous control. close is NULL-safe, but you still need to call wuji_joint_command_publisher_close to release the publisher handle, otherwise the resource leaks.