4、双目标定:立体视觉标定、外参计算与极线校正

双目标定,说白了就是给两个相机做「联姻」。单目标定只关心每个相机自己看得清不清,双目标定要解决的是:两个相机之间到底什么关系?它们能不能配合起来算深度?

我刚开始做双目项目时,以为把两个相机分别标定好就完事了。结果一算三维坐标,误差大得离谱。后来才明白——双目标定的核心,是求两个相机之间的外参,也就是旋转矩阵 R 和平移向量 T。

4.1 双目标定的本质

双目标定要输出三样东西:

  • 左相机内参 + 畸变系数
  • 右相机内参 + 畸变系数
  • 左右相机之间的外参(R, T)

外参描述的是:从右相机坐标系到左相机坐标系的刚体变换。有了它,我们才能把左右两幅图像上的点对应起来,进而计算视差和深度。

关键公式: P_left = R * P_right + T
其中 P_left 和 P_right 是同一个空间点在左右相机坐标系下的坐标。

4.2 HALCON 中的 binocular_calibration 算子

HALCON 把双目标定封装成了一个算子——binocular_calibration。你不需要手动解方程,也不需要自己算本质矩阵。只要把左右相机的标定数据喂进去,它一次性给你算完。

我个人习惯用这个算子的标准流程:

  1. 分别对左右相机做单目标定,拿到各自的内参和畸变
  2. 拍摄一组左右同步的标定板图像(至少 10 对)
  3. 调用 binocular_calibration,传入左右相机标定数据和图像对
  4. 输出外参 R, T,以及优化后的内参
* 左相机标定数据
create_calib_data ('calibration_object', 'calibration_plate', 1, CalibDataL)
set_calib_data_cam_param (CalibDataL, 0, 'area_scan_division', StartCamParamL)
set_calib_data_calib_object (CalibDataL, 0, CalibObjDescr)
* 右相机标定数据
create_calib_data ('calibration_object', 'calibration_plate', 1, CalibDataR)
set_calib_data_cam_param (CalibDataR, 0, 'area_scan_division', StartCamParamR)
set_calib_data_calib_object (CalibDataR, 0, CalibObjDescr)

* 双目标定
binocular_calibration (CalibDataL, CalibDataR, NStartCamParamL, NStartCamParamR, \
                       CalibObjDescr, 'all', 'all', CalibObjFindL, CalibObjFindR, \
                       NCamParamL, NCamParamR, NCalibObjPoseL, NCalibObjPoseR, \
                       RelPose, Errors)
我的经验: 标定板在左右图像中的位置要尽量覆盖整个视野。如果只拍中间区域,外参在边缘会不准。我曾经吃过这个亏,后来每次拍 15 对以上,覆盖九宫格位置。

4.3 外参计算:R 和 T 的物理意义

外参 RelPose 输出的是一个 7 元组:[X, Y, Z, Rx, Ry, Rz, Type]。前三个是平移向量 T,后三个是旋转角度(或轴角)。

举个例子:如果 T = [100, 0, 0] mm,说明右相机在左相机右边 100mm。如果 Rx = 0.5°,说明右相机绕 X 轴转了半度。

参数 含义 典型值范围
X, Y, Z 右相机原点在左相机坐标系下的位置 几十到几百 mm
Rx, Ry, Rz 旋转角度(取决于 Type) 通常小于 5°
Type 旋转表示方式:'gba' 或 'axis_angle' 常用 'gba'

你想想看,如果两个相机完全平行,R 应该接近单位矩阵,T 只有 X 方向有值。但实际装配总有误差,所以 R 会有小角度偏移,T 也会有 Y 和 Z 分量。

4.4 极线校正:让图像对齐

极线校正的目的,是把左右图像变换到同一个平面上,让对应点在水平方向上对齐。这样后续的立体匹配只需要搜索同一行,效率翻倍。

HALCON 用 gen_binocular_rectification_map 生成校正映射:

* 生成校正映射
gen_binocular_rectification_map (MapL, MapR, NCamParamL, NCamParamR, RelPose, \
                                 'geometric', 'bilinear', RectL, RectR, CamParamRectL, CamParamRectR)

* 应用校正
map_image (ImageL, MapL, RectifiedL)
map_image (ImageR, MapR, RectifiedR)
注意: 校正后的图像会有黑边,因为原始图像边缘被旋转拉伸了。如果黑边太大,可以设置 'crop' 参数裁剪掉无效区域。但裁剪会损失视野,需要权衡。

校正完成后,你可以用 draw_epipolar_lines 画几条极线验证。如果极线是水平的,说明校正成功。我记得第一次做校正时,极线歪了 3 个像素,找了半天发现是标定板拍得太少了。

4.5 双目标定的避坑指南

  • 左右图像必须同步: 如果两个相机不是硬件触发,运动物体在左右图像中的位置会不一致,导致外参计算错误。我曾经用软件触发拍运动工件,结果外参误差 20%。后来换了硬件触发才解决。
  • 标定板不能太小: 标定板在图像中至少要占 1/4 面积。太小了角点检测精度不够,外参会飘。
  • 不要只用一组姿态: 标定板要倾斜、旋转、远近都拍。只拍正面的外参,在边缘区域会失效。
  • 检查重投影误差: binocular_calibration 输出的 Errors 包含重投影误差。如果大于 0.5 像素,说明标定质量有问题,需要重新拍。
快速验证方法: 校正后取一张左右图像,在左图上点一个特征点,看右图上同一行能不能找到对应点。如果垂直偏差超过 1 像素,说明外参或校正参数有问题。

4.6 双目标定知识体系

下面这张图总结了双目标定的核心流程和关键输出:

双目标定核心流程 左相机单目标定 内参 + 畸变 右相机单目标定 内参 + 畸变 同步标定图像对 ≥ 10 对,覆盖全视野 binocular_calibration 联合优化内外参 左相机内参 优化后的 fx, fy, cx, cy 右相机内参 优化后的 fx, fy, cx, cy 外参 RelPose R, T 矩阵 gen_binocular_rectification_map 生成校正映射 → 极线对齐 校正后图像 水平对齐,无垂直视差

从流程图可以看出,双目标定不是简单的「1+1=2」。它把左右相机的标定数据融合在一起,通过联合优化得到更精确的内参和外参。校正环节是承上启下的关键——没有好的外参,极线校正就是空中楼阁。

嗯,双目标定就讲到这里。记住一个原则:标定数据质量决定一切。多拍几组、覆盖全视野、检查重投影误差,这三步做到位,你的双目系统就稳了。