2. 传感器选型与标定
做无序分拣,第一步就是选一双好「眼睛」。我见过不少项目,算法调得再好,相机选错了,照样抓不准。今天咱们就把3D相机选型和标定这件事聊透。
2.1 三种主流3D相机技术对比
目前工业分拣用得最多的就三种:结构光、ToF、双目。各有各的脾气,我一个个说。
| 技术类型 | 原理 | 典型精度 | 适用场景 | 典型代表 |
|---|---|---|---|---|
| 结构光 | 投射编码条纹,三角法解算深度 | 0.1~1 mm | 近距离高精度分拣(<1m) | Ensenso、Kinect v1 |
| ToF | 发射调制光脉冲,测量飞行时间 | 1~10 mm | 中远距离、快速抓取(0.5~5m) | Basler ToF、Kinect v2 |
| 双目 | 双摄像头视差匹配,三角测量 | 1~5 mm | 室外/大视野、纹理丰富场景 | ZED、Intel RealSense D435 |
我的经验:结构光在反光件上容易「翻车」。有一次分拣镀铬金属件,结构光直接丢帧。后来换成双目加主动纹理投影,才算稳住。选型时一定拿实际工件去测,别只看参数表。
2.2 相机内参标定
内参标定,说白了就是搞清楚相机自己的「脾气」——焦距、主点、畸变系数。不标定,你算出来的3D坐标全是歪的。
2.2.1 标定流程
- 准备标定板:棋盘格或圆点阵,打印要平整,贴在刚性板上。
- 采集图像:至少15~20张,覆盖不同角度、距离、姿态。
- 角点检测:用OpenCV的
findChessboardCorners或findCirclesGrid。 - 求解内参:调用
calibrateCamera,得到相机矩阵K和畸变系数。 - 验证重投影误差:误差小于0.5像素才算合格。
避坑指南:我曾经用A4纸打印棋盘格贴在纸箱上标定,结果纸箱微弯,标定误差直接飙到2像素。后来换了3mm铝板贴标定板,误差降到0.2像素。记住——标定板必须平整。
2.2.2 代码示例
import cv2
import numpy as np
# 棋盘格尺寸(内角点)
pattern_size = (9, 6)
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
objpoints = [] # 世界坐标系点
imgpoints = [] # 图像坐标系点
for fname in image_files:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
print("相机内参矩阵:\n", mtx)
print("畸变系数:\n", dist)
2.3 手眼标定
相机标好了,还得知道它和机器人手臂的相对位置。这就是手眼标定。分两种:Eye-in-Hand(眼在手上)和 Eye-to-Hand(眼在手外)。
2.3.1 两种方式对比
| 项目 | Eye-in-Hand | Eye-to-Hand |
|---|---|---|
| 相机安装位置 | 固定在机器人末端 | 固定在工作台上方 |
| 标定目标 | 求相机→末端(手眼矩阵) | 求相机→基座(手眼矩阵) |
| 视野特点 | 随机械臂移动,视野变化 | 视野固定,覆盖整个工作区 |
| 典型应用 | 小件抓取、近距离操作 | 大范围分拣、料筐抓取 |
| 标定复杂度 | 中等(需多次移动末端) | 较低(一次标定即可) |
注意:Eye-in-Hand标定时,机器人末端姿态变化要足够大,否则解算会退化。我见过有人只平移不旋转,结果标定矩阵全是错的。至少让末端做3个以上不同方向的旋转。
2.3.2 标定原理
核心方程是 AX = XB,其中A是机器人末端位姿变化,B是相机看到的标定板位姿变化,X就是我们要的手眼矩阵。用Tsai两步法或OpenCV的 calibrateHandEye 求解。
import cv2
import numpy as np
# R_gripper2base, t_gripper2base: 机器人末端位姿(从机器人控制器读取)
# R_target2cam, t_target2cam: 标定板在相机坐标系下的位姿(从PnP求解)
R_cam2gripper, t_cam2gripper = cv2.calibrateHandEye(
R_gripper2base, t_gripper2base,
R_target2cam, t_target2cam,
method=cv2.CALIB_HAND_EYE_TSAI
)
print("手眼旋转矩阵:\n", R_cam2gripper)
print("手眼平移向量:\n", t_cam2gripper)
2.3.3 标定步骤(以Eye-in-Hand为例)
- 将标定板固定在工作台上,位置不变。
- 控制机器人末端移动到不同姿态,记录末端位姿
T_base2gripper。 - 在每个姿态下,用相机拍摄标定板,通过PnP求解
T_cam2target。 - 收集至少15组数据,调用
calibrateHandEye求解。 - 验证:用标定结果将相机检测到的点转换到机器人基座下,与真实位置对比。
我的习惯:标定完成后,我会在机器人工作空间内随机取5个点,用标定结果反算,再手动示教机器人去碰那个点。误差在2mm以内才算通过。这一步别省,省了后面调试抓取有你哭的。
2.4 本章知识体系
2.5 选型与标定的常见坑
- 选型只看分辨率:分辨率高不代表精度高。结构光的精度取决于投射器基线长度,ToF的精度受多路径干扰影响大。我建议你拿实际工件去现场测,别信宣传页。
- 标定板随便用:标定板不平、打印精度差、反光,都会导致标定失败。花几十块钱买块陶瓷标定板,比打印纸强一百倍。
- 手眼标定数据太少:少于10组数据,解算结果不稳定。我一般采集20~30组,覆盖工作空间的不同区域。
- 忽略畸变校正:有些人不做畸变校正直接做手眼标定,结果边缘区域误差大得离谱。先内参标定,再手眼标定,顺序别搞反。
一句话总结:选型看工件,标定看流程。结构光适合小件高精度,ToF适合大范围快速,双目适合纹理丰富场景。内参标定是基础,手眼标定是桥梁。这两步做扎实了,后面的抓取规划才有意义。