摘要:很多做机器人视觉的同学都有过这种经历:YOLO模型训练得挺好,mAP也上去了,但一上真机抓取就“歪”得离谱。问题往往不在检测算法本身,而在从“像素坐标”到“机械臂基座标”的转换环节。本文不讲枯燥的矩阵推导,直接从工程落地角度,梳理一套YOLO+手眼标定(Eye-to-Hand)的完整实施方案,包含标定流程、坐标变换核心逻辑以及实际项目中的避坑经验。

一、 为什么YOLO准了,抓取还是不准?

在视觉抓取系统中,YOLO解决的是 “是什么”“在哪里(像素级)” 的问题,而机械臂需要的是 “在哪个三维空间点(毫米级)”。这两者之间隔着一道鸿沟:

  1. 坐标系不统一:YOLO输出的是图像坐标系 (u,v)(u, v)(u,v),机械臂运动规划依赖的是基坐标系 {Base}\{Base\}{Base}
  2. 深度信息缺失:单目YOLO只能给出2D框中心点,缺乏Z轴深度,无法直接转换为3D位置。
  3. 安装误差:相机安装在机械臂末端(Eye-in-Hand)或外部支架(Eye-to-Hand),其相对位姿不可能完全理想,必须通过标定获取精确的变换矩阵。

本文聚焦场景Eye-to-Hand(眼在手外)。即相机固定在外部支架上,视野覆盖整个工作区。这是工业分拣、桌面抓取最常用的布局,标定一次即可长期使用,不受机械臂运动影响。

二、 系统架构与核心流程

在动手写代码前,先理清整个数据流。下图是典型的Eye-to-Hand视觉抓取系统处理流程:

离线标定阶段

相机采集图像

YOLO目标检测

检测到目标?

提取目标中心像素坐标 uv

畸变矫正 & 像素转相机坐标

利用标定板获取 Zc 深度

相机坐标系 -> 基坐标系变换

发送3D坐标给机械臂控制器

机械臂执行抓取

采集多组标定板图像

张正友标定法

获取内参矩阵K & 畸变系数D

采集多组标定板+机械臂位姿

求解 T_base_cam

保存标定参数文件

整个系统分为离线标定在线抓取两个阶段。很多人只关注在线推理,忽略了离线标定的精度直接决定了抓取的天花板。

三、 离线标定:精度是“磨”出来的

3.1 相机内参标定

这一步获取相机的焦距 (fx,fy)(f_x, f_y)(fx,fy)、主点 (cx,cy)(c_x, c_y)(cx,cy) 和畸变系数。推荐使用OpenCV的calibrateCamera函数,注意以下工程细节:

  • 标定板选择:建议使用9×6或11×8的棋盘格,格子尺寸根据视场大小确定,保证在画面中占1/3~1/2面积。
  • 图像数量:至少15张有效图像,覆盖画面四角和中心区域。不要只在同一个平面拍,要有不同的倾斜角度。
  • 重投影误差:这是衡量标定质量的硬指标。工程上要求平均重投影误差 < 0.5 pixel,否则后续坐标转换必然偏差大。如果超标,检查是否有模糊图像、角点检测错误,或重新拍摄。
# 关键参数检查示例
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    mean_error += error
print(f"平均重投影误差: {mean_error:.4f} pixel")
# 若 > 0.5,请重新检查标定数据质量

3.2 手眼标定(Eye-to-Hand)

目标是求解相机坐标系到机械臂基座标的变换矩阵 TbasecamT_{base}^{cam}Tbasecam

对于Eye-to-Hand,经典方法是使用AX=XB问题的变种。实际操作中,更推荐直接用OpenCV的calibrateHandEye函数,传入:

  • R_gripper2base, t_gripper2base:机械臂末端相对于基座的旋转和平移(从机器人SDK读取)。
  • R_target2cam, t_target2cam:标定板相对于相机的旋转和平移(从内参标定结果中获得)。

⚠️ 避坑提醒

  1. 机械臂位姿读取时机:必须在机械臂完全静止后再读取位姿,运动过程中的位姿有延迟和抖动。
  2. 姿态多样性:机械臂末端需要有不同的旋转角度,不能只做平移运动,否则旋转分量解算不稳定。建议设计6~8个差异明显的抓取预备位姿用于标定。
  3. 方法选择calibrateHandEye支持多种算法(Tsai, Park, Horaud等),实测Park方法在噪声较大时鲁棒性更好,优先尝试。

四、 在线抓取:从像素到3D坐标的转换

标定完成后,在线推理时的坐标转换是核心。假设YOLO检测到目标中心像素为 (u,v)(u, v)(u,v),转换步骤如下:

Step 1: 畸变矫正

原始像素坐标必须先经过畸变矫正,否则边缘区域误差显著:

# 使用undistortPoints一步完成去畸变+归一化
pts_uv = np.array([[[u, v]]], dtype=np.float64)
pts_norm = cv2.undistortPoints(pts_uv, camera_matrix, dist_coeffs, P=camera_matrix)
# pts_norm[0][0] 即为去畸变后的像素坐标

Step 2: 像素坐标 → 相机坐标系

这里需要知道目标在相机坐标系下的深度 ZcZ_cZc。对于Eye-to-Hand固定相机场景,常用两种策略:

  • 已知工作平面高度:如果所有物体都放在同一平面上(如传送带、工作台),且该平面在基坐标系下高度已知,则可通过平面方程反推 ZcZ_cZc。这是最简单可靠的方式。
  • RGB-D相机:直接使用深度图获取对应像素的深度值。注意要对齐RGB和Depth图像。

以已知平面为例,设工作平面在相机坐标系下的方程为 aX+bY+cZ+d=0aX + bY + cZ + d = 0aX+bY+cZ+d=0,结合归一化坐标 (xn,yn)(x_n, y_n)(xn,yn),可得:

Zc=−da⋅xn+b⋅yn+cZ_c = \frac{-d}{a \cdot x_n + b \cdot y_n + c}Zc=axn+byn+cd

进而得到相机坐标系下的3D点:Pcam=[xn⋅Zc,  yn⋅Zc,  Zc]TP_{cam} = [x_n \cdot Z_c, \; y_n \cdot Z_c, \; Z_c]^TPcam=[xnZc,ynZc,Zc]T

Step 3: 相机坐标系 → 基坐标系

利用标定得到的 TbasecamT_{base}^{cam}Tbasecam 进行齐次变换:

Pbase=Tbasecam⋅PcamP_{base} = T_{base}^{cam} \cdot P_{cam}Pbase=TbasecamPcam

这个 PbaseP_{base}Pbase 就是可以直接发送给机械臂的目标抓取点坐标。

五、 工程实战中的5个血泪教训

  1. YOLO的bbox中心 ≠ 抓取点:对于不规则物体,检测框中心可能不在可抓取区域。建议在训练时标注抓取关键点而非普通bbox,或在后处理中根据物体类别做偏移补偿。
  2. 标定板固定要稳:标定过程中标定板任何微小移动都会导致失败。务必使用磁性底座或夹具刚性固定,不要用胶带粘。
  3. 光照一致性:标定时和实际运行时的光照条件尽量一致。强光反光会导致角点检测偏移,建议使用漫射光源。
  4. 验证比标定更重要:标定完成后,一定要做独立验证。在工作区内放置若干已知坐标的测试点,用视觉系统测量并对比真实坐标,统计RMSE。只有验证通过的标定参数才能上线
  5. 定期复检:相机支架可能因振动松动,镜头可能被触碰。建议每周或每次换产线时用快速验证流程检查标定是否失效,不要等到批量抓取失败才排查。

六、 总结

机器人视觉抓取是一个系统工程,YOLO只是其中一环。标定精度决定下限,检测算法决定上限,而工程细节决定能否稳定量产。希望本文的实操经验能帮你少走弯路,把视觉抓取项目真正落地。

如果你在标定或坐标转换环节遇到具体问题,欢迎在评论区交流,看到必回。


参考资料

  • OpenCV官方文档:Camera Calibration and 3D Reconstruction
  • Tsai, R.Y. (1989). A versatile camera calibration technique for high-accuracy 3D machine vision metrology
  • Park, F.C. (1994). Robot sensor calibration: solving AX=XB on the Euclidean group
  • YOLOv8官方仓库及部署文档
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐