1. 项目概述与核心思路拆解
1.1 为什么偏偏选FAST_LIO做室内建图
先交代一下背景。我自己是做移动机器人感知方向的,室内场景的建图需求一直很刚。手头有一套16线激光雷达,刚开始用的是传统LOAM那一套,跑室外还行,一进室内就出问题,点云畸变、退化场景漂移、CPU占用居高不下,调参调到怀疑人生。
后来把目光放到FAST_LIO上,去年花了大概一周时间,从零开始配置环境、编译源码、折腾数据集,最后在室内走廊和实验室场景跑出了比较干净的建图结果。这篇文章就把完整的流程写出来,包括我踩过的坑和最后沉淀下来的一套可复现操作方案,给准备入门或者正在折腾FAST_LIO的朋友一个参考。
FAST_LIO全称是Fast LiDAR-Inertial Odometry,核心思路就是紧耦合的激光-惯性里程计。它把IMU的预积分结果和激光雷达的点云配准放在一个迭代卡尔曼滤波器框架里做,前端用的是IKFOM(迭代误差状态卡尔曼滤波器),也就是说IMU负责高频的状态预测,激光点云负责修正累积误差。相比LOAM那套松耦合方案,FAST_LIO在退化场景和快速运动场景下的鲁棒性明显更强,而且计算开销小,在Jetson Xavier这种嵌入式板子上也能跑得动。
室内场景恰恰是FAST_LIO发挥优势的地方。室内走廊、小房间这种环境,几何结构单一,纯LiDAR的配准很容易退化,但加了IMU约束之后,位置估计基本不会飘。还有一个实际收益——FAST_LIO不依赖GPS,室内完全可用。
1.2 室内建图的需求拆解与整体技术选型
在动手之前,我先把整个流程拆成了四个环节:环境配置、源码编译、数据集准备、建图运行与调优。每个环节都有各自的坑,四个环节串起来才算完整跑通。
在整体选型上,我的配置如下:
| 组件 | 选择 | 备注 |
|---|---|---|
| 操作系统 | Ubuntu 18.04 / 20.04均可 | 实测20.04更省心 |
| ROS版本 | Melodic / Noetic | 与Ubuntu版本对应 |
| 激光雷达驱动 | 自带rosbag数据集 | 室内场景无需实时传感器 |
| IMU数据 | 数据集内置 | 一般100Hz~200Hz |
| 编译工具链 | catkin_make 或 catkin build | 建议catkin_make,问题少 |
| 核心依赖 | PCL 1.8+、Eigen 3.3+、livox_ros_driver | 按需安装 |
这里多说一句选型逻辑。FAST_LIO官方仓库对livox雷达支持最完善,但代码本身依赖的是通用LiDAR-Inertial框架,所以只要是16线、32线这类机械雷达,配一个能发布标准/livox/lidar或者/points_raw话题的驱动,都可以套进来。我实际用的是其他品牌16线雷达的数据集,在源码里做了少量适配后就能正常跑。
2. 环境配置与依赖安装全记录
2.1 Ubuntu与ROS版本选择
先解决最基础的系统环境。FAST_LIO官方建议Ubuntu 18.04 + ROS Melodic,但我实测在Ubuntu 20.04 + ROS Noetic上编译运行也完全没问题。如果你手头有现成的20.04环境,不用专门为了这个项目重装系统。
不过有个细节要注意:Ubuntu 22.04 + ROS 2的兼容性相对麻烦一些,FAST_LIO主要面向ROS 1开发,如果你没有特殊理由,建议老老实实用20.04 + Noetic,网上资料最全,遇到问题也好搜。
ROS安装本身不复杂,国内建议走清华或中科大的镜像源,速度会快很多。装完记得初始化:
sudo apt install ros-noetic-desktop-full sudo rosdep init rosdep updaterosdep如果卡住,手动在/etc/ros/rosdep/sources.list.d/下建一个20-default.list文件,指向raw.githubusercontent.com的源即可。
2.2 核心依赖库的安装与版本坑
FAST_LIO对依赖库的要求其实不高,PCL、Eigen基本系统自带或者一行命令就能装:
sudo apt install libpcl-dev libeigen3-dev但有几个坑我在这里提前说清楚:
第一,Eigen必须是3.3.x以上。Ubuntu 18.04自带的Eigen 3.3.4没问题,Ubuntu 20.04自带3.3.7也行。如果你的系统版本偏老,编译时会报Eigen/Dense: No such file or directory之类错误,这时候别急着怀疑路径,先查版本:
pkg-config --modversion eigen3第二,PCL的版本兼容性。FAST_LIO源码里用了PCL的点云类型转换和KD-tree模块,PCL 1.8到1.12我都测过,都能过。唯一需要注意的是,如果你在编译时遇到no matching function for call to ‘pcl::PointCloud<...>::resize’这类报错,十有八九是PCL版本和代码预期不一致,优先考虑更新PCL版本,而不是改源码。
第三,livox_ros_driver这个依赖有点特殊。FAST_LIO的CMakeLists.txt里会直接找livox_ros_driver的包,如果你手里是机械雷达,理论上不需要装,但编译时会有find_package(livox_ros_driver)步骤,所以还是建议把驱动也拉到工作空间里一起编译,省得报错。
2.3 工作空间创建与编译环境梳理
等依赖全部就位,创建工作空间:
mkdir -p ~/fastlio_ws/src cd ~/fastlio_ws/src catkin_init_workspace cd ~/fastlio_ws catkin_make这一步目的就是先让工作空间“空转”一遍,确认catkin环境本身没问题。很多新手一上来就直接拉源码编译,结果编译报错了分不清是依赖问题还是源码问题,排错效率很低。我习惯先跑一个空编译,再往src里放代码,这样后期定位问题快。
环境变量也要在~/.bashrc里加一行:
source ~/fastlio_ws/devel/setup.bash这样每次开终端都不用手动source。
3. FAST_LIO源码编译实战
3.1 源码拉取与工程结构分析
源码直接用git拉取:
cd ~/fastlio_ws/src git clone https://github.com/hku-mars/FAST_LIO.git拉下来之后先别急着编译,我建议花十分钟把工程结构过一遍。FAST_LIO的代码量不算大,核心目录就几个:
include/:头文件,封装了IKFOM、点云处理等关键数据结构src/:主程序入口和核心算法实现config/:不同传感器配置的yaml参数文件launch/:启动文件,结合rosbag回放
config文件夹里默认提供了livox_avia等雷达的示例配置。如果你是机械雷达,需要新建一个自己的配置文件,核心差异在点云话题名和IMU内外参标定结果上。
这里建议先用官方自带的示例配置跑通编译,再去改参数,不要在第一步就引入太多变量。
3.2 编译过程与CMakeLists排错
源码放好后,在src/FAST_LIO目录下需要先处理一下livox驱动的问题。因为我的工作空间里没有livox_ros_driver,直接编译会报:
Could not find a package configuration file provided by "livox_ros_driver"解决办法有两个。一是把livox_ros_driver也克隆进来一起编译:
cd ~/fastlio_ws/src git clone https://github.com/Livox-SDK/livox_ros_driver.git cd ~/fastlio_ws catkin_make二是修改FAST_LIO的CMakeLists.txt,注释掉livox相关的依赖。但我不建议这么做,因为后续如果换用Livox雷达,还得改回来,不如一次到位。
正常编译的话,在~/fastlio_ws下执行:
catkin_make过程大概两三分钟,终端会刷很多编译信息。编译成功的标志是在devel/lib/下能看到fastlio_mapping这个可执行文件。
如果编译过程中出现内存不足的问题,可以在catkin_make前加一句:
export ROS_PARALLEL_JOBS='-j2'限制编译进程数,避免物理内存被打满导致卡死或假死。
3.3 编译之后的快速自检方法
编译过了不代表一定能跑通,我习惯在run之前先做两个快速检查。
第一,检查动态链接库:
ldd devel/lib/fastlio_mapping | grep "not found"如果输出为空,说明依赖库都正常。有not found的话,通常是PCL或Boost版本问题,优先用apt install libpcl-dev补装。
第二,直接无参数运行可执行文件,看是否报错:
./devel/lib/fastlio_mapping此时应该会提示Usage: fastlio_mapping path_to_config_file之类的参数缺失信息。能走到这一步,说明程序本体没有问题,可以进入数据集环节了。
4. 数据集准备与格式适配
4.1 数据集来源与下载渠道
FAST_LIO的室内建图,最好找一段同时包含激光点云和IMU数据的rosbag。这里分享几个我实测可用的渠道:
- FAST_LIO官方仓库的
doc目录下提供了演示数据的下载链接,里面有室内走廊场景,最适合第一次跑通全流程。 - 开源数据集平台(如OpenLORIS、KAIST数据集)有部分室内LiDAR+IMU sequence,格式多为
rosbag或hdf5,需要自行转换。 - 网站上也有不少搬运的rosbag,注意看数据的时间戳是否连续、话题是否齐全。
数据集下载到位之后,先解压,确认后缀是.bag或者.bag.active。如果遇到.bag.active,说明bag在录制过程中没有正常关闭,要用下面命令修复:
rosbag reindex xxx.bag.active rosbag fix xxx.bag.active fixed.bag这个坑我踩过一次,下载了一天的数据集结果一播放就崩溃,最后发现是录制异常导致。
4.2 话题名称与时间戳对齐检查
拿到bag后,第一件事不是急着跑算法,而是先看话题和频率:
rosbag info your_data.bag重点确认三件事:
- 激光雷达点云话题名是什么,
/livox/lidar还是/velodyne_points还是自定义名称 - IMU话题名和频率(一般100Hz~200Hz,低于50Hz会影响效果)
- 时间戳是否连续、是否有明显跳变
确认之后,再去改FAST_LIO的配置文件。这里给的示例配置中,默认读取的话题是/livox/lidar和/imu/data,如果你的bag话题名不一样,直接改yaml:
common: lid_topic: "/your_lidar_topic" imu_topic: "/your_imu_topic"注意IMU话题通常不止一个,要选频率高且噪声小的那个。部分数据集同时发布/imu/data和/imu/data_raw,后者通常是未经滤波的原始数据,实测下来FAST_LIO对这两者都能处理,但用data_raw往往效果更好,因为滤波会引入滞后。
4.3 机械雷达的点云适配技巧
如果你用的是机械雷达数据集(比如Velodyne VLP-16),还需要处理点云类型问题。FAST_LIO原生适配livox雷达的livox_ros_driver/CustomMsg格式,对标准sensor_msgs/PointCloud2反而需要做额外适配。
这里有两条路:
路线一:写一个rePublisher节点,把PointCloud2转成livox的CustomMsg格式。工作量不小,但能最大程度复用原生代码。
路线二:改FAST_LIO源码,在laserMapping.cpp的cloud_handler回调里同时兼容两种消息类型。简单说就是在回调函数里加一个判断,如果消息类型是PointCloud2,直接做一次fromROSMsg转换后再走后续流程。
我自己用的是路线二,改动量大约二三十行代码,核心逻辑就是给StandardCloudHandler增加一个PointCloud2回调分支,内部复用现有的process逻辑。这样机械雷达数据就能无缝塞进FAST_LIO。
如果你对C++不熟,就先用官方数据集跑通,后期再折腾适配。
4.4 数据集播放的频率控制
数据集播放这个细节很少有人专门讲,但实际影响挺大。
正常rosbag播放是均匀速度:
rosbag play your_data.bag但如果你电脑性能一般,建议用-r 0.5减慢播放速度,给算法留足计算时间:
rosbag play -r 0.5 your_data.bag否则会出现“算法处理速度跟不上数据进入速度”的情况,建图轨迹会明显漂移。另外,建议加--clock参数,把bag的时间作为ROS系统时间发布,保证TF和时间戳一致:
rosbag play --clock your_data.bag5. 室内建图运行与参数调优实战
5.1 启动FAST_LIO的完整流程
一切就绪后,开三个终端分别运行:
终端一:启动核心建图节点
source ~/fastlio_ws/devel/setup.bash roslaunch fastlio_mapping mapping.launch终端二:启动Rviz可视化
rosrun rviz rviz -d src/FAST_LIO/rviz/fastlio_mapping.rviz终端三:回放数据
rosbag play --clock your_data.bag这里的启动顺序是固定的。先启动建图节点,让它处于“等待话题”状态;再启动Rviz,避免画面加载不完整;最后才播放bag。如果先播bag,算法可能会丢失开头的部分点云,造成初始位置估计不准。
Rviz里如果看到点云和机器人模型在移动,地图在逐步叠加,说明已经跑起来了。
5.2 室内建图的核心参数解读
跑通第一遍之后,就该看参数了。FAST_LIO的yaml配置文件里有几个参数对室内建图效果影响极大:
| 参数名 | 作用 | 室内场景建议 |
|---|---|---|
filter_size_surf | 面特征体素滤波尺寸 | 0.2~0.4,越小细节越丰富但越慢 |
filter_size_map | 全局地图体素滤波尺寸 | 0.5左右,太小会导致地图臃肿 |
imu_enable | IMU融合开关 | true,关闭后几乎必飘 |
extrinsic_* | IMU到LiDAR的外参 | 必须准确标定 |
gravien | 重力大小 | 默认9.81,一般不用动 |
filter_size_surf是最值得调的参数。室内环境特征本来就少,如果体素滤波尺寸设置得太大,很多墙面细节会被抹掉,配准精度直接下降。但设得太小,点云数量暴增,实时性又跟不上。我的做法是先用0.3跑一遍,看CPU占用和地图效果,再微调。
外参extrinsic_*是另一个大坑。很多数据集发布时不会标定IMU跟LiDAR之间的外参,直接沿用默认参数可能导致建图发散。如果打开Rviz发现轨迹明显偏转,十有八九是外参不对。可以先用官方标定工具重新算一下,或者调小外参初值的影响范围。
5.3 室内退化场景下的实战调优
室内建图最常见的失败场景是长走廊。走廊方向两边都是平行墙面,激光点云在走廊方向上的约束很弱,如果没有IMU辅助,位置估计会慢慢漂移。
FAST_LIO官方对这个问题提供了一个思路:调整DEGRADE(退化检测)相关参数。在laserMapping.cpp里有一部分逻辑会检测退化方向,并通过IMU约束来兜底。实际使用中,如果发现走廊段有明显漂移,优先检查IMU频率是否够高,其次调整点云畸变补偿的强度。
另外还有一个实用技巧:把室内建图分成“子地图拼接”来做。FAST_LIO自带全局地图,但长时间在室内跑,地图会越来越大,实时性也会下降。我的习惯是一次bag只建一段地图,然后保存,再换bag继续建,后期用点云配准工具拼起来。这样既避免了长时运行的累积漂移,也更容易定位到问题段。
地图保存命令:
rosservice call /cloud_registered "{}"这个service会把当前地图保存成pcd文件,路径在launch文件里配置的save_path目录下。
5.4 试一试不同的启动文件与配置模板
FAST_LIO的launch目录下默认只有livox相关的启动文件,建议你复制一份改成自己的模板:
<launch> <param name="config_path" value="$(find fastlio_mapping)/config/your_own_config.yaml" /> <node pkg="fastlio_mapping" type="fastlio_mapping" name="fastlio_mapping" output="screen" /> </launch>这样每次跑新数据集,只需要改一个yaml文件就行,不需要反复改launch。不同传感器(16线、32线、livox)维护各自的配置模板,切换场景时秒级切换。这是我后期反复实验时最舒服的一部分。
6. 常见问题与排查技巧实录
6.1 编译阶段的报错速查表
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Could not find livox_ros_driver | 缺少livox驱动包 | 把livox_ros_driver克隆进src一起编译 |
Eigen/Dense: No such file or directory | Eigen未安装或版本过低 | sudo apt install libeigen3-dev |
fatal error: pcl_conversions/pcl_conversions.h | 缺pcl_conversions包 | sudo apt install ros-noetic-pcl-conversions |
undefined reference to ikfom::... | catkin工作空间缓存问题 | 删除build和devel目录重新编译 |
| 编译卡死或内存溢出 | 并行编译任务过多 | export ROS_PARALLEL_JOBS='-j2' |
这里特别提醒一下缓存问题。FAST_LIO的代码经过多次更新,如果你之前编译过旧版本,直接拉新代码再catkin_make,偶尔会出现“明明改了代码但编译的还是老逻辑”的情况。遇到这种莫名其妙的问题,先执行:
rm -rf build devel catkin_make强制全量重新编译,大概率能解决。
6.2 运行阶段的常见故障与处理
运行时的坑比编译时更多,我从自己的实操经验里挑了几个最典型的。
故障一:启动后点云完全不动或乱飞。
这种情况十有八九是坐标系问题。FAST_LIO默认使用camera_init作为全局坐标系,如果你的数据集TF树里没有这个坐标系,算法就会一直等待或使用默认值。在Rviz里把fixed frame改成camera_init即可,或者在你的数据驱动脚本里发一个静态TF:
rosrun tf2_ros static_transform_publisher 0 0 0 0 0 0 map camera_init故障二:地图抖得厉害,回环特别脏。
先检查IMU频率,再检查点云是否被截断。室内点云如果近距离物体太多,会在墙面和地面之间来回跳动,优先调filter_size_surf和filter_size_map,把噪声点滤掉一部分。
故障三:跑一段时间后程序自动退出。
检查内存占用。FAST_LIO维护的全局地图点云会持续增长,如果你保存了大量原始点云,内存可能爆掉。解决方案是减少地图体素尺寸的保存粒度,或者设置定期清空局部地图。
6.3 效果评估与地图后处理心得
建图结束不是终点,地图质量评估同样关键。我常用的方法是把生成的点云地图在CloudCompare里打开,看墙面是否平整、转角是否锐利、是否有重影。如果墙面“发毛”或出现双层墙,说明配准精度不够或者在拐弯处发生了滑动。
轻微重影可以裁掉噪声点云后用ICP或NDT做一次全局精配准,效果提升明显,也是我处理大场景map的兜底手段。如果重影严重,基本判定轨迹漂移了,回到参数调整环节,优先检查外参和IMU频率,这两个因素占室内建图失败原因的八成以上。
另外,室内建图成果的PCD文件默认坐标系是camera_init(也就是初始时刻的IMU坐标系),后续如果要导入其他工具做规划或语义标注,记得先做坐标转换,否则会出现模型“悬空”或“穿地”的诡异现象。
7. 项目扩展与经验总结
7.1 从室内走向室外的扩展思路
FAST_LIO这套流程真正跑通之后,换传感器、换场景的核心成本其实很低。我后来把同样的工程从室内搬到室外半封闭园区,只改了三处:IMU外参重新标定、点云滤波参数调大、关闭退化检测里的部分阈值限制。整个过程不到半天。
如果你想进一步扩展,还可以尝试FAST_LIO2。LIO2改用了ikd-tree维护全局地图,在室内的实时性更好,全局地图的体素管理也更聪明,但核心编译流程和这篇文章讲的几乎一致。
7.2 给新手的几步建议
最后整理几条新人上手最实用的建议,我自己一路摸过来,觉得这几条最值得提前知道:
第一,第一次跑通,不要一上来就追求完美效果。先用官方数据集、默认参数,把“能跑”这件事先做到,再逐步微调。
第二,学会看日志。FAST_LIO的控制台输出在debug模式下会打印每帧的处理耗时、特征点数等信息,不要忽略这些数字,它们是判断算法健康度最直接的窗口。
第三,养成“玩数据集”的意识。好的数据集能省掉你80%的调试时间,我在找数据上花了不少冤枉功夫,后来干脆自己用机器人在室内录制了几段标准化的rosbag,话题名、时间戳、外参全部标好,以后测试任何SLAM算法都用这套数据,对比效果非常方便。
第四,别怕读源码。FAST_LIO核心也就几个cpp文件,认真读一下laserMapping.cpp和ikfom.hpp,你对整个LIO系统的理解会上升一个档次,后期调参时脑子里有数得多。
我个人的体会是,FAST_LIO这套代码写得相当精炼,尤其是IKFOM那部分,把ESKF、迭代更新和点云配准揉在一起但思路依然清晰。编译和配置只是门槛,跨过之后真正有价值的是理解它每个参数选择背后的物理意义。室内建图只是第一个应用场景,当你把数据流、坐标系、外参标定这些都吃透之后,这套框架基本就是你的“SLAM通用工具箱”,想怎么玩都行。