
做移动机器人和自动驾驶感知的人应该都经历过这种时刻激光雷达和IMU各自的驱动都正常出数了跑起FAST-LIO或者LIO-SAM这类算法地图却怎么都对不齐轨迹飘得离谱。排查到最后发现罪魁祸首往往不是算法本身而是雷达和IMU之间那个最基础的外参没标好。外参标定这件事过去靠量尺量、靠经验凑效率极低还容易翻车。后来港大MARS Lab开源了lidar_imu_init把这件事变成了一条相对标准化的流程。这篇文章就以lidar_imu_init为核心工具把Livox Avia激光雷达和IMU的外参标定完整走一遍包括工具原理、环境编译、数据采集、配置运行、结果验证以及我在实际项目里踩过的坑。刚开始接触标定的新手可以照着操作老手也能从里面挑一些排查经验。1. 动手之前要搞明白外参标定到底在解什么1.1 坐标变换和外参在定位建图里的位置在雷达惯性系统里IMU是绝对的主角它负责高频状态预测激光雷达负责低频观测修正。雷达能够感知环境几何但它并不知道自己在地图坐标系里的位姿必须把雷达点云变换到IMU坐标系下才能借助IMU的运动模型完成状态估计。这个变换由旋转矩阵R和平移向量t共同描述合在一起就是外参。从工程实现的角度看外参标定相当于先修好坐标系之间的路后面所有传感器融合算法才能正常跑。举个例子如果旋转外参偏差1度在20米外就会产生约35厘米的点云错位。这种错位在特征丰富的场景里还能被配准算法部分吸收但到了走廊、隧道这类退化场景直接就发散。很多新手一上来就调算法参数折腾一整天没效果最后查出来居然是外参标定没做好这种事我见过太多了。1.2 lidar_imu_init的算法思路先解旋转再解平移lidar_imu_init的算法思路其实非常清晰分两个阶段走。第一阶段是标定旋转外参方法可以理解为手眼标定。机器人带着雷达和IMU一起运动对同一段运动IMU预积分能够得到相邻两帧之间的旋转增量激光雷达帧间配准也能算出对应的旋转增量。这两个旋转增量之间受外参约束数学形式就是AXXB。只要收集足够多不同姿态下的运动片段就能在这个约束下求解最优旋转外参。这个思路在机器人领域非常成熟关键是让传感器充分运动起来姿态变化越丰富约束越强。第二阶段是确定旋转之后再标定平移外参。这一步利用重力方向作为共同参考因为重力方向既可以由IMU直接测得也可以从雷达点云中的几何结构比如地面、墙面估计出来两组观测共同约束平移量就能通过线性最小二乘解出来。为什么把旋转放在前面因为平移标定本质上依赖旋转结果旋转若存在偏差平移误差会被放大好几倍拆成两步走是最稳妥的做法。1.3 Livox Avia的数据特点对标定流程影响不小Livox Avia是一颗非重复扫描雷达视场角70.4°x77.2°点频240,000点/秒量程450米每个点都带精确时间戳。非重复扫描模式下点云会随着时间推移逐渐加密到整个视场这对帧间配准是好事因为不同帧之间虽然部分重叠但点的分布模式不同几何约束信息更丰富配准稳定性反而更好。Avia还有一个容易被忽略的特点每个点都带精确的时间戳。lidar_imu_init这类工具在做点云运动畸变补偿时必须用到这些时间戳。畸变补偿的意思是雷达在扫描一帧的过程中传感器本身也在运动如果直接把一帧点云当作同一个时刻的观测必然引入运动畸变。有了逐点时间戳配合IMU插值出的位姿就能把每个点补偿到统一时刻。采集数据时如果时间戳没有正确设置后续所有处理都会受影响。另外Avia内置了一颗BMI088 IMU在livox_ros_driver下默认topic是/livox/imu。如果你外接了更高精度的IMU或组合导航作为主IMU那么标定目标就是雷达与外接IMU之间的外参。这两种场景的流程基本一致但topic、初始值和最终使用方式要区分清楚。我见过不少人把内置IMU的topic填错成外接IMU的topic结果标定出来的外参怎么都对不上。2. 环境准备把驱动、依赖和编译一次性理清楚2.1 硬件安装与坐标约定初始外参怎么给标定开始前先把物理安装做对。雷达和IMU必须刚性固定最好用铝合金支架或碳纤维板坚决避免任何松动。运动中任何微小晃动都会被当作传感器自身运动进入标定结果表现为外参漂移和精度下降。如果在机器人上安装还要检查支架的螺丝有没有拧紧这一步看起来基础但返工率非常高。然后是坐标约定。雷达坐标系通常是x轴向前y轴向左z轴向上以Livox默认定义为参考但不同产品略有差异。IMU坐标系一般以芯片封装上的箭头标识为准。在记录初始外参之前要把两个坐标系的轴方向都标出来可以用胶带贴在设备上写清楚省得后面对着安装方向发懵。接下来是给初始外参一个合理猜测。用量尺测量雷达中心相对IMU中心的近似位置方向上按实际安装姿态估算哪怕粗略到厘米级和几度也比直接给零强得多。lidar_imu_init本质还是要做非线性优化初始值离真值太远优化很可能掉进局部极小标定结果自然是错的。2.2 驱动、依赖安装和编译推荐Ubuntu 18.04或20.04配ROS Melodic或Noetic这是Livox官方驱动和lidar_imu_init支持得最成熟的组合。先把livox_ros_driver装好单独启动一次雷达确认点云和IMU数据都能正常出来再往下走。然后安装依赖gtsam、ceres-solver、glog、gflags、PCL、Eigen。glog和gflags可以直接用系统包sudo apt-get install libgoogle-glog-dev libgflags-dev libgtest-devgtsam和ceres-solver优先尝试用ROS的apt源安装sudo apt-get install ros-noetic-gtsam ros-noetic-ceres-solver如果系统源里没有对应版本就只能源码编译。这两块的编译时间都不短建议提前准备好不要等到标定现场才开始编译。接着创建工作空间拉代码编译mkdir -p ~/calib_ws/src cd ~/calib_ws/src git clone https://github.com/hku-mars/lidar_imu_init.git git clone https://github.com/Livox-SDK/livox_ros_driver.git cd ~/calib_ws catkin_make source devel/setup.bash编译过程中最常见的坑是依赖版本冲突和C标准问题。如果卡在gtsam相关的报错基本可以断定是版本不对齐换成apt版本通常能解决。还有一个容易出问题的地方是livox_ros_driver编译时依赖的Livox SDK路径如果报找不到头文件的错误去检查SDK是否装好路径是否加入了环境变量。2.3 跑标定前的一张自查清单我习惯在正式标定前把下面这张清单过一遍省得后面浪费时间雷达能出点云topic名确认为/livox/lidarIMU有数据topic名确认为/livox/imu或外接IMU的topic点云频率正常约10HzIMU频率正常内置IMU约200Hz用rostopic hz和rostopic echo检查话题别着急跑程序录制好的bag文件路径里不要带中文或空格初始外参已经填了一个合理估计值而不是全零launch文件里的雷达型号、topic路径、bag路径和实际硬件一一对应把这些都过一遍比后面跑了半天才发现某个topic写错要省时得多。这套检查习惯后来也沿用到其他传感器标定任务里效果一直很好。3. 实操全流程从采集数据到拿到外参3.1 数据采集的运动策略和场景选择数据质量决定标定质量而数据质量的主要矛盾在运动激励。运动上要做到三点大范围旋转、三轴都动起来、避免长时间匀速。绕x轴、y轴、z轴分别做正负几十度的摆动再做几个8字轨迹让旋转方向频繁变化。IMU在匀速运动时观测性很差长时间匀速会让标定方程接近病态旋转约束和重力约束的强度都会大打折扣。场景上要有丰富的非对称几何特征。室内的柱子、桌椅、墙角都可以关键是避免极端对称的空间。对着一个标准的正方体或一条对称走廊平移方向就可能不可观标定出来的平移值会在不同运行之间跳来跳去。如果有条件可以在地面或墙上贴一些反光标记或纹理丰富的海报给雷达提供更密集的特征。采集时长建议1到3分钟就足够了过长的bag反而让初始化耗时增加。手持设备或安装在机器人上都可以关键是运动过程中不要有物体遮住点云也不要剧烈撞击导致IMU饱和。IMU饱和意味着加速度计读数到了量程上限IMU预积分会引入明显误差标定结果自然不可靠。录制命令用rosbag即可rosbag record /livox/lidar /livox/imu -O calib_data.bag如果外接IMU把外接IMU的topic也加进去。录制前先看一下rostopic hz /livox/lidar的输出点云topic的发布频率稳定在10Hz左右再开始录。3.2 launch文件里最需要改的几个参数lidar_imu_init的launch文件一般涉及这几类参数bag路径、雷达类型、点云topic、IMU topic、初始外参、是否开启去畸变。一个典型的配置片段类似param namebag_path value/home/user/calib_ws/calib_data.bag/ param namelidar_topic value/livox/lidar/ param nameimu_topic value/livox/imu/ param namelidar_type valuelivox/ param namer_il value0.0 0.0 0.0/ param namet_il value0.05 -0.02 0.1/这里的r_il和t_il表示从雷达坐标系变换到IMU坐标系的初始旋转和平移r_il通常用欧拉角或旋转向量形式t_il用米为单位。不同的分支写法可能有差异跑之前建议翻一遍源码或者README把所有参数的含义确认清楚。尤其注意外参方向的定义是lidar到IMU还是IMU到lidar弄反了后面跑FAST-LIO时点云会直接散架。参数示例值说明容易踩的坑bag_path/home/user/calib_ws/calib_data.bag数据包路径路径不能带中文和空格lidar_topic/livox/lidar点云topic名与实际发布名不一致时程序静默等待数据imu_topic/livox/imuIMU的topic名外接IMU时一定要改成实际topicr_il0.0 0.0 0.0初始旋转外参必须给合理初值t_il0.05 -0.02 0.1初始平移外参单位是米别填成厘米参数配置里还有一个选项是是否开启去畸变。如果你的bag里点云时间戳正常强烈建议开启因为运动畸变是降低配准精度的隐形杀手。如果点云时间戳看起来有问题先解决时间戳问题再开去畸变否则会适得其反。3.3 运行标定怎么看过程、怎么判断收敛配置完成后直接用roslaunch启动roslaunch lidar_imu_init livox_avia.launch工具会启动RViz显示点云和运动轨迹。启动后程序通常会暂停等待一般需要按一下界面提示比如回车或空格才开始处理bag。处理过程中观察点云显示如果初始外参偏差较大一开始点云会显得比较乱但随着迭代应该逐渐收敛到一起。什么时候算结束程序会在优化完成后输出标定结果通常打印在终端里包含旋转和平移外参。有些分支版本还会把结果保存到文件。如果你看到终端里输出一组R和t就可以ctrlC退出。注意一点标定程序运行完成后不要只盯着终端里的数字还要回看RViz里点云是否被压准。如果点云边缘依然模糊、有重影哪怕终端提示成功也要多录几组数据重新跑。我见过好几次终端输出结果很漂亮但RViz里点云完全对不上的情况多半是数据质量或初始值的问题。3.4 结果解读以及你拿到的外参到底该怎么用拿到结果后先做两件事。第一把旋转矩阵或旋转向量转换成后续算法期望的格式。FAST-LIO、LIO-SAM这些算法通常用四元数或旋转矩阵而标定工具输出可能是旋转向量或欧拉角格式不统一换算错了等于白干。第二确认坐标系定义代码输出的外参到底是雷达坐标系在IMU坐标系下的表示还是IMU坐标系在雷达坐标系下的表示。这个一定要看代码注释或README不能靠猜。我自己的习惯是拿到结果后用一个快速脚本验证选取某一帧点云的几个特征点根据标定出的外参变换到IMU坐标系下再拿IMU姿态推算同一时刻的朝向对比一下方向是否一致。这一步只需要几十秒但能拦住大部分低级错误。后面再注册优化环节时这个验证脚本还能反复用。4. 常见坑点与排查实录4.1 初始化不收敛头号原因是运动激励不够表现程序运行很久终端没有输出收敛结果或者输出的旋转外参明显不合理偏差超过10度。原因运动缺乏旋转激励导致IMU预积分和激光雷达帧间配准之间的约束太弱手眼标定方程接近病态。排查检查bag数据里是否有很多段静止或近似直行的片段用rostopic echo查看IMU的角速度数据如果角速度长时间接近零说明运动激励不足。裁掉这些无效段只保留旋转充分的片段。重新采集时把设备拿在手里大幅转动每个轴都来几次大幅摆动别心疼数据量。4.2 平移标定结果跳动特征退化与杆臂问题表现多次运行得到的外参旋转部分接近但平移部分每次相差几厘米甚至十几厘米没有稳定趋势。原因一是场景几何特征退化平移方向缺少约束二是IMU与雷达之间的杆臂平移量本身较大而运动旋转中心离传感器组合太近导致平移可观性变差。排查换个特征丰富、非对称的场景多跑几组数据看重复精度。如果结果还跳加大旋转运动幅度特别是大半径的摆动让平移约束变强。另外可以尝试调整运动路径让设备在做旋转的同时有明显的前后位移给平移标定提供更多约束。4.3 时间戳错位让标定结果时好时坏表现标定结果有时看着还行但换一组数据就完全不对或者点云和IMU在时间上存在明显的错位感。原因点云的时间戳与IMU时间戳不在同一时基或者livox_ros_driver的时间戳配置有问题。常见于外接IMU时未做时间同步或者使用了仿真数据但时间戳存在跳变。排查先用rostopic echo对比几个时间戳如果发现IMU数据时间戳和雷达数据时间戳相差超过几十毫秒需要先做时间同步。雷达和IMU都应该是硬件时间戳不要用上位机收到消息时的软件时间戳。另外可以检查livox_ros_driver里时间戳时间源的设置把时基统一到GPS或PTP时钟上。异常现象疑似原因快速排查手段解决方向长时间不收敛运动激励不足查IMU角速度是否长时间为零增加旋转运动裁减无效段平移每次结果差几厘米场景退化或杆臂大多组数据对比平移量换场景、加大旋转幅度结果时好时坏时间戳时基不一致rostopic echo对比时间戳统一硬件时间源每次收敛值差异巨大初始值太差导致局部极小多次运行对比结果给合理初值或复用历史结果4.4 初始值给得太离谱导致局部收敛表现标定结果每次跑都不一样而且都和量尺测量的结果差异很大。原因非线性优化对初始值敏感给了一个偏差很大的初始外参后优化掉进了局部极小。排查把初始值改成更合理的估计甚至直接用上一次成功的结果作为初值。如果设备没有动过上一次的标定结果完全可以复用。另外也可以尝试多组不同的初始值分别运行看结果是否收敛到同一个点。如果多次运行都稳定到同一个值那这个点大概率就是真值如果每次都收敛到不同地方基本就是数据质量或者参数配置出了问题。5. 标定结果验证与多传感器联动5.1 用LIO系统做联动验证拿到外参后不要直接部署先拿去跑一遍FAST-LIO或者LIO-SAM。如果地图清晰、无重影、轨迹闭环误差小说明外参基本可信。如果地图边缘毛糙、轨迹发散先别怀疑算法回头再检查外参方向、格式和坐标系定义。实测下来很多新手容易犯的错误是外参标定结果是正确的但往FAST-LIO里写的时候把雷达和IMU的变换方向写反了导致系统直接跑飞。建议在写配置时把代码里的注释读清楚用自己的坐标定义亲手写一遍别只看格式对不对。5.2 误差传播规律旋转误差为什么比平移误差更致命旋转误差对点云位置的影响与距离成正比距离越远错位越大平移误差则是一个固定的常数偏移。在20米处1度旋转误差产生约35厘米的错位这个量级足以让配准算法拉不回来。平移误差只要控制在几厘米以内通常能被后续的迭代最近点配准或回环检测吸收。所以如果时间有限优先保证旋转外参准确再尽量压低平移误差。这也是lidar_imu_init把旋转标定放在第一阶段的原因。验证时可以将标定后的外参和量尺测量的平移值对比如果旋转部分一致但平移差两三厘米通常不影响系统正常工作如果旋转部分偏差0.5度以上就要重新检查标定流程了。5.3 如果系统里还有RGB-D相机标定工作的扩展思路如果你的系统里还有D435这类RGB-D相机并且把视觉信息也接入了系统外参标定就往多模态方向扩展。流程上一般是先把雷达和IMU标定好再把相机和雷达标定好最后利用统一坐标系把三者的关系串起来。VINS系列使用的IMU-相机标定思路和lidar_imu_init有些类似都是先解旋转再解平移理解了这一套方法其他传感器组合的标定也就不难上手了。多传感器标定还有一个现实的好处当某一个外参出现漂移时可以通过其他传感器的观测来快速定位问题。比如雷达和IMU的外参出了问题地图会出现特定方向的拖影而相机和IMU的外参出了问题视觉重投影误差会明显变大。把不同外参对应的故障特征记在心里排查问题时能节省大量时间。最后再分享一点我个人的体会。外参标定这种工作最忌讳的是一次成功论。我给自己定了一条规矩每次标定都录至少三组不同运动模式的数据分别跑一遍确认旋转外参重复精度在0.1度以内、平移外参重复精度在1厘米以内才往系统里写。平时也要把每次标定的bag、配置文件、结果都归档好设备一旦动过位置直接拿历史数据对比能快速判断问题出在安装上还是标定上。标定这件事本质上是在给整个系统打地基地基稳了后面做多传感器融合才能省心。