3、单目标定基础:棋盘格标定板设计、角点检测原理、张正友标定法核心思想
好,咱们进入单目标定这个环节。说实话,这是整个多摄像头系统里最基础、也最容易被忽视的一步。很多人一上来就急着做双目标定、联合标定,结果发现数据对不上,回头一查——单目标定就没做扎实。
我个人习惯,做任何多传感器融合项目之前,先把每个传感器的内参和畸变系数标得清清楚楚。这就像盖房子打地基,地基歪了,上面再漂亮也没用。
3.1 棋盘格标定板设计——别小看这张格子纸
棋盘格,说白了就是一张黑白相间的格子图案。但这里面的门道,比你想象的多。
3.1.1 棋盘格的物理参数
设计棋盘格时,你需要明确两个关键参数:
- 内角点数量:比如 9×6,指的是内部角点,不是格子数。我见过有人把格子数当角点数用,结果程序报错半天找不出原因。
- 方格尺寸:单位通常是毫米。比如每个格子 30mm×30mm。
重要提醒:角点数量 = (列数-1) × (行数-1)。比如一张 10×7 的棋盘格,内角点是 9×6 = 54 个。
3.1.2 打印与使用建议
我在项目中遇到过最坑的事——用普通A4纸打印棋盘格,贴在纸板上。结果环境湿度一变,纸皱了,角点检测全偏了。
所以我的建议是:
- 用铝塑板或亚克力板做底板,保证平整
- 打印精度至少 600dpi,边缘要清晰
- 棋盘格尺寸不要太小,否则远距离拍摄时角点看不清
- 尺寸也不要太大,否则近距离拍摄时拍不全
我的经验:对于常见的工业相机(500万像素左右),棋盘格边长 25-40mm 比较合适。内角点数量推荐 9×6 或 11×8。
3.2 角点检测原理——计算机是怎么找到那些交叉点的?
你想想看,一张棋盘格图片里,计算机怎么知道哪些点是角点?
嗯,这里要讲一个经典算法——Harris 角点检测。不过 OpenCV 里用的其实是它的改进版,叫 findChessboardCorners。
3.2.1 角点的数学定义
角点,就是图像中两个边缘方向都发生剧烈变化的点。说白了,你往上下左右移动一个小窗口,如果窗口内的像素变化都很大,那这个点就是角点。
数学上,Harris 用了一个矩阵来描述这种变化:
M = ∑ [ Ix² IxIy ]
[ IxIy Iy² ]
其中 Ix 和 Iy 是图像在 x 和 y 方向的梯度。然后计算响应值 R:
R = det(M) - k * trace(M)²
R 大于某个阈值,就是角点。
3.2.2 OpenCV 中的实现
实际用的时候,你不需要自己写 Harris。OpenCV 已经封装好了:
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
这个函数会返回角点的像素坐标。但注意,它返回的坐标是亚像素级别的——什么意思?就是精度能达到小数点后几位。
我曾经踩过的坑:直接用 findChessboardCorners 返回的坐标做标定,结果重投影误差总是下不去。后来发现需要再用 cornerSubPix 做亚像素精细化。这一步不能省。
亚像素细化的代码:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
3.3 张正友标定法核心思想——为什么它成了行业标准?
张正友标定法,1998年提出的,到现在二十多年了,依然是单目标定的主流方法。为什么?因为它解决了两个核心问题:
- 不需要知道标定板的精确三维位置——你只需要拿着棋盘格在相机前晃一晃
- 精度足够高——对于绝大多数应用场景,重投影误差能控制在 0.1 像素以内
3.3.1 核心思想:从单应性矩阵出发
张正友的方法,说白了就是利用棋盘格是平面这个事实。因为棋盘格是平的,所以世界坐标系的 Z 坐标恒为 0。这样,投影方程就简化了。
投影方程简化后:
s * [u, v, 1]^T = K * [r1, r2, t] * [X, Y, 1]^T
其中 K 是内参矩阵,r1、r2 是旋转矩阵的前两列,t 是平移向量。
这个 3×3 的矩阵,就是单应性矩阵 H。每拍一张棋盘格图片,就能得到一个 H。
3.3.2 求解过程:两步走
张正友的方法分两步:
- 第一步:线性求解——利用多张图片的单应性矩阵,构建超定方程组,用最小二乘法求出内参的初始值
- 第二步:非线性优化——用最大似然估计,对所有参数(内参、畸变、外参)进行联合优化,最小化重投影误差
关键点:至少需要 3 张不同角度的棋盘格图片才能求解。我一般拍 15-20 张,覆盖视野的各个区域,尤其是边缘部分。
3.3.3 畸变模型
张正友方法还考虑了镜头畸变,主要是径向畸变和切向畸变:
x_distorted = x * (1 + k1*r² + k2*r⁴ + k3*r⁶) + 2*p1*x*y + p2*(r² + 2*x²)
y_distorted = y * (1 + k1*r² + k2*r⁴ + k3*r⁶) + p1*(r² + 2*y²) + 2*p2*x*y
其中 k1、k2、k3 是径向畸变系数,p1、p2 是切向畸变系数。
我的习惯:对于普通工业镜头,一般只标定 k1、k2 和 p1、p2 就够了。k3 通常用于鱼眼镜头。标定太多参数反而容易过拟合。
3.4 实战中的注意事项
最后,分享几个我在项目中积累的经验:
| 注意事项 | 说明 |
|---|---|
| 图片数量 | 至少 10 张,覆盖不同角度和距离。我一般拍 15-20 张 |
| 光照条件 | 避免反光和阴影。均匀光照最好 |
| 棋盘格姿态 | 要有倾斜、旋转、平移的变化。不要只拍正对着的 |
| 重投影误差 | 一般应小于 0.5 像素。如果大于 1 像素,说明标定有问题 |
| 标定板平整度 | 这是最容易忽略的。不平整的标定板,标定结果全是错的 |
嗯,单目标定这块内容就这些。下一章我们会讲双目标定,到时候你会看到,单目标定的结果会直接影响双目系统的精度。所以,这一章的内容,建议你动手实践一下,拍几张棋盘格跑一遍标定流程,感受一下。
记住一句话:标定不是一次性的工作。每次更换镜头、调整光圈、或者环境温度变化较大时,都建议重新标定一次。