机器人装上眼睛之后,抓取还是歪?很多项目团队第一反应是换算法、换模型、调参数,折腾了一两周,精度纹丝不动。其实在工业现场,视觉定位精度不够,绝大多数情况下不是模型的问题,而是手眼标定没做好。
这篇文章会从实际项目视角出发,讲清楚手眼标定到底是什么、不同安装方式怎么选、标定流程怎么走、常见坑在哪,以及如何验证标定结果是否真的可靠。无论你是刚接触机器视觉的初学者,还是已经在现场调试的工程师,这篇文章都值得收藏备用。
1. 这篇文章真正要解决的问题
先聊一个现场最常见的现象:视觉系统已经能准确识别工件位置,图像坐标看着没问题,但机器人抓过去就是偏,有时偏几毫米,有时偏一两厘米,甚至姿态不对直接撞飞工件。
问题出在哪里?很多人第一反应是“模型精度不够”“算法识别不准”,于是一头扎进调参、换网络、加数据集。但真实情况往往很扎心:识别算法再准,也解决不了机器人坐标系统和相机坐标系统之间的映射错误。
打个比方,你拿着手机地图导航,地图上标注的位置是准的,但你的手机指南针没校准,箭头指向偏了十度,那你跟着地图走,走得越远,偏得越多。手眼标定解决的问题,就是让“地图坐标”和“脚底坐标”对齐。
本文要解决的问题包括:
- 手眼标定是什么,为什么它决定了视觉引导的最终精度。
- 眼在手上、眼在手外两种方案怎么选,各自优缺点是什么。
- 标定流程怎么设计,从标定板、数据采集到矩阵计算,每一步怎么做。
- 如何验证标定结果,怎么判断标定是成功还是失败。
- 现场最常见的标定坑和排查思路。
- 从工程角度给出最佳实践建议。
一句话总结:如果你在现场遇到机器人抓偏问题,先别急着换模型,先检查手眼标定。这篇文章就是帮你系统排查和解决这个问题的。
2. 机器视觉手眼标定的核心概念
2.1 什么是手眼标定
手眼标定(Hand-Eye Calibration)是机器视觉与机器人集成中一个关键步骤。它的目标,是求解相机坐标系与机器人坐标系之间的变换关系。这个变换关系,通常用一个 4×4 的齐次变换矩阵表示,包含旋转矩阵和平移向量。
在视觉引导机器人作业的场景里,系统流程一般是:
- 相机拍摄工件,得到图像。
- 视觉算法识别工件,输出工件在图像中的像素坐标或相机坐标系下的坐标。
- 系统把这个坐标转换到机器人坐标系下。
- 机器人根据转换后的坐标进行抓取、装配、码垛等操作。
第 3 步就是这个转换关系的核心,也就是手眼标定要解决的。
没有手眼标定,或者标定不准,视觉系统看到的“世界”和机器人实际作业的“世界”就是两个平行宇宙,互相无法映射。
2.2 手眼标定为什么容易出问题
很多团队在项目初期不重视手眼标定,觉得“相机装好,识别出来,给机器人发个坐标就行”。等到联调阶段发现精度不够,才回过头来补标定,这时候往往已经在现场耗费了大量时间。
更麻烦的是,手眼标定精度受很多因素影响:标定板精度、相机畸变矫正、机器人运动学精度、标定数据采集质量、求解算法选择、坐标系定义一致性。任何一个环节出问题,最终精度都会受影响。而这些问题,往往不会报错,系统运行起来看着一切正常,就是精度差那么一点点。
2.3 关键术语速查
| 术语 | 含义 |
|---|---|
| 相机坐标系 | 以相机光心为原点建立的坐标系 |
| 机器人基坐标系 | 以机器人底座为原点的坐标系 |
| 工具坐标系 | 固定在机器人末端法兰或工具上的坐标系 |
| 齐次变换矩阵 | 包含旋转和平移的 4×4 矩阵,用于坐标变换 |
| 外参 | 相机相对于某个世界坐标系的位姿 |
| 内参 | 相机焦距、主点、畸变系数等固有参数 |
| 标定板 | 带有已知图案的平面板,用于提取特征点 |
| AX=XB | 手眼标定的核心方程 |
3. 眼在手上与眼在手外:两种方案怎么选
手眼标定按相机安装方式分为两类:眼在手上(Eye-in-Hand)和眼在手外(Eye-to-Hand)。这两种方案不仅标定方程不同,适用场景也完全不同。
3.1 眼在手上(Eye-in-Hand)
相机固定在机器人末端法兰上,随机器人一起运动。
这种方式的特点是:
- 相机可以随机器人移动到任意角度拍摄,视野灵活。
- 近距离拍摄可以获得更高的分辨率,适合精细定位。
- 标定关系是相机坐标系与机器人末端坐标系之间的变换,需要用 AX=XB 求解。
- 由于相机随机器人运动,每次机器人位姿变化后,相机看到的视野都在变。
适用场景:
- 机器人需要从不同角度观察工件。
- 工件位置不固定,需要大范围搜索。
- 需要近距离高精度检测,比如装配、插件、焊缝跟踪。
需要注意:
- 相机随机器人运动,线缆管理要设计好,避免运动过程中拉扯。
- 机器人运动学精度会影响标定结果,如果机器人本身重复定位精度差,手眼标定精度也会受影响。
3.2 眼在手外(Eye-to-Hand)
相机固定在支架或外围设备上,相对于机器人基座是静止的。
这种方式的特点是:
- 相机视野固定,适合工件位置相对固定的场景。
- 标定关系是相机坐标系与机器人基坐标系之间的变换。
- 标定时通常让机器人末端带着标定板运动,在不同位置拍照。
- 逻辑相对简单,标定一次后,只要相机位置不变就能持续使用。
适用场景:
- 工件在固定区域上料,机器人从固定方位抓取。
- 视觉引导码垛、拆垛、上下料。
- 对节拍要求较高,不希望相机运动占用时间的场景。
需要注意:
- 相机安装高度和角度要合适,视野覆盖整个抓取区域。
- 一旦相机被碰撞或松动,标定结果就失效,需要重新标定。
- 对大面积视野,往往需要更高分辨率的相机。
3.3 两种方案对比
| 对比项 | 眼在手上 | 眼在手外 |
|---|---|---|
| 相机安装位置 | 机器人末端 | 固定支架或外围 |
| 视野 | 随机器人运动,灵活 | 固定范围内 |
| 标定方程 | AX=XB | AX=ZB 或直接求外参 |
| 标定时机 | 安装后标定一次,换工具需重标 | 安装后标定一次,相机松动需重标 |
| 典型应用 | 装配、插件、焊缝跟踪 | 上下料、码垛、分拣 |
| 精度影响因素 | 机器人运动学标定、末端负载变化 | 相机安装牢固度、标定板平面度 |
4. 环境准备与前置条件
在开始标定之前,需要确认软硬件环境和工具都满足要求。
4.1 硬件环境
- 机器人系统:支持手动示教或脚本控制,能够输出末端位姿。常见的如 ABB、KUKA、发那科、安川、埃斯顿等工业机器人,或优傲、法奥等协作机器人。
- 相机系统:工业相机或 3D 相机,能够输出图像或点云数据。无论 2D 相机还是 3D 相机,都需要先完成相机内参标定。
- 标定板:常见的棋盘格标定板或圆点标定板,要求平面度好、图案清晰。标定板尺寸要和相机视野匹配。对 3D 相机,也可以使用标准球或平面标定块。
- 计算机:用于运行标定程序,安装机器人通信 SDK 和相机 SDK。
4.2 软件环境
以下以 Python 为例说明,这是目前视觉标定最常用的语言。具体版本以实际项目为准,本文重点演示通用思路。
# Python 3.8+ pip install opencv-python numpy scipy如果是 3D 相机,一般使用相机厂商提供的 SDK,例如 Halcon、VisionPro 或各厂商自研的标定工具,同时也可以配合 OpenCV 和 PCL 做点云处理和标定计算。
4.3 前置检查清单
在开始标定之前,按以下顺序检查:
- 相机能正常出图,图像清晰,没有严重噪声。
- 机器人控制器能够正常回传末端位姿。
- 机器人工作区域内没有遮挡标定路径的障碍物。
- 标定板表面干净,图案完整无破损。
- 确认机器人末端工具的中心点(TCP)已经标定过,或者使用法兰中心作为参考点。
这一条特别容易出问题。如果 TCP 本身没标定,手眼标定的结果也会受到牵连。
5. 手眼标定完整流程与 Python 代码实现
5.1 流程总览
手眼标定流程可以拆为几步:
- 安装相机,选择标定方案(眼在手上或眼在手外)。
- 进行相机内参标定。
- 设计标定轨迹,采集多组数据。
- 计算手眼变换矩阵。
- 验证标定精度。
- 将标定结果写入系统配置。
5.2 采集标定数据
采集数据是整个标定流程中最关键的步骤。数据采集质量不好,后续算法再先进也没用。
以眼在手外为例,标定时将标定板固定在机器人末端,然后控制机器人移动到多个不同位姿,在每个位姿下:
- 记录当前机器人末端在基坐标系下的位姿。
- 相机拍照,检测标定板在相机坐标系下的位姿。
以眼在手上为例,则是标定板固定在外部,机器人带着相机移动到多个位姿,分别记录相机位姿与标定板位姿。
无论哪种方式,都需要采集至少 10 到 15 组数据,并且保证位姿变化足够多样。如果所有数据都在同一高度、同一角度附近,求解出来的矩阵会非常不稳定。
下面是一段示意性的数据采集代码,读取多张标定板图像并记录对应的机器人位姿。
# ------- # 采集标定图像和机器人位姿 # 假设 images 目录下存放已拍摄的标定板图像 # robot_poses.json 存放对应的机器人末端位姿 # ------- import json import glob import cv2 import numpy as np image_paths = sorted(glob.glob("calib_images/*.png")) robot_pose_file = "calib_images/robot_poses.json" with open(robot_pose_file, "r") as f: robot_poses = json.load(f) # 机器人末端位姿格式:[x, y, z, rx, ry, rz] # 其中 rx, ry, rz 是旋转向量表示,单位弧度 print(f"图像数量: {len(image_paths)}") print(f"机器人位姿数量: {len(robot_poses)}") assert len(image_paths) == len(robot_poses), "图像数量与位姿数量不匹配"这里用到的是旋转向量格式,实际项目中也可能用四元数或欧拉角,需要根据机器人 SDK 的输出格式做转换。
5.3 相机内参标定
手眼标定之前,相机内参必须准确。内参不准,外参计算就会带有系统性偏差。
内参标定通常使用 OpenCV 的calibrateCamera,采集 10 到 20 张不同角度的标定板图像即可。
# ------- # 相机内参标定 # ------- import cv2 import numpy as np import glob CHECKERBOARD = (7, 10) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) objpoints = [] imgpoints = [] images = glob.glob("intrinsic_images/*.png") for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None) if ret: objpoints.append(objp) corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) imgpoints.append(corners2) cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret) cv2.imshow("corners", img) cv2.waitKey(100) cv2.destroyAllWindows() ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera( objpoints, imgpoints, gray.shape[::-1], None, None ) print("内参矩阵:\n", mtx) print("畸变系数:\n", dist) np.save("camera_matrix.npy", mtx) np.save("dist_coeffs.npy", dist)5.4 求解手眼变换矩阵
有了内参,就可以从标定板图像中提取标定板在相机坐标系下的位姿。结合机器人末端位姿,就可以求解手眼变换矩阵。
眼在手上使用cv2.calibrateHandEye,眼在手外则需要先做一次公式变换,或者使用对应的求解方式。
# ------- # 眼在手上求解手眼矩阵 # 代码参考 OpenCV 官方示例 # ------- import cv2 import numpy as np import json def euler_to_rotation_matrix(rx, ry, rz): # 将欧拉角转为旋转矩阵,实际项目中需要根据机器人 SDK 定义确认顺序 Rx = cv2.Rodrigues(np.array([rx, 0, 0]))[0] Ry = cv2.Rodrigues(np.array([0, ry, 0]))[0] Rz = cv2.Rodrigues(np.array([0, 0, rz]))[0] return Rz @ Ry @ Rx def pose_to_matrix(pose): x, y, z, rx, ry, rz = pose R = euler_to_rotation_matrix(rx, ry, rz) T = np.eye(4) T[:3, :3] = R T[:3, 3] = [x, y, z] return T # 加载数据 with open("calib_images/robot_poses.json", "r") as f: robot_poses = json.load(f) # 已计算的标定板在相机坐标系下的位姿 # 这里假设 board_poses 是预先提取好的 [x, y, z, rx, ry, rz] 列表 with open("calib_images/board_poses.json", "r") as f: board_poses = json.load(f) # 构造 OpenCV 需要的输入格式 R_gripper2base_list = [] t_gripper2base_list = [] R_target2cam_list = [] t_target2cam_list = [] for pose in robot_poses: T = pose_to_matrix(pose) R_gripper2base_list.append(T[:3, :3]) t_gripper2base_list.append(T[:3, 3]) for pose in board_poses: T = pose_to_matrix(pose) R_target2cam_list.append(T[:3, :3]) t_target2cam_list.append(T[:3, 3]) # 求解手眼矩阵 R_cam2gripper, t_cam2gripper = cv2.calibrateHandEye( R_gripper2base_list, t_gripper2base_list, R_target2cam_list, t_target2cam_list, method=cv2.CALIB_HAND_EYE_TSAI, ) T_cam2gripper = np.eye(4) T_cam2gripper[:3, :3] = R_cam2gripper T_cam2gripper[:3, 3] = t_cam2gripper.flatten() print("相机到末端的手眼矩阵:\n", T_cam2gripper) np.save("hand_eye_matrix.npy", T_cam2gripper)这里的关键是cv2.calibrateHandEye支持的几种方法:CALIB_HAND_EYE_TSAI、CALIB_HAND_EYE_PARK、CALIB_HAND_EYE_HORAUD、CALIB_HAND_EYE_ANDREFF、CALIB_HAND_EYE_DANIILIDIS。实际使用中,可以换不同方法求解,对比结果的差异。如果多种方法得到的结果偏差很大,说明采集的数据质量有问题。
6. 运行结果与效果验证
标定完成后,必须验证。这一步很多人会跳过,结果上线后才发现问题。
6.1 肉眼验证
先做一个最简单的验证:固定标定板不动,控制机器人末端移动到标定板中心位置。然后通过手眼矩阵将标定板在相机坐标系下的坐标转换到机器人坐标系下,对比是否一致。
6.2 定量验证
更可靠的验证方法是采集一组没有参与标定的测试数据:
- 控制机器人到某个位姿,拍摄标定板。
- 通过手眼矩阵计算出标定板在机器人基坐标系下的位姿。
- 与机器人示教器上显示的标定板实际位置对比。
- 计算位置误差和姿态误差。
如果位置误差在项目允许范围内,说明标定结果可用。如果误差大,需要返回检查和调整。
6.3 一个简单的验证代码示例
# ------- # 验证手眼矩阵精度 # ------- import cv2 import numpy as np T_cam2gripper = np.load("hand_eye_matrix.npy") # 假设在某次验证中,从图像得到标定板在相机坐标系下的位姿 T_target2cam = np.eye(4) T_target2cam[:3, :3] = ... # 实际从图像解算 T_target2cam[:3, 3] = ... # 实际从图像解算 # 机器人当前末端位姿 T_gripper2base = np.eye(4) T_gripper2base[:3, :3] = ... # 从机器人 SDK 获取 T_gripper2base[:3, 3] = ... # 从机器人 SDK 获取 # 标定板在机器人基坐标系下的位姿 T_target2base = T_gripper2base @ T_cam2gripper @ T_target2cam print("标定板在机器人基坐标系下的位置:", T_target2base[:3, 3])代码中的省略号代表需要根据实际项目填写的内容,运行时请用真实数据替换。
6.4 判断标定成功的标准
判断标定是否成功,可以从几个维度看:
- 重投影误差:把计算出的手眼矩阵放回标定数据中,重新计算标定板在机器人坐标系下的位置,与原始已知位置对比,误差应该在毫米级或亚毫米级。
- 多组验证数据一致性:使用多组独立验证数据,计算误差的均值和标准差。均值表示系统偏差,标准差表示随机波动。
- 现场抓取效果:最终还是要看真实抓取。先做低速、低负载测试,再逐步加大节拍。
如果出现报错,第一步先看数据采集是否成功、内参是否参与计算、机器人位姿格式是否与代码预期一致。
7. 手眼标定常见问题与排查思路
以下表格汇总了现场最常见的标定问题和排查方向。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 标定求解结果不稳定,多次运行差异大 | 数据采集位姿变化范围不够,或数据量太少 | 检查 15 组以上数据是否覆盖不同高度、不同姿态 | 增加数据量,扩大位姿范围 |
| 标定结果看起来没问题,但抓取时偏 | 相机内参不准,或机器人 TCP 不准 | 分别检查内参标定和 TCP 标定 | 重新做相机内参标定,校正 TCP |
| 图像中标定板检测失败 | 亮度不均、反光、标定板污损 | 查看原始图像,调整光源和曝光 | 改善光源,清洁或更换标定板 |
| 机器人运动到标定路径时报错 | 路径有奇异点,或超出机器人运动范围 | 在示教器上手动确认路径 | 调整标定路径,避开奇异点和限位 |
| 换了一个相机/镜头后标定结果不准 | 换硬件后没有重新标定内参和手眼 | 确认更换记录 | 每次更换相机或镜头后重新标定 |
| 手眼矩阵计算出来是“镜像”的 | X 轴或 Y 轴方向定义不一致 | 对比相机坐标系和机器人坐标系的轴向定义 | 统一坐标系方向定义,调整轴向映射 |
| 3D 相机标定后精度不高 | 点云配准误差、标定块尺寸测量不准确 | 检查标定块实际尺寸和点云质量 | 使用更精确的标定块,优化点云采集参数 |
7.1 数据采集时最容易忽略的细节
数据采集时有一个常见误区:为了省事,只在几个相近位置拍照。这样采到的数据看似很多,但信息量很少,求解矩阵时会退化。一个良好的标定数据集,应该让标定板在相机视野内覆盖不同位置、不同姿态,且姿态变化不能太小。
另一个容易被忽略的点是:机器人不能只平移不旋转。如果机器人在所有采集点都是同一姿态,只有位置变化,那么旋转部分的约束不足,求解出来的旋转矩阵会不稳定。
7.2 机器人坐标系方向不一致的问题
现场调试中,最容易让人困惑的问题之一,就是坐标系轴向定义不一致。
相机坐标系通常是 x 向右、y 向下、z 向前的右手系。机器人基坐标系则根据厂商定义,可能是 x 向前、y 向左、z 向上的右手系。如果直接套用手眼标定公式,没有注意轴向映射,就可能出现坐标“翻转”或“镜像”的诡异现象。
解决办法是在标定之前,先确认相机坐标系、机器人坐标系、工具坐标系的轴向定义,并且写好对应的坐标变换代码,保持全局统一。
8. 最佳实践与工程建议
8.1 标定板选择与维护
标定板的精度直接决定标定上限。工业现场建议使用陶瓷或玻璃基材的高精度标定板,避免使用普通打印纸。打印纸容易受潮、变形、褶皱,标定精度很难保证。
标定板尺寸要和视野匹配。如果标定板太小,在图像中只占很小面积,特征点提取精度会下降;如果太大,可能超出视野范围。
8.2 数据采集建议
- 至少采集 15 组以上数据。
- 位姿覆盖相机视野的多个区域,避免聚集在同一位置。
- 避免在机器人奇异点附近采集数据。
- 每组数据之间,机器人的姿态变化要足够明显。
- 采集过程中,环境光照尽量稳定。
8.3 标定周期与触发重新标定的条件
手眼标定结果不是一劳永逸的。以下情况需要重新标定:
- 相机位置发生移动或碰撞。
- 更换镜头或调整焦距。
- 更换机器人末端工具,导致工具坐标系变化。
- 机器人本体经过维修或重新标定运动学。
- 长时间使用后精度下降,经排查确认与标定相关。
建议在项目上线时做一次完整标定,并保留标定数据记录。后续如果出现精度问题,可以对比历史数据,快速定位是标定问题还是机械问题。
8.4 标定结果管理
标定结果应保存为独立配置文件,包含:
- 相机内参。
- 手眼变换矩阵。
- 标定日期、操作人、标定板型号。
- 标定时的验证误差。
- 使用的机器人型号和工具型号。
这样无论什么时候出问题,都能回溯。
8.5 安全与权限提醒
在做标定时,需要控制机器人运动,整个过程存在安全风险。务必做到:
- 在安全区域内进行标定。
- 降低机器人运动速度。
- 确认周边没有人员进入。
- 操作人员具备机器人操作权限。
- 修改机器人配置前,先备份当前参数。
- 所有标定操作优先在测试环境验证,确认无误后再用于生产。
8.6 精度达不到要求的排查顺序
如果标定后精度仍然不达标,建议按照以下顺序排查:
- 确认机械安装是否牢固,相机支架是否晃动。
- 确认机器人重复定位精度是否满足项目需求。
- 重新做相机内参标定,排除内参误差。
- 检查机器人 TCP 标定是否准确。
- 重新采集标定数据,检查数据质量。
- 尝试不同手眼标定算法,对比结果。
- 如果所有标定环节都正常,再考虑视觉识别算法层面的优化。
这个顺序很重要,因为机械和标定问题远远比算法问题更常见。
9. 总结与后续学习方向
手眼标定是机器视觉项目中绕不开的关键环节。它解决的,是相机坐标系与机器人坐标系之间的对齐问题。很多项目“视觉抓不准”的根源,其实不在模型,而在这一层映射关系没有做好。
本文详细讲解了手眼标定的核心概念、两种主流安装方式、完整的数据采集流程、Python 代码实现、效果验证方法、常见问题排查方案和工程最佳实践。看完之后,建议你找一个实际的机器人平台,采集一批数据,亲手跑一遍完整流程。只有亲手做过一次,才能理解数据质量对标定结果的巨大影响。
接下来可以继续深入的方向包括:
- AX=XB 方程的数学原理和不同求解算法对比。
- 3D 相机手眼标定与 2D 相机标定的差异。
- 康耐视、Halcon 等商业视觉软件中的标定工具用法。
- 机器人运动学标定与手眼标定的联合优化。
- 基于深度学习的视觉定位与传统方法的精度对比实践。
机器人视觉抓取是一个系统工程,手眼标定只是第一步,但这一步走不稳,后面所有环节都会跟着偏。建议把本文收藏备用,下次现场遇到“总抓偏”的问题,先翻出来对照排查一遍。