HumansXDocumentation Center 日本語Website ↗
Knowledge Base › Software › Wuji SDK

Best Practices

Operations and Appendix

Error Handling

SDK operation failures raise WujiException or one of its subclasses, such as NodeOfflineError, so catch WujiException at the SDK boundary. Only argument-type or overflow errors that the Python interpreter raises before the SDK call keep their native exception types:

from wuji_sdk import SdkManager, WujiException

try:
    manager = SdkManager.instance()
    glove = manager.auto_connect(device_name="glove")
    sub = glove.tactile().subscribe()
    frame = sub.recv()
except WujiException as e:
    error_msg = str(e)
    if "DeviceNotFound" in error_msg:
        print("Device not found, check connection")
    elif "Disconnected" in error_msg:
        print("Device disconnected")
    elif "Connection timeout" in error_msg:
        print("Connection timed out, check network/IP settings")
    elif "Operation timeout" in error_msg:
        print("Operation timed out, device not responding, retrying...")
    else:
        print(f"Error: {error_msg}")

For the full list of error types, see Appendix — Python Exception Types. For C return codes and device fault codes, see Appendix — Error Code Index.

Reconnection Strategy

After disconnection, reconnect and resubscribe:

import time
from wuji_sdk import SdkManager, WujiException

manager = SdkManager.instance()

def connect_with_retry(sn, max_retries=5):
    for attempt in range(max_retries):
        try:
            return manager.connect(sn=sn, device_name="glove")
        except WujiException:
            wait = 2 ** attempt  # Exponential backoff
            print(f"Connection failed, retrying in {wait}s...")
            time.sleep(wait)
    raise RuntimeError("Max retries exceeded")

Multi-device Coordination

Collect data from multiple devices simultaneously:

import asyncio
from wuji_sdk import SdkManager

async def collect_from_device(glove, name):
    sub = glove.tactile().subscribe()
    while True:
        frame = await sub.recv_async()
        print(f"[{name}] max={max(frame.data):.1f}")

async def main():
    manager = SdkManager.instance()
    devices = manager.scan()

    gloves = []
    for i, dev in enumerate(devices):
        glove = manager.connect(sn=dev.sn, device_name=f"glove_{i}")
        gloves.append((glove, f"glove_{i}"))

    tasks = [collect_from_device(g, n) for g, n in gloves]
    await asyncio.gather(*tasks)

asyncio.run(main())

Cross-device Merged Data

In multi-device setups, use global resources to get aggregated data from all devices without manual merging:

manager = SdkManager.instance()
left = manager.connect(sn="WG1JA00XXXXXXXXX", device_name="left")
right = manager.connect(sn="WG1KA00XXXXXXXXX", device_name="right")

# Subscribe to global coordinate transforms — automatically merges left and right hand data
sub = manager.tf_static().subscribe()
transforms = await sub.recv_async()
for tf in transforms.transforms:
    print(f"{tf.parent_frame_id} → {tf.child_frame_id}")

Log Control

Adjust SDK log level for debugging:

import wuji_sdk

wuji_sdk.set_log_level("debug")   # Show debug information
wuji_sdk.set_log_level("trace")   # Show all logs
wuji_sdk.set_log_level("info")    # Default level
wuji_sdk.set_log_level("warn")    # Warnings only
wuji_sdk.set_log_level("error")   # Errors only
wuji_sdk.set_log_level("off")     # Disable terminal and dated SDK logs

The SDK writes its log to the terminal and to ~/.wuji/logs/sdk_<date>.log. The log level does not affect error_codes.log. For its location, fault states, and code lookup, see Fault Logs and Error Codes.

Device Logging

Device logging access varies by product. See the corresponding device documentation:

Data Collection and Privacy

Wuji SDK uploads anonymous usage data by default to help improve the product. It doesn’t collect personal or sensitive information. For more details, see the Privacy Policy. On first use, the terminal shows the following privacy notice:

Privacy Notice
Wuji SDK collects anonymous usage data to improve the product.
No personal or sensitive information is collected.
Privacy Policy: https://wuji.tech/en/terms/privacy
If your environment has information security requirements, you can disable telemetry upload for the current process by setting:
WUJI_TELEMETRY_DISABLE=1 <your_command>

To stop data uploads, set WUJI_TELEMETRY_DISABLE to 1 or true before starting the application.

# Executable
WUJI_TELEMETRY_DISABLE=1 ./<your_program>

# Python script
WUJI_TELEMETRY_DISABLE=1 python <your_script.py>

Recording Best Practices

Choose the Right Compression

  • Use "lz4" for real-time capture (low latency). Use "zstd" for long-term storage (high compression ratio)
  • For high-frequency channels (>500 Hz), prefer "lz4" to avoid compression becoming a bottleneck

Episode Management

Split long capture sessions into multiple episodes for easier playback and analysis:

import asyncio
from wuji_sdk import TopicRecorder

recorder = TopicRecorder(compression="lz4")
recorder.record(glove.tactile().subscribe())

for i in range(5):
    handle = await recorder.start(f"/en/docs/wuji-sdk/data/episode_{i:03d}.mcap")
    await asyncio.sleep(60)  # 60 seconds per episode
    await handle.stop()

Monitor Data Quality

For long recording sessions, enable quality monitoring and handle alerts promptly:

import asyncio

handle = await recorder.start("/en/docs/wuji-sdk/data/session.mcap")

async def watch_alerts():
    async for alert in handle.subscribe_alerts():
        print(f"Warning: {alert.message}")

asyncio.create_task(watch_alerts())

Performance Optimization

Reduce Unnecessary Subscriptions

Only subscribe to data streams you actually need to avoid wasting bandwidth and CPU:

# ✓ Subscribe only to needed data
sub_tactile = glove.tactile().subscribe()

# ✗ Don't subscribe to unused data
# sub_imu = glove.imu_palm().subscribe()

Close Subscriptions Promptly

Close subscriptions when they’re no longer needed:

sub = glove.tactile().subscribe_with_callback(callback=on_data)
# ... done collecting data
sub.close()  # Release resources