API 参考
1. 字段列表
本节介绍 wujihandpy 中所有可用的数据字段。
1.1 Hand 层
该层包含整手设备唯一的通用字段,例如手性、固件版本等。
handedness
该字段表示灵巧手的手性(左/右手,0 表示右手,1 表示左手)。
- 类型:
uint8 - 读写属性:只读
firmware_version
该字段表示灵巧手固件的版本号。
- 类型:
uint32 - 读写属性:只读
firmware_date
该字段表示灵巧手固件的编译日期。
- 类型:
uint32 - 读写属性:只读
system_time
该字段表示灵巧手系统运行时间,单位为毫秒。
- 类型:
uint32 - 读写属性:只读
temperature
该字段表示灵巧手手腕附近的当前温度,单位为摄氏度。
- 类型:
float32 - 读写属性:只读
input_voltage
该字段表示灵巧手母线电压的实测值,单位为伏特。
- 类型:
float32 - 读写属性:只读
1.2 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_limit 和 joint_lower_limit 字段。
因此,这两个字段记录的是经标定的实际物理极限值,通常略大于 URDF 文件中定义的范围。
URDF 文件中的关节范围是理论设计值,经过保守缩放以考虑加工误差,可用于仿真或神经网络训练。
而 joint_upper_limit 与 joint_lower_limit 表示实际测得的可运动范围。
joint_lower_limit
该字段记录关节的运行位置下限。
- 类型:
float64 - 读写属性:只读
2. API 列表
2.1 Hand 类
2.1.1 构造函数
-
__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互斥(详见 教程 §1.3)
2.1.2 手指访问
finger(index) -> Finger获取指定索引的手指对象,索引范围 0-4
2.1.3 线程安全控制
disable_thread_safe_check() -> None禁用线程安全检查,允许在多线程环境中并发访问 Hand 对象
注意:禁用线程安全检查后,用户需要自行使用互斥锁(threading.Lock)确保多线程操作的正确性。
2.1.4 系统信息
固件信息
-
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获取缓存的输入电压
2.1.5 关节数据(批量读取,返回 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 limitread_joint_effort_limit_unchecked(timeout=0.5) -> None非阻塞读取所有关节 effort limitget_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]获取缓存的所有关节固件编译日期
2.1.6 关节控制(支持单个值或 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 limitwrite_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None]异步设置所有关节 effort limitwrite_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None]异步批量设置各关节 effort limitwrite_joint_effort_limit_unchecked(value, timeout=0.5) -> None非阻塞设置所有关节 effort limitwrite_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非阻塞批量重置各关节错误
2.1.7 实时控制器
realtime_controller(enable_upstream, filter) -> IController创建实时控制器,用于高频控制场景
2.2 Finger 类
2.2.1 关节访问
joint(index) -> Joint获取指定索引的关节对象,索引范围 0-3
2.2.2 关节数据(返回 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 limitread_joint_effort_limit_unchecked(timeout=0.5) -> None非阻塞读取手指所有关节 effort limitget_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]获取缓存的手指所有关节固件编译日期
2.2.3 关节控制(支持单个值或 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 limitwrite_joint_effort_limit_async(value, timeout=0.5) -> Awaitable[None]异步设置手指所有关节 effort limitwrite_joint_effort_limit_async(value_array, timeout=0.5) -> Awaitable[None]异步批量设置手指各关节 effort limitwrite_joint_effort_limit_unchecked(value, timeout=0.5) -> None非阻塞设置手指所有关节 effort limitwrite_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非阻塞批量重置手指各关节错误
2.3 Joint 类
2.3.1 关节数据(返回单个值)
位置数据
-
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 limitread_joint_effort_limit_unchecked(timeout=0.5) -> None非阻塞读取关节 effort limitget_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获取缓存的关节固件编译日期
2.3.2 关节控制(单个值)
使能控制
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 limitwrite_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非阻塞重置关节错误
2.4 IController 接口
2.4.1 上下文管理
__enter__() -> IController支持 with 语句的上下文管理器入口__exit__(arg0, arg1, arg2) -> None支持 with 语句的上下文管理器出口close() -> None手动关闭实时控制器
2.4.2 实时数据获取
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计算输出百分比
2.4.3 实时数据设置
set_joint_target_position(value_array) -> None实时设置所有关节目标位置(高频写入,参数为 4 数组)
2.5 数组形状说明
- Hand 类批量操作:返回/接收形状为
{5, 4}的数组(5个手指 × 4个关节) - Finger 类批量操作:返回/接收形状为
{4}的数组(4个关节) - Joint 类操作:返回/接收标量值(单个关节)
- IController 操作:处理形状为
{5, 4}的数组,用于高频实时控制
2.6 操作模式说明
- 同步操作 (
read_*,write_*):阻塞操作,确保读/写成功 - 异步操作 (
*_async):返回 Awaitable 对象,支持 await 语法 - 非阻塞操作 (
*_unchecked):立即返回,不保证操作成功,适用于高频率场景 - 缓存获取 (
get_*):获取最近一次读取的数据,不阻塞
3. 触觉感知手套
触觉感知手套(Tactile Sensing Glove)是 Wuji Hand 的选配触觉感知配件。SDK 通过 TactileGlove 类提供设备连接和数据读取接口。
3.1 核心类
TactileHandedness
手型枚举:
TactileHandedness.LEFT(0):左手TactileHandedness.RIGHT(1):右手
TactileFrame
单帧触觉数据,包含以下属性:
| 属性 | 类型 | 说明 |
|---|---|---|
hand | TactileHandedness | 左手或右手 |
sequence | int(uint16) | 帧计数器,0–65535 循环 |
timestamp_ms | int(uint32) | 设备启动后的毫秒时间戳 |
pressure | numpy.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 时自动发现总线上唯一的设备,若存在多台会抛出异常。
3.2 设备连接
自动发现
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:连接设备,成功返回Truedisconnect() -> None:断开连接并停止流式读取is_connected() -> bool:查询连接状态get_handedness() -> TactileHandedness:返回设备手型
3.3 数据读取
阻塞读取
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_streaming与read_frame互斥,不可同时使用- 所有阻塞操作释放 Python GIL
3.4 配置与诊断
| 方法 | 说明 |
|---|---|
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 | 读取 serial、hw_revision、fw_version |
get_fw_build() -> TactileFwBuild | 读取固件构建信息(git_short_sha) |
get_diagnostics() -> TactileDiagnostics | 读取计数器:uptime_ms、frame_count、crc_err_count、dropout_count、usb_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 |
3.5 异常处理
| 异常 | 触发场景 |
|---|---|
RuntimeError | read_frame 超时、读取期间 USB 断开、未连接时调用 read_frame,或在已流式时重复调用 start_streaming |
TactileError | 命令返回设备协议错误(长度错误、CRC 错误、未知命令、负载错误) |
3.6 完整示例
覆盖 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.npy3.7 与 Wuji Hand 联合使用
Hand 与 TactileGlove 使用独立的 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 退出时自动停止流式、关闭关节使能并断开手套
3.8 协议参考
本节面向需要自行解析触觉数据帧的高级开发者。使用 SDK 的 TactileGlove 类时无需关注协议细节。
数据帧格式
触觉感知手套通过 USB CDC(虚拟串口)以最高 120 Hz 输出固定 3088 字节的数据帧。实际速率由 set_sample_rate_hz(1–120 Hz)控制:
| 偏移量 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0–1 | 2 | 帧头 | u8[2] | 固定 0xAA 0x55 |
| 2–3 | 2 | 帧长度 | u16 LE | 固定 3088 |
| 4 | 1 | 手型 | u8 | 0x00 = 左手,0x01 = 右手 |
| 5–7 | 3 | 填充 | u8[3] | 将触觉数据对齐到 4 字节边界 |
| 8–3079 | 3072 | 触觉数据 | f32[24][32] LE | 压力矩阵,行优先,[0.0, 1.0]。NaN = 无效单元 |
| 3080–3081 | 2 | 序号 | u16 LE | 帧计数器,0–65535 循环 |
| 3082–3085 | 4 | 时间戳 | u32 LE | 设备启动后毫秒数 |
| 3086–3087 | 2 | CRC16 | u16 LE | CRC16-CCITT 校验 |
CRC16-CCITT
- 多项式:
0x1021,初始值:0xFFFF - 校验范围:字节
[2, 3086)(帧长度到时间戳,共 3084 字节) - 不含帧头和 CRC 字段本身
帧同步算法
- 逐字节扫描,匹配帧头
0xAA 0x55 - 读取字节 2–3,验证帧长度 == 3088
- 读取剩余字节,凑满完整帧
- 计算 CRC16 并比对
- CRC 失败:丢弃,从 header_position + 1 继续扫描
- 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}")4. logging 模块
wujihandpy.logging 子模块用于在运行时配置 SDK 日志行为。默认行为、环境变量与排查建议见 入门 - 获取日志。
4.1 Level 枚举
| 成员 | 数值 | 含义 |
|---|---|---|
Level.Trace | 0 | 最详细,含原始收发字节 |
Level.Debug | 1 | 调试细节 |
Level.Info | 2 | 关键事件(默认) |
Level.Warn | 3 | 警告 |
Level.Error | 4 | 错误 |
Level.Critical | 5 | 严重错误 |
Level.Off | 6 | 关闭日志 |
4.2 函数
set_log_path
设置日志文件目录。必须在 logger 初始化之前调用(常见 Hand 流程下即创建第一个 Hand 之前,flush() 或其他日志接口也会触发 logger 初始化),否则抛出 RuntimeError。
def set_log_path(value: str) -> Noneset_log_level
设置日志级别。可在任意时刻调用,立即生效。
def set_log_level(value: Level) -> Noneset_log_to_file
控制是否写入文件。仅在 logger 初始化时 WUJI_LOG_TO_FILE 为 true 才会打开日志文件,初始化后调用此函数可临时关闭或恢复文件输出。若初始化时已被禁用,运行期调用 set_log_to_file(True) 不会重新启用文件输出。
def set_log_to_file(value: bool) -> Noneset_log_to_console
控制是否将日志输出到终端(标准输出 / 标准错误)。可在任意时刻调用,立即生效。
def set_log_to_console(value: bool) -> Noneflush
强制将缓冲区中的日志刷入文件和终端。进程退出时 SDK 已自动调用,正常使用无需手动触发。
def flush() -> None