ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

UR机械臂+Realsense深度相机手眼标定完整指南(ROS)

UR机械臂+Realsense深度相机手眼标定完整指南(ROS) 做机器人抓取或者视觉定位最绕不开的一步就是手眼标定。UR机械臂配Realsense深度相机这套组合在实验室和工业现场都很常见但很多人在标定阶段就被卡住了——要么标出来的结果误差大得离谱要么压根不知道该采哪些数据、数据怎么用。我前前后后给几套UR5、UR10和D435i、D415都做过手眼标定踩过的坑不少这次把完整流程和关键细节一次性理清楚。这套方案解决的是相机看到的物体位置怎么换算成机械臂能抓的坐标这个核心问题。适合正在做视觉抓取、视觉定位、移动抓取项目的开发者参考也适合刚接触ROS和机械臂、想走通一遍标定流程的新手。内容会覆盖原理、环境准备、实操步骤、数据保存和结果验证全部基于ROS Noetic Ubuntu 20.04UR机械臂仿真和真机都适用。1. 手眼标定核心概念与方案选型1.1 为什么机械臂和相机必须建立变换关系机械臂自己只知道我的末端在基座坐标系下的位姿相机只知道目标物体在相机坐标系下的位置。这两个坐标系之间没有直接联系机器人就没办法根据相机看到的目标位置去规划抓取路径。手眼标定干的事情就是求解相机坐标系和机械臂基座坐标系之间的变换矩阵让相机看到的像素坐标能够换算成机械臂基座坐标系下的三维坐标。这个过程里有两个坐标系变换关系是已知的一是机械臂基座到工具末端的变换可以直接从机械臂控制器的正运动学读取二是相机到标定板的变换可以通过检测标定板的特征点并解PnP问题得到。手眼标定要算的就是机械臂基座到相机或者相机到机械臂末端的那个固定变换。这里要理解一个核心逻辑标定板放在机械臂的工作空间内位置固定不动机械臂末端带着或者对着相机从不同角度观察标定板。换句话说机械臂动、标定板不动、相机跟着动或者固定不动三种情况组合出两种标定模式。1.2 eye-in-hand与eye-to-hand两种模式的本质区别根据相机安装位置手眼标定分为两大类。第一种是眼在手上eye-in-hand相机固定在机械臂末端法兰盘上随着机械臂一起运动第二种是眼在手外eye-to-hand相机固定在外部支架上工作过程中不移动。这两种模式下待求的变换关系完全不同。眼在手上要求解的是相机坐标系到机械臂末端坐标系的变换cam到end因为相机跟随末端移动这个变换是固定不变的眼在手外要求解的是相机坐标系到机械臂基座坐标系的变换cam到base相机固定时这也是一个固定值。实操中怎么判断自己该用哪种看你的应用场景如果是机械臂末端安装吸盘或夹爪相机装在末端朝下拍工作台上的工件那就是典型的眼在手上如果是相机架在工作区域正上方或者斜上方机械臂在相机视野内活动那就是眼在手外。两种模式标定的流程也有差异。眼在手上时标定板要放在机械臂前方固定位置机械臂运动到不同姿态拍摄标定板眼在手外时标定板通常是固定在机械臂末端机械臂带着标定板运动让外部相机从不同角度拍摄。注意这个区别很多人一开始会搞混导致采集数据全部作废。1.3 AXXB方程与最小二乘求解思路不管哪种模式手眼标定在数学上都归结为求解AXXB形式的矩阵方程。这个方程的意义是机械臂两次运动之间的齐次变换矩阵A与相机两次观察标定板之间的齐次变换矩阵B通过未知的手眼变换X构成一个闭环约束。具体来说对眼在手上的情况假设机械臂末端在两个位置之间的相对变换是A可以由正运动学算出来相机在两个位置观察同一块固定标定板的相对变换是B可以由检测标定板并解PnP得到那么满足AXXB。每两次不同姿态的运动就能提供一个这样的方程多次运动后构成超定方程组再用李群李代数和最小二乘方法求解最优的X。为什么至少要采集两次不同姿态因为一次位置只能建立一个约束方程两个未知数旋转和平移解不出来。实际工程中要求采集8到12组不同姿态的数据姿态差异还要足够大否则矩阵病态、求解结果不稳定。我习惯每次采集之间让机械臂的末端姿态至少改变30度以上确保旋转部分的约束充分。1.4 标定工具选型与对比ROS生态里做手眼标定主流的工具是easy_handeye另外还有OpenCV自带的calibrateHandEye函数、Visp手眼标定模块。我从易用性和维护活跃度两个维度对比一下。工具适用场景优点缺点easy_handeyeROS环境URRealsense组合集成度高RViz可视化直接发布TF依赖aruco/棋盘格检测需要配置launch文件OpenCV calibrateHandEye纯算法验证非ROS项目接口简单支持多种求解方法需要自己管理输入输出数据格式Visp hand-eye视觉伺服项目精度高鲁棒性强配置繁琐学习曲线陡我推荐直接用easy_handeye因为它专门为ROS机器人设计自带标定板的检测、位姿采集、求解、结果发布全套流程而且和UR机械臂的MoveIt驱动接口能无缝对接。数据采集过程中还能在RViz里实时看到相机检测标定板的结果排查问题直观得多。2. 环境准备与驱动配置细节2.1 ROS环境搭建与鱼香ROS一键安装标定工作依赖ROS环境目前最稳的组合是Ubuntu 20.04 ROS Noetic。UR机械臂官方驱动ur_robot_driver和Realsense的realsense-ros都完整支持这个组合。如果你是从零开始搭建环境ROS安装这一步很容易劝退新手——那么多依赖包、换源、初始化、环境变量每一步都可能出错。我实测下来比较推荐鱼香ROS的一键安装脚本它在国内网络环境下非常省心。安装命令就一行wget http://fishros.com/install -O fishros . fishros执行后它会弹出功能选项菜单选择ROS安装对应的选项再选择NoeticUbuntu 20.04版本即可。脚本会自动处理软件源、依赖、环境变量和命令行补全整个过程大概十几分钟比手动逐个apt install靠谱很多。需要注意安装过程中它会询问是否添加rosdep建议选是后面编译工作空间经常需要它来安装依赖。提示如果你的Ubuntu版本是22.04对应的ROS发行版是HumbleUR官方驱动也有支持但教程里很多launch文件和依赖包名称会有差异。稳定起见标定这种精细活建议先别追新用20.04 Noetic的组合最省事。2.2 UR机械臂驱动与仿真环境搭建UR机械臂以UR5为例在ROS中的驱动方案是ur_robot_driver配合MoveIt。真机连接时这个驱动通过Ethernet与机械臂控制柜的Dashboard接口通信可以读取关节状态、发送运动指令、控制机械臂使能和释放。如果没有真机也可以在Gazebo仿真环境里先跑通整个标定流程。ur_robot_driver本身不直接支持Gazebo需要额外安装ur_gazebo和ur_description这两套描述包。我建议先仿真后真机仿真环境里即使标定错误也不会损坏设备适合验证流程。ur_robot_driver的安装sudo apt install ros-noetic-ur-robot-driver要启动仿真环境通常会配合使用ur5e_moveit_config这类配置包。运行仿真机械臂的launch命令大致是roslaunch ur_gazebo ur5e_bringup.launch roslaunch ur5e_moveit_config moveit_planning_execution.launch sim:true启动之后MoveIt的RViz界面里会有一个可以拖拽规划、执行运动的UR5E模型仿真状态下手眼标定流程跟真机完全一致。这里有个实操心法先用仿真把easy_handeye的采集流程完完整整走一遍搞清楚每一步的输入输出再上真机能帮你省下大量现场调试时间。2.3 Realsense相机驱动与ROS接口配置Realsense D435i或者D415在ROS下使用需要装两个层面的东西底层硬件驱动librealsense和ROS封装层realsense-ros。sudo apt install ros-noetic-realsense2-camera这个包装了之后可以通过launch文件启动相机节点发布彩色、深度和IMU话题。推荐先用官方的rs-enumerate-devices命令确认固件版本和序列号识别正常再启动ROS节点。roslaunch realsense2_camera rs_camera.launch默认launch会发布/camera/color/image_raw、/camera/depth/image_rect_raw等话题。手眼标定模式下我们需要的是彩色图像话题/camera/color/image_raw和对应的相机内参/camera/color/camera_info。这两个话题在easy_handeye中直接通过ROS话题名引用。2.4 easy_handeye安装与依赖检查easy_handeye在ROS Noetic下可以直接用apt安装也可以从源码编译。源码编译的好处是可以拿到最新的bug修复缺点是编译时间稍长。我这里说apt方式sudo apt install ros-noetic-easy-handeye它会一起装上aruco检测相关的依赖库。需要确认一下aruco_ros、visp_hand2eye_calibration这些依赖包是否被正确解析。注意easy_handeye在不同发行版中的launch文件组织方式略有不同。Noetic版本中一般是eye_on_hand和eye_on_base两个launch文件分别对应眼在手上和眼在手外模式后面实操章节我会具体展开。3. 手眼标定完整实操流程3.1 标定板的选择与生成easy_handeye默认支持aruco码标定板推荐使用aruco板而不是棋盘格因为aruco码在图像中更容易被检测、每个码自带ID、部分遮挡时鲁棒性更好。生成aruco标定板的方式很多我用的是easy_handeye仓库自带的生成脚本。rosrun aruco_ros create_marker 0 # 生成单个ID0的aruco码不过单个aruco码在标定中容易因为视角过大导致检测失败我更推荐打印一张4x4或5x5的aruco标定板。可以用以下Python脚本生成并保存为PDF打印import cv2 import numpy as np aruco_dict cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50) board cv2.aruco.GridBoard_create(5, 7, 0.04, 0.02, aruco_dict) img board.draw((840, 594), marginSize20) cv2.imwrite(aruco_board.png, img)关键参数说明GridBoard_create前两个参数是标定板的格子数量7列5行0.04是每个格子边长米0.02是码与码之间的间距米。你打印之后需要测量实际物理尺寸把这两个值改成你打印纸上的真实值。注意标定板一定不要用普通的A4纸直接放桌上最好贴在硬纸板或者亚克力板上保证表面平整。标定板弯曲是标定精度杀手误差甚至能达到厘米级。3.2 相机内参确认手眼标定的输入数据包含相机的内参矩阵如果内参不准确后面PnP求解的位姿全是歪的手眼标定结果也会带上同样偏差。Realsense出厂时自带内参校准正常使用可以直接读取/camera/color/camera_info话题里的内参。启动相机后查看内参的方式rostopic echo /camera/color/camera_info输出的K矩阵就是内参。如果发现内参异常比如焦距、主点偏移过大则需要用ROS的camera_calibration包重新标定一次。rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.108 image:/camera/color/image_raw camera:/camera/color注意这里的棋盘格内角点数和格子实际边长要填准确。如果用的是Realsense出厂内参通常精度够用一般不推荐自己重新标定因为需要打印很标准的高精度棋盘格普通打印机的精度反而可能引入误差。3.3 眼在手外eye-to-hand标定全流程眼在手外模式适合相机固定、机械臂运动的工作场景。整个标定的目标是通过easy_handeye求解相机坐标系到机械臂基座坐标系的变换关系。这里我把完整操作拆解成步骤。第一步确认机械臂和相机节点正常启动。真机场景roslaunch ur_robot_driver ur5e_bringup.launch robot_ip:192.168.1.100 roslaunch realsense2_camera rs_camera.launch如果使用仿真环境则分别启动ur_gazebo和moveit相关launch文件。第二步启动easy_handeye的眼在手外配置roslaunch easy_handeye publish.launchpublish.launch实际上是发布标定板TF用的有些版本放在easy_handeye的示例launch里。这一步要确保相机能够检测到并发布标定板的TFTF名称通常类似/aruco_marker或/target。第三步启动手眼标定主程序rosrun easy_handeye eye_on_base_calibrate.py运行后会弹出easy_handeye的GUI窗口同时显示相机图像和标定检测结果。如果检测到标定板画面中会出现aruco码的边框和坐标轴。第四步规划机械臂运动到初始化位姿。在MoveIt的RViz界面中拖动机械臂模型到能看到标定板的初始位置执行规划并让机械臂运动过去。这个初始位置建议让标定板在图像中央附近标定板的姿态不要太倾斜法线与相机光轴夹角不要超过45度。第五步在easy_handeye GUI中点击Plan按钮。它会自动计算下一步机械臂的运动目标让机械臂以一个新的姿态观察标定板。检查规划结果没有碰撞后点击Execute执行运动。第六步运动完成后确认相机画面中的标定板处于有效检测状态然后点击Take sample采集一组数据。每组数据包含机械臂的位姿和相机检测到的标定板位姿。重复这个过程8到12次。这里有个关键技巧机械臂每次运动的姿态差异要大一点不要只在同一姿态附近微调。姿态变化不够会导致求解出的旋转矩阵分量条件数差标定结果看起来每个姿态都合理但不同初始值解出的结果差异很大。第七步采集完所有样本后点击Compute按钮计算标定结果。easy_handeye会输出一个齐次变换矩阵并保存到.yaml文件里。得到的文件路径会在输出信息中显示通常是~/.ros/easy_handeye/eye_on_base_xxx.yaml。这个yaml文件里包含旋转四元数和平移向量是最终要用的标定结果。它的意义是相机坐标系在机械臂基座坐标系中的位姿也就是从相机坐标变换到机械臂基座坐标的变换矩阵。3.4 眼在手上eye-in-hand标定流程差异眼在手上模式和眼在手外模式流程上几乎相同区别在于标定板放在固定位置机械臂末端带着相机从多个角度观察标定板。启动方式改变为roslaunch easy_handeye publish.launch rosrun easy_handeye eye_on_hand_calibrate.py以及对应地相机固定在机械臂末端时标定过程中的数据采集逻辑是机械臂运动、相机跟着运动、标定板不动。计算出的结果是相机坐标系在机械臂末端坐标系下的变换。3.5 标定结果验证方法标定完不能直接拿去用必须先验证。验证方法很简单让机械臂运动到工作空间内的另一个位置用标定得到的结果将相机检测到的标定板中心点坐标变换到机械臂基座坐标系再和机械臂正运动学中已知的标定板真实位置对比。如果想在RViz里直观验证可以在终端用rostopic查看当前相机检测的标定板位置然后用TF工具将坐标系转换到base_link看是否与标定板的实际位置重合。误差在几毫米以内算是正常范围如果误差超过1厘米那这个手眼标定结果基本不可用需要重新再来。此外可以检查一个数据点将相机检测到的标定板中心变换到机械臂基座坐标系后与机械臂示教器上显示的工具中心点TCP位置做比较。标定板如果正好在机械臂末端下方两者的距离应该约等于标定板的厚度和安装偏移量偏差过大说明标定结果有系统误差。4. 标定常见问题与精度专项排查4.1 标定结果跳变与不稳定的原因如果连续两次标定算出来的结果差异很大或者每次加入新的数据样本后结果明显跳变大概率是机械臂运动规划时姿态变化不够导致AXXB方程的病态约束。我在一次UR10标定中遇到过前6组数据姿态变化只有5度左右算出来的平移量前后相差5厘米后来加大姿态变化到30度以上重新采集结果就稳定在2毫米以内。数据采集时机械臂还可能出现温飘问题尤其是真机连续运行较长时间后关节间隙和减速器温度变化会导致正运动学计算出的末端位姿与实际不一致。解决办法是采集过程中不要拖太久一次标定采集尽量在5到10分钟内完成同时让机械臂预热一段时间再开始标定让关节温度进入稳定状态。4.2 aruco码检测失败的处理相机画面中看不到标定板的识别边框这是新手最常见的卡点。排查路径分三步先检查图像话题是否正常/camera/color/image_raw里有没有画面再检查标定板曝光和清晰度Realsense在强光下容易过曝aruco码边缘泛白会导致识别失败这时候适当降低相机的曝光时间或者用遮挡物避免直射光——我见过不少用D435i的小伙伴直接对着窗户光标定怎么都识别不出来拉上窗帘就好了最后检查标定板尺寸参数是否和打印出来的实际尺寸匹配如果格子边长填错了PnP求解出来的位姿会非常不稳定。如果镜头离标定板太近导致aruco码超出视野或者太远导致码区域过小小于30像素也会检测失败。4.3 坐标变换与TF树连接问题标定完成后如果发现坐标变换发布不正常要检查TF树是否完整连接。正常的TF树应该有如下链路base_link - (眼在手外标定结果) - camera_color_optical_frame如果是眼在手上则是base_link - tool0 - (眼在手内标定结果) - camera_color_optical_frame可以运行rosrun tf view_frames生成TF树图来检查连接情况。常见的问题是相机的TF名称不对Realsense发布的相机光学坐标系名称通常是camera_color_optical_frame而不是camera_link使用easy_handeye的launch时需要在配置中指定正确的相机TF名称否则标定结果发布时TF树会断开。4.4 精度评估的量化指标手眼标定的精度不能光靠感觉建议做一次量化评估。选定工作空间内3到5个不同的验证点让机械臂末端或固定标定板移动到每个点记录相机检测到的位置和机械臂示教器显示的实际位置计算两者误差验证点X方向误差(mm)Y方向误差(mm)Z方向误差(mm)综合误差(mm)点11.20.81.52.1点20.91.11.31.9点32.11.82.53.8综合误差超过5毫米时需要重新标定或者检查机械臂末端负载是否导致正运动学误差增大。有个经验数据UR系列机械臂本身重复定位精度是±0.1毫米级别手眼标定的误差通常主要来自相机标定板检测误差和标定方程求解误差正常标定结果综合误差在2到4毫米是比较常见的水平。注意标定结果与机械臂的工作区域有关。UR机械臂在工作空间边缘区域的正运动学误差比中心区域大所以标定时的采样位置尽量覆盖实际抓取任务中会用到的工作区域不要只在机械臂正前方的一个小区域采集数据。4.5 真机标定前的安全检查如果使用真机标定安全第一位。我的习惯是先在空载状态下用慢速MoveIt中设置最大速度比例0.2做一次完整的走点流程确认机械臂的运动路径上没有障碍物和人员再正式开始采集数据。采集过程中操作人员应该站在机械臂工作空间之外手边随时能摸到急停开关。UR控制柜的示教器上也要提前设置好安全停止的I/O配置。切记不要在机械臂运动过程中伸手调整标定板等机械臂停稳后再调整位置。还有一个细节真机标定时机械臂运动到某些极限姿态时可能出现奇异点导致MoveIt规划失败。遇到这种情况可以手动调整一下初始位姿再重新规划或者适当降低目标位置离奇异点的距离。5. 标定结果在视觉抓取项目中的应用标定完成后手眼变换关系要接入到视觉抓取或者视觉定位的完整流程中。这里以眼在手外的模式为例说明标定数据怎么用。相机检测到目标物体的像素坐标通过深度图获得物体在相机坐标系下的三维坐标然后通过标定得到的齐次变换矩阵变换到机械臂基座坐标系最终通过MoveIt规划机械臂移动到目标位置。核心代码思路如下用C和ROS的tf库实现tf::TransformListener listener; tf::StampedTransform cam_to_base; // 等待并获取标定得到的变换 listener.waitForTransform(base_link, camera_color_optical_frame, ros::Time(0), ros::Duration(3.0)); listener.lookupTransform(base_link, camera_color_optical_frame, ros::Time(0), cam_to_base); // 目标在相机坐标系下的位置 tf::Vector3 pos_in_cam(x, y, z); tf::Vector3 pos_in_base cam_to_base * pos_in_cam;如果用Python写也是一样的逻辑用tf2_ros.Buffer接口查询变换然后把相机坐标点通过变换矩阵映射到机械臂基座坐标系。这段代码的本质就是把标定结果变成运行时的一个坐标变换关系。需要注意的是标定得到的yaml文件要加载到参数服务器并且在程序初始化时读取。实际项目中手眼标定的结果往往不是一成不变的——相机如果被撞了一下、机械臂末端负载变化、镜头拧动过都可能导致标定结果失效。所以一个可以单独启动的标定验证工具非常重要。我的实践是在项目里留一个验证模式每次开始任务前用标定板放在已知位置快速验证一下误差超标就触发重新标定提醒。提示如果使用eye-in-hand模式变换链是base_link - tool0 - camera_color_optical_frame。执行抓取时除了手眼变换还需要考虑工具末端坐标系到末端执行器比如吸盘的偏移建议单独定义tool0到抓取点的tf变换与手眼变换分开管理层次清晰便于维护。6. 标定工作流优化与个人经验小结从零走通UR机械臂和Realsense相机的ROS手眼标定整个流程的各个关键点都覆盖了。再强调几个对结果影响最大的环节标定板必须平整、机械臂采样姿态差异要大、数据采集要快避免温飘、结果必须做量化验证。我在实际项目中形成的习惯流程是先用虚拟仿真环境跑通整套标定流程确认launch文件和代码逻辑无误再切换到真机操作真机上先低速走一遍运动轨迹确认安全再正式采集数据标定完成后在工作空间多个位置做验证测试误差达标后才把标定结果接入视觉抓取流程。最后分享一个调试小技巧如果标定结果用起来总觉得差一点不要急着重复整个标定流程先在RViz里标出相机检测到的标定板中心和机械臂示教器得到的基准位置看看误差分布在哪个方向上。有一次我遇到Z方向固定偏差查了半天发现是标定板厚度导致的坐标系偏移——标定板贴在亚克力板上厚度1厘米检测到的坐标系在标定板表面而机械臂参考的是标定板底部差了整整一个厚度。这种问题不把数据可视化出来靠猜很难发现。
返回列表