SDK 接口
Wuji Hand 2 的 Python 语义化 API。统一模式:hand.{resource}().{action}()。所有操作都针对整只手(20 关节)。
本页聚焦 Wuji Hand 2 专属的语义化 API。SDK 基于通用的 Wuji SDK(wuji_sdk,pip install wuji-sdk)——通用的安装、设备连接与数据订阅模型见 Wuji SDK 文档,源码见 wuji-sdk 仓库。
1. 连接
1.1 通过 SdkManager 连接(推荐)
from wuji_sdk import SdkManager, Handedness
manager = SdkManager.instance()
# 自动发现并连接局域网内首个 Wuji Hand 2
hand = manager.auto_connect(device_name="wuji_hand_2")
# 已知序列号或地址时显式连接
hand = manager.connect(sn=<device_sn>, device_name="wuji_hand_2")
hand = manager.connect(address="192.168.3.110:50001", device_name="wuji_hand_2")
# 或按左右手直接连接,无需序列号
hand = manager.connect(handedness=Handedness.Right, device_name="wuji_hand_2")auto_connect 和 connect 的 device_name="wuji_hand_2" 重载会直接返回 WujiHand2 实例(带类型提示)。handedness 与 sn、address 互斥。多只同手性 Hand 2 在网时会抛 AmbiguousHandedness,请改用 sn 指定。
1.2 ConnectOptions
from wuji_sdk import SdkManager, ConnectOptions
opts = ConnectOptions(
timeout_ms=1000,
retry_count=3,
enable_bridge=True, # 默认 True:允许多个客户端(Wuji Studio + 录制脚本 + 业务程序)同时连接
)
hand = manager.connect(sn=<device_sn>, device_name="wuji_hand_2", options=opts)设 enable_bridge=False 切换成独占单客户端模式。
1.3 实例属性
| 属性 | 类型 | 说明 |
|---|---|---|
serial_number | str | 设备序列号 |
device_name | str | 连接时指定的名字(默认 "wuji_hand_2") |
info | Optional[DeviceInfo] | 设备信息:serial_number、firmware_version |
is_connected | bool | 连接状态 |
hand.hw_version().get() 返回出厂硬件版本 HwVersion(major, minor, patch)。0.0.0 表示未在工厂端写入。
2. Hand 级资源
所有操作都针对整只手(20 关节)。无单关节 API。单关节信息通过 hand.joints() / hand.fingers() 返回的句柄获取(见 第 3 节 关节遍历)。
2.1 handedness(GET)
获取左右手识别结果(left / right)。
side = hand.handedness().get() # → "left" 或 "right"2.2 online_joints_count(GET)
获取在线关节数(0–20)。
n = hand.online_joints_count().get() # → int,0–202.3 diagnostics(GET)
诊断快照:返回 20 个关节各一条 DiagnosticStatus 的诊断报告(下标对齐 hand.joints())。
report = hand.diagnostics().get() # → DiagnosticArray
for s in report.status:
print(s.name, s.level, s.message) # name 形如 "thumb_S1",level:0=OK / 1=WARN / 2=ERROR关节角请用 JointState.actual_pos。
2.4 comm_diag(GET,1 Hz)
通信诊断:每秒返回 5 根手指的吞吐 / 错误率。
diag = hand.comm_diag().get() # → HandCommunicationDiagnostics
for finger in diag.fingers: # 5 根手指各自的吞吐 / 错误率
print(finger.tx_kbps, finger.rx_kbps, finger.error_per_sec)用于排查关节与控制板之间的通信问题。
2.5 control_mode(SET)
设置控制模式。
hand.control_mode().set("mit") # "off" / "mit" / "position" / "velocity" / "current"大小写敏感,必须小写。
2.6 effort_limit(SET/GET)
力矩限幅:读写各关节的力矩上限(A)。
hand.effort_limit().set(1.5) # 全部关节设为 1.5 A
limits = hand.effort_limit().get() # → list[Optional[float]]2.7 mit_params(SET/GET)
MIT 阻抗参数:5×4 矩阵,逐关节设置 kp / kd。
kp = [[1.0]*4 for _ in range(5)] # 5 手指 × 4 关节
kd = [[0.05]*4 for _ in range(5)]
hand.mit_params().set(kp=kp, kd=kd) # 每关节独立的 kp / kd
mp = hand.mit_params().get() # → MitParams(kp=[[...]], kd=[[...]]),离线关节填 NaN2.8 clear_fault(SET)
清除故障。
hand.clear_fault().set(1)2.9 用户零点(origin)
把当前物理位置标定为某关节(或整手)的关节侧零位。如果关节正在运动,会延迟到下次 IDLE 才生效。
hand.set_origin(joint_index) # 单关节:把 joint_index (0..19) 当前位置当成新的零位
hand.clear_origin(joint_index) # 单关节:清除用户零点(offset3 → 0)
hand.set_origin_all() # 整手:所有关节同时刷新用户零点
hand.clear_origin_all() # 整手:清除所有关节的用户零点2.10 软限位(soft_limit)
每关节的关节侧角度软限位(rad)。enabled=False 时回退到出厂硬件范围。
hand.set_soft_limit(joint_index, min_rad, max_rad, enabled=True) # 配置,min <= max
min_rad, max_rad, enabled = hand.get_soft_limit(joint_index) # 读回约束:min_rad <= max_rad。min == max 是合法的「单点钳位」。两端必须为有限值(不能 NaN / Inf)。
2.11 使能 / 失能
hand.enable() # 使能全部关节
hand.disable() # 失能全部关节2.12 joint_state(SUB)
关节状态流:订阅 20 关节的实时状态帧。
sub = hand.joint_state().subscribe() # → Subscription[JointState]
data = sub.recv() # 同步非阻塞,None = 暂无数据
data = await sub.recv_async() # 异步等待
print(data.cycle_id)
for j in data.joints:
print(j.actual_pos, j.actual_vel, j.torque)
sub.close()回调模式:
def on_state(state):
print(state.cycle_id, [j.actual_pos for j in state.joints[:4]])
cb = hand.joint_state().subscribe_with_callback(on_state)
# ...
cb.close()actual_pos 为关节侧角度(rad),actual_vel 为关节侧角速度(rad/s),这是获取关节角与关节速度的唯一推荐路径。diagnostics() 返回的是状态级报告(DiagnosticArray),不包含可直接消费的关节角。
2.13 joint_command(PUB)
关节命令:发布 20 关节的位置 / 速度 / 力矩前馈。
pub = hand.joint_command_publisher() # → JointCommandPublisher
pub.send(positions) # list[float],20 个位置 (rad)
pub.send(positions, velocities) # + 速度
pub.send(positions, velocities, efforts) # + 力矩前馈
pub.close()2.14 Flash 日志导出(诊断)
# 导出当前固件的运行日志(绝大多数排查场景用这个)
result = await hand.dump_hand_logs(bank="current", out_dir="./logs")bank | 含义 | 何时用 |
|---|---|---|
"current" | 当前正在运行的固件写的日志 | 默认使用,排查现网问题 |
"other" | 升级或回滚之前那个固件版本残留的日志 | 仅当需要追溯升级 / 回滚前的现象时再导 |
每次调用在 out_dir 下生成一个独立的会话目录 <sn>-<unix_ts>/,里面包含每个关节的 joint{0..19}.log 加一个 sboard.log(JSONL:每行一条 {"timestamp_ms", "level", "target", "message"})。out_dir 可省略,默认为 ~/.wuji/hand_logs。返回一个 dict:
result = await hand.dump_hand_logs(bank="current")
print(result["session_dir"]) # str: 本次会话目录绝对路径
print(result["files"]) # list[str]: 已写入的 .log 文件路径3. 关节遍历
JointHandle 和 FingerHandle 仅提供 label / index 信息,用于与 diagnostics() / joint_state 返回数组做下标对应。
| 方法 | 返回类型 | 说明 |
|---|---|---|
hand.joints() | list[JointHandle] | 全部 20 个关节 |
hand.fingers() | list[FingerHandle] | 全部 5 根手指 |
for joint in hand.joints():
print(joint.label, joint.index) # e.g. "thumb_S1" 0
for finger in hand.fingers():
for joint in finger.joints(): # 每根手指 4 个关节,按 S1..S4 顺序
print(joint.label) # "thumb_S1", "thumb_S2", ...3.1 JointHandle
关节句柄:提供 label / index。
| 属性 | 类型 | 说明 |
|---|---|---|
label | str | 关节标签,格式 {finger}_S{1..4},如 "thumb_S1"、"pinky_S4" |
index | int | 全局索引(0–19) |
3.2 FingerHandle
手指句柄:遍历该手指的 4 个关节。
| 方法 | 返回类型 | 说明 |
|---|---|---|
joints() | list[JointHandle] | 该手指 4 个关节,按 S1..S4 顺序 |
4. 数据类型
4.1 DiagnosticArray / DiagnosticStatus
诊断报告:hand.diagnostics().get() 返回值。
class DiagnosticArray:
seq: int
timestamp_us: int
frame_id: str
status: list[DiagnosticStatus] # 20 条,下标对齐 hand.joints()
class DiagnosticStatus:
level: int # 0=OK / 1=WARN / 2=ERROR
name: str # 关节标签,如 "thumb_S1"
message: str # 人类可读信息
hardware_id: str # 设备 SN
values: list[tuple[str, str]] # 诊断键值对(离线关节为空)values 固定 6 个 key(值类型注解的是字符串里承载的语义):
| key | 类型 | 说明 |
|---|---|---|
vbus | float | 总线电压 (V) |
temperature | float | 温度 (°C) |
inverter_state | int | 逆变器状态(见下表) |
fault_code | int | 故障码(0 = 正常) |
raw_angle | int | 原始编码器角度 |
encoder_valid | int | 编码器有效标志(0 / 1) |
inverter_state 枚举:
| 值 | 状态 | 说明 |
|---|---|---|
| 0 | Initialization | 硬件初始化(EN=0, nSLEEP=1, PWM=off) |
| 1 | SelfCheck | 自检中 |
| 2 | Ready | 准备就绪,等待 Enable |
| 3 | PreCharging | 母线预充电(EN=1, PWM=on, CCR=0) |
| 4 | Enabled | 电机使能,控制激活 |
| 5 | Sleep | 低功耗睡眠(EN=0, nSLEEP=0) |
| 6 | Fault | 故障,输出禁用,可通过 clear_fault() 清除 |
| 7 | Fatal | 致命错误,仅硬件复位可恢复 |
4.2 MitParam / MitParams
MIT 参数类型。
class MitParam: # 单关节
kp: float
kd: float
class MitParams: # 整手矩阵(hand.mit_params().get() 返回)
kp: list[list[float]] # 5×4,离线关节为 NaN
kd: list[list[float]]4.3 JointState
关节状态帧:hand.joint_state().subscribe().recv() 返回值,20 关节聚合。
| 属性 | 类型 | 说明 |
|---|---|---|
cycle_id | int | 帧周期 ID |
num_joints | int | 关节数量 |
joints | list[JointEntry] | 关节状态数组 |
4.4 JointEntry
单关节状态。
| 属性 | 类型 | 说明 |
|---|---|---|
nid | int | 节点编号 |
status | int | 状态码 |
target_pos | float | 目标位置(rad,关节侧) |
actual_pos | float | 实际位置(rad,关节侧) |
target_vel | float | 目标速度(rad/s) |
actual_vel | float | 实际速度(rad/s) |
torque | float | 力矩 (A) |
bcast_seq | int | 帧序号 |
4.5 HandCommunicationDiagnostics
通信诊断数据。
class HandCommunicationDiagnostics:
fingers: list[FingerCommunicationDiagnostics] # 长度 5
class FingerCommunicationDiagnostics:
tx_frame_total: int
rx_frame_total: int
tx_kbps: int
rx_kbps: int
error_per_sec: int
crc_error_total: int
frame_format_error_total: int
uart_hw_error_total: int
transfer_stats: list[TransferStats]
nodes: list[NodeDiagnostics] # per-node:online / ms_since_last_response / response_rate_pct5. 关节编号
| 手指 | S1 | S2 | S3 | S4 |
|---|---|---|---|---|
| thumb | 0 | 1 | 2 | 3 |
| index | 4 | 5 | 6 | 7 |
| middle | 8 | 9 | 10 | 11 |
| ring | 12 | 13 | 14 | 15 |
| pinky | 16 | 17 | 18 | 19 |
标签格式:{finger}_S{1..4}(如 thumb_S1、pinky_S4)。
6. 异常处理
所有 SDK 操作错误统一抛 WujiException,消息中携带错误类型前缀(Disconnected、Timeout、PathNotFound、SchemaMismatch、SerializeError、…)。业务侧按需 try / except 即可。
from wuji_sdk import WujiException
try:
hand.joint_state().subscribe()
except WujiException as e:
print(f"SDK error: {e}")