激光雷达与相机外参标定实战:基于Autoware棋盘格方案的完整指南

📅 发布时间:2026/10/17 1:41:55
激光雷达与相机外参标定实战:基于Autoware棋盘格方案的完整指南
激光雷达和相机的外参标定是所有做传感器融合的人绕不开的第一道坎。不管是目标检测、可行驶区域分割还是BEV感知只要想把点云和图像对齐到同一个坐标系就得先把这两个传感器之间的旋转和平移关系搞定。Autoware这套开源自动驾驶软件栈自带了基于棋盘格的联合标定工具我在几套不同配置的车上用它标过激光雷达和相机的组合流程和坑都摸得比较透。这篇文章就把从原理到实操的完整路子盘一遍适合刚开始接触传感器融合的开发者也适合已经用Autoware但标定结果总不理想的同学参考。标定这件事看起来只是算一个矩阵但实际做下来你会发现真正花时间的不是最后那一次求解而是前面的数据采集和后面的结果验证。数据没采好算法再强也白搭结果没验证你根本不知道它到底准不准。所以我把文章的重心放在这两块算法的部分讲清楚为什么这样做实操的部分讲清楚每一步怎么走。1. 先搞清楚标定的对象外参到底是什么1.1 三个坐标系和一个齐次变换在动手标定之前先把坐标系的关系理清楚。激光雷达坐标系通常以自身为原点X轴指向正前方Z轴垂直向上部分型号驱动里会把Y轴设为前向具体以你用的驱动和tf树为准标定前先确认一次。相机这边习惯用optical坐标系Z轴沿光轴朝前X轴朝右Y轴朝下。这个Y轴朝下跟很多人脑子里的上为Y是反的刚上手时很容易在这里绕晕涉及旋转方向判断时尤其要小心。所谓外参描述的就是这两个坐标系之间的刚体变换具体来说是一个4×4的齐次变换矩阵T包含一个3×3的旋转矩阵R和一个3×1的平移向量t。点云里的一个点P_lidar经过T变换后就能落到相机坐标系下再配合相机内参投影到图像平面。整个链路用一句话概括外参负责让两个传感器的视线方向在三维空间中对齐内参负责把三维点变成图像上的二维像素。这个变换为什么不能靠卷尺直接量出来因为激光雷达和相机的安装位置有加工公差安装支架本身也可能在磕碰后轻微变形。毫米级的误差在几十米外的投影上会被放大得非常明显你手工量出来的平移可能差两三厘米旋转差个一两度投到50米外就是好几米的偏差。因此必须通过观测同一个目标来反推这个变换这就是标定的本质。我习惯用一个类比来跟新人解释把激光雷达和相机看成两个人的眼睛外参标定就是搞清楚左眼相对于右眼到底在什么位置、朝向哪里。两个人同时看一个目标通过比对两者观察到的方位差就能反推两眼的相对位姿。标定棋盘格就是给这两只眼睛找一个足够清晰、足够精确的共同目标。1.2 外参在Autoware里怎么表示、怎么用在Autoware这套体系里标定结果通常保存成一个YAML文件。文件里一般就是translation和rotation两个字段平移是三个浮点数旋转是四元数。不同版本的工具生成的文件格式略有差异但本质都是同一个变换矩阵用不用得起来关键看你是否理解它的语义。比较关键的一点是必须搞清楚这个变换到底是激光雷达到相机还是相机到激光雷达。很多人在这一步栽跟头把YAML里的值拿去发布静态tf结果方向反了点云和图像彻底对不上。我的建议是别背公式直接读工具源码里调用这个变换的代码顺序或者用一个近处已知物体验证一次方向对不对立刻见分晓。在工程里外参的使用场景无非就两种要么要求出静态tf发布命令行把变换写进tf树要么在感知模块的配置文件里直接加载YAML。前者要保证parent和child的frame命名跟代码里查找tf的逻辑一致一个字符都不能差后者要确认单位是米、旋转部分用的是四元数且顺序是qx qy qz qw不要误当成欧拉角。另外提醒一下如果你用的是较新版本的AutowareROS 2体系标定工具链和坐标系约定可能跟ROS 1时代不完全一样但底层原理不变核心还是那个刚体变换和重投影误差。下面我按个人用得最多、也最成熟的ROS 1原版工具来讲思路完全能迁移到新版本上。2. 方案选型Autoware棋盘格标定的原理与前提2.1 为什么偏要用棋盘格主流的激光雷达和相机外参标定方法可以分成两类有靶标和无靶标。无靶标方法靠自然场景里的特征来对齐比如路面边缘、树干、建筑角点优势是不用额外带设备、部署快缺点是依赖场景纹理优化迭代容易跳到局部最优而且对初始值敏感。棋盘格方案属于有靶标方法它把特征做成了人工定制、高对比度、几何精确已知的图案让两个传感器都能稳定、唯一地检测到相同的角点。这就是它的底气所在可复现、可量化、对场景依赖小。Autoware里的calibration_camera_lidar工具用的就是这一路。棋盘格在图像里用OpenCV的角点检测算法就能稳定找到在点云里棋盘格表现为一个平面矩形通过平面分割和边界提取也能找出来。有了2D-3D对应点以后外参就可以通过标准的多视角几何方法求解。整套链路都是成熟算法不需要自己造轮子这也是我推荐新手先从这套工具入手的原因。如果你觉得带棋盘格麻烦也有全自动或半自动的替代方案比如用连续帧的自然点云和图像特征做联合优化。但这类方法在纹理稀疏的园区、地下车库很容易翻车而且结果好坏难以肉眼判断。作为工程上的第一步我还是建议先用棋盘格把这套车的基准外参确定下来后面再考虑用无靶标方法做在线校正。2.2 算法链路的四步走工具内部的处理流程我拆成四步理解了这四步你后面排查问题时就有方向感。第一步图像端提角点。输入相机图像用OpenCV的角点检测方法检测棋盘格的内角点得到一组2D像素坐标。这里检测的是内角点也就是黑白格交界处的角点而不是棋盘格最外沿的顶点数量是行列各减一。第二步点云端提平面。在激光雷达点云里把用户框选出来的区域做平面拟合通常用RANSAC或PCL的SACSegmentation拿到棋盘格所在平面的方程。再把落在该平面附近的点投影到平面坐标系提取矩形轮廓根据已知的方格边长反算出棋盘格所有内角点在激光雷达坐标系下的三维坐标。第三步建立2D-3D配对。同一个棋盘格图像里有N个2D角点点云里有对应的N个3D角点一一对应。这一步对行列方向的一致性要求极高如果两者的行列方向没有对齐后面优化就会错乱。第四步求解与优化。先用PnP解出初始旋转和平移再用非线性最小二乘比如Levenberg-Marquardt最小化所有帧所有角点的重投影误差得到最终外参。重投影误差的意思是把3D角点按当前外参和内参投影到图像上计算投影点和图像检测角点之间的像素距离。这个距离越小说明外参越准。为什么需要采集多帧、多个位姿因为单帧棋盘格只能约束一部分自由度。比如棋盘格放在正前方时你对旋转的某些方向几乎不敏感。只有让棋盘格出现在不同距离、不同偏航角、不同俯仰角下所有自由度才被充分约束。这个道理跟拍照一样你只拍人脸正面看不出侧面轮廓多角度拍才能完整重建。2.3 一个容易忽略的前提相机内参这里必须强调顺序问题相机内参必须先标好。内参包括焦距、主点、畸变系数。内参不准外参标定就是在沙子上盖楼。你可能会遇到外参怎么优化重投影误差都下不去的情况查到最后发现是内参里的畸变系数不对尤其是广角镜头画面边缘畸变很厉害棋盘格放在边缘位置时误差特别明显。内参标定的方法很成熟用OpenCV的棋盘格标定流程跑一遍就行大概采集20到30张不同姿态的棋盘格照片。标完内参后把结果写进相机驱动或感知模块的配置文件。确认图像在全画面范围内都没有明显畸变再开始做外参否则后面的每一步都会带着这个系统性误差。依赖方面这个工具主要依赖ROS、PCL点云库和OpenCV。编译时常见的问题是OpenCV版本差异导致接口不兼容建议和Autoware主版本匹配的ROS发行版一起安装。环境搭好以后先用官方自带的示例数据跑通一遍流程再上自己的车能省下大量排查时间。3. 数据采集前的准备棋盘格制作与布场3.1 棋盘格尺寸、材质和制作棋盘格不是随便打印一张纸就能用的。先说尺寸我推荐内角点数量至少7×7对应8×8个方格常见的有9×7、11×8。方格边长方面10到20厘米是比较稳的范围具体看你的激光雷达线数和安装距离。如果你用的是16线激光雷达点云相对稀疏棋盘格太小或者放太远可能只有一两条扫描线打在板上平面拟合根本做不准那就需要更大的棋盘格、放得更近。材质和制作比很多人想象的重要。一定要打印在哑光介质上不要覆亮膜否则强光下反光会让角点检测失败。然后底板要刚性我用过亚克力板和铝塑板效果都不错但千万别用泡沫板或者软海报夹子夹着。软底板在点云里看起来是一个弯曲的平面标出来的外参在远距离投影时会发散这个问题我后面还会细讲。另外激光雷达打到黑格和白格上反射强度差异明显这其实是好事说明点云看得到棋盘格图案有些工具甚至能利用反射强度辅助提取棋盘格轮廓。你可以在录制后检查一下点云的强度通道能不能看清黑白格的边界。如果棋盘格区域全部是一个强度值说明材质选得不对换个哑光材质再试试。3.2 位姿规划距离、角度和帧数采集数据之前先规划好棋盘格要摆的位置。原则上要让棋盘格完整地同时出现在两个传感器的视野交叠区域并且覆盖不同的距离和角度。我常用的策略是三组距离搭配三种姿态距离上分3米、5米、8米三档每个距离上棋盘格分别正对传感器、左右偏转约30度、上下俯仰约15度。这样算下来最少八九帧我通常会录到12帧左右留出冗余防止个别帧质量差需要剔除。每个位姿下要让棋盘格保持静止至少稳定一两秒再开始记录。如果车或者标定板在动图像和点云的时间戳对不齐重投影就会带运动误差。操作上我习惯用三脚架架棋盘格调整姿态时先停下稳定之后再采集不要边动边录。这个细节决定了数据能不能用别嫌麻烦。还有一个容易忽略的点标定场景里不要有其他明显的平面物体靠近棋盘格比如墙面、地面、旁边的金属围栏。点云在做平面拟合时如果棋盘格和背景平面距离太近容易把背景点也拟合进棋盘格平面角点位置就会偏。停车场里那种网格状的墙面尤其坑反射强度和棋盘格区分不开采数时尽量避开这类背景。3.3 录制bag话题、帧率和时间戳录制前先确认话题。相机图像一般是/image或者/image_raw点云一般是/points_raw或者/lidar_points。用rostopic list看一眼再用rostopic hz检查发布频率和抖动情况。相机和激光雷达的驱动最好跑在同一台工控机上时间戳才能相对一致。如果两个传感器各挂一台计算设备又没有做时钟同步后面标定时会发现怎么都对齐不了这个问题我在文末还会提到一次。录制命令很简单只录需要的两个话题就行rosbag record -O calib_lidar_cam_01 /camera/image_raw /points_raw每个位姿单独录一包文件名里带上位置和姿态描述比如calib_3m_front.bag、calib_5m_left30.bag。文件名写清楚后面回放和选帧时效率会高很多。我一般不会录一个超长的bag然后靠时间戳切分因为切分本身就是额外的工作量。回放的时候用--pause参数进入暂停模式逐帧检查。如果发现某一帧图像过曝、棋盘格被遮挡、点云里棋盘格点太少直接跳过这一帧不要勉强选进去。一次高质量的10帧数据比30帧烂数据标出来的结果稳定得多。这个原则适用于所有标定类任务数据质量永远优先于数据数量。4. 实操全过程从启动界面到结果落盘4.1 启动工具、核对参数、填初始猜测环境没问题、数据录好之后就可以开始标定了。以ROS 1版本的Autoware为例启动命令是roslaunch calibration_camera_lidar calibration_camera_lidar.launchlaunch文件会拉起一个图形界面同时订阅相机图像话题和点云话题。启动前先检查launch或配置文件里的话题名是否正确以及棋盘格参数是否填对。棋盘格参数一般包含内角点列数和行数、方格边长两个关键数值。这里特别容易填错的是内角点数和格子数量的概念比如一张8×8个方格的棋盘格内角点是7×7配置里填的是7×7不是8×8。填反了角点检测会直接失败或者检测出来的图案方向不对。进入界面后第一步要填初始猜测。这个初始猜测就是你认为激光雷达和相机之间大概的平移和旋转。一般用卷尺量一下两个传感器的安装位置就能给出差不多的值比如激光雷达在相机前方0.3米、偏左0.05米、比相机高0.1米。旋转通常都接近单位阵如果两者安装朝向差异大按实际角度填。初始猜测的重要性在于后面的非线性优化是非凸的初值给得太离谱优化会掉进局部极小值标出一个在重投影误差上很漂亮、但物理上完全不对的结果。4.2 ROI框选与逐帧确认回放bag时界面上会在图像和点云两个视图里显示当前帧。你要做的核心操作就是调整ROI把棋盘格在图像里框出来再在点云里把棋盘格所在区域选出来。图像侧比较好理解拖一个矩形框把棋盘格完整包含进去框得越紧越好不要把周围杂乱背景包进来。点云侧稍微复杂一些通常是通过设置x、y、z方向的距离范围或者用鼠标直接框选只保留棋盘格附近的一小块点云。这个ROI的意义很大如果点云ROI里混入墙面或地面平面拟合就会被污染棋盘格角点的三维位置就会偏后面所有优化结果都会跟着偏。在图像里工具会自动做角点检测并把检测到的角点连线画出来。你要确认连线的行列方向跟棋盘格的实际方向一致如果工具把9列当成了7列或者方向转置了后续的2D-3D配对就会出问题。点云侧同理确认提取出的角点数量、间距符合预期。我是逐帧处理的调到某一帧确认角点正确点击提取或确认再调到下一帧。不要把整个bag从头放到尾然后一次性批量处理那样一旦某几帧检测错了你根本不知道是哪一帧在污染优化结果。批量处理适合数据质量极高的情况手工逐帧多花十分钟后面误差检查能省一小时这笔账怎么算都划算。4.3 误差检查重投影误差怎么看所有帧确认完毕后点击标定按钮。工具会将所有选中的帧统一丢进优化器迭代求解外参。优化完成后界面上一般会显示重投影误差的统计值比如平均误差、最大误差。这个误差怎么看我个人的经验阈值是平均重投影误差在1到2个像素以内属于非常理想的结果3到5个像素属于可以接受但需要检查的水平超过5个像素基本说明有脏数据或者初始化不对直接保存的话后面投影验证大概率会翻车。点云本身的噪声大尤其是16线激光雷达角点三维位置的噪声有几个厘米很正常所以也别强求每个角点都精确到亚像素。如果误差降不下来优先做的不是调优化参数而是排查数据。把那一帧的图像角点和点云角点逐个看通常能找到一两帧角点提取错乱的坏数据把坏帧剔除重新优化误差往往会明显下降。另一种情况是初始猜测离真实安装位置太远优化卡在局部极小把初始猜测拉近重新优化即可。4.4 结果YAML解析与静态tf发布标定完成并保存后会生成一个YAML文件典型内容如下%YAML:1.0 --- transformation: translation: [0.265, 0.085, -0.162] rotation: [0.998, 0.012, 0.021, -0.031]rotation字段是四元数顺序是x、y、z、w。但注意不同版本工具输出语义可能不同有的存的是激光雷达到相机的变换有的存的是相机到激光雷达。拿到文件之后先用一个小脚本验证语义我的做法是直接把点云按这个变换投影到图像上看近处目标是否贴合。如果贴合方向就对了如果完全错乱试试取逆变换再投影。验证没问题后发布静态tf的命令大概是这样rosrun tf2_ros static_transform_publisher 0.265 0.085 -0.162 0.012 0.021 -0.031 0.998 lidar_link camera_link注意这个命令的参数顺序是x y z qx qy qz qw别把四元数顺序写错。发布前还要确认frame_id名称跟Autoware感知模块里查tf时使用的名称完全一致。我以前遇到过标定结果本身没问题但因为lidar_link和实际点云frame的名字不一致导致模块根本找不到静态变换白白排查了很久。5. 验证与排查标定完不等于标定对5.1 投影对齐三距离验证法标定结果的最终判断标准只有一个点云投影到图像上到底对齐不对齐。我习惯把投影验证分成三层。近处看5米内的物体比如路沿、消防桩、车身前方的障碍物看点云边缘和图像边缘是否吻合中距看20到30米的电线杆、树木、围栏立柱这类竖直目标对旋转误差特别敏感远处看50米以上的建筑物立面稍微错位还能接受但如果错得越来越离谱大概率是旋转分量没标对。具体操作有两种方式。第一种是在rviz里同时显示图像和点云点云按标定外参变换到相机坐标系后叠加显示第二种是写一个简单的投影脚本把点云按内参外参画到图像平面上存成一张张对比图。第二种更适合定量检查因为看得清每一个像素的偏差。一个重要技巧是如果发现近处对齐、远处发散说明旋转误差偏大这时候不要只靠已有数据重标而是要在标定数据里增加更远距离的棋盘格帧。远距离约束对旋转分量的观测能力更强。反过来如果近处就错、远处也错但偏移方向一致更像是平移分量的问题。记住这个规律平移误差导致的是整体偏移旋转误差导致的是随距离放大的发散。用这个规律可以快速定位问题出在哪个分量上。5.2 高频问题速查表现象可能原因处理办法点云里提取不到棋盘格ROI没包含板、板距离太远、线束太稀疏扩大ROI、把板移近、换更大尺寸棋盘格图像角点检测失败过曝、反光、棋盘格被部分遮挡改善光照、调整曝光、重新摆放检测到角点但方向不对配置里行列数填反核对内角点参数行列互换重填重投影误差很大混入坏帧、初始猜测太差逐帧剔除坏帧、拉近初始猜测优化结果物理上不合理局部极小值、帧数太少增加不同位姿帧、换个初始猜测重标投影近处对齐远处偏旋转误差偏大增加远距离标定帧、重点约束旋转点云和图像整体错位变换方向反了检查YAML语义尝试取逆变换这张表基本覆盖了我遇到的绝大多数情况。如果问题不在表里还有一个通用的排查原则先怀疑数据再怀疑算法最后才怀疑工具本身。Autoware这套工具在社区里被反复使用核心优化代码是靠谱的绝大多数异常都出在数据质量和参数配置上不要一上来就去改优化参数。5.3 几个真实踩坑教训最后聊几个真实的教训。有一次我用了一块背面贴软泡沫的棋盘格点云平面拟合时一直处于能拟合但残差偏大的状态当时没在意结果标完外参10米内投影是好的上到30米全部偏到一边。后来换成铝塑板底板同样的数据量重标问题直接消失。这就是底板刚度的重要性软底板上每个点的微小形变在远处会被放大成不可接受的偏移。还有一次连着标了三遍结果都不对重投影误差都很小但投影验证就是差一截。折腾半天发现是相机内参文件里的畸变系数被之前改错了。内参错了外参优化器会在数学上找一个补偿方案让重投影误差看起来很小但物理上两个传感器的位姿关系是错的。所以再次强调内参不过关外参别开工。时间同步也坑过我一次。当时激光雷达驱动和相机驱动分别挂在两台计算设备上没有做时钟同步回放bag时发现图像里棋盘格边缘和点云轮廓有可见错位标出来的结果时好时坏。把两个驱动挪到同一台工控机并统一时钟源之后问题彻底解决。对静态场景标定来说这个影响通常不大但如果你打算做在线动态标定或者车辆缓动状态下采集数据时间戳一致性就是硬约束。我个人在实际操作中的体会是外参标定这件事七分靠数据三分靠算法。工具给你的优化器再强大也架不住糊弄的数据。每次标完我都会花同样多的时间做投影验证而不是急着把YAML丢进感知模块毕竟外参标错感知模型再强也是白搭。这个流程走顺之后一套车从布场到出结果半天时间足够了。如果你正在为标定结果发愁按这个路子重新梳理一遍数据采集和验证流程大概率能把问题找出来。