做激光雷达SLAM和IMU融合定位这些年,我踩得最多、也最容易被忽略的坑就是传感器之间的“初始位姿”到底准不准。外壳夹具拧歪一度,地图立马换个画风;点云重影、地图漂移、定位跳变,排查到最后大概率都是外参的锅。lidar_imu_calib就是专门把激光雷达和IMU之间的旋转、平移以及时间延迟一次标出来的开源工具,非常适合和我一样用VLP-16这类多线机械雷达搭配9轴IMU做建图、定位、多传感器融合的玩家。这篇文章我直接拿VLP-16实测跑通一遍:从环境搭建、数据采集到参数配置和结果验证,全部写清楚,按步骤做就能拿到能用的外参。
1. 为什么激光雷达和IMU需要联合标定
1.1 标定的到底是什么
很多刚入坑的同学觉得,雷达和IMU都装在同一个板子上,直接卡尺量一下、角尺比一下不就行了?真没这么简单。装上之后,雷达坐标系和IMU坐标系之间存在一个固定但未知的三维旋转R和一个三维平移t,这个变换在融合算法里叫外参。同时,两个传感器的时间戳系统很难做到完全同步,VLP-16在10Hz频率下工作,IMU通常跑200Hz,两个数据流之间的延迟哪怕差几十毫秒,在转动剧烈的场景下点云也能错出明显重影。所以标定要做两件事:一个是空间对齐(R和t),一个是时间对齐(时延td)。
这个工具解决的是“雷达-IMU融合”里的公共第一步。后面无论是做LIO(lidar-inertial odometry)、把IMU用于点云去畸变,还是视觉激光雷达融合,第一步都是先把外参标准。你要是外参不对,算法再牛也会被带偏。
1.2 VLP-16这类雷达的误差放大效应
VLP-16是16线机械雷达,水平360度、垂直视场从正15度到负15度,标称测距能到100米。这个雷达性价比高,在室内外巡检、小车上非常常见。但它也有一个物理特性:角分辨率不够细,垂直方向相邻两线之间隔着2度,水平方向10Hz时大约0.2度。正因为角分辨率有限,外参差一点,远距离点云就会偏移得离谱。
举个例子,如果外参绕水平轴偏了1度,30米远处的点云会偏移大约0.5米。这点误差映射到地图上,就是墙面分层、地面出现双影、柱子变成两根。IMU的安装角通常还带着几度的偏差,靠机械加工去保证精度,成本高还不稳定。对做SLAM的人来说,与其跟“建图飘”的问题死磕,不如先把标定做了,效率高得多。
2. 标定原理速通:知道三条主线就行
2.1 工具内部在做什么
lidar_imu_calib的整体思路可以拆成三阶段:先是初始旋转估计,再是时间偏移估计,最后是联合优化。
第一阶段,程序从激光点云里提取边缘点和平面点,利用相邻两帧之间的点云匹配,估计出激光雷达在短时间内的自身运动。与此同时,拿IMU加速度计和陀螺仪积分出同一段时间的姿态变化。把这两段运动放到一起,用最小二乘就能求出一个初始的旋转外参。
第二阶段,引入一个时间偏移量td。程序把IMU轨迹在时间轴上左右平移,让IMU运动序列和激光雷达运动序列对齐,对齐效果最好的那一个td就是时间延迟的初值。
第三阶段,把外参R、t和时延td一起放进Ceres优化器里,联合优化。激光雷达这边给的是点云帧间配准残差,IMU这边给的是姿态积分残差,两股约束合起来求解,得到最终的外参。整个过程背后的数学主要是四元数、李群SO(3)和B样条插值,对使用来说不需要全部精通,但理解这个流程能帮你知道参数文件里的每一项在干什么。
2.2 为什么标定数据必须“动起来”
我见过有人把设备往桌子上一放,录了五分钟rosbag就开始标,结果外参乱七八糟,然后怀疑工具不行。其实不是工具不行,是数据里根本没有足够的运动激励。
激光雷达帧间配准需要雷达前方有视差变化,需要有边、有角、有平面;IMU积分需要设备真正发生转动和平动,加速度计和陀螺仪才有有效输出。如果设备静止,雷达看到的是同一幅点云,IMU积分出来的是重力向量,约束全部退化,优化结果完全不可信。所以采集必须包含六个自由度的激励:前后左右平移、上下颠簸、左右旋转、点头抬头,都要有。后面我会给一套可直接照抄的运动方案。
3. 环境准备与工具安装
3.1 硬件安装与时间同步
开工之前先检查机械固定。雷达和IMU之间绝对不能有相对运动,螺丝要紧固,支架不能软。要是设备标定过程中被撞了一下,后面所有结果都得作废。
时间同步这件事容易被忽略。VLP-16驱动默认会带时间戳,但如果你通过USB转网口或者交换机中转,时间戳可能抖动。IMU如果用的是串口转USB,也容易有周期抖动。工具本身能估计时间延迟,但它的能力范围在几十到几百毫秒级别,如果你系统时间本身就是乱的,那谁也救不了。我的习惯是:先把工控机的系统时间用NTP同步好,再确认VLP-16的PTP或者gPTP时钟正常,最后看一眼IMU话题的时间戳曲线是否平稳。
IMU的数据频率也要达标,建议至少100Hz,最好200Hz。VLP-16一般跑10Hz,两个频率跨一个数量级,标定才有余量。
3.2 编译依赖与避坑
我在Ubuntu 18.04 + ROS Melodic上编译运行,一次通过。Ubuntu 20.04 + ROS Noetic也能跑,但更容易踩编译版本坑,建议新手直接用18.04,省心。
依赖主要是三个:Ceres Solver、PCL、Eigen。Ceres建议用1.14.0版本,直接用系统源安装:
sudo apt install libceres-dev然后创建工作空间并拉代码:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone https://github.com/APRIL-ZJU/lidar_imu_calib.git cd ~/catkin_ws catkin_make -j4编译如果不通过,优先检查Ceres版本。Ubuntu 20.04自带的高版本Ceres搭配这个老代码,经常会出现函数签名不匹配的报错,解决办法是源码编译Ceres 1.14.0:
git clone https://github.com/ceres-solver/ceres-solver.git cd ceres-solver git checkout 1.14.0 mkdir build && cd build cmake .. make -j4 sudo make install编译成功后,用rospack find lidar_imu_calib验证路径能找到,说明环境OK。Noetic用户如果遇到C++标准问题,需要在CMakeLists里把C++标准改成14,代码本身不至于大改。
4. 采集一份合格的标定数据
4.1 场景选择和运动激励方法
数据是否合格,决定了标定能不能收敛,也决定标定结果靠不靠谱。先把场景选好:要选有结构感的区域,比如园区里的柱子、墙角、停车场的隔离墩、树干、围墙、栅栏。太空旷的广场、什么都没有的走廊、大玻璃幕墙区域都不行,点云匹配会退化。
运动方案我建议按顺序录:
- 原地顺时针慢慢转一圈,再逆时针转一圈,每圈10秒左右;
- 推着设备走一个S形或8字形,过程中自然包含转向;
- 走直线的同时突然加速和减速,来回两三趟;
- 把设备上下点头、左右摇晃,幅度不用大,但要连续;
- 最后做几个“边转边平移”的组合动作,相当于把旋转和平动耦合起来。
整套动作录下来,1到3分钟足够。我实测下来,2分钟的数据标出来的结果就很稳。录的时候观察rviz里的点云,不要出现大面积断帧,人也不要挡在雷达正前方不动。
4.2 rosbag录制与话题检查
先确认两个核心话题有数据:
rostopic hz /velodyne_points rostopic hz /imu/data雷达话题频率要在10Hz上下,IMU要在100Hz到200Hz。如果IMU话题半天跳不出来,先查驱动,不要急着录。
确认无误后开始录制:
rosbag record /velodyne_points /imu/data -O lidar_imu.bag录完后马上检查:
rosbag info lidar_imu.bag重点看话题名是否正确、总时长是否足够、消息数量是否合理。我踩过一次坑:IMU驱动发布的话题是/imu/data_raw,而工具配置里写的是/imu/data,跑起来之后程序在等数据,日志一直不动。所以录制前一定要把话题名记下来,后面配参数要用。
5. 参数配置逐项讲解
5.1 雷达与IMU参数文件
lidar_imu_calib的参数文件主要集中在config目录下,不同fork版本文件组织略有差异,但核心就是雷达配置、IMU配置和主标定配置三类。
雷达配置里,最要紧的几个参数是话题名、最小距离和最大距离。VLP-16最远标称100米,但过远的点云噪声大、线数稀,反而干扰配准,建议max_range设在30到50米。min_range设在1到1.5米,把近处的非地面杂点滤掉。
IMU配置里,除了话题名,一般还要填噪声密度和随机游走。这两个值如果是随便抄的,工具也能跑,但优化精度会受影响。最靠谱的做法是用imu_utils或者Kalibr先把IMU内参标定出来,再把结果填进去。如果你手上的IMU是入门级九轴模块,噪声参数填个大概也够用,毕竟这个工具的重点是外参。
5.2 标定主参数说明
主标定配置里,我建议重点关注三个:时间偏移初值offset_init、是否启用时延估计、优化迭代设置。
offset_init默认给0通常没问题。如果雷达和IMU驱动之间延迟明显,比如你发现点云和IMU在时间上差了好几百毫秒,可以先在rviz里观察一下大概差多少,然后把offset_init填成这个负值或正值,帮助工具更快收敛。
时延估计开关一定要打开。很多同学觉得“我时间同步做得好,不需要估计时延”,但实际系统里驱动转发、串口缓冲、bag落盘都会引入延迟,把这行关掉等于自动放弃了工具一个很有用的能力。
迭代次数和收敛阈值保持默认就行,不用动。真正要花心思的是采集数据,参数本身给工具留的余量非常大。
6. 跑通流程与结果验证
6.1 运行与日志解读
启动工具前,把参数文件里的话题名和你录的bag对齐。直接回放bag:
rosbag play lidar_imu.bag然后再开标定节点。不同版本的启动命令不一样,我的经验是看仓库README,常见的启动方式是:
roslaunch lidar_imu_calib lics_calib.launch跑起来后日志会按阶段输出。第一阶段会显示点云特征提取和帧间匹配的进度,第二阶段会打印时间偏移估计值,第三阶段优化完成后会直接打印最终外参,一般以四元数加平移量的形式给出,例如 q_imu_lidar 和 t_imu_lidar。
拿到四元数可以先自己心算一下合理性。如果雷达和IMU大致都朝前安装,旋转外参应该接近单位四元数;如果是垂直安装,会比较接近90度对应的四元数。如果算出来的旋转角是45度、137度这种和机械安装完全对不上的值,先别急着用,数据或参数大概率有问题。
6.2 结果怎么验证
标定输出不能直接拿来就跑,一定要验证。我最常用的方法有三个。
第一个方法,用TF把雷达点云转到IMU坐标系,在rviz里同时打开标定前后的点云。静止环境下,远处墙面应该清晰锐利,不该出现双影。如果墙面出现了两层点云边界,说明旋转外参还有残差。
第二个方法,把标定外参写进LIO算法,比如Fast-LIO或LIO-SAM,然后回放同一段标定bag做一次建图。如果建图干净、回环闭合半径小,说明外参是可信的。如果地图出现蘑菇云形状,或者转弯处明显漂移,多半还是外参或者时延的问题。
第三个方法,重复标定三次。每次重新录一段数据,比较三次标定结果。如果旋转角变化在0.1度以内、平移变化在1厘米以内,说明稳定性良好。如果三次结果差别很大,说明数据激励不足或场景不合适,直接重新采数据。
7. 常见问题与避坑指南
7.1 常见报错速查表
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 编译报错找不到Ceres | Ceres没装或版本过高 | 源码安装Ceres 1.14.0 |
| Noetic下编译报C++标准错误 | gcc版本太高 | CMakeLists中指定C++14 |
| 启动后日志一直停在等待话题 | 参数文件话题名和bag不匹配 | 用rosbag info核对话题名 |
| 点云特征提取大量失败 | 场景空旷或雷达频率不稳 | 换有柱子、墙角、围栏的场景 |
| 优化结果反复横跳 | 数据激励不足或时长太短 | 按4.1的运动方案重新录2分钟 |
| 时延估计收敛到边界值 | 系统时间戳漂移严重 | 校准系统时间,检查驱动时间戳 |
| 结果和机械安装角差距过大 | IMU坐标系定义不同 | 核对坐标系方向,尝试对IMU坐标轴取反 |
7.2 标定精度翻车排查思路
标定结果“看起来能用但仔细一测就露馅”,通常不是工具不行,而是数据采集或坐标系约定出了偏差。
安装松动是第一大杀手。设备固定螺丝没拧紧,机器人动起来之后雷达和IMU相对位姿一直在变,标定出来的当然是个随机值。这一点我建议每次采集前都重新拧一遍。
坐标系方向没对齐也很常见。VLP-16的坐标系定义一般是x轴向前、y轴向左、z轴向上,但IMU的坐标系不同厂商定义千奇百怪,有的z轴向上,有的z轴向下,有的x轴指向后方。如果IMU的某一个轴方向和工具假设的相反,标定结果里会出现一个奇怪的偏转角。遇到这种情况,先检查IMU驱动手册里的坐标定义,必要时在驱动层把坐标轴修正统一。
时间戳抖动是另一个容易忽略的点。我遇到过IMU驱动串口波特率设置不对,导致数据丢包、时间戳重复,标出来的时延值特别大。用rostopic hz观察话题频率曲线,如果忽高忽低,先修驱动再标定。
7.3 最终自检清单
每次拿到标定结果,我都按下面这份清单检查一遍:
- 旋转外参是否和机械安装方向大致一致;
- 平移量是否在厘米到几十厘米的可信范围;
- 时延是否落在相对稳定的合理区间;
- rviz里点云转坐标系后没有明显重影;
- 连续标定三次,结果波动在可接受范围内;
- 用一套独立于标定数据的bag验证建图效果。
这六条全过了,这组外参才敢往算法里放。我个人的经验是,与其在出问题时反复调算法参数,不如用2分钟录一段好数据、花10分钟标定一次,把外参这个地基夯实。标定这东西,机器不会骗你,数据骗你的时候才最麻烦。后续再做LIO建图或者多传感器融合,你会感谢当初把标定认真做完的自己。