这套环境组合我折腾了整整三周才跑通,中间一度想放弃直接用真机。如果你也需要在XTDrone仿真环境里加入Livox Mid-360激光雷达模型,并且打算跑Faster-LIO做里程计或SLAM验证,这篇文章就是照着做就能少走弯路的完整记录。我会从XTDrone环境准备、Mid-360的Gazebo模型构建,到Faster-LIO的参数适配和联调,把每一步的原理和实际操作都讲清楚,最后还会列出我在调试中遇到的高频坑和排查思路。
1. 为什么要在XTDrone里"造"一个Mid-360出来
先说说我这么干的动机。Mid-360这颗雷达的价格虽然比几年前亲民了一些,但你要同时买好几台做多机协同或者长时间老化测试,成本依然不低。更麻烦的是,在算法开发阶段,你不可能每次都扛着设备到户外跑真机,室内走廊、狭窄楼道这类场景倒是可以,但只要天气不对、光线不对,数据质量波动就很大。仿真环境的价值在于它能让你在完全可控的条件下反复验证算法逻辑,先把定位和建图的"功能正确性"跑通,再上真机处理"物理世界噪声"。
1.1 XTDrone能给你的和不能给你的
XTDrone是一个非常完整的无人机/机器人仿真平台,它不是单一一个Gazebo模型,而是把ROS、Gazebo、PX4固件、MAVROS、QGroundControl整合到了一套环境里。你拿到手之后,不用再自己去拼凑MAVROS和PX4的通信链路,也不用担心SDF模型和ROS插件的兼容性问题。
但XTDrone默认提供的传感器模型里,确实没有Livox Mid-360。它主要偏向PX4无人机常用的视觉传感器、普通单线/多线雷达和IMU。这也是为什么需要在它上面做"二次开发"——把Mid-360的模型插进XTDrone的体系里。XTDrone能给你的是环境基底和通信框架,Mid-360的建模、点云生成、话题对接,这些都得自己搞定。
1.2 Mid-360的非重复扫描特性是建模的核心难点
这里必须先讲清楚一个关键点:Livox Mid-360不是传统意义上的机械式360度雷达,它没有整圈旋转的电机,而是通过棱镜反射实现扫描。它最特别的是非重复扫描特性。传统机械雷达的扫描线是固定的、每帧都重复的,但Mid-360在积分时间很短时,点云会呈现类似花瓣状的分布,随着时间的推移,扫描区域会逐渐覆盖整个视场。
这个特性带来两个直接影响:
- 点云在短时内的分布不均匀,边缘区域点比较少,中心附近略多
- 不能简单用"线束扫描"算法直接处理它的点云,Faster-LIO这类算法专门做了适配
如果你在Gazebo里只是给模型挂一个gpu_laser插件,然后让它输出一圈均匀射线,那出来的效果和真实的Mid-360差异会很大。你在仿真里调通的参数,拿到真机上很可能完全不可用。所以建模的关键不是"看起来像一颗雷达",而是要尽量模拟非重复扫描的分布特征。
1.3 适配Faster-LIO的收益与边界
Faster-LIO是2022年前后提出的激光惯性里程计算法,它和FAST-LIO2最大的区别在于状态更新方式。FAST-LIO2用迭代卡尔曼滤波(IEKF)做状态估计,而Faster-LIO改用迭代线性二次调节器(iLQR)的思想,收敛速度明显更快,在CPU较弱的平台上也能跑出不错的频率。
在仿真里适配Faster-LIO的意义在于:你可以先把整个算法链路验证通,确认点云预处理、畸变补偿、外参配置这些逻辑没问题,再上真机。但必须清醒地认识到,仿真的点云太干净了,没有运动畸变以外的噪声,也没有反射率缺失、边缘断裂、动态物体拖影这些问题。所以仿真适合验证"算法逻辑",不适合验证"鲁棒性"。
2. 环境搭建与版本选型
很多人在第一步就栽了跟头,因为XTDrone对系统版本有比较严格的要求,不是一个git clone就能跑起来的。
2.1 系统版本:Ubuntu 20.04是稳妥选择
XTDrone官方推荐的是Ubuntu 20.04 + ROS Noetic + Gazebo 11。我强烈建议你在这个组合上操作,而不是用Ubuntu 22.04。原因很简单:XTDrone的脚本和PX4固件的编译链对接的是Noetic;Gazebo 11是ROS Noetic时代的标准版本,插件接口成熟;Faster-LIO的依赖(livox_ros_driver)对ROS1的支持也是Noetic最省心。
Ubuntu 22.04要装ROS1 Noetic不是不行,但需要手动添加ROS源、处理Python版本冲突,而且Gazebo 11在22.04上安装时容易碰到系统库依赖问题。你要是不想为了环境浪费一个周末,直接装20.04。
装好系统后,先安装ROS Noetic:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-full sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential sudo rosdep init rosdep update安装完成后,千万别忘了初始化ROS环境:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrcGazebo会随着ros-noetic-desktop-full自动安装,所以不需要单独装Gazebo。
2.2 XTDrone安装流程与验证
XTDrone的环境搭建分为几个层次:主仓库代码、ROS工作空间依赖(mavros等)、PX4固件。
首先是主仓库:
git clone https://github.com/robin-shaun/XTDrone.git在XTDrone目录下会有针对不同系统的配置脚本,你先执行脚本完成基础配置,它会帮你安装mavros、gazebo插件依赖这些内容。如果脚本中途报错,不要马上重跑,先看报错信息,多半是缺了某个apt包。
接着是配置MAVROS的串口和链接方式。XTDrone通常通过MAVROS连接PX4 SITL仿真。你需要把MAVROS的配置指向本机的UDP端口,这一块XTDrone文档里写得很细,核心是在~/catkin_ws里编译mavros,并设置/mavros/px4/的参数。
最后是PX4固件:
cd ~ git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot make px4_sitl gazebo这一步会编译PX4的SITL(Software In The Loop)版本,编译时间取决于机器性能,通常20分钟到40分钟。编译完成后,可以用make px4_sitl gazebo拉起一个默认的无人机Gazebo仿真环境,验证PX4和Gazebo是否通信正常。
2.3 Gazebo界面闪烁的一定要处理的关键问题
不少人在运行XTDrone的时候遇到Gazebo界面一直在闪的情况,这是搜索热词里出现频率很高的一个问题,我也踩过。
界面闪烁的原因通常不是Gazebo本身,而是渲染引擎和显卡驱动不匹配。Gazebo 11默认使用OGRE 1.x渲染引擎,在部分NVIDIA卡上,如果驱动版本太新或太老,都会出现画面闪烁、纹理撕裂的现象。
解决思路从简单到复杂:
- 先检查显卡驱动是否正常安装:
nvidia-smi glxinfo | grep "OpenGL renderer"- 如果是虚拟机(VMware或VirtualBox),那基本是OpenGL硬件加速的问题。此时启动Gazebo前设置:
export LIBGL_ALWAYS_SOFTWARE=1软件渲染能解决闪烁问题,但画面帧率会明显下降。如果你只是做后台仿真、不需要盯着界面看,这是性价比最高的方案。
如果是物理机,优先调整NVIDIA X Server Settings中的"OpenGL Settings",开启"Sync to VBlank",把"Allow Flipping"关闭。这一步能解决大部分垂直同步导致的闪烁。
终极方案是切换到Gazebo的替代渲染后端
ogre2,但这是个费时费力的操作,XTDrone的模型基本都是为OGRE1优化的,切到ogre2后很多模型的材质会异常,所以不建议在XTDrone环境里贸然切换。
3. 在Gazebo中创建Livox Mid-360雷达模型
这部分是整个工作的核心。Lightweight、可通信、点云特征接近真实,这是三个必须同时满足的要求。
3.1 模型文件结构设计
我建议把你的自定义雷达模型放到一个独立的ROS包里,而不是直接塞进XTDrone原有的模型目录。这样方便版本管理,也不影响XTDrone的升级更新。
我的目录结构是这样的:
livox_mid360_gazebo/ ├── CMakeLists.txt ├── package.xml ├── models/ │ └── mid360/ │ ├── model.config │ ├── mid360.sdf │ └── materials/ │ └── textures/ ├── urdf/ │ └── mid360.urdf ├── launch/ │ └── spawn_mid360.launch └── config/ └── mid360_gazebo.yaml简单的做法是直接用URDF描述雷达外观和传感器参数,通过spawn_urdf放进Gazebo里挂到飞行器或机器人的base_link下。模型文件里的外形可以用一个简单的圆柱体代替,重点是插件配置。
3.2 雷达参数与gpu_laser插件配置
先用MID-360的真实公开参数来对照建模。
| 参数 | 值 |
|---|---|
| 水平视场角 | 360度 |
| 垂直视场角 | 59度(-7度到52度) |
| 测距量程 | 40米(10%反射率) |
| 测距精度 | 2cm(1sigma @ 10m) |
| 点频 | 200000点/秒 |
| 帧率 | 10Hz |
| 盲区 | 0.1m |
| 重量 | 约265g |
在Gazebo里,最接近激光雷达模拟的插件是libgazebo_ros_laser.so或者libgazebo_ros_gpu_laser.so。前者是CPU射线检测,对每条射线做物理碰撞检测,计算量大而且精度一般;后者使用GPU加速,在雷达点数较大时性能明显更好。我这里选择gpu_laser。
SDF文件里的插件配置:
<plugin name="gpu_laser" filename="libgazebo_ros_gpu_laser.so"> <topicName>/livox/lidar</topicName> <frameName>livox_frame</frameName> <min_angle>-3.110177</min_angle> <max_angle>3.110177</max_angle> <samples>360</samples> <min_range>0.1</min_range> <max_range>40.0</max_range> <horizontal_fov>6.283185</horizontal_fov> <update_rate>10.0</update_rate> </plugin>这里的horizontal_fov是弧度值,6.283185对应360度;垂直方向通过min_angle和max_angle控制在-3.11到3.11弧度之间。但要注意,gpu_laser本身只能模拟在一个平面上一圈射线,模拟不了59度的垂直视场角。要模拟垂直视场,更合理的方式是用一个垂直方向小角度多线模拟。
但GPT的常见做法是用gazebo_ros_block_laser插件,它可以配置多条水平扫描线来构成一个垂直视场。比如设置scan_line = 6,然后在每条垂直线上有一定的角度增量,这样就能模拟出Mid-360的大致垂直覆盖范围。
3.3 非重复扫描特性的近似模拟
这是仿真和真实差异最大、但也最需要做的一步。真实的Mid-360非重复扫描会让点云在短时间内呈现不均匀分布,而Gazebo的激光插件每一帧输出的点都是均匀角度分布的。如果你完全接受这种均匀分布,那Faster-LIO在仿真里很可能工作得很好,但这个好效果是"假阳性"——因为算法在真实数据上要处理的非均匀分布完全没被模拟到。
我的做法是用一个ROS节点在回调点云数据时做动态角度采样。具体思路是:保持gpu_laser每隔一帧输出一次完整扫描,然后在PointCloud2回调里,根据一个随仿真时间变化的伪随机函数,对点云做下采样。这个伪随机函数的种子与时间戳绑定,保证相邻两帧的采样位置不重复,模拟出"花瓣覆盖"的效果。
核心代码逻辑:
import numpy as np import rospy from sensor_msgs.msg import PointCloud2, PointField def random_sample_points(cloud_msg, timestamp): # 将PointCloud2转成numpy数组 points = cloud_to_numpy(cloud_msg) # 利用时间戳生成伪随机掩码,模拟非重复扫描 rng = np.random.default_rng(int(timestamp.to_sec() * 1000)) mask = rng.uniform(0, 1, size=len(points)) < 0.4 return points[mask]这里的0.4是采样保留比例,你可以根据自己的需要调整。比例越小,点云越稀疏,Faster-LIO的处理难度越高,但这更接近真实雷达在短积分时间下的表现。
3.4 坐标系与话题配置
坐标系是仿真和算法对接时最容易出问题的地方。在Gazebo模型里,你定义的雷达link名称是什么,输出的frame_id就是什么。我建议统一用livox_frame,方便后续在Faster-LIO里配置。
在你的URDF里加上:
<link name="livox_frame"> <visual> <geometry> <box size="0.06 0.06 0.06"/> </geometry> </visual> <inertial> <mass value="0.265"/> <inertia ixx="0.0001" iyy="0.0001" izz="0.0001" ixy="0" ixz="0" iyz="0"/> </inertial> </link> <joint name="livox_joint" type="fixed"> <parent link="base_link"/> <child link="livox_frame"/> <origin xyz="0 0 0.3" rpy="0 0 0"/> </joint>如果你把雷达放在无人机顶部,就需要根据实际情况设置xyz和rpy。这里的rpy应该和Faster-LIO里配置的外参一致,不然算法输出的点云会错位。
话题名称建议设置成/livox/lidar和/livox/imu。虽然Faster-LIO允许自定义话题名,但保持和Livox真机驱动默认一致,能让你以后从仿真切换到真机时少改很多配置。IMU话题在仿真里可以直接用PX4或者机器人模型的IMU数据,把它重映射到/livox/imu就行。
4. Faster-LIO的编译、参数配置与联调
环境搭好后,真正的重头戏是Faster-LIO的适配。Faster-LIO原本设计的输入是Livox的真实驱动话题,但它的接口足够通用,只要话题类型和点云格式匹配,就能在Gazebo仿真数据上运行。
4.1 编译准备:依赖与源码
我推荐在XTDrone的ROS工作空间里单独clone一个Faster-LIO源码包,而不是直接塞进XTDrone的源码树里。我建了一个~/livox_ws,专门放和雷达有关的代码。
mkdir -p ~/livox_ws/src cd ~/livox_ws/src git clone https://github.com/ISP-C-Lab/Faster-LIO.git cd .. catkin_make source devel/setup.bash编译依赖主要是Eigen、PCL和livox_ros_driver。如果catkin_make报错找不到livox_ros_driver的package,说明还没安装这个驱动包。Faster-LIO在ROS1下需要你同时构建livox_ros_driver:
cd ~/livox_ws/src git clone https://github.com/Livox-SDK/livox_ros_driver.git cd .. catkin_make编译时要注意Eigen版本,Ubuntu 20.04自带的Eigen3是3.3.7,Faster-LIO官方要求至少3.3.4,一般没问题。PCL在Noetic里默认是1.10,兼容性也OK。
4.2 参数文件适配:话题、外参与雷达类型
Faster-LIO的配置主要在config目录下的yaml文件里。你新建一个mid360.yaml,参考库里已有的avia.yaml或livox.yaml来改。
common: lid_topic: "/livox/lidar" imu_topic: "/livox/imu" time_sync_en: false preprocess: lidar_type: 1 # 1代表Livox scan_line: 6 blind: 0.1 point_filter_num: 4 mapping: acc_cov: 0.1 gyr_cov: 0.1 b_acc_cov: 0.0001 b_gyr_cov: 0.0001 fov_degree: 360 extrin_T: [0.0, 0.0, 0.3] extrin_R: [1, 0, 0, 0, 1, 0, 0, 0, 1]几个关键参数:
lidar_type: 1代表Livox,这在Faster-LIO里必须设置对,否则点云预处理会走错分支blind: 盲区0.1m,和Mid-360真实盲区一致,范围内的点会被过滤掉point_filter_num: 每几个点抽一个,默认4,仿真数据比较干净,可以设成2,增加参与计算的点的数量extrin_T: IMU到雷达的平移外参,单位米。你要和URDF里雷达的安装位置对齐。这里从base_link到雷达是z轴0.3m,那么IMU到雷达的外参也应该是[0,0,0.3](前提是IMU本身就在base_link位置)extrin_R: 旋转外参,单位矩阵,保持默认即可
这里有一个非常容易错的细节:extrin_R在Faster-LIO里是IMU到雷达的旋转矩阵,不是雷达到IMU的方向。如果你把方向搞反,点云会严重变形,但不会崩溃,你会看到算法"很努力地在追踪一个错误的东西",最后输出紊乱的轨迹。
4.3 仿真中启动Faster-LIO的正确流程
我先梳理一下整个系统的数据流:
Gazebo仿真环境生成雷达点云话题和IMU话题,通过ROS发布;Faster-LIO订阅这两个话题,做点云特征提取,再进行状态估计和建图,最终输出里程计话题/Odometry和点云地图话题/map。
启动流程:
第一步,启动XTDrone的无人机或机器人仿真环境。这一步会因为你的具体机型而不同,确保雷达话题和IMU话题已经在发布。可以用:
rostopic hz /livox/lidar rostopic hz /livox/imu如果频率显示10Hz和200Hz,说明数据链路通了。
第二步,启动Faster-LIO:
roslaunch faster_lio mapping.launch你需要在launch文件里把参数文件路径改成刚才写的mid360.yaml:
<rosparam file="$(find faster_lio)/config/mid360.yaml" />第三步,在RVIZ里添加Faster-LIO的话题显示。重点是/Odometry路径和/map点云。
正常情况下,你操控仿真机器人前后左右移动,RVIZ里雷达里程计的轨迹应该平滑地跟随机器人的运动,点云地图会逐渐累积拼接出来。
4.4 用rosbag离线验证替代实时联调
实时联调有一个麻烦问题:Gazebo的仿真速度受机器性能影响,如果性能不足,雷达和IMU的时间戳会有抖动,Faster-LIO在跑的时候容易因为时间戳跳变而出现异常。这种情况下,我建议用rosbag录一段数据,离线回放给Faster-LIO跑。
录制命令:
rosbag record -O sim_data.bag /livox/lidar /livox/imu /tf控制仿真机器人运动30秒到1分钟,然后停掉。回放时:
rosbag play sim_data.bag --clock同时启动Faster-LIO,这样你可以反复用同一份数据调试参数,不用每次重新操作无人机飞行。这个方法对于判断"是数据问题还是参数问题"特别有效。
5. 从仿真到真机的避坑清单
这部分内容是我最想强调的。仿真环境的问题相对"干净",但真实世界里的问题会以各种方式倒灌进你的调试过程。以下是我在实际操作中遇到的典型问题和高频踩坑点。
5.1 Gazebo中点云异常的常见原因
问题一:雷达点云在仿真界面中完全看不到
先检查插件是否加载成功。看Gazebo启动时有没有报错,比如gpu_laser插件路径是否正确。另外,如果模型是直接挂在URDF里的,需要确认URDF里确实包含了<gazebo>插件标签,而不是只写了link和joint。很多人在URDF里写了<sensor>标签,但那不是Gazebo插件,Gazebo是不认的。
问题二:雷达点云旋转了90度
这说明URDF里雷达link的rpy和Faster-LIO里外参的旋转矩阵不一致。最直接的排查方法是固定机器人不动,手动旋转机器人,看RVIZ里点云和机器人模型之间的相对关系。理论上它们应该完全同步旋转。如果点云的旋转滞后或超前90度,确认激光雷达的光轴方向和URDF里设置的roll/pitch/yaw是否匹配。
问题三:Gazebo界面里雷达射线正常,但ROS话题里没有点云
这是话题名称或frame_id不匹配造成的。gazebo_ros_gpu_laser插件在发布点云前会检查frame_id是否在TF树中存在。如果URDF里声明的link名和插件配置的frameName不一致,TF树不完整,点云就会发布失败。你运行:
rosrun tf tf_echo base_link livox_frame看看TF链路是否完整。
5.2 Faster-LIO不收敛或漂移的排查
在仿真环境里跑Faster-LIO,如果出现轨迹发散(RVIZ里轨迹跑飞)或者建图明显漂移,大概率不是算法问题,而是配置问题。
排查第一步:IMU频率是否合理
Faster-LIO极度依赖IMU做运动补偿和状态预测。真实Mid-360内置IMU的频率通常在200Hz左右。如果仿真里的IMU话题只有50Hz,那算法在两次IMU测量之间的运动状态预测误差就会很大。你在Gazebo里需要把IMU更新频率配置到200Hz以上。
排查第二步:时间戳是否对齐
检查雷达和IMU的时间戳差。因为仿真环境里的传感器时钟来自Gazebo的/clock话题,如果Faster-LIO的time_sync_en没有设成true,它会直接使用两个话题各自的时间戳来同步。即使Gazebo里的传感器都挂在同一个时钟下,数据经过不同插件处理也可能引入微小的延迟。在仿真中,把time_sync_en: true开启,让算法自己估计时间偏移,是个更稳的选择。
排查第三步:外参是否和URDF一致
这是我在实战中踩过最深的一个坑。我在URDF里把雷达安装位置放在了无人机中心前上方的位置(xyz为[0.15, 0, 0.1]),但Faster-LIO的yaml里忘改,保持默认的[0, 0, 0]。结果就是算法初始化时不会报错,但在运动之后,点云和IMU的数据无法对齐,轨迹漂移得非常快。所以每次改完URDF里的模型位置,一定要回头检查Faster-LIO的外参。
5.3 仿真参数与真机参数的差异
请记住一个事实:你在仿真里调好的参数,搬到真机上大约只能直接沿用60%。这不是因为你工作没做到位,而是仿真环境过于理想。
具体来说:
- 仿真点云没有真实传感器噪声。
gpu_laser返回的测距值在精度上几乎接近零误差,而真实Mid-360在10米处约2cm的测距误差,这会直接影响建图的点云厚度 - 仿真IMU没有零偏漂移和随机游走,即使你给它加了高斯噪声,它也没有长时间尺度上的缓慢漂移。Faster-LIO中
b_acc_cov和b_gyr_cov如果设得很小,在真机上会追不上IMU零偏变化,导致位姿发散 - 仿真的运动模型比较理想,没有真实环境中的机械振动、柔性变形。在真机上,雷达和IMU外参在剧烈运动时是有轻微形变的,而仿真里没有
所以我建议:在仿真里验证算法的整体流程、数据结构、位姿估计趋势,也就是验证"算法逻辑";然后把point_filter_num调大、给点云添加一些随机噪声、给IMU添加轻微零偏,再观察算法是否稳定。这类"压力测试"能让仿真数据更接近真实。
我个人的经验值是,把Faster-LIO的point_filter_num从4调到2,能明显提升仿真中点云地图的厚度和细节,而算法的帧率仍然保持在40Hz以上。如果你自己有复杂的运动轨迹,建议多跑几次rosbag离线数据,把不同运动状态下的表现都记录下来,再上真机,这会省下大量的调参时间。