Overview
The glove’s six data streams push at around 130 Hz by default. Most applications can’t use that much, andSubscription.set_rate(hz) lowers the rate at the device itself, saving bandwidth and CPU compared with decimating frames in your application.
One hard requirement: the device firmware must support rate control. The Wuji Glove needs v0.11.4 or later, and the Wuji Hand 2 needs v2.3.0 or later. The glove used here runs firmware 0.11.2, so what this page documents in full is the behavior when that requirement isn’t met, with the success path described from the API contract.
Matching examples: examples/python/wuji_glove/6.set_stream_rate.py and examples/c/wuji_glove/1_set_stream_rate.c.
For the list of adjustable streams and their API definitions, see Hand Tracking. This page covers only how to run it and what the measurements show.
Prerequisites
Hardware
Check the firmware version:
Software
- Python
- C
Before You Start
Read Subscribing With Callbacks first.set_rate is a method on a subscription handle, so a subscription is required before calling it.
Which Streams Are Adjustable
Lowering a source stream lowers every stream derived from it. Drop
emf_poses to 30 Hz and hand_joint_angles, hand_skeleton, and tip_poses all follow, sparing you a proportional share of the solver cost.
Stream Rate Scope
This is the most commonly misread part ofset_rate:
- The setting applies to every subscriber of that stream, including other processes, Wuji Studio, and recorders
- When the last subscription to that stream closes, the rate returns to the device default
- A device reconnect resets it as well
Keep the handle open for as long as you want its rate to stay in effect.
When several clients share a device, the rate you lower is one everyone feels. Coordinate before changing it in a shared setup.
Data Flow
The checks run in order, and that order decides which error you see.Failure Paths
The device runs firmware 0.11.2, below the required v0.11.4.Rate Entry Streams Stop at the Firmware Gate
emf_poses, tactile, and tactile_zones all report the same thing. Passing 0 to restore the default hits the same wall, because the firmware gate comes before the parameter.
Derived Streams Stop at the Entry-Point Check, Regardless of Firmware
Global Streams Report an Internal Error
tf and tf_static report this. It isn’t a friendly message, so recognize it as “I called set_rate on a global stream.” Global streams don’t support rate control at all, since they aggregate across devices and no single device can receive the command.
Parameter Range: uint16
The C signature reads:
Negatives and anything above 65535 are stopped in the binding layer and never reach the device.
The Two Bindings Handle Errors Differently
Given the same firmware-gate error, the two examples react in opposite ways:
Measured output from the C example:
set_rate in a try:
Runtime Handling
The older
emf_poses_rate_divider API (Python glove.emf_poses_rate_divider(), C wuji_glove_get/set_emf_poses_rate_divider) has been removed. Upgrade to firmware v0.11.4 or later and use set_rate on an emf_poses subscription. One practical difference: the old divider persisted across reconnects, while set_rate doesn’t.Integration Steps
- Python
- C
set_rate. Without an active subscription, the active-subscription gate rejects the call.
Running It
- Python
- C
set_rate:
device applied may differ from the request, because the device quantizes to a rate it supports.
Adapting It
Use the Return Value, Not the Request
set_rate returns the rate the device actually applied. Sizing timeouts, buffers, or sampling windows from the requested value goes wrong as soon as quantization kicks in.
How Much Lowering the Rate Saves
It scales linearly. From 130 Hz to 30 Hz, both network traffic and callback count fall to about 23%. Recording benefits most directly: all eight streams with zstd measured about 72 MB per minute, which becomes roughly 17 MB per minute at 30 Hz. Loweringemf_poses also saves hand-solver CPU, because derived streams follow.
Lowering the Rate Doesn’t Change Queue Capacity
The subscription queue still holds 1024 frames. At a lower rate those same 1024 frames cover a longer window, so a slow consumer actually gains headroom. See Subscribing Asynchronously.Keeping a Low Rate Without Holding a Handle
There’s no mechanism that persists across connections today. The oldemf_poses_rate_divider had that property and has been removed. The current approach is to hold the subscription handle inside your long-running service.
Related
- Subscribing With Callbacks: measured rates for all six streams
- Subscribing Asynchronously: the subscription queue’s 1024-frame boundary
- Recording Data: how lowering the rate affects file size
- Wuji Hand 2 Adjusting Stream Rate: the same API on the dexterous hand
- Data Subscription: subscription model and lifecycle