SDK Data Reference

Hand Shape Calibration

This page covers the SDK interface for hand shape calibration (IK calibration). To run the same calibration in Wuji Studio, see Device Calibration. The other calibration—Tactile Contact Calibration—trains a tactile contact detection model, runs in Wuji Studio only, and is independent of hand shape calibration.

Wuji Glove uses EMF data to solve finger-joint angles, which first requires an IK calibration to generate a URDF model matched to the wearer's hand. Calibration output belongs to the SDK user and hand side. Any glove of the same side under the same user shares it, so swapping gloves needs no recalibration.

When to Calibrate

  • The first time an SDK user uses a glove on a given side
  • A new wearer, calibrated under their own user

Left and right gloves calibrate independently. Under the same SDK user, switching to another glove of the same side reuses the existing calibration, no recalibration needed. With SDK user isolation on, each user keeps their own calibration URDF. See SDK User Management.

SDK User Requirement

Calibration output belongs to a named SDK user. The default user neither stores calibration output nor loads it — real-time IK always uses the built-in default model. Create and switch to a named user before calibrating:

from wuji_sdk import SdkManager

manager = SdkManager.instance()
user = manager.create_user("alice")     # "alice" is the display name
manager.switch_user(user["user_id"])    # switch by user_id, not display name

Calibrating or setting a custom URDF under the default user raises an error that tells you to run create_user and switch_user first.

Calibration Flow

Calibration asks the wearer to follow a set of guided poses while the SDK captures EMF data and solves the matching URDF.

When calibration starts, the SDK temporarily forces the EMF rate divider to 1 and restores the original value when capture ends (including error and cancel paths). Calling glove.emf_poses_rate_divider().set(N>1) beforehand does not stretch the calibration time. See Output Rate Tuning.

Synchronous Blocking Call

Best for scripts and command-line tools:

from wuji_sdk import SdkManager, Handedness

manager = SdkManager.instance()
glove = manager.connect(handedness=Handedness.Left, device_name="glove")

result = glove.calibrate_blocking(timeout_s=900.0)

print("handedness:", result["handedness"])
print("calibrated urdf:", result["calibrated_urdf"])
print("poses collected:", result["poses_collected"])
print("sdk user:", result["sdk_user"]["display_name"])

calibrate_blocking() does not return until calibration finishes or times out, and Ctrl+C is deferred until the call returns.

Asynchronous Call

asyncio programs use the async API, which supports standard cancellation:

result = await glove.calibrate(timeout_s=900.0)

Parameters

ParameterTypeDefaultDescription
skip_constraintsboolFalseSkip stability and constraint checks (debug only)
timeout_sfloat900.0Overall timeout in seconds, must be a finite positive number
on_feedbackCallable or NoneNoneReal-time feedback callback

hand_profile is deprecated and ignored. Calibration always produces one hand model per side using the canonical baseline. Passing "wujihand" or "wujihand2" emits a DeprecationWarning, and other values raise an error. Remove this argument.

Real-Time Feedback

Use the on_feedback callback to track calibration state:

def on_feedback(fb):
    if fb.get("state") == "collecting":
        progress = fb.get("progress", 0.0)
        step = fb.get("step_index", 0)
        total = fb.get("step_total", 0)
        print(f"step {step}/{total}: {progress*100:.0f}%")
    for hint in fb.get("hints", []):
        print(f"hint: {hint}")

result = glove.calibrate_blocking(on_feedback=on_feedback)

The fb dict carries the current pose index, state, progress, captured frame count, pose-deviation diagnostics (metrics), and hint text (hints).

Exceptions raised inside the callback are logged by the SDK and do not interrupt calibration.

Custom URDF Override

glove.hand_model_path().set(path) points to a user-supplied external URDF file. It's the canonical accessor for the calibration.hand_model_path resource and has the highest priority in the URDF lookup order:

# Set a custom hand URDF (highest real-time IK priority)
glove.hand_model_path().set("/path/to/custom_hand.urdf")

# Read back the current override
print(glove.hand_model_path().get())

Setting a path reloads the online-IK streams (hand_joint_angles, tip_poses, hand_skeleton) against the custom URDF. Set an empty path to fall back to the current user's calibrated model. Only paths outside the SDK-managed directory count as an external override — a path inside the managed directory has no effect.

The custom URDF override is available only to a named SDK user. Calling set() under the default user is rejected, so switch to a named user first, matching the user isolation of calibration output.

URDF Lookup Order

Priority for real-time IK and tf_static when loading the hand URDF:

  1. External custom path: calibration.hand_model_path pointing to a file outside the SDK-managed directory
  2. Current user's calibrated model: the stable per-side files left_hand.urdf / right_hand.urdf for that SDK user
  3. Built-in default URDF: the SDK's own model per hand side

The default SDK user skips levels 1 and 2 and always uses the built-in default model. To use a calibrated or custom URDF, switch to a named user first.

The SDK loads no legacy output — old profile paths, managed hand_model_path values written by older versions, and per-serial-number calibration files are no longer loaded at runtime, and there's no automatic migration. Recalibrate to regenerate. The offline pipeline API also accepts an explicit urdf_path, which takes priority over all of the above when passed.

Return Fields

Summary returned by calibrate() / calibrate_blocking():

FieldTypeDescription
handednessstrThe calibrated hand side ("left" or "right")
calibrated_urdfstrThe stable per-user model path this calibration produced
poses_collectedintNumber of poses captured
frames_per_posedict[str, int]Pose name to captured-frame-count map
sdk_userdictSDK user info at calibration time (user_id / display_name / description / is_default)

Where Calibration Is Stored

The stable files are named by SDK user and hand side. All same-side gloves under one user share the same model:

SDK UserModel Directory
Named user~/.wuji/sdk/users/<user_id>/models/left_hand.urdf, right_hand.urdf

Uniqueness comes from the (user_id, hand_side) pair and no longer binds to a device serial number. Switching the SDK user reloads the matching model for connected devices automatically, no reconnect required. Writes use an atomic replace, so a failed calibration never corrupts existing output.

Calibration bundles use a new format: hand models are user-level, tactile data stays per serial number. Importing a bundle from an older SDK version keeps the tactile data but skips the old per-serial-number calibration (no longer loaded, no migration). Bundles exported by this version require an up-to-date SDK to import.

Upgrading From Older Versions

After upgrading to this version, previously generated calibration URDFs are no longer loaded — real-time IK and tf_static fall back to the built-in default model. The old files stay on disk but the runtime ignores them, so recalibrate once under a named SDK user to restore your calibration. Swapping to another glove of the same side needs no recalibration.

  • The calibration.hand_profile and calibration.hand_model_paths.* parameters (including the hand_1 / hand_2 aliases) were removed. Setting these paths fails with PathNotFound. Values left on disk stay readable but are ignored at runtime.
  • The calibration result no longer includes device_sn, hand_profile, active_hand_profile, generated_hand_profiles, or calibrated_urdfs. Use handedness and calibrated_urdf instead.

Error Handling

ScenarioBehavior
Calibrating or setting a custom URDF under the default SDK userRaises an error telling you to create_user and switch_user first
timeout_s is not a finite positive numberRaises ValueError immediately
Calibration times outRaises TimeoutError, and this run's capture is not persisted
SDK user switches mid-calibrationRaises WujiException (message contains SDK user changed during calibration), this run's capture is discarded, and the old model is left untouched
URDF write failsRaises WujiException, rolls back this run's new files automatically, and leaves the previous model untouched
Calibration solver fails to convergeRaises WujiException, and the previous model is left untouched

Every failure path keeps the last successful calibration. A failed run never corrupts the existing model, so you can retry directly.

Resource Path Reference

Resource PathAccessDescription
calibration.hand_model_pathGET / SETUser-supplied external URDF override (highest priority, canonical accessor glove.hand_model_path())