SensorsCalibration 环绕相机标定指南:四套 AVM 工具(无标定板/标定板/手动/鱼眼)的原理与实战

原创2026-10-15 13:21:331,669 阅读
文章标签:自动驾驶计算机视觉点云图像处理

SensorsCalibration 环绕相机标定指南:四套 AVM 工具(无标定板/标定板/手动/鱼眼)的原理与实战

导读

本文围绕开源多传感器标定工具箱 SensorsCalibration 中的 surround-camera 模块,系统讲解自动驾驶环视(Surround View / AVM)相机外参标定的四套工具:面向普通环视相机的 target-less 自动标定(auto_calib)、面向环视鱼眼相机的 target-less 自动标定(auto_calib_fisheye)、基于标定板的产线级自动标定(auto_calib_target),以及支持 pinhole / Ocam / fisheye 三种模型的交互式手动标定(manual_calib)。读完本文,你将掌握每套工具的适用场景、编译运行方式、命令行参数、相机模型选择依据,以及从 BEV 投影、纹理提取到随机搜索优化(coarse-to-fine)的完整算法链路,并能在真实部署中根据传感器类型(普通相机或鱼眼)与场景(道路 or 产线)正确选择工具。

一、模块总览:一套 AVM 工具箱,四种标定路线

surround-camera/README.md 将本模块定位为 "a calibration toolbox for surround view cameras or surround view fisheye cameras",即同时覆盖普通环视相机与环视鱼眼相机。四套工具的选型关系可用下表概括(来源于该 README 的工具矩阵):

calibration param calibration type calibration method mannual calibration auto calibration usage documentation
surround_cameras (fisheye) extrinsic target-less ✔ manual_calib
surround_cameras (fisheye) extrinsic target-less ✔ auto_calib_fisheye
surround_cameras extrinsic target-less ✔ auto_calib
surround_cameras extrinsic target ✔ auto_calib_target

几点关键结论:

  • 四套工具全部解决**外参(extrinsic)**标定问题,即各环视相机相对车体(或 BEV 地面坐标系)的旋转和平移。
  • 前三套标定路线均为 target-less(无标定板),依靠场景自然纹理完成自动优化;只有 auto_calib_target 属于 target(标定板) 路线,且 README 明确指出它 可用于环视相机生产线的标定("This tool can be used for production line calibration of surround view cameras")。
  • 手动标定(manual_calib)同时支持普通环视相机与鱼眼环视相机,是唯一同时覆盖两种传感器的手动方案。

从仓库目录结构看,四个子模块的实现高度同构:auto_calib 与 auto_calib_fisheye 均包含 include/{optimizer.h, texture_extractor.h, transform_util.h} 与 src/{calibration.cpp, optimizer.cpp, texture_extractor.cpp},二者的核心区别在于主程序入口(main)中选择的 camera_model 与 data_set;manual_calib 依赖 include/extrinsic_param.hpp 与 include/intrinsic_param.hpp 从 JSON 加载内参/外参;auto_calib_target 则包含独立的 src/calibration.cpp(solvePnP 求解外参)与 src/stitching.cpp(环视拼接验证)。

二、编译与运行:统一构建流程与可执行文件约定

四套工具都采用标准 CMake 构建流程,各子模块 README 中给出的编译步骤完全一致:

mkdir -p build && cd build
cmake .. && make

构建产物统一输出到模块根目录下的 bin/ 目录(由各 CMakeLists.txt 的 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) 决定,见 auto_calib/CMakeLists.txt)。

各工具的可执行文件与运行方式如下:

工具 可执行文件 运行方式(在模块根目录执行)
auto_calib run_AVM_Calibration cd ~./auto_calib/ 后 ./bin/run_AVM_Calibration
auto_calib_fisheye run_AVM_Calibration cd ~./auto_calib_fisheye/ 后 ./bin/run_AVM_Calibration
auto_calib_target run_AVM_Calibration / run_stitching ./bin/run_AVM_Calibration 或 ./bin/run_stitching
manual_calib run_avm 见下节命令行参数

其中 auto_calib 与 auto_calib_fisheye 的 CMakeLists 均为 project(online_Calibration)、add_executable(run_AVM_Calibration src/calibration.cpp)(fisheye 版 与 普通版 完全一致),因此可执行文件名相同,需要放在各自目录下运行。

2.1 依赖与构建前提

从 CMakeLists 可归纳出构建依赖:

  • auto_calib / auto_calib_fisheye:C++17(-std=c++17 -pthread,用于多线程随机搜索)、Eigen3(/usr/include/eigen3)、OpenCV(find_package(OpenCV REQUIRED),代码中用到 fisheye::distortPoints、remap、findContours 等接口)。
  • auto_calib_target:C++11、Eigen3、OpenCV;CMake 默认在 /usr/local/opencv-2.4.13/share/OpenCV/ 下查找 OpenCV 2.4.13(NO_DEFAULT_PATH),若使用其他版本需自行调整该路径。
  • manual_calib:C++11、Pangolin(GUI 显示)、Boost(filesystem、system)、OpenCV、jsoncpp(libjsoncpp.a),链接关系见 manual_calib/CMakeLists.txt。

2.2 运行示例与输出

无标定板自动标定(auto_calib / auto_calib_fisheye)为无参数交互程序:入口 main 直接读取 ./imgs/ 下的 Front/Left/Back/Right 四张图像(普通版为 png、鱼眼版为 jpg),标定过程中会依次打印左/右/后相机各阶段(1st 粗搜、2nd 精调、3rd 微调)的 luminosity loss、最优搜索参数(roll/pitch/yaw/tx/ty/tz)与优化后的外参矩阵,最终在模块根目录输出拼接结果图 after_all_calib.png,并将各阶段对比图写入 after_*_calib*.png(详见 auto_calib/src/calibration.cpp)。

auto_calib_target 的 run_stitching 直接读取 imgs/front_125.png 等四张图像,投影到地面后用 addWeighted 叠加生成并保存 surround_view.png(stitching.cpp)。

三、无标定板自动标定:auto_calib 与 auto_calib_fisheye

这两套工具共享同一套算法框架(optimizer + texture_extractor),仅通过 main 中的三个配置项区分:

// camera_model: 0-fisheye; 1-Ocam; 2-pinhole
int camera_model = 2;        // auto_calib 用 pinhole
int camera_model = 0;        // auto_calib_fisheye 用 fisheye
string data_set = "common";  // auto_calib
string data_set = "fisheye"; // auto_calib_fisheye

camera_model 控制投影时使用的畸变模型(见 Optimizer::project_on_ground 中 fisheye::distortPoints / distortPointsOcam / distortPoints 三分支),data_set 则决定内参、畸变、外参初值、BEV 高度与裁剪尺寸等初始化参数(见 optimizer.cpp 的 initializeK/initializeD/initializePose/initializeKG/initializeHeight/initializetailsize)。

3.1 算法主链路:BEV 投影 → 公共视野纹理提取 → 光度一致性优化

整个标定流程(以 fixed = "front" 固定前相机为例)可分为四步:

  1. 生成初始 BEV 鸟瞰图:利用四台相机初始外参 T_CG(ground→camera)、内参 K_C、畸变 D_C 与地面相机内参 K_G,对地面网格像素逐个反投影、畸变、remap 采样,得到四张 BEV 图(project_on_ground,代码见 optimizer.cpp)。
  2. 裁剪公共视野区域:Optimizer::tail 按 sizef/sizel/sizeb/sizer 保留前/左/后/右相机各自靠近车身一侧的条带区域,其余区域补黑(optimizer.cpp),使相邻相机之间形成可比较的公共视野。
  3. 纹理提取:extractor 对相邻 BEV 对(fl/fr/bl/br)做二值化(Binarization,两图同位置均非黑则置 255)、膨胀腐蚀与 findContours 提取最大轮廓,再按梯度幅值筛选纹理点并随机下采样 500 个点,同时计算曝光补偿系数 ncoef(两图公共视野平均灰度的比值)与各相机灰度gamma校正(见 texture_extractor.cpp)。
  4. 光度一致性随机搜索优化:以待标定相机外参微扰 deltaT 将 BEV 纹理点重投影到相邻相机图像上,计算灰度差平方和作为 loss,用多线程随机采样最小化。

标定顺序上,前相机固定,先标 right,再标 left,最后标 behind(main 中的调用顺序),每次标定完会用最优外参重新生成该相机 BEV 并更新公共视野纹理。

3.2 粗到精(coarse-to-fine)三阶段搜索策略

CameraOptimization(见 auto_calib/src/calibration.cpp)对每个相机依次执行三阶段搜索,各阶段参数如下:

阶段 线程数 迭代次数 roll/pitch/yaw 搜索范围 tx/ty/tz 搜索范围
1st 粗搜(coarse) 7 100000/线程 [-3°, 3°] 按 x/y/z 轴拆分区间 [-0.01, 0.01] m
2nd 精调(fine) 6 15000/线程 [-1°, 1°] 为主 [-0.005, 0.005] m 为主
3rd 微调(fine) 3 8000/线程 [-0.01°, 0.01°] [-0.002, 0.002] m

第一阶段的 7 个线程将三维姿态空间(每个轴 -3,3] 度)拆分成若干子区间并行搜索(例如 Calibrate_right(iter_nums, -3, 3, -3, 3, -3, 3, ...)),随后用 fine_random_search_params 在更小邻域继续细化。搜索过程中使用独立 mutex 保护各相机的最优解更新(mutexleft/mutexright/mutexbehind),并且只有 loss 严格下降才接受新外参(random_search_params 与 fine_random_search_params 中的 loss_new < cur_*_loss 判断,见 [optimizer.cpp)。

对畸变模型的适配细节:优化过程中 back_camera_and_compute_loss 重投影纹理点时同样按 camera_model 选择 fisheye::distortPoints、distortPointsOcam 或简化 pinhole 投影(optimizer.cpp)。

3.3 两种数据集的关键初始化参数对比

data_set 为 "common"(auto_calib)与 "fisheye"(auto_calib_fisheye)时,初始化参数差异如下(均取自 initialize* 系列函数):

参数 common(pinhole) fisheye
相机内参 K(示例 Front) fx=fy=390.425287, cx=cy=750 fx≈422.13, cx≈612.83, cy≈545.06(四相机各异)
畸变 D(示例 Front) [0,0,0,0] [-0.07031853, 0.00387505, -0.00333139, 0.00056406]
地面 BEV 内参 K_G fx=fy=390.425287, cx=cy=750 fx=1/0.15, fy=-1/0.15, cx=bcols/2, cy=brows/2
相机高度 h 5.1 m(四相机) 1.0 m(四相机)
tail 裁剪尺寸 sizef=sizel=sizer=450, sizeb=350 sizef=340, sizel=sizer=390, sizeb=380
BEV 尺寸 1500×1500 1000×1000

需要说明的是,common 数据集的初始外参 T_CG 中还叠加了人为施加的姿态扰动(left/right/behind 的扰动欧拉角与平移见 optimizer.cpp),用于模拟真实安装误差并验证优化算法的收敛能力;fisheye 数据集则直接使用 ROCES 标定结果作为初值。

3.4 曝光一致性处理

由于四台相机曝光参数未必一致,公共视野两侧灰度可能存在系统性偏差。纹理提取阶段通过 exposure_flag 开关控制:开启时 ncoef = mean(gray1) / mean(gray2)(公共视野平均灰度比),优化 loss 计算中将参照侧灰度除以该系数后再做差,从而把曝光差异从几何误差中解耦。auto_calib 中 fl/fr 对 exposure_flag=0、bl/br 对 exposure_flag=0,而 auto_calib_fisheye 全部置 1,且对 bl/br 还启用了公共视野边缘过滤(edge_flag=1,按与相机中心的距离剔除贴近 BEV 边缘的弱纹理点)。这些开关均为 main 中的可调参数(auto_calib_fisheye/src/calibration.cpp)。

3.5 结果可视化对比

auto_calib 与 auto_calib_fisheye 在优化前会先用初始外参生成 before_all_calib.png,优化结束后用 SaveOptResult 基于最终外参生成 after_all_calib.png。两个版本仓库内均附带示例结果图:

注意:标定板在无标定板自动标定中仅起到提供纹理信息的作用(README 原文 "the calibration board here only provides some texture information"),并不参与几何约束求解。

四、基于标定板的产线级自动标定:auto_calib_target

auto_calib_target 面向生产场景,思路与无标定板路线完全不同:它利用已知车体系 3D 点与图像 2D 点的一一对应,通过 solvePnP 直接求解各相机外参。

4.1 求解流程:已知 3D 点 + 2D 点 → solvePnP

src/calibration.cpp 的 main 中:

  1. InitializeK 为四台相机设置相同的针孔内参(fx=fy=390.425287, cx=cy=750),InitializeD 将畸变置零(5 维向量全 0)。
  2. Initialize3dPoints 定义标定板特征点在车体坐标系下的 3D 坐标,例如前相机前方 x=3.5 m 平面上的 12 个点((3.5, ±0.12, ±0.36) 等),左/后/右相机分别对应 y=2.0 / x=-3.0 / y=-2.0 平面上的对称点组(见 calibration.cpp)。
  3. Initialize2dPoints 给出同一组特征点在四张图像中的像素坐标(2D 匹配点,calibration.cpp)。
  4. 对每台相机依次调用 solvePnP(pts_3d, pts_2d, K, D, rvecs, translation) 求解旋转向量与平移,再经 Rodrigues 转为旋转矩阵,hconcat 拼成 4×4 外参矩阵 T_F/T_L/T_B/T_R 并打印(calibration.cpp)。

因此,要移植到自己的产线,核心工作量在于替换上述 3D 点(由标定板相对车体的精确安装位置决定)与 2D 点(标定板角点/特征在图像中的检测结果,当前仓库为手工预设样本)。

4.2 拼接验证工具 run_stitching

src/stitching.cpp 提供独立的环视拼接验证程序:读取 imgs/front_125.png 等四张图,用初始化函数中的内参/外参投影到 1500×1500 的 BEV(dX=dY=0.005,即 5 mm/像素),将各相机 BEV 裁剪掉互相遮挡的区域后以 0.5 权重 addWeighted 叠加生成 surround_view.png(stitching.cpp)。该工具可快速检验 auto_calib_target 求出的外参是否能让环视画面无缝拼接。

仓库内附带四张带标定板图像的 BEV 投影验证结果:前 img_GF.png、左 img_GL.png、后 img_GB.png、右 img_GR.png,以及最终 surround_view.png。

五、交互式手动标定:manual_calib

manual_calib 提供基于 Pangolin 的实时可视化手动微调工具,支持 pinhole、Ocam(全方位相机)、fisheye 三种相机模型,仓库内附 pinhole_samples 与 ocam_samples 两套示例数据。

5.1 命令行参数与运行示例

程序要求 5 个参数(run_avm.cpp):

./bin/run_avm <image_path> <intrinsic_path> <extrinsic_path> <camera_model>

其中 <camera_model> 取值:0 表示 OpenCV fisheye 模型,1 表示 Ocam(Omnidirectional)模型,2 表示 pinhole 模型。README 给出的三种运行方式:

# 直接运行(使用默认参数)
cd ~./manual_calib/
./bin/run_avm

# pinhole 示例数据
./bin/run_avm pinhole_samples/imgs pinhole_samples/intrinsic pinhole_samples/extrinsic 2

# Ocam 示例数据
./bin/run_avm ocam_samples/imgs ocam_samples/intrinsic ocam_samples/extrinsic 1

注意两套示例数据对应的相机安装高度不同:代码中默认 bev2ground=5.1(pinhole 样本),当 camera_model==1 时切换为 Ocam 样本的四个高度值(Front/Left/Back/Right 分别约 6.078/6.252/6.310/6.281 m,见 run_avm.cpp),更换数据时务必核对安装高度。

5.2 数据格式:JSON 内参与外参

程序通过 include/intrinsic_param.hpp 的 LoadIntrinsic 与 include/extrinsic_param.hpp 的 LoadExtrinsic 读取 JSON。内参文件要求形如 pinhole_samples/intrinsic/Front.json:顶层键为 <sensor>-intrinsic,其 param.cam_K.data 为 3×3 矩阵、param.cam_dist.data 为畸变系数行向量。外参文件形如 pinhole_samples/extrinsic/Front.json:顶层键为 <sensor>-to-<target>-extrinsic,param.sensor_calib.data 为 4×4 变换矩阵(此处为 ground/BEV 系到相机系的 T_CG)。四台相机固定命名为 Front/Left/Back/Right.json。

5.3 交互操作与参数微调

程序启动后以 Pangolin 窗口显示 BEV 投影结果,并提供控制面板与键盘两种微调方式:

  • 键盘微调:字符表 {'q','a','w','s','e','d','r','f','t','g','y','h'} 分别对应 roll/pitch/yaw/tx/ty/tz 六个自由度的正向与负向调整(ManualCalibration,见 run_avm.cpp),每按一键即对当前相机外参乘以对应的微调增量矩阵。
  • 面板微调:Pangolin 面板提供步长调节(deg step 默认 0.3°,t step(cm) 默认 6 cm,对应代码中 cali_scale_degree_=0.3、cali_scale_trans_=0.06)、相机选择(frame 0-3)、单相机微调按钮(± x/y/z degree、± x/y/z trans)、Reset(恢复初始外参)与 Save Image 按钮。
  • 拼接模式:stiching 开关开启后,窗口显示四相机联合环视图,保存时会将四台相机结果一次性写入 calibration_0.txt ~ calibration_3.txt(JSON 格式 4×4 矩阵,见 saveResult)。

每次调整后程序都用当前外参重新执行 project_on_ground 并刷新画面,实现所见即所得的微调体验。完整操作流程可参照仓库 lidar2camera/README.md 中描述的手动标定交互规范。

六、核心原理小结与选型建议

从源码可以归纳出两套并行的技术路线:

无标定板自动标定(auto_calib / auto_calib_fisheye)

  • 依赖场景纹理而非标定板,适合道路场景快速部署;
  • 算法本质是基于公共视野光度一致性的外参优化:BEV 投影建立"地面像素 ↔ 相机像素"映射,纹理提取圈定可比较区域,随机搜索在 6 维外参空间最小化灰度差平方和;
  • 粗(±3° 分区间并行搜索)→ 精(±1°)→ 微调(±0.01°)三阶段搜索有效扩大收敛域;
  • 通过 camera_model 与 data_set 两个开关即可切换 pinhole/fisheye 与对应初始化参数。

标定板路线(auto_calib_target)+ 手动微调(manual_calib)

  • auto_calib_target 用 solvePnP 从 3D-2D 对应直接求解外参,流程确定、可重复,适合产线;
  • manual_calib 适合在自动结果基础上做人工精修,或在自动标定不可用的场景兜底;
  • 两者共用 pinhole/Ocam/fisheye 三种投影模型的选择逻辑,保证模型一致性。

选择建议:普通环视相机优先用 auto_calib,鱼眼环视相机用 auto_calib_fisheye(注意其内部已内置 fisheye 畸变初值与 1000×1000 BEV 配置);产线场景用 auto_calib_target 并自行替换 3D/2D 标定点;需要在自动结果上精修或无法自动标定时,用 manual_calib 按传感器模型选择对应示例数据作为模板。

引用

本文内容基于 surround-camera/README.md 及其四个子模块文档整理,算法细节与参数均取自对应源码。该模块对应的学术工作为论文 Automatic Surround Camera Calibration Method in Road Scene for Self-driving Car(arXiv:2305.16840),若在研究中使用了本项目,可按仓库 README 中给出的 BibTeX 条目引用:

@misc{2305.16840,
Author = {Jixiang Li and Jiahao Pi and Guohang Yan and Yikang Li},
Title = {Automatic Surround Camera Calibration Method in Road Scene for Self-driving Car},
Year = {2023},
Eprint = {arXiv:2305.16840},
}
登录后查看全文
SensorsCalibration