1. 从“融合”到“直接”:FAST-LIVO2到底解决了什么问题
做机器人、无人机或自动驾驶感知的朋友,这几年应该没少被“多传感器融合里程计”这个词轰炸。激光雷达、惯性测量单元、相机三者各有所长,雷达测距准但点云稀疏、纹理弱,相机纹理丰富但易受光照影响,IMU短时可靠长时漂移,单靠任何一个都撑不起长时间、大范围的高精度定位。于是大家一窝蜂去搞LiDAR-Inertial-Visual融合,但真正落地时你会发现,大部分系统是把“特征提取”和“融合”当成了两件事先后做,中间隔着一层明显的瓶颈。
FAST-LIVO2这个项目,全称是Fast, Direct LiDAR-Inertial-Visual Odometry,我是在一次多源融合方案选型时偶然刷到的。当时一看到“Direct”这个词我就来了兴趣,因为它避开了业界最常用的特征点匹配路线,直接把原始量测拿来做配准和约束。简单说,它是一套紧耦合的激光-惯性-视觉里程计系统,能在CPU上跑到实时甚至更快,同时把三类传感器数据以“非特征化”的方式统一到一个迭代误差状态卡尔曼滤波框架里。用它跑一圈公共数据集,再对比之前用过的LIO-SAM加VINS-Mono的松耦合方案,最直观的感受就是墙角、树木边缘这类弱纹理区域的定位稳定度提升了不止一个档次。
这系统适合谁参考?我觉得有两类人最值得看:一类是正在做相关系统集成、被多传感器时间同步和外参标定折磨的工程开发人员,另一类是研究多模态融合算法、想看紧耦合框架怎么设计状态更新和残差模型的在校学生。前者可以把它当成开箱即用的定位前端,后者能顺着代码和论文拆出一整套直接法融合的设计范式。接下来的内容,我会按“设计思路—核心模块—实操复现—问题排查”这个顺序,把我复现和二次开发过程中的经验完整写出来,尽量做到能帮你在自己的设备上少折腾几个晚上。
2. 设计思路拆解:为什么直接法能在多传感器融合里胜出
2.1 传统特征点方案的三个隐痛
在讲FAST-LIVO2的设计之前,先聊聊我为什么抛弃那套“先特征、后融合”的老路线。以视觉特征为例,大家熟悉的ORB-SLAM系列和VINS-Mono,核心都是先提取ORB角点或光流跟踪特征点,再用对极几何或PnP求出相机位姿。这套体系在纹理丰富、光照稳定的室内环境表现不错,可一旦到了自车运动过快导致运动模糊,或者墙面大面积纯色、树荫下光线斑驳的场景,特征点数量断崖式下跌,前端直接“失明”。
激光这边也有同样的问题。经典的LOAM系列靠提取边缘点和平面点来完成帧间配准,存在两个隐患:一是特征提取本身有信息损耗,比如那些纹理不够“典型”但对几何约束很有价值的点会被直接丢弃;二是特征匹配错误时很难在线识别,一旦匹配错了,后端优化再强也救不回来。更麻烦的是,雷达和相机的特征点往往不是一一对应的——雷达提取的是几何边缘,相机提取的是灰度角点,你很难用同一套数据结构去刻画它们,最后只能退化成“各自算位姿、再加权平均”的伪融合。
2.2 直接法:省掉特征,保留全部信息
FAST-LIVO2走的是另一条路——直接法。激光这边,它不提取角点和平面点,而是把整帧点云直接扔进一个自适应体素地图里做配准,用点到面残差来约束位姿;视觉这边,它不做特征提取和描述子匹配,而是直接最小化像素灰度残差,把当前图像和前一帧或局部地图建立起光度一致性约束。IMU则负责在两帧之间做运动预测,给雷达和相机的优化提供一个足够好的初值。
直接法的优势,一句话就能说清:信息利用效率高。它只做“原始测量”和“预测值”之间的比较,不经过特征提取这个有损压缩环节。用图像来类比,特征法是先在图上找几十个“锚点”,然后只盯住这些锚点算运动;直接法是把整张图的灰度梯度都利用起来,哪怕纹理很弱、但只要有一点梯度变化,就能贡献约束。激光同理,传统方法只保留角点和平面点,直接法则让每一个点都参与配准,相当于把雷达的分辨率“榨干”了。
2.3 紧耦合与松耦合的本质区别
很多人对“紧耦合”理解得比较模糊,以为只要把多个传感器的数据放进同一个优化框架就算紧耦合。其实不然。松耦合是每个传感器先独立计算位姿,再做加权平均或融合。这样做问题很明显——某一个前端挂了,整体精度立刻崩;而且各传感器独立计算时的协方差估计本身就不准,加权权重自然也不可信。
紧耦合则是把原始测量直接放进同一个状态向量的更新方程里,视觉约束和激光约束共享同一个位姿状态、同一个误差状态卡尔曼滤波器。在FAST-LIVO2的框架里,IMU负责状态传播,雷达和视觉提供观测更新,三者之间不存在“先各自定位再融合”的过程,而是每一帧都在状态层面互相修正。这种设计的好处是,某个传感器退化时,其他传感器的约束能自然补偿,而不是像松耦合那样整个前端失控。
3. 核心模块拆解:自适应体素地图、IESKF和光流配准
3.1 自适应体素地图:从“多分辨率打架”到“按需生长”
FAST-LIVO2的前身FAST-LIVO里,用的是一种固定分辨率体素地图。固定分辨率的问题在于:建图范围大时,体素太细会导致内存爆炸;建图范围小时,体素太粗又会损失精度。FAST-LIVO2升级为自适应体素地图后,每个体素会维护一个平面方程参数,系统根据局部点的分布自动决定是否继续细分或合并。
举个具体例子。当雷达扫过一面平整的墙面时,落在同一个大尺寸体素里的点足够拟合出一个精确平面,那就没必要再细分;但如果扫过的是灌木丛或树叶,点云分布杂乱无章,大尺寸体素拟合出的平面残差会非常大,系统就会自动把这个体素分裂成更小的子体素,直到拟合残差满足阈值。这种“由粗到细”的机制,本质上是用计算量换精度,而且只在需要的地方花计算量,整体效率反而更高。
自适应体素地图还有一个工程上的关键设计——增量更新。新点云帧进入地图时,不需要把整个地图重新构建,而是在已有体素上做局部更新。这一条对实时性至关重要。我实测下来,在中等规模场景里(大约50米半径),体素地图的更新时间可以控制在3毫秒以内,占用总体耗时很小。
3.2 迭代误差状态卡尔曼滤波:不是普通的EKF
状态估计部分用的是IESKF,全称迭代误差状态卡尔曼滤波器。和传统的扩展卡尔曼滤波器相比,IESKF在更新步骤里引入了迭代优化:每当新的观测到来,它不是一次性算出后验,而是反复迭代,让状态更新后的残差逐步收敛到极小值。
这套机制和视觉领域的高斯-牛顿法有些类似,但本质区别在于:IESKF的迭代是在卡尔曼滤波框架内做最大后验估计,每一步都会考虑状态协方差和观测噪声的统计特性。实际操作中,IESKF对初值非常敏感,但好在IMU能提供足够准的传播状态作为初值,所以整个滤波器跑起来很稳。
滤波器的状态向量包含位置、速度、姿态、加速度计零偏和陀螺仪零偏,一共15维。每次雷达或图像观测到来时,滤波器通过测量模型计算出残差和雅可比矩阵,再对状态做一次迭代更新。这一步涉及大量的矩阵运算,代码里用到了LDLT分解等数值技巧来保证稳定性,而不是直接求逆,否则十几维矩阵求逆的数值误差会把状态估计“带偏”。
3.3 激光直接配准:点到面残差是怎么算出来的
激光配准的核心是点到面残差。当前帧的每一个点,会被变换到地图坐标系下,然后在自适应体素地图里找到它所在的体素,获取该体素拟合出的平面方程(有法向量和中心点)。残差就是当前点到这个平面的距离。
残差的计算公式很直观:假设当前点为p,平面方程为n·(x - q) = 0,其中n为单位法向量,q为平面上一点,那么残差就是n·(p - q)的绝对值。构建优化问题时,我们希望这个残差对所有点的平方和最小。
这里有个细节容易忽略:体素里拟合平面的点越多,平面的置信度越高,因此在加权时我会给这些点更高的权重。FAST-LIVO2的代码里直接用平面拟合的协方差矩阵来判断置信度,比我之前手动设权重的方式雅观得多。另外,地图会维护体素的最近访问时间,长时间没被观测到的体素会被剔除,防止地图无限膨胀。删体素的策略很激进——一旦点云视角移开,旧的体素立刻被清掉,只保留窗口范围内的局部地图。
3.4 视觉直接配准:用稀疏光流替代特征匹配
视觉这边的设计非常聪明。FAST-LIVO2不是对整幅图像做稠密光度误差优化(那样CPU扛不住),而是先用图像梯度提取一批“次像素级”的感兴趣点,再用稀疏光流法对这些点进行帧间跟踪,最后用这些点的光度残差构造视觉观测模型。
这地方要理解清楚:它虽然做了点提取,但提取的不是角点、边缘等“语义特征”,而只是“灰度梯度足够明显的像素”,不做描述子,不做匹配。跟踪用的是经典的Lucas-Kanade光流,通过最小化灰度差的平方和来计算像素运动。这样一来,既能利用图像的梯度信息,又绕开了特征描述子在大视角变化下容易失效的坑。
视觉观测的残差定义是当前帧预测的像素灰度与实际观测灰度之差,雅可比矩阵通过链式法则从像素坐标一直传到相机位姿。由于相机与IMU/雷达之间存在外参,视觉残差也能直接约束整个状态向量。这里我只强调一点:相机内参和外参的标定质量,直接决定视觉约束能不能帮上忙,而不是帮倒忙。
3.5 传感器之间的“质检员”:一致性检查和退化检测
多传感器融合最容易翻车的地方,是某个传感器给出的约束本身是错的,但滤波器不知道,照样拿它更新状态。FAST-LIVO2引入了一套几何一致性检查机制,在每次观测更新前先做一个“质检”。
激光方面,它用点到面残差的分布来判断当前帧配准是否收敛;如果残差中位数明显偏大,说明配准质量差,就降低这一帧激光的权重。视觉方面,光流跟踪时如果点的前后向误差过大,会直接剔除。更关键的是退化检测:当系统处于走廊、隧道这类几何退化环境时,激光在某些维度上几乎没有约束能力。代码里通过比较预测分布与后验分布的协方差矩阵行列式之比来量化退化程度,一旦检测到激光退化,滤波器会自动降低激光权重,把主导权交给视觉和IMU。这个机制在真实隧道场景里非常有效,我测试时故意把车开进地下车库,系统依然能保持稳定,视觉+IMU平滑衔接上了。
4. 环境配置与复现实操:从源码到跑通自己的数据集
4.1 硬件与系统要求:先别急着编代码
复现任何SLAM系统,第一件事不是拉代码,而是确认硬件和软件环境。FAST-LIVO2官方支持ROS1和ROS2两个版本,我的测试机配置是Intel i7-12700K、32GB内存、一块中端NVIDIA显卡(其实不需要GPU加速,主要靠CPU),系统是Ubuntu 20.04 + ROS Noetic。
有一点要提前说明:代码仓库里强调了CPU需要支持AVX2指令集,否则部分矩阵运算库无法编译。如果你的CPU比较老,建议先用lscpu命令确认一下有没有avx2标志。我见过有人在一台老至强服务器上编译报错,折腾了一晚上才发现是CPU指令集不支持。另外,OpenCV版本建议3.4以上,PCL版本1.10以上,Eigen用3.3.x或更高版本。编译前把这些依赖装齐,能省掉后面90%的报错。
4.2 一步步编译:踩着坑走完流程
编译过程本身不复杂,但有几个细节容易卡住。第一步是创建ROS工作空间并克隆代码仓库,这里关键是要记得把子模块一起拉下来。项目依赖的几个子模块如果没有同步拉取,编译时会报“找不到头文件”的错误。
第二步是安装依赖。Ubuntu 20.04下可以直接用apt安装大部分依赖,但Livox的驱动需要单独处理,因为不同版本的ROS环境下它的编译方式有点区别。我建议在编译前先把livox_ros_driver编译一遍,确认能正常出节点,再回头编译主项目,这样能把环境问题和技术问题隔离,排查起来快得多。
编译命令就是常规的catkin_make或catkin build,但强烈推荐在编译前设置好并行编译参数,比如catkin_make -j4,避免内存不足导致的编译失败。我实测在32GB内存下并行8个任务没问题,但如果你机器内存只有16GB,建议老老实实用-j4。
4.3 数据集准备与格式转换:公共数据集也能直接跑
FAST-LIVO2官方训练和测试数据主要来自自己采集的固态激光雷达数据,但其代码兼容标准ROS bag,所以很多公共数据集也能直接跑。我第一次用它跑的是M2DGR数据集,这个数据集包含多种传感器,提供ROS bag格式,不需要自己转换,省了很多事。
跑之前需要改一改launch文件,把话题名称和传感器参数改成对应数据集的配置。比如M2DGR的激光雷达话题是/lidar_points,IMU话题是/imu/data,相机话题是/camera/color/image_raw,每一类话题都要在配置里对应好。另一个容易忽略的是外参文件,不同的数据集里雷达和相机之间的外参都不同,需要从数据集说明文档里提取出来填到配置文件里,否则系统跑起来位姿直接发散。
启动顺序也有讲究:先启动livox驱动和imu驱动,再启动主程序。等出现“map initialized”之类的日志后再播放bag。播放bag时建议用--rate=1.0或者更低倍速,让滤波器和地图有充足的时间收敛,尤其是首次建图阶段,速度过快容易导致点云畸变。
4.4 评价指标:量化对比才有说服力
复现完系统后,最好用EVO之类的工具量化评估轨迹精度。把算法输出的轨迹保存为TUM格式,再用evo_ape和evo_rpe分别计算绝对位姿误差和相对位姿误差。我跑M2DGR部分序列时得到的ATE RMSE大概在0.3到0.5米之间,这个精度在中等规模室外环境下已经相当不错。
同一个序列,我当时也跑过LIO-SAM和VINS-Fusion做对比。LIO-SAM在有明显几何特征的区域精度和FAST-LIVO2接近,但在植被密集、几何特征退化的区段,FAST-LIVO2明显更稳;VINS-Fusion在快速旋转时容易漂移,FAST-LIVO2则因为有激光约束,姿态估计更平滑。需要强调的是,这种对比必须保证外参标定精度一致,否则结论参考价值不大。
5. 常见问题与排查技巧实录
5.1 编译阶段高频报错
编译时报错最多的就是找不到OpenCV或PCL的头文件。这类问题的根源往往是多个版本的库共存时CMake找错了路径。排查方法很简单,手动指定库路径,或者在CMakeLists里直接打印find_package的搜索路径。另一个高频问题是Eigen版本过旧导致数学函数缺失,升级Eigen到3.4基本能解决。
如果你用的是ROS2版本,还可能遇到依赖包版本不匹配的问题,比如某个消息类型的定义在两版ROS中改动过。这种时候我建议先放弃ROS2,用ROS1版本跑通整个流程,毕竟核心算法两端完全一致,ROS2版本主要是为生产环境准备的。
5.2 运行时的诡异漂移:先查时间戳还是先查外参
系统跑起来但轨迹漂移严重,是最让人头疼的问题。我总结了一个排查顺序:第一步看时间同步,第二步看外参,第三步看点云畸变。时间同步问题的典型表现是平移轨迹出现周期性的锯齿状跳动,因为IMU和激光雷达的时间戳没有对齐。解决方法是检查bag里的时间戳频率,确保IMU消息的时间增量均匀,如果有明显跳变,说明原始数据时间戳本身有问题。
外参错误的表现则更隐蔽:系统在静止时看起来没问题,但一开始运动就漂移,而且漂移方向和真实运动方向不规律。这种时候可以用系统自带的初始化工具或者手动标定的方式重新标定外参,不要盲目调滤波器噪声参数,那是治标不治本。
5.3 内存暴涨与体素地图失控
自适应体素地图虽然能控制体素数量,但如果不设置合理的雷达视场角范围,地图依然可能快速膨胀。我在一次户外长距离测试时,地图体素数量在10分钟内涨到了几百万个,内存占用飙到20GB以上。
排查后发现是我把点云预处理里的距离阈值设得过大,把远处大量稀疏点云也纳入地图。FAST-LIVO2的代码里有体素最近访问时间剔除机制,但剔除频率默认偏低。我的建议是,在launch文件里把剔除时间间隔调短一点,比如从默认的5秒改为2秒,同时把最大建图范围控制在80米以内,这样既能覆盖大部分机器人应用场景,又不会让地图无限膨胀。
5.4 快速旋转时的视觉跟踪丢失
视觉光流在快速旋转时容易出现跟踪点大面积丢失,导致视觉约束失效。这个问题在纯视觉系统里几乎无法避免,但在FAST-LIVO2的紧耦合框架下并不可怕,因为还有激光和IMU兜底。但我发现一个规律:如果相机的曝光时间设置过长,快速运动时会产生严重的运动模糊,光流跟踪基本全废。解决办法是把相机的曝光时间调短,或者启用全局快门相机。如果你用的相机支持自动曝光,建议在快速运动测试前先固定曝光参数,避免自动曝光引发的亮度突变干扰光度残差计算。
6. 实测表现与优化心得
6.1 一次户外混合场景的完整测试
为了验证FAST-LIVO2的真实水平,我在一个包含树林、建筑墙面、草地和斜坡的混合场景里做了完整测试。测试设备是手持平台,包含一台16线机械式激光雷达、一个全局快门相机和一个工业级IMU,整个平台总重不到2公斤。
测试开始后,我先沿着树林边缘走了大约200米,这段路的特征是树干密集、地面起伏,对纯激光系统来说很容易因为平面特征不足而丢失。FAST-LIVO2的表现让我印象很深——它在树干稀疏的地方靠视觉纹理约束,在树干密集的地方靠激光几何约束,两者切换非常平滑,轨迹没有出现明显的抖动。到了建筑墙面区域,由于墙面是纯色大平面,视觉约束几乎失效,但激光的平面拟合非常稳定,位置漂移控制得很好。整个测试下来,闭环后的轨迹误差在0.8米以内,对于没有回环检测的开环里程计来说,这个数字已经相当能打了。
6.2 参数调优:几个影响最大的旋钮
参数调优是复现过程中最有意思的环节。FAST-LIVO2的配置文件里有大量参数,但真正值得反复调整的不多,我总结出三个最重要的:体素分辨率、光流金字塔层数和滤波器迭代次数。
体素分辨率决定了雷达配准的精度和地图的细度,分辨率太高会导致计算量激增,太低则精度下降。我的经验值是0.5米到1.0米之间,室外大场景用0.8米,室内精细场景用0.5米。光流金字塔层数影响视觉跟踪对快速运动的鲁棒性,层数越多越能处理大位移,但计算量也越大,我的实测值是三层足够。滤波器迭代次数默认是3次,我试过调到5次,精度提升不到2%,但时间开销多了将近一倍,所以保持默认就好。
6.3 后续可以怎么扩展
FAST-LIVO2本身不做回环检测和全局优化,所以跑大场景时会积累漂移。我自己的做法是把它当成一个高质量前端,后端接一个因子图优化框架,在检测到回环时做全局位姿图调整。这种“前紧后松”的架构在实际工程里非常常见——前端保证短时精度,后端保证长时一致性。
另外,代码里的状态估计模块相对独立,如果需要在特定场景下增加新的观测约束,比如加入轮速计或气压计,可以从IESKF的观测模型部分下手扩展,替换残差计算函数即可,整体框架不需要大改。如果你对多传感器融合感兴趣,完全可以把它当成一本“活教材”:状态向量怎么设计、观测残差怎么写雅可比、协方差怎么传播,都有非常干净的参考实现。
7. 避坑总结与个人体会
复现这个项目的整个过程,让我对多传感器融合有了更深的理解。直接法不是银弹,它对传感器标定质量、时间同步精度和计算资源都有更高要求,但一旦这些前置条件满足,它带来的精度提升和鲁棒性提升是非常可观的。
我最后想给三点建议。第一,不要跳过外参标定,拿默认外参跑自己的设备,大概率是要翻车的,找一本好的标定工具书或开源工具,花半天时间认真标一次,后面会省下无数个调试的夜晚。第二,先用公共数据集跑通流程,再上自己的设备,公共数据集能帮你排除硬件问题,快速验证代码环境是否正常。第三,跑起来之后一定要做量化的轨迹评估,不要只看可视化界面里的轨迹形状,肉眼觉得“差不多”和实际误差之间差着一个数量级。
如果你想在自己的机器人平台上部署一套稳定、实时、精度高的激光-惯性-视觉融合里程计,FAST-LIVO2是目前开源方案里绕不开的一个参考。我自己的下一步计划,是把它和回环检测模块接入到无人车的导航系统里做长期测试,顺便在弱纹理室内场景下对比一下它和另外几套视觉惯性方案的表现差异。到时候如果有新发现,再来和大家分享。