
拖了挺久的ORB-SLAM3单目IMU模式这周总算在Ubuntu 20.04上完整跑通了。之前跑过纯单目也跑过双目但单目IMU一直卡在初始化不稳和轨迹漂移上。我把从编译依赖、参数文件到跑EuRoC数据集、评估轨迹的整个过程踩过的坑和判断逻辑完整记一遍。这份记录适合两种人看一是刚把ORB-SLAM3源码编译通过、正准备跑单目IMU的同学二是已经在跑但遇到初始化失败、yaw漂移、轨迹不对这些问题的人。1. 单目IMU和纯单目之间差的不只是传感器1.1 单目SLAM的老问题尺度漂移和初始化脆弱纯单目SLAM的本质问题是尺度不可观。一张2D图像只能给出像素坐标无法直接给出物理距离所以单目系统估计出来的轨迹和地图都只有一个“相对尺度”这个尺度还会随时间缓慢漂移。更麻烦的是初始化纯单目得靠相机主动产生足够的视差才能三角化出初始地图如果一开始对着白墙平移或者原地旋转初始化很大概率会失败。ORB-SLAM3的纯单目模式其实已经做得很好有自动初始化、多地图系统但工程里只要涉及测量、导航、避障这类需求就离不开尺度。你总不能拿一个不知道1米等于多少像素的轨迹去做距离测算。这也是我为什么非要折腾单目IMU的原因——IMU能把尺度拉回米制让轨迹真正“可用”。1.2 IMU在ORB-SLAM3里的实际角色IMU在单目IMU系统里做的事情不是简单“加一个传感器”而是同时承担好几项任务帧间的粗略位姿传播。相邻两帧图像之间如果IMU频率够高常见200Hz可以在没有视觉匹配的情况下估算出相机的相对运动这给视觉匹配提供了很好的初始值。重力方向对齐。IMU能感知重力向量这意味着系统能知道“下”在哪轨迹自然会从任意坐标系被掰到重力对齐的坐标系里体现在轨迹上就是roll和pitch会被重力约束住。尺度估计。通过加速度计的积分IMU能给视觉轨迹提供一个物理单位上的约束ORB-SLAM3在初始化阶段会把尺度因子作为待估变量一起优化。但IMU不是万能的。它不能直接约束yaw绕重力方向的旋转这也就是很多人跑单目IMU时发现yaw还是会慢慢飘的原因。后面我会单独讲这个。ORB-SLAM3在视觉惯性初始化上做了不少文章尤其是它把重力方向、速度、陀螺仪零偏、加速度计零偏和尺度因子放在一起做最大后验估计而不是像早期一些方案那样分步求解。这带来的好处是初始化对噪声更鲁棒坏处是——一旦你给它的初始运动不够丰富它一样会初始化失败。1.3 我先跑通的数据集和运行方式实践时我优先选择EuRoC数据集而不是自己拿真机采数据。原因很简单EuRoC的相机内参、IMU噪声特性、时间戳同步都是官方标定好的还带运动捕捉系统的真值轨迹方便后面用evo做精度评估。我的基础环境如下后面的所有操作都以这套组合为基准Ubuntu 20.04gcc 9.4OpenCV 4.2.0Eigen 3.3.7Pangolin 0.6ORB-SLAM3源码GitHub上的官方版本跑的是EuRoC的MH_01到MH_05五个机械臂序列以及后来补了一部分TUM-VI的数据做对比。2. 环境配置和编译90%的“运行失败”其实倒在这里2.1 依赖版本怎么选才少踩坑很多人拿ORB-SLAM3的源码下来直接./build.sh然后报错了才回来问。我建议在编译之前先把依赖版本定好否则后面改起来很痛苦。下面是我实测下来比较稳的组合依赖推荐版本备注OpenCV3.4.x 或 4.2.x太高比如4.8会有一堆API兼容问题太低则部分示例代码编译不过Eigen3.3.x3.4也能编但老代码有些API要小改Pangolin0.6用于显示轨迹和地图不要用最新masterAPI变化大boost1.71DBoW2依赖Ubuntu 20.04自带就行g2o/DBoW2/Sophus源码Thirdparty内自带不需要自己装但需要能正常加载子模块ORB-SLAM3的Thirdparty里有内置的DBoW2、g2o、Sophus编译脚本会先编这些。如果你的源码是Git clone下来的千万不要漏掉--recursive否则Thirdparty里是空的后面怎么编都过不去。2.2 OpenCV4与老代码之间的兼容性处理ORB-SLAM3发布的时候OpenCV3还是主流所以源码里难免有一些OpenCV3时代的写法。最典型的几个CV_LOAD_IMAGE_UNCHANGED在OpenCV4里已经没了要改成cv::IMREAD_UNCHANGED。CV_FONT_HERSHEY_SIMPLEX、CV_RGB这类宏在OpenCV4里要改成cv::FONT_HERSHEY_SIMPLEX、cv::RGB。个别文件里#include opencv/cv.h这种老路径也会编译失败改成#include opencv2/opencv.hpp。如果编译时遇到fatal error: opencv/cv.h: No such file or directory不用怀疑就是这个问题。我一开始图省事装了OpenCV 4.5结果遇到好几处这种报错后来直接降到4.2基本就没再因为OpenCV版本折腾过。2.3 g2o、Eigen与编译器的经典冲突另一个高频报错是Eigen的内存对齐问题。如果你看到类似/Eigen/src/Core/PlainObjectBase.h: Error: static assertion failed: YOU_MIXED_VECTORS_OF_DIFFERENT_SIZES或者各种EIGEN_MAKE_ALIGNED_OPERATOR_NEW相关警告多半是因为别的库比如Pangolin在Eigen内存对齐设置上和ORB-SLAM3不一致。我的处理办法是尽量用官方build.sh默认的编译选项不要自己加-marchnative之类的额外优化否则容易踩到AVX指令集和Eigen对齐的坑。还有usleep报未声明的情况。ORB-SLAM3里有几个文件用了usleep但C11标准下它不在全局命名空间里。编译报错‘usleep’ was not declared in this scope时去对应源文件头部补上#include unistd.h就行这个坑在不少SLAM项目里都有。2.4 编译流程与常见报错对照我实际的编译流程很简单cd ORB_SLAM3 chmod x build.sh ./build.sh第一次编译会比较久主要是Thirdparty里的g2o和DBoW2会全部编一遍。如果只想先跑Examples可以只编Examples对应的部分但我建议还是完整编译后面跑各种模式不用来回折腾。我整理了一份编译期常见报错对照表遇到类似问题可以直接照着处理报错片段常见原因处理办法opencv/cv.h: No such fileOpenCV版本过新或路径不对检查OpenCV版本把旧宏替换为OpenCV4写法usleep was not declaredC11下unistd未包含在对应cpp文件加#include unistd.hInvoking cmake failedThirdparty子模块缺失或依赖没装全确认clone时带--recursive确认Pangolin等已编译Eigen对齐相关警告/报错编译器优化选项和Eigen不匹配用默认build.sh不加额外架构优化参数undefined reference to boost::...boost库链接缺失安装lboost系列sudo apt install libboost-all-dev3. EuRoC数据集准备与第一条单目IMU轨迹3.1 ASL格式与TUM格式EuRoC默认用哪一种EuRoC数据集官方提供两种格式ASL格式和TUM格式。ORB-SLAM3的官方示例程序比如mono_inertial_euroc、stereo_inertial_euroc读的是ASL格式的目录结构。ASL格式的状态是这样一个目录MH_01/ ├── cam0/ │ ├── data/ │ │ ├── 1403715282262142976.png │ │ └── ... │ └── data.csv ├── cam1/ │ ├── data/ │ └── data.csv ├── imu0/ │ ├── data.csv └── state_groundtruth_estimate0/ └── data.csv如果你下载的是TUM格式里面通常是mav0/cam0/data、mav0/imu0/data.csv这种带mav0前缀的结构。直接用ORB-SLAM3官方示例去跑TUM格式会找不到路径。两种办法解决一是把TUM格式改成ASL格式的目录结构二是写个小脚本把CSV时间戳和图片串起来。我图省事直接下载的ASL格式。3.2 运行单目IMU命令的每一步拆解编译完成后进入ORB_SLAM3根目录运行单目IMU的EuRoC示例./Examples/Monocular-Inertial/mono_inertial_euroc \ Vocabulary/ORBvoc.txt \ Examples/Monocular-Inertial/EuRoC.yaml \ ~/Data/EuRoC/MH_01 \ Examples/Monocular-Inertial/FrameTrajectory_TUM_Format.txt四个参数分别是ORB词典路径、单目IMU配置文件路径、数据集目录路径、输出轨迹文件路径。注意最后一个参数是程序运行后自动生成的文件里面保存的是逐帧的轨迹格式对齐TUM方便后续用evo评估。跑起来之后你会看到Pangolin窗口弹出左边是当前关键帧和ORB特征点右边是相机位姿和地图点。终端会持续输出当前帧号、处理耗时、共视关键帧数量等信息。3.3 启动阶段该怎么判断系统状态单目IMU启动阶段最关键的是看IMU初始化是否完成。系统启动后并不会立刻建图而是先让IMU预积分积累信息同时视觉SLAM开始做初始化。只有视觉初始化和IMU初始化都完成后状态才会切到正常跟踪。我的判断依据有三点终端日志里出现IMU初始化成功的标志不同版本日志措辞不同但通常会有IMU initialized之类字样。Pangolin窗口里相机位姿突然“定住”并开始稳定输出而不是来回跳。轨迹文件里有持续的、帧间位移合理的位姿输出。如果程序跑了几秒钟还在原地打转或者终端反复打印初始化失败的警告先别急着改代码先看运动是否给够了。单目IMU初始化需要一个“充分激励”的过程让IMU能观测到线加速度和角速度的变化。我实际跑下来先小幅平移再慢慢转体比纯平移或纯旋转都更容易初始化成功。4. 参数文件与IMU标定决定轨迹精度最关键的几个数值4.1 EuRoC.yaml关键参数逐个拆解ORB-SLAM3的Examples/Monocular-Inertial/EuRoC.yaml是官方针对EuRoC数据集调好的参数我强烈建议第一次跑不要改动任何数值先跑通再说。但如果你想跑自己的数据就必须理解每个参数到底在干什么。单目IMU相关参数里下面这几个最重要。相机内参部分Camera.fx: 458.654 Camera.fy: 457.296 Camera.cx: 367.215 Camera.cy: 248.375 Camera.k1: -0.28340811 Camera.k2: 0.07395907 Camera.p1: 0.00019359 Camera.p2: 1.76187114e-05 Camera.fps: 20.0 Camera.RGB: 1fx、fy、cx、cy是相机内参单位是像素k1、k2、p1、p2是畸变系数。很多人在自己数据上跑飞就是因为换了相机却忘了改内参。注意ORB-SLAM3使用的是针孔模型加径向切向畸变如果你用的是鱼眼相机光改这几个参数远远不够还得换相机模型。IMU参数部分IMU.NoiseGyro: 1.7e-4 IMU.NoiseAcc: 2.0e-3 IMU.GyroWalk: 1.0e-5 IMU.AccWalk: 3.0e-5 IMU.frequency: 200.0这几个数值是IMU的噪声密度和零偏随机游走单位分别是rad/s/sqrt(Hz)、m/s^2/sqrt(Hz)、rad/s^2、m/s^3。它们决定了IMU在优化里的权重噪声密度越小IMU的约束越“硬”视觉相对越“软”反之IMU几乎不起作用。如果你用的是自己的IMU不要照抄EuRoC数值。从IMU芯片手册里查Allan方差曲线或者用imu_utils工具标定出来再填进去。乱填的结果就是轨迹不是飘就是抖。还有频率参数IMU.frequency必须和你数据里IMU实际频率一致。EuRoC是200HzTUM-VI也是200Hz左右但很多自采设备是100Hz或者500Hz填错了预积分的时间戳会全乱。4.2 自采数据时间戳对齐和相机-IMU外参标定才是大头我最想强调的一点单目IMU“跑不出来”或者“轨迹发散”绝大多数时候不是ORB-SLAM3本身的问题而是数据没喂对。时间戳对齐是第一个坑。单目IMU要求图像时间戳和IMU时间戳在同一个时钟域下而且延迟要足够小。你如果用一个电脑时间戳采图像、另一个系统时间戳采IMU开机后两边时钟不同步差个几十毫秒系统就会出现严重的拖影式漂移和频繁重定位。第二个坑是相机-IMU外参。ORB-SLAM3的配置文件里通过Calib相关参数定义相机坐标系和IMU坐标系之间的旋转和平移关系。如果你把IMU歪着装又不标定外参靠手量几个厘米塞进去重力对齐基本不会准初始化也容易失败。我自己的经验是用kalibr这类工具做相机-IMU联合标定输出T_cam_imu之后再转成ORB-SLAM3需要的格式。4.3 三个值得一试的调参经验跑完官方EuRoC之后我开始尝试改参数总结出三条实际调参感受不要用纯旋转序列调IMU参数。纯旋转时加速度计没有足够的激励你调出来的NoiseAcc基本是错的换到正常运动序列上立刻露馅。NoiseGyro对yaw漂移的影响比NoiseAcc更直接。如果近距离轨迹看着还行但yaw在几十秒内明显偏转先查时间戳和陀螺仪零偏估计不要盲目把NoiseGyro调大那只会让系统更信任一个零偏没标定的陀螺仪。如果初始化总是不成功可以看是不是重力对齐在作怪。单目IMU初始化时要估计重力在相机坐标系下的方向如果一开始相机几乎不动重力方向无法被准确观测初始化就会拖很久。5. 轨迹评估与漂移排查从“能跑”到“跑得准”5.1 用evo评估ATE和RPEORB-SLAM3运行结束会生成轨迹文件默认是FrameTrajectory_TUM_Format.txt。要量化轨迹精度我习惯用evo工具。安装很简单pip install evo --upgrade --no-binary evo然后对EuRoC结果做ATE评估evo_ape tum FrameTrajectory_TUM_Format.txt \ ~/Data/EuRoC/MH_01/mav0/state_groundtruth_estimate0/data.csv \ -a-a参数表示自动对齐轨迹因为单目IMU虽然恢复了尺度但初始坐标系和真值坐标系之间还是有个刚体变换要先对齐再比较。RPE相对位姿误差可以这样看evo_rpe tum FrameTrajectory_TUM_Format.txt \ ~/Data/EuRoC/MH_01/mav0/state_groundtruth_estimate0/data.csv \ -a --delta 1--delta 1表示比较每间隔1米的相对位姿误差。我跑下来的典型结果是MH_01到MH_05的ATE RMSE大约在0.05到0.2米之间视具体序列和初始化运气而定。其中MH_01这种简单环形运动最稳MH_05那种带快速旋转和遮挡的序列就要看启动几秒钟的身体动作是否给够。5.2 yaw慢漂到底是哪里出的问题很多人跑完单目IMU后发现一个现象不平移的时候roll和pitch都很稳但yaw会慢慢偏。这不是ORB-SLAM3的bug而是单目IMU的固有退化方向。原因要从可观性讲。IMU的加速度计只能观测到重力方向也就是把roll和pitch约束住yaw是绕重力方向的旋转加速度计完全观测不到陀螺仪零偏又会让yaw积分慢慢漂。所以单目IMU系统里yaw的长期稳定性主要靠视觉和回环来维持。如果你发现yaw漂移速度异常快十几秒就能看出来偏了那就要反向排查了陀螺仪零偏是否标定准确零偏估计如果偏差大yaw积分会线性漂移。时间戳是否稳定图像和IMU之间的延迟抖动会引入额外的旋转误差。是不是一直对着低纹理场景旋转视觉约束一旦失效yaw就只能靠IMU裸奔漂移自然快。如果是正常范围内的慢漂不用太焦虑加回环检测、跑通回环后yaw会被重新拉回来。5.3 初始化失败的正确排查顺序单目IMU初始化失败的排查我建议严格按这个顺序来不要一上来就改参数看运动是否充分。启动后有没有平移、旋转、加减速如果只是把相机静静放在桌上IMU完全无法估计出可靠的重力向量和尺度初始化不可能成功。看时间戳。IMAGE和IMU的时间戳是否单调递增有没有跳变、重复、大间隔看内参和畸变系数是否与实际一致。自采数据尤其要确认用错内参时视觉特征匹配和三角化都会异常。看IMU频率是否写对。配置文件里的IMU.frequency如果和实际数据不一致预积分会错乱。看日志中视觉SLAM是否先初始化成功。有时候是视觉地图还没建出来IMU初始化就卡在等待状态。我遇到的最典型情况是用一张自采数据集图像时间戳来自相机SDKIMU时间戳来自另一台设备初始化长时间完不成。后来把两个流统一到同一时钟源重采一遍就好了。6. 跑通之后的几条务实建议6.1 启动瞬间的动作决定成败我必须单独拎出来说在单目IMU模式下启动后前几秒的运动模式对后续整段轨迹质量影响极大。这算是ORB-SLAM3这种VI-SLAM的通病。我的使用习惯是开始录制数据时先让设备静止1到2秒让IMU零偏估计有个基准然后缓慢平移加转弯不要让相机猛地甩动。猛甩会导致图像运动模糊特征点到期帧匹配不上IMU预积分却还在累积系统很容易在初始化阶段就直接挂掉。如果是自己写程序做真机部署建议在系统里主动加一个“运动提示”逻辑检测到IMU激励不足时提示操作者先转几个方向再进入正式建图。6.2 单目测距与尺度恢复的实战注意点既然搜索词里有“单目测距”我多说一句和ORB-SLAM3单目IMU相关的事。单目IMU能把尺度恢复到米制所以理论上它可以替代一部分单目测距工作但和真正测距相比还是得小心。首先要明确ORB-SLAM3输出的是相机轨迹和稀疏地图点不是深度图。要测某个目标的距离通常需要先知道目标在地图中的3D位置再算它到相机光心的距离。而且单目IMU恢复的尺度虽然大体准确但会受IMU零偏残差影响长距离长时间使用后会积累尺度误差。所以我的建议是基于ORB-SLAM3做单目测距时尽量让相机在目标附近有足够的平移视差不要站在一个位置不动去测远处的物体同时开启回环回环发生时系统会修正累计漂移测距结果会明显更稳。6.3 从数据集到真机的检查清单最后给一份我在自采数据前会过的清单照着做能省掉大半“跑飞”的排查时间确认相机和IMU刚性固定中间没有软连接和明显振动。确认图像和IMU时间戳来自同一时钟源或已经做过软同步。确认相机内参、畸变系数已经标定不要把网络随便找的规格书数值直接填进去。确认相机-IMU外参已经标定坐标系转换关系在配置文件中正确填写。确认IMU频率、噪声密度、随机游走数值来自Allan方差标定或者主题规范。启动设备后先静止片刻再做缓慢的平移和旋转给足初始化激励。观察前几秒日志确认视觉初始化和IMU初始化都成功后再开始正式移动。从ORB-SLAM2一路用过来单目IMU模式确实把SLAM的可用性往前推了一大截。我实际跑下来MH序列里最好的结果ATE RMSE能到5厘米以内最差的MH_05也就十几厘米的水平作为视觉惯性里程计来说已经能在不少工程场景里直接用了。如果你也卡在单目IMU这条路上希望这份记录能帮你少走几步弯路。