SDK 接口

Wuji Hand 2 的 Python 语义化 API。统一模式:hand.{resource}().{action}()。所有操作都针对整只手(20 关节)。

本页聚焦 Wuji Hand 2 专属的语义化 API。SDK 基于通用的 Wuji SDKwuji_sdkpip 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_connectconnectdevice_name="wuji_hand_2" 重载会直接返回 WujiHand2 实例(带类型提示)。handednesssnaddress 互斥。多只同手性 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_numberstr设备序列号
device_namestr连接时指定的名字(默认 "wuji_hand_2"
infoOptional[DeviceInfo]设备信息:serial_numberfirmware_version
is_connectedbool连接状态

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–20

2.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=[[...]]),离线关节填 NaN

2.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_radmin == 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. 关节遍历

JointHandleFingerHandle 仅提供 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。

属性类型说明
labelstr关节标签,格式 {finger}_S{1..4},如 "thumb_S1""pinky_S4"
indexint全局索引(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类型说明
vbusfloat总线电压 (V)
temperaturefloat温度 (°C)
inverter_stateint逆变器状态(见下表
fault_codeint故障码(0 = 正常)
raw_angleint原始编码器角度
encoder_validint编码器有效标志(0 / 1)

inverter_state 枚举:

状态说明
0Initialization硬件初始化(EN=0, nSLEEP=1, PWM=off)
1SelfCheck自检中
2Ready准备就绪,等待 Enable
3PreCharging母线预充电(EN=1, PWM=on, CCR=0)
4Enabled电机使能,控制激活
5Sleep低功耗睡眠(EN=0, nSLEEP=0)
6Fault故障,输出禁用,可通过 clear_fault() 清除
7Fatal致命错误,仅硬件复位可恢复

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_idint帧周期 ID
num_jointsint关节数量
jointslist[JointEntry]关节状态数组

4.4 JointEntry

单关节状态

属性类型说明
nidint节点编号
statusint状态码
target_posfloat目标位置(rad,关节侧)
actual_posfloat实际位置(rad,关节侧)
target_velfloat目标速度(rad/s)
actual_velfloat实际速度(rad/s)
torquefloat力矩 (A)
bcast_seqint帧序号

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_pct

5. 关节编号

手指S1S2S3S4
thumb0123
index4567
middle891011
ring12131415
pinky16171819

标签格式:{finger}_S{1..4}(如 thumb_S1pinky_S4)。

6. 异常处理

所有 SDK 操作错误统一抛 WujiException,消息中携带错误类型前缀(DisconnectedTimeoutPathNotFoundSchemaMismatchSerializeError、…)。业务侧按需 try / except 即可。

from wuji_sdk import WujiException

try:
    hand.joint_state().subscribe()
except WujiException as e:
    print(f"SDK error: {e}")