跳到主要内容 LIO-SAM 算法在 Ubuntu 22.04 与 ROS2 Humble 下的仿真实现 | 极客日志
C++ AI 算法
LIO-SAM 算法在 Ubuntu 22.04 与 ROS2 Humble 下的仿真实现 在 Ubuntu 22.04 和 ROS2 Humble 环境下配置并运行 LIO-SAM 激光雷达里程建图算法的完整流程。内容包括环境依赖安装、GTSAM 库编译、Gazebo 仿真启动及键盘控制,以及使用现有数据集进行离线仿真的方法。重点解决了点云去畸变、因子图优化原理及常见编译错误处理,适用于机器人状态估计与 SLAM 学习。
微码行者 发布于 2026/3/30 更新于 2026/4/13 1 浏览背景
传统的激光雷达是通过 ICP(Iterative Closest Point)算法去找到两个连续 lidar 帧(点云)之间的相对变换来实现的。
ICP 是一种迭代优化算法,输入为两组点云数据(源点云和目标点云),输出为两组点云之间的刚体变换(旋转矩阵 R 和平移向量 T)。
ICP 能用来估计相邻雷达帧之间的相对运动,通过点云配准计算出两帧之间的旋转和平移,能有效解决帧间点云的对齐问题,但当帧与帧之间的变换角度过大时,ICP 可能难以收敛。
并且由于激光雷达在采集点云时会旋转,点云会发生倾斜,当使用倾斜的点云特征进行配准时,会导致较大的漂移。
因此,激光雷达通常与其它传感器(如 IMU、GPS)结合,用于状态估计和地图构建,即传感器融合。其融合方式又分为两种:松耦合融合和紧耦合融合。
松耦合融合 :各传感器独立运行,通过其整体的输出(如 IMU 的位姿估计、GPS 的全局位置)进行融合,即先融合各个传感器的最终结果,再用简单加权或滤波方式进行综合计算。
紧耦合融合 :各传感器的底层原始数据(如 IMU 加速度、GPS 伪距、激光点云)直接参与联合优化,实时生成整体状态,其融合精度和鲁棒性更高。
LIO-SAM 算法工作机理
2.1 点云去畸变处理
激光雷达数据的一个重大问题是点云中的时间差引起的畸变,LIO-SAM 通过 IMU 数据来修正扫描点的时间偏移(通常称为点云去畸变)。具体步骤如下:
提供点时间戳:使用 IMU 数据进行点云矫正,需要提供相对扫描点的时间。
提供点环号:环号用于将点分配到激光雷达的通道中,组织成矩阵结构。
2.2 因子图优化
因子图主要是用于对多传感器融合后的状态估计问题进行建模,LIO-SAM 算法的因子图构建主要使用 1 种变量类型和 4 种因子类型。
变量类型 :表示机器人在特定时间的状态,并分配给图的节点。
IMU 预积分因子 :用于使用来自 IMU 的测量值来推断机器人的运动,主要解决 IMU 噪声和偏置问题。
Lidar 里程计因子 :用于特征提取、关键帧选择和相对变换。
GPS 因子 :系统在长时间导航任务中会收到漂移的影响,因此需要使用提供绝对测量值的传感器(如 GPS)来消除漂移。
回环闭合因子 :采用基于欧几里得距离的环路闭合检测方法。
仿真操作
3.1 环境准备与配置
所需功能包 :解压特定的 LIO-SAM ROS2 压缩包到 ubuntu 系统中,src 路径中包含算法包、机器人控制包、Gazebo 仿真包和 Velodyne 模拟器包。
配置文件修改:
params.yaml:确保点云 (Point Cloud) 和 IMU 的话题名称 (Topic Name) 与仿真模型 SDF 文件中定义的一致。修改 LiDAR 和 Base Link 的坐标系名称。设置 savePCD 为 true 以便仿真结束后保存地图。
SDF 文件:确认 Velodyne 传感器的定义,确保话题名称与 params.yaml 中一致。
3.2 安装依赖
3.2.1 手动下载源码包
访问 GTSAM 官方下载页面获取源码。
下载并解压至本地目录。
3.2.2 准备编译环境 现在我们需要把这个解压出来的文件夹搬到主目录,并准备编译。请在终端中执行:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
free -h
最好再将虚拟机虚拟内存改为 8G!(电脑本身运行内存为 32G 的还可以调大些)
cd ~/Downloads
mv gtsam-4.1.1 ~/gtsam
(这条命令会把刚才解压的文件夹移动到您的主目录,并改名为 gtsam)
cd ~/gtsam
mkdir build && cd build
四。配置编译参数: (4.1.1 版本对新系统兼容较好,不需要复杂的 TBB 参数,但建议加上系统 Eigen 库支持)
cmake -DGTSAM_USE_SYSTEM_EIGEN=ON ..
(如果没有报错,最后会显示 Build files have been written to...)
五。开始编译 (这一步可能需要 10 分钟以上,请耐心等待进度走到 100%):
七。验证是否成功:
执行完 sudo make install 后,输入以下命令检查:
ls /usr/local/include/gtsam
只要屏幕上显示出 base, geometry, inference 等文件夹的名字,就说明安装成功了!
sudo apt update && sudo apt install curl gnupg lsb-release -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
观察输出:在刷新的过程中,请留意屏幕滚动的文字,看到类似 Get:5http://packages.ros.org/ros2/ubuntu jammy InRelease 的字样就说明 ROS 2 仓库连接成功了。
安装其他必要的 ROS2 依赖:为了防止编译时缺这缺那,我们先把通用的导航包装上:
sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup ros-humble-pcl-conversions
sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup ros-humble-pcl-conversions ros-humble-xacro ros-humble-robot-localization ros-humble-tf-transformations -y
sudo apt update
sudo apt install ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros
sudo apt update
sudo apt install ros-humble-gazebo-plugins ros-humble-gazebo-ros2-control
source /opt/ros/humble/setup.bash
cd ~/lio_sam_gazebo_ros2
source install/setup.bash
安装完新包后,必须让当前的终端'知道'新包的存在。
gedit ~/lio_sam_gazebo_ros2/src/velodyne_simulator/velodyne_description/package.xml
删除里面的所有内容,然后将下面这段标准的 ROS 2 配置代码复制粘贴进去,保存并关闭:
<?xml version="1.0" ?>
<package format ="3" >
<name > velodyne_description</name >
<version > 2.0.3</version >
<description > URDF description for Velodyne 3D LIDARs</description >
<maintainer email ="[email protected] " > maintainer</maintainer >
<license > BSD</license >
<buildtool_depend > ament_cmake</buildtool_depend >
<exec_depend > urdf</exec_depend >
<exec_depend > xacro</exec_depend >
<export >
<build_type > ament_cmake</build_type >
</export >
</package >
sudo apt update
sudo apt install ros-humble-pcl-conversions
修改代码以适配 GTSAM 4.1.1 (关键):
找到并打开 CMakeLists.txt:
gedit ~/lio_sam_gazebo_ros2/src/LIO-SAM/CMakeLists.txt
**修改 C++ 标准:**在 project(...) 这一行下面手动添加:
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
解释:这两行命令强制告诉编译器:'不管你默认是什么,我必须用 C++17 标准来编译,否则就报错'。
**添加 find_package:**找到文件前面的一堆 find_package,在其中找个空行,插入下面这行:
find_package(pcl_conversions REQUIRED)
**添加头文件路径:**在 find_package 后面找到 include_directories,把它修改为包含 pcl_conversions 的路径。如果找不到,可以直接在 include_directories(include) 的下一行添加:
include_directories(include ${pcl_conversions_INCLUDE_DIRS})
**添加依赖:**搜索 ament_target_dependencies,然后在括号里的最后加上 pcl_conversions,例如:
ament_target_dependencies(lio_sam_imageProjection ${dependencies} pcl_conversions)
(注意:文件里会有 4-5 个 ament_target_dependencies,建议每一个都检查一下)
保存并关闭。
修改配置文件 (Params.yaml):
打开配置文件
gedit ~/lio_sam_gazebo_ros2/src/LIO-SAM/config/params.yaml
修改保存路径:找到 savePCDDirectory,将其修改为您当前用户下的路径:
savePCD: true
savePCDDirectory: "~/Downloads/LOAM/"
(提示:如果您还没创建这个文件夹,记得去终端运行 mkdir -p ~/Downloads/LOAM)
添加依赖:在 <depend>...</depend> 的区域,找个空行添加:
<depend > pcl_conversions</depend >
保存并关闭。
开始编译:
3.3 运行仿真
3.3.1 启动 Gazebo 仿真环境(终端 1)
模型准备
创建模型目录
mkdir -p ~/.gazebo/models
将 lio_sam_gazebo_ros2/models 下的三个文件夹(brick_box、construction_barrel、rtab_map_stage)移入到 .gazebo/models 文件夹中。
若在 home 中没有看见 .gazebo 文件夹,可能是被隐藏了,按下 Ctrl + H 即可。
覆盖雷达配置文件
cd ~/lio_sam_gazebo_ros2/src/velodyne_simulator/velodyne_description/urdf
rm -f VLP-16.urdf.xacro
gedit VLP-16.urdf.xacro
粘贴标准修复代码
将以下代码完整复制,粘贴到刚才打开的 gedit 编辑器中,然后保存并关闭。
<?xml version="1.0" ?>
<robot xmlns:xacro ="http://www.ros.org/wiki/xacro" name ="VLP-16" >
<xacro:property name ="M_PI" value ="3.1415926535897931" />
<xacro:macro name ="VLP-16" params ="*origin parent:=base_link name:=velodyne topic:=/velodyne_points hz:=10 lasers:=16 samples:=1875 collision_range:=0.3 min_range:=0.9 max_range:=130.0 noise:=0.008 min_angle:=-${M_PI} max_angle:=${M_PI} gpu:=false" >
<joint name ="${name}_base_mount_joint" type ="fixed" >
<xacro:insert_block name ="origin" />
<parent link ="${parent}" />
<child link ="${name}_base_link" />
</joint >
<link name ="${name}_base_link" >
<inertial >
<mass value ="0.83" />
<origin xyz ="0 0 0.03585" />
<inertia ixx ="${(0.83 * (3.0*0.0516*0.0516 + 0.0717*0.0717)) / 12.0}" ixy ="0" ixz ="0" iyy ="${(0.83 * (3.0*0.0516*0.0516 + 0.0717*0.0717)) / 12.0}" iyz ="0" izz ="${0.5 * 0.83 * (0.0516*0.0516)}" />
</inertial >
<visual >
<geometry >
<mesh filename ="package://velodyne_description/meshes/VLP16_base_1.dae" />
</geometry >
</visual >
<visual >
<geometry >
<mesh filename ="package://velodyne_description/meshes/VLP16_base_2.dae" />
</geometry >
</visual >
<collision >
<origin rpy ="0 0 0" xyz ="0 0 0.03585" />
<geometry >
<cylinder radius ="0.0516" length ="0.0717" />
</geometry >
</collision >
</link >
<joint name ="${name}_base_scan_joint" type ="fixed" >
<origin xyz ="0 0 0.0377" rpy ="0 0 0" />
<parent link ="${name}_base_link" />
<child link ="${name}" />
</joint >
<link name ="${name}" >
<inertial >
<mass value ="0.01" />
<origin xyz ="0 0 0" />
<inertia ixx ="1e-7" ixy ="0" ixz ="0" iyy ="1e-7" iyz ="0" izz ="1e-7" />
</inertial >
<visual >
<origin xyz ="0 0 -0.0377" />
<geometry >
<mesh filename ="package://velodyne_description/meshes/VLP16_scan.dae" />
</geometry >
</visual >
</link >
<gazebo reference ="${name}" >
<sensor type ="ray" name ="${name}-VLP16" >
<pose > 0 0 0 0 0 0</pose >
<visualize > false</visualize >
<update_rate > ${hz}</update_rate >
<ray >
<scan >
<horizontal >
<samples > ${samples}</samples >
<resolution > 1</resolution >
<min_angle > ${min_angle}</min_angle >
<max_angle > ${max_angle}</max_angle >
</horizontal >
<vertical >
<samples > ${lasers}</samples >
<resolution > 1</resolution >
<min_angle > -${15.0*M_PI/180.0}</min_angle >
<max_angle > ${15.0*M_PI/180.0}</max_angle >
</vertical >
</scan >
<range >
<min > ${collision_range}</min >
<max > ${max_range}</max >
<resolution > 0.001</resolution >
</range >
<noise >
<type > gaussian</type >
<mean > 0.0</mean >
<stddev > ${noise}</stddev >
</noise >
</ray >
<plugin name ="gazebo_ros_laser_controller" filename ="libgazebo_ros_velodyne_laser.so" >
<topicName > ${topic}</topicName >
<frameName > ${name}</frameName >
<min_range > ${min_range}</min_range >
<max_range > ${max_range}</max_range >
<gaussianNoise > ${noise}</gaussianNoise >
</plugin >
</sensor >
</gazebo >
</xacro:macro >
</robot >
编译并重启
sudo apt update
sudo apt install ros-humble-velodyne-simulator
cd ~/lio_sam_gazebo_ros2
rm -rf build/velodyne_description install/velodyne_description
colcon build --symlink-install --packages-select velodyne_description
source install/setup.bash
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
ros2 launch robot_gazebo robot_sim.launch.py
最终检查:打开新终端
ros2 topic list
进入工作空间并刷新环境:
cd ~/lio_sam_gazebo_ros2
source install/setup.bash
启动仿真:
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
ros2 launch robot_gazebo robot_sim.launch.py
如果报错找不到包:请输入 ros2 launch 然后按两下 Tab 键,看看有哪些包是以 robot_ 或 neo_ 开头的,替换上面的命令。
**列出话题:**打开终端 1 没有 gazebo 窗口弹出的话,打开终端 4,输入以下命令
ros2 topic list
(确认有没有 /velodyne_points 或类似名字)
强制重新编译雷达插件:
清理旧文件:
cd ~/lio_sam_gazebo_ros2
rm -rf build/velodyne* install/velodyne*
重新编译:(这次我们只编译雷达相关包,速度会很快)
colcon build --symlink-install --packages-select velodyne_description velodyne_gazebo_plugins velodyne_simulator
检查点:观察编译输出。如果在编译 velodyne_gazebo_plugins 时没有报错,且显示 Finished,说明插件生成成功了。
再次刷新环境:
source install/setup.bash
再次启动仿真 (终端 1):
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
ros2 launch robot_gazebo robot_sim.launch.py
再次检查话题 (终端 4):
ros2 topic list
重要:检查有没有类似 /velodyne_points 的输出,或者出现 /gazebo_ros_laser_controller/out……
确定雷达点云名称
ros2 topic info /gazebo_ros_laser_controller/out
在终端 4 中输入以上命令,观察结果中 Type 这一行:
如果显示 Type: sensor_msgs/msg/PointCloud2
操作:直接去修改 LIO-SAM 的 params.yaml,把 pointCloudTopic 改成 /gazebo_ros_laser_controller/out,然后直接运行!
否则如果显示 Type: sensor_msgs/msg/LaserScan 或者其他
操作:请检查插件是否正确加载。
重新编译与验证
编译
cd ~/lio_sam_gazebo_ros2
colcon build --packages-select robot_gazebo --symlink-install
启动仿真
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
source install/setup.bash
ros2 launch robot_gazebo robot_sim.launch.py
3.3.2 启动 LIO-SAM 建图算法 (终端 2)
进入工作空间并刷新环境:
cd ~/lio_sam_gazebo_ros2
source install/setup.bash
启动算法:
ros2 launch lio_sam run.launch.py
成功标志:Rviz (可视化界面) 会自动弹出来。
3.3.3 控制机器人移动 (终端 3)
运行键盘控制节点 (这是一个通用的 ROS 工具):
ros2 run teleop_twist_keyboard teleop_twist_keyboard
(如果没有安装,运行 sudo apt install ros-humble-teleop-twist-keyboard 安装)
操作方法:
确保鼠标点击在这个终端窗口上(终端 3)。
按 i 前进,, 后退。
按 j 左转,l 右转。
按 k 停止。
注意: 速度不要太快(1 左右就差不多了),否则雷达匹配可能会跟丢。
终端没有输出显示是正常的,长按上述按键即可。
可以看见随机器人(小车)移动,Rviz 窗口点云图越来越完善。
3.3.4 点云图保存
保存地图:
回到 终端 2 (LIO-SAM)。
按下 Ctrl + C。
地图会自动保存到 ~/Downloads/LOAM/ (之前设置的路径)。
使用 CloudCompare 查看:
在终端输入 cloudcompare 打开软件。
VMware 显卡警告:如果弹出 'Color ramp shader…' 警告,直接点击 OK 或忽略,这不影响使用。
点击 Apply,即可看到 3D 点云地图。
左键旋转,右键平移,滚轮缩放
利用已有数据集进行仿真(无需启动 Gazebo)
4.1 安装转换
安装转换工具 (我们需要一个 Python 小工具):
pip install rosbags
(如果没有安装 pip,请先运行 sudo apt install python3-pip)
执行转换:(假设您的文件名叫 campus_small_dataset.bag,请替换为您真实的文件名)
python3 -m rosbags.convert --src campus_small_dateset.bag --dst campus_small_dataset_ros2
注意:需要在文件所在目录下运行
等待完成:转换完成后,当前目录下会多出一个新的文件夹,名字为上述输出文件夹名
4.2 文件修复
修复 metadata.yaml 文件并查找话题名:
寻找 topics_with_message_count 或者 topics 这一段。
点云话题 (Lidar):
寻找包含 points 或 velodyne 字样的名字。
通常是:/velodyne_points 或 /points_raw。
IMU 话题:
寻找包含 imu 字样的名字。
通常是:/imu/data 或 /imu_raw。
示例(您的文件长得可能像这样):
...topics_with_message_count:
- topic_metadata:
name: /velodyne_points
type: sensor_msgs/msg/PointCloud2
...
- topic_metadata:
name: /imu/data
type: sensor_msgs/msg/Imu
...
修改 LIO-SAM 配置
拿到上面两个名字后,修改 LIO-SAM 的配置文件:
gedit ~/lio_sam_gazebo_ros2/src/LIO-SAM-ros2/config/params.yaml
把 pointCloudTopic 改成您在文件里看到的点云名字。
把 imuTopic 改成您在文件里看到的 IMU 名字。
保存并关闭。
4.3 运行仿真(运行 LIO-SAM)
编译(确保配置生效):
cd ~/lio_sam_gazebo_ros2
colcon build --packages-select lio_sam --symlink-install
运行(终端 1):
source install/setup.bash
ros2 launch lio_sam run.launch.py
终端 2:
ros2 bag play campus_small_dataset_ros2
方法一与方法二之间的转换
5.1 方法 1:
5.1.1 获取点云及 IMU 话题名
启动仿真 (终端 1):
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
ros2 launch robot_gazebo robot_sim.launch.py
再次检查话题 (终端 4):
ros2 topic list
重要:检查有没有类似 /velodyne_points 的输出,或者出现 /gazebo_ros_laser_controller/out……
确定雷达点云名称
ros2 topic info /gazebo_ros_laser_controller/out
通过上述操作我确定的雷达点云名称为 /gazebo_ros_laser_controller/out,imu 名称为 /imu_plugin/out
在 params.yaml 文件中修改如下:
pointCloudTopic: "/gazebo_ros_laser_controller/out"
imuTopic: "/imu_plugin/out"
5.1.2 编译运行
重新编译
cd ~/lio_sam_gazebo_ros2
colcon build --symlink-install
启动仿真(终端 1)
export GAZEBO_MODEL_DATABASE_URI=""
export SVGA_VGPU10=0
export LIBGL_ALWAYS_SOFTWARE=1
source install/setup.bash
ros2 launch robot_gazebo robot_sim.launch.py
启动 Rviz(终端 2):
cd ~/lio_sam_gazebo_ros2
source install/setup.bash
ros2 launch lio_sam run.launch.py
运行键盘控制节点 (终端 3):
ros2 run teleop_twist_keyboard teleop_twist_keyboard
5.2 方法 2:
5.2.1 获取点云及 IMU 话题名
打开 metadata.yaml 文件查找话题名
gedit ~/campus_small_dataset_ros2/metadata.yaml
我查找到的雷达点云名称为 /points_raw,imu 名称为 /imu_correct
在 params.yaml 文件中修改如下:
pointCloudTopic: "/points_raw"
imuTopic: "/imu_correct"
5.2.2 编译运行
重新编译 (因为改了 yaml)
cd ~/lio_sam_gazebo_ros2
colcon build --packages-select lio_sam --symlink-install
启动 LIO-SAM(终端 1)
source install/setup.bash
ros2 launch lio_sam run.launch.py
播放数据(终端 2:)
ros2 bag play campus_small_dataset_ros2
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown 转 HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online