HumansXDocumentation Center 日本語Website ↗

Running an MIT Joint Sweep

SDK Practice

Overview

Enable the hand, send joint 0 a fixed cosine position sequence, then disable and disconnect. The whole run takes 2 seconds.

This is the smaller of the two motion examples, meant to verify the command path works rather than to watch fingers move. The cosine amplitude on joint 0 is only 0.02 rad, about 1.15 degrees, and static friction leaves it with almost no visible displacement.

How much actually moves, though, depends on the hand’s starting posture. Each frame carries a complete 20-element command where all 19 joints other than joint 0 target position 0. If the hand starts curled, the very first frame pulls those 19 joints toward zero, and without any interpolation. In one run, nid 19 went from 0.6498 rad to 0.0143 rad, about 36 degrees of real motion, far beyond joint 0’s 1.15 degrees.

Matching examples: examples/python/wuji_hand_2/7.mit_sweep.py and examples/c/wuji_hand_2/7_mit_sweep.c.

For API and field definitions, see JointCommand and MitParam.

This example enables the motors. Clear people, cables, and desk objects from around the hand before starting, keep someone watching throughout, and stay ready to cut power.

When the hand doesn’t start at zero, the first frame pulls all 20 joints toward zero with no interpolation. For a gentle return to zero, run the smooth homing routine in Publishing Control Commands first.

Prerequisites

Hardware

ItemRequirement
Wuji Hand 21, with all 20 joints online, or the example errors out
ConnectionEthernet
NetworkOnly this device may be on the network, for the reason under Runtime Branches

Software

ItemRequirement
Operating systemLinux or a Mac with Apple silicon, meeting the system requirements
Wuji SDKv2026.8.31 or later
Python3.10 or later
C toolchainCMake 3.16 or later, plus the extracted SDK package

This example takes no command-line arguments.

Before You Start

Confirm the device’s MIT parameters and effort limit sit at factory values:

mit_params:    kp=3, kd=0.05   (identical across all 20 joints)
effort_limit:  1.5             (identical across all 20 joints)

Lowering kp erases what little displacement there is, and raising it goes beyond this example’s safety envelope.

Data Flow

The sequence is generated locally in full, then sent frame by frame with no interpolation and no replanning. Each JointCommand is a position, velocity, and torque triple, and this example fills only position, leaving velocity and torque at 0.

Note that every frame is a complete 20-element command. The 19 positions other than joint 0 are hard-coded to 0, which continuously commands them home, and that motion isn’t bounded by the 0.02 rad amplitude.

Integration Steps

1. Connect and Check the Joints

Discover and connect to a Wuji Hand 2, then read the online joint count. The example requires all 20 joints online and doesn’t enable the device if this check fails.

hand = manager.auto_connect(device_name="hand_2_mit_sweep")
if int(hand.online_joints_count().get()) != 20:
    raise RuntimeError("expected 20/20 online joints")

C performs the same check with wuji_scan(), wuji_connect(), and wuji_hand_2_online_joints_count().

2. Enable and Capture the Starting Posture

Call enable() and create a joint_command publisher. The example also subscribes to joint_states, uses nid_to_joint_index to arrange a complete frame in the fixed 20-joint order, and transitions smoothly from the current posture to the sweep start.

hand.enable()
publisher = hand.joint_command().publish()
start = capture_start_positions(hand)
sweep = generate_sweep()
send_sweep(ramp_frames(start, sweep[0], 1000), publisher,
           wuji_sdk, interval=0.001)

C uses wuji_hand_2_enable(hand, NULL) and wuji_hand_2_joint_command_publish(), and copies a complete state frame through a callback.

3. Send Sweep Commands at a Fixed Interval

The sweep changes only joint 0, with an amplitude of 0.02 radians. It sends 101 frames at 20 ms intervals. Each frame contains 20 JointCommand entries, with other joints held at zero. The send loop must respond to Ctrl+C.

send_sweep(sweep, publisher, wuji_sdk)

capture_start_positions(), ramp_frames(), generate_sweep(), and send_sweep() are helpers in Python example. Retain complete-frame validation, the one-second starting transition, and stop checks. Don’t send the first sweep frame from an unknown posture.

C uses the corresponding capture_start_positions(), send_ramp(), and send_sweep() helpers. They send commands through wuji_joint_command_publisher_send(publisher, commands).

4. Stop Motion and Clean Up

On completion or interruption, disable first, close the publisher, then disconnect and release the device. The C example uses wuji_hand_2_disable(hand, NULL) and wuji_joint_command_publisher_close(publisher), with the same cleanup order on every error path.

hand.disable()
publisher.close()
manager.disconnect_all()

Python example and C example catch cleanup errors individually so a failed disable doesn’t skip publisher closure or disconnection.

Running It

  • Python

  • C

python examples/python/wuji_hand_2/7.mit_sweep.py
./build/hand2/7_mit_sweep

A normal run prints nothing to standard output and exits 0. Only errors go to standard error. Two quiet seconds mean success.

Measurements

On a right-hand Wuji Hand 2 with all 20 joints online, subscribing to joint_states alongside to record joint 0’s response:

Commanded positionActual positionActual torque
+0.0000+0.0050−0.0030
+0.0100+0.0046+0.0245
+0.0200+0.0045+0.0803
+0.0100+0.0046+0.0392
+0.0000+0.0045−0.0219
MetricMeasured
Commands and duration101 commands over 2.016 seconds
Actual send rate49.9 Hz
Command peak0.0200 rad
Actual joint displacement range0.0004 rad
Torque range0.1384
enable() duration0.001 seconds

Joint 0 barely moves, and that’s expected. Torque tracks the command’s rise and fall precisely, showing the command went out and the motor responded under MIT impedance. A 0.02 rad target error at kp=3 produces only about 0.06 of torque, which doesn’t overcome the joint’s static friction, leaving an actual displacement of 0.0004 rad, roughly 0.023 degrees and invisible to the eye.

The table above comes from a hand already near zero. From a contrasting run: starting with the hand curled, the same example pulled the other 19 joints toward zero with the displacements below.

nidBeforeAfterDisplacement
7+0.5018−0.0018−0.5036
9+0.6070+0.0382−0.5688
19+0.6498+0.0143−0.6355
22+0.5375+0.0652−0.4723

nid 19 moved 0.6355 rad, about 36 degrees. So “smallest amplitude” holds only when the hand already sits at zero.

Success here means the process exits quietly with code 0, plus a torque response visible on a joint_states subscription, not fingers you can see move. To watch real motion, see Replaying a Thumb Opposition.

Notes

Branch pointBehaviorWhat to do
Several devices on the networkThe example uses auto_connect(), which raises Found 6 devices, please specify SN to connect and exits without connecting to the wrong oneOn a shared network, name a serial number. See Adapting It
Fewer than 20 joints onlineRaises expected 20/20 online joints, got N/20 without enablingInvestigate the offline joints first
Enable failsRaises enable failed: ... and sends no commandsCheck fault codes and call clear_fault() if needed
Ctrl+CExit code 130, stopping further commands and performing no return motionThe hand holds its position, then is disabled
Runtime exceptionExit code 1Cleanup still runs
Normal completionExit code 0Disables right after the last command

Whichever path it takes, cleanup disables the motors, closes the publisher, and disconnects. Neither errors nor interrupts add any recovery motion, so the hand never returns to its starting posture on its own.

Adapting It

Name an SN on a Shared Network

auto_connect() means “connect to the one device found on the bus.” With more than one present it refuses and lists every serial number rather than guessing:

Internal error: Found 6 devices, please specify SN to connect.
Available: WG1KA00XXXXXXXXX, WH2KA00XXXXXXXXX, DF1XE00XXXXXXXXX, ...

That design avoids disturbing someone else’s device, at the cost of the example not running as-is on a shared network. Replace step 1:

hand = manager.connect(sn="WH2KA00XXXXXXXXX", device_name="hand_2_mit_sweep")

To Actually See a Joint Move

This example isn’t built for observing motion. For visible range, use the complete motion sequence in Replaying a Thumb Opposition. Raising this example’s amplitude yourself exceeds its safety envelope and isn’t advised.

How to Read Joint State

joint_states is a subscription resource and has no get() method. To read actual positions while commanding, call subscribe_with_callback() to receive continuously and take values from the latest frame. Calling hand.joint_states().get() directly raises AttributeError.

Changing the Command Rate

The interval is hard-coded at 20 ms. A shorter interval raises the command rate, but the fixed 101-frame sequence simply finishes sooner without changing the trajectory’s shape.