
做了四五个视觉引导项目从2D相机到3D相机都碰了一遍回头再看手眼标定这件事真的值得好好写一篇总结。不管是刚入门的新手还是已经踩过不少坑的老手手眼标定都是视觉定位项目里绕不开的坎。很多人觉得标定就是跑个例程出来一组矩阵就完事了但实际项目里根本不是这么回事——标定结果忽好忽坏、装完相机手一抖就要重标、机械臂换了姿态精度就崩这些问题我都遇到过。这篇就来完整梳理一下2D和3D相机手眼标定的原理、步骤、注意事项以及我实测下来最实用的排查思路。先说清楚一件事手眼标定要解决的核心问题是告诉机器人“相机看到的物体到底在哪个位置”。听起来简单但里面牵扯到四个坐标系之间的转换关系任何一环没对齐后面视觉引导的精度就全是空的。2D相机和3D相机在这件事上有共同的理论框架但实际做法的区别非常大3D相机虽然多了深度信息反而在标定环节更容易翻车。1. 手眼标定到底在解决什么问题1.1 四个坐标系与整个链路一个典型的视觉引导场景比如机器人抓取传送带上的工件会涉及四个坐标系像素坐标系、相机坐标系、机械臂基座坐标系、机械臂末端坐标系。相机看到的是像素坐标也就是图像上某个点的行和列通过相机内参焦距、畸变系数、中心点可以换算成相机坐标系下的三维坐标接下来要把这个坐标告诉机械臂就得把相机坐标系下的点转换到机械臂基座坐标系下。手眼标定求的就是这中间那一套旋转平移矩阵。这里要分清楚两个概念相机内参标定和手眼标定。内参标定是求相机本身的焦距、畸变、中心点解决的是“像素坐标怎么变成相机坐标”的问题手眼标定是求相机和机器人之间的相对位姿解决的是“相机坐标怎么变成机器人坐标”的问题。很多工程问题是混在一起的比如你内参没标好手眼标定再怎么调也白搭。所以网上很多教程反复强调“先标内参再标手眼”这个顺序不是随便说的。1.2 Eye-in-Hand 和 Eye-to-Hand到底选哪种手眼关系分两种。一种是相机装在机械臂末端上跟着机械臂一起动叫Eye-in-Hand眼在手上另一种是相机固定在工作区域上方或者侧面不跟着动叫Eye-to-Hand眼在手外。Eye-in-Hand的好处是相机可以贴近工件精度比较高想拍哪个角度就拍哪个角度适合复杂曲面的定位缺点是机械臂运动过程中相机视野会变而且要小心走线、碰撞。Eye-to-Hand适合大范围的定位相机固定不动视野稳定但精度受安装距离影响离得越远一个像素对应的实际尺寸越大定位误差也跟着放大。选哪种并不是绝对的。我做过的项目里小工件精密装配用的Eye-in-Hand大工件上下料用的Eye-to-Hand。手眼标定的数学原理对这两种是统一的只是求解时采集数据的策略不同。下面分别讲2D和3D的具体做法。2. 2D相机手眼标定的核心方法2.1 9点标定法最实用也最好理解2D相机做手眼标定最常用的就是9点标定法。原理特别直白在相机视野内取9个点通常是3×3的网格让机械臂末端分别移动到这9个点记录下每个点的像素坐标和机器人坐标然后求一个二维变换矩阵。用OpenCV求解时一般是拿到一组匹配点对调用cv2.estimateAffine2D或者cv2.getPerspectiveTransform。9点标定一般用仿射变换6个自由度3个点就可以解9个点是冗余的能平均误差就够了因为工件在传送带或平台上基本是平面运动不需要透视变换的8个参数。这里要补充一个容易搞混的点9点标定求出来的矩阵其实已经把相机内参和手眼关系合并在一起了。它不再区分“像素坐标→相机坐标→机器人坐标”而是直接一步到位。好处是流程简单写代码也快坏处是换相机、换镜头、动过安装位置就得重新标。而且它只适用于平面场景工件如果不在一个固定高度精度会明显下降。2.2 9点标定的完整操作流程实际操作中9点标定有两种数据采集方式。第一种机械臂带着工件或针尖走9个点位相机拍下机械臂末端在像素坐标系下的坐标同时记录机器人坐标系下的坐标。这里要特别注意机械臂末端上要有一个特征明显的点比如针尖、圆点贴纸、或者特定形状的工件角点。相机识别到这个特征点的像素坐标和机器人示教器上读到的坐标一一对应。第二种固定相机让机械臂末端移动到视野内的几个固定位置每次用相机拍照识别末端记录末端在相机视野中的像素坐标和机械臂基座坐标。这里需要注意末端姿态要保持一致因为2D相机没有深度信息末端转了角度之后识别点实际上已经不是同一个位置了。标定板或者特征点的布局也有讲究9个点尽量铺满整个相机视野不要只在中间一小块区域否则标定结果的插值外推效果很差。我的经验是9个点要覆盖视野范围的80%以上不要取边缘太极限的位置因为镜头畸变在边缘最大一般的仿射模型纠正不了这部分畸变。数据采集完用最小二乘拟合出变换矩阵然后保存下来。之后的运行阶段相机识别到目标物的像素坐标直接用这个矩阵换算成机器人坐标。2.3 Eye-in-Hand的传统手眼标定AXXB9点标定在Eye-to-Hand场景下特别常用但在Eye-in-Hand场景下更标准的做法是求解经典的AXXB问题。这个公式的含义是机械臂末端在两个不同位姿之间的变换关系A乘上相机相对于机械臂末端的变换X再乘上目标点在相机坐标系下的变换B最终要满足AXXB。听起来绕但本质就是不管机械臂怎么动标定物在机器人基座坐标系下的位置是不变的。利用这个约束从多组运动中把相机到机械臂末端的变换矩阵X解出来。求解AXXB在OpenCV里有一个专门函数cv2.calibrateHandEye支持Tsai、Park、Horaud等多种算法。实践中我一般用Tsai方法比较稳但要求采集的数据质量高机械臂移动时位姿变化要大、要多样不能只是平移必须要有明显的旋转变化。这里有一个新手特别容易犯的错误采集数据时机械臂只是平移没有转动这样AXXB的约束退化解出来的矩阵很不稳定。我自己的经验是采集至少15组以上数据机械臂姿态要有大幅度的变化比如俯仰、偏航、翻滚都尽量有一些标定结果才会稳。2.4 2D标定时的内参处理前面提到9点标定一步到位不需要单独做内参标定。但如果用棋盘格做手眼标定尤其是Eye-in-Hand就需要先把相机内参标出来。内参不准确畸变没矫正角点提取的位置偏差会直接传给手眼矩阵造成整体偏置。棋盘格标定要注意拍不同角度时棋盘格必须保证在景深范围内不能太斜否则角点提取误差大。工业镜头上如果光圈开太大景深太浅标定板稍微倾斜一点边缘角点就模糊了。把光圈拧小两档虽然进光量变少但景深变大标定板成像更锐利。另外OpenCV的findChessboardCorners在棋盘格反光严重或者光照不均匀时经常找不全角点。解决办法是贴哑光标定板或者调低相机曝光不要让标定板表面出现过曝。我在现场遇到过几次反光的棋盘格角点检测全乱套最后换了哑光陶瓷板才解决。3. 3D相机手眼标定的差异点3.1 3D结构光相机多了深度也多了麻烦3D相机比如结构光相机、双目相机、ToF相机可以直接输出三维点云或者深度图这意味着我们能够获得目标物在相机坐标系下的三维坐标不再局限于平面。听起来省掉了2D平面标定的高度限制但手眼标定的复杂度反而更高。为什么因为2D的9点标定只需要二维坐标的映射而3D相机的手眼标定需要求解的是三维空间中的旋转平移矩阵6个自由度对应4×4的齐次变换矩阵。为了解这6个参数需要多个不在同一平面上的标定点或者多次不同姿态的观测。常见的3D手眼标定方法有两种一种是用球形靶标或者圆锥靶标通过点云拟合出球心作为标定点另一种是用平面棋盘格通过拍摄不同姿态的棋盘格利用平面约束来求解位姿。3.2 3D结构光相机的手眼标定步骤以我常用的一种方式为例在使用深视智能或者海康的3D结构光相机时相机输出的是一帧点云每个点都有(x, y, z)坐标单位通常是毫米。第一步内参校准。3D相机的内参一般指相机的焦距、主点、畸变在出厂时会标定但运输、安装、温变之后可能需要重新标定。有些品牌如深视智能的SDK里自带标定接口可以根据自己的场景重新算。第二步准备一个特征明显的目标物比如一个标准球或者一个棱角分明的方块。这样做的原因是点云中拟合球心或者顶点比依赖灰度图提取角点更稳定。3D相机对反光表面很敏感纯黑色的物体、亮面的金属反光物体都会产生噪点导致拟合出来的中心位置偏移。第三步让机械臂带着目标物在相机视野内移动多个位置采集点云拟合出目标物在相机坐标系下的坐标同时记录机械臂末端在基座坐标系下的坐标。注意移动姿态也要多样不能只是平移最好有旋转保证三点不共线、四点不共面。第四步用SVD等方法求解两组三维点集之间的刚体变换。OpenCV里没有直接封装好的三维手眼标定函数但可以用cv2.estimateAffine3D或者自己写SVD求解Umeyama算法。3.3 3D标定的姿态选取策略3D标定最容易忽视的就是姿态选取。和2D不同3D相机标定如果只在同一个高度、同一个角度采集目标物那点云中的位置约束其实是退化的。三维空间的6自由度标定需要目标点在空间中分布得足够立体最好形成一个三维的包围盒而不是在一个平面上。我做过一个项目用的是固定上方的3D结构光相机机械臂带着标准球在视野内走一个立方体的轨迹分别在立方体的八个顶点附近各采几组数据总共采了20多组。这样解算出来的手眼矩阵不但在视野中心精度高在视野边缘也比只在一个平面上标定的结果稳得多。另外还要注意3D相机点云在处理时会涉及滤波参数比如离群点移除、降采样体素大小。如果滤波参数设置不当点云坐标本身就有偏差标定出来矩阵再漂亮也没用。我的建议是标定之前先用一个标准尺寸的工件验证一下相机本身的尺寸精度。比如量一个100mm的标准块用点云测量看是不是100mm左右如果偏差太大先调相机参数再标定。4. 实操记录一套完整的2D/3D手眼标定流程4.1 标定前的环境准备关于环境准备先说一个很多人不以为意但影响很大的点光源和曝光。2D相机标定极度依赖图像质量标定板区域的过曝、反光、阴影都会直接导致角点提取精度下降。我一般把相机调成手动曝光模式不要用自动曝光因为自动曝光会在不同位置改变亮度影响特征提取的一致性。标定板的固定也要注意。2D标定板要贴平不要有折痕3D标定球要有漫反射涂层不要用高光镜面球。如果现场条件有限临时用A4纸打印的棋盘格也能做但打印纸的平整度不好误差会比较大只能用来快速验证流程不能作为最终标定。机械臂方面手动示教9个点时每次都要确认机械臂末端姿态一致不要在某个点让末端倾斜。示教的点位要和实际生产使用的拍照位姿尽量接近这样可以避免大角度差异带来的误差。4.2 2D相机9点标定实操这里我以Eye-to-Hand的2D相机为例写一下我的操作习惯。第一步机械臂装上针尖或者吸嘴示教9个点成一个3×3的网格。网格范围尽量覆盖相机视野。我在示教时会在PC端实时显示相机画面一边移动机械臂一边看画面中的位置确保9个点都在视野内且分布均匀。第二步对于每个示教点机械臂停稳后记录当前机器人坐标。同时相机拍摄一张图像识别针尖在像素坐标系下的坐标。识别针尖用最简单的办法在针尖附近贴一张高对比度的圆形标记用二值化和cv2.HoughCircles或者轮廓重心来找圆心。如果直接用针尖本身在图像中的特征很容易被环境干扰。我这里提醒一下识别到的像素坐标精度直接影响标定结果要把识别算法在标定阶段做准不要指望后续阶段去补偿。第三步9组数据准备好之后写几行代码用仿射变换拟合。import cv2 import numpy as np pixel_pts np.array([[...], ...], dtypenp.float32) # 9个像素坐标 robot_pts np.array([[...], ...], dtypenp.float32) # 9个机器人坐标 # 仿射变换矩阵 2x3 transform_matrix, inliers cv2.estimateAffine2D(pixel_pts, robot_pts) print(transform_matrix)这里有个现场细节estimateAffine2D默认使用的是RANSAC如果数据里有某个点因为识别问题偏差很大RANSAC会把该点当作外点剔除掉。如果你的数据是通过高精度示教和稳定识别得到的也可以指定methodcv2.LMEDS或者干脆用cv2.estimateAffinePartial2D来比较不同的鲁棒估计效果。我更推荐在标定阶段严格要求数据质量而不是过度依赖RANSAC因为RANSAC对外点的识别也会误伤少量正常点。第四步验证标定结果。把机械臂移动到第10个随机点让相机识别像素坐标用变换矩阵换算成机器人坐标再和示教器读出的实际坐标对比。误差在0.5mm以内通常算合格0.2mm左右就非常好了。4.3 3D相机手眼标定实操3D相机的标定流程拿深视智能这类结构光相机来举例。它提供SDK可以直接取到点云通常以FrameData或者类似结构返回。标定步骤如下。第一步采集一帧点云确认点云坐标系。3D相机一般输出的是相机坐标系下的点云数据原点在相机光心Z轴指向场景。调试时可以把点云用软件可视化确认坐标系方向和单位。第二步放一个标准球在视野内让机械臂末端针尖触碰到球心然后读取机械臂坐标。实际操作中很难让针尖刚好碰到球心所以我通常在球的上方放一个已知高度的标记用机械臂按压接触的方式间接得到球心高度。第三步用SDK或者自写点云处理算法从点云中分割出标准球拟合球心坐标。球心拟合可以用RANSAC 最小二乘球拟合OpenCV不提供专门的球拟合函数可以用PCL也可以自己写。我用PCL比较多pcl::SACSegmentation可以很方便地分割出球模型并得到球心。第四步让机械臂带动标准球或者带动相机移动采集至少10组不同的球心坐标和对应的机器人坐标。移动时注意姿态要变化丰富最好让球在相机视野的前、后、左、右、上、下都出现。第五步用Umeyama算法求解两组三维点集的刚体变换。我贴一下自己常用的计算代码。import numpy as np def umeyama(src, dst): assert src.shape dst.shape n src.shape[1] mu_src src.mean(axis1, keepdimsTrue) mu_dst dst.mean(axis1, keepdimsTrue) src_c src - mu_src dst_c dst - mu_dst cov (dst_c src_c.T) / n U, D, Vt np.linalg.svd(cov) S np.eye(3) if np.linalg.det(U) * np.linalg.det(Vt) 0: S[2, 2] -1 R U S Vt t mu_dst - R mu_src T np.eye(4) T[:3, :3] R T[:3, 3] t.flatten() return T src_pts np.array([...]) # 相机坐标系下的球心形状 3xN dst_pts np.array([...]) # 机器人基座坐标系下的球心形状 3xN T umeyama(src_pts, dst_pts) print(T)这里说一下为什么用SVD/Umeyama而不是直接求逆矩阵。因为数据组数通常多于未知数组数4是超定方程组最小二乘意义下的解才是稳定的。SVD分解在数值上比直接求伪逆更稳定尤其是数据点近似共面或者共线时SVD能告诉你奇异值的情况方便判断数据是否退化。4.4 标定精度的验证方法标定完之后不要急着投入生产一定要做一次独立验证。验证不能只用标定时的数据点因为那是“闭卷考试”模型会把这些点拟合得很好看不出真实泛化能力。我的验证方法是另外选8到10个随机位置机械臂末端带着一个尖锐探针移动到这些位置让相机识别这些位置的坐标通过手眼矩阵换算成机器人坐标再和探针的实际坐标做差统计均方根误差和最大误差。如果误差很大不要急着认为标定失败先判断是哪个环节出了问题。2D平面标定中误差常见原因是相机安装松动、标定板不平、识别特征不准确3D标定中误差常见原因是点云噪声大、球心拟合不准、机械臂位姿记录不准。判断环节的方法也很简单把标定求出来的矩阵固定用多组数据反投影回去看残差的分布。如果残差在整个视野内均匀偏大说明手眼矩阵整体不对如果残差在视野边缘很大、中心很小说明畸变校正或者透视模型有问题如果残差没有规律说明数据采集有噪声可能是机械臂定位抖动或者相机本身不稳定。5. 常见问题与排查技巧实录5.1 标定结果误差大的排查方向用手眼标定踩过的坑我列一个速查表对应不同现象、可能原因和解决方向这样排查起来效率高很多。现象特征可能原因排查方向误差均匀偏大所有点都偏移手眼矩阵整体求错检查像素坐标与机器人坐标是否对应错位检查手眼解算代码视野中心好、边缘差镜头畸变没校正或内参不准单独做内参标定开启畸变校正或改用更高阶的变换模型个别点误差特别大标定点识别错误或机械臂示教抖动检查该点的像素坐标识别结果重新采集该点同一位置重复标定结果不同安装松动或相机/机械臂本身重复精度差紧固相机安装件检查机械臂的重复定位精度3D标定在旋转后误差大姿态数据不足导致旋转解算退化补充更多不同姿态的数据避免共面数据5.2 相机或者机械臂动了要不要重新标定这个问题几乎每个项目都会被问到。我的回答是只要相机相对于机械臂的位置关系发生了改变就必须重新标定。注意这里的“相对位置”包括三个维度平移、旋转、以及镜头的光学状态。现场最容易忽略的是镜头的变焦和光圈。如果项目里用的镜头支持变焦或者光圈调节一旦动了之后内参就变了手眼矩阵也必然失效。因此我强烈建议量产设备中镜头用定焦并锁定光圈环机械臂固定相机的法兰或支架用防松螺丝加标记漆。每次设备维护后做一个快速验证用第4节中的独立验证法判断是否仍然满足精度。还有一种情况是相机拆下来擦了擦镜头又装回去。这种物理位置变化很小的操作不一定会让手眼矩阵完全失效但会带来微小的平移偏差。在精度要求高的场景下不要偷懒重新标一遍才是稳妥的。5.3 3D相机点云噪声对标定的影响3D结构光相机在黑色、反光、透明物体上的点云质量通常很差产生大量飞点。如果标定球表面反光点云拟合出的球心就会偏移。处理办法是用哑光的标定块或者给标定球喷一层显影剂保证表面能稳定反射。采集点云后还可以做一个预处理对点云进行统计滤波去除离群点。PCL的StatisticalOutlierRemoval配合表面法向估计能有效剔除大部分飞点。但滤波参数不要调太狠否则真实表面点也被删掉球心拟合结果也会偏。5.4 关于海康、大华等工业相机的SDK细节很多项目用的是海康工业相机它提供的MVS SDK最大优势就是图像获取稳定。手眼标定中要连续采集多张图像如果用USB接口尤其要注意带宽和帧率设置。海康相机丢帧是不少人遇到的问题大多不是相机的问题而是主机USB控制器带宽不足或供电不稳。建议使用高质量USB线缆甚至直接改用千兆网口相机网络相机在工业现场的稳定性普遍更好。如果相机已经用网口接入记得调整网卡巨型帧参数能明显降低丢包率。深视智能的3D相机SDK使用上注意各个型号的数据格式和坐标系定义不同标定前一定仔细阅读对应型号的坐标说明文档。我遇到过同一个品牌不同型号的SDK有的点云单位是毫米有的是米差1000倍标定后怎么都不对查了半天才发现是单位问题。6. 标定工具和维护规范的几条建议手眼标定的工具链无非是OpenCV、PCL、以及相机厂商自带的SDK。OpenCV适合2D平面标定和棋盘格角点提取PCL适合3D点云的滤波、球拟合等相机SDK负责取图和基本参数设置。如果项目预算充足Halcon的标定助手确实方便但价格不低而且对license的依赖比较重。关于标定频率我建议在项目初期阶段每次修改视觉程序后重新标定一次项目稳定后不做频率限制但要建立一个定期验证机制。比如每天开机后先跑一次自动验证用放在视野固定位置的参考工件用标定矩阵计算位姿与实际位置对比偏差超过阈值就提醒重新标定。这种做法可以有效防止设备长时间运行后出现螺丝松动、温漂等问题带来的精度下降。最后再分享一个细节现场标定时所有工具要提前备齐。比如内六角扳手、标定板、固定用的双面胶、支架、水平尺。看似不起眼但很多标定失败或者精度不稳定的问题到最后发现只是安装架没拧紧机械臂一动就晃。标定前的机械结构检查有时候比算法更重要。