Skip to main content

Overview

The Wuji Glove exposes six device data streams at once: raw tactile, zoned tactile, EMF poses, finger joint angles, hand skeleton, and tactile point cloud. Add the two cross-device coordinate-transform streams tf and tf_static, and a single subscription pass gives you everything the glove produces in real time. Callbacks fire on SDK background worker threads, so your main thread never blocks. This is the shortest path to glove data. Matching examples: examples/python/wuji_glove/0.subscribe_callback.py and examples/c/wuji_glove/0_subscribe_callback.c. For field definitions of each stream, see SDK Reference. This page covers only how to run it and what the measurements show.

Prerequisites

Hardware

This example only reads. It writes no parameters.

Software

Before You Start

Nothing. Power the device and connect the network. Hand model calibration isn’t a prerequisite for subscribing, though it does affect how well hand_joint_angles and hand_skeleton match your hand. See Hand Model Calibration.

Data Flow

Device streams subscribe through a device handle. Global streams take no device handle and are subscribed once per process.

Frame Structures

Measured on a glove resting on a desk, unworn, sampled for 3 seconds.

Six Device Streams

Two Global Streams

tf runs an order of magnitude faster than the device streams because several upstream sources drive it. Heavy work inside a callback shows up here first.

Why 744, 572, and 526 Add Up

-1.0 marks a taxel as invalid or masked. It isn’t a pressure reading. On this glove, 218 of the 744 channels report -1.0: All three converge on 526. tactile_zones includes only channels that map to a hand region, and the 172 unzoned channels happen to be invalid. tactile_point_cloud drops invalid channels entirely and emits only points that have coordinates. Use the point cloud when you need to know where the force lands, zones when you need to know which finger, and tactile when you need raw channel alignment.

Skeleton Joint Names

The 21 joints in hand_skeleton follow the MediaPipe hand topology in a fixed order:
joints[0] is wrist, and its pose is always the origin, because the whole skeleton chain uses the wrist as its reference frame. For where the wrist sits in the world, read the waist → r_wrist transform on the tf stream. The example printing wrist=[+0.000, +0.000, +0.000] is expected, not a data fault.

The Fifth Joint Angle

hand_joint_angles.fingers[i].angles always has length 5, but only the thumb uses all of it. Measured at rest: For every finger but the thumb the fifth value stays 0 as alignment padding. confidence reflects solver quality and drifts between 0.4 and 0.75 at rest on an uncalibrated glove.

Runtime Handling

C callbacks fire on dedicated worker threads, and the frame pointer is valid only for the duration of the callback. Heap arrays such as data, joints, and poses are freed as soon as it returns, so never retain the pointer.

Integration Steps

Always close subscriptions before disconnecting. The other way around, a worker thread may still touch a device you’ve already released.

Running It

On the Python side, all six streams interleave without throttling:
Five seconds produced 3201 lines, about 640 per second, consistent with six streams at roughly 130 Hz each. The C side throttles printing to every 30th frame and adds the two global streams:
Measured on a right-hand Wuji Glove resting on a desk, unworn.

Adapting It

The Example’s avg() Goes Negative on Invalid Channels

This is the one thing worth watching in this example. The Python example averages each zone with avg():
-1.0 lands straight in the numerator. With 20 of the palm’s 308 channels invalid, the output reads palm=-0.1 — a negative reading from a glove under no load at all. The C example prints palm[0], so when that first channel happens to be invalid it shows -1.000, which is exactly what the measurement produced. Filter negatives first on both sides:
After filtering, the same frame gives a palm mean of +0.015. The share of invalid channels varies by glove, so don’t hard-code a count.

Name an SN When Several Devices Share a Network

Both examples iterate every glove they find and connect to each one. On a shared network, limit that with --sn:
The C example takes no command-line arguments, so compare list[i].serial_number in the source before connecting.

Subscribe Only to What You Need

All six streams open means about 640 callbacks per second at rest. For tactile analysis keep tactile or tactile_point_cloud, for hand pose keep hand_skeleton, and callback pressure drops immediately.

Keep Callbacks Light

Each subscription gets its own worker thread. Callbacks within one subscription run serially and in order, while different subscriptions may run concurrently, so guard any state you share across them. Move file writes, algorithms, and lock waits to a consumer thread, and copy only the minimum inside the callback.

Switch to Async

If callbacks don’t suit you, use subscribe() with recv() and control the pacing yourself. See Subscribing Asynchronously.