
做过多传感器融合的人应该都有这种感觉激光雷达和相机各说各话点云里清清楚楚的一堵墙图像里也明明就在那里可一旦要把两个结果对齐偏差能大到让融合算法直接“摇头”。这个问题的根治办法就是用工具把两个传感器之间的空间关系彻底标定清楚。我最近把 OpenCalib 这套开源标定工具箱完整跑了一遍从数据准备到结果可视化走通了一条激光雷达与相机联合标定的可行路径。这篇就按我实际操作时的顺序把每一步的原理、配置、坑点和判断标准都写出来给正准备做联合标定或者已经被标定折磨过的人一个参考。联合标定这件事很多人把它想得很复杂一上来就翻算法论文。但实际操作下来真正决定成败的往往不是优化求解那几十行代码而是数据采得够不够好、初值给得准不准、结果出来之后你会不会验证。这几个环节我都会展开讲。1. 标定前先想清楚联合标定到底在解决什么问题1.1 坐标系、外参和“对齐”的本质所谓激光雷达与相机联合标定本质上是求两个传感器坐标系之间的刚体变换关系。激光雷达看到的目标在雷达坐标系里有一个三维坐标相机看到的同一个目标在像素坐标系里有一个二维坐标。要让算法能把这两类数据当作同一个对象处理就必须把雷达的三维点投影到图像上或者把图像信息反投影到三维空间。这个变换关系的数学表示很简单P_cam R * P_lidar t。其中 P_lidar 是激光雷达坐标系下的点R 是一个 3x3 的旋转矩阵t 是一个 3x1 的平移向量P_cam 是同一个点在相机坐标系下的坐标。这一步做完之后再套用相机内参 K 和畸变模型就能把三维点映射到像素坐标上。整套流程里R 和 t 合称外参也就是我们要标定的东西。你可以把两个传感器理解成两个人站在同一个房间里一个拿着测距仪一个拿着望远镜。测距仪告诉你目标在东北方向 5 米处望远镜告诉你目标在画面偏右的位置。如果这两个人之间没商量好彼此的站位和朝向他们的描述永远对不上。外参标定就是给他们统一一套说话的方式。这里有一个容易被忽略的点旋转矩阵 R 和平移向量 t 的单位和坐标轴方向会直接影响后续所有融合算法的输入。比如 t 的单位必须是米如果你的安装量测是用厘米记的忘了换算投影结果就会整体偏移几十个像素排查起来非常痛苦。我自己的习惯是写配置文件之前先在纸上画出两个传感器的坐标系朝向再顺手把量测值换算成米这个习惯帮我避免了好几次低级错误。1.2 OpenCalib 的标定方案好在哪开源界做联合标定的工具不算少但 OpenCalib 在易用性和自动化程度上做了很好的平衡。它不只是一套激光雷达与相机的标定代码而是一个覆盖相机内参、相机间外参、激光雷达与相机外参、多激光雷达外参的完整工具箱。每个模块相对独立你只需要用到其中的 lidar2camera 部分不强制安装全套。和某些需要人工在图像和点云里反复点选对应点的工具相比OpenCalib 对标定板特征做自动提取。它使用的是圆形标定板图像端通过椭圆检测提取圆心点云端通过拟合平面和分割圆形区域估计中心两边特征一一对应之后再用PnP或者优化方法求解外参。检测环节一旦自动化人为操作带来的随机误差就少了一大截标定结果也稳定得多。另一个让我觉得舒服的特点是它保留了完整的中间结果输出。标定过程里到底提取到了多少特征点、点云平面拟合得怎么样、投影误差是多少都能看到。对于调试来说这比一个黑盒工具直接丢给你一个最终外参文件要有用得多。你能够知道问题出在数据端还是求解端这一点在实操里价值极高。2. 数据准备标定成败的决定性环节2.1 标定板尺寸、图案与制作要点OpenCalib 的 lidar2camera 模块走的是标定板路线所以选一块合适的板子是第一件事。板面图案不是常见的棋盘格而是圆形阵列通常叫 circle grid。圆心的提取精度在图像和点云两端都能做得比较高而且圆形的几何对称性对透视变形不太敏感这是它比棋盘格更适合多传感器标定的原因。标定板的尺寸影响巨大。激光雷达在远距离上的点云密度会迅速下降板子太小的话几米之外就只剩寥寥几个点平面拟合和圆心估计都会失效。我建议板子边长不要小于 600 毫米有条件的话做到 1 米左右更好。圆形阵列的间距也要提前量准比如列间距和行间距都是 150 毫米这个值是后续求解平移尺度的重要参考。打印误差会直接进到标定结果里所以有条件就尽量用高精度喷绘别用普通家用打印机出图。板子的材质同样不能马虎。最好是 5 毫米以上的铝塑板或者亚克力板表面要哑光处理。雷达点云对高反射率物体很敏感如果板面反光点云上会出现一些异常亮斑或飞点干扰平面分割。我自己第一次用的就是普通相纸覆膜结果在点云视图里那个板面斑斑驳驳的圆心提取根本不稳定。后来换了哑光铝塑板效果立刻正常了。另外板子边缘要平整背面如果有支架尽量不影响前表面否则点云拟合平面时会出现倾斜。2.2 现场采集的姿势规范和样本量要求数据采集是整个标定流程里最耗时也最影响最终效果的一环。核心目标是让标定板在两个传感器的共同视野里尽可能多地覆盖不同位置、不同角度、不同距离。这样做不是因为程序有什么特殊要求而是因为外参求解的本质是从多个视角的对应关系里反推刚体变换。位姿越丰富约束条件越充分最终求得的解越不容易陷入局部极小。实际操作时我建议按这样的姿势序列采集正对传感器放在视野中央向左偏移、向右偏移向上仰、向下俯板面左右倾斜、前后倾斜距离分别拉远到 3 米、5 米、8 米左右。每个姿势保持静止 2 到 3 秒不要边动边采集。激光雷达的一帧点云是一个扫描周期内逐步累加的结果板子如果一直在晃点云里的板面会是扭曲的平面拟合直接报废。样本量至少要有 15 到 20 组有效姿态我通常会采到 30 组以上。比较稳妥的做法是给每个姿态编个号采集的时候记录一下大概位置方便后面筛选。现场还得避开强光直射和玻璃反光区域。另外人员不要出现在标定板附近雷达会把人体也扫进去影响平面分割。采集完成之后别急着跑标定。先做一次快速预览把点云和图像按时间戳大致对齐确认每一组数据里板面都完整出现在雷达点云和图像中。有些姿态在图像里看着挺好但板子正好落在雷达扫描的盲区边缘点云里只有半块板子这类数据就要果断丢掉。2.3 数据同步与格式检查多数方案在数据准备阶段会忽略时间同步问题但它在联合标定里很关键。如果相机和雷达没有硬件同步至少要在软件层做时间戳对齐。采集时我习惯用 rosbag 记录两个传感器的话题时间戳统一走 ROS 时钟后续处理时按时间差最小原则把图像和点云配对。如果用的是离线数据文件也要先检查一对图像和点云是否真的对应同一时刻的同一场景。一个简单的验证方法是找一帧点云投射到图像上看板子是否在同一个位置。如果发现点云里的目标轮廓和图像里的目标轮廓有明显错位而外参明明已经给得很好了那大概率就是时间对齐出了问题。我在一次采集里就遇到过这种情况当时还以为是外参算法有 bug最后发现是相机和雷达的帧率不匹配配对时选错了帧。所以别嫌麻烦数据检查这一步非常值得。3. OpenCalib 实操全流程从编译到出外参3.1 环境依赖与编译先把基础环境准备好。OpenCalib 官方比较推荐在 Ubuntu 18.04 或者 20.04 上用 ROS Melodic 或 Noetic 环境核心依赖包括 OpenCV、PCL、Eigen、yaml-cpp以及 Ceres 或者 Sophus 这类非线性优化库。如果你已经有跑过激光雷达的 ROS 环境大部分依赖应该已经就位了。我的环境是 Ubuntu 20.04 ROS Noetic装完依赖之后把代码放进工作空间的 src 目录然后编译。命令很简单mkdir -p opencalib_ws/src cd opencalib_ws/src git clone OpenCalib仓库地址 cd .. catkin_make编译过程中常见的坑是 OpenCV 和 PCL 的版本冲突。Noetic 自带的 OpenCV 4 和 PCL 1.10 在大部分情况下是兼容的但如果你之前手动编译过其他版本的 PCL很容易在链接阶段报一些莫名其妙的符号错误。我的建议是尽量用系统自带的库版本不要贪新鲜升级。真的遇到依赖冲突用 Docker 把环境隔离起来反而是最快的出路。我见过不少人在编译上卡了一整天最后换成 Docker 镜像十分钟就过了。3.2 配置文件逐个拆解OpenCalib 里每个标定任务都对应一份 YAML 配置文件里面有几个关键字段直接决定标定能否跑通。首先是相机内参包括 fx、fy、cx、cy 和畸变系数。内参必须来自独立的相机标定结果这一步不能用激光雷达去反推。官方推荐的做法是用棋盘格 OpenCV 的标定流程至少采集 10 到 15 张不同姿态的棋盘格图像把重投影标定误差控制在 0.5 像素以内再继续。然后是两个传感器的话题名或者数据路径。用 rosbag 跑的话要填 topic 名称并且确保图像话题是原始图像不要选已经被压缩编码过的话题否则 OpenCV 读入会出问题。接着是标定板的几何参数包括圆形阵列的行数、列数、圆心间距以及板子实际物理尺寸。这个区域容易出错的地方是行数列数方向定义不同工具里往往不一致。你必须打开点云和图像确认配置里的行方向对应的是图像里的哪个方向不然检测到的对应点关系会整体错位。外参初值也要填进去。旋转部分可以用欧拉角表示再转换成矩阵平移部分就是激光雷达相对于相机的三维位移单位是米。初值允许有误差OpenCalib 的优化过程会修正它但初值和真实值如果差太多很容易陷入局部最优。实际操作里初值其实不用精确测量拿尺子量个大概旋转角度估个方向比如雷达在相机右侧零点几米、稍向下倾斜一点这类量级的信息就够了。3.3 跑标定时的中间结果怎么读一切就绪后启动标定程序屏幕或者日志里会持续输出当前检测到的特征数量、点云平面拟合的残差、当前迭代的误差指标。很多人看到这些输出就忽略了但它们是判断数据质量的第一手指标。如果某一帧特征点数量过少或者平面拟合残差异常大说明这一帧数据可能存在问题不用等最终结果出来直接标记剔除。优化求解过程一般会显示迭代次数和误差下降曲线。正常情况下误差会逐步下降并趋于平稳。如果误差忽大忽小甚至到了最后几轮还在明显跳动多半是数据里混入了错误的对应点或者初值方向给反了。我遇到过一次旋转初值方向填反的情况优化过程错误提示不明显但最终投影结果图像和点云的边缘整体错开了很大一段距离最后回查配置文件才发现问题。标定完成后结果会输出到外参文件一般包含一个 4x4 的齐次变换矩阵或者分拆的 R 和 t。保存好这个结果同时也建议把对应的配置文件备份因为外参是和坐标定义强相关的一旦换了坐标系约定这份外参就不再有效了。4. 结果可视化与精度验证外参对不对一眼就知道4.1 点云投影叠加图像的三种可视化做法拿到外参之后第一件事就是可视化验证。最简单粗暴的方法是把雷达点云按外参投影到图像上按深度着色之后叠在一起看。边缘轮廓贴合得好不好一眼就能看出来。这个操作不需要复杂的软件OpenCV 加几行代码就能完成。我自己常用的流程是读一帧图像读一帧点云对每个有效点做坐标系变换跳过投影后落在图像范围之外的点然后把剩下的点按距离着色画到图像上。伪代码大致是这个意思import cv2 import numpy as np # 假设 points 是 N x 3 的雷达点云K 是相机内参R, t 是外参 def project_lidar_to_image(points, K, R, t): cam_pts (R points.T t.reshape(3, 1)).T # 过滤掉相机后方的点 mask cam_pts[:, 2] 0 cam_pts cam_pts[mask] proj (K cam_pts.T).T u proj[:, 0] / proj[:, 2] v proj[:, 1] / proj[:, 2] return u, v, cam_pts[:, 2], mask如果用的是 ROS 环境更省事的做法是在 RViz 里同时显示图像和 PointCloud2然后通过发布外参对应的 TF 变换把点云显示到相机坐标系下。设置一个半透明的图像面板叠加效果非常直观。还有一种做法是用 PCL 的可视化窗口加载点云再在同一个窗口里显示图像纹理但对配置要求高一些日常调试没必要上这么重的方案。判断可视化结果的好坏不能只盯着标定板看。我习惯找场景里的明显结构物来做交叉验证比如墙角、柱子、路缘石、灯杆这类线条清晰的目标。如果这些结构在图像和点云里都精确贴合说明外参在视野的多个区域都可信而不只是在标定板附近自圆其说。4.2 重投影误差怎么看可视化是定性判断重投影误差是定量判断。所谓重投影误差就是把标定板上的特征点先从点云侧提取出来再投影到图像上和图像侧检测到的对应像素位置做差。这个差值越小说明外参越准。评判标准上我个人的经验是均值误差在 1 到 2 像素之间属于非常好2 到 5 像素属于可接受多数融合算法都能正常运作超过 10 像素基本不可用。但要注意重投影误差取决于特征点的空间分布如果所有点都集中在画面中央误差小也不能说明边缘区域标定得好。所以量化验证时最好把标定板放在画面的不同位置分别统计误差。还有一个不起眼但很实用的检验方法就是用一组没有参与标定的数据来做交叉验证。如果参与优化拟合的帧误差都很小但新数据一测试就明显错位说明标定结果过拟合了模型被少数特定姿态“带偏”。遇到这种情况回炉补采数据比调算法参数有用得多。4.3 基于可视化结果微调外参的实操技巧可视化还有一个应用容易被忽略就是辅助判断外参偏差的方向。如果投影出来的点云整体往右偏通常说明平移向量或旋转角度在某些轴上有系统偏差。你可以微调外参后在可视化里不断验证虽然这种方法不够严谨但能帮你快速定位问题来源。比如点云整体偏右试着把 t 向对应方向调整几个厘米看看是否改善如果整体绕某个轴旋转试着微调欧拉角。很多时候最终优化结果不好未必是算法的问题而是初值或者坐标系定义本身就歪了可视化验证能帮你快速建立直觉。这也是为什么我会建议无论如何都要先把外参文件加载到可视化环境里看一眼再决定下一步做什么。5. 高频问题与避坑记录5.1 常见问题速查表我把实际操作中遇到过的典型问题整理成了表格方便对照排查。问题现象可能原因解决方案图像里检测不到圆圈格标定板尺寸太小、距离太远、图像模糊增大标定板、靠近拍摄、检查相机对焦点云里找不到板平面板面反光、雷达视角被遮挡、板子倾斜过大换用哑光板、调整姿态、让板面尽量正对雷达特征对应关系混乱圆形阵列的行列方向定义不一致打开可视化确认行列方向修改配置里的行列数定义重投影误差整体偏大相机内参不准或外参初值偏差大重新做相机内参标定、回采数据提高初值精度误差在优化过程中不收敛数据里有错误对应点、时间未对齐、板子在动筛选数据帧、检查时间戳、保证每个姿态静止投影结果边缘区域偏差明显样本姿态覆盖范围不够增加画面边缘、远距离、大角度姿态这张表里的每一条我都实际踩过。尤其是行列方向定义那个问题看起来只是配置里的一行数字一旦错了整个点云会在点对应关系上发生错位导致优化结果完全失真而且错误提示并不明显只能靠可视化排查。建议在开始跑优化之前先输出一次特征提取和匹配的可视化结果确认。5.2 几个隐藏很深的坑第一个容易忽略的坑是打印比例。标定板的圆形阵列间距必须以实际打印出来的尺寸为准而不是设计文档里的尺寸。打印过程可能会按默认设置缩放导致实际间距比设计值小几个百分点这个误差最终会直接进入平移向量的尺度造成数厘米甚至更大的偏差。拿到板子后第一件事用尺子量一下实际间距再填进配置文件。第二个坑是室外采集时光照变化对图像检测的影响。太阳角度一变标定板上的圆圈可能在图像里出现过曝或者阴影遮挡检测到的圆心位置会偏移。如果必须在室外采集尽量选阴天或者背光环境保证板面光照均匀。我在一次下午采集中连续翻车就是因为板面一半被阳光直射一半在阴影里圆心的提取精度明显下降。第三个坑藏在点云预处理环节。有些激光雷达驱动会输出带有强度值的同时也输出一些离群噪声点这些点如果不做滤波处理会让平面拟合的残差变大。但要注意滤波半径不能设得太大否则板面上的有效点也会被一起滤掉。我的经验是先做一次简单的直通滤波把范围限制在标定板可能出现的大致区域然后再做平面拟合稳定性和精度都能兼顾。最后一个坑是“标定完成”不等于“标定永久有效”。传感器支架可能在车辆运行中被磕碰相机镜头可能因为震动产生微小位移。外参标定是一次性的但传感器状态不是永久的。如果融合系统里出现追踪不稳定、投影偏移渐进变大的现象多半不是因为算法退化而是外参已经失效。定期用同样的流程在固定场景里做一次复标定能把这个问题控制在早期。还有一点要特别提醒保存外参文件时最好把相机内参、外参、标定板参数、采集时间记录下来放到同一个目录里。这样后续做数据分析或者问题回溯时能完整还原当时的标定条件。别问我为什么知道——当你手里有五个不同时期的标定结果却分不清哪个对应哪次采集时就明白了。写到这里关于 OpenCalib 做激光雷达与相机联合标定这件事我能分享的实操经验基本都覆盖了。如果只留一句话给后来者我的体会是标定算法已经足够成熟输出结果好不好绝大多数时候取决于你是否愿意在数据采集和结果验证上多花一点耐心。把采集流程标准化把每一次标定的配置和结果记录下来你会慢慢发现标定从一件玄学变成一件很踏实的事。