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

# Adjusting Stream Rate

## Overview

`joint_states` broadcasts at about 1 kHz by default, which is more than most applications need. `set_rate()` asks the firmware to push at a rate you choose, cutting network and CPU cost.

The rate belongs to the **stream**, not to a subscription handle: the setting applies to every subscriber of that stream, and it returns to the device default once the last subscription closes or the device reconnects.

Matching examples: `examples/python/wuji_hand_2/4.set_stream_rate.py` and `examples/c/wuji_hand_2/4_set_stream_rate.c`.

For the list of adjustable streams and their API definitions, see [SDK Reference](/en/docs/wuji-hand/latest/sdk-reference). This page covers only how to run it and what the measurements show.

<Info>
  This feature has a firmware requirement. **Wuji Hand 2 needs firmware v2.3.0 or later**, and devices below that raise on the call. The device verified here runs older firmware, so the success path is described from the release notes and the failure path is measured.
</Info>

## Prerequisites

### Hardware

| Item | Requirement |
| - | - |
| Wuji Hand 2 | 1, **firmware v2.3.0 or later** |
| Connection | Ethernet |

### Software

| Item | Requirement |
| - | - |
| Wuji SDK | **v2026.8.17 or later**, which introduced `Subscription.set_rate()` |
| Python | 3.10 or later |
| C toolchain | CMake 3.16 or later, plus the extracted SDK package |

## Adjustable and Derived Streams

| Device | Adjustable streams |
| - | - |
| Wuji Hand 2 | `joint_states`, `joint_diagnostics`, `imu`, `fingertip/{thumb,index,middle,ring,pinky}/data` |
| Wuji Glove | `emf_poses`, `tactile`, `tactile_zones`, `imu_raw/*` |

**Derived streams reject `set_rate`.** `hand_joint_angles`, `hand_skeleton`, `tip_poses`, and `imu_data/*` are computed from source streams and follow their rate, so calling it on them directly is rejected.

**`joint_states` and `joint_diagnostics` share one device stream.** Adjusting the rate through either subscription changes both.

## Data Flow

```mermaid theme={null}
flowchart TD
    SUB["joint_states().subscribe()<br/>get a subscription handle"]
    SET["sub.set_rate(100)<br/>request 100 Hz from the firmware"]
    FW["firmware<br/>quantizes to a supported rate"]
    RET["returns the rate applied<br/>which may differ from the request"]
    ALL["every subscriber of the stream<br/>receives at the new rate"]
    REST["sub.set_rate(0)<br/>restore the device default"]

    SUB --> SET
    SET --> FW
    FW --> RET
    RET --> ALL
    ALL -->|"or the last subscription closes<br/>or the device reconnects"| REST
```

The request is quantized to a rate the device supports, and **the return value is what actually took effect**. Don't assume it equals the request.

## Runtime Handling

| Branch point | Behavior | What to do |
| - | - | - |
| **Firmware doesn't support it** | Raises `WujiException: Unsupported operation: device 'SN' runs firmware without rate control` | Upgrade the firmware to v2.3.0 or later |
| Called on a derived stream | Rejected | Adjust its source stream instead |
| Unsupported stream on the C side | Returns `WUJI_STATUS_ERR_UNSUPPORTED` (-8) and **sends no request to the device** | Check the return code |
| Passing 0 | Restores the device default rate | Returns the default value |
| Last subscription closes | The rate returns to the device default automatically | Keep the handle open to keep the setting |
| Device reconnects | The rate returns to the device default | Set it again after reconnecting |

## Integration Steps

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio
    from wuji_sdk import DeviceType, SdkManager

    async def main():
        manager = SdkManager.instance()
        hands = [d for d in manager.scan() if d.device_type == DeviceType.WujiHand2]
        sub = None
        try:
            hand = manager.connect(sn=hands[0].sn, device_name="hand")

            # 1. Get a subscription handle. The rate lasts while the handle lives
            sub = hand.joint_states().subscribe()

            # 2. Request a lower rate. The return value is what the device applied
            actual = sub.set_rate(100)
            print(f"requested 100 Hz, device applied {actual} Hz")

            for _ in range(5):
                frame = await sub.recv_async()
                print(f"frame at {actual} Hz: {len(frame.joints)} joints")

            # 3. Pass 0 to restore the device default
            print(f"restored to default: {sub.set_rate(0)} Hz")
        finally:
            if sub is not None:
                sub.close()
            manager.disconnect_all()

    asyncio.run(main())
    ```
  </Tab>

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

    /* 1. Subscribe */
    struct WujiSub *sub = NULL;
    wuji_hand_2_subscribe_joint_states(dev, on_frame, &ctx, &sub);

    /* 2. Adjust the rate. Any subscription handle can do this */
    uint32_t actual = 0;
    WujiStatus st = wuji_sub_set_rate(sub, 100, &actual);
    if (st == WUJI_STATUS_ERR_UNSUPPORTED) {
        /* -8: this stream has no rate control, and no request went to the device */
    } else if (st == WUJI_STATUS_OK) {
        printf("requested 100 Hz, device applied %u Hz\n", actual);
    }

    /* 3. Pass 0 to restore the default */
    wuji_sub_set_rate(sub, 0, &actual);

    wuji_sub_close(sub);
    ```
  </Tab>
</Tabs>

The rate restores itself once the handle closes, so keep the handle open to keep the setting in effect.

## Running It

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

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

With supporting firmware, output looks like this:

```text theme={null}
Connected: WH2KA00XXXXXXXXX
joint_states: requested 100 Hz, device applied 100 Hz
joint_states frame at 100 Hz: 20 joints
...
restored to the device default: 1000 Hz
```

### Measured Behavior Without Firmware Support

Calling this on a device below firmware v2.3.0 raises immediately:

```text theme={null}
wuji_sdk.WujiException: Unsupported operation: device 'WH2KA00XXXXXXXXX' runs firmware without rate control
```

The exception comes from the `set_rate()` call site and leaves the subscription untouched — **it keeps working, simply at the device default rate**. On that device, `joint_states` measured about 1088 Hz by default.

The most direct way to test support is to call `set_rate()` once and see whether it raises. The SDK has no separate capability query.

## Adapting It

### Picking a Rate

Requests are quantized. To confirm what took effect, read the return value of `set_rate()`, or count frames over a fixed interval after subscribing.

### Effect on Other Subscribers

The rate belongs to the stream. With several subscribers on one stream, a change by any of them reaches all of them. That matters most when processes share a device, where one process lowering the rate can undercut another's timing.

### Lowering joint\_states Also Lowers joint\_diagnostics

They share one device stream, so lowering only one of them isn't possible.

### When to Restore

When you don't need an explicit restore, just close the handle and the rate returns to the device default. Passing 0 explicitly suits continuing to receive at the default rate on the same handle.

## Related

* [Subscribing With Callbacks](/en/docs/wuji-hand/latest/sdk-practice/subscribe-callback): how subscriptions are created and live
* [Subscribing Asynchronously](/en/docs/wuji-hand/latest/sdk-practice/subscribe-async): using `recv_async()` alongside a lowered rate
* [Wuji Glove Adjusting Stream Rate](/en/docs/wuji-glove/sdk-practice/stream-rate/): the same capability on the glove, requiring firmware v0.11.4 or later


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