SDK Data Reference

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. The SDK ships a Python calibration API, runtime hand-profile switching, user-supplied URDF override, and per-user / per-device-serial-number isolation of calibration files.

When to Calibrate

  • First time you use a glove
  • A different wearer puts it on
  • The application needs to switch between the Wuji Hand and Wuji Hand 2 models

The device serial number already encodes which side (left or right) the glove belongs to, so the left and right gloves are calibrated independently and never share URDFs. When SDK user isolation is enabled, each user keeps a separate calibration URDF. See SDK User Management for user management.

Calibration Flow

Calibration asks the wearer to follow a sequence of poses while the SDK collects EMF data and solves a matching URDF.

At the start of calibration, the SDK temporarily forces the EMF divider to 1 and restores the prior value when capture ends — including error and cancellation paths. Calling glove.emf_poses_rate_divider().set(N>1) beforehand won't stretch calibration runtime. See Output Rate Tuning.

Synchronous Blocking Call

Best for scripts and CLI 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("device_sn:", result["device_sn"])
print("active hand profile:", result["active_hand_profile"])
print("generated profiles:", result["generated_hand_profiles"])
print("URDFs:", result["calibrated_urdfs"])
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. Ctrl+C is deferred until the call returns.

Asynchronous Call

Use the async API in asyncio programs. It honors standard asyncio 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
hand_profileWujiHandProfile, str, or NoneNoneProfile to generate (see below)
on_feedbackCallable or NoneNoneReal-time feedback callback

Real-time Feedback

Pass an on_feedback callback to receive per-step status:

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)

fb carries the current pose index, state, progress, frame count, pose-deviation diagnostic metrics (metrics), and human-readable hints (hints).

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

Hand Profile Selection

Wuji Glove ships two real-time IK models:

HandString ValueEnum
Wuji Hand"wujihand"WujiHandProfile.WUJI_HAND
Wuji Hand 2"wujihand2"WujiHandProfile.WUJI_HAND_2

Pin a Hand Profile at Calibration Time

from wuji_sdk import WujiHandProfile

result = glove.calibrate_blocking(
    hand_profile=WujiHandProfile.WUJI_HAND_2,
)
# result["generated_hand_profiles"] == ["wujihand2"]
# result["calibrated_urdfs"] == {"wujihand2": "/path/to/...urdf"}

When you omit hand_profile, the SDK uses the same captured data to generate URDFs for both profiles and writes them to:

  • calibration.hand_model_paths.wujihand
  • calibration.hand_model_paths.wujihand2

Real-time IK defaults to "wujihand" and can switch at runtime.

Switch Hand Profile at Runtime

Set calibration.hand_profile to swap the real-time IK model:

# Switch to Wuji Hand 2
glove.set("calibration.hand_profile", "wujihand2")

# Switch back to Wuji Hand
glove.set("calibration.hand_profile", "wujihand")

The switch takes effect on the next frame. When calibration.hand_profile is unset, the SDK defaults to "wujihand".

Custom URDF Override

calibration.hand_model_path lets you point real-time IK at a user-supplied URDF. It has the highest priority:

glove.set("calibration.hand_model_path", "/path/to/custom_hand.urdf")

Clear the value to fall back to the calibration-generated URDF for the current calibration.hand_profile.

URDF Lookup Order

Real-time IK, tf_static, and offline_pipeline resolve the hand URDF in this order:

  1. User-supplied path: calibration.hand_model_path pointing to a file outside the SDK-managed directory
  2. Calibrated URDF for the active profile: calibration.hand_model_paths.<profile> (wujihand or wujihand2)
  3. Legacy compatibility path: calibration.hand_model_paths.hand_1/hand_2 or calibration.hand_model_path written by older SDK versions
  4. Built-in default URDF: shipped with the SDK, selected by hand side

If the target profile's calibrated URDF is missing, the SDK silently falls back to the built-in default and logs a warning. Hand tracking keeps running but accuracy drops. In production, check the warning log to confirm IK is using the expected URDF.

Return Fields

Summary returned by calibrate() / calibrate_blocking():

FieldTypeDescription
device_snstrDevice serial number (already encodes the hand side)
hand_profilestr or NoneThe profile passed in, or None if omitted
active_hand_profilestr or NoneProfile real-time IK will use after this calibration
generated_hand_profileslist[str]All profiles actually generated this run (["wujihand"] or ["wujihand","wujihand2"])
calibrated_urdfsdict[str, str]Map of profile to URDF file path
calibrated_urdfstr or NoneThe single path when only one profile is generated, None otherwise
poses_collectedintNumber of poses captured
frames_per_posedict[str, int]Pose name to frame count
sdk_userdictSDK user info captured at calibration start (user_id / display_name / description / is_default)

Calibration Artifacts

Generated URDF files are named <sn>_hand_<profile>_<calibration_id>.urdf. The root directory is scoped by SDK user:

SDK UserURDF DirectoryParameter File
Default user~/.wuji/sdk/models/~/.wuji/sdk/params/<sn>.toml
Named user~/.wuji/sdk/users/<user_id>/models/~/.wuji/sdk/users/<user_id>/params/<sn>.toml

Uniqueness is guaranteed by the (user_id, device_sn, hand_profile) tuple. Switching the SDK user reloads the matching URDF for connected devices automatically, no reconnect required.

Error Handling

ScenarioBehavior
hand_profile is a string or enum other than "wujihand" / "wujihand2"Raises ValueError / WujiException immediately. Calibration never starts
timeout_s is not a finite positive numberRaises ValueError immediately
Calibration times outRaises TimeoutError. The current attempt is not persisted
SDK user switches mid-calibrationRaises WujiException (message contains SDK user changed during calibration). The current attempt is dropped and the previous URDF is preserved
URDF write failsRaises WujiException. The current run's new files are rolled back automatically, and the previous URDF and hand_model_paths parameters are left untouched
Calibration solver fails to convergeRaises WujiException. The message names the failing hand_profile, and the previous URDF is preserved

Every failure path preserves the last successful calibration, so a failed attempt cannot corrupt existing URDFs. Just retry.

Resource Reference

Resource PathAccessDescription
calibration.hand_profileGET / SETCurrent real-time IK hand profile ("wujihand" / "wujihand2")
calibration.hand_model_paths.wujihandGET / SETWuji Hand calibrated URDF path, written by the SDK
calibration.hand_model_paths.wujihand2GET / SETWuji Hand 2 calibrated URDF path, written by the SDK
calibration.hand_model_pathGET / SETUser-supplied URDF override (highest priority)