C 接口参考
Wuji SDK 提供 C 接口(libwuji_sdk_c.so + wuji_sdk.h),与 Python 接口语义一致——设备发现、连接、按设备类型的 typed 订阅回调、全局坐标变换。安装方式与 Python 并列见 产品介绍。
完整 typed 回调与结构体定义参见 SDK tarball 解压后的 include/wuji_sdk.h,配套 CMake 工程示例与构建说明见 wuji-sdk 仓库 examples/c/ 目录。
初始化与约定
初始化
进程级初始化一次,结束时释放:
#include "wuji_sdk.h"
if (wuji_init(NULL) != WUJI_STATUS_OK) {
fprintf(stderr, "init failed: %s\n", wuji_last_error());
return 1;
}
// ... 业务逻辑
wuji_shutdown();返回值分类
WujiStatus— 多数操作(wuji_init/wuji_scan/wuji_connect/ 订阅打开等),WUJI_STATUS_OK表示成功,失败时通过wuji_last_error()取当前线程错误描述。int32_t— 元信息读取(wuji_dev_serial_number/wuji_dev_device_name)返回写入字节数。uint8_t— 状态查询wuji_dev_is_connected。void—wuji_shutdown与资源释放函数。
资源释放函数
wuji_discovered_free—wuji_scan后释放设备列表wuji_dev_release— 与wuji_connect配套,断开后释放设备句柄wuji_sub_close— 关闭订阅句柄
设备发现与连接
扫描局域网内的 Wuji 设备:
WujiDiscovered* devs = NULL;
size_t count = 0;
wuji_scan(&devs, &count);
for (size_t i = 0; i < count; i++) {
printf("[%zu] %s @ %s\n", i, devs[i].serial_number, devs[i].address);
}
wuji_discovered_free(devs, count);按 SN 或地址连接(target.kind 用 WUJI_CONNECT_TARGET_KIND_SN 或 _ADDR,本地别名作为 wuji_connect 第二参数传):
WujiConnectTarget target = {
.kind = WUJI_CONNECT_TARGET_KIND_SN,
.value = "WG1KA00260209001",
};
WujiConnectOptions opts = { .timeout_ms = 5000, .retry_count = 0 };
WujiDevice* dev = NULL;
if (wuji_connect(&target, "glove", &opts, &dev) != WUJI_STATUS_OK) {
fprintf(stderr, "connect failed: %s\n", wuji_last_error());
return 1;
}
char sn_buf[64];
wuji_dev_serial_number(dev, sn_buf, sizeof(sn_buf));
printf("connected: sn=%s\n", sn_buf);
// ... 业务
wuji_dev_disconnect(dev);
wuji_dev_release(dev);device_name 与 Python 语义一致:本地别名,不参与设备匹配,不决定返回类型,要求非空且不含 /、.。详见 设备连接。
订阅设备数据流
每种 typed 数据流都有专用包装 wuji_<device>_subscribe_<name>,回调签名与数据 schema 绑定。以 Wuji Glove 触觉为例:
void on_tactile(WujiFrameKind kind, const WujiTactileFrame* frame, void* user) {
if (kind != WUJI_FRAME_KIND_OK) return;
// frame->data, frame->header.seq, frame->header.timestamp_us
}
WujiSub* sub = NULL;
wuji_glove_subscribe_tactile(dev, on_tactile, /*user=*/NULL, &sub);
// ... 回调在后台触发
wuji_sub_close(sub);Wuji Glove 暴露的 typed 订阅入口:tactile、tactile_zones、tactile_binary、tactile_residual、tactile_point_cloud、emf_poses、hand_joint_angles、hand_skeleton、tip_poses、imu_palm 及五指 imu_<finger> / imu_data_<finger>。Wuji Hand 2 目前仅暴露 wuji_hand_2_subscribe_joint_state,更多 typed 入口规划中。
订阅全局资源
跨设备聚合的坐标变换通过 manager.tf() 在 Python 暴露,C 等价为 device-less 包装:
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++) {
const WujiFrameTransform* t = &frames->transforms[i];
printf("%s -> %s\n", t->parent_frame_id, t->child_frame_id);
}
}
WujiSub* sub = NULL;
wuji_subscribe_tf(on_tf, NULL, &sub);
// 同样:wuji_subscribe_tf_static(...)
wuji_sub_close(sub);使用约束
C SDK 用户必须遵守以下约束,否则会出现 use-after-free、死锁或读到失效错误描述:
- typed 回调的 frame 指针仅在回调期间有效,返回后堆字段即被释放。需要保留的数据必须在回调内拷贝出来,不得保存指针。
- 不要在订阅自身的回调里调
wuji_sub_close。close会 join worker 线程,在自身回调中调用必死锁。请在外部线程上 close。 wuji_last_error()返回值仅在同线程下一次wuji_*调用前有效。需要保留的错误描述应立即拷贝。END/ERROR是终止帧,回调收到这两类帧后流不再有新数据,但仍需调用wuji_sub_close释放订阅句柄。
Python 接口对照
| 功能 | C | Python |
|---|---|---|
| 初始化 | wuji_init(NULL) | SdkManager.instance() |
| 扫描 | wuji_scan(&devs, &count) | manager.scan() |
| 连接 | wuji_connect(&target, alias, &opts, &dev) | manager.connect(...) / manager.auto_connect(...) |
| 设备订阅 | wuji_glove_subscribe_tactile(dev, cb, user, &sub) | glove.tactile().subscribe_with_callback(...) |
| 全局订阅 | wuji_subscribe_tf(cb, user, &sub) | manager.tf().subscribe() |
| 断开 | wuji_dev_disconnect + wuji_dev_release | device.disconnect() |
CMake 工程示例
完整可运行 C 工程(含 CMakeLists.txt + 订阅回调示例)见 wuji-sdk 仓库的 examples/c/ 目录,构建步骤参见该目录 README。最小链接方式:
cc my_app.c -I "${SDK}/include" -L "${SDK}/lib" -lwuji_sdk_c -o my_app
LD_LIBRARY_PATH="${SDK}/lib" ./my_app其中 ${SDK} 是解压后的 tarball 根目录。CMake 工程通过 -DWUJI_SDK_INCLUDE_DIR 与 -DWUJI_SDK_LIB 指向同样路径。