设备连接与筛选
本节介绍如何在 wujihandpy 中连接灵巧手设备,以及在存在多台设备时进行筛选的方法。连接与筛选接口概述
连接设备
调用wujihandpy.Hand() 可创建一个 Hand 对象并连接灵巧手:
筛选设备
当系统中连接了多台灵巧手时,使用无参构造函数将导致错误。例如:按序列号指定
通过serial_number 参数明确指定设备:
按左右手指定
当系统中恰好连接一只左手和一只右手时,可直接用side 参数选择,无需记忆序列号:
side 与 serial_number 互斥两者只能选一个传入。同时指定会抛 ValueError。side 仅接受字符串 "left" 或 "right",其他取值会抛 ValueError。匹配失败的常见情形:- 未接入对应侧的手 → 抛
ConnectionError,提示No <side> hand found - 同侧接入了两只 → 抛
ConnectionError,提示改用serial_number
查询序列号
灵巧手有两种序列号:- USB 序列号:USB 硬件描述符中的标识符,用于连接时区分多台设备。构造函数的
serial_number参数使用的是此序列号。 - 产品序列号:固件中存储的产品追踪编号,用于售后和设备管理。
查询 USB 序列号
在命令行中执行以下命令,查看系统中连接的所有灵巧手:serial_number 参数。
查询产品序列号
连接设备后,调用get_product_sn() 获取产品序列号:
读取数据
本节介绍如何以同步模式读取数据。读取接口概述
调用read_* 系列函数会向灵巧手发送同步读请求,并阻塞当前线程直到设备返回结果。函数返回则表示读取已成功完成。
单值读取
当读取层级与对象层级一致时,函数返回单个标量值。hand 读取灵巧手的上电运行时间(Hand 层):
joint 读取食指近端关节的当前位置(Joint 层):
批量读取
当读取层级低于对象层级时,函数返回包含多个数据的矩阵。hand 请求整手所有关节(共 20 个)的当前位置数据(Joint 层):
np.NDArray[np.float64],每行对应一个手指、每列对应一个关节:
finger 请求单个手指的所有关节位置:
更多读取示例
写入数据
本节介绍如何以同步模式写入数据。写入接口概述
调用write_* 系列函数会向灵巧手发送同步写请求,并阻塞当前线程直到设备返回结果。函数返回则表示写入已成功完成。
单值写入
当写入层级与对象层级一致时,函数使用单个标量值。批量写入
当写入层级低于对象层级时,函数既可使用单个标量值,也可使用包含多个数据的矩阵。hand 使能整手所有关节(Joint 层):
非法数值写入
通常,非法数值写入会被自动处理,例如:- 超出合法范围的角度会被自动限幅至上下界。
- 将关节的 effort limit 设为非法值(过大或过小)
更多写入示例
实时控制
本节介绍在 wujihandpy 中以实时模式控制灵巧手的方法。实时控制接口概述
Hand.realtime_controller() 可使灵巧手进入实时控制模式,并返回一个实时控制器对象 IController。
进入实时控制模式后,电机侧会启动一个 16 kHz 实时滤波器,将控制量滤波为平滑的 16 kHz 信号。
滤波器
实时控制器需要一个滤波器对象 (filter.IFilter) 来平滑地调整目标值。
滤波器的初值会自动设置为灵巧手的实际位置,避免初始过渡问题。
filter.LowPass(cutoff_freq: float):一阶低通滤波器,截止频率可调。当设置截止频率 >= 1 kHz 时,滤波器被关闭。
上下文管理器
IController 可作为上下文管理器使用:
with 块结束时,会自动调用 controller.close(),确保灵巧手正确退出实时控制模式。
自动析构
若未使用上下文管理器,IController 在析构时也会自动调用 close(),由于 Python 的垃圾回收存在延迟,灵巧手可能不会立即退出实时模式。若不需精确控制退出时机,可直接依赖析构行为。
实时写入
set_joint_target_position() 函数会非阻塞地将目标控制量尽可能快地发送至电机驱动板。
实时读取
电机以 1 kHz 的频率自动上报当前位置和 effort。以下函数均为非阻塞调用,会立即返回缓存中最新的读取结果。什么是 Effort?Effort 是电流空间的执行器作用量,经过滤波处理后输出。它不是实际测量的电流值,应将其理解为相对驱动强度。典型应用场景:
- 碰撞检测:effort 突增表示关节受阻
- 负载监控:可通过
effort / effort_limit计算当前输出百分比
实时控制进阶示例
异步读/写
本节介绍在 wujihandpy 中以异步模式读/写数据的方法。异步接口概述
读/写接口均提供异步版本,函数名以_async 为后缀,用于在协程环境中执行非阻塞的 I/O 操作。
await,在等待期间不会阻塞事件循环。函数返回则表示操作已成功完成。
示例
异步进阶示例
多线程操作
本节介绍在 wujihandpy 中进行多线程操作的方法。多线程接口概述
默认情况下,wujihandpy 会对Hand 对象进行线程安全检查,以防止多线程并发访问导致的数据竞争问题。若需在多线程环境中使用同一个 Hand 对象,需先调用 Hand.disable_thread_safe_check() 禁用该检查。
多线程安全注意事项
- 必须在多线程操作
Hand对象之前调用disable_thread_safe_check() - 用户需要自行使用互斥锁(
threading.Lock)等同步机制保护对Hand对象的并发访问 - 建议使用
with lock:语句块包裹所有对Hand对象及其派生对象(如IController)的操作
预期效果
运行示例后,终端将输出类似以下内容:- 首行显示线程安全检查已禁用
[Reader]打印当前关节位置矩阵(5×4,对应 5 个手指各 4 个关节)[Writer]打印当前目标位置(余弦波,从 0 逐渐增大)- 两个线程交替输出,顺序可能交错,这是多线程的正常现象
- F1(拇指)保持不动,F2(食指)、F3(中指)、F4(无名指)、F5(小指)会做周期性弯曲-伸展动作
多线程进阶示例
USB 断连处理
设备运行期间,灵巧手与电脑之间的 USB 连接可能中断,例如数据线被拔出。本节介绍 wujihandpy 在断连时的行为,以及捕获断连异常的方法。断连时的行为
USB 连接中断时,wujihandpy 会抛出异常。捕获该异常即可在断连后恢复,进程不会崩溃。 断连后再次发起的操作会立即抛出 Python 内置的ConnectionError,不再等待 500 ms 超时,涉及接口包括:
- 同步读 / 写(
read_*/write_*) - 异步读 / 写(
read_*_async/write_*_async)
断连瞬间已在等待中的异步请求是个例外:它会以
TimeoutError 结束,而非 ConnectionError。若代码中存在并发的异步操作,建议捕获两者的共同基类 OSError,即可同时覆盖断连与超时。捕获断连异常
将设备操作放入try 块,捕获 ConnectionError 即可处理断连:
ConnectionError 中断,程序正常退出而不会崩溃。
实时控制模式下的断连检测
实时控制环路依赖 PDO 高速数据,断连时这些接口仅停止更新缓存,不抛出异常。若需在实时控制场景中检测断连,请在旁路低速循环中周期性调用一次同步读取(如read_input_voltage())作为探测,并捕获 ConnectionError。
实时控制器以上下文管理器使用时,with 块结束会自动调用 close() 完成清理。断连状态下 close() 也会安全返回,无需额外处理。