Wuji Hand SDK 已归档,文档仍可访问,内容不再更新。 当前一代产品请参考 Wuji SDK

API 参考

字段列表

本节介绍 wujihandpy 中所有可用的数据字段。

Hand 层

该层包含整手设备唯一的通用字段,例如手性、固件版本等。

handedness

该字段表示灵巧手的手性(左/右手,0 表示右手,1 表示左手)。

  • 类型uint8
  • 读写属性:只读

firmware_version

该字段表示灵巧手固件的版本号。

  • 类型uint32
  • 读写属性:只读

firmware_date

该字段表示灵巧手固件的编译日期。

  • 类型uint32
  • 读写属性:只读

system_time

该字段表示灵巧手系统运行时间,单位为毫秒。

  • 类型uint32
  • 读写属性:只读

temperature

该字段表示灵巧手手腕附近的当前温度,单位为摄氏度。

  • 类型float32
  • 读写属性:只读

input_voltage

该字段表示灵巧手母线电压的实测值,单位为伏特。

  • 类型float32
  • 读写属性:只读

Joint 层

该层包含每关节持有一个独立副本的字段,例如使能、目标位置等。

joint_firmware_version

该字段记录关节驱动板固件的版本号。

  • 类型uint32
  • 读写属性:只读

joint_firmware_date

该字段记录关节驱动板固件的编译日期。

  • 类型uint32
  • 读写属性:只读

joint_control_mode

该字段用于设置关节的控制模式。

  • 类型uint16
  • 读写属性:只写

说明:模式会自动切换,一般情况下不应修改该字段。

joint_sin_level

该字段用于设置关节驱动的正弦波激励档位。

  • 类型uint16
  • 读写属性:只写

说明:该字段用于内部调试,一般情况下不应修改该字段。

joint_effort_limit

该字段用于设置关节允许的 effort 上限。

  • 类型float64
  • 单位:安培 (A)
  • 默认值1.5
  • 合法范围0.0 ~ 3.5(超过 3.5 会被限制为 3.5)
  • 读写属性:读写

什么是 Effort?

Effort 是电流空间的执行器作用量,经过滤波处理后输出。它不是实际测量的电流值,应将其理解为相对驱动强度,适用于负载监控和碰撞检测等场景。详见 joint_effort 字段。

过热保护:Effort 限制 会随温度动态调整

为保护设备,固件会根据关节温度自动降低实际生效的 effort 限制:

温度状态触发条件实际 Effort 限制
正常低于 85°C用户设定值 (默认 1.5A)
温度预警 (Tier 1)大于等于 85°C用户设定值 × 70%
温度告警 (Tier 2)大于等于 93°C用户设定值 × 40%

恢复阶段采用 4°C 回退阈值:

  • 从 Tier 2 回退到 Tier 1:温度需降到 89°C 以下
  • 从 Tier 1 恢复到正常:温度需降到 81°C 以下

当温度恢复到安全范围后,effort 限制会自动恢复。

警告:修改 Effort 限制 可能导致设备损坏

修改此参数会改变关节的最大输出能力。如因调整 effort limit 导致设备损坏,用户需自行承担责任。

  • 默认值 1.5 适用于大多数应用场景
  • 最大值 3.5,超过此值会被自动限制
  • 增大此值会增加关节负载能力,但也增加损坏风险
  • 影响相关关节运动寿命,加速各零件磨损且无法预估影响大小
  • 可能在某工况下直接造成相关关节出现不可逆的损坏
  • 可能加速关节热保护触发,减少持续运动时间
  • 可能出现除以上情况下的其他严重后果

joint_bus_voltage

该字段记录关节当前的母线电压实测值。

  • 类型float32
  • 读写属性:只读

joint_temperature

该字段记录关节驱动板当前的温度,单位为摄氏度。

  • 类型float32
  • 读写属性:只读

joint_reset_error

向该字段写入 1 可清除关节上报的错误状态。

  • 类型uint16
  • 读写属性:只写

该字段仅能清除错误码,不能解决灵巧手出现的问题。
切勿滥用此字段,尤其不能在循环中使用此字段,否则可能导致灵巧手损坏。

joint_error_code

该字段表示各关节的当前错误码,用于诊断运行状态。 值为 0 时表示关节无异常。

  • 类型uint32
  • 读写属性:只读

joint_effort

该字段提供关节的实时 effort 反馈。

  • 类型float32
  • 单位:安培 (A)
  • 读写属性:只读

Effort 的含义

Effort 是电流空间的执行器作用量 (actuation effort in current space),经过滤波处理后输出。

  • 不是实际测量的电流值
  • 不是电流环的直接输出
  • 应将其理解为相对驱动强度,适用于负载监控和碰撞检测

典型应用场景:

  • 碰撞检测:effort 突增表示关节受阻
  • 输出监控:可通过 effort / effort_limit 计算当前输出百分比

此字段仅在实时控制模式下通过 IController.get_joint_actual_effort() 访问。

joint_enabled

该字段控制关节的使能和失能。

  • 类型bool
  • 默认值False
  • 合法范围True / False
  • 读写属性:只写

joint_actual_position

该字段提供关节的实际位置。

  • 类型float64
  • 读写属性:只读

joint_target_position

写入该字段可下发关节的目标控制角度。若数值超出合法范围,会自动限幅至允许区间。

  • 类型float64
  • 合法范围joint_lower_limit ~ joint_upper_limit
  • 读写属性:只写

joint_upper_limit

该字段记录关节的运行位置上限。

  • 类型float64
  • 读写属性:只读

与 URDF 关节范围的区别

各关节在加工时存在微小的精度误差,使每个关节的实际物理极限略有不同。
灵巧手在出厂时会对各关节的实际极限进行标定,结果写入 joint_upper_limitjoint_lower_limit 字段。
因此,这两个字段记录的是经标定的实际物理极限值,通常略大于 URDF 文件中定义的范围。

URDF 文件中的关节范围是理论设计值,经过保守缩放以考虑加工误差,可用于仿真或神经网络训练。
joint_upper_limitjoint_lower_limit 表示实际测得的可运动范围。

joint_lower_limit

该字段记录关节的运行位置下限。

  • 类型float64
  • 读写属性:只读

API 列表

Hand 类

构造函数

  • __init__(serial_number=None, *, side=None, usb_pid=0x2000, usb_vid=0x0483, mask=None) -> None 初始化灵巧手连接,支持通过序列号、左右手、USB ID 或设备掩码指定设备

    • serial_number:USB 序列号字符串,精确指定一台设备
    • side"left""right",按手性选择设备,与 serial_number 互斥(详见 教程 - 筛选设备

手指访问

  • finger(index) -> Finger 获取指定索引的手指对象,索引范围 0-4

线程安全控制

  • disable_thread_safe_check() -> None 禁用线程安全检查,允许在多线程环境中并发访问 Hand 对象

注意:禁用线程安全检查后,用户需要自行使用互斥锁(threading.Lock)确保多线程操作的正确性。

系统信息

固件信息
  • read_firmware_version(timeout=0.5) -> uint32 同步读取固件版本号

  • read_firmware_version_async(timeout=0.5) -> Awaitable[uint32] 异步读取固件版本号

  • read_firmware_version_unchecked(timeout=0.5) -> None 非阻塞读取固件版本号

  • get_firmware_version() -> uint32 获取缓存的固件版本号

  • read_firmware_date(timeout=0.5) -> uint32 同步读取固件编译日期

  • read_firmware_date_async(timeout=0.5) -> Awaitable[uint32] 异步读取固件编译日期

  • read_firmware_date_unchecked(timeout=0.5) -> None 非阻塞读取固件编译日期

  • get_firmware_date() -> uint32 获取缓存的固件编译日期

  • read_handedness(timeout=0.5) -> uint8 同步读取左右手标识(0=右,1=左)

  • read_handedness_async(timeout=0.5) -> Awaitable[uint8] 异步读取左右手标识

  • read_handedness_unchecked(timeout=0.5) -> None 非阻塞读取左右手标识

  • get_handedness() -> uint8 获取缓存的左右手标识

系统状态
  • read_system_time(timeout=0.5) -> uint32 同步读取系统运行时间(毫秒)

  • read_system_time_async(timeout=0.5) -> Awaitable[uint32] 异步读取系统运行时间

  • read_system_time_unchecked(timeout=0.5) -> None 非阻塞读取系统运行时间

  • get_system_time() -> uint32 获取缓存的系统运行时间

  • read_temperature(timeout=0.5) -> float32 同步读取手掌温度(摄氏度)

  • read_temperature_async(timeout=0.5) -> Awaitable[float32] 异步读取手掌温度

  • read_temperature_unchecked(timeout=0.5) -> None 非阻塞读取手掌温度

  • get_temperature() -> float32 获取缓存的手掌温度

  • read_input_voltage(timeout=0.5) -> float32 同步读取输入电压(伏特)

  • read_input_voltage_async(timeout=0.5) -> Awaitable[float32] 异步读取输入电压

  • read_input_voltage_unchecked(timeout=0.5) -> None 非阻塞读取输入电压

  • get_input_voltage() -> float32 获取缓存的输入电压

关节数据(批量读取,返回 4 数组)

位置数据
  • read_joint_actual_position(timeout=0.5) -> NDArray[float64] 同步读取所有关节实际位置(弧度)

  • read_joint_actual_position_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取所有关节实际位置

  • read_joint_actual_position_unchecked(timeout=0.5) -> None 非阻塞读取所有关节实际位置

  • get_joint_actual_position() -> NDArray[float64] 获取缓存的所有关节实际位置

  • read_joint_upper_limit(timeout=0.5) -> NDArray[float64] 同步读取所有关节上限位置(弧度)

  • read_joint_upper_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取所有关节上限位置

  • read_joint_upper_limit_unchecked(timeout=0.5) -> None 非阻塞读取所有关节上限位置

  • get_joint_upper_limit() -> NDArray[float64] 获取缓存的所有关节上限位置

  • read_joint_lower_limit(timeout=0.5) -> NDArray[float64] 同步读取所有关节下限位置(弧度)

  • read_joint_lower_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取所有关节下限位置

  • read_joint_lower_limit_unchecked(timeout=0.5) -> None 非阻塞读取所有关节下限位置

  • get_joint_lower_limit() -> NDArray[float64] 获取缓存的所有关节下限位置

状态监控
  • read_joint_bus_voltage(timeout=0.5) -> NDArray[float32] 同步读取所有关节母线电压(伏特)

  • read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[NDArray[float32]] 异步读取所有关节母线电压

  • read_joint_bus_voltage_unchecked(timeout=0.5) -> None 非阻塞读取所有关节母线电压

  • get_joint_bus_voltage() -> NDArray[float32] 获取缓存的所有关节母线电压

  • read_joint_temperature(timeout=0.5) -> NDArray[float32] 同步读取所有关节温度(摄氏度)

  • read_joint_temperature_async(timeout=0.5) -> Awaitable[NDArray[float32]] 异步读取所有关节温度

  • read_joint_temperature_unchecked(timeout=0.5) -> None 非阻塞读取所有关节温度

  • get_joint_temperature() -> NDArray[float32] 获取缓存的所有关节温度

  • read_joint_error_code(timeout=0.5) -> NDArray[uint32] 同步读取所有关节错误码

  • read_joint_error_code_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取所有关节错误码

  • read_joint_error_code_unchecked(timeout=0.5) -> None 非阻塞读取所有关节错误码

  • get_joint_error_code() -> NDArray[uint32] 获取缓存的所有关节错误码

Effort 限制 数据
  • read_joint_effort_limit(timeout=0.5) -> NDArray[float64] 同步读取所有关节 effort limit(安培)
  • read_joint_effort_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取所有关节 effort limit
  • read_joint_effort_limit_unchecked(timeout=0.5) -> None 非阻塞读取所有关节 effort limit
  • get_joint_effort_limit() -> NDArray[float64] 获取缓存的所有关节 effort limit
固件信息
  • read_joint_firmware_version(timeout=0.5) -> NDArray[uint32] 同步读取所有关节固件版本

  • read_joint_firmware_version_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取所有关节固件版本

  • read_joint_firmware_version_unchecked(timeout=0.5) -> None 非阻塞读取所有关节固件版本

  • get_joint_firmware_version() -> NDArray[uint32] 获取缓存的所有关节固件版本

  • read_joint_firmware_date(timeout=0.5) -> NDArray[uint32] 同步读取所有关节固件编译日期

  • read_joint_firmware_date_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取所有关节固件编译日期

  • read_joint_firmware_date_unchecked(timeout=0.5) -> None 非阻塞读取所有关节固件编译日期

  • get_joint_firmware_date() -> NDArray[uint32] 获取缓存的所有关节固件编译日期

关节控制(支持单个值或 4 数组)

使能控制
  • write_joint_enabled(value, timeout=0.5) -> None 同步设置所有关节使能状态
  • write_joint_enabled(value_array, timeout=0.5) -> None 同步批量设置各关节使能状态
  • write_joint_enabled_async(value, timeout=0.5) -> Awaitable[None] 异步设置所有关节使能状态
  • write_joint_enabled_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置各关节使能状态
  • write_joint_enabled_unchecked(value, timeout=0.5) -> None 非阻塞设置所有关节使能状态
  • write_joint_enabled_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置各关节使能状态
位置控制
  • write_joint_target_position(value, timeout=0.5) -> None 同步设置所有关节目标位置(弧度)
  • write_joint_target_position(value_array, timeout=0.5) -> None 同步批量设置各关节目标位置
  • write_joint_target_position_async(value, timeout=0.5) -> Awaitable[None] 异步设置所有关节目标位置
  • write_joint_target_position_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置各关节目标位置
  • write_joint_target_position_unchecked(value, timeout=0.5) -> None 非阻塞设置所有关节目标位置
  • write_joint_target_position_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置各关节目标位置
模式控制
  • write_joint_control_mode(value, timeout=0.5) -> None 同步设置所有关节控制模式
  • write_joint_control_mode(value_array, timeout=0.5) -> None 同步批量设置各关节控制模式
  • write_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None] 异步设置所有关节控制模式
  • write_joint_control_mode_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置各关节控制模式
  • write_joint_control_mode_unchecked(value, timeout=0.5) -> None 非阻塞设置所有关节控制模式
  • write_joint_control_mode_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置各关节控制模式
Effort 限制 配置

警告:修改 effort limit 可能导致设备损坏,详见字段说明。

  • write_joint_effort_limit(value, timeout=0.5) -> None 同步设置所有关节 effort limit(安培)
  • write_joint_effort_limit(value_array, timeout=0.5) -> None 同步批量设置各关节 effort limit
  • write_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None] 异步设置所有关节 effort limit
  • write_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置各关节 effort limit
  • write_joint_effort_limit_unchecked(value, timeout=0.5) -> None 非阻塞设置所有关节 effort limit
  • write_joint_effort_limit_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置各关节 effort limit
其他参数配置
  • write_joint_sin_level(value, timeout=0.5) -> None 同步设置所有关节正弦波激励档位

  • write_joint_sin_level(value_array, timeout=0.5) -> None 同步批量设置各关节正弦波激励档位

  • write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None] 异步设置所有关节正弦波激励档位

  • write_joint_sin_level_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置各关节正弦波激励档位

  • write_joint_sin_level_unchecked(value, timeout=0.5) -> None 非阻塞设置所有关节正弦波激励档位

  • write_joint_sin_level_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置各关节正弦波激励档位

  • write_joint_reset_error(value, timeout=0.5) -> None 同步重置所有关节错误

  • write_joint_reset_error(value_array, timeout=0.5) -> None 同步批量重置各关节错误

  • write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None] 异步重置所有关节错误

  • write_joint_reset_error_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量重置各关节错误

  • write_joint_reset_error_unchecked(value, timeout=0.5) -> None 非阻塞重置所有关节错误

  • write_joint_reset_error_unchecked(value_array, timeout=0.5) -> None 非阻塞批量重置各关节错误

实时控制器

  • realtime_controller(enable_upstream, filter) -> IController 创建实时控制器,用于高频控制场景

Finger 类

关节访问

  • joint(index) -> Joint 获取指定索引的关节对象,索引范围 0-3

关节数据(返回 4 元素数组)

位置数据
  • read_joint_actual_position(timeout=0.5) -> NDArray[float64] 同步读取手指所有关节实际位置(弧度)

  • read_joint_actual_position_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取手指所有关节实际位置

  • read_joint_actual_position_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节实际位置

  • get_joint_actual_position() -> NDArray[float64] 获取缓存的手指所有关节实际位置

  • read_joint_upper_limit(timeout=0.5) -> NDArray[float64] 同步读取手指所有关节上限位置(弧度)

  • read_joint_upper_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取手指所有关节上限位置

  • read_joint_upper_limit_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节上限位置

  • get_joint_upper_limit() -> NDArray[float64] 获取缓存的手指所有关节上限位置

  • read_joint_lower_limit(timeout=0.5) -> NDArray[float64] 同步读取手指所有关节下限位置(弧度)

  • read_joint_lower_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取手指所有关节下限位置

  • read_joint_lower_limit_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节下限位置

  • get_joint_lower_limit() -> NDArray[float64] 获取缓存的手指所有关节下限位置

状态监控
  • read_joint_bus_voltage(timeout=0.5) -> NDArray[float32] 同步读取手指所有关节母线电压(伏特)

  • read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[NDArray[float32]] 异步读取手指所有关节母线电压

  • read_joint_bus_voltage_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节母线电压

  • get_joint_bus_voltage() -> NDArray[float32] 获取缓存的手指所有关节母线电压

  • read_joint_temperature(timeout=0.5) -> NDArray[float32] 同步读取手指所有关节温度(摄氏度)

  • read_joint_temperature_async(timeout=0.5) -> Awaitable[NDArray[float32]] 异步读取手指所有关节温度

  • read_joint_temperature_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节温度

  • get_joint_temperature() -> NDArray[float32] 获取缓存的手指所有关节温度

  • read_joint_error_code(timeout=0.5) -> NDArray[uint32] 同步读取手指所有关节错误码

  • read_joint_error_code_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取手指所有关节错误码

  • read_joint_error_code_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节错误码

  • get_joint_error_code() -> NDArray[uint32] 获取缓存的手指所有关节错误码

Effort 限制 数据
  • read_joint_effort_limit(timeout=0.5) -> NDArray[float64] 同步读取手指所有关节 effort limit(安培)
  • read_joint_effort_limit_async(timeout=0.5) -> Awaitable[NDArray[float64]] 异步读取手指所有关节 effort limit
  • read_joint_effort_limit_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节 effort limit
  • get_joint_effort_limit() -> NDArray[float64] 获取缓存的手指所有关节 effort limit
固件信息
  • read_joint_firmware_version(timeout=0.5) -> NDArray[uint32] 同步读取手指所有关节固件版本

  • read_joint_firmware_version_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取手指所有关节固件版本

  • read_joint_firmware_version_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节固件版本

  • get_joint_firmware_version() -> NDArray[uint32] 获取缓存的手指所有关节固件版本

  • read_joint_firmware_date(timeout=0.5) -> NDArray[uint32] 同步读取手指所有关节固件编译日期

  • read_joint_firmware_date_async(timeout=0.5) -> Awaitable[NDArray[uint32]] 异步读取手指所有关节固件编译日期

  • read_joint_firmware_date_unchecked(timeout=0.5) -> None 非阻塞读取手指所有关节固件编译日期

  • get_joint_firmware_date() -> NDArray[uint32] 获取缓存的手指所有关节固件编译日期

关节控制(支持单个值或 4 元素数组)

使能控制
  • write_joint_enabled(value, timeout=0.5) -> None 同步设置手指所有关节使能状态
  • write_joint_enabled(value_array, timeout=0.5) -> None 同步批量设置手指各关节使能状态
  • write_joint_enabled_async(value, timeout=0.5) -> Awaitable[None] 异步设置手指所有关节使能状态
  • write_joint_enabled_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置手指各关节使能状态
  • write_joint_enabled_unchecked(value, timeout=0.5) -> None 非阻塞设置手指所有关节使能状态
  • write_joint_enabled_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置手指各关节使能状态
位置控制
  • write_joint_target_position(value, timeout=0.5) -> None 同步设置手指所有关节目标位置(弧度)
  • write_joint_target_position(value_array, timeout=0.5) -> None 同步批量设置手指各关节目标位置
  • write_joint_target_position_async(value, timeout=0.5) -> Awaitable[None] 异步设置手指所有关节目标位置
  • write_joint_target_position_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置手指各关节目标位置
  • write_joint_target_position_unchecked(value, timeout=0.5) -> None 非阻塞设置手指所有关节目标位置
  • write_joint_target_position_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置手指各关节目标位置
模式控制
  • write_joint_control_mode(value, timeout=0.5) -> None 同步设置手指所有关节控制模式
  • write_joint_control_mode(value_array, timeout=0.5) -> None 同步批量设置手指各关节控制模式
  • write_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None] 异步设置手指所有关节控制模式
  • write_joint_control_mode_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置手指各关节控制模式
  • write_joint_control_mode_unchecked(value, timeout=0.5) -> None 非阻塞设置手指所有关节控制模式
  • write_joint_control_mode_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置手指各关节控制模式
Effort 限制 配置

警告:修改 effort limit 可能导致设备损坏,详见字段说明。

  • write_joint_effort_limit(value, timeout=0.5) -> None 同步设置手指所有关节 effort limit(安培)
  • write_joint_effort_limit(value_array, timeout=0.5) -> None 同步批量设置手指各关节 effort limit
  • write_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None] 异步设置手指所有关节 effort limit
  • write_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置手指各关节 effort limit
  • write_joint_effort_limit_unchecked(value, timeout=0.5) -> None 非阻塞设置手指所有关节 effort limit
  • write_joint_effort_limit_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置手指各关节 effort limit
其他参数配置
  • write_joint_sin_level(value, timeout=0.5) -> None 同步设置手指所有关节正弦波激励档位

  • write_joint_sin_level(value_array, timeout=0.5) -> None 同步批量设置手指各关节正弦波激励档位

  • write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None] 异步设置手指所有关节正弦波激励档位

  • write_joint_sin_level_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量设置手指各关节正弦波激励档位

  • write_joint_sin_level_unchecked(value, timeout=0.5) -> None 非阻塞设置手指所有关节正弦波激励档位

  • write_joint_sin_level_unchecked(value_array, timeout=0.5) -> None 非阻塞批量设置手指各关节正弦波激励档位

  • write_joint_reset_error(value, timeout=0.5) -> None 同步重置手指所有关节错误

  • write_joint_reset_error(value_array, timeout=0.5) -> None 同步批量重置手指各关节错误

  • write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None] 异步重置手指所有关节错误

  • write_joint_reset_error_async(value_array, timeout=0.5) -> Awaitable[None] 异步批量重置手指各关节错误

  • write_joint_reset_error_unchecked(value, timeout=0.5) -> None 非阻塞重置手指所有关节错误

  • write_joint_reset_error_unchecked(value_array, timeout=0.5) -> None 非阻塞批量重置手指各关节错误

Joint 类

关节数据(返回单个值)

位置数据
  • read_joint_actual_position(timeout=0.5) -> float64 同步读取关节实际位置(弧度)

  • read_joint_actual_position_async(timeout=0.5) -> Awaitable[float64] 异步读取关节实际位置

  • read_joint_actual_position_unchecked(timeout=0.5) -> None 非阻塞读取关节实际位置

  • get_joint_actual_position() -> float64 获取缓存的关节实际位置

  • read_joint_upper_limit(timeout=0.5) -> float64 同步读取关节上限位置(弧度)

  • read_joint_upper_limit_async(timeout=0.5) -> Awaitable[float64] 异步读取关节上限位置

  • read_joint_upper_limit_unchecked(timeout=0.5) -> None 非阻塞读取关节上限位置

  • get_joint_upper_limit() -> float64 获取缓存的关节上限位置

  • read_joint_lower_limit(timeout=0.5) -> float64 同步读取关节下限位置(弧度)

  • read_joint_lower_limit_async(timeout=0.5) -> Awaitable[float64] 异步读取关节下限位置

  • read_joint_lower_limit_unchecked(timeout=0.5) -> None 非阻塞读取关节下限位置

  • get_joint_lower_limit() -> float64 获取缓存的关节下限位置

状态监控
  • read_joint_bus_voltage(timeout=0.5) -> float32 同步读取关节母线电压(伏特)

  • read_joint_bus_voltage_async(timeout=0.5) -> Awaitable[float32] 异步读取关节母线电压

  • read_joint_bus_voltage_unchecked(timeout=0.5) -> None 非阻塞读取关节母线电压

  • get_joint_bus_voltage() -> float32 获取缓存的关节母线电压

  • read_joint_temperature(timeout=0.5) -> float32 同步读取关节温度(摄氏度)

  • read_joint_temperature_async(timeout=0.5) -> Awaitable[float32] 异步读取关节温度

  • read_joint_temperature_unchecked(timeout=0.5) -> None 非阻塞读取关节温度

  • get_joint_temperature() -> float32 获取缓存的关节温度

  • read_joint_error_code(timeout=0.5) -> uint32 同步读取关节错误码

  • read_joint_error_code_async(timeout=0.5) -> Awaitable[uint32] 异步读取关节错误码

  • read_joint_error_code_unchecked(timeout=0.5) -> None 非阻塞读取关节错误码

  • get_joint_error_code() -> uint32 获取缓存的关节错误码

Effort 限制 数据
  • read_joint_effort_limit(timeout=0.5) -> float64 同步读取关节 effort limit(安培)
  • read_joint_effort_limit_async(timeout=0.5) -> Awaitable[float64] 异步读取关节 effort limit
  • read_joint_effort_limit_unchecked(timeout=0.5) -> None 非阻塞读取关节 effort limit
  • get_joint_effort_limit() -> float64 获取缓存的关节 effort limit
固件信息
  • read_joint_firmware_version(timeout=0.5) -> uint32 同步读取关节固件版本

  • read_joint_firmware_version_async(timeout=0.5) -> Awaitable[uint32] 异步读取关节固件版本

  • read_joint_firmware_version_unchecked(timeout=0.5) -> None 非阻塞读取关节固件版本

  • get_joint_firmware_version() -> uint32 获取缓存的关节固件版本

  • read_joint_firmware_date(timeout=0.5) -> uint32 同步读取关节固件编译日期

  • read_joint_firmware_date_async(timeout=0.5) -> Awaitable[uint32] 异步读取关节固件编译日期

  • read_joint_firmware_date_unchecked(timeout=0.5) -> None 非阻塞读取关节固件编译日期

  • get_joint_firmware_date() -> uint32 获取缓存的关节固件编译日期

关节控制(单个值)

使能控制
  • write_joint_enabled(value, timeout=0.5) -> None 同步设置关节使能状态
  • write_joint_enabled_async(value, timeout=0.5) -> Awaitable[None] 异步设置关节使能状态
  • write_joint_enabled_unchecked(value, timeout=0.5) -> None 非阻塞设置关节使能状态
位置控制
  • write_joint_target_position(value, timeout=0.5) -> None 同步设置关节目标位置(弧度)
  • write_joint_target_position_async(value, timeout=0.5) -> Awaitable[None] 异步设置关节目标位置
  • write_joint_target_position_unchecked(value, timeout=0.5) -> None 非阻塞设置关节目标位置
模式控制
  • write_joint_control_mode(value, timeout=0.5) -> None 同步设置关节控制模式
  • write_joint_control_mode_async(value, timeout=0.5) -> Awaitable[None] 异步设置关节控制模式
  • write_joint_control_mode_unchecked(value, timeout=0.5) -> None 非阻塞设置关节控制模式
Effort 限制 配置

警告:修改 effort limit 可能导致设备损坏,详见字段说明。

  • write_joint_effort_limit(value, timeout=0.5) -> None 同步设置关节 effort limit(安培)
  • write_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None] 异步设置关节 effort limit
  • write_joint_effort_limit_unchecked(value, timeout=0.5) -> None 非阻塞设置关节 effort limit
其他参数配置
  • write_joint_sin_level(value, timeout=0.5) -> None 同步设置关节正弦波激励档位

  • write_joint_sin_level_async(value, timeout=0.5) -> Awaitable[None] 异步设置关节正弦波激励档位

  • write_joint_sin_level_unchecked(value, timeout=0.5) -> None 非阻塞设置关节正弦波激励档位

  • write_joint_reset_error(value, timeout=0.5) -> None 同步重置关节错误

  • write_joint_reset_error_async(value, timeout=0.5) -> Awaitable[None] 异步重置关节错误

  • write_joint_reset_error_unchecked(value, timeout=0.5) -> None 非阻塞重置关节错误

IController 接口

上下文管理

  • __enter__() -> IController 支持 with 语句的上下文管理器入口
  • __exit__(arg0, arg1, arg2) -> None 支持 with 语句的上下文管理器出口
  • close() -> None 手动关闭实时控制器

实时数据获取

  • get_joint_actual_position() -> NDArray[float64] 实时获取所有关节实际位置(高频读取,返回 4 数组)
  • get_joint_actual_effort() -> NDArray[float64] 实时获取所有关节 effort(高频读取,返回 4 数组,单位:安培)

Effort 实时反馈

get_joint_actual_effort() 返回电流空间的执行器作用量 (actuation effort in current space),经过滤波处理。

  • 仅在实时控制器模式下可用
  • 适用于碰撞检测和负载监控
  • 可通过 effort / effort_limit 计算输出百分比

实时数据设置

  • set_joint_target_position(value_array) -> None 实时设置所有关节目标位置(高频写入,参数为 4 数组)

数组形状说明

  • Hand 类批量操作:返回/接收形状为 {5, 4} 的数组(5个手指 × 4个关节)
  • Finger 类批量操作:返回/接收形状为 {4} 的数组(4个关节)
  • Joint 类操作:返回/接收标量值(单个关节)
  • IController 操作:处理形状为 {5, 4} 的数组,用于高频实时控制

操作模式说明

  • 同步操作 (read_*, write_*):阻塞操作,确保读/写成功
  • 异步操作 (*_async):返回 Awaitable 对象,支持 await 语法
  • 非阻塞操作 (*_unchecked):立即返回,不保证操作成功,适用于高频率场景
  • 缓存获取 (get_*):获取最近一次读取的数据,不阻塞

触觉感知手套

触觉感知手套(Tactile Sensing Glove)是 Wuji Hand 的选配触觉感知配件。SDK 通过 TactileGlove 类提供设备连接和数据读取接口。

核心类

TactileHandedness

手型枚举:

  • TactileHandedness.LEFT(0):左手
  • TactileHandedness.RIGHT(1):右手

TactileFrame

单帧触觉数据,包含以下属性:

属性类型说明
handTactileHandedness左手或右手
sequenceint(uint16)帧计数器,0–65535 循环
timestamp_msint(uint32)设备启动后的毫秒时间戳
pressurenumpy.ndarray(24, 32)float32压力矩阵

压力值语义:

  • 范围:[0.0, 1.0]——0.0 表示无接触,1.0 表示最大接触
  • NaN 标记无效单元(不属于任何手指或手掌区域),处理时用 numpy.isnan() 跳过

CRC 校验在 SDK 内部完成,调用方收到的帧均已通过校验。

TactileGlove

触觉感知手套设备的连接与数据读取。

构造函数:

TactileGlove(serial_number: str | None = None)

serial_number 为 USB 设备序列号。传 None 时自动发现总线上唯一的设备,若存在多台会抛出异常。

设备连接

自动发现

from wujihandpy import TactileGlove

glove = TactileGlove()
glove.connect()  # 返回 True 表示连接成功

指定设备

glove = TactileGlove(serial_number="ABC123")
glove.connect()

Context Manager

with TactileGlove() as glove:
    frame = glove.read_frame()

连接管理方法

  • connect() -> bool:连接设备,成功返回 True
  • disconnect() -> None:断开连接并停止流式读取
  • is_connected() -> bool:查询连接状态
  • get_handedness() -> TactileHandedness:返回设备手型

数据读取

阻塞读取

import numpy as np

frame = glove.read_frame(timeout_ms=100)
print(f"序号: {frame.sequence}")
print(f"压力矩阵形状: {frame.pressure.shape}")  # (24, 32)
print(f"最大压力: {np.nanmax(frame.pressure)}")
  • 内部执行帧同步和 CRC 校验
  • 超时抛出 RuntimeError
  • 连接断开抛出 RuntimeError

流式回调

import numpy as np

def on_frame(frame):
    print(f"Seq={frame.sequence}, Max={np.nanmax(frame.pressure)}")

glove.start_streaming(on_frame)
# ... 业务逻辑 ...
glove.stop_streaming()
  • 回调在内部读取线程中调用,不可阻塞
  • 通过 set_disconnect_callback(fn) 注册断开回调以响应 USB 拔出,之后流式读取自动停止
  • start_streamingread_frame 互斥,不可同时使用
  • 所有阻塞操作释放 Python GIL

配置与诊断

方法说明
set_sample_rate_hz(hz)设置采样率,范围 1–120 Hz
get_sample_rate_hz() -> int读取当前采样率
set_streaming(enable)开关设备数据帧流
get_streaming_enabled() -> bool读取流式输出开关状态
set_disconnect_callback(callback)注册 Callable[[], None],USB 设备脱离总线时调用一次
get_device_info() -> TactileDeviceInfo读取 serialhw_revisionfw_version
get_fw_build() -> TactileFwBuild读取固件构建信息(git_short_sha
get_diagnostics() -> TactileDiagnostics读取计数器:uptime_msframe_countcrc_err_countdropout_countusb_reset_count
reset_counters()清零诊断计数器
get_device_time() -> TactileDeviceTime读取设备单调时钟(device_monotonic_ns
sync_host_epoch(host_unix_ns) -> TactileSyncResult用主机 UTC 纳秒交换同一时刻的设备时钟
reset_device()软复位。设备会从总线断开并重新枚举,调用后需重新 connect()
enter_bootloader(magic)跳转至 bootloader 以执行 OTA,传入 TACTILE_BOOTLOADER_MAGIC

异常处理

异常触发场景
RuntimeErrorread_frame 超时、读取期间 USB 断开、未连接时调用 read_frame,或在已流式时重复调用 start_streaming
TactileError命令返回设备协议错误(长度错误、CRC 错误、未知命令、负载错误)

完整示例

覆盖 TactileGlove 的完整工作流:自动发现与设备信息查询、主机/设备时钟同步、采样率配置、阻塞读取与流式回调、数据保存以及诊断计数器读取。reset_device / enter_bootloader 会断开当前连接(设备复位或进入 bootloader),仅在末尾以注释形式给出。

import time
import numpy as np
from wujihandpy import (
    TactileGlove,
    TactileHandedness,
    TactileError,
    TACTILE_BOOTLOADER_MAGIC,
)


def on_disconnect():
    print("[event] USB disconnect detected")


# 1. Connect — context manager calls disconnect() on exit.
#    To target a specific device: TactileGlove(serial_number="ABC123").
with TactileGlove() as glove:
    if not glove.is_connected():
        print("Not connected")
        exit(1)

    glove.set_disconnect_callback(on_disconnect)

    # 2. Device info
    info = glove.get_device_info()
    hw = ".".join(str(x) for x in info.hw_revision[:3])
    fw = ".".join(str(x) for x in info.fw_version[:3])
    build = glove.get_fw_build()
    print(f"Serial: {info.serial}")
    print(f"Hardware: v{hw}, Firmware: v{fw} ({build.git_short_sha})")

    # 3. Handedness
    hand = glove.get_handedness()
    side = "left" if hand == TactileHandedness.LEFT else "right"
    print(f"Handedness: {side}")

    # 4. Clock sync and device time
    sync = glove.sync_host_epoch(time.time_ns())
    skew_ns = sync.device_ns_at_sync - sync.host_ns_echo
    dev_time = glove.get_device_time()
    print(f"Host/device clock skew: {skew_ns} ns")
    print(f"Device monotonic time: {dev_time.device_monotonic_ns} ns")

    # 5. Sample rate
    print(f"Current sample rate: {glove.get_sample_rate_hz()} Hz")
    glove.set_sample_rate_hz(60)
    print(f"New sample rate: {glove.get_sample_rate_hz()} Hz")

    # 6. Streaming toggle + single blocking read
    glove.set_streaming(True)
    print(f"Streaming enabled: {glove.get_streaming_enabled()}")
    try:
        frame = glove.read_frame(timeout_ms=200)
        side_str = "L" if frame.hand == TactileHandedness.LEFT else "R"
        print(
            f"Frame: seq={frame.sequence}, ts={frame.timestamp_ms} ms, "
            f"hand={side_str}, max={np.nanmax(frame.pressure):.3f}"
        )
    except RuntimeError as e:
        print(f"Read failed (timeout or disconnect): {e}")
    except TactileError as e:
        print(f"Device protocol error: {e}")

    # 7. Reset counters, then stream and collect
    glove.reset_counters()
    frames = []

    def collect(frame):
        frames.append(frame.pressure.copy())

    glove.start_streaming(collect)
    time.sleep(5)  # Collect for 5 seconds
    glove.stop_streaming()

    # 8. Diagnostics
    diag = glove.get_diagnostics()
    print(
        f"Collected {diag.frame_count} frames, CRC errors {diag.crc_err_count}, "
        f"dropouts {diag.dropout_count}, USB resets {diag.usb_reset_count}, "
        f"uptime {diag.uptime_ms} ms"
    )

    # 9. Save data
    if frames:
        data = np.stack(frames)  # shape: (N, 24, 32), dtype: float32
        np.save("tactile_data.npy", data)
        print(f"Saved {len(frames)} frames to tactile_data.npy")
    else:
        print("No frames collected")

# Destructive operations (commented by default; both invalidate the handle):
# glove.reset_device()
# glove.enter_bootloader(TACTILE_BOOTLOADER_MAGIC)

预期输出示例

[xxxx-xx-xx xx:xx:xx.xxx] [wuji] [info] SDK vx.x.x initialized
[xxxx-xx-xx xx:xx:xx.xxx] [wuji] [info] Log files can be found at ~/.wuji/log/<timestamp>_<pid>.log
Serial: WTxxxxxxxxxxxxxx
Hardware: vx.x.x, Firmware: vx.x.x (xxxxxxx)
Handedness: left
Host/device clock skew: -1779537846723254078 ns
Device monotonic time: 142623339080000 ns
Current sample rate: 60 Hz
New sample rate: 60 Hz
Streaming enabled: True
Frame: seq=33917, ts=142623244 ms, hand=L, max=0.820
Collected 326 frames, CRC errors 0, dropouts 0, USB resets 0, uptime 142628802 ms
Saved 326 frames to tactile_data.npy

与 Wuji Hand 联合使用

HandTactileGlove 使用独立的 USB 传输和线程,可在同一进程内并行使用,无需额外协调。帧回调在触觉手套的流式消费线程中触发,关节命令运行在调用它们的线程上。该模式适合用触觉反馈驱动运动控制的闭环场景,例如根据当前压力峰值动态缩放关节运动幅度。

参考示例:example/joint_with_tactile.py

运行效果:

  • 启动后终端打印 Squeeze the glove to dampen the motion. Ctrl-C to exit.
  • 关节使能后,F2–F5 四指以 100 Hz 实时循环做周期性弯曲–伸展,F1(拇指)保持不动
  • 触觉手套压力峰值越大,关节运动幅度越小:松开手套时幅度最大(约 0.4 rad),用力按压时幅度趋近于 0
  • 松手后幅度自动恢复
  • Ctrl-C 退出时自动停止流式、关闭关节使能并断开手套

协议参考

本节面向需要自行解析触觉数据帧的高级开发者。使用 SDK 的 TactileGlove 类时无需关注协议细节。

数据帧格式

触觉感知手套通过 USB CDC(虚拟串口)以最高 120 Hz 输出固定 3088 字节的数据帧。实际速率由 set_sample_rate_hz(1–120 Hz)控制:

偏移量长度字段类型说明
0–12帧头u8[2]固定 0xAA 0x55
2–32帧长度u16 LE固定 3088
41手型u80x00 = 左手,0x01 = 右手
5–73填充u8[3]将触觉数据对齐到 4 字节边界
8–30793072触觉数据f32[24][32] LE压力矩阵,行优先,[0.0, 1.0]NaN = 无效单元
3080–30812序号u16 LE帧计数器,0–65535 循环
3082–30854时间戳u32 LE设备启动后毫秒数
3086–30872CRC16u16 LECRC16-CCITT 校验

CRC16-CCITT

  • 多项式:0x1021,初始值:0xFFFF
  • 校验范围:字节 [2, 3086)(帧长度到时间戳,共 3084 字节)
  • 不含帧头和 CRC 字段本身

帧同步算法

  1. 逐字节扫描,匹配帧头 0xAA 0x55
  2. 读取字节 2–3,验证帧长度 == 3088
  3. 读取剩余字节,补齐完整帧
  4. 计算 CRC16 并比对
  5. CRC 失败:丢弃,从 header_position + 1 继续扫描
  6. CRC 成功:解析并交付帧

0xAA 0x55 可能出现在压力数据中(伪帧头),CRC 校验是区分真伪帧头的唯一可靠手段。

参考实现

下面的 Python 片段不依赖 SDK,直接从原始字节流(例如通过 pyserial 打开 CDC 串口)解析单帧,可作为自定义解析器的起点:

import struct
import numpy as np

FRAME_LEN = 3088
HEADER = b"\xAA\x55"


def crc16_ccitt(data: bytes, init: int = 0xFFFF) -> int:
    crc = init
    for b in data:
        crc ^= b << 8
        for _ in range(8):
            if crc & 0x8000:
                crc = ((crc << 1) ^ 0x1021) & 0xFFFF
            else:
                crc = (crc << 1) & 0xFFFF
    return crc


def parse_frame(buf: bytes) -> dict:
    """Validate a 3088-byte candidate and decode its fields."""
    if len(buf) != FRAME_LEN or buf[:2] != HEADER:
        raise ValueError("bad header or length")
    if struct.unpack_from("<H", buf, 2)[0] != FRAME_LEN:
        raise ValueError("length field mismatch")
    if crc16_ccitt(buf[2:3086]) != struct.unpack_from("<H", buf, 3086)[0]:
        raise ValueError("CRC mismatch")
    return {
        "hand": "L" if buf[4] == 0 else "R",
        "pressure": np.frombuffer(buf, dtype="<f4", count=24 * 32, offset=8).reshape(24, 32),
        "sequence": struct.unpack_from("<H", buf, 3080)[0],
        "timestamp_ms": struct.unpack_from("<I", buf, 3082)[0],
    }


def read_frame(port) -> dict:
    """Resync on a raw byte stream and return the next valid frame."""
    buf = bytearray()
    while True:
        chunk = port.read(FRAME_LEN)
        if not chunk:
            raise TimeoutError("stream exhausted")
        buf.extend(chunk)
        i = buf.find(HEADER)
        while 0 <= i and i + FRAME_LEN <= len(buf):
            try:
                frame = parse_frame(bytes(buf[i:i + FRAME_LEN]))
                del buf[: i + FRAME_LEN]
                return frame
            except ValueError:
                i = buf.find(HEADER, i + 1)
        # Keep tail so a header spanning chunk boundaries survives.
        if len(buf) > FRAME_LEN - 1:
            del buf[: len(buf) - (FRAME_LEN - 1)]

调用示例:

import serial  # python3 -m pip install pyserial

with serial.Serial("/dev/ttyACM0", timeout=1.0) as port:
    frame = read_frame(port)
    print(f"seq={frame['sequence']}, ts={frame['timestamp_ms']} ms, "
          f"hand={frame['hand']}, max={np.nanmax(frame['pressure']):.3f}")

logging 模块

wujihandpy.logging 子模块用于在运行时配置 SDK 日志行为。默认行为、环境变量与排查建议见 入门 - 获取日志

Level 枚举

成员数值含义
Level.Trace0最详细,含原始收发字节
Level.Debug1调试细节
Level.Info2关键事件(默认)
Level.Warn3警告
Level.Error4错误
Level.Critical5严重错误
Level.Off6关闭日志

函数

set_log_path

设置日志文件目录。必须在 logger 初始化之前调用(常见 Hand 流程下即创建第一个 Hand 之前,flush() 或其他日志接口也会触发 logger 初始化),否则抛出 RuntimeError

def set_log_path(value: str) -> None

set_log_level

设置日志级别。可在任意时刻调用,立即生效。

def set_log_level(value: Level) -> None

set_log_to_file

控制是否写入文件。仅在 logger 初始化时 WUJI_LOG_TO_FILEtrue 才会打开日志文件,初始化后调用此函数可临时关闭或恢复文件输出。若初始化时已被禁用,运行期调用 set_log_to_file(True) 不会重新启用文件输出。

def set_log_to_file(value: bool) -> None

set_log_to_console

控制是否将日志输出到终端(标准输出 / 标准错误)。可在任意时刻调用,立即生效。

def set_log_to_console(value: bool) -> None

flush

强制将缓冲区中的日志刷入文件和终端。进程退出时 SDK 已自动调用,正常使用无需手动触发。

def flush() -> None
订阅更新