第一次把ORB-SLAM3跑在Euroc的MH01序列上,我看着屏幕上不断刷新的地图点和轨迹线,第一反应是“终于跑通了”,第二反应是“这轨迹怎么和真值差了这么多”。后来仔细一查,问题居然出在一个非常不起眼的地方——我给单目模式传的yaml文件里,Camera.fps写的是20,但数据集实际发布频率是20Hz没错,可时间戳文件里第一帧不是从0开始的,导致评估对齐时整体偏移。类似这样的细节,在Euroc数据集的测试过程中比比皆是。
如果你正准备用Euroc数据集检验ORB-SLAM3,或者想复现论文里那几条漂亮的精度曲线,这篇分享能帮你少走不少弯路。我会从数据集选型、编译安装、四种传感器模式的运行命令、评估工具一直聊到那些不写进文档的坑,最后再给几个批量测试的小习惯。内容都以我自己的实际操作经验为准,命令和参数都是实测可跑的。
1. 你测试Euroc前需要先搞懂的三件事
1.1 Euroc数据集到底长什么样
Euroc数据集是苏黎世联邦理工发布的微型无人机MAV数据集,在视觉SLAM圈子里几乎是“标准试卷”。它由一架四旋翼搭载双目相机和IMU采集,场景分为Machine Hall(MH系列)和Vicon Room(V系列)两类。MH序列在机器大厅里运行,纹理丰富、光照稳定,属于入门难度;V系列在动捕房间里,有更多快速旋转和近距离遮挡,难度更高。
数据集的目录结构很固定,每个序列解压后都是这样的:
MH01/ └── mav0/ ├── cam0/ │ ├── data/ │ │ ├── 1403715282262142976.png │ │ └── ... │ └── data.csv ├── cam1/ │ ├── data/ │ └── data.csv ├── imu0/ │ ├── data.csv └── state_groundtruth_estimate0/ └── data.csv整个路径里最关键的三样东西:cam0/data和cam1/data是左右目灰度图,imu0/data.csv是200Hz的IMU测量,state_groundtruth_estimate0/data.csv是动捕系统给的米制真值轨迹,用于评估。ORB-SLAM3的命令行参数几乎就是在告诉你“我需要哪个路径、哪份时间戳、哪份真值”。
很多新手在这里犯的第一个错:把数据集根目录当成了mav0。注意ORB-SLAM3的Euroc示例程序里,传入的dataset_path是指包含mav0的上一级目录,比如~/Datasets/EuRoC/MH01,而后面跟的mav0/cam0/data是相对路径。这个路径搞错,程序会直接崩,后面我会细说。
1.2 和TUM、KITTI对比,Euroc的独特价值
做SLAM测试的人绕不开TUM、KITTI和Euroc这三板斧。TUM RGB-D主要在室内用Kinect采集,偏向单目/RGB-D和手持运动,真值由运动捕捉系统提供,精度高但场景范围小;KITTI是室外车载场景,双目加激光雷达,地面真值来自高精度GPS/IMU组合,适合自动驾驶环境,但视觉纹理变化很大,动态物体也多。
Euroc的特点是它天生为视觉惯性SLAM设计。它同时提供了高频IMU数据和双目图像,时间戳也做了硬件同步,这正好是ORB-SLAM3最擅长的赛道——视觉惯性融合和IMU初始化。换句话说,如果你想验证ORB-SLAM3单目+IMU或双目+IMU的效果,Euroc是最合适的公开数据集。它也提供纯双目序列,MH01-MH05可以用连续序列模拟“长时间运行”,V系列考验快速运动下的鲁棒性。
1.3 测试前先定好“口径”:单目、双目、要不要IMU
这个决定会影响你后面所有命令和评估方式。单目模式下,ORB-SLAM3输出轨迹没有真实尺度,单位是“任意单位”,需要用Sim(3)相似变换对齐到真值才能算精度;双目和IMU模式输出的是米制轨迹,可以直接和真值做SE(3)对齐。
另外,ORB-SLAM3的官方Demo里分了四个可执行文件:
mono_euroc:纯单目,只读左目stereo_euroc:纯双目,读左右目mono_inertial_euroc:单目+IMUstereo_inertial_euroc:双目+IMU
四种模式的命令参数和结果差异都不同。我建议初学者先跑stereo_inertial_euroc,因为它最能体现ORB-SLAM3的完整能力,而且不容易因为尺度问题让人困惑。想理解纯视觉极限的再跑单目,想对比IMU增益的再跑双目的两种模式。别一上来就全跑一遍,先跑通一种模式,再延展。
2. 编译ORB-SLAM3时我亲历的依赖地狱
2.1 依赖版本组合:OpenCV、Eigen、Pangolin到底怎么配
ORB-SLAM3的官方README里只简单列了Pangolin、OpenCV、Eigen三个依赖,但实际编译时版本细节非常多。我先后在Ubuntu 18.04和20.04上编译过,踩过不少版本组合的坑。
先说我目前在Ubuntu 20.04上稳定可用的组合:
| 依赖 | 版本 | 备注 |
|---|---|---|
| Pangolin | 0.6 | 老版本稳定,和ORB-SLAM3的接口匹配 |
| OpenCV | 3.4.x 或 4.2.0 | 注意4.x以上需要C++11兼容 |
| Eigen3 | 3.3.7 | 直接用apt安装即可 |
| g2o / DBoW2 / Sophus | 仓库自带Thirdparty | 无需手动装 |
Pangolin是最容易出问题的一个。如果你用的是0.8甚至更新的版本,可能会出现pango_view_handler命名空间变化、gl::前缀要求等编译错误。我自己实测,直接用0.6版本可以避免大部分接口不兼容问题。当然,新版Pangolin配合最新ORB-SLAM3主分支有时也能过,但没有必要在测试数据集阶段给自己提高难度。
OpenCV版本方面,apt直接装的libopencv-dev在Ubuntu 20.04是4.2,编译ORB-SLAM3没问题。但如果你是在Python环境里顺手装了opencv-python,不要混用。ORB-SLAM3用的是C++版本的OpenCV,务必通过pkg-config opencv4确认系统库已经正确安装。
2.2 build.sh背后的编译逻辑与常见错误处理
ORB-SLAM3根目录下有一个build.sh,它会顺序进入Thirdparty/DBoW2、Thirdparty/g2o、Thirdparty/Sophus、Examples等目录,依次执行cmake和make。理论上你只需要:
cd ORB_SLAM3 chmod +x build.sh ./build.sh但实际执行中很容易在某个子模块卡住。我的经验是,不要直接全量跑,最好先单独编译每个Thirdparty,确认没问题后再编译主目录。你可以这样操作:
cd Thirdparty/DBoW2 mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j4每个子模块都成功后,再回到根目录执行./build.sh。这样即使出错,也能明确知道是哪一环。全量脚本最大的问题是它把错误信息淹没在一大片日志里,很难定位。
还有一个常见坑:ORB-SLAM3要求C++11或更高,但某些编译器默认可能不是。如果你在编译g2o时出现“#error This file requires compiler and library support for the ISO C++ 2011 standard”,就在cmake时加上-DCMAKE_CXX_FLAGS="-std=c++11"。不要在调用make时临时加,那样没用。
2.3 跑通前先用自带的小样本或示例验证安装
编译完成后,先别急着下载完整Euroc数据集。你可以先看一眼Examples目录下的可执行文件是否都生成了:
ls -lh Examples/Monocular/mono_euroc ls -lh Examples/Stereo-Inertial/stereo_inertial_euroc如果能看到可执行文件,说明主程序编译成功。但“编译成功”不等于“能跑”,因为还要依赖数据集和yaml文件。ORB-SLAM3仓库自带了Examples/Monocular/EuRoC.yaml、Examples/Stereo/EuRoC.yaml、Examples/Monocular-Inertial/EuRoC.yaml等参数文件,所以配置这块不用自己写。
我建议第一次运行就用mono_euroc跑MH01序列的很小一段来验证,因为单目程序启动快,依赖少。后面第3节会给出具体命令。如果你能坚持到可视化窗口出现并稳定跟踪几秒,就算基本通了。
3. 四行命令跑完全部传感器模式,但每行都不简单
3.1 单目模式命令与参数逐项解析
单目Euroc示例的命令格式如下:
./Examples/Monocular/mono_euroc \ ../Vocabulary/ORBvoc.txt \ ./Examples/Monocular/EuRoC.yaml \ ~/Datasets/EuRoC/MH01 \ mav0/cam0/data \ ./Examples/Monocular/EuRoC_TimeStamps/MH01.txt \ dataset-MH01_mono.txt我逐个说参数的含义。第一个是词袋文件ORBvoc.txt,路径不能错;第二个是相机内参和IMU参数文件;第三个是数据集根目录,这里我用的是绝对路径,推荐你也在测试时用绝对路径,避免相对路径带来的环境依赖;第四个是相对dataset_path的左目图像目录,所以是mav0/cam0/data;第五个是时间戳文件,ORB-SLAM3仓库里已经为每个Euroc序列准备好了单独的txt;最后一个是你想保存的轨迹文件名。
运行后,终端会输出当前处理的图像索引、特征点数量、跟踪状态等。如果你只看到图像编号在涨,但地图点数量一直是0,多半是yaml里的内参和数据集不匹配,或者图像加载路径不对。
单目命令输出的轨迹文件格式是每行timestamp tx ty tz qx qy qz qw,这正好是TUM格式。后面评估时可以直接用。
3.2 双目模式:为什么直接给scale没问题
双目命令在单目基础上增加了一个右目路径参数:
./Examples/Stereo/stereo_euroc \ ../Vocabulary/ORBvoc.txt \ ./Examples/Stereo/EuRoC.yaml \ ~/Datasets/EuRoC/MH01 \ mav0/cam0/data \ mav0/cam1/data \ ./Examples/Stereo/EuRoC_TimeStamps/MH01.txt \ dataset-MH01_stereo.txt双目模式的关键在于左右目时间戳必须严格对应。ORB-SLAM3内部会通过时间戳文件同步左右目图像,如果时间戳文件有问题,会出现右目图像加载失败或特征匹配数量骤降。
另外,双目模式下EuRoC.yaml里的Camera.fps、Camera.bf(双目基线乘焦距)这些参数不能乱改。Euroc数据集的基线是固定的,yaml里已经配好,改了会直接导致深度估计错误。我在刚开始测试时,为了让某个序列跑得更好,去调整了Camera.bf,结果轨迹明显歪掉,后来重置回去才恢复。
3.3 单目+IMU与双目+IMU:IMU初始化带来的效果差异
单目+IMU命令:
./Examples/Monocular-Inertial/mono_inertial_euroc \ ../Vocabulary/ORBvoc.txt \ ./Examples/Monocular-Inertial/EuRoC.yaml \ ~/Datasets/EuRoC/MH01 \ mav0/cam0/data \ mav0/imu0/data.csv \ ./Examples/Monocular-Inertial/EuRoC_TimeStamps/MH01.txt \ dataset-MH01_monoi.txtIMU模式的输入多了一项mav0/imu0/data.csv。这里要注意,data.csv是Euroc官方给的IMU数据文件,列包括时间戳、陀螺仪和加速度计读数。ORB-SLAM3会读取这个文件,并通过时间戳与图像帧对齐。
单目+IMU最大的看点是IMU初始化。ORB-SLAM3在启动时会尝试用视觉轨迹和IMU测量联合估计初始重力方向、速度、尺度,这个过程通常需要几秒钟的激励运动。如果你跑的序列太短或者运动太平缓,初始化可能失败,程序会一直卡在IMU initializing的状态。MH01序列运动幅度刚好,是比较安全的测试选择。
双目+IMU命令:
./Examples/Stereo-Inertial/stereo_inertial_euroc \ ../Vocabulary/ORBvoc.txt \ ./Examples/Stereo-Inertial/EuRoC.yaml \ ~/Datasets/EuRoC/MH01 \ mav0/cam0/data \ mav0/cam1/data \ mav0/imu0/data.csv \ ./Examples/Stereo-Inertial/EuRoC_TimeStamps/MH01.txt \ dataset-MH01_stereoi.txt双目+IMU是四种模式里精度上限最高的,因为双目直接给了尺度,IMU又提供了帧间的约束和重力方向。测试时你会发现它比纯双目的跟踪更稳,快速转动时不容易丢,这就是IMU预积分的作用。
3.4 运行输出文件内容:时间戳和轨迹格式
上面每个命令最后的dataset-*.txt是轨迹输出路径。ORB-SLAM3会在每处理完一帧后追加一行,格式为:
timestamp tx ty tz qx qy qz qw其中四元数顺序是x, y, z, w。这个格式可以直接被evo识别为TUM格式。但这里有个细节:Euroc的ground truth中四元数顺序是w, x, y, z,就用原始数据时要注意转换。用evo读取euroc格式的话,它会自动处理,如果你自己写脚本比对,就必须先统一四元数顺序。
4. 在Euroc上评估精度:别只会看漂移曲线
4.1 官方评估脚本还是evo?我的选择
ORB-SLAM3仓库里有一个evaluation目录,里面包含evaluate_ate_scale.py、evaluate_rpe.py等脚本,用于复现论文中的ATE和RPE。这些脚本需要Python2或Python3,还依赖numpy、matplotlib。如果你只是偶尔跑几个序列,用官方脚本足够,但每次都要处理时间戳关联、参数对齐,脚本的输入输出格式也比较“实验室化”。
我更推荐用evo。它是一个专门评估SLAM轨迹的工具,支持TUM、KITTI、Euroc等格式,一行命令就能算ATE/RPE,还能出图。安装很简单:
pip install evo --upgrade --no-binary evo用--no-binary evo是为了避免预编译包在某些环境下的兼容问题。我的实际使用体验是,evo在Python 3.8上非常稳定,对比结果清晰,而且能自动处理尺度对齐和SE(3)对齐。
4.2 具体评估命令与对齐方式
用evo评估Euroc测试结果,我通常分两步。第一步把ORB-SLAM3保存的轨迹文件转换成可评估的格式,实际上不用转换,直接用TUM格式读取即可。第二步用真值文件评估。
Euroc序列的真值文件是mav0/state_groundtruth_estimate0/data.csv,格式是:
timestamp, p_RS_R_x, p_RS_R_y, p_RS_R_z, q_RS_w, q_RS_x, q_RS_y, q_RS_z, ...用evo读取:
evo_ape euroc \ ~/Datasets/EuRoC/MH01/mav0/state_groundtruth_estimate0/data.csv \ dataset-MH01_mono.txt \ -a \ -s \ --plot参数-a表示进行SE(3)/Sim(3)对齐(取决于是否加-s)。-s表示同时估计尺度,针对单目轨迹必须加;对于双目或IMU模式,因为尺度已经是米制,可以不加-s,但如果轨迹和真值之间存在微小尺度差,加了也无妨,作为参考。
如果想看相对位姿误差RPE,用:
evo_rpe euroc \ ~/Datasets/EuRoC/MH01/mav0/state_groundtruth_estimate0/data.csv \ dataset-MH01_mono.txt \ -a \ -s \ --plot注意,无论是eval脚本还是evo,最关键的是时间戳关联。ORB-SLAM3保存的轨迹时间戳来自图像时间戳,而真值时间戳覆盖整个采集过程,两者的起点不一定完全一致。evo会自动做时间戳匹配,但如果你看到“matching failed”之类的提示,就要检查轨迹文件最后有没有写完整,或者时间戳文件是否有缺失。
4.3 一组典型的Euroc测试结果解读
以我自己在MH01上的实测为例,大致范围是这样的(机器配置不同会有波动,仅供参考):
| 模式 | ATE(米) | 备注 |
|---|---|---|
| 单目 | 0.15~0.35 | 尺度对齐后,受初始化影响大 |
| 双目 | 0.03~0.08 | 尺度准确,长走廊容易漂 |
| 单目+IMU | 0.02~0.05 | IMU初始化成功后很稳 |
| 双目+IMU | 0.01~0.04 | 四种模式中表现最好 |
看结果时不要只看ATE数字,还要看轨迹的误差曲线是否有突变。比如单目模式可能在某个转角的误差突然飙升,这通常说明局部地图退化或跟踪丢失重定位。这种定性的观察能帮你快速定位算法的瓶颈。
5. 实测中高频出现的四个坑与排查思路
5.1 一运行就段错误:先查路径,再查yaml权限
我遇到过好几次,编译一切正常,一运行mono_euroc立刻Segmentation fault。第一次我怀疑是OpenCV库冲突,查了半天,最后发现是数据集路径传错了——我传成了~/Datasets/EuRoC/MH01/mav0,而程序内部拼接图像路径时又加了一次mav0/cam0/data,导致找不到任何图片,读取空向量时越界。
排查这类问题,最快的方式是先确认数据集目录结构:
ls ~/Datasets/EuRoC/MH01/mav0/cam0/data | head能正常列出png文件,再检查命令行参数。还要注意路径中不要有中文和空格,ORB-SLAM3对这类路径处理得很脆弱。
另一类段错误和yaml文件有关。如果EuRoC.yaml中的Camera.fx、Camera.fy被改成了0或异常值,三角化时会除零。建议直接用仓库自带的yaml,不要自己从头写。
5.2 IMU轨迹发散:问题大多出在时间戳或yaml的IMU参数
我在跑V2_02序列时,单目+IMU的轨迹跑着跑着就飞了。排查过程很有意思:单目纯视觉没问题,双目纯视觉没问题,单目+IMU就发散。后来我把IMU数据csv的前几行和图像时间戳做了对比,发现两者起始时间差了几毫秒,但ORB-SLAM3在初始化IMU时对时间偏差很敏感。
这类问题我最终的解决方案是:使用仓库自带的EuRoC_TimeStamps时间戳文件,不要自己从data.csv里裁剪生成。这些时间戳文件是官方预处理过的,和图像、IMU的时间戳可以对齐。另外,检查EuRoC.yaml里的IMU.Frequency是否为200,这个是Euroc IMU的实际频率,填错会让预积分的时间间隔全部错掉。
5.3 Pangolin窗口卡死或无响应:和GPU驱动、显示环境强相关
有几次我在远程服务器上跑,用X11转发显示Pangolin窗口,结果窗口出现不到一秒就卡死,程序也失去响应。这是因为Pangolin的可视化需要OpenGL支持,远程转发的GL性能很差。换到本地带GPU的机器上,一切正常。
如果你必须在无显示环境或远程环境测试,建议关闭可视化。最简单的方式是修改Examples/Monocular/mono_euroc.cc这类示例程序,把创建Viewer的代码注释掉,或者修改System构造时bUseViewer参数为false。当然这会改变源码,只适合自己实验。如果不想改源码,可以用xvfb-run提供一个虚拟显示,但可能影响Pangolin渲染效率。
5.4 别人能复现的结果你复现不了:先检查编译类型和随机性
ORB-SLAM3论文里的数值是在特定机器、特定ROS版本、特定编译优化下得到的,直接复现时差几倍都很正常。我这里想提醒的是,build.sh默认的cmake类型是Release,但如果你自己cmake时用了Debug模式,跟踪速度和精度都会显著下降。用cat CMakeLists.txt就能确认,一定要保证是-DCMAKE_BUILD_TYPE=Release。
另外,SLAM算法本身的特征点提取和优化存在线程调度上的随机性,同一个序列跑两遍,ATE也会有细微差异。如果你想写进报告,最好每个配置跑三到五遍,取中位数或平均值,并记录范围。别拿一次运行的结果下结论。
6. 让Euroc测试从跑通到高效的三个小习惯
6.1 写一个shell脚本批量跑完所有序列
手动一条条敲命令太容易漏参数了。我习惯把要跑的序列列表放在一个数组里,用循环批量执行。下面这段脚本可以按需修改,核心是把数据集根目录、时间戳路径、输出文件名做成变量:
#!/bin/bash VOCAB=../Vocabulary/ORBvoc.txt DATASET_ROOT=~/Datasets/EuRoC MODE=stereo_inertial EXE=./Examples/Stereo-Inertial/stereo_inertial_euroc YAML=./Examples/Stereo-Inertial/EuRoC.yaml TS_ROOT=./Examples/Stereo-Inertial/EuRoC_TimeStamps for seq in MH01 MH02 MH03 MH04 MH05; do echo "Running ${seq} ..." $EXE $VOCAB $YAML $DATASET_ROOT/$seq \ mav0/cam0/data mav0/cam1/data mav0/imu0/data.csv \ $TS_ROOT/$seq.txt \ result_${MODE}_${seq}.txt done注意每个模式的时间戳文件目录不同,比如单目模式在Examples/Monocular/EuRoC_TimeStamps,双目模式在Examples/Stereo/EuRoC_TimeStamps,IMU模式在对应Monocular-Inertial或Stereo-Inertial目录。批量跑之前先单独跑一个序列验证路径无误,再全量执行,否则脚本会带着错误路径跑完所有序列,浪费时间。
6.2 记录硬件与系统状态,避免结果无法追溯
SLAM的测试结果跟CPU频率、内存、是否开启Turbo Boost、显卡都有关系。批量跑之前,我通常先把系统信息保存到文件里:
lscpu > test_env.txt free -h >> test_env.txt nvidia-smi >> test_env.txt 2>/dev/null cat /etc/os-release >> test_env.txt如果之后想对比不同机器上的数据,这些信息非常有用。更重要的是,测试过程中要留意程序占用的CPU线程数。ORB-SLAM3默认用四线程加载特征点,如果机器只有双核,跑起来会明显变慢,结果也会受影响。
6.3 输出文件统一命名,方便评估脚本批量处理
我给每个结果文件命名时,遵循模式_序列名.txt的规律,比如mono_MH01.txt、stereoi_V201.txt。评估时可以直接用shell通配符批量处理,不用每次手动输入文件名。另外,轨迹文件不要存放在ORB-SLAM3源码目录里,单独建一个results文件夹,免得后面重新编译时被清理掉。
还有一个细节:时间戳文件里如果存在重复或乱序的时间戳,ORB-SLAM3可能会卡在等待图像那里。批量跑完一遍后,检查一下输出轨迹的行数是否和序列图像数量大致一致,如果少很多,大概率是中间丢帧或崩溃了,这种结果直接归档容易误导后面的对比。
踩过几次坑之后,我现在跑Euroc测试的顺序固定是:先检查目录结构,再确认yaml参数,跑一个短序列验证,再批量执行,最后统一评估。这套流程看起来很笨,但能最大程度避免“跑了半天结果无效”的尴尬。ORB-SLAM3的工程细节确实多,但只要把Euroc这个标准测试跑顺了,后面换其他数据集、加传感器融合,都会从容很多。