☰
LIO-SAM实战:激光雷达与IMU联合标定全流程指南
2026/10/7 16:00:41 网站建设 项目流程

一年前我给一台室内巡检机器人做停车场环境建图,LIO-SAM跑出来的地图在回环处直接裂开,同一面墙出现了三层重影。排查到第三天,才发现罪魁祸首是params.yaml里那组拍脑袋填的雷达与IMU外参。标定完再跑同一份bag,地图精度完全是两个段位,回环闭合后墙面对齐得干净利落。从那以后,我把项目里所有激光雷达+IMU组合都重新标了一遍,顺手也帮同事们的机器人小车检查了一圈。

这篇文章就把LIO-SAM这条技术栈下的激光雷达与IMU联合标定完整梳理一遍:从外参标定的原理、标定工具选型,到数据采集实操和参数回填,最后是把LIO-SAM跑通之后的调参与验证方法。内容偏实战,适合已经有LIO-SAM基本使用经验、想进一步提升建图精度,或者正在搭建激光惯性SLAM系统的朋友参考。

1. 先搞明白联合标定到底在标什么:外参、内参与坐标系约定

1.1 外参标定的本质:一个旋转矩阵加一个平移向量

激光雷达和IMU是两个独立的传感器,各自有坐标系。LIO-SAM在做点云配准和IMU预积分之前,必须把雷达点云从雷达坐标系变换到IMU坐标系。这个变换就是一组刚体变换:旋转矩阵R加上平移向量t,合起来叫外参(extrinsic)。

数学上很简单:假设雷达坐标系里有一个点p_l,它在IMU坐标系里的坐标p_i就是:

p_i = R * p_l + t

旋转矩阵R决定姿态对齐,平移向量t决定原点偏移。就是这组数值,直接影响后续所有环节——点云畸变校正、特征提取、scan-to-map匹配、因子图优化,全都在这个变换之后进行。

实际项目里最典型的坑是:把雷达装在IMU正上方,平移向量大概估一个(0.05, 0, 0.15),旋转全填0,就开始跑地图。这种拍脑袋外参在小场景、短时间里问题不大,但建图时间一长、场景一大,误差就被因子图优化慢慢放大,最后地图扭曲到你怀疑算法被改坏了。

1.2 外参不准在地图上到底长什么样

我在多个项目里遇到过外参不准的情况,表现通常很一致:

  • 地图出现重影、叠影,尤其是墙面和门框边缘,同一特征在点云图里有两到三层轮廓;
  • 机器人静止时,点云还在缓慢旋转或漂移,IMU明明已经静止,建图节点却认为自己在动;
  • 走过长走廊这类退化环境时,轨迹发散严重,回环形同虚设;
  • 回环检测成功后地图出现明显断裂或错位,整个走廊对不齐。

这些现象有一个共同特点:靠调LIO-SAM的里程计参数基本救不回来。我见过有人连续调了好几天voxel leaf大小和匹配线程数,最后才发现是外参初值错了。外参是系统级的输入错误,优化器只会把它分摊到轨迹和地图里,让每一个环节都看起来有问题。

1.3 标定方案选型:imu_utils、lidar_align、手动迭代怎么搭配

LIO-SAM这条技术栈下,标定任务其实分两类:IMU内参标定和雷达与IMU外参标定。

标定类型常用工具输出内容适用场景
IMU内参imu_utils、kalibr_allan噪声密度、随机游走、零偏稳定性配置LIO-SAM噪声模型
外参标定lidar_align雷达到IMU的旋转矩阵和平移向量中高精度外参初值
手动迭代LIO-SAM自带extrinsic参数粗略外参快速验证、冷启动

实际项目里我的做法是三件套一起用:先用imu_utils标出IMU的噪声参数,配置进LIO-SAM的noiseModel;再用lidar_align求解外参,得到一个比"拍脑袋"靠谱得多的初值;最后回填进LIO-SAM,跑几轮数据微调。

1.4 为什么拿LIO-SAM当标定验证骨架

LIO-SAM本身并不直接输出外参标定结果,但它是最好的标定验证框架。reason很简单:LIO-SAM的前端同时用到了雷达配准和IMU预积分,两个传感器之间的外参只要有一点不对,系统误差就会以明显的方式暴露出来。

另外它的params.yaml把所有外参、噪声模型参数都放在明面上,改起来非常方便。标定结果好不好,跑一段数据看地图和轨迹就知道。相比单独写一个优化脚本来验证外参,直接跑SLAM反而更贴近真实使用场景。

2. 环境搭建阶段最容易被拖死的几个细节:版本、依赖与话题配置

2.1 Ubuntu版本与ROS发行版怎么选

目前我主力环境是Ubuntu 20.04 + ROS Noetic。如果你的团队还在用Ubuntu 18.04 + Ros Melodic,LIO-SAM同样能跑通,但编译依赖上会有一些差异。新项目建议直接上20.04,后面接其他激光雷达驱动、相机驱动也少踩坑。

安装ROS本体之后,先把基本的依赖装上:

sudo apt update sudo apt install -y ros-noetic-pcl-ros ros-noetic-cv-bridge ros-noetic-image-transport ros-noetic-tf2-geometry-msgs

这里有个容易忽略的点:LIO-SAM源码里用到PCL和OpenCV,但它是通过ROS的pcl_ros、cv_bridge间接依赖的。如果你系统里之前装过别的OpenCV版本,可能出现头文件或库冲突。最干脆的做法是在一个干净的catkin工作空间里编译,别把其他项目的第三方源码混进来。

2.2 GTSAM版本:LIO-SAM编译的第一大坑

LIO-SAM依赖GTSAM做因子图优化,最稳的是4.0.2版本。我从源码编译过最新版GTSAM,结果imuPreintegration.cpp里好几个接口对不上,报错信息五花八门,最后老老实实切回4.0.2。

编译GTSAM 4.0.2时也要注意,默认情况下会编译自带的可视化工具和测试,非常慢。建议关掉不需要的模块:

git clone https://github.com/borglab/gtsam.git cd gtsam git checkout 4.0.2 mkdir build && cd build cmake .. -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DGTSAM_BUILD_UNSTABLE=ON make -j8 sudo make install

编译LIO-SAM之前,确认一下GTSAM是否被正确找到。如果你用的是ROS Noetic,有时需要给CMake指定GTSAM的安装路径,否则它会去系统默认目录找旧版本。

2.3 雷达驱动和IMU驱动的话题检查清单

驱动都装好后,别急着跑标定,先把话题健康状态检查一遍。这是整个流程里最省时间的操作。

rostopic hz /livox/lidar rostopic hz /imu/data rostopic echo /imu/data -n 1

重点检查几个地方:

  • IMU话题频率是否稳定,一般需要100Hz以上,200Hz更好;频率不稳会影响IMU预积分的精度;
  • 雷达点云话题的frame_id是否设置正确,建议统一成lidar_link或laser_frame;
  • IMU消息里的linear_acceleration和angular_velocity是否有明显数值跳变,如果静止时数值乱跳,说明IMU驱动或硬件有问题,先修驱动再标定;
  • 是否有多个节点抢同一个话题名,导致数据源混乱。

还有一个frame_id的坑:LIO-SAM启动的时候会跑到TF树里找雷达和IMU的坐标变换关系。如果你在驱动里把雷达点云的frame_id写错了,启动之后虽然不报错,但点云全被变换到奇奇怪怪的坐标位置,地图自然就废了。

3. 联合标定实操全流程:从IMU内参到外参回填LIO-SAM

3.1 第一步:用imu_utils标定IMU内参

IMU内参主要影响LIO-SAM参数文件里三组噪声模型的数值:

imuAccNoise: 0.001 imuGyrNoise: 0.001 imuAccBiasN: 0.001 imuGyrBiasN: 0.001

这些数值如果随意填,IMU预积分对加速度和角速度的信任程度就是错的。往大了填,系统过于信任激光配准,在退化场景里容易飘;往小了填,系统过于信任IMU,激光配准跳出局部最优的能力又被削弱。

我的做法是用imu_utils来标定,编译它之前要先编译code_utils:

mkdir -p ~/catkin_lio_ws/src cd ~/catkin_lio_ws/src git clone https://github.com/gaowenliang/code_utils git clone https://github.com/gaowenliang/imu_utils cd ~/catkin_lio_ws catkin_make

如果你在Ubuntu20.04上编译code_utils报错,通常是缺少依赖或者std::bind相关接口问题,可以先把系统的build-essential和cmake升级到最新,然后再试。之后录制一段至少两小时的静态IMU数据:

rosbag record -O imu_static.bag /imu/data

录制过程中传感器要放在稳定平面上,不要碰它。两小时起步,时间越长Allan方差曲线尾部越稳定,标定出的随机游走值越可信。录完之后用imu_utils的launch文件处理bag,会输出一个包含噪声密度和随机游走的yaml文件,把这些数值填进LIO-SAM的params.yaml即可。

3.2 第二步:用lidar_align求解雷达与IMU外参

lidar_align是ETH开源的雷达与IMU外参标定工具,原理是找一个旋转矩阵和平移向量,让IMU轨迹积分出的运动与雷达配准出的运动对齐。它对输入数据的要求不低,但同一份数据能同时优化出外参和轨迹,在工程上很实用。

编译lidar_align需要先建一个catkin工作空间并把他放进去:

mkdir -p ~/lidar_align_ws/src cd ~/lidar_align_ws/src git clone https://github.com/ethz-asl/lidar_align.git cd ~/lidar_align_ws catkin_make

编译完成后,修改lidar_align的launch文件,指定bag路径、雷达话题和IMU话题。建议先把IMU话题频率降到100Hz左右再录标定数据,这样lidar_align处理起来更快,CPU压力小一些。

采集标定数据的要点是:传感器在空间里做充分的多自由度运动,既有平移也有旋转,最好走"S"形轨迹,并包含俯仰和滚转变化。尽量避免在狭窄走廊、空旷广场这些退化环境里采集,否则优化问题病态化,标定结果会非常离谱。

我自己的经验是:室内采一段2-3分钟、运动充分的数据,然后换个房间再采一段。lidar_align运行结束后,终端里会打印一组四元数和平移向量,这就是雷达坐标系到IMU坐标系的变换。

这里有一个需要注意的问题:lidar_align给出的结果对初值有一定敏感性。如果初值和真实值差得太远,它可能优化到局部最优。所以最稳妥的流程是先利用安装几何位置粗估外参,再喂给lidar_align做优化。

3.3 第三步:把外参写进LIO-SAM的params.yaml

LIO-SAM的params.yaml里有两个关键字段:

extrinsicRPY: [0.0, 0.0, 0.0] extrinsicTrans: [0.0, 0.0, 0.0]

extrinsicRPY是旋转的欧拉角,顺序通常按roll、pitch、yaw;extrinsicTrans是平移向量。在写入之前,一定确认lidar_align输出的变换方向到底是雷达到IMU还是IMU到雷达。LIO-SAM的约定是使用雷达坐标系到IMU坐标系的变换,也就是前面提到的p_i = R * p_l + t。

如果你把lidar_align输出的变换直接照着抄进去,但方向刚好反了,地图大概率会散成一片。批量操作的时候可以写一个小脚本,把lidar_align输出的四元数转成欧拉角,再填进yaml里。

回填之后,先不要急着跑长距离建图。用同一份标定bag跑一小段,观察初始阶段是否正常、地图是否对齐。确认没有问题后再去跑正式的SLAM数据。

3.4 第四步:快速验证标定结果是否靠谱

验证方法我推荐两种结合使用。

第一是肉眼观察地图质量:跑一段包含回环的数据,看回环闭合之后墙面对不齐。如果外参差几度,地图很可能在闭合处有几厘米到十几厘米的错位。

第二是用evo工具做轨迹评估。如果你的系统有GPS、动捕系统或者cartographer的高精度轨迹作为真值,可以对比LIO-SAM里程计输出与真值的绝对轨迹误差。evo的安装和使用非常直接:

pip install evo evo_ape tum groundtruth.tum lio_sam_odometry.tum

如果外参正确,里程计轨迹的APE应当显著低于未标定时的结果。我见过一个项目标定前APE误差在20厘米以上,标定后降到5厘米左右,数据非常直观。

3.5 手动微调:当工具标定结果还不够好怎么办

lidar_align这类自动工具在大多数场景下能给出不错的初值,但有些传感器组合或运动环境下,优化结果仍有偏差。这时候可以做一个简单的手动迭代:

先固定一个外参初值,跑LIO-SAM,观察地图;如果地图朝某个方向倾斜或旋转,就按对应方向微调外参;重复几次。

这个方法比较费时间,但非常实用。关键是一次只调一个变量:先调旋转,再调平移;先调roll,再调pitch,最后调yaw。如果一次改了好几个参数,地图变了也不知道是哪个参数导致的。

4. 成功跑通LIO-SAM之后的参数调优与效果评估

4.1 params.yaml里的关键参数到底怎么读

外参回填之后,LIO-SAM并不是就万事大吉了。params.yaml里还有一组参数直接决定系统性能,我也在这里做一个梳理。

voxelLeafSize: 0.5 imuAccNoise: 3.9939570888238808e-03 imuGyrNoise: 1.5783679948825181e-03 imuAccBiasN: 6.4356659353465690e-05 imuGyrBiasN: 3.5640318696367613e-05

voxelLeafSize是降采样体素尺寸。这个值太小,点云数量太大,CPU消耗飙升;太大,特征被抹平,匹配精度下降。室内场景我一般从0.5起步,室外大场景可以调到1.0到2.0。

IMU相关的四个噪声参数,来自imu_utils标定结果,描述的是IMU本身的噪声密度和随机游走。这些值如果标定得准,LIO-SAM对IMU的置信度就比较合理,预积分和因子图优化的权重分配也更可信。

4.2 启动顺序与话题流检查

LIO-SAM启动后,正常情况能看到多个线程在跑:特征提取、scan-to-map匹配、IMU预积分。启动命令很简单:

roslaunch lio_sam run.launch

但启动之前要保证雷达和IMU话题已经发布,否则LIO-SAM会一直等待输入。启动之后用rqt_graph查看节点和话题连接是否正常,尤其确认pointcloud和imu数据确实进入了lio_sam节点。

如果启动后map发布频率很低,建图原地踏步,大概率是CPU算力不足或voxelLeafSize设置过小。可以先降低点云频率,或者在录制好的bag上测试不同参数组合,再回到实机上使用。

4.3 调参经验:多快好省的组合拳

很多人在LIO-SAM跑通之后会陷入一种状态:觉得地图不够准,但不知道调什么。我的经验是分三步走。

第一步,先确认外参和IMU内参没问题,这一步上面已经聊过,外参不对调什么都白搭。

第二步,调voxelLeafSize和匹配阈值。先从小体素开始,慢慢增大,观察匹配耗时和地图清晰度,找到曲线拐点。

第三步,调回环检测相关参数。如果你的场景经常出现回环,但回环优化后地图反而更差,很可能是回环检测过于敏感,把错误的位置当成了回环。这种情况可以通过降低回环检测频率、提高帧间匹配的分数阈值来过滤。

4.4 用evo量化标定前后的误差对比

我建议每个项目都应该把标定前后的轨迹误差记录下来,形成数据档案。尤其在对外汇报项目进度时,一组"标定前APE 0.35米、标定后APE 0.08米"的数据,比一百句"我们的系统精度提高了"都更有说服力。

操作方法是把同样的bag分别用标定前和标定后的参数跑两遍,输出轨迹,然后用evo对比。

evo_ape tum gt.tum before.tum -a evo_ape tum gt.tum after.tum -a

如果手头没有真值轨迹,可以用多个回环闭合点作为参考,量地图中的特征点之间的距离,和实际物理距离做对比。这个土办法在开放路面也够用。

5. 避坑指南:标定和跑通LIO-SAM过程中踩过的大坑

5.1 时间戳不同步:大家以为没事但后果很严重

LIO-SAM对IMU时间戳和雷达时间戳的同步有一定容忍度,但容忍度不等于零。时间戳偏差过大会导致IMU预积分的参考时间和雷达帧时间错位,表现是轨迹出现周期性抖动、地图边缘出现毛刺。

我遇到过一次,IMU驱动使用系统时间,雷达驱动使用设备内部时钟,两者差了将近200毫秒,跑出来的地图在小范围拐弯时经常漂移。解决方式是在驱动层面统一时间戳来源,或者在录制bag时将传感器时间同步的机制打开。

录制标定数据时也建议加上/tf和/tf_static,这样后续调试时可以通过TF树快速定位坐标变换问题。

5.2 退化场景导致标定发散

这是一个很难从程序日志发现的坑。lidar_align在标定过程中依赖点云配准来估计雷达运动,如果把设备放在一个狭长走廊或者白墙环绕的房间里,配准容易退化,外参优化也跟着发散。

我建议数据采集时选择有明显几何特征的环境,比如室内桌椅、货架区域,或者室外有树木、电线杆的场地。运动过程中适当加入一些俯仰和滚转动作,不要只走平面轨迹,否则roll和pitch这两个自由度几乎没有可观测性,标定出来的旋转矩阵在这两个方向上不可信。

5.3 点云畸变和去畸变问题

旋转式激光雷达在扫描过程中,雷达自身也在运动,导致一帧点云里的点不是同一时刻采到的。LIO-SAM内部利用IMU做去畸变,但如果外参不准,去畸变方向就错了,效果甚至比不去畸变更差。

判断是否有这个问题的办法是看雷达静止时的点云:如果静止墙面在点云里出现拖影,说明去畸变可能有问题。另外,如果雷达是固态雷达,扫描机制和旋转式不同,畸变模型也不一样,标定思路要相应调整。

5.4 GTSAM和PCL版本问题导致的重新编译

这部分在第二章详细说过,这里再补充一个现象:如果你在编译LIO-SAM时遇到形形色色的"undefined reference"错误,大概率是GTSAM版本和LIO-SAM源码接口不匹配,而不是你的CMake配置有问题。先检查GTSAM版本,再检查LIO-SAM的commit版本,两者最好和官方README保持一致。

我在新环境里复现LIO-SAM的速度已经降到20分钟内,就是因为把这套版本对应关系固定下来了。建议把环境依赖和版本写进项目的README,而不是靠记忆。

5.5 坐标系约定的各种隐藏陷阱

ROS社区里激光雷达坐标系通常是x向前、y向左、z向上,IMU通常也是x向前、y向左、z向上,但并非所有硬件都遵循这个约定。比如某些工业IMU默认z轴向上,但加速度计方向的定义可能和ROS的REP-103不完全一致。

如果标定结果看起来正确,但地图始终存在一个固定的旋转偏移,很可能是传感器坐标系本身的轴方向和ROS标准不一样。解决办法是在驱动节点里把原始数据转换到ROS标准坐标系下,再做标定。不要试图用一个"万能外参"来补偿所有坐标系定义差异,那样后期维护会非常痛苦。

5.6 一个很不起眼但常见的坑:IMU频率不足导致预积分失败

有些便宜的IMU模块最高只有50Hz输出,而LIO-SAM对IMU频率的要求通常建议在100Hz以上。频率太低,IMU预积分对载体运动描述的精度会下降,尤其在快速旋转时,角速度积分误差会迅速放大。

如果你的IMU只有50Hz,可以尝试提高雷达帧率来补偿,或者在运行LIO-SAM之前用插值把IMU数据升频到100Hz。插值虽然不能增加物理信息,但至少能让时间对齐更平滑。不过更推荐的方式是换一块更高输出频率的IMU硬件,省很多麻烦。

5.7 最后一个经验:标定结果要建立可复现的数据档案

把标定工具的输出参数、LIO-SAM中的配置文件、当时的bag数据全部打包存档。每次改动硬件或换传感器之后,重新标定并记录变化。

我踩过的最深的一个坑是:某次项目交付时,同事反映地图精度下降,排查了一整天才发现是因为传感器被拆下重装过,外参已经变化,但配置文件里还是旧的外参参数。如果没有版本管理,这种问题很容易演变成通宵排障的事件。

给传感器模组加一个清晰标签,或者在机器人启动脚本里把外参文件按硬件序列号索引,都能有效避免这个坑。

个人的经验是:激光雷达与IMU联合标定这件事,没有一劳永逸,只有一遍一遍把流程做扎实。每一次标定文档记录、参数回填和效果验证,都是在为后续更复杂的算法开发打地基。如果你现在也被外参不准折磨得头疼,不妨照着这个流程完整走一遍,应该能少走不少弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询