概述
针对《LIO-SAM: Tightly-coupled Lidar Inertial Odometry via Smoothing and Mapping》的所有代码进行学习。依托Deepseek/ChatGPT等AI工具学习代码,并对代码进行详细注释,以此学习SLAM。
代码:https://github.com/TixiaoShan/LIO-SAM
LIO-SAM的系统架构
项目中各个文件
<1> .github:通常存放 GitHub 相关配置文件。
<2> config:一般存储项目的配置文件,比如参数配置、算法配置等。
<3> include:用于放置头文件(.h 等),供项目代码引用。
<4> launch:存放 ROS 的启动文件(.launch),用于一键启动多个节点、配置参数等。
<5> msg:定义 ROS 自定义消息类型的文件夹。
<6> src:存储项目的源文件(.cpp 等),是代码实现的主要地方。
<7> srv:定义 ROS 服务(Service)类型的文件夹。
<8> MakeLists.txt:CMake 的构建配置文件,用于定义项目编译规则。
<9> Dockerfile:Docker 镜像构建文件。
<10> LICENSE:许可证文件,规定项目使用、分发等权限。
<11> README.md:项目说明文档,介绍项目功能、使用方法等。
<12>package.xml:ROS 包的描述文件,声明依赖、包信息等。
梳理项目配置与依赖
package.xml 文件
在 ROS 项目里,它是描述包信息、声明依赖的文件。能帮助了解项目依赖哪些 ROS 功能包,这些依赖决定了项目能调用哪些 ROS 提供的功能,也能辅助排查环境搭建问题 。
<?xml version="1.0"?>
<!-- XML声明:指定XML版本为1.0 -->
<package>
<!-- ROS软件包根标签:所有内容必须包含在此标签内 -->
<name>lio_sam</name>
<!-- 软件包名称:必须与包含此文件的文件夹名称一致 -->
<version>1.0.0</version>
<!-- 软件包版本号:遵循语义化版本规则 -->
<description>Lidar Odometry</description>
<!-- 软件包描述:简要说明软件包功能(激光雷达里程计) -->
<maintainer email="shant@mit.edu">Tixiao Shan</maintainer>
<!-- 维护者信息:姓名 + 联系邮箱 -->
<license>TODO</license>
<!-- 许可证类型:TODO表示需要后续补充具体许可证(如BSD/MIT/Apache等) -->
<author>Tixiao Shan</author>
<!-- 作者信息:软件包的主要开发者 -->
<!-- ===== 构建工具依赖 ===== -->
<buildtool_depend>catkin</buildtool_depend>
<!-- 构建工具依赖:必须的ROS构建系统 -->
<!-- ===== 构建依赖(编译时需要的库)===== -->
<build_depend>roscpp</build_depend>
<!-- 构建时依赖:ROS的C++客户端库 -->
<run_depend>roscpp</run_depend>
<!-- 运行时依赖:同上 -->
<build_depend>rospy</build_depend>
<!-- 构建时依赖:ROS的Python客户端库 -->
<run_depend>rospy</run_depend>
<!-- 运行时依赖:同上 -->
<build_depend>tf</build_depend>
<run_depend>tf</run_depend>
<!-- 坐标变换库:用于处理坐标系转换 -->
<build_depend>cv_bridge</build_depend>
<run_depend>cv_bridge</run_depend>
<!-- OpenCV-ROS桥接库:实现ROS图像消息与OpenCV格式的转换 -->
<build_depend>pcl_conversions</build_depend>
<run_depend>pcl_conversions</run_depend>
<!-- PCL点云库的ROS转换工具:用于ROS点云消息与PCL数据结构的转换 -->
<!-- ===== 常用ROS消息类型依赖 ===== -->
<build_depend>std_msgs</build_depend>
<run_depend>std_msgs</run_depend>
<!-- 标准消息类型(如Int32, String等) -->
<build_depend>sensor_msgs</build_depend>
<run_depend>sensor_msgs</run_depend>
<!-- 传感器消息类型(如点云PointCloud2、图像Image等) -->
<build_depend>geometry_msgs</build_depend>
<run_depend>geometry_msgs</run_depend>
<!-- 几何消息类型(如Pose, Twist, Vector3等) -->
<build_depend>nav_msgs</build_depend>
<run_depend>nav_msgs</run_depend>
<!-- 导航消息类型(如路径Path、地图OccupancyGrid等) -->
<build_depend>visualization_msgs</build_depend>
<run_depend>visualization_msgs</run_depend>
<!-- 可视化消息类型(如Marker,用于RViz显示) -->
<!-- ===== 自定义消息依赖 ===== -->
<build_depend>message_generation</build_depend>
<!-- 构建时依赖:生成自定义消息所需的工具 -->
<run_depend>message_generation</run_depend>
<!-- 运行时依赖:此处应改为message_runtime(当前写法可能错误) -->
<build_depend>message_runtime</build_depend>
<!-- 构建时依赖:自定义消息的运行时支持 -->
<run_depend>message_runtime</run_depend>
<!-- 运行时依赖:自定义消息的实际运行支持 -->
<!-- ===== 外部库依赖 ===== -->
<build_depend>GTSAM</build_depend>
<run_depend>GTSAM</run_depend>
<!-- 因子图优化库:用于SLAM后端优化 -->
</package>
关键依赖说明:
<1> 核心 ROS 库:
①roscpp/rospy:ROS 基础通信库
②tf:坐标变换管理
③catkin:构建系统
<2> 消息类型依赖:
①std_msgs:基础数据类型
②sensor_msgs:传感器数据
③geometry_msgs:几何数据
④nav_msgs:导航相关数据
⑤visualization_msgs:可视化数据
<3> 功能库依赖:
①pcl_conversions:点云处理
②cv_bridge:视觉处理
③GTSAM:因子图优化(用于激光 SLAM 后端优化)
<4> 消息生成:
④message_generation/message_runtime:自定义消息支持
CMakeLists.txt 文件
用于 CMake 构建系统,定义了项目怎么编译(比如要编译哪些源文件、链接哪些库 )。结合 package.xml ,能了解项目的构建流程和代码组织方式,后续看 src 里的代码,也能明白编译逻辑 。
cmake_minimum_required(VERSION 2.8.3) # 指定所需的最小 CMake 版本
project(lio_sam) # 设置项目名称,与 package.xml 中的名称保持一致
# 设置编译选项
set(CMAKE_BUILD_TYPE "Release") # 设置编译类型为 Release,启用优化
set(CMAKE_CXX_FLAGS "-std=c++11") # 使用 C++11 标准
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -g -pthread") # 发布版本的编译选项:优化、警告、调试符号、多线程支持
# 查找 catkin 依赖包
find_package(catkin REQUIRED COMPONENTS
tf
roscpp
rospy
cv_bridge
# pcl library
pcl_conversions
# msgs
std_msgs
sensor_msgs
geometry_msgs
nav_msgs
message_generation
visualization_msgs
)
# 查找系统依赖库
find_package(OpenMP REQUIRED) # 查找 OpenMP 并行计算库
find_package(PCL REQUIRED QUIET) # 查找点云库 (PCL)
find_package(OpenCV REQUIRED QUIET) # 查找 OpenCV 计算机视觉库
find_package(GTSAM REQUIRED QUIET) # 查找 GTSAM (Georgia Tech Smoothing and Mapping) 因子图优化库
find_package(Boost REQUIRED COMPONENTS timer) # 查找 Boost 库的 timer 组件
# 添加自定义消息文件
add_message_files(
DIRECTORY msg
FILES
cloud_info.msg # 自定义消息文件
)
# 添加自定义服务文件
add_service_files(
DIRECTORY srv
FILES
save_map.srv # 自定义服务文件
)
# 生成消息和服务代码
generate_messages(
DEPENDENCIES
geometry_msgs
std_msgs
nav_msgs
sensor_msgs # 指定消息生成依赖的其他消息包
)
# 配置 catkin 包导出信息
catkin_package(
INCLUDE_DIRS include # 导出的头文件目录
DEPENDS PCL GTSAM # 非 catkin 依赖
CATKIN_DEPENDS # catkin 依赖
std_msgs
nav_msgs
geometry_msgs
sensor_msgs
message_runtime
message_generation
visualization_msgs
)
# 包含目录
include_directories(
include # 项目内的头文件目录
${catkin_INCLUDE_DIRS} # catkin 依赖的头文件目录
${PCL_INCLUDE_DIRS} # PCL 头文件目录
${OpenCV_INCLUDE_DIRS} # OpenCV 头文件目录
${GTSAM_INCLUDE_DIR} # GTSAM 头文件目录
)
# 链接目录
link_directories(
include # 项目内的库目录
${PCL_LIBRARY_DIRS} # PCL 库文件目录
${OpenCV_LIBRARY_DIRS} # OpenCV 库文件目录
${GTSAM_LIBRARY_DIRS} # GTSAM 库文件目录
)
###########
## Build ##
###########
# 编译 Range Image Projection 节点
add_executable(${PROJECT_NAME}_imageProjection src/imageProjection.cpp)
add_dependencies(${PROJECT_NAME}_imageProjection ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_generate_messages_cpp)
target_link_libraries(${PROJECT_NAME}_imageProjection ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${OpenCV_LIBRARIES})
# 编译 Feature Association 节点
add_executable(${PROJECT_NAME}_featureExtraction src/featureExtraction.cpp)
add_dependencies(${PROJECT_NAME}_featureExtraction ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_generate_messages_cpp)
target_link_libraries(${PROJECT_NAME}_featureExtraction ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${OpenCV_LIBRARIES})
# 编译 Mapping Optimization 节点
add_executable(${PROJECT_NAME}_mapOptmization src/mapOptmization.cpp)
add_dependencies(${PROJECT_NAME}_mapOptmization ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_generate_messages_cpp)
target_compile_options(${PROJECT_NAME}_mapOptmization PRIVATE ${OpenMP_CXX_FLAGS}) # 添加 OpenMP 编译选项
target_link_libraries(${PROJECT_NAME}_mapOptmization Boost::timer ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${OpenCV_LIBRARIES} ${OpenMP_CXX_FLAGS} gtsam) # 链接 Boost timer 和 GTSAM
# 编译 IMU Preintegration 节点
add_executable(${PROJECT_NAME}_imuPreintegration src/imuPreintegration.cpp)
target_link_libraries(${PROJECT_NAME}_imuPreintegration Boost::timer ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${OpenCV_LIBRARIES} gtsam) # 链接 Boost timer 和 GTSAM
关键部分总结:
<1>编译设置:
1.使用 C++11 标准
2.启用优化 (-O3) 和多线程支持 (-pthread)
3.设置为 Release 构建类型
<2>依赖管理:
1.查找 ROS 依赖(catkin 包)
2.查找系统依赖(PCL、OpenCV、GTSAM、Boost、OpenMP)
<3>消息和服务生成:
1.定义自定义消息 cloud_info.msg
2.定义自定义服务 save_map.srv
3.生成相关的 C++/Python 代码
<4>编译目标:
生成 4 个可执行文件(ROS 节点):
a.lio_sam_imageProjection - 点云投影
b.lio_sam_featureExtraction - 特征提取
c.lio_sam_mapOptmization - 地图优化(使用 OpenMP 加速)
d.lio_sam_imuPreintegration - IMU 预积分(使用 GTSAM 优化)
<5>链接库:
主要链接库包括:
a.ROS 相关库(roscpp、tf 等)
b.PCL 点云库
c.OpenCV 计算机视觉库
d.GTSAM 因子图优化库
e.Boost timer 库
f.OpenMP 并行计算库
理解项目运行逻辑
launch 文件
里面的 launch 文件是 ROS 里用来一键启动多个节点、配置参数的配置文件。看看有哪些 launch 文件,打开后了解它启动了哪些节点、设置了什么参数,能帮助理清项目运行时的组件协作关系,知道 “项目跑起来的时候,到底在执行哪些模块” 。
<launch>
<!-- 定义参数:项目名称,默认为"lio_sam" -->
<arg name="project" default="lio_sam"/>
<!-- 加载系统核心配置参数 -->
<rosparam file="$(find lio_sam)/config/params.yaml" command="load" />
<!-- LOAM (Lidar Odometry and Mapping) 模块 -->
<!-- 负责激光雷达点云的处理、特征提取和里程计估计 -->
<include file="$(find lio_sam)/launch/include/module_loam.launch" />
<!-- 机器人状态发布模块 -->
<!-- 管理机器人各坐标系间的变换关系 (TF Tree) -->
<include file="$(find lio_sam)/launch/include/module_robot_state_publisher.launch" />
<!-- GNSS 导航卫星模块 -->
<!-- 融合 GPS/北斗等定位数据,提供全局定位参考 -->
<include file="$(find lio_sam)/launch/include/module_navsat.launch" />
<!-- Rviz 可视化模块 -->
<!-- 提供实时可视化界面,显示点云、轨迹和地图 -->
<include file="$(find lio_sam)/launch/include/module_rviz.launch" />
</launch>
模块化设计说明
<1>参数配置 (params.yaml)
包含系统所有可调参数:
a.传感器标定参数(外参矩阵、IMU 噪声参数)
b.算法调优参数(特征提取阈值、优化迭代次数)
c.坐标系定义(激光雷达、IMU、GPS 相对位置)
<2>LOAM 模块
核心算法模块,包含四个主要节点:
a.imageProjection - 点云预处理,生成距离图像
b.featureExtraction - 提取角点和平面点特征
c.mapOptimization - 基于因子图的全局优化,融合多传感器数据
d.imuPreintegration - IMU 预积分处理,提供高频状态估计
<3>机器人状态发布
维护 TF 坐标树,发布以下关键变换:
a.base_link 到 odom 的变换(里程计坐标系)
b.odom 到 map 的变换(全局地图坐标系)
c.传感器间的相对位置关系(如 LiDAR 到 IMU)
<4>导航卫星模块
1.接收 GNSS 原始数据,转换为 ROS 标准消息
2.提供全局坐标系下的位置参考
3.辅助 LIO-SAM 系统实现全局定位
<5>可视化模块
使用 Rviz 实时显示:
a.原始点云数据
b.提取的特征点
c.实时构建的地图
d.机器人轨迹
e.系统状态信息
启动流程
1.加载系统参数配置
2.启动 LOAM 核心算法节点,开始点云处理和状态估计
3.启动坐标系统管理,确保各传感器数据在统一坐标系下
4.启动 GNSS 数据处理,提供全局定位信息
5.启动可视化工具,提供用户交互界面
config 文件
用于存放各类配置文件(比如参数配置、校准数据等 ),结合 launch 文件里的参数引用,能明白项目运行时的可配置项,以及这些配置如何影响功能表现。
LIO-SAM 核心参数配置
lio_sam:
# 数据主题配置(ROS话题名称)
pointCloudTopic: "points_raw" # 激光雷达点云原始数据话题
imuTopic: "imu_raw" # IMU原始数据话题
odomTopic: "odometry/imu" # IMU预积分里程计话题(与IMU同频率)
gpsTopic: "odometry/gpsz" # GNSS定位数据话题(来自navsat模块)
# 坐标系定义(ROS坐标框架)
lidarFrame: "base_link" # 激光雷达坐标系(通常与机器人基座重合)
baselinkFrame: "base_link" # 机器人基座坐标系
odometryFrame: "odom" # 里程计坐标系
mapFrame: "map" # 全局地图坐标系
# GPS融合配置
useImuHeadingInitialization: true # 是否使用IMU航向初始化(GPS可用时建议开启)
useGpsElevation: false # 是否使用GPS高程(若GPS高程精度差则设为false)
gpsCovThreshold: 2.0 # GPS位置协方差阈值(m²,高于此值的GPS数据会被过滤)
poseCovThreshold: 25.0 # 姿态协方差阈值(m²,用于GPS数据筛选)
# 数据导出设置
savePCD: false # 是否保存点云数据到文件(开启会占用大量磁盘空间)
savePCDDirectory: "/Downloads/LOAM/" # 点云保存路径(注意:代码会先删除该目录再重建)
# 激光雷达传感器参数
sensor: velodyne # 激光雷达类型('velodyne'/'ouster'/'livox')
N_SCAN: 16 # 激光雷达通道数(如16线、32线等)
Horizon_SCAN: 1800 # 水平分辨率(Velodyne 16线为1800,Ouster可设512/1024等)
downsampleRate: 1 # 点云下采样率(数据量过大时可增大此值)
lidarMinRange: 1.0 # 激光雷达有效测量最小距离(滤除近场噪声)
lidarMaxRange: 1000.0 # 激光雷达有效测量最大距离
# IMU传感器参数(噪声与偏置,需根据实际传感器标定)
imuAccNoise: 3.9939570888238808e-03 # 加速度计噪声密度(m/s²/√Hz)
imuGyrNoise: 1.5636343949698187e-03 # 陀螺仪噪声密度(rad/s/√Hz)
imuAccBiasN: 6.4356659353532566e-05 # 加速度计偏置随机游走(m/s²/√Hz)
imuGyrBiasN: 3.5640318696367613e-05 # 陀螺仪偏置随机游走(rad/s/√Hz)
imuGravity: 9.80511 # 重力加速度值(m/s²,需根据当地实际值调整)
imuRPYWeight: 0.01 # IMU姿态权重(用于融合时调整姿态可信度)
# 传感器外参(激光雷达到IMU的变换矩阵,需通过标定获取)
extrinsicTrans: [0.0, 0.0, 0.0] # 平移向量 [x, y, z](单位:米)
extrinsicRot: [-1, 0, 0, # 旋转矩阵(3x3,按行展开)
0, 1, 0,
0, 0, -1]
extrinsicRPY: [0, -1, 0, # RPY旋转矩阵(3x3,按行展开)
1, 0, 0,
0, 0, 1]
# 特征提取阈值(影响LOAM前端性能)
edgeThreshold: 1.0 # 角点提取阈值(值越大,提取的角点越尖锐)
surfThreshold: 0.1 # 平面点提取阈值(值越小,提取的平面点越平坦)
edgeFeatureMinValidNum: 10 # 角点最小有效数量(低于此值可能导致跟踪失败)
surfFeatureMinValidNum: 100 # 平面点最小有效数量
# 体素滤波参数(降采样提高计算效率)
odometrySurfLeafSize: 0.4 # 里程计阶段平面点滤波体素大小(室外0.4,室内0.2)
mappingCornerLeafSize: 0.2 # 建图阶段角点滤波体素大小
mappingSurfLeafSize: 0.4 # 建图阶段平面点滤波体素大小
# 机器人运动约束(用于2D机器人场景)
z_tollerance: 1000 # z轴位置容差(米,设为大值表示不约束)
rotation_tollerance: 1000 # 旋转容差(弧度)
# CPU资源分配
numberOfCores: 4 # 建图优化使用的核心数
mappingProcessInterval: 0.15 # 建图处理间隔(秒,调节建图频率)
# 局部地图管理参数
surroundingkeyframeAddingDistThreshold: 1.0 # 关键帧添加距离阈值(米)
surroundingkeyframeAddingAngleThreshold: 0.2# 关键帧添加角度阈值(弧度)
surroundingKeyframeDensity: 2.0 # 局部关键帧密度(米,控制关键帧稀疏程度)
surroundingKeyframeSearchRadius: 50.0 # 局部关键帧搜索半径(米,用于扫描匹配)
# 回环检测参数(提升全局定位精度)
loopClosureEnableFlag: true # 是否启用回环检测
loopClosureFrequency: 1.0 # 回环检测频率(Hz)
surroundingKeyframeSize: 50 # 回环检测子图大小(关键帧数)
historyKeyframeSearchRadius: 15.0 # 历史关键帧搜索半径(米)
historyKeyframeSearchTimeDiff: 30.0 # 历史关键帧时间差阈值(秒)
historyKeyframeSearchNum: 25 # 历史关键帧搜索数量
historyKeyframeFitnessScore: 0.3 # ICP匹配适应度阈值(越小匹配要求越高)
# 可视化参数(Rviz显示效果)
globalMapVisualizationSearchRadius: 1000.0 # 全局地图可视化半径(米)
globalMapVisualizationPoseDensity: 10.0 # 全局地图姿态密度(米)
globalMapVisualizationLeafSize: 1.0 # 全局地图点云滤波大小(米)
Navsat 模块配置(GPS 坐标转换)
navsat:
frequency: 50 # GPS数据发布频率(Hz)
wait_for_datum: false # 是否等待基准点初始化
delay: 0.0 # 数据延迟(秒)
magnetic_declination_radians: 0 # 磁偏角(弧度,需根据当地地磁参数调整)
yaw_offset: 0 # 航向偏移量(弧度)
zero_altitude: true # 是否将当前高度设为海拔0
broadcast_utm_transform: false # 是否广播UTM坐标转换
broadcast_utm_transform_as_parent_frame: false
publish_filtered_gps: false # 是否发布滤波后的GPS数据
EKF(扩展卡尔曼滤波器)配置
ekf_gps:
publish_tf: false # 是否发布TF坐标变换
map_frame: map # 全局地图坐标系
odom_frame: odom # 里程计坐标系
base_link_frame: base_link # 机器人基座坐标系
world_frame: odom # 世界坐标系
frequency: 50 # EKF更新频率(Hz)
two_d_mode: false # 是否启用2D模式
sensor_timeout: 0.01 # 传感器超时时间(秒)
# IMU数据源配置(需与实际传感器话题一致)
imu0: imu_correct # IMU话题名称(已校正的IMU数据)
imu0_config: [false, false, false,
true, true, true,
false, false, false,
false, false, true,
true, true, true] # IMU数据协方差配置(详见ROS EKF文档)
imu0_differential: false # IMU是否为差分模式
imu0_queue_size: 50 # IMU数据队列大小
imu0_remove_gravitational_acceleration: true # 是否移除重力加速度
# 里程计(GPS)数据源配置
odom0: odometry/gps # GPS里程计话题
odom0_config: [true, true, true,
false, false, false,
false, false, false,
false, false, false,
false, false, false] # GPS数据协方差配置
odom0_differential: false # GPS是否为差分模式
odom0_queue_size: 10 # GPS数据队列大小
# 过程噪声协方差矩阵(15x15,影响EKF状态估计精度)
process_noise_covariance: [ 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 10.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0.04, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.015]