M2DGR数据集实战:从ORB-SLAM3到FAST-LIO的完整适配指南
如果你也是拿了新数据集就想立刻把算法包拉下来跑的人我劝你先冷静。M2DGR这种多模态SLAM数据集真正磨人的不是算法本身而是从bag数据到ORB-SLAM3、FAST-LIO这类框架之间的适配链路。我第一次折腾它时光排查话题名和外参方向就花了一天半地图倒是没建出来几张报错信息倒是存了一整个TXT。这篇文章把我踩过的坑、验证过的配置流程完整写出来从数据检查到ORB-SLAM3单目/双目适配再到FAST-LIO的机械雷达接入和evo轨迹评估一条线走通适合正在做机器人SLAM项目、准备SLAM面试或者想在M2DGR上验证自己算法的读者参考。1. M2DGR为什么值得折腾和KITTI、EuRoC比它补上了什么1.1 一个bag里同时装着视觉、雷达、IMU和真值M2DGR是面向地面机器人发布的多模态数据集它和很多人熟悉的KITTI、EuRoC不同采集平台是轮式机器人运动模型更贴近服务机器人、巡检机器人这类实际产品。它的bag里不仅有常见的针孔相机图像还有鱼眼相机、3D激光雷达、IMU、GPS部分序列还提供了深度和事件相机数据。这意味着你在一个数据集上就能验证视觉SLAM、雷达惯性SLAM、视觉惯性导航、多传感器融合以及动态环境下的鲁棒性不需要在不同数据集之间来回切换。更实际的一点是M2DGR为每个序列都提供了较为完整的标定参数和地面真值。对做算法评估的人来说真值文件是刚需——没有真值你没法量化算法精度只能说“看起来没飘”。M2DGR把真值、标定、原始数据打包好了省去了自己搞动捕系统或差分GPS的麻烦。我最早用TUM RGB-D做室内视觉SLAM验证后来转机器人项目后明显感觉M2DGR的场景更接近真实部署环境。1.2 和KITTI、EuRoC、TUM的差异很多人一开始以为数据集都差不多换个路径跑就完了。实际上不同数据集的“脾气”完全不同直接套配置很容易翻车。我把几个常见数据集的定位整理了一下数据集平台类型主要传感器适合验证方向真值方式KITTI自动驾驶汽车双目相机、64线激光雷达、GPS/IMU室外自动驾驶SLAM、目标检测GPS/IMU组合EuRoC无人机双目相机、IMU视觉惯性里程计、无人机定位VICON、激光跟踪仪TUM RGB-D手持相机RGB-D相机室内视觉里程计、RGB-D SLAM运动捕捉系统M2DGR轮式地面机器人多目相机、鱼眼、激光雷达、IMU、GPS多传感器融合、地面机器人SLAM多模态真值这个表能看出一个重要差异KITTI和EuRoC的传感器组合相对固定而M2DGR把视觉、激光、IMU、GPS全部装在一个平台上传感器之间的外参和时间同步关系更复杂也更接近真实机器人系统的状态。在KITTI上跑通的算法直接扔到M2DGR上第一步往往不是改代码逻辑而是先搞清楚该订阅哪个话题。1.3 它适合验证哪几类SLAM问题我自己的体会是M2DGR至少能覆盖四类SLAM实验场景。第一类是纯视觉SLAM比如你想比较ORB-SLAM3的单目、双目、RGB-D模式在同一场景下的表现M2DGR的多路视觉可以直接拆出来几组图像流。第二类是激光惯性SLAMFAST-LIO、LIO-SAM这类算法可以直接吃它的雷达和IMU数据。第三类是视觉和激光的融合算法比如松耦合或紧耦合的VILO系统M2DGR天然提供了两种模态的数据。第四类是动态环境SLAM它的街道序列和人员走动序列里包含大量运动物体这是KITTI静态场景里不太容易遇到的挑战。对于正在准备SLAM面试的人来说与其背十篇面经不如把M2DGR上的ORB-SLAM3和FAST-LIO完整跑通一遍。面试官问到多传感器融合的工程细节时你至少能说出话题同步、外参标定、坐标转换这些真实问题是怎么处理的这比单纯说“我会调包”要有说服力得多。2. 开局先别编译算法数据清单与话题检查决定你后面少走多少弯路2.1 rosbag info和rosbag play的基本检查我见过太多人拿到M2DGR的bag文件第一件事就是打开终端编译ORB-SLAM3编译完直接launch结果程序一直等待话题永远等不到数据。真正正确的第一步是先用rosbag info把bag的底细摸清楚。rosbag info M2DGR_gate_01.bag这个命令会列出bag的时长、消息数量、所有话题名以及对应的消息类型。M2DGR不同版本对话题的命名可能有差异比如相机图像话题可能是/cam0/image_raw也可能是/camera_0/image_raw雷达话题可能是/velodyne_pointsIMU话题可能是/imu/data或/imu/data_raw。不要凭记忆猜话题名一切以rosbag info的实际输出为准。接下来用rosbag play试播放观察rostopic echo是否真的有数据在发。rosbag play --pause M2DGR_gate_01.bag rostopic echo -n 5 /imu/data这一步是为了早点发现时间戳异常、消息类型不匹配、bag是否损坏等问题。如果rostopic echo都拿不到数据后面所有算法配置都是白搭。我在另一篇文章里建议大家用rosbag decompress先解压bag再播放这里同样适用——M2DGR的bag比较大直接播放时如果磁盘IO跟不上视觉特征提取会频繁丢帧直接影响ORB-SLAM3的表现。2.2 标定文件、外参文件、真值文件在哪获取M2DGR官方仓库除了bag文件之外还单独提供了标定calibration数据。标定文件通常是yaml或txt格式包含相机内参、畸变系数、雷达到IMU的外参、IMU噪声参数等。很多人忽略了这些文件以为算法可以自动估计外参但ORB-SLAM3和FAST-LIO都不会替你估计这些值——外参填错了系统能运行但轨迹一定会缓慢漂移或直接发散。拿到标定文件后先别急着看内参先确认三件事相机内参对应的分辨率是否和bag里的图像分辨率一致。不一致时fx、fy、cx、cy需要按比例缩放。标定文件中雷达与IMU的外参方向定义是什么。FAST-LIO需要的是雷达到IMU的变换或其他定义你手上的标定文件可能是IMU到雷达的直接用会出问题。真值文件是否存在单独下载目录还是和bag打包在一起。M2DGR通常提供ground_truth.txt一般位于序列根目录或专用的gt目录。head一下真值文件会比打开图形界面快得多head -5 ground_truth.txt这里能看到每一列是什么。大多数真值文件是时间戳加三维位置加四元数单位四元数但不同发布者对列顺序的定义可能略微不同一定要先看清楚再写评估脚本。2.3 时间同步和ROS版本的处理M2DGR官方发布的bag基于ROS1而当前不少新环境已经转到了ROS2这中间会卡住一批人。我的建议是先用ROS1的环境把流程跑通比如Ubuntu 20.04 ROS Noetic因为ORB-SLAM3和FAST-LIO在ROS1下的资料最全踩坑成本最低。如果你只有ROS2环境可以考虑用Docker容器跑一个ROS1的镜像或者用ros1_bridge做数据中转但这会增加排查问题的难度不推荐作为第一次上手的方案。时间同步是多传感器SLAM最容易忽视的一环。M2DGR的bag里各传感器话题的时间戳并不保证完全一致播放时建议设置/use_sim_time为true让节点统一使用bag的仿真时间而不是系统墙钟时间。rosparam set /use_sim_time true rosbag play M2DGR_gate_01.bag如果不设置use_sim_timeORB-SLAM3的帧率判断和FAST-LIO的IMU预积分时间戳都会乱。特别是FAST-LIOIMU预积分对时间戳异常非常敏感时间戳跳变会直接导致初始位姿估计错误地图产生重叠。3. ORB-SLAM3侧从bag里取出图像把鱼眼和针孔配置分开写3.1 先跑通单目改ros_mono.cc和配置文件ORB-SLAM3在M2DGR上的适配核心不在算法而在于怎么让图像话题进入系统。我推荐直接用ORB-SLAM3的ROS节点方式而不是先把bag转成图片序列再离线跑因为你最终还是要对比真值、调整参数ROS节点方式修改和调试效率更高。编译之前先确认依赖。ORB-SLAM3需要Eigen3、OpenCV、Pangolin、DBoW2和g2o其中Pangolin需要从源码编译。不同版本的OpenCV对ORB-SLAM3源码兼容性不一样旧版源码在OpenCV 4下经常报CV_RGB2GRAY这类宏定义找不到的问题我当时是被这个卡了一整个下午。如果你用的是最新源码大部分兼容性问题已经修复但最好还是先编译生成库再用ROS例子验证一遍。编译通过后找到Examples/ROS/ORB_SLAM3/src/ros_mono.cc改订阅的话题名。M2DGR的相机话题一般是/cam0/image_raw这一类的名字把image_subscriber对应的topic改成你的实际话题名即可。接着在launch文件里用remap也可以达到同样的效果我倾向于两者都用源码里写死一个默认值launch里再兜底。launch param name/use_sim_time valuetrue/ node nameORB_SLAM3_Mono pkgORB_SLAM3 typeMono outputscreen param namevoc_file value/your/path/ORBvoc.txt/ param namesettings_file value/your/path/M2DGR_mono.yaml/ remap from/camera/image_raw to/cam0/image_raw/ /node /launch配置文件的路径最好用绝对路径避免相对路径在不同终端工作目录下失效。很多人用~符号或者相对路径结果启动时报找不到文件这类问题浪费十分钟算快的。3.2 鱼眼相机的KannalaBrandt8参数怎么填M2DGR的视觉传感器里包含鱼眼相机这是它和KITTI这类纯针孔数据集最大的区别之一。鱼眼相机的畸变非常大如果你把Camera.type写成PinHole用普通针孔模型去跑鱼眼图像ORB-SLAM3的特征点提取和三角化质量会明显下降轨迹扭曲几乎是必然的。ORB-SLAM3对鱼眼相机的支持通过KannalaBrandt8模型实现。配置文件的模板大致是Camera.type: KannalaBrandt8 Camera.fx: 365.0 Camera.fy: 365.0 Camera.cx: 320.0 Camera.cy: 240.0 Camera.k1: 0.02 Camera.k2: -0.01 Camera.k3: 0.001 Camera.k4: 0.0001 Camera.fps: 20.0 Camera.RGB: 1注意k1到k4对应的是鱼眼畸变模型的四个系数不是针孔模型的径向畸变k1、k2、p1、p2。M2DGR标定文件里如果给的是鱼眼模型参数直接对应填进去即可如果标定文件里给的是其他模型比如Equidistant、Fisheye需要先转换成KannalaBrandt8的格式。转换过程如果不想自己推公式可以用OpenCV的cv::fisheye标定结果做一次投影校验确保重投影误差在几个像素以内再填入配置文件。另外关注Camera.RGB这个字段。M2DGR的图像可能是彩色也可能是灰度取决于你选的具体相机话题。如果图像是灰度图但配置写了Camera.RGB: 1ORB-SLAM3在转换时不会报错但特征点描述子计算用的通道顺序会出问题降低匹配稳定性。稳妥做法是播放bag后用rqt_image_view看一眼实际图像颜色格式再来填这个字段。3.3 找两路相机凑双目的实操单目跑通之后很多人想进一步做双目或RGB-D。M2DGR的视觉系统有好几路相机理论上可以凑出一对双目但要注意几件事。首先双目相机的两路图像需要有足够的公共视野。M2DGR的几路相机安装角度不同不是任意两路都适合做双目。我当时试过用左右两侧的鱼眼相机凑双目结果公共视野区域有限且鱼眼畸变叠加后立体匹配效果很差。比较稳妥的做法是优先选择官方标定文件里明确给出相对外参的两路相机并用它们的投影重叠区域做验证。其次ORB-SLAM3双目配置文件的写法是两套相机参数并列Camera.type: KannalaBrandt8 Camera1.fx: 365.0 Camera1.fy: 365.0 Camera1.cx: 320.0 Camera1.cy: 240.0 Camera1.k1: 0.02 ... Camera2.fx: 365.5 Camera2.fy: 365.5 Camera2.cx: 310.0 Camera2.cy: 245.0 Camera2.k1: 0.02 ... Camera.baseline: 0.12baseline的单位是米要填的实际上是两路相机光心之间的距离而不是随便拿个标定里的平移向量长度代进去。如果两路相机不是严格平行安装还需要在配置中体现旋转外参否则双目重建会出现系统性误差。这个环节没有捷径必须回到标定文件里仔细对照。我个人的建议是第一次跑M2DGR ORB-SLAM3优先把单目和单目IMU模式跑稳定再考虑双目。M2DGR是轮式机器人平台纯单目在缺少平移激励时容易出现尺度漂移视觉惯性模式反而能明显改善轨迹而双目模式对硬件标定质量要求更高一旦标定参数有微小偏差系统会表现得很不稳定排查成本很高。4. FAST-LIO侧把默认的Livox配置改成M2DGR的机械雷达4.1 话题名和lidar_type是第一个坑FAST-LIO默认的配置是为Livox系列雷达设计的比如AVIA或者MID-360。M2DGR上使用的是机械旋转雷达两者在数据结构和处理逻辑上有本质区别。Livox雷达发布的是livox_ros_driver/CustomMsg自定义消息而机械雷达发布的是sensor_msgs/PointCloud2标准消息。FAST-LIO内部会根据lidar_type决定去订阅哪个消息类型。所以第一步就是改配置里的lid_topic和imu_topiccommon: lid_topic: /velodyne_points imu_topic: /imu/data lidar_type: 2lidar_type有约定俗成的取值习惯1对应Livox2对应Velodyne这类旋转机械雷达3对应Ouster。不同版本的FAST-LIO可能在细节上略有差异但整体逻辑一致。配置不对时最常见的现象是节点启动后看不到任何点云输出或是在终端里刷timeout和no valid point之类的日志。另外一个容易踩的编译坑是即使你不用Livox雷达FAST-LIO的编译仍然依赖于livox_ros_driver这个包因为它的CMakeLists里要引入Livox的自定义消息头文件。很多教程没强调这一点导致新人在没有把livox_ros_driver放进catkin workspace的情况下编译FAST-LIO报各种找不到头文件的错误。cd ~/catkin_ws/src git clone https://github.com/Livox-SDK/livox_ros_driver.git cd ~/catkin_ws catkin_make编译顺序上优先把livox_ros_driver编好再编译FAST-LIO能省去不少麻烦。4.2 N_SCAN、Horizon_SCAN和外参的修改逻辑N_SCAN和Horizon_SCAN这两个参数是针对旋转机械雷达的它们描述了雷达点云的组织方式。N_SCAN代表雷达的线数比如16线雷达填1632线雷达填32Horizon_SCAN代表每条扫描线上期望包含的点数。M2DGR使用的机械雷达线数较高配置示例大概是preprocess: lidar_type: 2 N_SCAN: 32 Horizon_SCAN: 1800Horizon_SCAN这个值不一定要和雷达驱动的实际输出完全一致它影响的是点云在水平方向的分割粒度。如果设得太小同一条扫描线会被切成多段影响特征提取设得太大会增加无效的空点浪费计算资源。实操时可以先在rviz里看一帧点云数一下水平方向大致多少个点或者看雷达驱动发布的点云消息里每个scan的point_step和width字段来做判断。外参是FAST-LIO配置里的重头戏。M2DGR标定文件中会给出雷达与IMU之间的外参一般是一组旋转矩阵和一组平移向量# lidar - imu 外参具体定义以你手上的标定文件为准 extrinsic_R: [0.999, 0.001, 0.002, -0.001, 0.998, 0.004, -0.002, -0.004, 0.999] extrinsic_T: [0.02, 0.0, 0.05]我见过最典型的错误是直接把外参填反。填反之后的症状很有迷惑性系统能运行建图过程不报错但地图会明显分层轨迹和真值对不上看起来像时间不同步问题实际只是外参方向反了。对于新人我建议先用一个非常小的实验来验证外参方向把雷达和IMU的话题同时播放观察在静止状态下IMU加速度计输出和点云地面法向量方向的关系。如果外参正确静止时点云地面的法向量经过外参变换后应该和重力加速度方向一致。这一步验证比反复调整参数高效得多。4.3 跑通后如何保存全局点云地图FAST-LIO运行时会在rviz中实时显示点云地图但rviz里看到的不等于已经保存到磁盘。保存地图有两种常用方式直接修改源码调用PCL的保存函数或者在运行时用命令行工具把点云话题录制下来。最直接的方式是修改laserMapping.cpp里关于全局地图发布的代码在收到保存指令时将globalMap保存为PCD文件。很多版本里已经内置了键盘保存的接口按住对应按键会触发保存pcl::io::savePCDFileASCII(/home/user/m2dgr_map.pcd, *globalMapPtr);如果想不修改源码也可以用rosbag录制FAST-LIO发布的点云话题跑完之后再离线转成PCD。这种方式的缺点是bag文件会很大而且转PCD时还要做一次点云拼接比较麻烦。我自己的习惯是在源码里加一个保存接口简单直接不产生额外的大文件。保存地图时注意FAST-LIO保存的是激光雷达坐标系下的全局点云。如果你后续要把地图用于定位、导航或路径规划一般还需要转换到机器人的世界坐标系或map坐标系这一步涉及外参的二次处理不要省略。5. 轨迹评估真值坐标系的坑和evo的正确姿势5.1 先看真值文件是TUM格式还是KITTI格式跑完算法只是第一步没有评估的SLAM实验等于白跑。M2DGR提供了真值轨迹文件但你和真值之间还隔着一个格式转换的坎。真值文件通常是文本格式。我先用head查看列数然后判断格式如果每一行是timestamp x y z qx qy qz qw就是TUM格式可以用evo_ape tum直接评估。如果每一行是r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz就是KITTI格式要用evo_ape kitti。ORB-SLAM3运行结束后会输出KeyFrameTrajectory_TUM_Format.txt这是标准TUM格式和TUM格式的真值可以直接配对。FAST-LIO发布的位姿通常是nav_msgs/Odometry消息如果你不想写转换脚本可以用evo_ros直接从bag中提取evo_ros bag output.bag /Odometry --save_as_tum提取出的TUM格式轨迹文件再和真值文件对比。要注意的是两条轨迹的时间戳起点可能完全不同评估时用-a或-s参数让evo自动进行时间轴对齐和位姿对齐否则算出来的误差没有意义。5.2 用evo对比ORB-SLAM3和FAST-LIO的三个参数细节我第一次用evo评估M2DGR轨迹时直接跑了evo_ape tum gt.txt est.txt结果APE大得离谱。后来发现问题不是算法差而是我没有对齐两条轨迹的坐标系和尺度。首先是-s和-a的区别。-s表示用Umeyama算法同时估计相似变换旋转、平移、尺度适合单目SLAM这种尺度未知的场景。-a表示只做SE(3)对齐不对齐尺度。对M2DGR上的单目ORB-SLAM3应该用-s对双目或Fast-LIO这类有明确尺度信息的系统用-a。其次是时间戳匹配窗口。ORB-SLAM3的关键帧轨迹频率比较低FAST-LIO的位姿频率高真值频率可能又不一样。evo默认会对齐时间戳但如果你觉得匹配点太少可以显式指定匹配窗口evo_ape tum ground_truth.txt est_traj.txt -a -s --t_max_diff 0.01t_max_diff单位是秒这里设置成10毫秒意味着只有时间戳差小于10毫秒的两条轨迹点才会被匹配。如果设置太小匹配点太少太大会造成时间戳误差被隐藏。最后是绘图参数。evo_traj可以同时画出多条轨迹evo_traj tum keyframe_traj.txt --refground_truth.txt -a -s --plot --plot_modexyz--plot_modexyz能画出三维轨迹如果只看某个平面的误差分布可以用xy或xz。我看轨迹图时会搭配evo_res看误差统计表重点关注RMSE、Mean和中位数而不仅仅是最大值。最大值往往由个别漂移帧贡献不能反映整体性能。6. 实测中的典型翻车现场与调参方向6.1 为什么同样的代码在gate_01好用在street_02飘我在M2DGR上跑同一套ORB-SLAM3配置不同序列的表现差异非常大。gate_01这类场景相对规整墙面、立柱、地面纹理都清晰特征点数量和分布都比较健康单目视觉模式也能维持不错的轨迹。但到了street_02这种室外街道场景树影、玻璃、移动车辆会引入大量动态特征点或重复纹理ORB-SLAM3的特征匹配稳定性明显下降轨迹开始出现抖动甚至局部漂移。这个现象背后的原因很典型纯视觉SLAM对场景的静态性假设非常强而M2DGR的街道序列就是专门用来考验这一假设的。遇到这种情况我的第一反应不是去调ORB特征点数量而是先用evo看误差曲线判断漂移发生在哪个时间段。如果误差曲线在某几个时间点突然跳变回放bag找出这些时间点对应的场景基本都会发现是车辆或行人经过。FAST-LIO对动态物体的抵抗力强一些因为激光点云的动态物体通常只占一帧点云的一小部分通过离群点剔除和下采样可以在一定程度上缓解。但如果动态物体在场景中占比太大比如人员密集的走廊激光惯性系统也会被带偏。此时可以考虑减小point_filter_num或提高下采样的密度让静态点云占更多权重但代价是计算量上升。6.2 预积分、关键帧、去畸变这几个调参旋钮如果你确认配置和话题都没问题但跑出来的轨迹还是不理想就要深入到算法参数层面了。ORB-SLAM3这边视觉惯性模式下IMU预积分对M2DGR这类轮式平台比较敏感。轮式机器人运动时直线段长、转弯少IMU的加速度计和陀螺仪偏置估计可能迟迟收敛不了。一个实用的调整是适当增大IMU噪声参数让滤波器不要那么信任IMU的短期测量因为轮式平台的振动和颠簸会让IMU噪声比手持无人机场景更明显。另一个思路是调整关键帧生成阈值让系统在直线段少生成关键帧转弯处多生成关键帧给位姿图优化提供更好的约束分布。FAST-LIO这边主要调参对象是噪声协方差和迭代优化参数。M2DGR标定文件里通常会给出IMU的噪声密度和随机游走参数把这些值填进配置而不是沿用AVIA的默认值效果会有明显提升。laser_point_cov这个参数控制雷达点云的协方差如果调得过大系统会过于信任IMU预测建图细节丢失调得过小则容易受到噪声点干扰。我通常会先按标定值填一次再用几个序列做网格搜索找到误差RMSE最小的组合。最后强调一个容易忽略的细节雷达点云去畸变。机械雷达在扫描过程中机器人可能已经运动了一段距离导致一帧点云内部存在运动畸变。FAST-LIO对旋转雷达默认做去畸变但如果你发现地图边界有拖影或重影优先确认雷达驱动是否输出了正确的time偏移以及去畸变参数是否开启。M2DGR的bag是提前录好的驱动参数不会在播放时自动适配这部分需要你根据雷达型号手动对齐。我现在把M2DGR当作算法回归测试的标配场景用每次写完新的预处理模块或外参标定脚本先固定跑gate_01和parking_02两个序列对比跑之前的误差曲线能很快发现有没有引入新的退化或系统性偏差。这个习惯帮我挡掉了不少要等到发论文或上线前才暴露的问题。M2DGR本身的数据层次足够丰富从纯视觉到激光惯性再到多传感器融合都能在它上面找到对应的验证路径这套从ORB-SLAM3到FAST-LIO的配置链路值得把它沉淀成自己的标准流程。