Adjusting Stream Rate
SDK Practice
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. This page covers only how to run it and what the measurements show.
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.
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, and imu |
| Wuji Glove | emf_poses, tactile, tactile_zones, imu_raw/palm |
Derived streams reject set_rate. hand_joint_angles, hand_skeleton, tip_poses, and imu_data/palm 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
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
Python
C
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())#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); The rate restores itself once the handle closes, so keep the handle open to keep the setting in effect.
Running It
Python
C
python examples/python/wuji_hand_2/4.set_stream_rate.py./build/hand2/4_set_stream_rate With supporting firmware, output looks like this:
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:
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: how subscriptions are created and live
- Subscribing Asynchronously: using
recv_async()alongside a lowered rate - Wuji Glove Adjusting Stream Rate: the same capability on the glove, requiring firmware v0.11.4 or later
Documentation