☰
auboi5机械臂与D435i相机手眼标定实战:基于easy_handeye的完整指南
2026/10/8 2:49:15 网站建设 项目流程

1. 项目概述与核心需求解析

先介绍一下这个项目的整体情况。这里的核心任务非常明确:auboi5机械臂配合Intel RealSense D435i深度相机做手眼标定,使用了ROS社区里很常见的easy_handeye标定工具包。最终目的是搞清楚机械臂末端坐标系和相机坐标系之间的相对位姿关系,也就是求外参矩阵。只有把这个外参标准了,后面才能做视觉引导抓取、物体定位、避障这一系列下游应用。

很多刚接触机器人和视觉融合的工程师,一听到“手眼标定”头就大了。其实你把它拆开看,本质就是解决一个“我的手到底有多准”的问题。机械臂知道自己各个关节的角度,能算出末端执行器在世界坐标系的位姿;相机能看到目标物体,知道目标在相机坐标系的位姿。但这两个坐标系之间缺一个桥梁,桥梁就是我们要标定的外参。标定完了,相机“看到”的坐标才能换算成机械臂“够得着”的坐标,整套系统才真正闭环。

这个项目适合谁?第一类是刚入门机器人视觉、准备做抓取项目的研究生和工程师;第二类是已经在用easy_handeye但被各种报错和奇怪现象卡住的人。我会从原理、实操、踩坑三个层面完整梳理,把我在实际标定过程中记录的问题、排查思路和最终解决方案都拿出来分享。标定这件事,看似容易——把标定板往那一放,采十几组数据跑一下就好了——但真正上手你就会发现,坑全藏在细节里:机械臂和相机的驱动冲突、TF树不完整、数据采集时手抖了一下导致结果偏差、aruco码识别率低……每一个都能让你折腾一整天。

我用的环境是Ubuntu 18.04 + ROS Melodic,机械臂是遨博auboi5协作机械臂,相机是Intel RealSense D435i。这套组合在实验室里非常常见,所以如果你和我的环境接近,直接对照操作就行。

1.1 两种手眼模式的核心区别

在手眼标定里,首先要搞清楚你的系统属于哪种结构模式。很多人在这一步就懵了,因为easy_handeye 支持两种模式:eye-in-hand和eye-to-hand。

  • eye-in-hand(眼在手上):相机安装在机械臂末端,跟着机械臂一起动。标定板固定在世界某个位置不动。这种方案适合机械臂需要“凑近看”的场景,比如抓取时相机要移到目标上方。
  • eye-to-hand(眼在手外):相机固定安装在外部支架上,机械臂末端装标定板,用机械臂带着标定板在相机视野里摆姿势。这种方案适合监测全局场景,比如工作台面整体监控。

auboi5配D435i最常见的组合是eye-in-hand,因为D435i体积小、重量轻,挂在机械臂末端不构成多大的负载负担。我这次也是用 eye-in-hand 模式来做的。

很多人问,两种模式选哪种求解算法不一样吗?其实easy_handeye底层调用的都是经典的Tsai-Lenz或者Park-Martin之类的AX=XB求解方法。公式很直观:机械臂末端的位姿变化(A)乘以相机外参(X),等于相机位姿变化(B)乘以外参(X)。求解这个矩阵方程,X就是我们想要的外参。你只要记住一点:不管哪种模式,输入的都是机械臂末端位姿和相机观测到的标定板位姿,只是数据的采集方式不同。

1.2 为什么选择easy_handeye工具包

市面上手眼标定工具有不少,ROS里还有 handeye_calibration_calibrator,商用软件里也有VisionPro、Halcon之类的标定模块。为什么我推荐easy_handeye?有几点实际原因。

第一,它和ROS生态无缝集成。它基于rviz可视化界面操作,能实时显示标定板的检测状态、机械臂和相机的TF树关系。你不需要自己去写数据记录和求解的程序,采集够一定数量的点位后,一键就能算出结果。

第二,它支持aruco码和棋盘格两种标定板。aruco码的优势是单张图片就能提供完整的位姿信息,且检测鲁棒性非常好;棋盘格的优势是角点精度高,但需要所有角点完整可见才能解算位姿。实际操作中aruco码方便很多,因为机械臂运动时会抖动,棋盘格容易被部分遮挡导致检测失败。

第三,有图形化的数据管理。它能保存每一次采集的机械臂位姿和相机观测位姿,方便排查哪些数据点是离群值。这一点非常重要,因为手眼标定对数据质量极其敏感——一两组差数据就能让最终结果偏移好几毫米。

2. 环境准备与工具链部署

2.1 驱动安装与版本兼容性

在开始标定之前,环境配置是第一道坎。auboi5需要使用遨博官方提供的ROS驱动包,D435i则要用realsense-ros包。这两个东西单独用都没问题,但放在同一个ROS工作空间里编译时,偶尔会遇到依赖冲突。

我的建议是使用独立的ROS工作空间分别编译,然后用source叠加的方式加载。比如主工作空间是catkin_ws,存放auboi5的驱动和各种自定义功能包;另一个工作空间realsense_ws专门放realsense-ros和相关依赖。这样两边互不干扰,编译顺序也不会出问题。

具体编译步骤大致如下:

# 编译auboi5驱动工作空间 cd ~/catkin_ws catkin_make source devel/setup.bash # 编译realsense工作空间 cd ~/realsense_ws catkin_make source devel/setup.bash # 每次新开终端时把两个工作空间都source上 echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc echo "source ~/realsense_ws/devel/setup.bash" >> ~/.bashrc

有个细节需要注意:easay_handeye 这个包最好放在主工作空间里编译,因为它需要依赖你定义的机械臂URDF模型、move_group等接口。编译前先确认能不能正常打开rviz并显示auboi5的模型。

2.2 标定板的制作与内参准备

标定板是精度的重要来源之一。easy_handeye默认支持aruco码,你不需要自己生成,它会在aruco_ros包里提供现成的标定板,但你也可以自己用在线生成器生成一个PDF打印出来。打印时有几个注意点:

  • 纸张要平整,最好贴在硬质塑料板或者亚克力板上,别直接贴在纸板上。翘边会导致位姿解算偏差。
  • 一格的实际边长要量准。标定板参数里的marker_size是单格的边长,如果量错1毫米,最终外参会偏出好几毫米。我习惯用游标卡尺量三个不同位置的格子取平均值,确保误差在0.1毫米以内。
  • 分辨率要高。用激光打印机打印,不要用喷墨,喷墨容易晕染导致边缘模糊。

相机内参这块,easy_handeye不会替你做,需要提前单独标定。D435i虽然有出厂内参,但为了精度还是建议用ROS的camera_calibration工具重新标一遍。标定方法很成熟:打印一张棋盘格,用roslaunch realsense2_camera rs_rgb.launch打开彩色流,然后用camera_calibration节点标定rgb内参。

rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.025 image:=/camera/color/image_raw camera:=/camera/color

这里--size 8x6是内角点数,--square 0.025是棋盘格每格边长(单位米)。采集足够多不同姿态的图片后点击CALIBRATE,系统会自动生成内参矩阵和畸变系数。务必把标定结果保存下来,后面在easy_handeye的launch文件里直接指定内参文件路径,否则默认内参不准会导致标定结果偏差。

2.3 easy_handeye安装与launch文件配置

easy_handeye 在ROS Melodic下的源码安装很直接:

cd ~/catkin_ws/src git clone https://github.com/ros-vision-ros/easy_handeye.git cd .. rosdep install --from-paths src --ignore-src -r -y catkin_make

安装完成后,最关键的是配置launch文件。easy_handeye提供了两种标定启动方式:handeye_calibration.launch和handeye_calibration_target.launch。前者用于eye-in-hand(相机在手上),后者用于eye-to-hand(相机在外)。我们这里用的是前者。

launch文件里需要改几项核心配置:

<launch> <!-- 相机组 --> <arg name="camera_namespace" default="/camera" /> <arg name="camera_color_topic" default="/camera/color/image_raw" /> <arg name="camera_info_topic" default="/camera/color/camera_info" /> <!-- 标定板 --> <arg name="marker_size" default="0.032" /> <arg name="marker_id" default="100" /> <!-- 机械臂组 --> <arg name="robot_urdf" default="$(find my_aubo_description)/urdf/auboi5.urdf" /> <arg name="robot_base_frame" default="base_link" /> <arg name="robot_effector_frame" default="tool0" /> <arg name="callback_thread" default="2" /> </launch>

marker_size你务必填标定板单格的实际边长,单位是米。marker_id要和标定板上的id对应。robot_effector_frame填auboi5的末端工具坐标系,遨博默认一般是tool0,如果你修改过TCP记得要用自建的坐标系名。

3. 标定实操流程与关键环节

3.1 启动完整标定链路

所有驱动准备好之后,需要一个接一个地启动节点。启动顺序有讲究,我踩过几次坑之后总结了一套标准流程:

第一步,启动机械臂驱动和move_group:

roslaunch aubo_driver aubo_driver.launch robot_ip:=192.168.1.100 roslaunch aubo_moveit_config move_group.launch

注意 aubo_driver 的IP要改成你实际机械臂控制柜的IP地址。move_group不是标定必需的,但如果你希望在rviz里拖拽示教规划运动,还是建议开着。

第二步,启动D435i相机:

roslaunch realsense2_camera rs_camera.launch

如果你发现彩色图像有延迟或异常,可以在launch里加align_depth:=true开启深度对齐,但标定只需要彩色图像,所以实际不必开。

第三步,启动easy_handeye标定节点:

roslaunch easy_handeye handeye_calibration.launch

启动后rviz里会加载机械臂模型和一个标定板跟踪窗口。如果一切正常,你能在rviz中看到相机图像上叠加了aruco码的坐标轴。

3.2 数据采集的策略与技巧

这一步是整个标定过程中最考验耐心的环节。easy_handeye的工作流程是:你控制机械臂运动到某个位姿,让标定板出现在相机视野的某个位置,然后在界面上点击“Plan”规划末端运动、点击“Execute”执行、之后点击“Take Sample”采样。重复十几二十次,最后点击“Compute”求解。

我第一次做这个标定,随便采集了十几个点,运动范围很小,结果外参误差有2厘米还多。后来总结了几个关键策略,标定误差直接降到了毫米级:

  • 覆盖整个视野:不要让标定板一直待在画面中心。要让它在画面的九个区域都出现过(左上、中上、右上、中间、左下、中下、右下等),这样能充分约束相机的所有内参数和外参数。
  • 变动姿态,而非位置:机械臂末端除了平移变化,一定要大幅改变姿态(即改变末端绕x、y、z轴的旋转角)。如果姿态不变,AX=XB方程在旋转分量上自由度不足,解出来会很飘。
  • 标定板与相机平面夹角要有一定倾斜:不要总让标定板正对相机(夹角约30度、45度都可以),倾斜姿态能提供更多有效约束。
  • 运动要平滑,采集要稳定:机械臂运动到位后,等模型在rviz中完全稳定下来再点击take sample。机械臂停止后其实还有微小抖动,最好加一个短暂延时。

实际操作中,我一般采20到25组数据,每一组都换一个姿态,确保数据点分布足够分散。

3.3 求解参数验证与结果判断

全部数据采集完成后,点击“Compute”,easy_handeye会弹出求解结果,并按四种算法分别输出。常用的是OpenCV(Tsai-Lenz)、OpenCV(Park-Martin)、MATLAB(Danillidis)等。界面会显示每个算法的误差,通常以像素为单位。

我个人习惯取OpenCV的Tsai-Lenz结果作为最终外参,再手动验证一下:用rviz里的“Publish”按钮把外参发到TF树,然后让机械臂末端移动到标定板上方某个点,用相机检测标定板中心位置,换算到机械臂坐标系下,和实际末端位置对比。

简单验证命令:

# 发布外参到TF rosrun tf2_ros static_transform_publisher x y z yaw pitch roll camera_link tool0

然后看机械臂末端在rviz中是否“贴”在标定板的中心上。误差小于5毫米基本可接受,小于2毫米算非常好了。

还有一种快速验证方法:让机械臂带着相机在不同距离观察同一个点,外参如果正确,该点在机械臂坐标系下的坐标应基本不变。如果位置漂移很大,说明外参不准,需要重新标定或者排查数据质量。

4. 常见问题与排查技巧实录

4.1 aruco码检测不稳定

这是最常见的现象:标定板明明在视野里,但rviz里就是显示不出坐标轴,或者检测框一直在闪烁。排查步骤从易到难:

  • 检查marker_size是否和实际尺寸一致。实际尺寸30mm,但你参数填了32mm,检测虽然能解算,但位姿会整体放大或缩小。
  • 把相机分辨率降低到640x480再试试。D435i在1280x720下aruco检测对模糊和反光非常敏感,降分辨率之后识别率明显上升。
  • 检查光环境,过于强烈的直射光或反光会让边缘提取失效,尽可能用柔和的漫射光照明。

4.2 标定结果跳变异常大

如果你两次标定结果差了好几厘米,大概率是以下几个原因:

问题现象可能原因解决方案
两次标定结果差5cm以上机械臂URDF中的tool0定义错误检查robot_effector_frame是不是真正的TCP
误差指示小但实际验证偏差大数据点姿态变化不够增加大幅旋转姿态的数据点
每次Compute结果略有漂移数据点采集时末端有抖动运动完成后等待2-3秒再采样
验证时机械臂碰不到目标相机内参标定偏差大重新标定相机内参并加载正确文件

还有一个容易被忽略的问题:机械臂的轨迹插值方式。有些机械臂默认走关节空间插值,末端路径可能画弧线,但如果采集时刻末端位姿是通过URDF正确读出的,就不影响标定结果。反而是如果你在机械臂运动中添加了工具坐标系补偿(比如末端装了气爪但没改TCP),会造成系统性偏差。

4.3 TF树不完整导致变量报错

easy_handeye运行时报"Lookup would require extrapolation into the past"这类TF相关的错误,很常见。这说明它需要的坐标变换关系中某个父系坐标没连上。

排查方法:

rosrun tf2_tools view_frames.py

生成TF树PDF,重点看base_link->tool0这条链是否连续,以及camera_link是否有父系。如果没有,你需要给相机模型添加一个静态TF(比如camera_link挂载到tool0下,就算初始外参未知也先用零值占位),这样easy_handeye才能构建完整的变换链。

<node pkg="tf2_ros" type="static_transform_publisher" name="camera_tf" args="0 0 0 0 0 0 tool0 camera_link" />

4.4 求解报错但数据看起来正常

有几次我遇到点击Compute之后报"Not enough valid samples"或者解算失败。这时需要检查是否有离群数据点。easy_handeye界面里可以逐条查看数据,对应每一条数据你能看到重投影误差。

一个很实用的操作:把误差明显偏大的那几组数据删掉重新计算。因为机械臂在某个位姿可能抖动太大,或者标定板被部分遮挡,检测出的位姿质量差。保留的数据点不用多,15到20组干净数据的效果远好于30组里混了几个脏数据的结果。

4.5 标定板坐标系与机械臂坐标系不一致

最后想强调一点:标定板放在桌上时,它的坐标原点在世界坐标系的哪个位置其实无所谓,因为手眼标定求的是两个坐标系之间的相对变换,不是绝对位置。但如果标定板在采集过程中移动了位置(比如被碰了一下),那这组数据就废了。所以实际操作中,标定板一定要固定好,建议用重物压住或用双面胶固定在工作台上。

5. 精度验证与后续扩展思路

5.1 用实际抓取验证标定结果

标定完成后,纸上算出来的误差再小,最终还是要看实际能不能用。我习惯做一个简单的引导抓取测试:在工作区放置一个已知尺寸的方块,用D435i识别它的中心坐标,然后通过标定得到的外参换算到机械臂坐标系下,控制auboi5末端移动到该坐标。如果抓取位置偏移不超过5毫米,这个标定结果就可以放心投入使用了。

如果发现偏移呈线性增长(比如距离相机越远偏差越大),通常问题出在相机内参上,需要回头重新标内参。如果偏移是固定的大约几厘米,那大概率是外参的平移分量有误,可能是数据采集时标定板没固定好,或者机械臂TCP和URDF定义不一致。

5.2 从手眼标定到视觉伺服

标定完成之后,你获得的不仅仅是一组外参数值,更是一条完整的视觉-机械臂闭环通路。接下来可以做的事情非常多:

  • 基于D435i的深度信息,实现目标物体3D定位与抓取;
  • 配合MoveIt的规划能力,做动态避障和轨迹规划;
  • 加一个物体检测模型(如YOLO),把检测框的中心像素坐标通过相机内参和外参映射到机械臂坐标系,实现“识别-定位-抓取”全流程自动化。

我个人在实际操作中的体会是,手眼标定就是一个不断和误差做斗争的过程,所有误差源都不可忽略:相机内参不准、标定板不平整、机械臂末端抖动、数据采集覆盖不够。但只要你耐下心把每个环节逐一排查干净,标定精度完全可以达到毫米级,后面做视觉引导项目会省心非常多。最后再分享一个小技巧:标定完成后,把外参写进URDF的xacro文件里,替换掉原来占位的tf树,之后每次启动都不用重新发布静态外参,整个系统干净利落。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询