☰
从零搭建Gazebo仿真环境:基于Livox Mid360跑通FAST-LIO2全流程
2026/10/6 10:45:05 网站建设 项目流程

在真机上跑过 FAST-LIO2 的朋友,多少都经历过这样的场景:Mid360 昨天还好好的,今天一连上电就是点云断层;IMU 温度一漂,初始化飘出去几十米;想去楼下车库复现一个回环场景,结果真把车推下去绕了半小时,内存里还是只有一条走廊。硬件连接、设备校准、场地调度、环境光照……随便一环出问题,你就分不清到底是算法不行还是环境不行。这也是我后来把测试主战场从真机搬到 Gazebo 仿真的直接原因——我能在十分钟内重建一个可复现的测试环境,把 FAST-LIO2 的收敛性能、参数鲁棒性和坐标系设计都压到极限去验证,而不是把时间浪费在排查一根 USB 线缆上。

这篇文章我会完整记录从零搭建一套基于 Gazebo + ROS 的 Livox Mid360 仿真平台,并跑通 FAST-LIO2 的全过程。内容包括环境选型、Mid360 非重复扫描特性在 Gazebo 里的建模方法、launch 文件的适配技巧,以及我在调试中踩过的一批坑。文章默认读者了解 ROS 基础概念,但即便你是刚装完 Ubuntu 的新手,按步骤操作也能把平台跑起来。

1. 为什么我坚持用 Gazebo 做 FAST-LIO2 的验证场

很多人的第一个问题是:FAST-LIO2 官方不是自带 rosbag 数据吗?直接跑 bag 不香吗?香,但不够。bag 只能帮你验证算法能不能跑通,不能帮你验证算法面对“新场景”时的行为。你没法改变 bag 里的场景布局,没法换反射率的墙面,没法让机器人走另一条轨迹。而仿真平台的意义恰恰在于——可重复、可参数化、可边界化。

1.1 仿真要解决的真问题,不只是“省一台雷达”

先说一个很实际的计算成本。一块 Mid360 在 2024 年左右的流通价格还要小几千,加上配套的 IMU 和工控机,一套完整的 FAST-LIO2 真机测试平台没有一万下不来。Gazebo 用纯软件模拟,零硬件损耗,还能同时开多个机器人实例做多机联调——这在真机环境里是极其麻烦的事。

更重要的是可重复性。FAST-LIO2 是紧耦合的激光惯性里程计,它的初始化非常依赖 IMU 激励。真机上你把小车放在原地不动,点云和 IMU 都在输出,但算法死活不收敛,那是因为重力对齐之后缺乏足够的加速度激励去收敛偏置。仿真里我可以设计一条正弦摆动轨迹,保证初始化阶段 IMU 被充分激励;也可以故意从静止开始,观察算法在这种缺陷激励下的退化表现。这种“可控变量”的能力,是回放 rosbag 给不了的。

1.2 仿真平台的核心组成:雷达模型、IMU 模型、环境模型

在 Gazebo 里搭建这整套系统,本质上是三块拼图:

  • 传感器仿真:用 Gazebo 插件模拟 Mid360 的非重复扫描点云,同时输出 IMU 数据
  • 机器人模型:用 URDF 把激光雷达、IMU、机体底盘组合成带正确坐标系变换的刚体
  • 环境模型:用 sdf/world 文件构造室内或室外场景,供激光雷达产生有价值的点云特征

我见过不少人在这一步翻车:雷达模型挂上了,仿真里点云却是一个平面圆盘,没有任何特征,FAST-LIO2 根本起不来。原因是 Gazebo 默认的射线传感器是均匀扫描的旋转雷达模型,跟 Livox 的非重复扫描模式有本质区别。后面我会专门讲怎么用官方插件解决这个问题。

2. 版本组合与安装细节:Ubuntu 20.04 下的 Noetic + Gazebo 11

版本选型是仿真平台搭建里决定成败的一步,也是新手最容易忽略的一步。我在这个平台上线前先花了两天测试不同的 ros/gazebo 组合,最终选择了 Ubuntu 20.04 + ROS Noetic + Gazebo 11。

2.1 为什么没有选 Ubuntu 22.04 和 ROS 2

如果你关注过 FAST-LIO2 的仓库,会发现官方主分支明确支持 ROS 1(Melodic/Noetic),ROS 2 版本分散在 livox_ros_driver2 和 FAST-LIO2 的适配分支里,维护状态并不一致。FAST-LIO2 依赖 livox_ros_driver 的消息类型livox_ros_driver/CustomMsg,这套消息在 ROS 1 生态里集成最顺畅,官方 launch 文件也是按 ROS 1 写的。

Ubuntu 22.04 只能装 ROS 2 Humble 或者自己编译 Noetic 源码,后者在 Gazebo 插件编译时容易遇到 OpenGL 和 Qt 的兼容问题。Gazebo 11 在 Ubuntu 20.04 上是一等公民,直接随 Noetic 安装,不需要单独处理 Gazebo Garden 那套新架构。所以我的结论很直接:为了跑通流程,老老实实用 Noetic。

提示:如果你已经装了 Ubuntu 22.04,并且不想重装系统,可以用 Docker 跑一个 noetic 容器,把 Gazebo GUI 通过 X11 转发出来。但这样性能损耗明显,而且 3D 渲染偶尔会闪退。有条件还是建议单独装一台 Ubuntu 20.04 的机器或虚拟机。

2.2 安装全流程笔记

核心安装过程分几步,这里给出我实际操作过的一整套命令。国内用户如果 apt 源慢,记得先换阿里云或清华源。

# 1. 更新系统源 sudo apt update && sudo apt upgrade -y # 2. 设置 ROS 源(清华镜像示例) sudo sh -c 'echo "deb https://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 # 3. 安装 ROS Noetic 完整版 sudo apt update sudo apt install ros-noetic-desktop-full -y # 4. 初始化 rosdep sudo rosdep init rosdep update # 5. 配置环境变量 echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrc

装完后 Gazebo 11 会随desktop-full一起安装,验证一下:

gazebo --version # 应该输出 Gazebo multi-robot simulator, version 11.x.x

除此之外,还需要安装编译 FAST-LIO2 所需的依赖库:

sudo apt install ros-noetic-pcl-ros ros-noetic-cv-bridge ros-noetic-image-transport -y sudo apt install libeigen3-dev libyaml-cpp-dev -y # Sophus 需要从源码编译 cd ~ git clone https://github.com/strasdat/Sophus.git cd Sophus mkdir build && cd build cmake .. make -j$(nproc) sudo make install

关于“鱼香ROS一键安装”,我也试过,它确实能省很多事,尤其是装 ROS 本体时不用手动折腾 GPG key。但在模块化工作区管理和 Gazebo 插件编译上,一键安装脚本不会帮你处理,所以我更推荐手动装一遍,至少踩过一次坑后你知道每一步在干嘛。

3. Mid360 仿真建模的关键一步:非重复扫描插件

这是整个平台最核心的技术点。如果你只是想在 Gazebo 里挂一个“看起来像激光雷达”的东西,用<sensor type="gpu_ray">就够了。但如果你要跑 FAST-LIO2,这个做法基本等于自杀——算法看到的是均匀旋转扫描的数据,和真实 Livox 的扫描几何模式完全对不上,退化、飘移是必然的。

3.1 为什么不能用默认 Ray Sensor 替代 Mid360

Livox Mid360 的硬件设计是棱镜式非重复扫描,点云分布不是一条条均匀扫描线,而是在视场内形成花瓣状的高密度覆盖,时间累计后视场覆盖率逐渐趋于均匀。这个特性对 SLAM 算法有一个直接影响:特征提取时,非重复扫描对边缘和平面的采样密度更高,能更稳定地提取角点。而 Gazebo 默认的 ray sensor 模拟的是旋转式机械雷达,线束均匀分布,点云特征形态完全不同。

在 FAST-LIO2 的算法流程里,LivoxLidar特征提取环节专门对 Livox 点云格式做了适配,包括使用point_num做循环优化。如果喂进来的点云不是 Livox 格式,虽然理论上也能跑,但你在代码里需要额外转换,效率也会受影响。

3.2 使用官方 livox_laser_simulation 插件

Livox 官方在 GitHub 上开源了一个仿真插件livox_laser_simulation,专门解决这个问题。它实现了自定义 Gazebo 插件,根据棱镜旋转模型实时生成非重复扫描点云,并直接以livox_ros_driver/CustomMsg消息格式发布到 ROS 话题。这意味着从仿真节点出去的 Topic,格式和真机 Livox 驱动输出完全一致,FAST-LIO2 不需要任何改动就能订阅。

安装过程如下:

cd ~/catkin_ws/src git clone https://github.com/Livox-SDK/livox_laser_simulation.git cd ~/catkin_ws catkin_make source ~/catkin_ws/devel/setup.bash

注意:编译这个插件需要确认你的 Gazebo 版本对应的开发库已安装。Noetic 自带的 Gazebo 11 对应的是libgazebo11-dev:

sudo apt install libgazebo11-dev

3.3 在 URDF 里挂载 Mid360 模型与 IMU

有了插件,就要把它挂到机器人的 URDF 上。URDF 文件是机器人模型的“骨骼”,规定了每个部件的质量、惯量、坐标系和传感器挂载位置。下面是关键部分:

<!-- 底盘 link,作为承载基座 --> <link name="base_link"> <visual> <geometry><box size="0.3 0.2 0.08"/></geometry> <origin rpy="0 0 0" xyz="0 0 0"/> <material name="gray"/> </visual> <collision> <geometry><box size="0.3 0.2 0.08"/></geometry> </collision> <inertial> <mass value="4.0"/> <inertia ixx="0.05" ixy="0" ixz="0" iyy="0.05" iyz="0" izz="0.08"/> </inertial> </link> <!-- Mid360 传感器 link --> <link name="livox_frame"> <visual> <origin rpy="0 0 0" xyz="0 0 0.1"/> <geometry><mesh filename="package://livox_description/meshes/mid360.dae" scale="0.001 0.001 0.001"/></geometry> </visual> </link> <joint name="livox_joint" type="fixed"> <parent link="base_link"/> <child link="livox_frame"/> <origin rpy="0 0 0" xyz="0 0 0.1"/> </joint>

然后是 Gazebo 插件部分。这里我配置了 Livox 激光雷达插件和 IMU 插件:

<gazebo reference="livox_frame"> <plugin name="livox_laser" filename="liblivox_laser_simulation.so"> <topicName>/livox/lidar</topicName> <frameId>livox_frame</frameId> <updateRate>10</updateRate> <pointCloudTopic>/livox/lidar/pointcloud</pointCloudTopic> <laserCloudTopic>/livox/lidar</laserCloudTopic> <imuTopic>/livox/imu</imuTopic> <modelType>mid360</modelType> </plugin> </gazebo> <gazebo reference="base_link"> <plugin name="imu_plugin" filename="libgazebo_ros_imu.so"> <topicName>/livox/imu</topicName> <frameName>livox_frame</frameName> <updateRate>200</updateRate> <noiseDensity>0.0002</noiseDensity> <randomWalk>0.00005</randomWalk> <alwaysOn>true</alwaysOn> </plugin> </gazebo>

这里的参数有几个讲究:

  • updateRate:Mid360 真机的出图频率是 10Hz,IMU 频率是 200Hz。FAST-LIO2 内部的状态估计器需要 IMU 高频数据来做传播,点云低频做更新。我用 10Hz 雷达 + 200Hz IMU 是仿真环境里比较接近真机的配置。
  • noiseDensity和randomWalk:这两个参数控制 IMU 噪声。仿真环境默认什么都不加时 IMU 是理想传感器,这会让 FAST-LIO2 的状态估计变得过于乐观,掩盖真实场景里的退化问题。加上高斯噪声后更接近实战。
  • modelType:livox_laser_simulation插件支持 mid40、mid70、horizon 等多种型号,mid360 是需要手动指定的参数。

注意:不同版本的 livox_laser_simulation 插件,XML 标签名可能不同。比如某些分支使用<topicName>,另一些分支使用<laserTopic>。编译前先打开源码里的livox_laser_simulation_plugin.cc确认标签定义,避免 launch 后 topic 没输出还以为插件没编译成功。

4. 构建测试世界,并让仿真小车动起来

传感器模型挂好了,接下来需要把车放进一个能让激光雷达“看到东西”的环境里。FAST-LIO2 在空旷房间和结构化走廊里的表现差异巨大,所以测试场景的设计要贴近你想验证的问题。

4.1 搭建一个带纹理特征的室内场景

Gazebo 的 world 文件可以用图形界面搭建,也可以直接手写 SDF。我的做法是先用 Gazebo 图形界面摆上几面墙、几个货架模型,然后把 world 保存下来再手工调整位置。以下是一个简化版的 world 文件结构:

<sdf version="1.6"> <world name="test_livox"> <include> <uri>model://sun</uri> </include> <include> <uri>model://ground_plane</uri> </include> <include> <uri>model://construction_barrier</uri> <name>barrier_1</name> <pose>1.0 0.5 0 0 0 1.57</pose> </include> <include> <uri>model://cafe_table</uri> <name>table_1</name> <pose>2.0 -1.0 0 0 0 0</pose> </include> <include> <uri>model://bookshelf</uri> <name>shelf_1</name> <pose>-1.5 1.0 0 0 0 0.5</pose> </include> </world> </sdf>

在实际测试中我体会很深的一点是:仿真环境的点云特征密度直接影响 FAST-LIO2 的初始化质量。如果你只放一个 ground_plane 和四面墙,算法能收敛,但退化方向(比如沿走廊方向)的表现不明显。为了测试退化场景,我会故意构造一条长走廊,观察算法在纯几何重复场景下的行为。这个可控性是仿真平台最有价值的地方。

4.2 把 URDF 机器人模型写进 launch 文件

场景有了,机器人怎么放进去?答案是写一个 launch 文件,同时拉起 Gazebo、URDF 模型和 ROS 控制节点:

<launch> <!-- 加载机器人 URDF --> <param name="robot_description" textfile="$(find livox_description)/urdf/mid360_robot.urdf"/> <!-- 启动 Gazebo 并加载世界 --> <include file="$(find gazebo_ros)/launch/empty_world.launch"> <arg name="world_name" value="$(find livox_sim_worlds)/worlds/test_livox.world"/> <arg name="paused" value="false"/> <arg name="gui" value="true"/> </include> <!-- 在 Gazebo 中生成机器人 --> <node name="spawn_urdf" pkg="gazebo_ros" type="spawn_model" respawn="false" args="-param robot_description -urdf -model mid360_robot -x 0.0 -y 0.0 -z 0.1"/> </launch>

启动后,你可以先在终端里确认话题是否正常发布:

rostopic list | grep livox # 应该看到 /livox/lidar 和 /livox/imu

4.3 让小车动起来:遥操作与轨迹控制

FAST-LIO2 要求 IMU 在初始化阶段有足够的激励。如果你启动后把车停在原地,雷达点云可能在 Rviz 里一直显示不出来,那不是传感器坏了,而是算法在等待 IMU 对齐。此时你需要让机器人运动起来。

最简单的方案是用teleop_twist_keyboard:

sudo apt install ros-noetic-teleop-twist-keyboard rosrun teleop_twist_keyboard teleop_twist_keyboard.py

然后在终端里按 i、j、k、l 控制机器人前进后退和转弯。实测下来,z轴方向的小幅摆动对 IMU 初始化帮助最大,建议先做一个“原地左右晃 20 秒”的动作,再开始前进。

如果你想要更可控的轨迹复现,可以用gazebo_ros的set_model_state服务发送预设轨迹。我写过一个 Python 脚本,按贝塞尔曲线路径移动机器人,用来重复测试同一条轨迹下不同参数组合的表现。这个方法比键盘遥操作更可控,尤其适合批量跑实验。

5. FAST-LIO2 适配仿真环境:launch 文件与主题对接

Gazebo 和 URDF 都齐了,接下来就是重头戏:让 FAST-LIO2 在仿真数据上跑起来。这个环节最常见的坑是话题类型不匹配和时间戳问题,我会把每一步怎么改拆开细说。

5.1 编译 FAST-LIO2 的依赖准备

先在工作空间里拉代码:

cd ~/catkin_ws/src git clone https://github.com/hku-mars/FAST_LIO.git cd ~/catkin_ws catkin_make

如果编译报错提示找不到sophus/so3.hpp,大概率是 Sophus 没有正确安装到系统路径,或者版本不对。FAST-LIO2 要求 Sophus 使用fit3模板版本,官方主线代码已经兼容,直接sudo make install就行。

还有一个高频报错是fatal error: pcl_conversions/pcl_conversions.h: No such file or directory,这个只要装上ros-noetic-pcl-ros就可以解决,不需要手动编译 PCL。

5.2 修改 launch 文件适配仿真话题

FAST-LIO2 自带的 launch 文件默认启动livox_ros_driver作为数据源,但仿真环境下,Livox 数据已经由 Gazebo 插件直接发布,不需要再启动驱动节点。直接照搬原 launch 会导致端口冲突和时间源混乱。

以下是我修改后的 launch 文件核心部分:

<launch> <node pkg="fast_lio" type="fastlio_mapping" name="fastlio_mapping" output="screen"> <rosparam file="$(find fast_lio)/config/livox_mid360.yaml" /> <param name="common/lid_topic" value="/livox/lidar"/> <param name="common/imu_topic" value="/livox/imu"/> </node> </launch>

注意我完全没有启动livox_ros_driver节点,而是直接把话题指向 Gazebo 插件生成的/livox/lidar。

5.3 坐标系的坑:从 base_link 到 livox_frame 的转换

FAST-LIO2 的配置文件中,body_T_lidar和body_T_imu矩阵定义了传感器在机器人本体坐标系下的外参。如果你的 URDF 里livox_frame相对于base_link有平移或旋转,但配置文件中还保持默认值,算法输出的点云地图会整体偏斜。

检查坐标系最直接的方式是在 Rviz 里拉一个 TF 树,查看base_link -> livox_frame的关系是否与你配置的一致。我在 URDF 里把雷达放在z=0.1m的位置,所以配置文件里body_T_lidar的平移部分就要写0.0 0.0 0.1。IMU 同理。

经验:仿真环境下最容易犯的错误不是外参写错,而是写错了还不自知。真机上你还能通过把雷达对着墙测距离来校准,仿真里没人帮你验证。我后来专门写了一个脚本发布一个方向向量,在 Rviz 里投影对比 TF 和点云的位置,确保坐标系对齐。

5.4 在线验证点云及时序

启动 FAST-LIO2 后,第一步不是看地图,而是检查点云时间戳:

rostopic echo /livox/lidar/header/stamp

如果时间戳全部为 0 或者跳变很大,FAST-LIO2 的 IMU 传播和 LIDAR 更新会错位,地图会扭曲甚至直接挂掉。Gazebo 插件默认发布的是仿真时间,需要确保以下环境变量正确:

export ROS_MASTER_URI=http://localhost:11311 export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:~/catkin_ws/src

如果使用的是empty_world.launch,仿真时钟默认启用,/clock话题存在,问题不大。比较隐蔽的坑是你在外部手动rosbag record后再播放,bag 里的时钟源和 Gazebo 的仿真时钟冲突,导致时间轴混乱。别问我怎么知道的。

6. 调试中最常踩的五个问题,以及我的排查链路

这一节把所有我在调试过程中遇到的问题横着列一遍,包括症状、原因、定位方法和修复操作。内容比较杂,但每条都是实战总结。

6.1 雷达话题有数据,但 FAST-LIO2 不输出里程计

这是仿真平台刚搭好时最容易碰到的问题。

  • 症状:rostopic echo /livox/lidar有数据,IMU 有数据,但 FAST-LIO2 的/Odometry话题没有输出。
  • 大概率原因:坐标变换不一致导致初始里程计发散,代码抛出“Translation variance too large”之类的异常后自行退避。
  • 定位方法:查看 FAST-LIO2 进程的完整日志,找到std::cout输出的点云数量,确认点云确实被正确处理。
  • 修复:把配置文件里common/lid_topic和common/imu_topic改成仿真实际发布的话题名,并把外参矩阵改成和 URDF 一致。

6.2 点云在 Rviz 里显示为一条线而不是一片

  • 症状:PointCloud2可视化时只有一行点,没有形成雷达扫描面。
  • 原因:livox_laser_simulation 插件的非重复扫描本质上是随时间累积的,如果你只显示当前帧的原始点云,它看起来确实像一条线或几片花瓣。这其实是“正确”的行为。
  • 解决:不用改任何东西。确认 FAST-LIO2 输出的是累积后的地图cloud_registered,而不是原始点云。在 Rviz 里选择/cloud_registered话题显示就行。

6.3 IMU 数据频率太低导致初始化失败

  • 症状:启动后 FAST-LIO2 一直停在“wait for imu”阶段,或者初始化后很快漂移。
  • 原因:Gazebo 里 IMU 插件的updateRate设成 10Hz 甚至更低,但 FAST-LIO2 期望的 IMU 频率在 100Hz 以上。IMU 频率太低时,状态传播阶段的积分误差迅速积累。
  • 修复:把 IMU 插件的updateRate改成 200,并确认rostopic hz /livox/imu输出频率在 190~210 之间。

6.4 仿真环境下点云对墙面的“穿透”问题

  • 症状:机器人靠近墙面时,地图上出现墙面背后的几何噪点。
  • 原因:Gazebo 的自带碰撞检测和传感器射线在某些边界条件下会漏检,尤其是模型合并了多个 mesh 时。
  • 解决:把墙体和障碍物模型从mesh改为简单几何体(box/cylinder),射线检测精度会显著提升。视觉上略粗糙,但对 SLAM 仿真足够。

6.5 多机同时仿真时的 TF 冲突

  • 症状:开了两个机器人实例,第二个机器人的 TF 树和第一个冲突,地图错乱。
  • 原因:每个机器人默认都叫base_link和livox_frame,同一个 ROS 图上名冲突。
  • 解决:给每个机器人的坐标系加命名空间前缀,或者在 launch 里设置tf_prefix。FAST-LIO2 的订阅话题也要对应改成各自的/robot1/livox/lidar。

提示:在做的过程中,建议全过程用一个脚本记录所有关键参数,包括 URDF 外参、IMU 噪声、雷达频率、场景模型坐标。这样每次改动后如果算法行为有变化,能快速定位是哪一项参数引起的变化。批量实验时这几乎是必备的。

最后一点实操心得

这篇文章从头到尾记录了我在 Gazebo 里复现 FAST-LIO2 测试环境的完整思路。如果说有什么是比步骤本身更值得你带走的,那就是:仿真平台不是“真机的替代品”,而是一个让你能精确控制变量的实验装置。真机上你无法让同一堵墙的反射率改变两次,但仿真里一句话就能做到;真机上你无法让 IMU 在一条轨迹上产生完全一致的噪声序列,但仿真可以。

在多次反复调试之后,我的日常流程已经变成:先在仿真里快速验证算法和参数可行性,再带着这个基准去真机做对标。我自己在实际操作中最大的收益并不是省了多少硬件费用,而是建立了一套能随时重建、随时修改的实验闭环。希望这套流程能帮你省下我当初走过的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询