FAST-LIO 在宇树 G1 上部署可以分为两种方法:
-
源码部署
-
docker 部署
由于 FAST-LIO 使用环境为 ubuntu 20.04 + ROS1,而 G1 通用环境为 ubuntu 22.04 + ROS2,所以使用第二种方法可以直接上手,但是考虑到后续源码升级,选择第一种思路的升级版本
第一种思路又可以采用两种方法:
-
FastLIO-ROS2 + Nav2:参考 FASTLIO2_ROS2 升级 FastLIO 为 FastLIO-ROS2,然后 ROS2 + Nav2 基本思路如下:
-
用 FastLIO(ROS2) 做 LIO 里程计 + 累积点云
-
用 octomap_server 把 3D 点云转成 2D 投影占据栅格 /projected_map
-
用 map_saver_cli 把 2D 栅格保存为 mymap.yaml/.pgm
-
用 Nav2 加载 mymap.yaml 导航
整套思路已经完成部署测试,记录在了宇树 G1 部署(六)笔记中
- 第二种是这几天正好看到的,高翔老师新出的 CPU 友好型 3D 激光建图与定位模块:Lightning-LM(Lightning-Speed Lidar Localization and Mapping)
🐾 Github:https://github.com/gaoxiang12/lightning-lm
这个项目可以当成"一体化 LIO-SLAM + 2D 栅格 + 闭环 + 轻量后端"的工程
由于: Lightning-LM is a complete laser mapping and localization module,其前端(LIO):以 AA-FasterLIO 为核心(Faster-LIO 系列的加速版),完成去畸变、特征/体素匹配、紧耦合 IMU 预积分等,实时输出里程计
因此:此笔记在 Lightning-LM 框架中,分析 FasterLIO,方便后续替换
如果后续想继续轻量化自己搭建架构,则继续 1. FastLIO-ROS2 + Nav2 中的研发
首先强调一下,此笔记全是理论逻辑,初看可能一头雾水,但是部署完后再回头看一遍,对于升级优化发 paper 确实很有用
Faster-LIO 是在 FAST‑LIO2、FAST‑LIO 基础上进一步加速、轻量化的 LiDAR-IMU 里程计(LIO)系统
🐾 Github:https://github.com/gaoxiang12/faster-lio
📄 Paper:
https://github.com/gaoxiang12/faster-lio/blob/main/doc/faster-lio.pdf
https://ieeexplore.ieee.org/document/9718203
目录
[1 Lightning-LM 框架](#1 Lightning-LM 框架)
[2 Lightning-LM 架构拆解](#2 Lightning-LM 架构拆解)
[2.1 文件架构拆解](#2.1 文件架构拆解)
[2.1.1 应用层:src/app](#2.1.1 应用层:src/app)
[2.1.2 系统调度层:src/system](#2.1.2 系统调度层:src/system)
[***2.1.3 核心算法层:src/core](#***2.1.3 核心算法层:src/core)
[2.1.4 数据与公共类型:src/common](#2.1.4 数据与公共类型:src/common)
[2.1.5 IO / 适配层](#2.1.5 IO / 适配层)
[2.1.6 可视化 UI:src/ui](#2.1.6 可视化 UI:src/ui)
[2.1.7 杂项工具:src/utils](#2.1.7 杂项工具:src/utils)
[2.2 核心数据流和模块协作](#2.2 核心数据流和模块协作)
[2.2.1 LIO 前端(core/lio)](#2.2.1 LIO 前端(core/lio))
[2.2.2 建图后端:地图 + 回环 + 图优化](#2.2.2 建图后端:地图 + 回环 + 图优化)
[2.2.3 定位模块(core/localization)](#2.2.3 定位模块(core/localization))
[2.2.4 3D → 2D 栅格地图(core/g2p5)](#2.2.4 3D → 2D 栅格地图(core/g2p5))
[2.3 运行模式 & 系统层逻辑](#2.3 运行模式 & 系统层逻辑)
[2.3.1 在线 / 离线程序入口(src/app)](#2.3.1 在线 / 离线程序入口(src/app))
[2.3.2 system::Slam 和 system::LocSystem](#2.3.2 system::Slam 和 system::LocSystem)
[***2.4 配置体系 YAML → Options / Params](#***2.4 配置体系 YAML → Options / Params)
[2.4 数据流总结](#2.4 数据流总结)
[2.4.1 离线建图(run_slam_offline)](#2.4.1 离线建图(run_slam_offline))
[2.4.2 在线纯定位(run_loc_online)](#2.4.2 在线纯定位(run_loc_online))
[3 单独模块升级策略](#3 单独模块升级策略)
[3.1 Lightning-LM 的模块化程度(结构层级)](#3.1 Lightning-LM 的模块化程度(结构层级))
[3.2 System --- "胶水层"](#3.2 System — “胶水层”)
[3.3 模块升级方向(挖坑向)](#3.3 模块升级方向(挖坑向))
[3 Lightning-LM 效果展示](#3 Lightning-LM 效果展示)
1 Lightning-LM 框架
Lightning-LM 是一个完整的激光建图+定位模块
Lightning-LM特性:
-
done\] 完整的3D Lidar SLAM,快速的LIO前端(AA-FasterLIO),标配
-
done\] 实时回环检测,标配,选上的话会进行后端回环检测并闭环
-
done\] 地图分区动态加载方案,适用大场景
-
done\] 高频率IMU平滑输出,标配,100Hz
- 车辆里程计输入,选配 (TODO)

更多 -
done\] 轻量优化库miao以及增量式优化(来自g2o,但更轻更快,支持增量优化,不需要重新构建优化模型),标配,在回环、定位中均有用到
-
done\] 基于外推器和平滑器的高频率输出,平滑因子可调
2 Lightning-LM 架构拆解
Lightning-LM 本质上是一套完整的 3D 激光 SLAM + 定位系统,围绕一个 LIO 前端(AA-FasterLIO)和一个轻量图优化库(miao)搭出整套建图/定位流水线,目标是:
-
一个核心算法库,同时支撑:
-
在线 / 离线建图(SLAM)
-
在线 / 离线纯定位
-
-
多线程 + 增量优化 + 分块地图,保证在纯 CPU 上也能 0.8~1.2 个核跑起来
-
ROS2 + Pangolin UI 封装,方便工程集成与可视化
从目录树可以看出,它是一个层次性很强的结构:应用入口 → 系统调度层 → 算法核心 → 公共类型/工具 → 第三方库
Lightning-LM 以 AA-FasterLIO 为前端、以自研 miao 为图优化核心,围绕"分块地图 + 回环 + 动静态分层 + 3D→2D 栅格"的完整 Lidar SLAM/定位系统;上层由 system::Slam/LocSystem 统一调度,下层通过 core/ 与 miao/ 实现算法细节,中间通过 ROS2/bag 封装和 Pangolin UI 做工程化落地
2.1 文件架构拆解
核心文件 src 的目录架构可以拆解概括成这样:
2.1.1应用层:src/app
-
run_slam_online.cc / run_slam_offline.cc
-
run_loc_online.cc / run_loc_offline.cc
-
run_loop_offline.cc, run_frontend_offline.cc, test_ui.cc
-
这些是不同模式下的主程序入口(ROS2 node / 离线程序),负责:
-
解析参数和 YAML 配置
-
创建 system::Slam 或 system::LocSystem
-
连接 bag 或真实传感器
-
2.1.2 系统调度层:src/system
-
slam.{cc,h}:建图系统
-
loc_system.{cc,h}:纯定位系统
-
async_message_process.h:系统级异步消息调度
-
它们把各个算法模块(LIO、回环、地图、定位、UI...)"拼成一条流水线"
***2.1.3 核心算法层:src/core
- lio/:LIO 前端
-
eskf.{cc,hpp}:IMU 误差状态卡尔曼滤波 / IEKF
-
imu_processing.hpp:IMU 预积分、插值
-
laser_mapping.{cc,h}:激光因子更新 + 地图维护
-
pointcloud_preprocess.{cc,h}:点云去畸变、滤波、采样
-
pose6d.h:状态表示
-
ivox3d/:稀疏增量体素地图(FasterLIO 用的 IVox 结构)
-
g2p5/:3D → 2D 栅格转换(g2p5)
- g2p5_map, g2p5_grid_data, g2p5_subgrid 等封装 2D 地图块
- localization/:
-
localization.{cpp,h}:整体定位流程
-
localization_result.*:定位结果结构
-
pose_graph/:pgo.*, pose_extrapolator.*, smoother.h
-
lidar_loc/:激光定位(scan-to-map,对应在线定位前端)
-
pclomp/ 里是一套多线程 NDT (ndt_omp.*)
- loop_closing/:闭环检测与优化
- loop_closing.{cc,h}
- maps/:
- tiled_map.{cc,h}, tiled_map_chunk.{cc,h}:分块地图管理与动态加载
- miao/:轻量图优化库(类 g2o)
-
core/graph/*, solver/*, robust_kernel/*, types/*...
-
提供边/顶点结构、求解器、鲁棒核等
- lightning_math.hpp:一些公用数学工具
2.1.4 数据与公共类型:src/common
-
imu.h, odom.h, nav_state.{cc,h}:传感器和状态建模
-
keyframe.h, loop_candidate.h, measure_group.h:关键帧 / 回环候选 / 对齐好的测量批
-
point_def.h, functional_points.h:点类型定义(带强度、时间等)
-
options.{cc,h}, params.{cc,h}:将 YAML 配置解析成结构化参数
-
timed_pose.h, pose_rpy.h, eigen_types.h, std_types.h 等:基础类型
2.1.5 IO / 适配层
- src/io:
-
yaml_io.{cc,h}:读取配置
-
file_io.{cc,h}:读写 PCD、地图等
-
dataset_type.h:数据集枚举
- src/wrapper:
-
bag_io.{cc,h}:对 ROS2 bag 的遍历/读取封装
-
ros_utils.h:一些 ROS 相关工具
- srv/LocCmd.srv, srv/SaveMap.srv:暴露服务接口(定位命令、保存地图)
2.1.6 可视化 UI:src/ui
-
pangolin_window.* / pangolin_window_impl.*
-
ui_car.*, ui_cloud.*, ui_trajectory.*
-
使用 Pangolin 做 3D UI,用来展示点云、轨迹和车辆模型,可在 YAML 中开关 system.with_ui / system.with_2dui
2.1.7 杂项工具:src/utils
-
timer.{cc,h}:性能计时(Issues 里大家贴的 log 就是它)
-
pointcloud_utils.*, sync.h:点云操作和时间同步
-
async_message_process.h:更底层的异步队列,供 system 和 app 使用
整体来看,算法核心被放在 core/ ,和 ROS / UI / 数据集强解耦,方便以后复用
2.2 核心数据流和模块协作
2.2.1 LIO 前端(core/lio)
主要职责:把 IMU + LiDAR 同步后,输出流畅的 6 DoF 位姿,并维护局部地图
典型流程:
python
MeasureGroup{IMU, LiDAR} --> pointcloud_preprocess
--> IMU Processing / ESKF (eskf)
--> laser_mapping (scan-to-map + map update)
--> nav_state + keyframe + local map(IVox3d)
关键点:
-
使用 AA-FasterLIO 前端(Anderson Acceleration 强化的 FasterLIO)作为 LIO 算法,结合 ivox3d 稀疏体素地图提高效率
-
MeasureGroup 把时间同步后的激光帧与若干 IMU 数据打包,保证预积分时序正确
-
eskf 实现的是误差状态 Kalman / IEKF,laser_mapping 构建观测 Jacobian 并更新状态,对应 Issues 中 ObsModel (IEKF Build Jacobian) 的计时项
2.2.2 建图后端:地图 + 回环 + 图优化
建图模式下,系统更像:
python
LIO(front-end) --> keyframes & local map
|
+--> loop_closing (检测闭环、构建约束)
+--> pose_graph (pgo + miao optimizer)
+--> maps::TiledMap / g2p5 生成全局地图
- 地图层(maps/tiled_map*)
-
地图被划分为多个 TiledMapChunk,支持按需加载/卸载,适合大规模场景
-
对 LIO 和定位模块暴露统一接口(如:给定位姿取邻域点云)
- 回环检测(loop_closing)
-
使用 loop_candidate, keyframe 这些数据结构,从关键帧集合中寻找回环候选
-
检测到回环后,构造 "回环边" 交给 pose graph
- 图优化(core/localization/pose_graph + core/miao)
-
pgo.{cc,h} + pgo_impl.*:封装了带有增量特性的位姿图优化逻辑
-
底层依赖 miao:
-
仿 g2o 的图结构和线性求解器,但更轻、更适配当前项目
-
支持增量优化,无需重建整个图模型,这对在线回环修正非常关键
-
Lightning-LM 最新加入的"全局高度约束"也是通过后端图优化实现的:在 loop_closing 里可以配置 with_height,对 Z 轴加 soft constraint 抑制大场景 z 漂移
2.2.3 定位模块(core/localization)
纯定位和 SLAM 的前端是不一样的:
-
建图时:使用 LIO(点云 + IMU + ivox3d)
-
定位时:更多使用 scan-to-map / NDT 对现有地图匹配
目录结构里可以看到:
-
lidar_loc/:
-
lidar_loc.{cc,h}:整体激光定位算法封装
-
pclomp/:多线程的 NDT 实现(OpenMP 加速)
-
-
localization.{cpp,h}:把地图、初值、观测等 glue 在一起,形成完整的定位 pipeline
-
localization_result.*:封装定位结果(位姿、协方差、质量指标等)
-
pose_extrapolator.* + smoother.h:
-
前者根据 IMU / wheel odom /历史 pose 为下一帧提供预测
-
后者负责生成高频、平滑的输出轨迹(项目指出 100 Hz IMU 滤波输出)
-
配合 README 里的说明,定位系统还支持:
-
动静态图层分离:地图分为静态层 + 动态层,动态层可以短期/中期/永久三种策略:
-
短期:只保留最近的一段动态点
-
中期:缓存更长时间
-
永久:一直积累,用于长期存在的"动态但稳定"结构(如路边停的车)
-
这部分逻辑主要在 maps/ + localization/ 里实现,通过不同 map chunk 的管理和更新策略来体现
2.2.4 3D → 2D 栅格地图(core/g2p5)
g2p5 可以理解为一个独立的小模块:
python
3D 点云地图 --> g2p5_map/g2p5_subgrid --> 2D grid (map.pgm)
3D 点云地图 --> g2p5_map/g2p5_subgrid --> 2D grid (map.pgm)
-
g2p5_map.*:对整幅 2D 栅格地图的封装
-
g2p5_subgrid.*:局部子栅格块
-
g2p5_grid_data.h:每个栅格的数据结构(占据概率等)
系统可以通过配置 system.with_g2p5 来开启/关闭 2D 地图输出,实时生成并可保存 pgm
2.3 运行模式 & 系统层逻辑
2.3.1 在线 / 离线程序入口(src/app)
几类入口程序:
- 建图:
-
run_slam_online.cc
-
run_slam_offline.cc
- 定位:
-
run_loc_online.cc
-
run_loc_offline.cc
- 只跑前端:
- run_frontend_offline.cc
- 回环测试:
- run_loop_offline.cc
- UI 测试:
- test_ui.cc
这些文件做的事情大体类似:
-
通过 io::yaml_io 读取配置(config/default_*.yaml 等)
-
构造 system::Slam 或 system::LocSystem
-
对于 在线模式:
-
使用 wrapper::ros_utils 订阅 LiDAR/IMU 话题
-
推入系统的消息队列(async_message_process)
-
-
对于 离线模式:
-
使用 wrapper::bag_io 遍历 ROS2 bag(db3)
-
将每条消息按时间顺序喂给系统
-
2.3.2 system::Slam 和 system::LocSystem
它们是整个系统的"中枢神经":
- 管理各个子模块生命周期:
- LIO / maps / loop_closing / localization / UI / IO / pose graph...
- 在内部维护一个或多个处理线程:
-
消息接收线程:把 ROS/bag 数据转换为 MeasureGroup 或传感器帧
-
计算线程:按时间顺序取消息,调用 LIO / 定位 / 回环 / 2D 地图等过程
- 对外提供接口:
-
topic:发布 TF / Pose / Odometry 等
-
service:
-
SaveMap:保存地图
-
LocCmd:控制定位(重定位、重置等)
-
多线程相关的关键工具包括:async_message_process.h(系统级)、utils/sync.h(时间同步)、utils/timer.*(性能统计)
***2.4 配置体系 YAML → Options / Params
配置文件在 config/(如 default_nclt.yaml 等),通过 common::options / params 解析成结构体
README 中列了一些核心开关:
-
system.with_loop_closing:是否启用回环
-
system.with_ui / system.with_2dui:是否开启 3D/2D UI
-
system.with_g2p5:是否生成 2D 栅格地图
-
system.map_path:地图存储目录
-
fasterlio.point_filter_num:点云下采样系数
-
g2p5.esti_floor:是否动态估计地面参数
-
g2p5.grid_map_resolution:栅格地图分辨率
-
loop_closing.with_height:开启高度约束,抑制 Z 漂移(2025-11-13 新增)
2.4 数据流总结
2.4.1 离线建图(run_slam_offline)
python
ROS2 bag(db3)
|
v (wrapper::bag_io)
MeasureGroup{IMU+LiDAR}
|
v (system::Slam)
LIO front-end (core/lio) ----> IVox3d local map
| |
| v
| maps::TiledMap
| |
+--> keyframes --> loop_closing --+
--> pose_graph(pgo + miao)
|
v
优化后的位姿图 / 修正地图
|
v
g2p5: 3D->2D 栅格地图 (map.pgm)
最终在 data/new_map 目录下得到:
-
global.pcd:可视化用的全局点云
-
map.pgm:2D 栅格
-
以及块状地图、动态层数据等
2.4.2 在线纯定位(run_loc_online)
python
实时 LiDAR + IMU 话题
|
v
wrapper/ros_utils + utils/sync
|
v
system::LocSystem
|
+--> pose_extrapolator (高频预测)
|
+--> lidar_loc (NDT_OMP) 对 maps::TiledMap 扫描匹配
|
+--> localization_result (位姿 + 质量)
|
+--> smoother (轨迹平滑,100 Hz 输出)
同时根据配置选择:
-
是否使用动静态图层分离(更新 dynamic layer)
-
是否启用回环和图优化(对于特大场景,可以缓解累计漂移)
3 单独模块升级策略
Lightning-LM 的架构高度模块化,而且作者在设计时刻意把"算法核心"与"系统调度 / IO / 框架"做了彻底分离,所以可以单独替换、升级某个模块,而不破坏其他部分
3.1 Lightning-LM 的模块化程度(结构层级)
Lightning-LM 可以粗略分为 5 层:
python
App 层(online/offline main)
↓
System 层(Slam / LocSystem 流水线调度)
↓
Core 算法层(LIO / Localization / LoopClosing / Maps / g2p5)
↓
Math & Optimization(miao 图优化)
↓
IO、Wrapper、Utils
其中 核心算法层 就是想升级模块的主要战场
每个模块几乎都是目录级隔离 + 接口隔离 + 数据结构隔离,例如:
-
core/lio:LIO 前端
-
core/localization:定位系统(NDT + 匹配框架)
-
core/loop_closing:回环检测
-
core/maps:分块地图 / 动静态地图
-
core/g2p5:3D → 2D 栅格地图
-
core/miao:图优化
这些模块之间的依赖关系非常清晰,主要通过以下数据结构沟通:
-
NavState
-
MeasureGroup
-
KeyFrame
-
TiledMapChunk
-
LocalizationResult
-
PoseGraph / Vertex / Edge
👉 因此替换某个模块并不会影响整套 SLAM 运行,只要维持输入输出类型一致
3.2 System --- "胶水层"
System 层(system::Slam / system::LocSystem) 做的事情是:
-
维护线程
-
调用 LIO / Loop / PGO / Map
-
发布结果 / 更新地图
它不关心模块内部实现,只关心模块的"输入输出 API",所以在修改某个模块时,一般不会动 system 层。也就是说:模块替换不会破坏 main flow,只要保持接口一致
3.3 模块升级方向(挖坑向)
- 升级LIO(core/lio),耦合情况:
-
输入:MeasureGroup
-
输出:NavState + local map
-
下游:loop_closing 与 map 层依赖关键帧与局部地图
➡ 可以独立替换,只要保持 laser_mapping 的输出格式一致
- 升级 定位模块(core/localization),耦合情况:
-
输入:点云 + 地图块(tiled map)
-
输出:LocalizationResult(pose、质量、协方差)
➡ 完全可插拔,是 Lightning-LM 最容易独立替换的模块
- 升级 地图模块(core/maps)
➡ 只要对外仍然提供 "给定 pose → 返回周围点云块" 的接口,就是可替换的
- 升级 回环检测(core/loop_closing)
➡ 回环只要求:输入是关键帧,输出是loop_constraints(通常是 RelativePose + 信息矩阵)。因此是完全可 modular 的
- 升级 图优化(core/miao)
➡ 因为 miao 的接口非常接近 g2o,只要保持 PoseGraph 的输入接口一致,就是可替换的
- 升级 3D→2D 栅格地图(core/g2p5)
➡ g2p5 是完全独立的小模块,不依赖 SLAM 主流程:
3 Lightning-LM 效果展示
