第二章 相机标定:让机器人看懂三维世界的第一步
做双目视觉避障,第一步不是调算法,而是标定相机。
这话听起来有点枯燥,但我敢说,很多新手在这上面栽过跟头。我自己刚入行那会儿,拿着一个双目摄像头,对着棋盘格拍了上百张图,结果跑出来的深度图全是扭曲的——后来才发现,标定参数差了那么零点几个像素,整个三维重建就全偏了。
所以这一章,咱们把相机标定这件事彻底讲透。
2.1 相机标定到底在干什么?
说白了,相机标定就是搞清楚两件事:
- 内参:镜头本身的特性,比如焦距、光心位置、畸变系数
- 外参:两个相机之间的相对位置和朝向
你想想看,机器人要避障,得知道障碍物在三维空间里到底在哪儿。但相机拍到的只是二维像素点。怎么从像素坐标反推三维坐标?
嗯,这就需要内参和外参搭一座桥。
核心公式(针孔模型):
s * [u, v, 1]^T = K * [R | t] * [X, Y, Z, 1]^T
其中 K 是内参矩阵,R 和 t 是外参。没有标定,这个公式就是空壳。
我在项目里见过有人直接用厂家给的标称焦距,结果误差大到30%。记住:每一颗镜头都是独一无二的,必须现场标定。
2.2 张正友标定法:为什么它成了行业标准?
1998年,张正友提出了这个方法。到现在二十多年了,OpenCV里还在用。为什么?
- 只需要一个棋盘格,打印出来贴在平板上就行
- 不需要知道相机和棋盘格的精确位置
- 精度足够高,误差能控制在0.1像素以内
它的核心思路其实很巧妙:
- 先忽略畸变,用线性方法估算内参和外参
- 再考虑畸变,用非线性优化把参数调准
- 最后用最大似然估计收尾
我刚开始学的时候觉得这方法太绕了。后来自己手推了一遍公式才明白——先粗后精,这是工程上最实用的策略。
我的经验:张正友法要求棋盘格必须是平面。我见过有人用弯曲的纸板当标定板,结果标出来的畸变参数全是错的。标定板一定要贴在刚性平板上,玻璃板最好。
2.3 标定板制作:别在这步省事
标定板看着简单,但坑不少。我列几个关键点:
| 参数 | 推荐值 | 为什么 |
|---|---|---|
| 棋盘格尺寸 | 7×9 或 8×10 内角点 | 太少精度不够,太多容易遮挡 |
| 方格边长 | 20-30mm | 太小看不清,太大覆盖不了视野 |
| 打印精度 | 600dpi以上 | 边缘模糊会导致角点检测偏移 |
| 底板材质 | 亚克力或玻璃 | 保证绝对平面 |
我曾经用A4纸贴在纸箱上标定,结果标出来的焦距偏差了5%。后来换了亚克力板,一次就过了。
避坑指南:标定板不要反光!我吃过这个亏——在强光下用光面纸打印,角点检测全被高光吃掉。用哑光纸,或者喷一层哑光漆。
2.4 OpenCV标定实践:手把手走一遍
理论说完了,咱们直接上代码。我用的是OpenCV 4.x,C++版本。Python也类似,但嵌入式上C++更常见。
// 1. 读取图像并检测角点
std::vector<cv::Point2f> corners;
bool found = cv::findChessboardCorners(
image,
cv::Size(patternCols, patternRows),
corners,
cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE
);
// 2. 亚像素精化
cv::cornerSubPix(
gray,
corners,
cv::Size(11,11),
cv::Size(-1,-1),
cv::TermCriteria(cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 30, 0.1)
);
// 3. 标定
double rms = cv::calibrateCamera(
objectPoints, // 世界坐标系中的点
imagePoints, // 图像坐标系中的点
imageSize, // 图像尺寸
cameraMatrix, // 输出:内参矩阵
distCoeffs, // 输出:畸变系数
rvecs, tvecs, // 输出:外参
flags = cv::CALIB_FIX_PRINCIPAL_POINT
);
这里有个细节:CALIB_FIX_PRINCIPAL_POINT这个flag,我建议初学者先用上。为什么?因为光心位置如果自由优化,在图像数量不够时容易跑偏。等你有20张以上有效图像了,再放开它。
标定完成后,一定要看重投影误差:
std::cout << "重投影误差: " << rms << " 像素" << std::endl;
// 一般要求 < 0.5 像素,严格项目 < 0.3
我记得有一次标出来误差0.8像素,查了半天发现是标定板没放正,有几张图角度太单一。后来补拍了不同角度的图,误差降到0.2。
2.5 知识体系总览
下面这张图是我自己整理的标定流程,你照着走就不会乱:
2.6 标定后的验证:别信数字,信眼睛
标定程序跑完了,重投影误差0.3,看起来很漂亮。但别急着高兴。
我习惯做两件事验证:
- 畸变校正可视化:把标定前后的图像叠在一起看,边缘的直线有没有变直
- 立体校正测试:把左右图校正后,在水平方向画一条线,看对应点是否在同一行
有一次标定误差0.2,但立体校正后左右图差了3个像素。后来发现是标定板在拍摄时移动了——棋盘格和相机之间有相对运动,导致外参算错了。
我的习惯:标定完成后,拍一张新的棋盘格(不参与标定),用标定参数去重投影。如果误差和标定时差不多,说明参数泛化能力好。如果差很多,那就是过拟合了。
好了,相机标定这部分就到这里。记住:标定是双目视觉的基石,这一步省事,后面全是坑。下一章咱们聊特征点匹配——有了准确的标定参数,匹配才能又快又准。
公众号:蓝海资料掘金营,微信deep3321