4. 双目相机标定
双目视觉定位的精度上限,首先由标定质量决定。单目内参告诉我们“像素如何映射到相机坐标系”,双目外参告诉我们“左右相机之间的刚体变换”,而极线校正则把这种变换固化为可高速计算的规则结构。三者缺一不可:内参不准,深度会整体缩放失真;外参不准,视差与深度不再一一对应;极线未校正,立体匹配搜索会退化为二维搜索,既慢又易错。因此,标定不是一次性的“初始化动作”,而是贯穿系统生命周期的精度基线。
一、核心原理:从张正友标定到双目联合标定
张正友标定法(Zhang's method)的核心思想是:用一块已知棋盘格或圆点阵列的平面标定板,在多个不同姿态下拍摄图像,利用平面单应性约束求解相机内参和畸变系数。其数学链条可概括为:世界平面点 → 单应矩阵 H → 内参约束 → 闭式解初值 → 最大似然优化。由于标定板是平面,每个姿态提供一组单应性方程,多姿态叠加即可分离出焦距、主点、畸变等参数。
双目联合标定则是在单目标定基础上,进一步求解右相机相对于左相机的旋转矩阵 R 和平移向量 t。常用做法是:左右相机同时拍摄同一标定板,分别得到各自的外参(标定板到相机的变换),再通过坐标系传递得到 R 和 t。更严谨的做法是把左右图像中的角点重投影误差放在同一个优化目标中联合优化,同时微调内参、外参和畸变系数,使整体误差最小。
关键理解:张正友标定解决“单个相机怎么看世界”,双目联合标定解决“左右相机如何互相对齐”。前者是基础,后者是双目深度的前提。
二、极线校正:把立体匹配变成一维搜索
未校正的双目图像中,同一个空间点在左右图中的对应点可能出现在任意位置,匹配算法需要在二维邻域内搜索,计算量大且容易误匹配。极线校正通过构造两个虚拟相机平面,使左右图像的极线水平对齐:同一空间点在左右图中的对应点位于同一行,视差只沿水平方向变化。这样,立体匹配只需在水平线上搜索,深度计算也简化为 Z = f * B / d,其中 f 为焦距,B 为基线,d 为视差。
极线校正通常包括:去畸变、计算校正旋转矩阵、生成映射表、重采样输出校正图像。校正后还需检查左右图像的行对齐质量,常用指标是极线误差(epipolar error),即同一特征点在左右图中的行坐标偏差。
三、标定误差评估:看什么指标,如何判断合格
标定完成后,不能只看“程序跑通了”,必须量化误差。常用指标包括:
- 重投影误差(Reprojection Error):将标定板角点按标定参数投影回图像,计算与检测角点的像素距离,通常取均方根误差(RMS)。单目一般要求小于 0.5 像素,双目联合标定可放宽到 1.0 像素以内。
- 极线误差(Epipolar Error):校正后同一特征点在左右图中的行坐标偏差,理想值为 0,工程上通常要求小于 0.5 像素。
- 基线一致性:标定得到的基线长度与实测物理基线对比,偏差应在毫米级。
- 深度误差:用已知距离目标验证,观察不同距离下的相对误差是否稳定。
| 评估指标 | 含义 | 合格参考 | 异常常见原因 |
|---|---|---|---|
| 重投影误差 RMS | 角点投影偏差 | < 0.5 px(单目) | 标定板不平、角点检测不准 |
| 极线误差 | 校正后行对齐偏差 | < 0.5 px | 外参不准、校正映射错误 |
| 基线偏差 | 标定基线与实测差 | < 2 mm | 左右相机不同步、标定板姿态少 |
| 深度相对误差 | 已知距离验证 | < 2%(近距) | 视差精度不足、纹理弱 |
四、实操流程:从采集到验证的完整闭环
下面给出一套可复用的双目标定实操流程,适用于 OpenCV 环境。核心步骤是:采集 → 单目标定 → 双目标定 → 极线校正 → 误差评估。
# 1. 采集左右图像:标定板覆盖画面不同区域,姿态不少于 15 组
# 建议:棋盘格 9x6,方格 25mm,左右同步拍摄
# 2. 单目标定(分别对左右相机)
cv2.calibrateCamera(objpoints, imgpoints_L, gray_L.shape[::-1], None, None)
cv2.calibrateCamera(objpoints, imgpoints_R, gray_R.shape[::-1], None, None)
# 3. 双目联合标定
ret, K_L, D_L, K_R, D_R, R, T, E, F = cv2.stereoCalibrate(
objpoints, imgpoints_L, imgpoints_R,
K_L, D_L, K_R, D_R, gray_L.shape[::-1],
flags=cv2.CALIB_FIX_INTRINSIC
)
# 4. 极线校正
R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(
K_L, D_L, K_R, D_R, gray_L.shape[::-1], R, T, alpha=0
)
# 5. 生成映射表并重采样
mapLx, mapLy = cv2.initUndistortRectifyMap(K_L, D_L, R1, P1, size, cv2.CV_32FC1)
mapRx, mapRy = cv2.initUndistortRectifyMap(K_R, D_R, R2, P2, size, cv2.CV_32FC1)
rectL = cv2.remap(imgL, mapLx, mapLy, cv2.INTER_LINEAR)
rectR = cv2.remap(imgR, mapRx, mapRy, cv2.INTER_LINEAR)
# 6. 误差评估:重投影误差 + 极线误差
# 重投影误差由 stereoCalibrate 返回的 ret 给出
# 极线误差可选取若干特征点,比较校正后左右行坐标差
实操建议:标定板不要只放在画面中央,要覆盖四角、边缘和不同倾斜角;左右相机尽量硬件同步触发;标定完成后保存 K、D、R、T、R1、R2、P1、P2、Q 及映射表,后续深度计算直接复用,避免每次重新标定。
五、常见误区与复盘
误区一:只做单目标定就用于双目深度。单目标定只能保证各自相机内部一致,无法保证左右相机之间的几何关系正确,深度会系统性偏移。
误区二:标定板姿态太少或过于集中。姿态多样性不足会导致外参解不稳定,尤其旋转分量容易过拟合。建议至少 15 组,且覆盖不同距离、角度和画面位置。
误区三:忽略极线校正后的实际行对齐。有些流程只生成校正图,不检查极线误差,导致立体匹配仍然在二维搜索,速度和精度双输。
误区四:标定一次用到底。无人机振动、温度变化、碰撞都会改变相机相对位姿。建议在关键任务前做快速验证:拍摄已知距离目标,检查深度误差是否漂移;若超限,重新标定。
排障提示:若重投影误差正常但深度误差大,优先检查基线是否准确、左右图像是否同步、极线校正是否真正行对齐;若极线误差大,优先检查外参 R、t 和校正映射表是否与当前图像分辨率匹配。
总结来说,双目相机标定的正确顺序是:先分别标定左右内参,再联合标定外参,然后做极线校正,最后用重投影误差、极线误差和深度误差三重验证。只有把标定误差控制在合理范围内,后续的立体匹配、视差计算和三维定位才有可靠基础。