搞了快两周,把LVI-SAM在Ubuntu20.04上从源码编译到实机跑通,中间还折腾了6轴IMU的适配。这玩意儿网上教程不少,但大多只讲到“能跑数据集”就停了,真到自己上手接硬件,全是坑。这篇文章把我整个复现过程和实机部署流程捋一遍,重点说说6轴IMU接入时改了什么、为什么这么改,给后面要搞LVI-SAM的人省点时间。
LVI-SAM是Tixiao Shan在LIO-SAM基础上扩展出来的紧耦合SLAM方案,融合了激光雷达、相机和IMU三类传感器。它解决的问题很明确:单靠激光雷达在退化场景(长走廊、空旷场地)容易飘,视觉在光照变化和快速运动时容易丢,IMU短时间精度高但长期会漂,三者融合正好互补。官方代码基于ROS1,Ubuntu20.04对应Noetic版本,整个系统拆成视觉里程计、激光惯性里程计、回环检测与地图优化三个模块。对于正在系统学ROS、准备入门激光视觉融合SLAM的人来说,LVI-SAM是一个绕不开的经典开源项目。
1. 整体方案与核心设计思路
1.1 LVI-SAM到底在做什么
先理解这系统的整体结构。LVI-SAM本质上是两个紧耦合子系统并行工作:一个视觉惯性系统(VIS),一个激光惯性系统(LIS)。两个子系统通过因子图共享状态估计,激光惯性系统的结果给视觉系统提供初始值和回环候选,视觉系统的结果反过来给激光系统提供姿态先验。这种双向耦合比简单的传感器数据叠加要稳得多。
代码上对应三个核心节点:
- visualOdometry:处理相机图像,提取特征点,与IMU预积分结果做视觉-惯性里程计,输出6自由度位姿。
- imuPreintegration:接收IMU原始数据,做预积分,维护因子图,为其他模块提供高频位姿先验。
- mapOptimization:接收激光雷达点云,做特征提取(角点、平面点),scan-to-map匹配,同时处理回环检测和图优化,输出最终建图结果。
这三个节点之间的关系,用一条数据流来描述就是:IMU数据进imuPreintegration,得到当前位姿估计,这个估计同时喂给visualOdometry做视觉匹配初始值、喂给mapOptimization做激光匹配初始值。两个子系统各自产生的结果再回到因子图里做全局优化。
1.2 从LIO-SAM到LVI-SAM,视觉带来了什么
如果你看过LIO-SAM的源码,再看LVI-SAM会觉得很亲切,因为很多代码结构几乎一样。LVI-SAM最大的改动是在原有的雷达惯性系统上叠加了视觉里程计作为额外的观测来源,并在因子图中增加了视觉因子。
为什么要引入视觉?雷达在结构化环境中表现很好,但在长走廊、隧道这类几何特征单一的场景下,激光特征退化,匹配就容易飘。相机能提供丰富的纹理信息,在这些场景下反而能帮上大忙。反过来,相机对光照敏感,快速运动时图像模糊,这时候雷达又比视觉可靠。两个传感器各有短板,融合起来才能在不同场景下都有可接受的精度。
另一个值得注意的设计是,视觉里程计和激光里程计之间采用的是紧耦合,而不是简单的松耦合松耦合。LVI-SAM不是先把两个系统的结果拼在一起,而是在因子图层面做联合优化,这样能更好地处理传感器噪声和相关性问题。对实际部署来说,紧耦合的好处在于即使某一个传感器短暂失效,整个系统也不会立刻崩掉。
1.3 这套方案有什么取舍
LVI-SAM的取舍还是挺明显的。它用三传感器融合,精度和鲁棒性上来了,但系统复杂度和算力要求也上来了。作者的测试环境用的是一台高性能笔记本加独立显卡,实机部署在NVIDIA Jetson AGX Xavier这类平台上跑起来还比较流畅,但你要是想在树莓派或者低端ARM板上跑,帧率会非常难看。
另外,LVI-SAM对传感器标定和同步的要求比较高。三个传感器之间的外参、时间偏移如果不对,整个融合效果会直线下降。这也是为什么很多人在数据集上跑得很好,一上实机就开始各种飘的原因。实机部署的难点根本不在算法本身,而在工程问题。
2. 环境搭建与依赖准备
2.1 Ubuntu20.04和ROS Noetic是刚需
LVI-SAM官方没有明确说支持哪个ROS版本,但代码里大量使用了ROS1的API,跑在Noetic上没有任何问题。Ubuntu20.04配ROS Noetic是当前ROS1生态最主流的组合,软件包兼容性最好。
装系统没啥好说的,需要注意的是如果你用双系统,建议单独给Ubuntu分一个至少100GB的盘,因为光是依赖库、源码编译产物和数据集就能轻松吃掉几十GB。装完系统后先别急着装ROS,先把软件源换成国内镜像源,这个能省下后续大量下载时间。
ROS Noetic的安装推荐直接用鱼香ROS的一键安装脚本。这东西虽然名字听着有点草台班子,但实际用过之后会发现确实省心,脚本会自动帮你配置软件源、安装ros-noetic-desktop-full、初始化rosdep,整个过程基本不用手动干预。命令很简单:
wget http://fishros.com/install -O fishros && . fishros按照提示选择ROS1 Noetic安装项就行。装完之后确认一下环境变量有没有写进~/.bashrc:
source /opt/ros/noetic/setup.bash echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc验证一下能否正常使用:
roscore能启动就说明ROS装好了。
2.2 核心依赖逐个装:Ceres、GTSAM、OpenCV、PCL
LVI-SAM编译之前,有几个核心依赖需要提前搞定,任何一个出问题都可能导致编译失败。
第一个是Ceres Solver,版本建议用1.14.0。LVI-SAM在mapOptimization节点中用到了Ceres做scan-to-map匹配的优化求解。这个库直接apt装的话版本会比较老,而且经常和后续编译的LVI-SAM出现ABI兼容问题,所以推荐源码编译:
sudo apt-get install -y cmake libgoogle-glog-dev libgflags-dev libatlas-base-dev libeigen3-dev libsuitesparse-dev git clone https://github.com/ceres-solver/ceres-solver.git cd ceres-solver git checkout 1.14.0 mkdir build && cd build cmake .. make -j$(nproc) sudo make install第二个是GTSAM,这是因子图优化的核心依赖,LVI-SAM的imuPreintegration节点全靠它做增量平滑。GTSAM没有对应的Ubuntu apt包,需要源码编译。作者推荐使用4.0.2版本,实测4.1.1也能编过,但建议还是跟着官方文档走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_UNSTABLE=ON .. make -j$(nproc) sudo make install注意CMake参数里这个-DGTSAM_BUILD_UNSTABLE=ON一定要开,否则LVI-SAM里引用的部分头文件会缺失。
第三个是OpenCV和PCL。这两个不用特殊处理,Ubuntu20.04自带的OpenCV 4.2.0和PCL 1.10就够用,不需要额外折腾。LVI-SAM代码本身对OpenCV的调用并不深入,主要就是读图像、特征提取、描述子匹配这些基础操作。
Eigen也不用单独装,编译Ceres和GTSAM的时候会把它带上来。
2.3 创建catkin工作空间并编译LVI-SAM
依赖装齐了,接下来创建catkin工作空间。以我实际使用的目录结构为例:
mkdir -p ~/lvi_sam_ws/src cd ~/lvi_sam_ws/src git clone https://github.com/TixiaoShan/LVI-SAM.git cd .. catkin_make如果没有意外,这个编译过程会跑很久。第一次编译时,GTSAM的头文件和库文件可能会找不齐,常见报错是找不到gtsam/gtsam.h或者metis.h。这是因为GTSAM安装后头文件路径没有进到系统默认搜索路径。解决办法是修改LVI-SAM的CMakeLists.txt,在include_directories里手动加上GTSAM头文件路径:
include_directories( include /usr/local/include/gtsam /usr/local/include/gtsam/3rdparty/metis )编译完成后,如果devel/lib目录下生成了lvi_sam对应的三个可执行文件,说明编译通过。
3. 数据集复现:跑通官方bag验证系统
3.1 下载测试数据与启动launch文件
源码编译通过只是第一步,接下来要在数据集上验证整个系统能正常工作。LVI-SAM作者提供了测试用的rosbag文件,里面包含了相机图像、激光点云和IMU数据。我用的是官方推荐的handheld bag,传感器数据比较全,适合验证系统闭环效果。
启动前需要修改LVI-SAM的配置文件,路径在LVI-SAM/config/params.yaml。里面几个关键参数需要确认:
sensor: # 传感器话题名 lidar: "/velodyne_points" imu: "/imu/data" camera: "/camera/image" camera: # 相机内参 fx: 467.6719 fy: 465.5709 cx: 344.7813 cy: 228.5359 distortion: [0.0815, -0.1327, -0.0002, 0.0001, 0.0]不同版本的bag话题名可能不一样,先跑rosbag info看一下数据包里面的话题名,再对照修改。
启动方式:
source ~/lvi_sam_ws/devel/setup.bash roslaunch lvi_sam run.launch另开一个终端播放bag:
rosbag play your_bag.bag正常情况下,几秒钟之后Rviz里会开始出现点云地图和轨迹线。初始化需要一点时间,系统会对IMU做静止初始化。
3.2 跑数据集时关注什么
数据集跑通了,别急着庆祝,先观察几个关键点来判断系统状态是否健康:
看Rviz里的按时间上色的点云地图。地图应该随着传感器移动而不断扩展,轮廓清晰,没有明显拖影。如果地图出现重影,说明点云配准有问题,最常见的原因是IMU噪声参数不对或外参标定错误。
看轨迹输出。轨迹应平滑连续,没有跳变。如果出现跳到远处又跳回来的现象,大概率是回环检测误匹配导致图优化把轨迹拉歪了。
看CPU占用。在普通笔记本上用CPU跑,如果点云帧率不高,CPU占用在300%-500%之间是正常的。如果长期800%以上,后面实机部署就得考虑降低帧率或者用GPU推理了。
官方bag跑完,整个系统的基本链路就算验证通过了。从零开始到这一步,大概需要一天时间,大部分时间都耗在编译和依赖问题上。
4. 6轴IMU适配:原理与实操细节
4.1 为什么6轴IMU要单独适配
这是全文最核心的部分。
LVI-SAM原始代码默认使用9轴IMU,也就是包含加速度计、陀螺仪、磁力计的IMU模组。9轴IMU的磁力计可以提供绝对航向参考,帮助系统在初始化阶段把世界坐标系的航向对齐到地磁方向,同时也能抑制偏航角随时间漂移。
但实际市面上的很多IMU模组,尤其是那些体积小、功耗低的模块,都是6轴的,只有加速度计和陀螺仪,没有磁力计。用6轴IMU接入LVI-SAM,如果直接照搬默认配置,会出现几个典型问题:
第一,初始航向角不可观。六轴IMU静止时,加速度计只能感知重力向量,告诉你哪个方向是“下”,陀螺仪只能感知角速度变化,告诉你转了多少,但无法告诉你面朝哪个方向。整个系统的初始偏航角就失去了参考基准,只能依赖视觉或激光的第一次观测来初始化。
第二,偏航角累计漂移。没有磁力计校正,光靠陀螺仪积分偏航角,必然会产生缓慢漂移。短时间运行问题不大,但长时间建图会看到轨迹逐渐扭曲。
第三,代码中有一些隐含假设。LVI-SAM中IMU消息里的orientation字段在部分代码路径下会直接作为先验使用。如果IMU驱动输出的是一个没有绝对航向参考的orientation,这些先验就会带偏系统。
那怎么解决?思路是:在驱动层面正确处理6轴IMU的数据发布,在算法层面调整LVI-SAM的初始化逻辑,让系统不完全依赖磁力计提供航向。
4.2 IMU数据发布格式:至关重要的sensor_msgs/Imu
先说IMU数据怎么发。ROS中IMU数据的标准消息类型是sensor_msgs/Imu,核心字段有:
orientation:四元数表示的姿态,如果IMU融合算法能给出姿态就用它,给不了就填单位四元数,并把orientation_covariance[0]设为-1告诉下游“这个字段不可用”。angular_velocity:三轴角速度,单位rad/s,来自陀螺仪。linear_acceleration:三轴线性加速度,单位m/s2,来自加速度计。
很多自制IMU驱动的坑就出在orientation字段上。你需要想清楚:你的6轴IMU能不能输出可靠的姿态?
如果你的IMU模块内部有DMP(数字运动处理器),比如MPU6050的DMP固件,它能融合加速度计和陀螺仪数据,输出稳定的roll和pitch,但yaw是积分出来的,没有绝对参考。这种情况下,建议把DMP输出的四元数填进orientation字段,但要注意这个yaw会随时间漂移,算法端不要把yaw当作先验。
如果你的IMU模块只是裸的加速度计和陀螺仪,没有融合算法,那更简单,orientation直接填单位四元数,把orientation_covariance[0]设为-1,让下游自己处理姿态估计。LVI-SAM的预积分模块本来就要自己维护姿态,不会依赖这个消息里的orientation。
另外两个参数的设置也要注意。linear_acceleration发布时必须除以重力加速度g。ROS的Imu消息规范里,linear_acceleration的单位是m/s2,但很多IMU驱动会直接输出原始加速度计值(单位是g),比如静止时输出[0, 0, 1],这个在ROS里其实是错的,应该输出[0, 0, 9.80665]。LVI-SAM内部对IMU数据的处理有单位假设,搞错了重力方向就完全不对了。
angular_velocity的单位是rad/s,不是deg/s,这个坑也踩到过。
4.3 标定IMU内参:imu_utils + Allan方差
6轴IMU要跑出好效果,光把数据发对还不够,噪声参数必须标定。LVI-SAM的config/params.yaml中关于IMU有这几个参数:
imu: # IMU噪声参数,单位:连续时间 noise: [0.005, 0.005, 0.005, 0.05, 0.05, 0.05] bias: [0.001, 0.001, 0.001, 0.01, 0.01, 0.01]前面的noise对应加速度计和陀螺仪的噪声密度(noise density),后面的bias对应随机游走(bias random walk)。这两个参数如果不标定,直接照搬教程值,很多时候也能跑,但精度会差一截。尤其是在实机低速运动时,IMU噪声参数对系统初始化速度的影响非常明显。
IMU内参标定的标准做法是用Allan方差分析,工作在ROS环境下的开源工具是imu_utils配合code_utils。标定的大致流程是:
- 把IMU固定在一个稳定的平台上,静止放置2小时以上,录制一段长时间静止数据。
- 用
imu_utils对录制的数据做Allan方差分析,得到加速度计和陀螺仪各轴的噪声密度和随机游走。 - 把得到的数值填进
params.yaml。
这套工具编译起来也有点麻烦,因为code_utils要先编译,然后imu_utils依赖它。建议直接跟着官方仓库的README走。如果不想搞这么麻烦,一个应急的替代方案是用9轴IMU模组(比如MPU9250)的数据手册里的典型值,但效果只能算能用,不算最优。
4.4 外参标定:IMU和相机/雷达之间的位姿
如果说内参标定决定系统精度上限,外参标定就决定系统能不能正常收敛。LVI-SAM的params.yaml里有三个外参矩阵:
extrinsic_rot: [1, 0, 0, 0, 1, 0, 0, 0, 1] extrinsic_trans: [0.0, 0.0, 0.0]这个外参描述的是IMU坐标系相对于雷达坐标系的变换,也就是雷达和IMU的相对位姿。注意,这和你日常理解的“相机到雷达外参”不同,LVI-SAM用的是IMU作为基准,所以外参表达的是雷达在IMU坐标系下的位姿。
如果你用官方数据集跑,外参不用动。但实机部署时,这个外参必须标定。
传感器外参标定的工具选择看情况:
- IMU到相机:推荐用
Kalibr,这是苏黎世理工开源的相机IMU标定工具,可以同时标定相机内参、相机到IMU外参和时延。虽然Kalibr支持的ROS版本比较老,在Noetic上需要打补丁编译,但效果确实是目前开源方案里最好的。 - IMU到雷达:方案不统一,常见的有
lidar_imu_calib、direct_visual_lidar_calibration等。我实测下来效果比较稳定的是lidar_imu_calib,输入是静止环境下雷达点云和IMU数据,通过对比雷达拟合平面和IMU重力方向来标定外参中的旋转部分。
外参标定的一个重要原则是:不要用手量尺子去量传感器之间的物理距离。物理测量误差大,而且传感器的参考坐标系通常在外壳内部,你根本量不到准确值。用标定工具依靠数据自动求解,比手工测量靠谱得多。
4.5 代码层面针对6轴IMU的改动建议
内参、外参、话题格式都处理好了,还有最后一个收尾工作:检查LVI-SAM代码里有没有对9轴IMU的隐含假设。
我编译的源码版本里,imuPreintegration.cpp中IMU预积分初始化部分的逻辑是:用前几帧的加速度均值来估计重力方向,然后用重力方向来对齐世界坐标系。这个逻辑使用6轴IMU时天然可行,因为加速度计不需要磁力计就能感知重力。
但要留意的是如果系统启动时IMU不是静止的,初始化阶段对重力方向的估计就会出错,导致后续整个地图都是歪的。这严格来说不是6轴IMU的问题,但6轴IMU因为没有绝对航向参考,对初始化姿态错误更敏感。
另一处值得审查的地方是visualOdometry.cpp中视觉-惯性联合初始化的代码。部分版本的代码在视觉和IMU联合初始化时,会假设视觉给出的相对旋转和IMU积分出的相对旋转之间有一个近似恒定的偏置,这个偏置在6轴IMU上就是初始yaw不可观带来的。如果视觉和IMU外参标定不够准,或者视觉特征跟踪质量不好,初始化阶段就可能计算出一组很差的外参初值,后面怎么优化都救不回来。
我实际采用的最省事方法是:在LVI-SAM源码中不要修改核心算法逻辑,而是先把IMU驱动做好,确保发布的IMU数据干净、坐标系正确、时间戳准确。绝大多数人在实机上的问题根本不是代码需要改,而是IMU数据本身就不对。
4.6 6轴IMU适配的检查清单
把上面说的内容整理成一个自检清单,实机部署前逐项过一遍:
| 检查项 | 正确状态 | 错误表现 |
|---|---|---|
| linear_acceleration单位 | m/s2,静止时z轴约9.8 | 静止时z轴约1,说明发的是g值 |
| angular_velocity单位 | rad/s | 数值过大,说明发的是deg/s |
| orientation字段 | 不可用时covariance[0]=-1 | 填了错误姿态导致初始化发散 |
| 时间戳 | 持续单调递增,与雷达/相机在同一时钟域 | 时间戳跳变或为0 |
| 静止初始化 | 系统启动时IMU保持静止3-5秒 | 启动时晃动导致重力方向估计错误 |
| 外参 | 标定工具计算,非手工测量 | 地图出现系统性倾斜或重影 |
5. 实机部署流程:从桌面到小车的最后一公里
5.1 实机传感器配置与硬件选型
数据集跑通只是热身,实机上跑通才是最终目标。以我手头的测试平台为例,传感器配置如下:
- 激光雷达:速腾聚创RS-LiDAR-16线,16线机械式雷达,10Hz频率,点云话题
/rslidar_points。 - 相机:Intel RealSense D435i,虽然D435i自带IMU,但我实际用的是外置的独立IMU模组,因为RealSense自带的IMU噪声偏大,稳定性也一般。
- IMU:6轴的BMI088模组,通过串口转USB接入,200Hz输出,话题
/imu/data。 - 计算平台:Intel NUC11,i7-1165G7 + 16GB内存,跑LVI-SAM勉强够用。
这套配置不算高端,但能说明的是,LVI-SAM对传感器的要求其实是“中规中矩”:雷达16线以上、相机30帧以上、IMU 100Hz以上,都能跑出不错的效果。
传感器固定和安装是实机部署最容易被低估的环节。三个传感器之间的坐标变换关系要尽可能保持刚性,固定要足够牢靠,任何微小的松动都会导致外参失效。我见过一个案例,IMU只是用双面胶粘在支架上,结果在振动环境中外参漂移,建图精度大幅下降。
5.2 雷达到相机、雷达到IMU的时间同步问题
实机部署中,时间同步比外参标定更容易被忽略,但影响却是灾难性的。如果你传感器的驱动各自为政,一个用系统时间戳,一个用传感器内部时间戳,LVI-SAM收到的“同时刻”数据实际上差了100毫秒以上,融合效果会非常差。
目前比较常用的软件同步方案是:在驱动层面让所有传感器的时间戳都使用主机系统时钟。对USB接口的传感器(相机、IMU),驱动收到数据时打上系统时间戳;对网络接口的传感器(雷达),收到网络包时打上系统时间戳。
如果传感器数量更多、精度要求更高,可以考虑硬件同步,用触发线把相机曝光时刻和雷达扫描时刻对齐。但对绝大多数人来说,软件同步已经够了。
一个实测经验:如果雷达是10Hz、相机是30Hz、IMU是200Hz,只要时间戳都是系统时钟,LVI-SAM内部会自己处理不同频率之间的对齐和插值,不需要你做额外的频率匹配。
5.3 修改params.yaml适配实机
实机跑之前,需要把params.yaml中的话题名改成你自己的话题名:
sensor: lidar: "/rslidar_points" # 你雷达驱动发布的话题 imu: "/imu/data" # 你IMU驱动发布的话题 camera: "/camera/color/image_raw" # 你相机驱动发布的话题同时把前面标定得到的相机内参、IMU噪声参数、外参矩阵全部填进去。这部分工作没有什么捷径,只能一项项改、一项项试。
5.4 实机启动与调试流程
实机启动建议按以下顺序来:
第一步,启动雷达驱动,确认点云话题有数据、时间戳正常。
第二步,启动相机驱动,确认图像话题有数据、画面清晰不模糊。
第三步,启动IMU驱动,用rostopic echo查看IMU消息内容,静止时检查加速度计是否约等于重力加速度,转动时检查陀螺仪是否有响应。
第四步,把所有传感器数据录制到rosbag里,用rosbag record先录一组数据,离线回放调试。这一步非常关键,能让你在室内安全地调参,不用一遍遍推着车在走廊里跑。
第五步,离线跑通后,再上线实时跑。
我实际测试时,最常遇到的问题是传感器之间的时间戳存在固定的偏移。表现是:离线跑bag效果还行,但一上线就跑飞或者地图扭曲。排查方法是用rosbag play --clock回放bag,然后查看LVI-SAM的Rviz输出。如果离线效果和在线效果差异很大,大概率是时间同步问题。用rostopic hz分别查看三个话题的频率,再对比话题的时间戳分布,基本能定位问题出在哪个传感器上。
5.5 实机调试中的参数调整经验
实机跑通后,哪些参数值得微调?
- IMU噪声参数:标定出来的值先填进去,如果初始化慢或者初始化失败,可以把噪声密度调大一些,让系统更快收敛。
- 回环检测开关:
params.yaml里的loopClosureEnableFlag默认是true。在小场景实机测试时,如果回环检测误匹配导致轨迹跳变,可以先关掉回环检测,观察前端里程计的原始精度。 - 地图分辨率:默认的0.5米对室内建图有点粗糙,可以调到0.2-0.3米,地图细腻很多,但CPU占用会增加。在Jetson这类嵌入式平台上,还是保持0.5比较稳妥。
6. 常见问题与排查实录
LVI-SAM编译和运行过程中的坑,很多是共性问题。在这里把我遇到过和朋友们遇到过的问题汇总一下:
6.1 编译阶段
报错:找不到gtsam/gtsam.h或metis.h。GTSAM安装路径不在系统默认搜索路径,修改CMakeLists.txt手动添加include路径。
报错:libmetis.so: cannot open shared object file。GTSAM依赖的metis库没有安装到系统库路径。解决办法:
sudo apt install libmetis-dev sudo ln -s /usr/lib/x86_64-linux-gnu/libmetis.so /usr/local/lib/libmetis.so报错:OpenCV的CV_LOAD_IMAGE_GRAYSCALE未定义。OpenCV 4.0之后,CV_LOAD_IMAGE_GRAYSCALE改名为cv::IMREAD_GRAYSCALE。LVI-SAM代码中用的旧宏在新版OpenCV下会报错。解决办法是把cv::imread(..., CV_LOAD_IMAGE_GRAYSCALE)改成cv::imread(..., cv::IMREAD_GRAYSCALE),文件里一般只有一两处,全局搜索替换即可。
6.2 运行阶段
现象:系统启动后一直卡在初始化,Rviz没有点云。大概率是IMU数据有问题。先看rostopic echo /imu/data确认数据有内容、时间戳递增、加速度计数值量级正确。再确认雷达和相机话题也都正常。如果三个话题都正常,检查params.yaml中的话题名是否与实测话题名完全一致。
现象:点云地图严重重影或轨迹跳变。先检查外参标定是否准确,尤其是旋转外参。一个快速验证外参的方法:让小车低速直线行驶一段距离,观察建图结果中墙体是否笔直。如果墙体扭曲,说明外参与真实值偏差过大,需要重新标定。
现象:回环检测导致地图突然跳动。场景太小或者特征重复度高,回环检测产生误匹配。处理办法是先关闭回环检测,看前端里程计本身的漂移幅度。如果前端漂移不大,说明回环检测模块的参数需要调整,比如提高回环候选的分数阈值。
6.3 IMU适配相关的坑
踩到的坑:IMU驱动里把加速度计数据当作m/s2发布,但实际发的是g值。表现是系统初始化正常,但建图时地面总是倾斜的。排查方法:静止时用rostopic echo看linear_acceleration的z轴,如果是1.0左右说明发的是g值,应该约为9.8。
踩到的坑:6轴IMU的yaw漂移导致建图轨迹缓慢旋转。这不是代码bug,而是6轴IMU的物理特性。短时间运行可用,长时间运行需要考虑引入额外的偏航观测,比如用视觉的绝对方向估计来校正IMU偏航。
投产之前,把IMU固定在平台上静止放几分钟,用Rviz观察系统的静止精度。如果静止时轨迹仍然缓慢漂移,说明IMU零偏没有完全估计出来,可以检查IMU数据是否有异常噪点,或者增大IMU噪声参数来让滤波器对IMU信息降权。
写在最后的一点个人体会
LVI-SAM这套系统真正跑通之后,回头看会发现最耗时间的反而不是算法理解,而是环境配置、依赖编译、数据格式这些工程问题。装环境装到崩溃时确实会怀疑人生,但正是这些折腾,让我对ROS的消息机制、坐标系变换、传感器标定有了更系统的认识。6轴IMU适配这件事,核心也就一句话:把IMU数据发布正确,把内外参标定准确,系统会自己照顾好剩下的事情。建议后面入坑的朋友们,先跑通数据集,再上实机,实机先录bag离线调参,最后再实时跑。这个顺序能省掉至少一半的无效调试时间。