HumansXドキュメントセンター English公式サイトへ ↗

Data Subscription Mechanism

Developer Guide

このページは現在英語版のみです。日本語でのご説明が必要な場合は HumansX までお問い合わせください。

Wuji SDK uses a subscription mechanism to receive device data. Each data stream corresponds to a sensor or computed result, accessed through semantic APIs.

Subscription Modes

Async Receive

sub = glove.tactile().subscribe()
frame = await sub.recv_async()  # Wait for next data frame

Sync Non-blocking Receive

sub = glove.tactile().subscribe()
frame = sub.recv()  # Returns None when no data available

Callback Receive

  • Python

  • C

def on_data(frame):
    print(f"Max: {max(frame.data):.1f}")

sub = glove.tactile().subscribe_with_callback(callback=on_data)
# Receives automatically in background, non-blocking
void on_data(WujiFrameKind kind, const WujiTactileFrame *frame, void *user) {
    if (kind != WUJI_FRAME_KIND_OK) return;
    float max = 0.0f;
    for (size_t i = 0; i < frame->data_len; i++) {
        if (frame->data[i] > max) max = frame->data[i];
    }
    printf("Max: %.1f\n", max);
}

WujiSub *sub = NULL;
wuji_glove_subscribe_tactile(glove, on_data, NULL, &sub);
// A background thread receives automatically, non-blocking

Every C callback carries a WujiFrameKind. WUJI_FRAME_KIND_OK means the frame is readable, _LAG means the consumer fell behind and carries a lag count, and _END and _ERROR are terminal. The frame pointer is valid only for the duration of the callback, so copy anything you need to keep.

Closing Subscriptions

Callback subscriptions must be manually closed:

  • Python

  • C

sub = glove.tactile().subscribe_with_callback(callback=on_data)
# ... after some time
sub.close()
// Close from outside the subscription's own callback — close joins the worker thread
wuji_sub_close(sub);

All subscriptions on a device are automatically closed when the device is disconnected. After a terminal frame the stream delivers no more data, but the handle still needs closing.

Adjusting the Stream Output Rate

Adjust the firmware push rate of a subscribed stream. The call returns the rate the device applies:

  • Python

  • C

sub = glove.emf_poses().subscribe()

actual = sub.set_rate(30)   # the device may quantize the request
print(f"requested 30 Hz, applied {actual} Hz")

sub.set_rate(0)             # pass 0 to restore the device default
WujiSub *sub = NULL;
wuji_glove_subscribe_emf_poses(glove, on_poses, NULL, &sub);

uint16_t actual = 0;
wuji_sub_set_rate(sub, 30, &actual);   // the device may quantize the request
printf("requested 30 Hz, applied %u Hz\n", actual);

wuji_sub_set_rate(sub, 0, &actual);    // pass 0 to restore the device default
  • Call it on an open subscription handle. The setting stays in effect for the life of the subscription.
  • Rates can only go down, not above the native rate. The device quantizes the request to a supported rate (an integer division of the native rate), and the applied rate is what counts. In C, pass NULL for out_actual_hz when the applied rate isn’t needed.
  • The rate belongs to the stream, not to the handle: all subscribers of the stream share the setting, and the last write wins.
  • The setting doesn’t persist: the rate resets to the device default full rate when the last subscription to the stream is closed or the device reconnects.
  • Only streams produced directly by the device support rate control. Derived streams follow their source rate automatically, and global resources such as tf_static are aggregated by the SDK. Adjusting either raises WujiException in Python and returns WUJI_STATUS_ERR_UNSUPPORTED (-8) in C.
  • Requires firmware with stream rate control support. Firmware without it raises WujiException in Python and returns WUJI_STATUS_ERR_UNSUPPORTED (-8) in C.

Rate-adjustable streams and the derived streams that follow them, per device:

DeviceRate-adjustable streamsDerived streams that follow
Wuji Gloveemf_poses, tactile, tactile_zones, imu_raw/palmhand_joint_angles, hand_skeleton, tip_poses, tactile_point_cloud, tactile_residual, tactile_binary, imu_data/palm
Wuji Hand 2joint_states, joint_diagnostics, imu, fingertip/*/data—

joint_states and joint_diagnostics on the Wuji Hand 2 share one device stream, so adjusting either changes both.

For complete examples, see examples/python/wuji_glove/6.set_stream_rate.py, examples/python/wuji_hand_2/4.set_stream_rate.py, examples/c/wuji_glove/1_set_stream_rate.c, and examples/c/wuji_hand_2/4_set_stream_rate.c.

Cross-device Merged Subscription

Global resources automatically aggregate data from all connected devices. Global resources in the current version:

ResourceTypeDescription
tf_staticFrameTransformsStatic coordinate transforms (e.g., wrist → emf_tx)
tfFrameTransformsDynamic coordinate transforms (e.g., waist → wrist)

Query the full list of global resources at runtime, then subscribe:

  • Python

  • C

manager = SdkManager.instance()

# List all global resources
for topic in manager.global_topics():
    print(f"{topic.path} (sub={topic.can_sub})")

# Subscribe to a global resource — data is merged from all connected devices
sub = manager.tf_static().subscribe()
transforms = await sub.recv_async()
void on_tf(WujiFrameKind kind, const WujiFrameTransforms *frames, void *user) {
    if (kind != WUJI_FRAME_KIND_OK) return;
    for (size_t i = 0; i < frames->transforms_len; i++) {
        printf("%s -> %s\n", frames->transforms[i].parent_frame_id,
               frames->transforms[i].child_frame_id);
    }
}

// List all global resources
WujiTopic *topics = NULL;
size_t n = 0;
wuji_list_global_topics(&topics, &n);
for (size_t i = 0; i < n; i++) {
    printf("%s\n", topics[i].path);
}
wuji_topic_array_free(topics, n);

// Subscribe to a global resource — data is merged from all connected devices
WujiSub *sub = NULL;
wuji_subscribe_tf_static(on_tf, NULL, &sub);

Differences between device resources and global resources:

DimensionDevice ResourceGlobal Resource
Python accessglove.tactile().subscribe()manager.tf_static().subscribe()
C accesswuji_glove_subscribe_tactile(dev, ...)wuji_subscribe_tf_static(...), no device handle needed
Data sourceSingle deviceMerged from all connected devices
Query APIdevice.topics() / wuji_dev_list_topics()manager.global_topics() / wuji_list_global_topics()

Available Data Streams

Index by Product

ProductData streams and subscription APIs
Wuji Hand 2.2 (Beta)Wuji Hand 2 SDK Reference
Wuji Hand 2.1 (Beta)Wuji Hand 2 SDK Reference
Wuji GloveWuji Glove data streams overview

For devices equipped with fingertip tactile sensors, see Wuji Hand 2 Fingertip Tactile for subscription and decoding. Read the connected device’s sensor metadata to confirm that the interfaces are available.

Interface List

OperationPythonC
Open a typed subscriptionresource.subscribe(), resource.subscribe_with_callback(callback=...)wuji_<device>_subscribe_<stream>()
Subscribe to any stream by pathdevice.subscribe(path)wuji_sub_open()
Wait for the next frameawait sub.recv_async()No equivalent — frames arrive through the callback
Poll without blockingsub.recv()No equivalent — frames arrive through the callback
Close a subscriptionsub.close()wuji_sub_close()
Adjust the output ratesub.set_rate(frequency_hz)wuji_sub_set_rate()
List device streamsdevice.topics()wuji_dev_list_topics()
List global streamsmanager.global_topics()wuji_list_global_topics()
Subscribe to global transformsmanager.tf().subscribe(), manager.tf_static().subscribe()wuji_subscribe_tf(), wuji_subscribe_tf_static()

Python offers three ways to take frames: async, sync non-blocking, and callback. C offers callbacks only, and the frame pointer is valid only for the duration of the callback. For memory ownership and callback constraints in C, see the C Interface Reference.