Skip to main content

Overview

tactile_binary has already decided whether something touched the glove. tactile_residual hands you the continuous signal from the step before that: a signed residual per taxel, where positive means pressed harder than the calibrated baseline, near zero means no contact, and negative means lighter than baseline. That’s the difference — the threshold belongs to the consumer. tactile_binary uses a threshold the SDK derives from sensitivity, while tactile_residual makes no judgment and leaves the line for you to draw. This example lays the residual out as a 24x31 grid, shows intensity with a six-level character ramp, and lets you adjust the threshold live to watch the grid respond. It’s an observation tool, not another calibration workflow. Matching examples: examples/python/wuji_glove/9.tactile_residual_view.py and examples/c/wuji_glove/7_tactile_residual_view.c. For tactile field definitions, see Tactile. This page covers only how to run it and what the measurements show.
The grid rendering, character ramp rules, threshold behavior, and uncalibrated appearance on this page are all measured. The view under actual contact isn’t — producing a residual requires someone wearing the glove and touching something.

Prerequisites

Hardware

Software

Before You Start

Run tactile calibration first. The residual is computed against a calibrated baseline, and without that baseline there’s no residual — this stream measures exactly zero on an uncalibrated glove. See Tactile Calibration.

How the Residual Relates to the Binary Stream

Both streams come from the same model, and tactile_binary is tactile_residual after one threshold. Take the former when you want a ready-made verdict, the latter when you want to set the criterion yourself.

The Grid Matches the Binary View Exactly

tactile_residual also carries 744 values, with the same 218 invalid channels in the same places and the same row-major mapping of index = row * 31 + col. 24 x 31 = 744, so the grid is the sensor array’s physical layout. The hand outline is therefore identical between the two views, and switching between them never shifts anything. For where the channel counts come from, see Subscribing With Callbacks.

Six-Level Character Ramp

This is the main display difference from the binary view: The ramp maps proportionally:
A value in the first sixth of the threshold shows ., the second sixth shows :, and so on, with * meaning nearly at the threshold. The ramp is relative to the threshold, so adjusting the threshold redistributes the whole scale. It isn’t an absolute measure. The invalid-channel test uses an approximate float comparison rather than == -1.0, avoiding an equality check:

Threshold Behavior

The clamp has a floor only:
There’s no ceiling. That differs from sensitivity in the binary view, which the example clamps to [0.5, 3.0]. The residual threshold is your own criterion, and the example doesn’t bound it for you. threshold = 0 is a special case: in cell_for, the value <= 0.0 or threshold <= 0.0 branch renders every valid channel as . and # never appears. To “show everything,” use a very small positive number rather than zero. In an interactive terminal, + and - step by 0.1 (THRESHOLD_STEP = 0.1).

Status Line

The sign on peak matters. A negative peak means a whole region reads lighter than the calibrated baseline, which usually points at a loose fit or a hand posture far from the one used during calibration. seq confirms the stream is alive: a number that stops moving means no new frames.

Render Throttling

Throttled to 10 Hz, same as the binary view:
The stream runs at 130 Hz with callbacks firing per frame on an SDK worker thread. Without throttling, that thread spends itself on terminal output.

Runtime Handling

Integration Steps

The residual stream needs no device parameter at all, which is another difference from the binary stream, where sensitivity has to be set() on the device.

Running It

Measured output, uncalibrated and unworn:
Measured on a right-hand Wuji Glove resting on a desk, unworn and uncalibrated. The outline matches the binary view exactly, because invalid channels sit in the same places. peak=+0.000 and above=0 are what an uncalibrated glove looks like, while seq keeps incrementing to show the stream itself is alive — a stream carrying frames isn’t the same as a stream carrying content, which is what the seq field is for. Once calibrated and worn, contact regions render as the . : - = + * ramp by intensity, places at or above the threshold turn into #, and peak gives the strongest current residual.

Adapting It

Find Your Threshold From peak

Use the example as follows: hold the lightest contact your application cares about, watch where peak settles, and set the threshold just below that. It beats guessing, and it beats tuning sensitivity by trial and error.

Read the Ramp for Contact Edges

# tells you only where the threshold was crossed, while the ramp shows how contact falls off toward the edges. For grasp-posture analysis or contact-area estimation, that gradient carries more than a binary verdict.

A Negative Residual Is a Signal

A persistently negative peak means the whole glove reads lighter than its calibrated baseline, most often because it’s worn looser than during calibration. Refit the glove rather than lowering the threshold to compensate.

For a Verdict, Use the Binary Stream

Writing your own threshold logic costs real work: noise handling, temporal filtering, hysteresis. If all you need is whether something touched the glove, use tactile_binary, where the SDK has already done that. The residual earns its place when your criterion differs from the default.