ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ORB-SLAM3核心原理与实战:从编译到调参的完整指南

ORB-SLAM3核心原理与实战:从编译到调参的完整指南 ORB-SLAM3算是视觉SLAM里绕不开的一个名字了。从它开源那天起很多做机器人、无人机、AR定位的团队都拿它当基线系统有人在这上面做二次开发有人用它跑数据集做评测也有人纯粹是想搞清楚它内部那套“稀疏特征多地图视觉惯性融合”是怎么串起来的。这篇博文我就以自己从编译到跑通、再到改参数的实际经历为主线把ORB-SLAM3的结构、原理、实操和坑都过一遍。内容尽量保持“说人话”该贴公式的地方给结论该上命令的地方直接给可复制版本希望能帮刚接触它的人少走点弯路。1. ORB-SLAM3整体架构与设计思路拆解1.1 三个并行线程在一套系统里怎么分工ORB-SLAM3延续了ORB-SLAM系列的骨架核心是三个并行线程跟踪线程、局部建图线程、回环检测与地图融合线程。这三个线程从名字上看像是“各干各的”实际上它们之间通过关键帧、地图点、共视图这些数据结构保持着非常紧密的协作。跟踪线程的任务最直接就是估计当前帧的相机位姿并判断当前帧能否成为关键帧。它拿到一帧图像以后先提取ORB特征点然后用恒速运动模型或者上一帧的位姿做初始猜测再把地图点投影到当前帧上去找特征匹配。这个过程做完以后会跑一个以重投影误差为目标函数的优化只优化当前帧的位姿不碰地图点所以非常快。如果跟踪丢了它会启动重定位模式靠词袋模型在已有多地图里找回位置。局部建图线程负责管理地图点、筛选关键帧、执行局部BA。它会把新进来的关键帧里观测到的特征点三角化成新的地图点同时剔除那些观测质量差、误差大的点和冗余的关键帧。因为局部BA只优化共视图里邻近的关键帧和地图点所以计算量可控这也是ORB-SLAM3能在CPU上实时跑的原因。回环检测线程是保证全局一致性的关键。它用DBoW2词袋对当前关键帧做历史相似性检索如果发现回环就计算Sim3变换来修正累计漂移然后在本质图上做全局优化。到了ORB-SLAM3这一代这里还多了“地图融合”的职责因为Atlas里不止一张地图回环检测不仅要修位姿还要判断当前帧是不是回到了其他地图如果是就得把多张地图合并成一张。三个线程的结构不是ORB-SLAM3首创但它把每个线程的职责边界做得非常清楚模块之间通过关键帧队列和地图更新回调解耦。对想读源码的人来说这样的组织结构也特别好入门你可以从Tracking线程开始沿着关键帧的产生路径一条线读懂整个系统。1.2 Atlas多地图系统为什么需要“多地图”Atlas是ORB-SLAM3区别于前面版本的最大创新点。在ORB-SLAM2或PTAM这类传统系统里全局只有一张地图跟踪一丢地图要么被清空要么只能靠重定位找回。但单目相机在快速运动、遮挡、纯旋转等场景下跟踪丢失几乎是不可避免的一旦重定位失败前面建好的地图就废了。ORB-SLAM3的做法是维护一个地图集Atlas里面可以同时存在多张不连通的地图。当系统启动一个新的会话时如果跟踪连续丢了超过一定帧数它不急着重定位而是直接新建一张地图把当前帧作为这张新地图的初始关键帧。这样系统不会因为局部跟踪失败就“宕机”而是始终保持可用状态。后面如果检测到当前帧与旧地图产生了足够多的特征匹配再通过地图融合算法把两张地图合并。这个设计带来的直接好处是单目SLAM不再惧怕“短暂丢失”而且整个过程对用户无感。我以前用ORB-SLAM2跑单目出现丢失时要么手动重置要么等很久才重新定位换到ORB-SLAM3以后它会在后台悄悄开一张新地图继续跑等回环触发时再自动拼回去。地图融合在实现上依赖于回环检测提供的位姿约束。两个地图之间存在公共观测的区域会通过Sim3变换对齐再合并重复的关键帧和地图点最后对整个融合区域做一次全局BA。这个机制让系统的可扩展性提升了一个档次也让它能支持更长时间、更复杂轨迹的建图任务。1.3 传感器支持矩阵与紧耦合设计ORB-SLAM3支持的传感器组合是它另一个大卖点单目、双目、RGB-D三种视觉传感器可以单独用也可以分别与IMU组成视觉惯性组合。也就是说它一次性覆盖了单目、双目、RGB-D、单目IMU、双目IMU、RGB-DIMU六种配置。这种“一套代码全家桶”的设计便于团队在多个产品形态上复用同一套SLAM核心。更重要的是ORB-SLAM3的视觉惯性融合是紧耦合的不是把IMU的位姿估计和视觉的位姿估计做个加权平均。它在优化框架里把视觉重投影误差和IMU预积分误差放进同一个目标函数一起优化相机位姿、IMU偏置、速度和尺度/重力等状态量这样互相之间能校正鲁棒性比松耦合强很多。紧耦合带来的代价是初始化和标定更敏感。视觉单独工作可以“从零开始”但加入IMU以后系统必须估计IMU的偏置、重力方向、速度以及尺度单目时这套IMU初始化过程如果激活不充分后面的跟踪就容易飘。理解了这层关系你就能明白为什么作者在代码里给了那么多IMU初始化相关的参数也就能理解为什么跑视觉惯性数据集时不按数据集推荐的静止启动方式就很容易初始化失败。这个后面我会在实操部分展开。2. 核心原理拆解ORB-SLAM3到底强在哪里2.1 ORB特征为什么是视觉SLAM的最佳搭档ORB特征由两部分构成Oriented FAST关键点和Rotated BRIEF描述子。FAST角点检测速度极快但本身没有尺度和旋转不变性ORB通过构建图像金字塔解决尺度问题通过灰度质心法计算主方向再根据主方向旋转BRIEF采样模式恢复旋转不变性。在SLAM里用ORB特征最直观的好处是提取速度快、内存占用低、匹配效率高而且描述子本身就是二进制字符串可以直接用汉明距离匹配既快又省资源。相比SIFT或SURFORB在CPU上跑实时性要友好得多这对于嵌入式平台和机器人场景来说几乎是硬指标。但在实际使用中ORB特征也有明显短板低纹理环境下提取到的特征数量会急剧下降运动模糊和光照突变也会让匹配质量大幅退化。ORB-SLAM3在代码里默认每帧提取1000到2000个特征点算是稀疏特征方案里的均衡值特征太少容易跟踪丢失特征太多又会增加匹配和优化的耗时。另外ORB描述子计算时用到了机器码实现的位运算SIMD优化也方便。如果你在ARM板子上部署过会明显感受到ORB的实时性优势。这也是为什么很多工业界SLAM系统最终选ORB而不是学习型特征——在实时性、精度、功耗之间它确实是个很稳的折中方案。2.2 视觉惯性融合从松耦合到紧耦合视觉惯性SLAM里IMU提供的是高频、短时准确的角速度和加速度相机提供的是低频、长时稳定的视觉观测两者天然互补。松耦合的做法是分别跑一个视觉里程计和一个惯导解算再把结果融合比如用扩展卡尔曼滤波或因子图组合。这种做法实现简单但两个子系统各自的误差会直接输入到融合阶段很难做到互相修正。ORB-SLAM3选择在紧耦合框架里把视觉和IMU的误差项放在一起优化。它使用了IMU预积分技术将两个关键帧之间的IMU测量值预先积分成相对旋转、速度和位移增量避免在每次优化时都对IMU测量重新积分。这样每次优化时只需要线性化预积分项计算量大幅降低而且误差项的雅可比可以直接通过预积分公式求出来。代价是需要维护的状态量变多了。纯视觉SLAM通常只优化相机位姿和地图点视觉惯性SLAM还要额外优化IMU的速度、陀螺仪偏置、加速度计偏置单目情况下还要优化尺度和重力方向。这些额外的自由度让优化问题变得更高维但约束也更丰富。比如长时间纯视觉会出现的尺度漂移问题在双目或深度相机下不明显而在单目IMU情况下IMU的重力观测可以约束尺度漂移这也是为什么单目惯性方案能在EuRoC数据集上跑出低于0.6%的平均漂移率。紧耦合在数学上很漂亮工程上却很痛苦。IMU和相机的外参标定、IMU噪声密度、重力对齐、偏置估计每一项都要准确否则误差项之间会互相打架。ORB-SLAM3里对外参标定使用了一个可在线估计的变量也就是说即使标定不是太准系统在优化过程中也会尝试去修正它。这个设计在实际部署时非常有用。2.3 IMU初始化最容易被忽视却最关键的一步很多人在跑ORB-SLAM3视觉惯性模式时遇到的最典型问题就是“系统卡在初始化阶段不动了”或者初始化以后前几百帧漂移特别大。这多半不是代码问题而是IMU初始化没有完成。IMU初始化任务可以拆成两部分一是估计陀螺仪和加速度计的偏置二是估计视觉坐标系和IMU坐标系之间的重力方向、初始速度和尺度因子单目时。ORB-SLAM3把初始化过程建模成一个最大后验估计问题通过收集一段包含足够运动激励的帧序列同时优化偏置、重力、速度、尺度以及相机–IMU外参最后再送入视觉惯性BA里做精化。从实操角度讲初始化成功的前提是“运动要足够丰富”。你拿着设备文文静静放桌上不动或者只做非常缓慢的平移IMU激励不够加速度计的偏置和重力方向很容易耦合在一起初始化结果不靠谱。反过来如果一上来就剧烈甩动设备产生的运动模糊又会把视觉里程计带崩。作者在代码里设定的初始化窗口是15秒左右如果这15秒内没有产生足够多的有效关键帧和视差系统会一直停留在初始化等待阶段。所以正确的操作是启动后让设备做一些包含旋转和平移的“8字”运动持续几秒钟让IMU获得足够激励同时保持画面中有一定纹理内容这样初始化基本能在几秒内通过。2.4 回环检测、共视图与本质图优化回环检测在ORB-SLAM3里不止是“走回老地方时把漂移消掉”还承担着地图匹配和地图融合的判断职责。它使用的仍然是DBoW2词袋模型加载ORB词表以后每个关键帧都会被表示成一组带权重的视觉词向量检索时通过比较词袋向量的相似度快速找到候选帧。找到候选以后系统会验证几何一致性也就是计算当前关键帧与候选关键帧之间的Sim3变换再把候选关键帧附近的地图点投影到当前帧里做特征匹配。如果匹配数量足够就认为回环成立。这一步做完之后ORB-SLAM3会修正当前关键帧的位姿并把这个修正通过共视图传播到相邻关键帧最后在本质图上做一次全局图优化把累积漂移分配到整个图里。工程上比较比较有意思的一点是它用本质图Essential Graph而不是把所有关键帧之间的约束都纳入优化。本质图由生成树、共视关系强的边和回环边组成这样既保留了全局校正能力又控制了优化规模。理解了这个设计你就知道为什么ORB-SLAM3在做完回环修正后地图能“咔”一下被拉回正确位置而且整个过程对实时性影响很小。3. 手把手把ORB-SLAM3跑起来3.1 环境准备与依赖安装ORB-SLAM3本身是用C写的依赖项主要包括OpenCV、Eigen、Pangolin、DBoW2、g2o其中DBoW2和g2o直接从源码编译Pangolin负责可视化窗口。这里我以Ubuntu 20.04 OpenCV 4.2 Eigen 3.3为例梳理一份可直接执行的安装流程。sudo apt update sudo apt install -y build-essential cmake git pkg-config sudo apt install -y libeigen3-dev libopencv-dev sudo apt install -y libglew-dev libboost-all-dev libssl-dev sudo apt install -y libgtk2.0-dev libgtk-3-dev sudo apt install -y libpython3-dev python3-devEigen是纯头文件库安装后不需要额外配置。Pangolin如果直接用系统源安装版本可能偏旧你要是从ORB-SLAM3的Thirdparty目录编译它反而更省事。我试过用apt装Pangolin可视化窗口偶尔会崩溃最后换成编译源码就好了这也是建议大家优先用仓库自带的Thirdparty来编译的原因。编译前建议检查一下OpenCV版本ORB-SLAM3默认是为OpenCV 3写的OpenCV 4环境下编译一般会报一些函数命名空间的问题比较常见的是cv::xfeatures2d不存在这个其实不影响ORB-SLAM3因为它用的是内置ORB提取器不是OpenCV contrib模块。如果你编译时报错基本都是头文件路径或API命名问题网上都有对应修复方案后面排查部分我也列了几个高频坑。3.2 编译源码与数据集准备依赖装好以后拉取源码并编译这里不涉及ROS直接用官方build脚本git clone https://github.com/UZ-SLAMLab/ORB_SLAM3.git ORB_SLAM3 cd ORB_SLAM3 chmod x build.sh ./build.sh首次编译会连带编译Thirdparty里的DBoW2和g2o内存占用在4GB左右编译时间大概10到20分钟。如果你内存紧建议先把g2o的并行编译核数调小避免卡死。整个过程只要不报错最后会在Examples目录下生成一堆可执行文件。准备数据集我推荐EuRoC MAV它是一组无人机视觉惯性数据集同时提供双目图像、IMU数据和地面真实轨迹ORB-SLAM3论文里的很多实验就是在这个数据集上做的。下载以后解压目录结构大概是MH_01_easy这样里面有mav0/cam0、mav0/cam1、mav0/imu0等子目录保持这个结构别动因为ORB-SLAM3读取路径是写死的。跑双目惯性模式的命令./Examples/Stereo-Inertial/stereo_inertial_euroc \ ./Vocabulary/ORBvoc.txt \ ./Examples/Stereo-Inertial/EuRoC.yaml \ ./Datasets/EuRoC/MH_01 \ ./Examples/Stereo-Inertial/EuRoC_TimeStamps/MH01.txt跑起来以后你会看到Pangolin弹出一个窗口左边是当前图像的跟踪结果右边是这个序列的地图点云和相机轨迹。MH_01这个场景纹理比较丰富运动也比较平滑适合作为第一个跑通的序列。跑完以后终端会输出ATE绝对轨迹误差的统计值这就是用来评估精度的硬指标。3.3 用自己的相机和IMU跑起来数据集跑通以后大多数人会想用自己的传感器试。单目相机最简单先用标定板把内参标出来然后按照Examples/Monocular/里TUM1.yaml的格式写一个相机参数文件。需要注意ORB-SLAM3读的是针孔模型如果你用的是鱼眼相机需要先把图像矫正成针孔模型或者用支持鱼眼的配置。双目相机的标定比单目麻烦一点除了两个相机各自的内参还有双目标定得到的基线、旋转矩阵和畸变参数。ORB-SLAM3在yaml里只需要基线左右相机光心距离但畸变参数也要填正确否则特征三角化时会出现系统性的错位误差。带IMU的组合关键参数包括IMU噪声密度、陀螺仪随机游走、加计噪声密度以及IMU与相机的外参Tbc。这些参数通常可以通过Kalibr工具标出来。如果你的IMU内参标得不准最直接的影响是IMU预积分误差在优化里权重被错误放大系统会表现为跟踪不稳定、初始化失败或者几分钟后漂移明显。ORB-SLAM3虽然可以在线估计部分外参但内参和噪声密度这类参数还是尽量提前标好别想着靠SLAM本身帮你全部兜底。4. 调参与实践心得4.1 读懂ORB-SLAM3的yaml参数文件打开任何一个示例yaml文件你会看到一堆参数很多人直接不改就跑但实际场景不同合适的参数差异很大。我挑几个影响最直接的参数说说。相机内参行fx, fy, cx, cy对应标定得到的主点和焦距这几项不对特征重投影误差会整体变大系统可能频繁丢跟踪。畸变参数和畸变模型也要和标定结果一致ORB-SLAM3默认使用equal-distanceKannalaBrandt8或plumb_bobradtan两种模型模型选错哪怕参数写在文件里也是完全无效的。特征点数量在ORBextractor.nFeatures里配置默认是1000到2000。纹理匮乏场景建议适当提高到3000但每帧特征越多匹配和优化耗时越大你需要权衡。ORBextractor.nScaleLevels控制金字塔层数默认8层对于视差变化大的场景可以调到10到12提升尺度适应能力代价是内存占用变大匹配时间变长。IMU部分IMU.NoiseGyro、IMU.NoiseAcc是噪声密度单位是连续时间噪声密度Kalibr标定出来的通常就是这个单位。IMU.GyroWalk、IMU.AccWalk是随机游走参数对应偏置变化的速率。这几个参数直接参与了预积分协方差的计算写错会让优化里IMU约束的置信度判断失真最典型的表现就是初始化成功后系统依然不稳定或者静止时轨迹还在缓慢飘动。还有几个影响系统行为的开关参数比如System.Sensor它告诉系统当前用的是哪种传感器组合比如双目惯性模式的编号是3。这个数值如果填错了代码可能直接崩溃或者读取错误的模式所以拿到一个新yaml文件先检查这一行。4.2 不同场景下的调优策略我在做室内环境测试时常用配置是单目IMU光照比较稳定特征不算多把ORBextractor.nFeatures降到800反而能显著降低CPU占用同时跟踪稳定性没有明显下降。高速运动测试时我会把金字塔层数调高并开启更多跟踪候选点让特征在快速移动时仍然有足够多跨尺度的匹配对。低纹理场景比如白墙、走廊尽头是稀疏特征法的天然弱点。这种情况下单纯增加特征数量帮助不大因为根本没有可提取的点。我实际验证过两个有效手段一是提高图像分辨率再进算法让细小纹理能被FAST角点捕获二是在前端加一段直方图均衡化的预处理增强对比度让特征提取更敏感。ORB-SLAM3本身没有内置这些预处理所以在工程落地时经常需要自己做一层图像增强再喂给它。RFID等射频干扰环境对相机本身没影响但如果你的IMU是MEMS级别的电机振动、温度漂移都比较明显建议尽量使用高精度IMU或者在代码里适当调大IMU噪声密度降低IMU权重避免让被污染的IMU信号过度主导优化结果。我试过几次IMU噪声参数调偏一个数量级系统精度就会迅速劣化所以这个环节并不是放手不管就能自动适应的。4.3 工程部署中常见的性能优化手段ORB-SLAM3虽然能在主流CPU上实时运行但如果你想把它部署到Jetson、RK3588这样的嵌入式平台还是需要做加减法。第一降分辨率是最直接的优化手段很多场景640像素宽的分辨率已经足够得到稳定定位计算量却能直接减半。第二限制特征金字塔层数到6层甚至5层对大部分室内场景够用还能减少ORB提取和匹配时间。第三关闭Pangolin可视化窗口或者只在调试时开启可视化渲染本身会占用额外CPU。实测下来在Jetson Orin NX上将分辨率降到640p特征点降到800关闭可视化后ORB-SLAM3单目模式的CPU占用能控制在30%以下帧率维持30帧以上。如果你还要跑别的感知算法建议把跟踪频率降到15帧左右视觉SLAM在低帧率下依然能维持较好的精度只要场景不是高速运动就行。5. 常见问题与排查实录5.1 编译阶段的典型问题编译ORB-SLAM3最常遇到的坑就是OpenCV版本不兼容。比较典型的是在OpenCV 4环境下编译报错“usleepwas not declared in this scope”这个和OpenCV本身关系不大但常见于修改过系统库之后的环境解决办法是在出错的源文件里加#include unistd.h。另一个高频错误是“cv::imreadno matching function”这往往是因为代码里使用了CV_LOAD_IMAGE_UNCHANGED这类旧版宏在OpenCV 4里应该改成cv::IMREAD_UNCHANGED。还有一类问题来自缺少依赖库或库版本冲突比如pkg-config找不到OpenCV或者Eigen版本过低。ORB-SLAM3对Eigen版本要求不算苛刻但太老版本3.2以下会报缺少Eigen/Core的头文件。建议先更新系统的Eigen到3.3以上再重新编译。编译时还容易出现内存不足导致的GCC被系统杀死。解决方式是在build.sh里给cmake加-j2参数限制并行编译任务数。虽然编译时间会变长但稳定多了。5.2 初始化失败和跟踪丢失如果你跑单目或单目惯性模式时系统一直停在“Initialization”阶段先检查两个问题。一是场景是否太单一纯白墙、无纹理地面会让初始化永远无法三角化出足够的初始地图点二是运动方式是否合适纯旋转或纯平移都会让单目初始化退化最好保持一定的旋转和平移组合。跟踪中途丢失最直接的原因是快速抖动造成大范围运动模糊特征点迅速消失。可以先减小相机曝光时间或提高帧率从源头上减少运动模糊。如果代码上排查建议打开调试日志看是不是特征匹配数突然跌到了阈值以下。ORB-SLAM3的跟踪质量判断依赖一个关键帧重投影误差和匹配数量的综合条件特征匹配少就很容易判定为丢失。视觉惯性模式初始化失败我在前面已经提到过大概率是IMU激励不足。我的调试习惯是固定一个可以复现的启动动作设备先静止1到2秒然后做几个明显的八字形旋转和俯仰运动再回到近静态循环几次。这样既让IMU偏置估计收敛也让视觉三角化能得到充分的视差。5.3 精度漂移和回环失效跑完一段轨迹后发现误差很大先排除数据集时间戳同步问题尤其是自采数据相机和IMU时间戳不同步是硬伤差几十毫秒就可能让视觉惯性优化发散。EuRoC数据集已经对齐好了所以用数据集时基本没问题自采数据就要严格做好硬件同步或时间戳插值。回环失效的常见原因是回环检测词袋相似度阈值设置过严或者场景外观变化太大导致回到原位置时特征匹配数量不足。你可以先检查终端日志里回环检测阶段是否输出了候选帧数量如果数量长期为零说明词袋检索环节被判负了可以适当放宽LoopClosing.RecentFrameThreshold这类数量相关的阈值但注意别放宽太多否则会出现误回环影响比没有回环更致命。精度问题还可以从优化层面排查如果说轨迹跑完以后误差是“逐渐增大”而不是“突然跳变”大概率是局部BA和全局优化没有有效引入回环约束如果说误差是“局部突变”可能是某段路特征质量太差地图点退化或者IMU偏置估计被带偏了。评估的时候建议同时看绝对轨迹误差ATE和相对位姿误差RPE它们在定位问题的定位上各有侧重前者负责揭示全局漂移后者能看清局部抖动。5.4 常见问题速查表为了工程调试方便我把上面这些问题汇总成一个速查表遇到问题时可以照着排查。现象可能原因快速解决思路启动后一直等待初始化IMU激励不足或场景纹理差做“8字”运动保证画面有纹理跟踪丢帧地图频繁重置运动模糊严重或特征点太少提高帧率、增强对比度、增加特征数视觉惯性模式下轨迹持续漂移IMU噪声参数设置不准用Kalibr重新标定核对噪声密度单位回环检测长期不触发词袋相似度阈值过紧检查候选帧数量适当放宽阈值编译时报usleep未声明缺少unistd.h在源码中加入#include unistd.h可视化窗口崩溃Pangolin版本异常从Thirdparty重新编译PangolinCPU占用过高无法实时特征点和金字塔层数太多降分辨率、降特征数、关可视化这套系统我前后用了挺长时间从最早在台式机上跑EuRoC数据集到后来为了一台巡检机器人改参数、加预处理、做时间戳对齐越用越觉得ORB-SLAM3的价值不在于“开箱即用”而在于它把视觉SLAM里几个核心问题都考虑得非常完整特征怎么办、地图怎么管、回环怎么处理、IMU怎么融合。你把它读懂了再去看其他SLAM系统基本都能找到对应的模块和设计逻辑。真要说点什么个人体会就是不要一上来就扎进源码里逐行读而是先把它跑起来从数据流的角度理解每个模块在干什么再回头去看代码会轻松很多。如果你在部署过程中也遇到一些奇奇怪怪的问题欢迎留言或私信交流我经历过不少这类排查说不定正好碰过同一个坑。
返回列表