Reachy Mini Lite机械臂开发指南:从ROS 2连接到视觉伺服实战

Reachy Mini Lite机械臂开发指南:从ROS 2连接到视觉伺服实战

1. 项目概述:为什么选择Reachy Mini Lite?

最近在工作室捣鼓机器人,不少朋友看到我桌上那个“小胳膊”都会好奇地问:“这是个啥?看起来挺酷的。” 这就是我们今天要聊的主角——Reachy Mini Lite。它不是一个成品玩具,而是一个开源的、模块化的机器人开发平台,核心就是一个高精度的7自由度机械臂。如果你对机器人学、ROS(机器人操作系统)、或者想找一个能真实模拟工业场景但又没那么昂贵的实验平台感兴趣,那Reachy Mini Lite绝对值得你花时间研究。

简单来说,Reachy Mini Lite解决了一个很实际的问题:让开发者、研究者甚至是有热情的爱好者,能够在一个成本可控、软件生态成熟、硬件开箱即用的环境里,快速验证机器人算法、进行人机交互实验,或者开发新的应用。它不像动辄几十上百万的工业机械臂那样令人望而却步,也不像一些玩具级产品那样精度和扩展性都捉襟见肘。它定位在中间,提供了一个非常扎实的“ playground”。

我选择它的原因很直接:我需要一个能稳定运行ROS 2、关节反馈精度高、并且有活跃社区支持的机械臂,来测试我们团队在轨迹规划和人机协作方面的一些新想法。Reachy Mini Lite的官方SDK和URDF模型做得相当规范,省去了自己从零建模和驱动开发的巨大工作量,让我能把精力集中在算法本身。接下来,我就从开箱到让它“活”起来,详细拆解整个过程,并分享一些我踩过的坑和总结的经验。

2. 开箱与硬件初印象:不只是“迷你”

当你拿到Reachy Mini Lite的箱子,第一感觉可能是“比想象中小巧”。但千万别被“Mini”这个名字误导,它的“Lite”主要体现在去除了一些高级传感器(如力觉传感器)和更复杂的末端执行器,专注于提供核心的机械臂运动能力。开箱后,你会看到以下几个核心部件:

  1. 机械臂本体:一个7自由度的机械臂(通常包括肩部3个关节、肘部1个关节、腕部3个关节),这是它的核心。关节采用的是高精度的直流无刷电机搭配谐波减速器,这是保证运动平稳性和位置精度的关键。外壳是3D打印的(通常是PLA或ABS材质),你可以清晰地看到内部的走线和部分结构,这种开源硬件常见的“裸露”风格,反而方便你观察和理解其机械结构。
  2. 控制箱:一个集成了电源、电机驱动板和主控计算机的小盒子。最新版的Reachy通常内置了一台搭载了Ubuntu和ROS 2的微型电脑(如Intel NUC或类似规格的工控机),这意味着它是一台“自带大脑”的机器人,你只需要通过网线或Wi-Fi连接到它,就可以开始编程和控制,无需额外配置一台主机。
  3. 电源适配器:为整个系统供电。
  4. 必要的线缆:连接机械臂和控制箱的线束,通常是一条多芯的航空插头线,负责传输电力、电机控制信号和编码器反馈。

硬件上的几个关键点需要注意:

  • 关节限位:在初次上电前,务必手动检查每个关节的活动范围,确保没有任何线缆被机械结构挤压或缠绕。虽然软件里有软限位,但养成先进行物理检查的习惯能避免很多不必要的风险。
  • 供电安全:使用原装电源适配器。机械臂在启动和急停时电流可能较大,劣质电源可能导致驱动板不稳定甚至损坏。
  • 安装固定:机械臂底座有标准的法兰孔。我强烈建议你为它准备一个坚固、沉重的底座(比如一块厚重的铝板或直接固定在实验台上)。在高速运动或承载末端工具时,一个稳定的基座是安全性和精度的基础。我最初把它放在一张普通的办公桌上,结果在进行快速点到点运动时,整个桌子都在微微共振,严重影响了末端定位的重复精度。

注意:首次通电时,机械臂可能会执行一个“回零”或自检动作,发出一些电机运转的声音。这是正常现象。请确保机械臂周围有足够的空间(建议半径1米内无障碍物),并保持警惕,随时准备按下急停开关(如果控制箱配备的话)。

3. 软件环境搭建与首次连接

Reachy Mini Lite的软件核心是ROS 2。官方通常已经预装了Ubuntu操作系统和ROS 2 Humble或Iron版本,并安装了Reachy专用的SDK。你的开发机(可以是另一台Ubuntu电脑,也可以是Windows/Mac通过一些方式)需要与Reachy建立通信。

3.1 网络配置

最稳定的方式是使用网线直连,或者将Reachy和你的开发机连接到同一个局域网(Wi-Fi或交换机)。假设Reachy的IP地址是192.168.1.100,你的电脑IP是192.168.1.50

  1. 查找Reachy的IP:如果不知道IP,通常可以在Reachy的屏幕上看到(如果它有屏幕),或者通过路由器后台查看连接设备列表。更“机器人”的方式是,先用键盘和显示器直接接入Reachy的控制箱,在终端里输入ip addr命令查看。
  2. 配置主机名解析(可选但推荐):为了避免记忆IP,可以在你的开发机的/etc/hosts文件中添加一行:192.168.1.100 reachy。这样以后你就可以用reachy这个主机名来代替IP地址。
  3. 配置ROS 2环境变量:这是ROS 2多机通信的关键。在你的开发机终端里,你需要设置ROS_DOMAIN_ID并指定Reachy的地址。
    # 在你的开发机上操作 export ROS_DOMAIN_ID=0 # 确保Reachy和你的电脑使用相同的DOMAIN_ID,默认为0 export ROS_MASTER_URI=http://reachy:11311 # 如果使用ROS 1的桥接或部分工具可能需要 # 对于ROS 2,更关键的是设置RMW实现和发现 export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp # 使用CycloneDDS,跨子网能力更强 export CYCLONEDDS_URI="<path_to_your_config_file>" # 可以配置网络发现
    最简单初学的方式,是使用ROS_DOMAIN_ID。确保Reachy控制箱里的ROS 2也设置了相同的ROS_DOMAIN_ID(通常已在镜像中预设)。这样,在同一个局域网内,它们就能自动发现彼此。

3.2 安装Reachy SDK(在开发机上)

即使Reachy本体有了SDK,你在自己的开发机上也需要安装客户端库,以便编写和运行控制程序。

# 假设你使用Ubuntu和ROS 2 Humble sudo apt update # 安装Python3的reachy SDK包 pip3 install reachy2-sdk # 或者,如果你想从源码安装以获得最新特性(推荐给深度开发者) mkdir -p ~/reachy_ws/src cd ~/reachy_ws/src git clone https://github.com/pollen-robotics/reachy2_sdk.git cd ~/reachy_ws rosdep install --from-paths src --ignore-src -r -y colcon build --symlink-install source install/setup.bash

3.3 首次连接测试

安装好SDK后,就可以尝试连接了。创建一个简单的Python测试脚本test_connection.py

#!/usr/bin/env python3 import asyncio from reachy2_sdk import ReachySDK async def main(): # 连接到Reachy,使用它的主机名或IP reachy = await ReachySDK.connect(host="reachy") # 或者 host="192.168.1.100" # 打印机器人信息 print(f"Connected to Reachy: {reachy.name}") print(f"Robot has {len(reachy.arms)} arm(s)") # 获取右臂对象(Reachy Mini Lite通常被配置为右臂) right_arm = reachy.r_arm print(f"Right arm joints: {[joint.name for joint in right_arm.joints]}") # 读取当前所有关节位置 current_positions = [joint.present_position for joint in right_arm.joints] print(f"Current joint positions: {current_positions}") # 让机械臂稍微动一下(例如,肘关节轻微弯曲) # 先切换到位置控制模式 await right_arm.turn_on() # 设置目标位置(弧度制),这里让肘关节(通常索引为3的关节)弯曲30度 import math target_positions = current_positions.copy() target_positions[3] += math.radians(30) # 增加30度 # 平滑移动到目标位置 await right_arm.goto( goal_positions=target_positions, duration=2.0 # 用2秒时间完成动作 ) # 关闭连接 await reachy.disconnect() if __name__ == "__main__": asyncio.run(main())

运行这个脚本python3 test_connection.py。如果一切顺利,你会看到终端打印出关节信息,并且机械臂的肘关节会缓慢地弯曲一个角度。恭喜,这说明你的软硬件连接已经成功!

实操心得:

  • 防火墙:首次连接失败,十有八九是防火墙问题。确保你的开发机和Reachy的防火墙放行了ROS 2使用的端口(默认是UDP 7400-7600左右的一系列端口)。一个简单的测试方法是ping reachyros2 node list(在设置好环境变量后),看是否能发现对方。
  • 异步编程:Reachy SDK大量使用了Python的asyncio库。如果你不熟悉异步编程,会觉得代码写法有点别扭。记住核心:使用async/await,并通过asyncio.run()来启动主函数。所有涉及机器人动作的调用基本都是异步的。
  • 关节索引:不同版本的SDK或URDF模型,关节顺序可能略有差异。最可靠的方式是打印出joint.name来确认每个关节对应的索引,而不是硬编码数字。比如,可能是'r_shoulder_pitch','r_shoulder_roll','r_arm_yaw','r_elbow_pitch','r_wrist_pitch','r_wrist_roll','r_wrist_yaw'

4. 核心控制模式与编程实践

连接成功只是第一步,真正驾驭Reachy Mini Lite在于理解其控制模式。它主要提供了几种不同层级的控制方式,适用于不同场景。

4.1 关节空间位置控制

这是最基础的控制方式,直接给每个关节设定一个目标角度(弧度)。上面的测试脚本就是这种模式。关键在于goto函数,你可以指定目标位置列表、运动时间(duration)或运动速度(velocity)。我强烈建议在初期始终使用duration参数来控制运动时间,而不是直接指定速度,这样更容易预测机器人的运动过程,避免速度过快产生冲击。

async def joint_space_move(): reachy = await ReachySDK.connect(host="reachy") arm = reachy.r_arm await arm.turn_on() # 定义几个关键姿势的关节角度(单位:弧度) # 姿势A:伸展状态 (示例值,需根据实际校准) pose_a = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 姿势B:准备抓取姿态 pose_b = [math.radians(30), math.radians(-20), math.radians(10), math.radians(-90), math.radians(0), math.radians(0), math.radians(0)] # 缓慢移动到姿势A await arm.goto(goal_positions=pose_a, duration=3.0) await asyncio.sleep(1) # 等待1秒 # 缓慢移动到姿势B await arm.goto(goal_positions=pose_b, duration=3.0) await reachy.disconnect()

4.2 笛卡尔空间末端控制

这是更直观、也更常用的控制方式。你不需要关心每个关节怎么转,只需要告诉机器人“末端执行器要移动到空间中的哪个点(X, Y, Z),并且保持什么姿态(Roll, Pitch, Yaw)”。SDK会自动进行逆运动学(IK)解算,求出对应的关节角度。

async def cartesian_move(): reachy = await ReachySDK.connect(host="reachy") arm = reachy.r_arm await arm.turn_on() # 获取当前末端位姿(相对于基座标系) current_pose = arm.forward_kinematics() print(f"Current end-effector pose: Pos{current_pose.pos}, Rot{current_pose.rot}") # 定义目标位置(单位:米)和姿态(四元数或欧拉角) # 例如,让末端在X方向移动0.1米,Z方向降低0.05米,保持当前姿态 from reachy2_sdk.math import Pose, Rotation target_position = [ current_pose.pos[0] + 0.1, # X current_pose.pos[1], # Y 不变 current_pose.pos[2] - 0.05, # Z ] # 保持原有旋转(单位四元数) target_rotation = current_pose.rot target_pose = Pose(target_position, target_rotation) # 使用逆运动学移动到目标位姿 # 这里需要先进行IK解算,检查是否可达 ik_solution = arm.inverse_kinematics(target_pose) if ik_solution is not None: # 如果解算成功,则移动到对应的关节角度 await arm.goto(goal_positions=ik_solution, duration=2.0) else: print("目标位姿不可达!") await reachy.disconnect()

注意事项:

  • 奇异性:当机械臂完全伸直或处于某些特殊构型时,逆运动学解可能不存在或不稳定(雅可比矩阵秩缺失)。在编写笛卡尔空间控制代码时,必须加入IK解算成功的判断,否则机器人可能会尝试移动到无法到达的位置,导致关节剧烈抖动或报错。
  • 姿态表示:机器人学中姿态可以用旋转矩阵、四元数、欧拉角等多种方式表示。Reachy SDK内部通常使用四元数(x, y, z, w)。如果你更熟悉欧拉角(Roll, Pitch, Yaw),需要进行转换。Python的scipy.spatial.transform.Rotation库是处理这类转换的利器。
  • 轨迹规划:简单的goto是点到点的运动,末端轨迹不一定是直线。如果你需要末端精确走一条直线(直线插补)或圆弧,需要在外部进行轨迹规划,生成一系列中间点(路径点),然后让机械臂依次运动。ROS 2中的moveit2框架可以很好地完成这个工作,但集成需要额外配置。

4.3 力控与导纳控制模拟

Reachy Mini Lite是“Lite”版本,通常不配备直接的力/力矩传感器。但是,你可以通过电机的电流反馈来间接估计关节端的输出力矩,从而实现简单的力交互或导纳控制。这属于进阶应用。

其原理是:电机电流与输出扭矩成正比(在稳态下)。通过SDK可以读取每个关节的present_currentpresent_load。当你在末端轻轻推拉机械臂时,关节电机会抵抗这个运动,电流会发生变化。你可以编写一个控制循环,检测到这个电流变化(即外力),然后根据一个虚拟的“弹簧-阻尼”模型(导纳模型),计算出机器人应该产生的补偿运动。

async def simple_impedance_control(): reachy = await ReachySDK.connect(host="reachy") arm = reachy.r_arm await arm.turn_on() # 设置一个虚拟的导纳参数:质量(M)、阻尼(B)、刚度(K) # 这里简化处理,只对某个关节进行模拟 M, B, K = 0.1, 5.0, 50.0 # 示例值,需要精细调整 joint_index = 3 # 肘关节 initial_position = arm.joints[joint_index].present_position target_position = initial_position velocity = 0.0 try: while True: # 1. 读取当前关节位置和估计的负载(与电流/力矩相关) current_pos = arm.joints[joint_index].present_position # 注意:present_load 可能需要根据型号确认,有些版本用 current current_load = arm.joints[joint_index].present_current # 或 present_load # 2. 将负载(电流)转换为估计的外力矩(需要校准系数) # 假设一个简单的比例关系,系数k需要实验标定 k = 0.001 # 示例系数,完全不准!必须自己标定! estimated_torque = current_load * k # 3. 导纳模型计算: M * a + B * v + K * (x - x0) = F_ext # 其中 a 是加速度,v 是速度,x是当前位置,x0是平衡位置,F_ext是外力 # 我们离散化处理,计算目标位置的偏移 stiffness_force = K * (current_pos - target_position) damping_force = B * velocity # 根据 F_ext = M*a, 忽略加速度项简化计算,直接由力差产生位置修正 position_delta = (estimated_torque - damping_force - stiffness_force) / K * 0.01 # 加上一个小增益 new_target_position = target_position + position_delta # 4. 更新速度和目标位置(非常简化的积分) velocity = position_delta / 0.01 # 假设循环周期0.01秒 target_position = new_target_position # 5. 发送新的目标位置给关节(只控制一个关节作为示例) goal_positions = [jp for jp in arm.present_position] # 获取所有关节当前位置 goal_positions[joint_index] = new_target_position # 使用一个非常短的时间,实现“柔顺”的效果 await arm.goto(goal_positions=goal_positions, duration=0.05) await asyncio.sleep(0.01) # 控制循环周期 except KeyboardInterrupt: print("Impedance control stopped.") finally: await reachy.disconnect()

重要警告:上面的代码是一个极度简化的概念演示,绝对不要直接用于真实机器人而不进行严格的安全检查和参数标定!不恰当的参数可能导致机器人剧烈振荡甚至损坏。真正的力控实现需要:

  1. 精确的关节力矩标定(建立电流与扭矩的准确关系)。
  2. 考虑重力补偿(机械臂自身的重力会产生持续的关节力矩)。
  3. 更稳定和精确的控制循环(通常需要实时内核)。
  4. 完善的安全边界和急停逻辑。

5. 常见应用场景与项目实例

有了基本的控制能力,Reachy Mini Lite能做什么?它的应用场景非常广泛,尤其适合算法验证和原型开发。

5.1 视觉伺服(Visual Servoing)

这是机器人领域一个经典课题。结合一个摄像头(可以安装在末端,也可以是固定的全局相机),让机器人根据视觉反馈自动调整位置。例如,让机械臂末端去对准一个彩色小球。

  1. 硬件:在Reachy的末端安装一个USB摄像头,或者在工作台上方固定一个相机。
  2. 软件:使用OpenCV处理图像。在ROS 2中,你可以使用cv_bridge将图像消息转换为OpenCV格式,或者直接用rclpy配合OpenCV。
  3. 流程
    • 相机捕捉图像,识别目标(比如通过颜色阈值找到小球的像素中心)。
    • 计算目标在图像中的位置与图像中心的位置偏差(像素误差)。
    • 将这个像素误差通过一个相机模型(标定好的内参)转换到相机坐标系下的空间误差(可能需要深度信息,如果是单目则需其他假设或传感器融合)。
    • 将空间误差转换到机器人基座标系。
    • 根据误差,生成机器人末端的运动速度或增量位移指令(比例控制)。
    • 循环执行,直到误差小于阈值。
# 伪代码/简化流程示意 async def visual_servoing(): # 初始化机器人、摄像头 reachy = await ReachySDK.connect(...) cap = cv2.VideoCapture(0) # 或订阅ROS图像话题 while True: ret, frame = cap.read() # 图像处理:找到目标 target_pixel_x, target_pixel_y = find_red_ball_center(frame) # 计算图像中心与目标中心的像素偏差 img_center_x, img_center_y = frame.shape[1]//2, frame.shape[0]//2 error_x = target_pixel_x - img_center_x error_y = target_pixel_y - img_center_y # 将像素偏差转换为空间运动指令(简化:假设一个比例增益和固定的深度) # 这里需要一个相机标定矩阵 [fx, 0, cx; 0, fy, cy] gain = 0.001 move_x = -error_y * gain # 注意:图像Y轴对应机器人-Z轴(俯仰)是常见的 move_y = -error_x * gain # 图像X轴对应机器人-Y轴(偏航) # 获取当前末端位姿 current_pose = arm.forward_kinematics() # 生成新的目标位姿(只在X,Y方向微调) new_position = [ current_pose.pos[0], current_pose.pos[1] + move_y, current_pose.pos[2] + move_x, ] new_pose = Pose(new_position, current_pose.rot) # IK解算并运动 ik_solution = arm.inverse_kinematics(new_pose) if ik_solution: await arm.goto(goal_positions=ik_solution, duration=0.1) # 快速小幅度调整 if abs(error_x) < 5 and abs(error_y) < 5: # 像素误差足够小 print("Target centered!") break

5.2 模仿学习(Imitation Learning)

通过“手把手”教导机器人学习动作。一种简单的方法是“示教再现”。

  1. 记录示教轨迹:你可以用手轻轻地拖动Reachy的机械臂(在电机通电但处于“松弛”模式或低刚度模式下),让它经过一系列路径点。同时,以高频率(如100Hz)记录下每个关节的角度或末端位姿。
  2. 轨迹平滑与泛化:记录的原始数据可能包含抖动。你需要对数据进行滤波(如低通滤波或Savitzky-Golay滤波)和平滑处理。然后,你可以用多项式、样条曲线等方式对轨迹进行参数化。
  3. 再现与泛化:让机器人根据参数化后的轨迹模型重新运动。你还可以调整参数来改变轨迹的速度、幅度,甚至将轨迹应用到不同的起始点(通过坐标变换)。

Reachy SDK可能提供“零力”或“低刚度”模式,方便拖动。如果没有,你可以通过设置一个非常低的关节刚度(K)和阻尼(B)的导纳控制来实现类似效果。

5.3 与其他传感器的集成

Reachy Mini Lite的ROS 2架构使其能轻松与其他传感器节点集成。例如:

  • 语音控制:使用ROS 2的语音识别包(如ros2_humble_audio_common结合Vosk等离线引擎),将语音指令转换为运动命令。
  • Leap Motion/手势控制:通过Leap Motion传感器捕捉手部姿态,映射到机器人末端姿态,实现隔空操控。
  • 脑机接口(BCI)实验:将脑电信号(EEG)通过ROS 2话题发布,编写一个节点解析特定脑电模式(如想象左手运动),触发机器人执行抓取动作。这在人机交互和康复机器人研究中是常见课题。

6. 故障排查与维护心得

在使用过程中,你肯定会遇到各种问题。下面是我总结的一些常见问题及其解决方法。

问题现象可能原因排查步骤与解决方案
无法连接到Reachy1. 网络不通。
2. ROS_DOMAIN_ID不匹配。
3. 防火墙阻挡。
4. Reachy服务未启动。
1.ping <reachy_ip>测试连通性。
2. 检查双方echo $ROS_DOMAIN_ID
3. 暂时关闭防火墙测试sudo ufw disable(测试后记得重新开启并配置规则)。
4. 通过显示器直接登录Reachy,检查ros2 daemon是否运行,以及reachy_sdk_server节点是否启动。
机械臂不动,但代码无报错1. 未调用turn_on()
2. 目标位置与当前位置过于接近。
3. 关节处于错误状态(如过流保护)。
1. 确保在goto前执行了await arm.turn_on()
2. 打印目标位置和当前位置,看是否有显著差异。
3. 通过SDK检查关节状态joint.is_errorjoint.present_current是否异常高。尝试重启机器人。
运动过程中抖动或异响1. 轨迹速度或加速度设置过高。
2. 机械结构有干涉或螺丝松动。
3. 控制参数(PID)不匹配。
1. 增加gotoduration参数,降低运动速度。
2.立即停止运动,仔细检查各关节连接处、线缆是否被拉扯,所有螺丝是否紧固。
3. 如果是官方镜像,PID参数通常是调好的。如果是自己编译的SDK,可能需要检查参数。异响持续需联系技术支持。
逆运动学(IK)解算失败1. 目标位姿超出工作空间。
2. 目标姿态存在奇异性。
3. IK求解器数值问题。
1. 手动估算目标点是否在臂展范围内。从简单、可达的位置开始测试。
2. 尝试微调目标姿态(特别是Roll/Pitch/Yaw),避免完全伸直等奇异构型。
3. 检查URDF模型是否正确加载。可以尝试其他IK求解器(如TRAC-IK),但集成需要工作量。
Python脚本报asyncio相关错误1. 未在异步函数内调用await
2. 事件循环冲突(如在Jupyter Notebook中)。
3. SDK版本与Python版本不兼容。
1. 确保所有与Reachy交互的函数都是async def,并用await调用。
2. 在Jupyter中,尝试使用nest_asyncio.apply()。对于脚本,确保用asyncio.run(main())
3. 查看官方文档确认支持的Python版本(通常是3.8+)。

维护心得:

  • 定期检查:每隔一段时间,检查一下机械臂的线缆是否有磨损,关节处的螺丝是否松动。谐波减速器虽然寿命长,但连接件可能因振动而松动。
  • 清洁:使用气吹或软毛刷清理3D打印外壳缝隙里的灰尘,避免进入电机或编码器。
  • 软件更新:关注官方GitHub仓库的更新,特别是SDK和固件。更新前务必阅读Release Notes,并在测试环境中先验证。我曾因为跳过一个次要版本更新,导致API不兼容,浪费了半天时间回退。
  • 社区:Pollen Robotics的Discord或论坛是宝贵的资源。遇到奇怪的问题,先去搜索一下,很可能别人已经遇到过并解决了。提问时,尽量提供详细的错误日志、你的代码片段和系统环境信息。

最后,我想说的是,Reachy Mini Lite是一个强大的工具,但它更是一个学习平台。不要指望一开始就写出完美的、鲁棒的代码。从最简单的单关节运动开始,逐步增加复杂度,多写测试脚本,多用print()输出中间状态。机器人编程是“动手”的学问,很多理解是在调试和失败中获得的。当你看到自己写的代码让机械臂流畅地完成一个任务时,那种成就感是无与伦比的。