- Python
- C
The contact detection and contact residual streams (TactileBinary and TactileResidual) depend on a trained contact model. Before first use, run
wuji calib tactile to complete tactile calibration. The model loads automatically per SDK user and glove serial number.Tactile Matrix Layout
Tactile data comes from a 24×31 piezoresistive sensor matrix, 744 positions in total, of which 526 are active taxels. TactileFrame, TactileBinary, and TactileResidual all use this layout, in 24×31 row-major order. Access rowr, column c: data[r * 31 + c] (r is 0–23, c is 0–30).
The figure below marks where each column index (left) and row index (right) sits on the physical glove, letting you map a matrix index (or a point’s order in the exported point cloud) to a specific location on the glove.

Layout Change
Starting with firmware v0.11.0, the tactile matrix changes from 24×32 (768 values) to 24×31 (744 values), removing invalid column 26 from the 24×32 layout. The remaining columns keep their relative order: columns 0–25 keep their indices, and columns 27–31 shift left to become columns 26–30.
TactileFrame
Calibrated tactile matrix data. Frame rate: 120 FPS.data is an array of 744 values in 24×31 row-major order. Access row r, column c: data[r * 31 + c]. Invalid positions are fixed at -1.0, which distinguishes them from an active taxel reading zero pressure (0.0).
TactileZones
Tactile data aggregated by finger region. Each region is a rectangle in row-major order. Positions inside the rectangle that belong to the region carry a pressure value, and the rest are fixed at-1.0.
Active taxels across the six regions total 526, the same mask as TactileFrame. Array length is the rectangle’s cell count — index within it to stay in bounds.
PointField
Point cloud field descriptor.PointCloud
Tactile point cloud data: each frame gives the 3D position and pressure of the 526 active taxels on the glove. Positions come from linear blend skinning over the hand skeleton, in thel_wrist or r_wrist frame. Beyond x / y / z, each point also carries pressure (normalized to 0.0–1.0), so applications can color the 3D view by force.
The spatial positions in
glove.tactile_point_cloud() come from the EMF-derived hand pose, so its output rate follows the rate in effect on emf_poses. See Output Rate Tuning.sensor_msgs/PointCloud2: data is a flat byte stream, point_stride is the byte count per point, and fields describes the per-point fields (name, byte offset, type). The current implementation has 4 fields and 16 bytes per point, so data is point_count × 16 bytes packed point by point. Adding a new per-point field only means appending one entry to fields — the PointCloud message definition itself stays unchanged.
TactileBinary
Binary contact detection inferred from tactile data. Frame rate: 120 FPS, published at the same rate asglove.tactile().
data is laid out in 24×31 row-major order, with the same shape and mask as TactileFrame (526 active points). Access row r, column c: data[r * 31 + c]. Existing tactile-grid visualizers work unchanged.
Train a Contact Model
Train once, and the model is saved per SDK user and glove serial number (see tactile data paths) and loaded automatically when you subscribe—no path to configure. There are two training entries:- Wuji CLI: Run
wuji calib tactileand follow the terminal prompts through four recorded motions. The CLI then trains, verifies, and installs the model. For usage, see Tactile Calibration. - SDK calibration API:
await glove.calibrate_tactile()(or the blockingglove.calibrate_tactile_blocking()) records a few prompted hand motions, then trains and installs the model automatically. After each recording keep it, re-record it, or stop—pass anon_pose_promptcallback for interactive control, or omit it for a hands-off run. For complete usage, see the examples7.tactile_calibration.py(Python) and4_tactile_calibration.c(C—API details in Tactile Contact Calibration).
Move a Model to Another Computer
The recommended way iswuji user export and wuji user import. For a manual move, two requirements must be met:
- Copy all three model files:
contact.safetensors,contact.npz, andcontact.json(see tactile data paths).contact.jsonis the model’s commit marker — without it the model doesn’t load. - Put them into the
paths["dir"]directory thatmanager.tactile_model_paths(user_id, sn)returns.
TactileResidual
Per-taxel signed contact residual inferred from tactile data. Frame rate: 120 FPS, published at the same rate asglove.tactile().
data is laid out in 24×31 row-major order, with the same shape and mask as TactileFrame (526 active points). Access row r, column c: data[r * 31 + c].
The residual signal is not smoothed, normalized, or binarized — pick a threshold and post-process it yourself. It shares the same contact model as TactileBinary. Subscribing to both runs model inference only once.
tactile_residual and tactile_binary share one model directory, and it loads automatically from the current SDK user and glove serial number. For the training entries and how to move models across computers, see TactileBinary. The SDK hot-reloads when you switch SDK users or retrain the model.
wuji_glove_ prefix. For initialization conventions, memory management, error codes, and build setup, see the C API Reference.
Tactile Contact Sensitivity
wuji_glove_set_tactile_binary_sensitivity sets the tactile_binary sensitivity multiplier for the active SDK user. The SDK stores the value locally. It takes effect immediately and persists across reconnects. Pass a finite positive value. Values above 1.0 make contact detection more sensitive.
wuji_glove_get_tactile_binary_sensitivity returns the multiplier currently in effect. The returned value is limited to 0.5–3.0. If no valid value is available, the getter returns 1.0.
dev and out_sensitivity must be non-NULL. If either pointer is NULL, the getter returns WUJI_STATUS_ERR_INVALID_ARG. When out_sensitivity is valid, the getter leaves *out_sensitivity unchanged on failure. Call wuji_last_error() for details.
Tactile Contact Calibration
wuji_glove_calibrate_tactile_blocking runs the guided tactile contact calibration in one call: you record a few prompted hand motions, then the SDK collects the data, validates it, and trains the model.
Decide install before you call:
install = true: after training, the SDK runs load validation and installs the model. It’s stored per SDK user and glove serial number, and loads automatically when you subscribe totactile_binary/tactile_residual. The optional sensitivity pair (has_sensitivityset to true plussensitivity) is only accepted in this mode — otherwise the call fails.install = false: the SDK trains only. Load validation and install are skipped, the artifacts stay in the run directory, and nothing loads automatically. Use this to evaluate a training run offline before deciding whether to install.
on_pose_prompt makes the flow interactive: it fires before each motion starts and after each recording ends with a WujiTactilePromptRequest, and returns 0 to proceed, 1 to retry, or 2 to stop — any other value stops the run. on_feedback reports collect and train progress, and returns 0 to continue or 1 to abort — any other value aborts. Leave both unset for a hands-off run. Event pointers passed to callbacks are only valid for the duration of the callback. timeout_s is cooperative: on timeout the calibration is cancelled, but the function first waits for in-flight collect or train work and any running callback to return, so no callback ever fires after the function returns. If on_pose_prompt blocks indefinitely (for example, waiting on console input), the timeout can’t force it to return.
On success, read the results from summary. model_dir is populated in both outcomes — use installed to tell them apart:
installed = true:model_dirpoints to the installed model, which loads automatically when you subscribe.installed = false:model_dirpoints to the model saved in this run, which is not loaded automatically.
verified_alive_taxels is only valid when has_verified_alive_taxels is true. Call wuji_tactile_calibration_summary_free to release the heap fields once you’re done.
Retrieve failure details with wuji_last_error(). A library built without the model-export feature returns WUJI_STATUS_ERR_UNSUPPORTED. The Python counterpart is glove.calibrate_tactile_blocking() — the async glove.calibrate_tactile() is Python-only, with no async C entry point.
To watch contact and residual live, see Tactile Contact Grid View and Tactile Residual Grid View.