搞无人机SLAM的人,最近两年应该绕不开Livox Mid-360这颗雷达。转镜式固态设计,水平360度、垂直59度视场,点频20万点每秒,重量不到265克,特别适合装在小型无人机和机械臂上做感知。但真正上手就会发现,算法在仿真里跑得顺风顺水,一上真机就各种飘、各种丢点,问题源头往往是仿真里的传感器和真机差距太大,尤其是XTDrone这种以Gazebo为核心的平台,默认传感器列表里压根没有Mid-360。这篇文章我把从XTDrone里添加Livox Mid-360模型、点云插件选型、再到Faster-LIO驱动适配和参数调优的过程完整捋一遍,把我踩过的坑和验证方法一次性说清楚,适合正在做无人机实测、或者想把Fast-LIO系列算法先在仿真里跑通再移植真机的人参考。
1. 先搞清楚要仿真到什么程度:Mid-360与XTDrone的整体适配思路
1.1 Mid-360这颗雷达,仿真难点不在形状而在扫描方式
很多人第一次接触Mid-360,习惯性地把它当成一个“能转360度的雷达”,其实这个理解有偏差。它是固态转镜结构,内部没有机械旋转电机,靠棱镜反射实现视场覆盖,水平方向确实能扫满一圈,但垂直方向只有59度,而且是“非重复扫描”——每一帧的点云轨迹都不完全一样,长时间积分之后点云会逐渐填满整个视场。这个特性对仿真来说是个大麻烦。
Gazebo里默认的gpu_lidar或者ray_sensor,本质上是均匀线性扫描,每条扫描线的角度间隔固定,每帧点云形态几乎不变,跟Mid-360的“非重复扫描”完全不是一回事。如果你只是测试路径规划、避障,这个差异还能忍;但你要跑Faster-LIO这种紧耦合的雷达惯性里程计,点云分布特性直接影响特征提取和配准质量,仿真里点云太规整,算法收敛得漂亮,到了真机面对不规则扫描轨迹,同样参数可能就崩了。
所以第一个要明确的点:我们在XTDrone里建Mid-360模型,不是在CAD里画个外壳,而是要把这颗雷达的视场角、探测范围、点频、扫描轨迹特征尽量逼近真机,至少要做到“算法在仿真里遇到的点云密度和分布,跟真机在一个量级”。至于外壳模型,用简单几何体做视觉占位就够了。
1.2 XTDrone的传感器接入方式与方案选型
XTDrone本身是PX4与Gazebo深度绑定的无人机仿真平台,底层通过MAVROS跟飞控通信,同时也会在Gazebo里往ROS话题上发布传感器数据。它默认带了Livox Avia、Velodyne VLP-16、Realsense等模型,这些模型放在~/XTDrone/sitl_config/model目录下,每个传感器一个文件夹,里面是SDF描述文件、mesh文件、以及gazebo plugin的配置文件。
要给XTDrone加Mid-360,本质上就是在这个模型目录里新增一个传感器模型,然后把它挂到无人机模型的link上,再把话题接到Faster-LIO。整体链路是:
Gazebo传感器插件 -> 点云话题 -> Livox ROS Driver(或转换节点) -> Faster-LIO这里有个关键选择:是让Gazebo的gpu_lidar插件直接输出sensor_msgs/PointCloud2话题,还是让Gazebo输出类似Livox原始格式的数据,再走一遍Livox官方驱动里的算法去点。我建议尽量走后者,至少要把话题名和消息类型对齐到Livox驱动习惯,因为Faster-LIO的Livox分支默认订阅的是/livox/lidar(livox_ros_driver/CustomMsg)和/livox/imu。直接输出PointCloud2也能跑,但要改的代码点多,后面排查问题会绕远路。
1.3 环境准备:Ubuntu 22.04、ROS与Gazebo安装踩坑
XTDrone最早是基于Ubuntu 18.04 + ROS Melodic搓的,后来社区陆续有人移植到Ubuntu 20.04的Noetic,再往后就是Ubuntu 22.04 + ROS 1 Noetic这种“老牛拉新车”的组合。这里强烈建议别一上来就追新:
- Ubuntu 22.04默认装不了ROS Melodic,只能装Noetic,而且Noetic在22.04上属于社区维护,需要自己加源。
- 22.04如果走
apt install ros-noetic-desktop-full,会拉进来Gazebo 11,这正好是XTDrone支持得最好的Gazebo版本。 - 千万别手滑装成Ignition/Gazebo Sim,XTDrone原版是不认的,除非你后面专门做迁移。
安装Gazebo的时候,最容易出的问题就是界面闪、启动崩溃。很多人一开机就gazebo敲下去,结果窗口闪个七八次就没了,先别急着重装,大概率是显卡驱动和Gazebo的OpenGL渲染冲突。我一般这样处理:先看~/.gazebo日志,再检查nvidia-smi是否正常,然后临时用软件渲染跑一下,确认是不是硬件加速的问题。这个后面第5章详细说。
2. 从零搭一个能用的Livox Mid-360模型
2.1 雷达参数整理与SDF模型编写
先列一下Mid-360的关键参数,因为后面写SDF和调Faster-LIO都要用:
| 参数项 | 数值 | 对仿真的影响 |
|---|---|---|
| 水平视场角 | 360° | 全周覆盖,仿真里水平采样点数要足够 |
| 垂直视场角 | 59°(-7° ~ 52°) | 安装朝向下倾,注意pitch方向 |
| 测距范围 | 0.1m ~ 40m@10%反射率,360m@80klx | 仿真里设40m左右比较真实 |
| 点频 | 200,000 pts/s | 决定每帧点数,影响去畸变效果 |
| 帧率 | 10Hz(可配20Hz) | 直接和IMU频率配合 |
| 内置IMU | 6轴/9轴可选 | 必须绑定到雷达link上,Faster-LIO要用 |
SDF模型我建议这样写,以XTDrone默认的无人机机架为基准,把雷达放在机体正上方、重心附近:
<sensor name="livox_mid360" type="gpu_lidar"> <pose>0 0 0.25 0 0 0</pose> <topic>livox/lidar</topic> <update_rate>10</update_rate> <lidar> <scan_count>1</scan_count> <vertical_samples>128</vertical_samples> <horizontal_samples>720</horizontal_samples> <min_angle>0</min_angle> <max_angle>6.2831853</max_angle> <min_range>0.1</min_range> <max_range>40.0</max_range> <noise> <type>gaussian</type> <mean>0.0</mean> <stddev>0.02</stddev> </noise> </lidar> <plugin name="gazebo_ros_lidar" filename="libgazebo_ros_gpu_lidar.so"> <ros> <remapping>~/out:=livox/lidar</remapping> </ros> <output_type>sensor_msgs/PointCloud2</output_type> <frame_name>livox_mid360</frame_name> </plugin> </sensor>这里vertical_samples给128、horizontal给720,是往Mid-360的密度上靠,但严格说gpu_lidar依然是均匀扫描的,达不到非重复扫描的效果。想要更真实,得换插件,我下一节细说。
2.2 用Blender导出模型时被忽略的坐标系问题
网上有很多人问“blender导出gazebo模型”,多数是卡在坐标系上。Blender默认Z轴朝上,但很多雷达的模型在厂商SDK里是Y轴朝前、Z轴朝上,和ROS的REP-103规范(X前Y左Z上)还不一样。你在Blender里看着模型方向是对的,导出成STL或者DAE后挂到Gazebo里,往往发现扫描方向整体偏了一个轴。
我吃过这个亏,Mid-360模型从SolidWorks转出来的时候,我就没注意坐标系,结果在Gazebo里看到的点云是“躺着”的。排查了半天,最后发现是导出时模型本身的frame定义和ROS坐标不一致。解决办法是,在Blender里先把模型整体旋转到X轴朝前、Z轴朝上,再导出成STL,然后在SDF的<visual>和<collision>里分别用<pose>做一次对齐,不要只靠视觉猜。
另外,模型网格不需要太精细。Mid-360的雷达本体、散热罩这些细节,在15万面以内就足够了,面数太高Gazebo加载会卡,而且避障和配准算法根本感知不到外壳细节。我的习惯是:外壳用粗糙但拓扑干净的STL,传感器数据面靠插件决定,和mesh完全没有关系。
2.3 点云插件选择:gpu_lidar的局限与livox_laser_simulation
gpu_lidar插件适合快速验证,但它有硬伤:扫描线是均匀分布的,而且不能精确模拟非重复扫描,尤其在近距离物体上,点云间隔和真机差异特别明显。Fast-LIO这类算法很喜欢用点云“局部密度变化”来判断特征,仿真里密度太均匀,特征提取方差变小,算法会过度自信。
Livox官方后来开源了一个仿真插件livox_laser_simulation,用在Gazebo里模拟Livox扫描轨迹,它最大的价值是实现了非重复扫描的轨迹生成逻辑,点云的分布形态和真机接近,还支持模拟盲区、丢点这些现实现象。配置稍微麻烦一点:
cd ~/catkin_ws/src git clone https://github.com/Livox-SDK/livox_laser_simulation cd ~/catkin_ws && catkin_make然后在SDF里不要用gpu_lidar,改成插件liblivox_laser_simulation.so,它会按Livox的扫描模式生成带时间戳的点云,输出的话题可以直接用livox_ros_driver/CustomMsg发布,这样Faster-LIO几乎不用改驱动链路。
用这个插件有个注意点:它对GPU没有太多依赖,但还是建议把雷达放在相对静止的平台上单独测,先看轨迹扩散形态,再挂到无人机上。我第一次直接挂到XTDrone的多旋翼上,发现点云像被揉成一团,后来才意识到是插件内部的时间戳没有和Gazebo的仿真时钟对齐,导致运动补偿失效。
2.4 给仿真点云加噪声:接近真机的关键一步
仿真雷达再好,点云也是“干净”的,这一步必须人为加噪,不然后面移植真机必然痛苦。有两种加噪方式:
第一种,在SDF的<noise>标签里加高斯噪声,这个简单但只影响距离值,点云整体形态还是规整的。
第二种,在点云话题后面挂一个filter节点,对每个点做三件事:测距值加高斯噪声、少数点随机丢弃、在边缘处加一点丢点概率模拟物体反射率低的情况。我自己写了一个几十行的ROS节点,订阅/livox/lidar,处理后发布/livox/lidar_filtered,再喂给Faster-LIO。实测下来,加上这些噪声之后,建图轨迹的抖动模态和真机非常接近。
提示:不要过度加噪。仿真噪声调到一定程度后,算法会退化成“在噪声里找特征”,和真机差得反而更远。我的经验是把噪声水平调到真机采集数据估出来的协方差的70%~80%即可,给真机留一点余量。
3. 把点云喂给Faster-LIO:驱动话题与参数适配
3.1 Faster-LIO代码结构与话题入口
Faster-LIO是Fast-LIO的改进版,核心变化是用增量k-d树管理地图点,减少重复配准的计算量,降低CPU占用。代码结构上主要看两个地方:src/laserMapping.cpp和src/odom.cpp。其中odom节点负责前端配准,laserMapping节点负责维护地图和发布全局位姿。
它的参数入口在config/目录下,按雷达型号分成多个yaml。跑之前最好先确认它默认订阅的话题:
| 话题 | 消息类型 | 用途 |
|---|---|---|
/livox/lidar | livox_ros_driver/CustomMsg或sensor_msgs/PointCloud2 | 点云输入 |
/livox/imu | sensor_msgs/Imu | IMU输入 |
/odometry | nav_msgs/Odometry | 里程计输出 |
/path | nav_msgs/Path | 可视化轨迹 |
XTDrone里如果直接用gpu_lidar插件,默认输出是PointCloud2,话题名会在launch里通过remapping改掉;如果用livox_laser_simulation,话题名和类型更接近Livox官方风格,衔接最顺。
3.2 话题对接:从Gazebo到Livox ROS Driver的格式转换
如果你在Gazebo里用的是livox_laser_simulation插件,它输出的就是带offset_time的livox_ros_driver/CustomMsg,这时候Faster-LIO的LIVOX模式可以直接吃。如果你偷懒用gpu_lidar,拿到的是普通PointCloud2,那就要么在驱动层实现一个转换,要么改Faster-LIO的接收分支。
我的建议是尽量不改Faster-LIO的源码,优先写一个话题转换节点。原因很简单:Faster-LIO的代码迭代很快,你改了源码,下次pull代码就得解决一堆冲突;而单独起一个pointcloud2_to_custommsg节点,不侵入上层算法,后面真机上如果用的是官方固件,直接把转换节点摘掉就行。
转换节点的核心逻辑也不复杂,就是把PointCloud2的每个点的时间戳挨个打上offset_time,并把点云的frame_id统一改成livox_frame。要注意的是,仿真里点云的header时间戳是Gazebo的仿真时钟,不是系统时钟,Faster-LIO在计算IMU和雷达时间差的时候如果发现时间戳乱跳,会直接拒绝数据,所以你的转换节点里必须使用仿真时钟。
3.3 config参数调整:外参、IMU噪声、去畸变开关
yaml里最影响结果的几个参数量级,我列一下。
common: lid_topic: "/livox/lidar" imu_topic: "/livox/imu" time_sync_en: false extrinsic_T: [0.0, 0.0, 0.0] extrinsic_R: [1, 0, 0, 0, 1, 0, 0, 0, 1] preprocess: lidar_type: 1 scan_line: 4 blind: 1.0extrinsic_T和extrinsic_R是雷达相对IMU的外参。真机上需要手眼标定,仿真里可以直接设成零或者你SDF里的实际安装位置偏移,但注意坐标系方向要对齐,Mid-360安装到无人机上通常有俯仰角,这个角要算进T和R里。lidar_type: 1表示Livox格式,如果换Velodyne就改成2。scan_line这个参数,网上很多教程默认给16,容易让人以为和雷达线数有关。实际在Livox分支里,它更多是给后续算法做时间补偿用的,Mid-360官方驱动里一般给4更稳,仿真里我用的也是4。这个值不会一锤定音,但影响去畸变效果,换了驱动版本后要重新测。
IMU噪声参数我也习惯在仿真里直接设成比真机稍好一点,否则Faster-LIO如果一开始就发现IMU角速度噪声太大,会降低对点云的信任,建图结果会“软塌塌”的。
3.4 第一次跑建图的验收指标
第一次跑通之后,别急着调参,先确认几个指标:
- 轨迹是否连续,有没有跳变:在RViz里看Path是否平滑,出现90度折角就是配准或时间同步有问题。
- 地图厚度:把建出来的地图俯视图放大,墙面厚度应该小于10cm,如果整面墙像雾一样散开,多半是去畸变没生效。
- CPU占用:Faster-LIO号称比Fast-LIO省CPU,仿真里看单核占用率不应该爆满,如果持续超过100%,优化一下点频或者体素大小。
我自己的验收方法是让无人机在Gazebo里绕着一个矩形箱子转三圈,然后导出来地图跟真值模型做对比,算一下点到面的平均距离。这个数如果控制在5cm以内,基本说明传感器模型和算法参数是匹配的。
4. 仿真与真机的差距在哪里:一致性验证
4.1 为什么仿真能跑通、真机却飘
很多人把Faster-LIO在仿真里跑通了,信心满满上真机,结果轨迹几分钟就飘走。我见过最多的三类原因:
第一,仿真里时间同步太完美。Gazebo的所有话题都带的是同一个仿真时钟,IMU和雷达的延迟被“理想化”了,而真机上这两个传感器的时钟同步是一个大坑,尤其是雷达的时间戳滞后几十毫秒,直接导致点云去畸变失效。针对这个问题,我会故意在仿真IMU和雷达话题之间加一个几十ms的固定延迟,模拟真机的时间不同步,再去看轨迹能不能扛住。
第二,仿真里外参是已知的。你真机上外参标定差个几度,算法一开始还能撑住,积累几秒就开始发散。仿真里我会把外参故意写错一点,比如把roll角加0.5度,看看Faster-LIO什么时候会崩,这样就能有个直观感受:真机外参标定容错量大概是多少。
第三,仿真环境特征太丰富。XTDrone默认的场景里有大量明显的角点和墙面,但真机在园区、走廊这类场景里会碰到大面积白墙、玻璃、树木这类低特征区。MID-360的非重复扫描在空旷环境下退化尤其明显。我建议在Gazebo里加一个“白墙走廊”场景,专门测试退化环境下的表现。
4.2 在仿真里提前暴露问题的三个手段
我经常用的三个手段,都能在没有真机的情况下提前发现隐患:
- 人为断流测试:在仿真脚本里随机丢掉50%的雷达帧,观察Faster-LIO会不会因为缺帧导致轨迹突变。真机在剧烈运动时不丢帧几乎不可能,这个测试能逼你把前端惯导权重调得更鲁棒。
- 动态光照干扰:Gazebo里给场景加一个动态光源,反射率剧烈变化会让点云出现成片丢点,这时看地图是否会漂。
- 运动模式覆盖:只做匀速直线运动,很难暴露问题。我会让无人机做急转弯、快速升降、悬停小幅晃动,因为SLAM算法在不同运动激励下的表现差异巨大,如果仿真里这些模式都扛住了,真机测试的崩溃概率会低很多。
这些测试做完,你对算法参数的“边界”会有一个更清晰的认识,真机出问题时也更知道该往哪个方向查。
4.3 从仿真参数反推真机调参
仿真调参和真机调参是两种思路。真机参数一旦不对,很难判断是传感器标定问题、还是算法参数问题,变量太多;仿真里你有一个绝对真值,任何参数漂移都能量化。
所以我习惯先在仿真里做“参数敏感性分析”:固定其他参数,只改某个噪声系数,跑十次取轨迹误差的均值方差,画个趋势图。比如IMU的加速度计噪声从0.01调到0.05,轨迹误差是线性增长还是指数增长,这直接决定了你在真机上要不要优先处理IMU数据质量。
这个工作看起来费时间,实际上非常值得。我在XTDrone里把Faster-LIO的核心参数都过了一遍敏感性测试之后,真机上第一次跑Mid-360就用上了比较靠谱的初值,省掉了好几轮现场瞎调参的时间。
5. Gazebo仿真高频问题与排查实录
5.1 Gazebo界面一直闪、黑屏、崩溃怎么处理
“为什么gazebo界面一直在闪”这个问题,无论新手老手都绕不开。说白了,绝大部分都是显卡渲染问题,而不是Gazebo程序坏了。排查思路一定是从外到内:
- 第一步,看日志。启动Gazebo时加上
--verbose,如果日志里有libGL error: failed to load driver: swrast之类的字样,基本可以断定显卡驱动没和OpenGL匹配上。 - 第二步,确认驱动。命令行敲
nvidia-smi看驱动是否正常,注意Nvidia驱动版本和内核模块不匹配时,即使nvidia-smi能出信息,OpenGL渲染依然可能崩。 - 第三步,临时用软件渲染验证。启动前设置环境变量
LIBGL_ALWAYS_SOFTWARE=1,如果软件渲染下不闪了,问题就锁定在硬件加速这条链路。
如果家里环境比较特殊,比如远程桌面、虚拟机、或者核显和独显切换没做好,建议优先关闭桌面环境自带的合成器效果,再把GTK主题切回默认,这两步能解决大半渲染闪退问题。
5.2 版本兼容性:Gazebo Classic、Ignition与ROS2
XTDrone默认跑在Gazebo Classic上,也就是gazebo命令启动的那个版本。常见版本号是Gazebo 9、Gazebo 11,有人用gazebo --version看到Gazebo sim, version 8.15.0也不要慌,那个是Gazebo 8的老版本,有些教程基于这个版本写的,接口差异不大,但插件名和ROS版本兼容性会有不同。
如果你想把环境整体迁移到ROS2和Ignition/Gazebo Sim,那不是换个启动命令这么简单。Ignition的传感器插件、话题接口、坐标约定和Classic差异很大,同时Faster-LIO的ROS2分支也有自己的话题命名和参数结构,我建议至少先用Classic把整个流程跑通,再去做迁移,否则问题会叠在一起很难查。
对了,用XTDrone跑PX4 SITL时经常要和QGroundControl配合,QGC默认连UDP端口14550。如果你是在一台机器上开多路仿真,第二路仿真要和QGC冲突,记得改端口。遇到QGC连不上或者Gazebo里无人机不动的现象,先看MAVROS有没有把/mavros/state发出来,再去看/mavros/local_position/odom是否有数据,比反复重启QGC有效得多。
5.3 机械臂平台接入雷达仿真的注意点
XTDrone不仅支持无人机,还扩展了机械臂仿真,有不少人用它搭panda机械臂,在Gazebo里做力控、抓取、避障。这时候如果也想挂一颗Mid-360做感知,有两件事和无人机场景完全不同:
- 自遮挡问题。机械臂的连杆会伸到雷达视场里,真机上点云里会出现机械臂自己的点,但Gazebo默认的传感器插件会默认“看不到自身link”,导致仿真里完全没有自遮挡。要做真实,就得在雷达SDF的
<lidar>节点里打开<self_collide>相关设置,并给每个机械臂link加上对应的碰撞标签。 - 运动激励不同。无人机的IMU高频运动变化大,快速旋转多;机械臂是关节运动,机体平台相对静止,雷达点的运动轨迹更复杂。Faster-LIO在机械臂场景里更容易因为运动模型不匹配而发散,最好先让机械臂静止,单独标定雷达和IMU之间的外参,再做动态识别。
这个场景下,我建议点云话题不要走livox_laser_simulation的默认轨迹生成,而是单独调低扫描速度,模拟雷达相对静止、机械臂运动的环境,不然点云畸变模式会和真机差异过大。
5.4 视觉辅助场景:二维码、真值校正与多传感器标定
Mid-360在大面积白墙、玻璃幕墙这类退化场景里,会给Faster-LIO带来很大的压力。仿真里为了验证退化场景下的鲁棒性,很多人会在Gazebo里贴二维码(或者叫ar_tag),让视觉SLAM提供辅助观测。这个思路在Gazebo Classic时代比较麻烦,但在新版Gazebo Sim里比较容易实现,用传感器插件发布二维码的位姿真值,再和雷达建图轨迹做对比,就能算出雷达里程计的累计漂移。
我实际操作中会用二维码做一个“作弊”工具:让无人机在一个圆形走廊里转圈,每到一个角就记录二维码的位姿真值,然后和Faster-LIO输出的轨迹做对比,算累计漂移。这个做法最大的好处是,在建图前就搞清楚某个参数组合下系统到底漂多快,而不是等图建完才发现漂得没法看。
如果还要做雷达和相机的联合标定,也可以借助二维码:把标定板贴在墙上,用仿真里已知的精确位置作为初值,再用真机同样的算法流程去处理仿真数据。这样能把标定算法的正确性先在仿真里验证一遍,真机上即使标定结果有偏差,你也知道是采集数据的锅还是算法的锅,排查范围会小很多。
我在实际搭这套环境的过程中,最深的一个体会是:仿真不是越逼真越好,而是要“逼真到足以暴露问题,又足够可控以定位问题”。XTDrone本身是个很好的底座,但默认传感器库缺Livox Mid-360这颗关键雷达,直接硬套gpu_lidar会让Faster-LIO的仿真结果失去参考意义。花两天时间把雷达模型、点云插件、话题转换和参数敏感性测试做扎实,后面真机调试省下的时间远远不止两天。最后再分享一个小技巧:在Gazebo里先不要急着让无人机飞起来,把雷达装在固定支架上、手动推着仿真模型平移旋转,观察点云和轨迹输出,很多参数问题在这种静态慢速运动下更容易暴露,等这一步确认正常,再交给自动航线去验证高速运动下的稳定性。