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

# Position Control (Beta)

## Overview

Switch Wuji Hand 2 to Position control mode, adjust per-joint output limits and position-command filtering, then transition from the current posture into a whole-hand open/close sequence. Use this practice to check the position-control command path or connect an application-generated trajectory.

The matching Python example is [8.position\_mode.py](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/python/wuji_hand_2/8.position_mode.py). For API definitions, see [Position Control Interface Reference](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference.md#position-control-interface-reference). For mode selection, see [Position Control Mode](https://docs.wuji.tech/docs/en/wuji-hand/latest/control-guide.md#position-control-mode).

> [!WARNING]
>
> This example enables all 20 joints and sends the same open/close angle to every joint. Mount the device securely, clear people, cables, and obstacles from its surroundings, keep someone watching throughout, and stay ready to cut power.

## Prerequisites

### Hardware

| Item | Requirement |
| - | - |
| Wuji Hand 2 | One hand, with all 20 joints online |
| Connection | Powered on and reachable from the host over Ethernet |
| Workspace | Clear throughout the transition from the current posture and the open/close sequence |

### Software

| Item | Requirement |
| - | - |
| Python Environment | Install `wuji_sdk` following [Python SDK Installation](https://docs.wuji.tech/docs/en/wuji-sdk/latest/quick-start.md#install-the-python-sdk) |
| SDK | `v2026.11.9` |
| Firmware | `v3.0.0` or later, supporting Position control mode and N·m limits |

### Before You Start

Confirm the target serial number. If several Wuji Hand 2 devices are found, the example lists their serial numbers and prompts for a selection. Pressing Enter, or running without standard input, selects the first hand in the list.

| Parameter | Default | Purpose |
| - | - | - |
| `EFFORT_LIMIT_SCALE` | `0.8` | Scale applied to each joint's read Position control limit |
| `COMMAND_CUTOFF_HZ` | `40.0` Hz | Position-command low-pass cutoff |
| `AMPLITUDE_RAD` | `0.35` rad | Peak position target |
| `SWEEP_PERIOD_S` | `4.0` seconds | Duration of each open/close cycle |
| `SWEEP_CYCLES` | `2` | Number of cycles |
| `COMMAND_INTERVAL_S` | `1 / 120` seconds | Interval between sweep commands |
| `RAMP_DURATION_S` | `1.0` seconds | Transition from the current posture to the sequence start |

## Data Flow

```mermaid theme={null}
flowchart TD
    CONNECT["Select a hand and check all 20 joints"]
    MODE["Disable and switch to Position control mode"]
    READ["Read current limits and cutoff"]
    CONFIG["Scale per-joint limits and read back<br/>Set cutoff"]
    ENABLE["Enable and wait for all joints"]
    MOVE["Read current posture and ramp<br/>Run two open/close cycles"]
    CLEAN["Disable and close publisher<br/>Restore parameters and disconnect"]
    CONNECT --> MODE --> READ --> CONFIG --> ENABLE --> MOVE --> CLEAN
    CONFIG -. Error or interruption .-> CLEAN
    ENABLE -. Error or interruption .-> CLEAN
    MOVE -. Error or interruption .-> CLEAN
```

## Integration Steps

The snippets below come from the example. See the complete source for helper definitions.

### 1. Connect and Check the Joints

`connect_hand()` scans for devices and connects the selected Wuji Hand 2. If fewer than 20 joints are online, the example reports an error and exits without starting motion.

```python theme={null}
hand = connect_hand(manager, "hand_2_position_mode", wuji_sdk)
online = int(hand.online_joints_count().get())
if online != JOINT_COUNT:
    raise RuntimeError(f"expected 20/20 online joints, got {online}/20")
```

### 2. Switch Modes and Read the Original Parameters

Disable the hand before switching to Position control mode. Read limits only after the switch succeeds, so the saved, adjusted, and restored values all belong to this mode, in N·m.

```python theme={null}
hand.disable()
hand.control_mode().set(wuji_sdk.ControlMode.Position)

effort_limit = hand.effort_limit()
cutoff = hand.position_command_low_pass_cutoff_frequency()
original_limits = read_effort_limits(effort_limit)
original_cutoff = read_command_cutoff(cutoff)
```

The read limits are the values in effect, not necessarily the defaults. `read_effort_limits()` requires 20 valid values. A failed check stops configuration and enabling.

### 3. Adjust Per-Joint Limits and Set the Cutoff

Multiply each joint's own read limit by `EFFORT_LIMIT_SCALE` instead of applying one fixed torque value to every joint.

```python theme={null}
scaled_limits = [value * EFFORT_LIMIT_SCALE for value in original_limits]
written_limits = write_effort_limits(effort_limit, scaled_limits)
cutoff.set(COMMAND_CUTOFF_HZ)
```

`write_effort_limits()` reads all joints after writing. A mismatch raises an error that lists the affected joints. The example registers each restoration before the corresponding write, so it attempts to restore saved parameters even after a write error. For API rules, see [Output Limits](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference/effort-and-mit.md#effort_limit-set/get).

### 4. Enable and Transition from the Current Posture

After `enable()`, `wait_enabled()` waits until the newest diagnostics frame reports all 20 joints as Enabled. The example then reads a complete posture, arranges it by joint index, and uses a 1-second cosine transition to the sequence start.

```python theme={null}
hand.enable()
wait_enabled(hand)
publisher = hand.joint_command().publish()
sweep = generate_sweep()
start = capture_start_positions(hand)
ramp_steps = round(RAMP_DURATION_S / RAMP_INTERVAL_S)
send_sweep(ramp_frames(start, sweep[0], ramp_steps), publisher,
           wuji_sdk, interval=RAMP_INTERVAL_S)
```

An enable timeout or incomplete starting posture enters cleanup without sending the open/close sequence.

### 5. Send the Open/Close Sequence

Every joint follows a cosine target from `0` to `0.35 rad`, with two cycles of 4 seconds each. `send_sweep()` sends 20 `JointCommand` entries per frame, with `velocity` and `effort` both set to `0`.

```python theme={null}
send_sweep(sweep, publisher, wuji_sdk)
```

The target command interval is `1 / 120` seconds. Host scheduling affects the actual rate.

### 6. Stop and Restore Parameters

Normal completion, errors, and Ctrl+C enter cleanup: attempt to disable, close the publisher, restore the original cutoff and limits, and disconnect. A cleanup failure doesn't prevent later cleanup actions. Limit restoration also reads back every joint.

The hand stays in Position control mode after exit. For details on saving and restoring limits, see [Save and Restore Limits](https://docs.wuji.tech/docs/en/wuji-hand/latest/control-guide.md#save-and-restore-limits-optional).

## Running and Output

### Run the Example

From the `wuji-sdk` repository root, run:

```bash theme={null}
python examples/python/wuji_hand_2/8.position_mode.py
```

### Check the Output and Result

The terminal shows the selected device, control mode, original limits, scale, written values read back from the hand, and cutoff frequency. Limits are grouped by four joints per finger, in thumb, index, middle, ring, and pinky order.

Check completion as follows:

* Normal completion returns exit code `0`. Runtime or cleanup failures return `1`. Ctrl+C returns `130`
* The hand is disabled after the sequence, with no `cleanup error` output. If restoration fails, check device state and parameters before continuing
* To assess tracking, record `joint_states` separately and compare target and feedback positions. Printed parameter values aren't a motion-completion check

## Notes

| Condition | Example Behavior |
| - | - |
| Mode switch or original-parameter read fails | Reports an error, attempts to disable and clean up, and doesn't start motion |
| A limit write doesn't take effect | Lists joints with mismatched readbacks and attempts restoration |
| Enable timeout or incomplete posture | Enters cleanup without sending the open/close sequence |
| Ctrl+C during motion | Stops sending, attempts to disable and restore parameters, and doesn't add a homing move |
| Joints go offline during cleanup | Reports restoration errors. Don't assume all parameters were restored |

## Extensions

### Use Another Position Trajectory

Replace the 20-joint position sequence from `generate_sweep()`. Keep parameter snapshots, readback checks, enable waiting, the starting transition, and cleanup. Sending the same angle to all joints is specific to this example, not a general grasping posture.

## Related Content

* [Position Control Mode](https://docs.wuji.tech/docs/en/wuji-hand/latest/control-guide.md#position-control-mode): mode selection
* [Position Control Interface Reference](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference.md#position-control-interface-reference): mode switching, filtering, and command publishing
* [Output Limits](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-reference/effort-and-mit.md#effort_limit-set/get): units, validation, and partial writes
* [Callback Subscription](https://docs.wuji.tech/docs/en/wuji-hand/latest/sdk-practice/subscribe-callback.md#integration-steps): reading joint states


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