Skip to main content

Overview

The glove’s six data streams push at around 130 Hz by default. Most applications can’t use that much, and Subscription.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.
The success path on this page isn’t verified on hardware, because the available device’s firmware doesn’t meet the requirement. The failure paths, evaluation order, parameter bounds, and the difference in behavior between the two bindings are all measured.

Prerequisites

Hardware

Check the firmware version:

Software

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 of set_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
So keeping a lower rate in effect means holding that subscription handle open. The example says as much in a comment: 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

This check runs before the firmware gate, so newer firmware reports exactly the same error. Seeing this means changing which stream you call, not upgrading firmware.

Global Streams Report an Internal Error

Both 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:
Python uses the same 16-bit unsigned type. Measured bounds: 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:
The C approach is the sturdier one. Rate control is an optional optimization and shouldn’t take down a capture pipeline because a device runs older firmware. When adapting the Python example, wrap 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

Subscribe before calling set_rate. Without an active subscription, the active-subscription gate rejects the call.

Running It

Python output on firmware 0.11.2, where the program stops at set_rate:
C output on the same device, which keeps receiving frames after the error:
Measured on a right-hand Wuji Glove running firmware 0.11.2. With firmware that meets the requirement, Python output looks like this:
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. Lowering emf_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 old emf_poses_rate_divider had that property and has been removed. The current approach is to hold the subscription handle inside your long-running service.