简介:面向ROS机械臂与视觉标定开发者,这份资料围绕Ubuntu 18.04下睿尔曼机械臂与RealSense D435相机的眼在手上手眼标定,整理了一整套可复现的工程方案,也适合需要从零搭建ROS melodic、OpenCV 3.2.0、Aruco识别与easy_handeye标定链路的入门及进阶用户。包内共1137个文件,约62.24MB,以launch启动文件、C++/Python源码、消息与服务定义、cmake/make编译脚本、xacro/urdf机器人描述和yaml参数配置为主,覆盖环境搭建、依赖配置、单目相机驱动、标定执行到Moveit!集成的完整环节。内容预览中出现多个launch与.bak备份配置,便于读者在调试时对照原始设置,减少误改参数带来的重复劳动。目前已吸引5153人学习,适合正在搭建同类机械臂视觉系统、需要绕过环境配置坑的开发者。拿到后可直接复用功能包配置与标定流程,还能够参考目录内的分类结构与测试记录,快速定位问题、验证标定结果。 做机器人抓取和视觉分拣的朋友,应该都绕不开这个场景:Ubuntu下跑着ROS,机械臂是睿尔曼的协作臂,相机是Intel RealSense,任务是从ROS话题里拿到目标坐标,让机械臂准确抓过去。这套系统听起来不复杂,真正动手时会发现,坐标系对不上才是最大的坎——相机看到的点是在相机坐标系里,机械臂执行操作是在基座坐标系里,两个世界要对上,必须先做一次手眼标定。这篇文章我把从零开始配齐睿尔曼机械臂和RealSense、完成手眼标定的整个过程记录下来,包含具体的安装命令、标定流程和踩坑记录,适合正在做视觉抓取、机器人分拣和毕业设计的同学参考。
1. 先把整体思路盘清楚:机械臂和视觉之间差的是什么
1.1 这套系统到底解决什么问题
机械臂加视觉,核心是把“看得见”变成“够得着”。RealSense会把环境变成点云和深度图,但给出的坐标是基于相机自身坐标系;睿尔曼机械臂的运动学计算是基于基座坐标系。要完成抓取,必须计算出这两个坐标系之间的固定变换关系,这个变换就是手眼标定要解的东西。它的本质不是“把图像调清晰”,而是“把两个坐标系缝在一起”。
对于第一次搭这套系统的同学,我建议不要上来就调抓取算法,先把标定这步做扎实。标定不准,后面的识别、规划、抓取全都会飘,而且飘的方向还没有规律,排查起来非常痛苦。很多开源项目demo跑起来好看,一换到自己的工位上就失灵,问题多数出在标定这层,而不是算法本身。
1.2 先选好模式:eye-to-hand 还是 eye-in-hand
机械臂和相机有三种相对关系。相机固定在机械臂以外的工作空间上方,叫eye-to-hand;相机装在机械臂末端,跟着机械臂一起动,叫eye-in-hand。还有一种情况是相机装在第三方支架上但也会移动,工程上很少见,这里不展开。
从标定难度上讲,eye-to-hand的优点是相机不动,场景稳定,标定一次之后只要不碰相机就长期有效;缺点是机械臂移动时容易遮挡相机视野,标定和工作时要给机械臂留出足够的活动空间。eye-in-hand则相反,相机随末端移动,视野始终跟着目标走,适合结构紧凑的工位,但标定后相机与末端的相对位姿必须固定,固定支架一定要锁死,否则标定结果就是废的。
睿尔曼的协作臂加上RealSense深度相机,两种模式我都试过,最终项目里选的是eye-to-hand。原因很简单:D455这类相机放在侧面支架上,能看到整个抓取区域,而且标定板贴在机械臂末端,机械臂在相机视野里摆各种姿态时,不容易走到相机盲区。这篇文章按eye-to-hand的主流程写,但中间涉及的launch参数,eye-in-hand只需要改一个参数,我已经在对应小节里标出来。
1.3 硬件和版本怎么搭配
先确认你手上的RealSense是哪个型号。这几年的视觉抓取项目里,D415、D455、D405、D457都有人用,但它们的最佳工作距离差别很大,直接影响标定板尺寸和安装位置。我整理了一张表:
| 型号 | 深度工作范围(大概) | 适合场景 |
|---|---|---|
| D415 | 0.15m ~ 10m | 老款,适合中距离扫描,目前新项目不建议选 |
| D405 | 0.1m ~ 0.5m | 近距高精度,适合小目标检测,手眼标定时要放很小的板子 |
| D455 | 0.4m ~ 6m | 中距离抓取定位,深度精度高,目前最均衡 |
| D457 | 0.2m ~ 5m | 分辨率更高,适合精细特征识别,但算力要求也高 |
如果你的目标是放在桌面上抓取小物件,D405可能会更方便,因为它离目标很近也能出图,但标定板要用很小的尺寸,否则机械臂末端的标定板离相机太近,反而超出视野。我项目里用的是D455,放在工作台侧面,距离机械臂大概0.8米,标定板用大约10cm的ArUco板,识别和标定都比较稳。
2. 环境准备:Ubuntu、ROS和RealSense驱动三板斧
2.1 Ubuntu 20.04 与 ROS Noetic 的选择
这一套项目我建议锁在Ubuntu 20.04和ROS Noetic上。Ubuntu 22.04对应的ROS 2虽然也能用,但睿尔曼驱动和realsense-ros的老版本包很多还是围绕ROS 1写的,你不想在环境问题上浪费一个周末的话,就直接用20.04。装ROS的方式有很多种,最省事的是用网络上的“鱼香ROS一键安装”脚本,它把换源、依赖安装都封装好了,基本能做到一条命令装完。如果你更想手动装,就用国内源替换默认源,然后按官方wiki装ros-noetic-desktop-full。
装完以后,验证一下环境:
source /opt/ros/noetic/setup.bash roscore能正常起core,说明ROS本体没毛病。这时候再顺手把工作空间初始化一下,后面所有功能包都往这个空间里放:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws catkin_make2.2 librealsense和realsense-ros怎么装
RealSense要正常工作,先装librealsense驱动库,再装realsense-ros功能包。librealsense用Intel官方apt仓库装比较省心:
sudo mkdir -p /etc/apt/keyrings curl -sSf https://librealsense.intel.com/Debian/librealsense.pgp | sudo tee /etc/apt/keyrings/librealsense.pgp sudo add-apt-repository "deb https://librealsense.intel.com/Debian/apt-repo $(lsb_release -cs) main" sudo apt update sudo apt install librealsense2-dev librealsense2-utils装完用realsense-viewer打开相机,能把RGB和深度图同时显示出来,就说明SDK没问题。然后编译realsense-ros:
cd ~/catkin_ws/src git clone https://github.com/IntelRealSense/realsense-ros.git -b 2.3.2 cd ~/catkin_ws catkin_make注意分支别选错了。realsense-ros的2.3.x系列对应ROS Noetic,最新的main分支虽然也能编译,但依赖可能有变化。编译完以后,启动相机节点:
roslaunch realsense2_camera rs_camera.launch启动后可以用rqt_image_view订阅/camera/color/image_raw看彩色图,订阅/camera/depth/image_rect_raw看深度图。这一步能过,后面才有戏。
2.3 睿尔曼机械臂的ROS驱动接入
睿尔曼的机械臂,比如常见的RM65、RM75系列,官方提供了ROS驱动包,一般包含机械臂描述文件(URDF)、驱动节点和消息定义三个部分。你把仓库clone下来放进src目录编译一下,然后根据机械臂的通信方式启动。串口通信还是TCP通信,官方文档里会有明确说明,按实际接线和IP配置即可。
启动驱动以后,至少确认三件事:关节状态话题有数据、机械臂能响应运动指令、TF树里有从base_link到各关节的完整变换。尤其是TF树,没有它,手眼标定根本没法定,因为easy_handeye要拿机械臂末端的位姿来参与计算,机械臂驱动必须先把末端坐标发出来。
顺带说一个很多人纠结的问题:要不要自己算DH参数。如果你的方案里只是用官方驱动和URDF,完全不需要自己算DH。DH参数是描述机器人运动学的一种数学方式,官方URDF里已经包含了这些几何关系,驱动会帮你转成TF。只有在你要做Gazebo仿真、自己写正逆解或把机械臂模型移植到其他仿真框架时,才需要去查官方给的DH表,而且要特别注意单位——如果官方给的是毫米,你建模型时用了米,整个仿真就全是错的。
3. 相机内参检查与标定板准备
3.1 RealSense的内参到底要不要自己重标
很多教程上来就让你用棋盘格标相机内参,我觉得得分情况。RealSense出厂时是带内参标定数据的,在realsense-viewer里能看到每个相机的焦距、主点、畸变系数。如果相机不是磕碰过的工程机,这些内参在0.3-2米范围内做抓取定位完全够用。我自己做项目时,除非发现点云有明显的弯曲变形,或者要做毫米级测量,否则不会单独再标一遍内参。
不过有个点必须检查:在标定手眼之前,确认相机话题里的内参数据和相机实际一致。做法是在ROS终端里打印相机信息:
rostopic echo /camera/color/camera_info看width、height、K矩阵是否正常,如果K是全零或者畸变参数异常,说明相机节点没正常出内参,这个问题一般要先解决。
3.2 标定板的尺寸、打印和固定
手眼标定通常用ArUco码而不是棋盘格,因为ArUco可以单帧识别,而且easy_handeye默认支持ArUco。我建议从网上生成一张ArUco 4x4或者6x6的marker,打印尺寸控制在8-15cm之间。原则是:相机工作距离远,用大一点的码;工作距离近,用小一点的码。比如D455放在0.8米外,10cm的ArUco识别很稳,如果放到4cm,识别率就会明显下降。
打印和固定有四个坑要避开:
- 纸张不能软,最好贴到硬纸板或者亚克力板上,否则小幅弯曲会导致位姿估计误差直接放大;
- 打印出来后用尺子量一下实际边长,把量出来的值填进标定配置里。打印机普遍有缩放误差,填理论值等于自己给自己埋雷;
- 贴标定板时注意别让胶带覆盖到ArUco边缘,边缘一旦反光或被遮挡,检测到的角点就会偏移;
- 如果是eye-to-hand,标定板贴在机械臂末端时,尽量贴得和末端法兰面平行,而且方向固定好,不要每次换装都重新贴。
4. 手眼标定实操:从启动到拿到变换矩阵
4.1 启动相机、ArUco检测和机械臂驱动
在标定开始前,需要同时跑起三部分:相机节点、ArUco检测节点、机械臂驱动节点。相机用上一节的rs_camera.launch启动即可。ArUco检测可以用aruco_ros的单marker样例:
roslaunch aruco_ros single.launch markerId:=10 markerSize:=0.10 reference_frame:=camera_color_optical_frame这里markerId要和你打印的ArUco编号一致,markerSize填你实际量出来的边长(米)。reference_frame要填成realsense实际发布的彩色光学坐标系,通常对应camera_color_optical_frame。如果你发现识别出的marker位姿在RVIZ里到处乱跳,多半是reference_frame填成了camera_link或者camera_depth_optical_frame,坐标系本身带了旋转,解算出来的位姿自然就不对。
4.2 easy_handeye的launch怎么写
easy_handeye是这个任务里的核心工具。它通过机械臂末端位姿和相机观察到的标定板位姿,求解手眼方程AX=XB。可以简单理解为:机械臂换个姿态,标定板在相机视野里的相对变化和机械臂末端在世界里的变化,这两组变化量之间藏着一个固定的变换,解出这个变换就是标定结果。安装方式可以直接用apt装:
sudo apt install ros-noetic-easy-handeye我用的launch配置大概长这样(eye-to-hand模式):
<launch> <arg name="eye_on_hand" default="false"/> <node pkg="easy_handeye" type="easy_handeye" name="easy_handeye" output="screen"> <param name="eye_on_hand" value="$(arg eye_on_hand)"/> <param name="robot_base_frame" value="base_link"/> <param name="robot_effector_frame" value="ee_link"/> <param name="tracking_base_frame" value="camera_color_optical_frame"/> <param name="tracking_marker_frame" value="aruco_marker_frame"/> </node> <node pkg="easy_handeye" type="rqt_easy_handeye" name="rqt_easy_handeye"/> </launch>如果你做的是eye-in-hand,把eye_on_hand改成true,并且把tracking_marker_frame换成固定在工作台上的标定板的坐标系名,其他参数基本不用动。要注意的是,robot_effector_frame这个名字不一定就叫ee_link,有些机械臂的URDF里叫tool0或者flange,你要根据自己机械臂的TF树实际名称来填。
4.3 采样、计算和验证
rqt_easy_handeye启动后,界面里能看到相机的实时画面和marker识别结果。操作流程是:先让机械臂带着标定板动到第一个位置,确认marker在相机画面里居中、清晰,然后点击“Set current pose”,再点“Add sample”。接着继续换姿态,重复采样。
采样有两条硬性要求:
- 不要只让机械臂纯平移,姿态变化要明显,绕不同轴旋转都要有,否则标定方程会退化成奇异问题,算出来的结果看着合理实际全错;
- 每次采样时,机械臂停在当前位置要停稳,不要边动边采样,也不要采样瞬间还在振动。
我一般采12-15个点,耗时十分钟左右。点太少算出来不稳,点太多对工程时间不友好,12-15个点是经验上的平衡点。采样结束后点“Compute”,easy_handeye会把结果发布到TF树上,你可以直接看到camera和robot_base之间多了一个固定变换。
拿到结果后一定要做验证,别急着标定完就开抓。我的验证方法是:在相机视野里放一个相对较小的标记物,识别出它在相机坐标系的坐标,然后用标定出的变换转换到机器人基坐标系,再用手动示教把机械臂末端移到这个坐标附近,看看误差有多大。如果转换误差在1-2cm以内,标定基本合格,可以做普通抓取;如果大于3cm,建议重新检查采样姿态和marker尺寸。
5. 常见问题与排查技巧实录
5.1 RealSense在ROS里测不到深度
最典型的三个原因:USB带宽不够、多个RealSense同时接入占用了相同序列号、固件版本和librealsense不匹配。先用rs-enumerate-devices确认相机能被SDK发现,再用dmesg | tail看内核日志,看到usb相关报错就先换一个USB口,最好是主板原生USB 3.0。多个相机接入时,在realsense2_camera的launch里给每个相机分别指定serial_no。
5.2 机械臂驱动起来了但TF树不完整
如果RVIZ里看不到完整的base_link到ee_link,先检查机械臂驱动有没有加载URDF,很多驱动节点默认不会自动加载描述文件。驱动正常但缺少末端工具坐标时,要手动加一个静态变换发布器把工具坐标系挂到末端下面,easy_handeye里的robot_effector_frame就指向这个坐标系。
5.3 标定误差大或者结果一直发散
排查看两个方向:一是ArUco识别出来的marker位姿是否稳定,把marker固定在桌面上静止不动,打印位姿话题,如果数值波动很大,说明是光照、反光或实际尺寸问题;二是机械臂末端位姿是否准确,睿尔曼机械臂重复定位精度很高,但如果你让机械臂以较大的姿态角到达采样点,而机械臂本身没有做负载补偿,末端实际位置可能会和理论位置有偏差。
我把这些常见问题整理成了速查表:
| 问题 | 现象 | 常见原因 | 解决办法 |
|---|---|---|---|
| 相机无深度数据 | 彩色图正常,深度图全黑 | 强光干扰或相机距离目标过近 | 调整视角和距离,避免阳光直射,给深度图设短距离上限 |
| marker识别不到 | RVIZ里看不到识别框 | markerId填错、尺寸填错、离相机太远 | 核对打印的marker编号和实际边长,调整相机位置 |
| 标定结果漂移 | 每次Compute结果差异大 | 采样姿态单一、标记点太少 | 重采,加入旋转姿态,增加到12个点以上 |
| 转换误差大 | 验证时投影到机器人坐标偏差超3cm | marker贴纸不平、ArUco尺寸量错 | 重新贴平板,重新量实际尺寸 |
| TF树报错 | 找不到camera_color_optical_frame | 相机启动时disable了color话题或坐标系名不同 | rostopic list查看实际话题名,用tf_prefix统一 |
最后补充一个小技巧。标定完成之后,如果你后面还要做深度相机引导抓取,建议把标定出的静态变换保存下来,写成固定TF发布器写进启动文件里,不要每次启动都重新跑一遍rqt_easy_handeye,也不要在相机没挪动的情况下反复重标。我自己在项目里就把相机支架做了一个固定的限位卡槽,相机拆下来重装后可以直接卡回原位,标定结果能保持很久。这套流程跑通之后,后续再做视觉抓取,基本就是标准化的活了。
本文还有配套的精品资源,点击获取