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

# Running an MIT Joint Sweep

## 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 MIT control mode and parameter safety bounds, see [Control Guide](/en/docs/wuji-hand/latest/control-guide), and for the torque and MIT parameter APIs, see [Effort and MIT Parameters](/en/docs/wuji-hand/latest/sdk-reference/effort-and-mit). This page covers only how to run it and what the measurements show.

<Info>
  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](/en/docs/wuji-hand/latest/sdk-practice/publish-command) first.
</Info>

## Prerequisites

### Hardware

| Item | Requirement |
| - | - |
| Wuji Hand 2 | 1, with **all 20 joints online**, or the example errors out |
| Connection | Ethernet |
| Network | **Only this device may be on the network**, for the reason under Runtime Branches |

### Software

| Item | Requirement |
| - | - |
| Operating system | Ubuntu 22.04 LTS or later |
| Wuji SDK | v2026.8.31 or later |
| Python | 3.10 or later |
| C toolchain | CMake 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:

```text theme={null}
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

```mermaid theme={null}
flowchart TD
    GEN["generate_sweep()<br/>101-frame cosine sweep"]
    PUB["joint_command().publish()<br/>publisher"]
    SEND["send() 20 JointCommand per frame<br/>20 ms apart"]
    FW["MIT impedance control<br/>kp=3, kd=0.05, limit 1.5"]
    J["joint 0 follows the cosine<br/>the other 19 are pulled toward zero"]

    GEN -->|"101 frames, only joint 0 non-zero"| PUB
    PUB --> SEND
    SEND -->|"about 50 Hz"| FW
    FW -->|"torque = kp x position error"| J
```

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.

## Runtime Handling

| Branch point | Behavior | What to do |
| - | - | - |
| **Several devices on the network** | The example uses `auto_connect()`, which raises `Found 6 devices, please specify SN to connect` and **exits without connecting to the wrong one** | On a shared network, name a serial number. See Adapting It |
| Fewer than 20 joints online | Raises `expected 20/20 online joints, got N/20` without enabling | Investigate the offline joints first |
| Enable fails | Raises `enable failed: ...` and sends no commands | Check fault codes and call `clear_fault()` if needed |
| `Ctrl+C` | Exit code 130, stopping further commands and **performing no return motion** | The hand holds its position, then is disabled |
| Runtime exception | Exit code 1 | Cleanup still runs |
| Normal completion | Exit code 0 | Disables 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.

## Integration Steps

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import math, time, wuji_sdk

    manager = wuji_sdk.SdkManager.instance()

    # 1. Connect. With several devices around, use connect(sn=...)
    hand = manager.auto_connect(device_name="hand_2_mit_sweep")

    # 2. Gate: all 20 joints must be online
    if int(hand.online_joints_count().get()) != 20:
        raise RuntimeError("expected 20/20 online joints")

    # 3. Enable
    hand.enable()

    # 4. Get a publisher
    publisher = hand.joint_command().publish()

    # 5. Send frame by frame, one every 20 ms
    for i in range(101):
        phase = 2.0 * math.pi * i / 100
        position = [0.0] * 20
        position[0] = 0.02 * 0.5 * (1.0 - math.cos(phase))
        publisher.send([wuji_sdk.JointCommand(v, 0.0, 0.0) for v in position])
        if i < 100:
            time.sleep(0.02)

    # 6. Clean up: disable, close the publisher, disconnect
    hand.disable()
    publisher.close()
    manager.disconnect_all()
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    #include "wuji_sdk.h"

    /* 1. Initialize and connect */
    wuji_init(&opts);
    wuji_scan(&list, &n);
    wuji_connect(&tgt, "hand2", &copts, &dev);

    /* 2. Gate: all 20 joints must be online */
    uint32_t online = 0;
    wuji_hand_2_online_joints_count(dev, &online);
    if (online != 20) { /* report an error and exit without enabling */ }

    /* 3. Enable */
    wuji_hand_2_enable(dev);

    /* 4. Get a publisher */
    struct WujiPub *pub = NULL;
    wuji_hand_2_joint_command_publisher(dev, &pub);

    /* 5. Send frame by frame */
    WujiJointCommand cmd[20] = {0};
    for (int i = 0; i <= 100; i++) {
        double phase = 2.0 * M_PI * i / 100.0;
        cmd[0].position = 0.02 * 0.5 * (1.0 - cos(phase));
        wuji_hand_2_publish_joint_command(pub, cmd, 20);
        if (i < 100) usleep(20000);
    }

    /* 6. Clean up */
    wuji_hand_2_disable(dev);
    wuji_pub_close(pub);
    wuji_dev_disconnect(dev);
    wuji_dev_release(dev);
    wuji_shutdown();
    ```
  </Tab>
</Tabs>

The order matters. Gate before enabling, and disable before closing the publisher.

## Running It

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    python examples/python/wuji_hand_2/7.mit_sweep.py
    ```
  </Tab>

  <Tab title="C">
    ```bash theme={null}
    ./build/hand2/7_mit_sweep
    ```
  </Tab>
</Tabs>

**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 position | Actual position | Actual 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 |

| Metric | Measured |
| - | - |
| Commands and duration | 101 commands over 2.016 seconds |
| Actual send rate | 49.9 Hz |
| Command peak | 0.0200 rad |
| **Actual joint displacement range** | **0.0004 rad** |
| Torque range | 0.1384 |
| `enable()` duration | 0.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.

| nid | Before | After | Displacement |
| - | - | - | - |
| 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](/en/docs/wuji-hand/latest/sdk-practice/opposition-replay).

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

```text theme={null}
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:

```python theme={null}
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](/en/docs/wuji-hand/latest/sdk-practice/opposition-replay). 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.

## Related

* [Replaying a Thumb Opposition](/en/docs/wuji-hand/latest/sdk-practice/opposition-replay): the larger complete four-finger motion
* [Publishing Control Commands](/en/docs/wuji-hand/latest/sdk-practice/publish-command): general `joint_command` usage
* [Subscribing With Callbacks](/en/docs/wuji-hand/latest/sdk-practice/subscribe-callback): how the measurements on this page were collected
* [Common Data Types](/en/docs/wuji-sdk/data-reference/): `JointCommand` and joint-state structures


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