手型标定
Wuji Glove 用 EMF 解算手部关节角度前,需要先做一次 IK 标定,生成与穿戴者手型匹配的 URDF 模型。标定产物按 SDK 用户与左右手保存,同一用户下任意同侧手套共享,更换手套无需重新标定。
何时需要标定
- 某个 SDK 用户首次使用某一侧手套
- 更换穿戴者,为新用户单独标定
左手套和右手套各自独立标定,互不影响。同一 SDK 用户下换用另一只同侧手套时,直接复用已有标定,无需重标。启用 SDK 用户隔离后,每位用户独立持有自己的标定 URDF,见 SDK 用户管理。
SDK 用户前提
标定产物归属具名 SDK 用户。默认用户既不保存也不加载标定产物,实时 IK 始终使用内置默认模型。标定前先创建并切换到具名用户:
from wuji_sdk import SdkManager
manager = SdkManager.instance()
user = manager.create_user("alice") # "alice" 是显示名
manager.switch_user(user["user_id"]) # 按 user_id 切换,不是显示名在默认用户下标定或设置自定义 URDF 会报错,错误信息提示先执行 create_user 与 switch_user。
标定流程
标定要求穿戴者按提示完成一组规定动作,SDK 采集 EMF 数据并解算出适配的 URDF。
标定开始时 SDK 自动把 EMF 降频因数临时强制为 1,采集结束恢复原值(含错误与取消路径)。预先调用 glove.emf_poses_rate_divider().set(N>1) 不会拉长标定时间。详见 输出率调节。
同步阻塞调用
适合脚本与命令行工具:
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() 在标定结束或超时前不返回,Ctrl+C 延迟到方法返回后才响应。
异步调用
asyncio 程序使用异步 API,支持标准 cancellation:
result = await glove.calibrate(timeout_s=900.0)参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
skip_constraints | bool | False | 跳过稳定性和约束检查(仅调试用) |
timeout_s | float | 900.0 | 整体超时(秒),必须为有限正数 |
on_feedback | Callable 或 None | None | 实时反馈回调 |
hand_profile 参数已废弃并被忽略。标定始终按统一基准为每一侧生成单个手型模型。传入 "wujihand" 或 "wujihand2" 会触发 DeprecationWarning,传入其他值会报错。请移除这个参数。
实时反馈
通过 on_feedback 回调获取标定过程中的状态:
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 字典包含当前姿势序号、状态、进度、采集帧数、姿势偏差诊断指标 (metrics) 与提示文本 (hints) 等字段。
回调内抛出的异常会被 SDK 记录到日志,不会中断标定。
自定义 URDF 覆盖
glove.hand_model_path().set(path) 指定用户提供的外部 URDF 文件,是 calibration.hand_model_path 资源的规范访问器,在 URDF 查找顺序中优先级最高:
# 设置自定义手部 URDF(实时 IK 最高优先级)
glove.hand_model_path().set("/path/to/custom_hand.urdf")
# 读回当前覆盖值
print(glove.hand_model_path().get())设置路径后,在线 IK 数据流(hand_joint_angles / tip_poses / hand_skeleton)按自定义 URDF 重载。传入空路径则回退到当前用户的标定模型。仅 SDK 托管目录之外的路径算作外部覆盖,指向托管目录内的路径不生效。
自定义 URDF 覆盖仅具名 SDK 用户可用。默认用户调用 set() 会被拒绝,需先切换到具名用户,与标定产物的用户隔离一致。
URDF 查找顺序
实时 IK、tf_static 加载手部 URDF 的优先级:
- 外部自定义路径:
calibration.hand_model_path指向 SDK 托管目录之外的文件 - 当前用户的标定模型:该 SDK 用户对应左右手的稳定文件
left_hand.urdf/right_hand.urdf - 内置默认 URDF:按左右手返回 SDK 自带模型
默认 SDK 用户跳过第 1、2 级,始终使用内置默认模型。要使用标定产物或自定义 URDF,先切换到具名用户。
SDK 不读取任何旧格式产物——旧 profile 路径、旧版本写入的托管 hand_model_path、按序列号命名的旧标定文件运行时都不再加载,也没有自动迁移。旧数据重新标定即可。离线管线接口另支持显式传入 urdf_path,传参时优先于上述所有来源。
返回字段
calibrate() / calibrate_blocking() 返回的标定汇总:
| 字段 | 类型 | 说明 |
|---|---|---|
handedness | str | 本次标定的手别("left" 或 "right") |
calibrated_urdf | str | 标定生成的稳定模型路径(按用户保存) |
poses_collected | int | 采集到的姿势数 |
frames_per_pose | dict[str, int] | 姿势名 → 该姿势采集帧数 |
sdk_user | dict | 标定当时的 SDK 用户信息(user_id / display_name / description / is_default) |
标定产物存储
标定生成的稳定文件按 SDK 用户与左右手命名,同一用户下所有同侧手套共享同一模型:
| SDK 用户 | 模型目录 |
|---|---|
| 具名用户 | ~/.wuji/sdk/users/<user_id>/models/left_hand.urdf、right_hand.urdf |
唯一性由 (user_id, 左右手) 二元组保证,不再绑定设备序列号。切换 SDK 用户时,已连接设备自动重新加载该用户名下对应的模型,无需重连。写入走原子替换,标定失败不会损坏已有产物。
标定 bundle 采用新格式:手型模型提为用户级,触觉数据仍按设备序列号保存。导入旧版本 bundle 时只保留触觉数据,旧的按序列号标定一律跳过(已失效,不做迁移)。本版本导出的 bundle 需用最新 SDK 导入。
从旧版本升级
升级到本版本后,之前生成的标定 URDF 不再被加载,实时 IK 与 tf_static 回落到内置默认模型。磁盘上的旧标定文件仍保留、但运行时不再读取,需在具名 SDK 用户下重新标定一次以恢复个人标定效果。同侧更换手套无需重新标定。
calibration.hand_profile与calibration.hand_model_paths.*(含hand_1/hand_2别名)参数已移除。对这些路径执行 SET 会报PathNotFound,磁盘上的旧值仍可读取但运行时忽略。- 标定结果不再含
device_sn、hand_profile、active_hand_profile、generated_hand_profiles、calibrated_urdfs字段。改用handedness与calibrated_urdf。
错误处理
| 场景 | 行为 |
|---|---|
| 在默认 SDK 用户下标定或设置自定义 URDF | 报错,信息提示先 create_user 与 switch_user |
timeout_s 非有限正数 | 立即抛 ValueError |
| 标定超时 | 抛 TimeoutError,本轮采集结果不持久化 |
| 标定中途切换 SDK 用户 | 抛 WujiException(错误信息含 SDK user changed during calibration),本轮采集结果丢弃,旧模型不动 |
| URDF 写入失败 | 抛 WujiException,自动回滚本轮新文件,旧模型保持原值 |
| 标定算法收敛失败 | 抛 WujiException,旧模型不动 |
所有失败路径都保留上一次成功的标定结果,失败的本轮不会污染已有模型,可直接重试。
资源路径速查
| 资源路径 | 访问 | 说明 |
|---|---|---|
calibration.hand_model_path | GET / SET | 用户自定义外部 URDF 覆盖(优先级最高,规范访问器 glove.hand_model_path()) |