roboparty_dexhand 是面向 Linux 的 RP_Hand 6DOF 灵巧手 C++/Python
控制库,通过外部配置的 SocketCAN CAN-FD 接口进行通信。库提供统一的
HandDriver 工厂接口、运动控制和反馈读取能力。
本文命令在连接灵巧手的 Linux 主机或板子的 Bash 终端中执行。 首次使用按顺序完成:选择安装方式 → 加载环境 → 配置 CAN-FD → 配置反馈周期 → 运行 Python 示例或 ROS 节点。构建不需要连接硬件;回零和运动示例会驱动真机。
| 名称 | 含义 |
|---|---|
roboparty_dexhand |
本源码项目,提供 C++ 驱动、Python 模块 dexhand_py 和配置工具 |
roboparty-dexhand |
本驱动的 Debian 软件包名 |
roboparty-base |
Debian 驱动包依赖的 RoboParty 基础环境包;安装它不等于安装灵巧手驱动 |
/opt/roboparty |
RoboParty 软件包共用的安装目录,可以包含多个组件 |
roboparty_dexhand_ros |
独立的 ROS 2 适配项目,调用本驱动并提供话题和服务 |
首次从本仓库部署,按下面的源码安装流程操作即可。 已经取得匹配目标系统的
驱动 .deb 及其依赖时,也可以选择 Debian 安装。两种方式提供同一项目的驱动,
但版本可能不同;每个终端只选择一种驱动环境。
| 安装方式 | 驱动安装位置 | 每次新开终端加载 |
|---|---|---|
| 本文的源码安装 | ~/roboparty_dexhand/install/ |
source ~/roboparty_dexhand/setup.bash |
| Debian 包安装 | /opt/roboparty/ |
source /opt/roboparty/setup.bash |
源码目录中的 setup.bash 会查找相邻的 install/,设置 Python、命令行工具
和 CMake 的搜索路径。它不会编译驱动或启动硬件。源码安装无需安装
roboparty-base,也不会自动覆盖 /opt/roboparty 中的版本。
使用本库可以在机器人应用中创建 RP_Hand 驱动、发送关节运动目标并读取 灵巧手反馈。日常使用顺序是先在系统中配置 CAN-FD,再按需完成一次反馈 周期配置,然后初始化驱动、控制关节,最后显式释放驱动资源。
- Linux x86-64 与 AArch64;
- RP_Hand 6DOF,CAN-FD,CANopen node ID 为
1..127; - Python 模块
dexhand_py与 C++ 头文件include/hand_driver.hpp; - 一个进程使用一个活动的厂商 SDK 实例。
本部署只支持 RP_Hand 6DOF。唯一公开支持的模型值为
RP_HAND_6DOF=0。为保持 pybind 兼容性,RP_Hand 品牌发布前的枚举表示
仍被保留;同一个数值别名的 .name、str() 和 repr() 可能显示品牌发布
前的规范标识符。这不表示安装了过时的软件包;普通代码应使用
HandModel.RP_HAND_6DOF,需要数值时使用 .value。
源码安装和下文的 Debian 软件包安装任选一种。以下流程使用独立 CMake 构建, 不要求安装 ROS 2,也不需要连接灵巧手。已在 Orange Pi 的 Ubuntu 22.04 / AArch64、 系统 Python 3.10 环境验证;仓库也包含 Linux x86-64 对应的厂商 SDK。
sudo apt update
sudo apt install git build-essential cmake python3-dev pybind11-dev libspdlog-dev libfmt-dev can-utils iproute2需要支持 C++17 的编译器和 CMake 3.15 或更新版本。下文统一使用
/usr/bin/python3,避免虚拟环境中的 Python 与编译出的扩展版本不匹配。
首次下载(已有源码时跳过):
cd ~
git clone https://github.com/Roboparty/roboparty_dexhand.git以下假定源码已放在 ~/roboparty_dexhand;如果放在其他目录,修改第一行。
命令在目标板上执行,无需交叉编译。安装到项目内的 install/,无需 sudo。
cd ~/roboparty_dexhand
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
-DPython3_EXECUTABLE=/usr/bin/python3 \
-DCMAKE_DISABLE_FIND_PACKAGE_ament_cmake=TRUE \
-DBUILD_TESTING=OFF \
-DDEXHAND_ENABLE_VCAN_TESTS=OFF &&
cmake --build build --parallel 4 &&
cmake --install buildCMAKE_DISABLE_FIND_PACKAGE_ament_cmake=TRUE 将本流程固定为独立 CMake 构建,
即使终端已加载 ROS 环境,也不会额外要求 ament 的检查工具;它不影响后续
ROS 节点导入 dexhand_py。通过 colcon 构建本驱动时不要使用此选项。
BUILD_TESTING=OFF 关闭软件测试构建;上述命令只编译和安装,不会访问硬件。
成功时应看到:
- 编译完成,没有错误;
- 安装输出路径位于
~/roboparty_dexhand/install/。Up-to-date表示已有文件是最新的,属于正常结果。
源码更新后可重复执行整段命令。内存不足导致编译进程被杀死时,将编译的
--parallel 4 调小为 --parallel 1。
每次新开 Bash 终端都要加载一次环境。setup.bash 按自身位置寻找同目录下的
install/,可以从任意工作目录加载;重复加载不会重复追加路径。
source ~/roboparty_dexhand/setup.bash
/usr/bin/python3 -c 'import dexhand_py; print(dexhand_py.__file__)'
/usr/bin/python3 -c 'from dexhand_py import HandModel; print(HandModel.RP_HAND_6DOF.value)'
roboparty-dexhand-config --help第一条 Python 命令应输出本项目 install/ 下的 dexhand_py*.so 路径,
第二条应输出 0,最后一条应显示 feedback-period show/apply 的用法。
以上步骤验证模块和 CLI 能正常加载,不会连接或使能灵巧手。
安装内容包括 install/bin/ 中的 CLI、install/include/ 中的 C++ 头文件、
以及 install/lib/ 中的驱动库、厂商 SDK、CMake package config 和
pythonX.Y/site-packages/ 中的 Python 扩展。X.Y 由构建时 Python 决定,
无需手写到环境变量中。
- 直接用 Python 控制:加载上述环境后,用
/usr/bin/python3运行应用。 接手前先完成下文的 CAN-FD 设置和反馈周期配置,再执行 Python 示例。 模块成功导入不代表接线、通信及真实运动已经验收通过。 - 用于 ROS 2:
roboparty_dexhand_ros在节点进程内直接导入dexhand_py, 不需要另起一个驱动服务。先加载 ROS 2 环境,再加载本项目setup.bash, 最后加载 ROS 工作空间的install/setup.bash。接下来按 ROS 包 README 构建并启动;本仓库不包含 ROS 节点。 - 硬件验收:见 VALIDATION.md。该文档使用软件包安装路径,
源码安装时应将其中的
source /opt/roboparty/setup.bash替换为source ~/roboparty_dexhand/setup.bash,并使用/usr/bin/python3。
| 现象 | 处理方式 |
|---|---|
| CMake 找不到 pybind11、spdlog、fmt 或 Python 开发文件 | 确认第 1 步的开发包安装成功,再重新配置 |
| CMake 提示源目录与缓存不一致 | 项目移动或复制后,改用新的构建目录,如 -B build-local,后续编译和安装也使用该目录 |
setup.bash 提示安装目录不存在或找不到唯一模块 |
先完成安装;脚本要求项目的 install/ 下恰好有一个 dexhand_py*.so。更换 Python 版本后,先移走旧安装目录再重新安装 |
ModuleNotFoundError: dexhand_py |
在运行应用的同一终端重新 source 环境,并使用 /usr/bin/python3 |
| 模块来自其他安装目录或无法加载动态库 | 检查输出的模块路径,确认加载的是本次安装;不要单独拷贝 Python 扩展,需保留完整安装目录 |
本节适用于已取得与目标系统、CPU 架构、Python 版本匹配的软件包的用户。
下面以 Ubuntu 22.04 / ARM64 为例。需要 roboparty-dexhand 和
roboparty-base (>= 1.0.0);后者需预先安装或可从你配置的 RoboParty 软件源取得。
这些包不是 Ubuntu 默认软件源的通用依赖,若没有软件包或软件源,请使用上面的
源码安装方式。不要将 ARM64 包安装到 x86-64 主机。
先在终端进入存放 .deb 的目录。以下要求当前目录恰好有一个匹配的驱动包:
(
shopt -s nullglob
deb_candidates=(./roboparty-dexhand_*_arm64.deb)
if (( ${#deb_candidates[@]} != 1 )); then
printf 'expected exactly one ARM64 package, found %d\n' \
"${#deb_candidates[@]}" >&2
exit 1
fi
sudo apt install "${deb_candidates[0]}"
)安装完成后,每个新终端加载环境并检查实际使用的模块:
source /opt/roboparty/setup.bash
/usr/bin/python3 -c 'from dexhand_py import HandDriver, HandModel; import dexhand_py; print(dexhand_py.__file__)'
roboparty-dexhand-config --help
dpkg-query -W roboparty-dexhand roboparty-base模块路径应位于 /opt/roboparty/。若环境脚本不存在,检查基础环境包是否安装完整;
若提示找不到 dexhand_py,检查驱动包是否安装以及 Python 版本是否匹配。
不要通过加载源码版环境来掩盖软件包安装问题。
需要支持 CAN-FD 的适配器、正确的设备供电和接线。普通 CAN 适配器不能替代 CAN-FD。先查看系统提供的接口:
ip -brief linkcan0 只是接口名称示例,请按实际端口修改。接口不存在时,先检查适配器、
连接和 Linux 驱动。配置前停止使用该接口的其他程序;下面的 down 会中断
整个接口上的通信。已由系统正确配置的接口可以跳过配置,直接检查状态。
CAN-FD 接口由系统或部署脚本在库外配置。下面以 can0 为例,设置名义
速率 1 Mbps、数据速率 5 Mbps、采样点和同步跳转宽度后启动接口:
sudo ip link set can0 down
sudo ip link set can0 type can \
bitrate 1000000 sample-point 0.8 sjw 4 \
dbitrate 5000000 dsample-point 0.75 dsjw 2 fd on
sudo ip link set can0 txqueuelen 10000
sudo ip link set can0 up
ip -details -statistics link show can0将命令中的 can0 替换为实际连接灵巧手的物理接口。库只使用已存在的
SocketCAN 接口,永远不会替应用配置速率、采样点、队列长度或接口状态。
预期看到接口 UP、CAN <FD>、bitrate 1000000 和 dbitrate 5000000。
双手分别连接 can0、can3 时,两者都需要配置;将上面各条命令中的 can0
替换为 can3 再执行一次。上述配置不是持久化设置,重启或重新插拔后应重新检查。
新设备、替换设备、恢复出厂设置的设备,或当前配置未知的设备,需要完成一次 反馈周期配置;普通每次启动不需要重复执行。操作前先停止其他手控制进程, 然后执行:
roboparty-dexhand-config feedback-period apply --interface can0 --node-id 1 --milliseconds 20 --save只给灵巧手本体断电再上电,然后执行查询:
roboparty-dexhand-config feedback-period show --interface can0 --node-id 1查询结果中六个轴的 raw value 都必须是 200。如果有轴需要改变,
apply --save 会写入六个目标值并保存;如果六个轴已经都是 200,CLI
会报告 result=already-compliant,不会重复写入或保存。无论哪种结果,
都要给灵巧手本体断电再上电,然后运行上面的 show 进行确认。20 ms
表示两个反馈类型各自以 50 Hz 发送,因此合计约为 100 帧/秒。正常的
init_hand() 永远不会写入或修改持久化反馈周期参数。
双手需要分别配置和查询;右手使用 can3 时,将上述命令的 --interface can0
替换为 --interface can3。--node-id 必须与对应设备一致。
以下是可直接复制到 Bash 执行的单手示例。先加载所选安装方式的环境,完成
CAN-FD 和反馈周期配置,并停止 ROS 节点及其他手控制程序。
示例控制 can0 上 node ID 为 1 的手;其他接线请修改代码中的接口和 ID。
运行会使能、回零,再将六关节移动到目标位置并返回零位。 确认目标位置
1200 和速度 2000 适合实际机构、负载,运动范围内无障碍物,并准备好硬件
停止手段。失败时停止排查,不要绕过回零继续发送位置命令。
home_wait_time=5.0 是最大超时,不是固定等待。驱动收到回零后的新鲜
0x50 位置反馈和 0x5A 状态反馈后,会检查六轴均已停止、无报警且接近
零位,并立即返回;超时则初始化失败并禁用电机。仅软件进程重启、手本体
没有掉电且零位可信时,可用 init_hand(True, False, 0.0) 快速恢复,不应
重复执行回零。
/usr/bin/python3 - <<'PYTHON'
import time
from dexhand_py import HandDriver, HandModel
CAN_INTERFACE = "can0"
TARGET_POSITION = 1200
TARGET_VELOCITY = 2000
def read_positions(hand, label):
positions = [hand.get_now_position(joint) for joint in range(1, 7)]
values = " ".join(
f"joint{joint}={position}"
for joint, position in enumerate(positions, start=1)
)
print(f"{label}: {values}")
hand = HandDriver.create_hand(
"RP_Hand", "canfd", CAN_INTERFACE, HandModel.RP_HAND_6DOF, 1
)
initialized = False
try:
if not hand.init_hand(True, True, 5.0):
raise RuntimeError("init_hand failed")
initialized = True
hand.check_health()
total, active = hand.get_dof()
if active != 6:
raise RuntimeError(f"unexpected active DOF: {active} (total={total})")
alarms = [hand.get_now_alarm(joint) for joint in range(1, 7)]
if any(alarm != 0 for alarm in alarms):
raise RuntimeError(f"nonzero joint alarms: {alarms}")
hand.set_move_no_home(0)
for joint in range(1, 7):
hand.set_target_position(joint, TARGET_POSITION)
hand.set_position_velocity(joint, TARGET_VELOCITY)
hand.move_motors(0)
time.sleep(1.0)
read_positions(hand, "target")
for joint in range(1, 7):
hand.set_target_position(joint, 0)
hand.set_position_velocity(joint, TARGET_VELOCITY)
hand.move_motors(0)
time.sleep(1.0)
read_positions(hand, "zero")
finally:
if initialized:
try:
hand.set_move_no_home(0)
finally:
hand.deinit_hand()
else:
hand.deinit_hand()
PYTHON预期终端打印 target 和 zero 两组位置,同时观察实际动作。读数是驱动缓存,
固定等待时间不保证所有设备都已完成运动;完整反馈验收见 VALIDATION.md。
interface 字符串决定 Linux 将数据发送到哪个 SocketCAN 接口。使用
can0、can1、can2 或 can3 时,只需把工厂参数或上面示例中的
CAN_INTERFACE 改成对应名称;库不会自动探测或选择端口。端口必须已经
连接到目标灵巧手并完成 CAN-FD 配置。
支持的双手部署方式是每只手使用一个 OS 进程。厂商 SDK 每个进程只支持 一个活动实例,接收回调也是进程级资源。每个进程必须明确指定对应接口和 node ID,不能在一个 Python 进程中创建两个活动的手实例。
需要现成的双手启动命令时,使用
roboparty_dexhand_ros。
该项目会分别启动左右手节点,默认接口为 can0 和 can3;请先完成其 README
中的 ROS 安装和构建步骤。纯 Python 应用则将单手控制逻辑分别运行在两个进程中。
最小生命周期示例只使用公共头文件中的接口。它会初始化(包括正常回零)、
检查状态并释放驱动,不设置目标位置,也不调用 move_motors()。真实硬件
会启用并回零;运行前确认工作空间已清空。以下为嵌入 C++ 应用的 API 示例,不是可直接粘贴到终端的命令:
#include <hand_driver.hpp>
int main() {
auto hand = HandDriver::create_hand("RP_Hand", "canfd", "can0", HAND_RP_HAND_6DOF, 1);
int result = 1;
try {
if (hand->init_hand(true, true, 5.0)) {
hand->check_health();
int total = 0;
int active = 0;
hand->get_dof(total, active);
bool alarms_clear = true;
for (int joint = 1; joint <= 6; ++joint) {
if (hand->get_now_alarm(joint) != 0) alarms_clear = false;
}
result = (active == 6 && alarms_clear) ? 0 : 1;
}
} catch (...) {
hand->deinit_hand();
throw;
}
hand->deinit_hand();
return result;
}以下是 HandDriver 的公共 Python/C++ API。C++ 方法使用 hand->method(),
Python 方法使用 hand.method()。
- 创建与生命周期:
create_hand()、init_hand(enable_motors, home_motors, home_wait_time)、deinit_hand()。工厂参数依次为hand_type、interface_type、interface、hand_model、canfd_node_id;本部署使用"RP_Hand"、"canfd"和 C++ 模型常量HAND_RP_HAND_6DOF;Python 使用HandModel.RP_HAND_6DOF。注意:对已初始化的驱动,重复init_hand()仅在参数完全一致时幂等返回true;参数不同 (例如先以enable_motors=false初始化、再请求true)会返回false,此时应先deinit_hand()再重新初始化。home_wait_time是回零确认的最大超时;满足反馈条件会提前返回,传入0.0则保留不等待、不验证回零结果的兼容行为。 - 枚举名称按语言区分:Python 使用
HandCommType.CANFD和HandModel.RP_HAND_6DOF;C++ 使用HandCommType::CANFD和HAND_RP_HAND_6DOF。RP_Hand 品牌发布前版本中的标识符仍保持源码兼容, 但新代码应使用上述公共名称。 - 运动执行:
move_motors(finger_id=0)、stop_motors(finger_id=0)、home_motors(finger_id=0)的默认finger_id是0,表示广播到全部 关节。set_enable(finger_id, enable)必须显式传入finger_id,但传入0仍表示广播;set_move_no_home(enable)没有finger_id,只接受1(允许未完成回零时运动)或0(要求先完成回零)。 - 目标参数:
set_target_position(finger_id, position)(编码器计数)、set_target_angle(finger_id, angle)(角度)、set_position_velocity(finger_id, velocity)(计数/秒)、set_max_current(finger_id, current)(mA)。这些方法需要明确的finger_id,没有默认值;按公共/厂商约定传入0表示广播到全部关节。 本手册示例仍显式使用关节1..6,以便逐关节检查和控制。 - 反馈与状态:
get_now_position(finger_id)、get_now_angle(finger_id)、get_now_status(finger_id)、get_now_current(finger_id)、get_now_alarm(finger_id)、clear_alarm(finger_id)。clear_alarm(0)清除全部关节报警;其他读取方法按指定关节返回缓存值。 - 设备信息与健康:Python 中
total, active = hand.get_dof()返回总关节数 和活动关节数;C++ 中使用int total, active; hand->get_dof(total, active);。get_can_name()返回接口名,check_health()在驱动故障时抛出异常。
- 启动控制程序前,先在库外配置并拉起目标 SocketCAN CAN-FD 接口;node ID 必须与实际灵巧手一致,并避免同一总线上出现冲突的 node ID。
- 首次或配置未知的手必须先完成 20 ms 反馈周期配置并确认六个 raw value
都为
200;配置命令执行期间不要运行其他手控制进程。 init_hand()返回false或抛出异常时,不要继续发送运动命令。运动前 根据实际机构确认目标位置、速度、负载和安全边界。- 每个成功创建的驱动都必须显式调用
deinit_hand();不要把 Python 对象 销毁或 C++ 智能指针析构当作清理替代方案。 - 双手使用时保持一手一进程,并在每个进程中明确传入自己的 interface 和 node ID。
RoboParty 编写的源代码采用 GPL-3.0。仓库内捆绑的厂商 SDK 头文件和二进制
文件保留其自身的许可与再分发边界;相关说明请参阅
thirdparty/README.md。