3、视觉传感器与标定:让机器人的眼睛看得准
做视觉SLAM和机械臂协同控制,第一步就是搞定传感器。说白了,你得先让机器人看清楚这个世界,然后才能谈定位和抓取。这一章我们聚焦相机成像模型、标定方法和深度相机选型——这些都是后续所有工作的基础。
核心要点:相机标定的精度直接决定后续视觉定位和手眼协同的成败。我见过太多项目因为标定马虎,最后机械臂抓取偏差好几厘米,返工成本极高。
3.1 相机成像模型
相机怎么把三维世界变成二维图像?这背后是几何投影的数学关系。理解这个模型,你才能知道标定到底在标什么。
3.1.1 针孔模型
针孔模型是最基础的成像模型。想象一个暗箱,前面有个小孔,光线穿过小孔在后面的感光平面上成像。这个模型用四个坐标系来描述:世界坐标系、相机坐标系、图像坐标系、像素坐标系。
从三维点到像素的映射关系可以写成:
s * [u, v, 1]^T = K * [R | t] * [Xw, Yw, Zw, 1]^T
其中 K 是内参矩阵,包含 fx、fy(焦距)、cx、cy(主点偏移)。[R | t] 是外参,描述相机在世界坐标系中的位姿。
我个人习惯把内参理解为「相机自己的脾气」——每个镜头都不一样,必须通过标定来获取。外参则是「相机放在哪、朝哪看」,每次安装位置变了都要重新标。
3.1.2 畸变模型
现实中的镜头不是完美针孔。透镜的物理形状会引入畸变,主要是两种:
- 径向畸变:光线通过透镜边缘时弯曲更厉害,造成图像边缘的直线变弯。分为桶形畸变(负向)和枕形畸变(正向)。
- 切向畸变:透镜和成像平面不平行,导致图像倾斜。这个在制造装配中产生,通常比径向畸变小。
畸变模型用多项式来近似:
x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2)
y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y
其中 k1、k2、k3 是径向畸变系数,p1、p2 是切向畸变系数。标定就是求解这些参数。
经验之谈:我曾经在一个项目中用了廉价镜头,畸变特别大,边缘的棋盘格角点都变形了。标定后校正图像,发现校正后的边缘分辨率损失严重。后来我换了个好点的镜头,问题迎刃而解。所以,镜头质量直接影响标定效果和后续视觉精度。
3.2 相机内参标定:张正友法
张正友法是目前最主流的相机标定方法。它只需要拍摄不同角度的棋盘格图像,就能解算出内参和畸变系数。为什么它这么流行?因为操作简单、精度高、鲁棒性好。
基本流程是这样的:
- 准备棋盘格:打印一张棋盘格,贴在平整的板上。格子数量建议 9×6 或 7×5,每个格子边长已知。
- 采集图像:从不同角度拍摄 15-20 张棋盘格图像。注意覆盖视野的各个区域,尤其是边缘。
- 角点检测:用 OpenCV 的
findChessboardCorners()提取角点。 - 求解内参:先忽略畸变,用线性方法求解内参初值。然后加入畸变模型,用非线性优化(比如 Levenberg-Marquardt)迭代求精。
- 评估结果:看重投影误差,一般小于 0.5 像素就算不错。
代码实现很简单:
import cv2
import numpy as np
# 棋盘格尺寸
pattern_size = (9, 6)
square_size = 0.025 # 25mm
# 准备世界坐标系中的点
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)
objp *= square_size
# 存储所有图像的点
objpoints = [] # 3D点
imgpoints = [] # 2D点
# 遍历图像
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)
避坑指南:我曾经遇到标定结果重投影误差很小,但实际测量偏差很大的情况。后来发现是棋盘格没贴平,有微小褶皱。另外,拍摄时不要只拍中间区域,一定要让棋盘格出现在图像边缘和角落,否则畸变参数估计不准。
3.3 手眼标定
相机标定好了,但相机是装在机械臂上的。怎么把相机看到的物体位置告诉机械臂?这就需要手眼标定。
3.3.1 Eye-in-Hand vs Eye-to-Hand
两种安装方式,标定逻辑完全不同:
| 对比项 | Eye-in-Hand(眼在手上) | Eye-to-Hand(眼在手外) |
|---|---|---|
| 相机位置 | 固定在机械臂末端 | 固定在外部,不随机械臂运动 |
| 标定目标 | 求相机坐标系到末端坐标系的变换 | 求相机坐标系到基座坐标系的变换 |
| 优势 | 视野随机械臂移动,灵活;近距离测量精度高 | 视野固定,适合全局监控;标定后不用再动 |
| 劣势 | 标定复杂;机械臂运动时图像会模糊 | 远距离测量精度受限;可能被机械臂遮挡 |
| 典型应用 | 抓取、装配、焊接 | 视觉引导、安全监控、全局定位 |
我个人更倾向于 Eye-in-Hand 做精密抓取。为什么呢?因为相机跟着机械臂走,可以凑近看,精度高。但标定确实麻烦一些。
3.3.2 标定原理与步骤
手眼标定的核心是求解 AX = XB 方程。A 是机械臂末端位姿变化,B 是相机看到的标定板位姿变化,X 就是我们要找的手眼变换矩阵。
具体步骤:
- 在机械臂工作空间内固定一个标定板。
- 控制机械臂运动到多个不同位姿,记录每个位姿的关节角度和末端位姿。
- 在每个位姿拍摄标定板图像,用相机标定结果解算标定板在相机坐标系下的位姿。
- 构建 AX = XB 方程组,用 Tsai 法或 Park 法求解。
我用 OpenCV 的 calibrateHandEye() 函数实现过:
import cv2
import numpy as np
# R_gripper2base, t_gripper2base: 机械臂末端在基座坐标系下的位姿(多组)
# R_target2cam, t_target2cam: 标定板在相机坐标系下的位姿(多组)
R_cam2gripper, t_cam2gripper = cv2.calibrateHandEye(
R_gripper2base, t_gripper2base,
R_target2cam, t_target2cam,
method=cv2.CALIB_HAND_EYE_TSAI
)
经验之谈:标定时机械臂的运动幅度要大,旋转角度最好超过 30 度,平移距离超过 10cm。如果运动太小,方程会病态,解出来的结果误差很大。我踩过这个坑,后来每次标定都让机械臂走一个「大范围」的轨迹。
3.4 深度相机选型
单目相机只能得到二维图像,没有深度信息。要做三维定位,需要深度相机。目前主流的有三种技术路线。
3.4.1 三种技术对比
| 技术 | 原理 | 工作距离 | 精度 | 抗环境光 | 典型产品 |
|---|---|---|---|---|---|
| 结构光 | 投射红外点阵/条纹,根据变形计算深度 | 0.1-5m | 高(mm级) | 弱(室内可用) | Kinect v1, Intel RealSense D415 |
| ToF(飞行时间) | 发射红外脉冲,测量往返时间 | 0.3-10m | 中(cm级) | 较强 | Kinect v2, Azure Kinect, PMD |
| 双目立体视觉 | 两个相机视差计算深度 | 0.1-20m+ | 中高(依赖基线) | 强(被动式) | ZED, Intel RealSense T265 |
3.4.2 选型建议
怎么选?我根据项目经验给几个建议:
- 室内近距离抓取(0.3-1m):结构光最合适,精度高。我用 RealSense D415 做过桌面抓取,效果不错。
- 中距离导航(1-5m):ToF 更稳定,不受纹理影响。Azure Kinect 在这个范围表现很好。
- 室外或大范围(5m+):双目是唯一选择,因为它不依赖主动光源。但要注意,双目在弱纹理区域(比如白墙)会失效。
- 高动态场景:ToF 的帧率可以做到 30-60fps,双目也能做到 30fps,结构光稍慢。
避坑指南:我曾经在阳光直射的车间用结构光相机,深度图全是噪点,根本没法用。后来换成双目,虽然精度差一点,但至少能稳定工作。所以,环境光评估一定要在选型前做。
3.5 本章知识体系
下面这张图把本章的核心内容串起来了:
从这张图可以清楚看到,整个视觉传感器与标定的知识体系分为三大块:先理解相机成像模型,然后做内参标定,再根据安装方式做手眼标定,最后根据应用场景选深度相机。每一步都是下一步的基础,环环相扣。
本章小结:相机标定是视觉SLAM和机械臂协同控制的「第一块基石」。内参标定解决「相机怎么看」的问题,手眼标定解决「相机和机械臂怎么配合」的问题,深度相机选型解决「用什么看」的问题。这三件事做好了,后面的定位和抓取才有精度保障。