1. 传感器标定基础
什么是传感器标定?
传感器标定,说白了就是给传感器做一次「体检」和「校准」。
我刚开始做SLAM那会儿,总觉得标定是个可有可无的步骤。直到有一次,我在一个室内停车场做测试,明明算法写得没问题,地图就是拼不齐,轨迹一直在飘。折腾了两天,最后发现是相机内参出了问题——出厂参数跟实际差了0.3个像素。嗯,从那以后,我再也不敢跳过标定了。
传感器标定的本质,是建立传感器内部测量值与真实物理量之间的映射关系。对于视觉SLAM来说,我们需要知道:
- 像素坐标(u, v)对应到相机坐标系下的什么方向?
- 不同传感器之间的相对位置和姿态是怎样的?
- 镜头畸变造成了多大的图像变形?
这些问题的答案,都藏在标定参数里。
为什么标定对跟踪至关重要?
你想想看,SLAM的核心任务之一就是跟踪——跟踪特征点、跟踪相机位姿、跟踪地图点。如果传感器本身的数据就是「歪」的,那后续所有计算都会跟着跑偏。
标定误差对跟踪的影响,我总结为三个层面:
- 特征匹配失败:畸变校正不准,同一个角点在左右图像上的位置对不上
- 三角化精度下降:外参有偏差,深度估计直接崩掉
- 位姿估计漂移:内参不准,本质矩阵分解出来的旋转和平移全是错的
我记得有个项目,用双目相机做AGV导航。标定的时候偷了个懒,用了厂家给的参数没做精标。结果在直线行驶时,左右目视差一直有0.5个像素的系统偏差。别看这0.5像素,换算成3米处的深度误差,直接差了15厘米——AGV撞了三次货架,我才意识到问题出在标定上。
标定的核心参数
标定参数分三大类,我一个个说。
1. 内参(Intrinsic Parameters)
内参描述的是传感器内部的几何特性。对于相机来说,就是镜头和感光芯片之间的映射关系。
标准的内参矩阵长这样:
K = [fx 0 cx]
[ 0 fy cy]
[ 0 0 1]
- fx, fy:焦距,单位是像素。说白了就是镜头到成像平面的距离,用像素个数来衡量
- cx, cy:主点坐标,理论上应该在图像正中心,但实际总有偏移
我的经验:fx和fy的比值越接近1,说明镜头畸变越小。如果比值超过1.05,建议换个镜头。我在一个项目里用过廉价USB摄像头,fx/fy=1.12,标定完直接放弃了——畸变太大,边缘特征点根本没法用。
2. 外参(Extrinsic Parameters)
外参描述的是传感器之间的相对位置关系。在多传感器融合中,这是最容易被忽视、也最容易出问题的地方。
外参包括:
- 旋转矩阵 R:3x3矩阵,描述两个坐标系之间的旋转关系
- 平移向量 t:3x1向量,描述两个坐标系原点之间的位移
举个例子,如果你用相机+IMU做融合,外参就是相机坐标系到IMU坐标系的变换矩阵:
T_cam_imu = [R t]
[0 1]
注意:外参标定不是一次性的。我曾经遇到过,相机和IMU的固定支架在振动环境下松动了0.5毫米,结果VIO的精度直接掉了30%。所以,定期检查外参很有必要。
3. 畸变系数(Distortion Coefficients)
镜头不是完美的,光线经过透镜时会发生弯曲,这就是畸变。畸变主要分两种:
- 径向畸变:光线在透镜边缘弯曲更严重,表现为图像边缘的直线变弯
- 切向畸变:透镜和成像平面不平行,表现为图像倾斜
常用的畸变模型有5个参数(k1, k2, p1, p2, k3):
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是修正项。
避坑指南:我曾经用鱼眼镜头做SLAM,畸变系数k1=-0.35,边缘畸变非常严重。当时没做去畸变就直接提取ORB特征,结果图像边缘的特征点全匹配错了。后来老老实实先做去畸变,再提取特征,问题就解决了。
标定的知识体系
下面这张图,是我自己整理的标定知识框架。你看一眼就能明白标定在整个SLAM系统中的位置:
从这张图你可以看到,标定参数是整个SLAM系统的「地基」。地基不稳,上面盖的楼再漂亮也得塌。
标定的常见方法
目前最常用的标定方法是张正友标定法。你只需要打印一张棋盘格,从不同角度拍20-30张照片,就能解算出内参和畸变系数。
我用OpenCV做过很多次标定,核心步骤就这几行代码:
# 找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (cols, rows), None)
# 标定相机
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
# 去畸变
undistorted = cv2.undistort(img, K, dist)
我的建议:拍标定照片时,注意让棋盘格覆盖图像的各个区域——四个角、中心、边缘都要有。我见过有人只拍了中心区域的照片,标定出来的畸变系数在边缘完全不准。另外,棋盘格不要太小,建议占图像面积的1/4到1/3。
标定质量的评估
标定完了,怎么知道标得好不好?我一般看三个指标:
| 指标 | 合格标准 | 说明 |
|---|---|---|
| 重投影误差 | < 0.5 像素 | 标定点反投影到图像上的误差,越小越好 |
| fx/fy比值 | 0.95 ~ 1.05 | 接近1说明镜头质量好 |
| 主点偏移 | < 图像宽高的5% | cx偏离图像中心太多说明镜头装配有问题 |
如果重投影误差超过1个像素,我建议重新标定。检查一下是不是棋盘格照片拍得不够好,或者标定板本身不平整。
我曾经踩过的坑:有一次标定双目相机,重投影误差只有0.3像素,看起来很好。但实际做立体匹配时,视差图全是噪声。后来发现是左右目的同步有问题——两个相机采集时间差了20毫秒,运动场景下图像对不上。所以标定不仅要看参数,还要检查传感器本身的硬件同步。
好了,这一章的内容就到这里。标定是SLAM的基石,花时间把标定做好,后面的跟踪和建图才能事半功倍。