ARTICLE DETAIL

资讯详情

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

从零入门Visual SLAM:视觉里程计、后端优化与回环检测实践指南

从零入门Visual SLAM:视觉里程计、后端优化与回环检测实践指南 你有没有遇到过这种情况手里明明有一台装了摄像头的机器人它却分不清自己是在走廊里前进还是原地打转。或者做AR应用时虚拟物体总在真实场景里“漂移”怎么也钉不住。我最初做Visual SLAM相关项目时最大的感受就是——这事看起来门槛高实际拆开之后它并没有那么玄但每一步都藏着能让你熬夜排查的细节。这篇笔记就是我从零开始啃Visual SLAM算法、在真机和数据集上反复调试之后沉淀下来的个人总结。内容包括视觉里程计、后端优化、回环检测、建图以及大量我在工程实践里踩过的坑。如果你正在入门视觉SLAM或者准备在自己的机器人、无人机、AR项目里接入视觉定位能力这篇笔记可以作为你动手前的路线图参考。1. Visual SLAM到底在解决什么问题1.1 拿“闭眼走路”来理解定位与建图Visual SLAM的全称是Visual Simultaneous Localization and Mapping视觉同步定位与建图。它的任务是让一台只有摄像头或RGB-D相机、双目相机的机器在一个未知环境里运动时一边估计自己的位姿一边构建环境地图。我经常拿“闭眼走路”来类比你闭着眼睛走进一个房间每走一步都靠脚底感觉和记忆推断自己走到哪了同时用手摸墙壁构建房间的布局。但问题是——光靠感觉走误差会不断积累几圈下来你根本不知道自己站在哪里。而如果你睁着眼也就是视觉看到墙角、桌子、门这些“地标”就能不断修正自己的判断。Visual SLAM干的就是这件事摄像头是眼睛算法是大脑的推理过程。这里要留意一个“鸡生蛋”问题要知道自己在哪里需要地图要构建地图又需要知道自己在哪。所以SLAM系统一般会交替进行“根据已有地图估计位姿”和“根据新位姿补充地图”这两个步骤迭代推进。1.2 为什么偏偏是视觉而不是激光雷达早期SLAM主流方案是激光雷达也就是扫地机器人上常见的那种旋转雷达。激光雷达测距准、不受光照影响、角度分辨率高但它贵而且只能拿到二维点常见2D雷达或者稀疏的三维点。摄像头则便宜得多一个普通USB摄像头就能开始实验。视觉方案的最大优势是信息密度。一张普通的640x480图像有几十万个像素大量纹理信息全在里面。这些信息既能用于定位也能用于语义理解——比如识别出门、桌子、人。甚至在纯视觉SLAM中我们还能用深度学习做物体级别的建图这在激光雷达上是很难实现的。当然视觉也有代价对光照敏感、快速运动时会模糊、纯纹理缺失的墙面会“瞎”。所以现在很多工业级方案用的是“视觉惯性”融合VIO用IMU的加速度和角速度数据补足视觉在快速运动时的短板。这个思路我在第5部分会展开说。1.3 经典SLAM系统长什么样四个模块一个都不能少目前学术界和工业界公认的视觉SLAM标准框架大致分四个模块前端视觉里程计Visual Odometry, VO负责估算相邻帧之间的相机运动并生成局部地图。后端优化Backend / Optimization负责对相机位姿和路标点进行联合优化消除长期累积的误差。回环检测Loop Closure Detection负责识别“我之前是不是来过这里”当重新回到曾访问过的位置时把累积漂移“拉”回来。建图Mapping根据优化后的位姿和图像观测生成可用于导航、避障、交互的地图。这四块不是孤立存在的而是像流水线一样协作。你可以把VO理解为“短时记忆”后端优化是“中期修正”回环检测是“长期记忆校正”建图是“成果输出”。任何一个环节出问题整体效果都会大打折扣。我在实机调试时最深的感受就是很多人以为SLAM难在数学推导其实难在四个模块之间的参数配合和异常处理。2. 前端视觉里程计逐帧推算运动的核心2.1 特征点法 vs 直接法先想清楚你要什么前端VO的核心任务是估计两个相邻关键帧之间的相机运动。主流思路分成两派特征点法和直接法。特征点法先提取图像中的显著点比如角点、斑点为每个点计算一个描述子一串数字向量然后匹配两幅图像中的对应点再根据对应关系恢复相机运动。代表作品是ORB-SLAM系列。直接法则不提取特征点而是基于“灰度不变假设”直接利用像素亮度信息来估计位姿代表作品是LSD-SLAM和DSO。我在项目里用的是特征点法原因是它更成熟、对光照和运动模糊的抗性更好。直接法在纹理稀疏环境下通常更鲁棒因为在白墙场景下特征点提取会失败而直接法还能利用梯度信息。但直接法对相机内参和曝光时间极其敏感调参难度大很多。如果你是第一次做建议从特征点法入手等理解了整个框架再尝试直接法做对比。2.2 ORB特征为什么是性价比之王说到特征点法就不能不提ORB特征。它是ORB-SLAM的核心基础全称是Oriented FAST and Rotated BRIEF说白了就是用FAST角点检测带方向的BRIEF描述子。FAST角点检测的思路很直观如果一个像素和它周围一圈像素的亮度差异足够大那它就是一个候选角点。缺点是FAST本身不带尺度信息所以在ORB里要构建图像金字塔在不同分辨率的图像上都提取FAST角点实现一定程度上的尺度不变。方向信息则通过灰度质心法计算让描述子具备旋转不变性。BRIEF描述子则是“比较像素对亮度大小”生成的一个二进制串。假设我们对关键点周围随机选128对像素每对像素比较亮度亮度大的记为1否则为0最后得到一个128位的二进制描述子。这种二进制描述子的好处是匹配时可以用汉明距离不同位个数快速衡量相似度速度极快。实际工程中提取ORB特征时我有两个习惯一是控制提取数量比如在640x480图像上只取1000到1500个点太多会拖慢匹配太少则容易造成跟踪丢失二是开启图像金字塔通常设4到8层让特征点对尺度变化有一定容忍度。还有个细节是ORB特征的分布均匀性很重要——如果所有特征点都集中在画面某个角落运动估计会退化。ORB-SLAM里会用网格化的方式做特征点均匀化这个策略在自研系统里也值得借鉴。2.3 从特征匹配到位姿求解本质矩阵、单应矩阵与PnP有了两帧图像的特征点下一步就是匹配。常见的方式有暴力匹配和FLANN近似最近邻匹配。ORB这种二进制描述子用暴力匹配汉明距离其实很快如果特征点上万再考虑用FLANN。匹配结果里一定会有误匹配所以几乎各家系统都会接一个剔除步骤RANSAC随机采样一致性。RANSAC的思路是反复随机抽取少量匹配对计算一个模型比如本质矩阵然后统计有多少匹配对符合这个模型留下内点最多的一次作为最终结果。这个操作极其重要我见过不少初学者不看RANSAC结果直接用全部匹配对求位姿结果出来的轨迹在第三帧就开始乱飘。得到匹配对之后求解相机运动分两种情况单目相机未知深度需要先通过本质矩阵或单应矩阵估计两帧之间的相对旋转和平移然后再三角化出三维点。双目相机或RGB-D相机因为深度已知可以直接用PnPPerspective-n-Point估计当前帧相对地图的位姿。本质矩阵Essential Matrix是一个3x3矩阵编码了两个相机坐标系之间的旋转和平移关系前提是相机已标定。如果场景近似平面单应矩阵Homography会更稳定。这也是ORB-SLAM在初始化时同时计算本质矩阵和单应矩阵然后根据评分选择其中一个的原因——这个设计非常务实因为实际场景里平面结构墙壁、桌面太常见了。PnP则求解的是已知一组三维点和它们在图像上的投影求相机的位姿。常见解法包括DLT直接线性变换、P3P然后在后端用非线性优化比如Bundle Adjustment进一步细化。在实际操作中我一般先用RANSACP3P得到初始位姿再做一次局部BA光束平差精修这样在动态场景里能明显减少漂移。3. 后端优化与回环检测怎么把误差按回去3.1 为什么前端再准轨迹还是会飘前端VO是“增量式”的每一帧都只计算当前帧相对于上一帧的运动。问题是每次估计都有微小误差。这些误差不会消失而是像滚雪球一样累积。你走10米可能误差只有1厘米但走100米误差可能已经到了半米。这就是所谓的漂移drift。后端优化的任务就是在更大尺度上校正这些漂移。你可以把它想象成一种“全局视图”前端看到的只有最近一两帧后端则把几分钟甚至几小时内的所有帧和路标点拉出来一起做优化让整体的误差尽量小。早期的SLAM用的是滤波器方法比如扩展卡尔曼滤波EKF-SLAM它的本质是马尔可夫假设——当前状态只跟上一时刻状态有关。这个方法在小场景里够用但地图点一多协方差矩阵的维度就爆炸实时性完全跟不上。后来的图优化Graph-Based SLAM方法则把问题建模成图节点是相机位姿或路标点边是它们之间的观测约束。于是SLAM后端变成了一个大规模的最小二乘问题。我在实际项目里90%的情况用的都是图优化。相比EKF它更灵活、精度更高、便于增量更新也更容易加入回环约束。3.2 图优化里的关键概念顶点、边、误差函数图优化的好处是便于理解。想象一张网络每个相机位姿是一个顶点每个三维路标点也是一个顶点如果相机看到了某个路标点就在它们之间连一条边。边的“约束”就是观测值——比如“相机在位姿T时路标点P投影到图像的像素坐标应该是(u,v)”但实际上因为噪声投影位置总有误差。优化的目标就是调整所有顶点位姿和路标点使得所有边的总体误差最小。用数学语言说就是最小化误差项的二范数之和。这是一个非线性最小二乘问题常用的求解方法包括高斯牛顿法Gauss-Newton和LM算法Levenberg-Marquardt。LM相当于在GN的基础上加了一个阻尼因子保证迭代过程更稳定实际工程里基本都用LM。在代码实现上我最早用的是g2o后来因为Ceres的文档更完善、自动求导更方便就慢慢切换到Ceres Solver。如果你只是验证算法g2o也能用但如果你的项目涉及自定义误差函数、要长时间维护我更推荐Ceres——它把自动微分做得很优雅新手不容易在求导上栽跟头。有一点必须提醒图优化在工程实现时信息矩阵也就是每条边的权重的设置非常重要。我见过有人把所有传感器观测统一用单位矩阵权重结果视觉观测弱的区域轨迹彻底失控。正确做法是根据传感器噪声模型设定协方差再取逆作为信息矩阵这样高置信度的观测对优化结果的影响才更大。3.3 回环检测让机器人认出“我来过这里”无论前端和后端多优秀纯靠增量式计算的轨迹漂移是不可避免的。回环检测是唯一能从全局层面消除长期漂移的手段。回环检测要做的事情很简单判断当前帧是否曾经访问过。一旦检测到回环就能构建一条“当前位置—历史位置”的强约束边让后端优化把累积漂移一次性拉平。主流方案是基于外观的回环检测代表是词袋模型Bag of Words。思路是把图像里的特征描述子聚类成“视觉单词”类似把每张图像抽象成一个词汇列表然后用TF-IDF等权重衡量当前图像和历史图像的相似度。ORB-SLAM用的DBoW2库就是干这个的。实际操作中回环检测最怕的是“感知歧义”——比如两间长得一模一样的房间算法会误判为回环这会导致地图被错误“拉坏”。所以工业级实现里会做几何一致性校验先判断词袋相似度是否超过阈值再验证两帧之间是否存在足够的几何匹配特征最后才把回环边加入优化。这一步能过滤掉绝大多数误回环一定要做好。我也试过基于深度学习的全局描述子比如NetVLAD做回环检测它的鲁棒性比传统词袋好特别在光照变化大的场景下但计算量更大嵌入式设备上要谨慎采用。如果跑在Jetson这类国产或进口的边缘平台上可以考虑把NetVLAD做成离线预计算在线查询的方式。4. 建图从稀疏点到能用的地图4.1 稀疏地图、半稠密、稠密你的任务决定你的选择很多刚接触SLAM的人以为“有了SLAM就等于有了三维模型”这话只说对了一部分。Visual SLAM生成的“地图”有多种形态选择哪一种完全取决于你的下游任务。稀疏地图Sparse Map就是一堆三维路标点ORB-SLAM默认生成的就是这种。它用于定位足够了——因为定位只需要知道“我看到的特征点在空间中的位置”。但如果你拿到这种地图直接去做碰撞检测或导航基本没法用墙面中间只有几个点根本不知道障碍物轮廓在哪。半稠密地图Semi-Dense Map在LSD-SLAM里比较典型它对图像中梯度明显的区域边缘、轮廓估计逆深度生成一种轻度稠密的点云。优点是计算量适中比稀疏地图信息量大得多能一定程度上做避障。稠密地图Dense Map才是真正意义上的模型每个像素都对应一个三维点。生成方式包括双目立体匹配、RGB-D相机直接获取深度、或者单目加深度学习深度估计。稠密地图再往上走还有八叉树地图OctoMap、TSDF模型、栅格地图。我做移动机器人项目时其实最常用的是2D栅格地图Occupancy Grid Map把三维空间投影到地面网格每个格子标记占据、空闲或未知。视觉SLAM也可以生成这种地图——先跑出相机轨迹再把深度信息投影到地面栅格。原理不复杂但要注意建图时滤除掉地面以上的动态物体否则地图上会出现“幽灵障碍”。4.2 深度估计构建三维信息的命门不管哪种建图核心都是获取像素点的三维坐标。在单目相机方案里深度是缺失的所以要先通过三角化triangulation恢复路标点的深度。三角化的原理是同一个三维点被不同位置的两台相机观测到两条投影射线在空间中的交点就是该点的三维位置。由于噪声射线不会严格相交所以通常用最小二乘求一个“最接近”的点。三角化对两帧之间的基线baseline长度很敏感基线太短深度估计的误差会大得离谱基线太长特征匹配的难度又增加。所以工程里一般会设置一个“关键帧间距”的阈值让关键帧之间保持足够但不过分的视差。双目相机或RGB-D方案则可以直接获取深度。RGB-D相机比如常见的结构光或ToF方案能直接输出逐像素深度图在室内短距离场景下精度很高特别适合做人形机器人抓取、AR遮挡等任务。缺点是强光下ToF容易饱和结构光在远距离时会失效。室外大场景还是得靠双目立体匹配或者单目多帧重建。这里补充一点个人经验如果要做室内机器人的建图导航RGB-D方案是最省心的配合VINS-Fusion或ORB-SLAM3的RGB-D模式效果相当稳。但如果做室外无人机那几乎只能用双目或者单目IMU融合光照变化才是最大的敌人。4.3 地图在机器人上到底怎么用导航与避障有了地图机器人才能规划路径。工程上最常见的用法是先用SLAM构建栅格地图然后在地图上做A*或Dijkstra全局路径规划再配合动态窗口法DWA或TEB做局部避障。这里SLAM输出的位姿就相当于“机器人在哪”地图告诉机器人“哪里能走”。如果你做的是无人机可能用的是OctoMap八叉树地图它能方便地表示三维空间被占据的情况支持概率更新对动态障碍物也有良好的更新机制。用八叉树的一个好处是分辨率可变远处用粗分辨率、近处用细分辨率内存开销可控。我做室内无人机避障时就用OctoMap 深度相机简单可靠跑在Jetson Nano级别的小卡上也能维持实时帧率。我要特别强调一个容易忽略的点地图的坐标系必须和机器人底盘、IMU、相机的外参标定准确。很多时候SLAM跑得没问题但机器人一动起来就撞墙排查半天发现是相机到机体系的外参精确度不够。外参标定这事千万别省哪怕用棋盘格手动标定也比拍脑袋估算好得多。5. 工具链与学习路径用最少的时间落地一套SLAM5.1 从ORB-SLAM2开始还是直接上ORB-SLAM3想快速上手Visual SLAM最直接的办法就是跑通一个成熟的开源系统然后在上面改代码。我个人建议从ORB-SLAM2或ORB-SLAM3入手原因有三代码结构清晰、注释丰富、配套论文讲解多支持单目、双目、RGB-D三种传感器社区大踩坑资料随手就能搜到。ORB-SLAM3相比2代加入了视觉惯性融合VI-SLAM、多地图系统在纯视觉模式下也有更强的鲁棒性。如果你有IMU直接上ORB-SLAM3收益很大如果只是想理解经典四模块结构先从ORB-SLAM2开始也可以代码更简单调试更容易。跑通开源系统的时候我强烈建议你做到以下几步用Euroc数据集跑通释例再换TUM数据集看效果用Webcam实时跑一次体验真实场景下的挑战最后在ORB-SLAM2的代码里自行添加一行输出相机位姿的代码确保你理解了数据流。这一步“改代码”比单纯跑通重要得多。5.2 必备库和调试工具一次装齐少走弯路在Linux一般用Ubuntu环境下搭建Visual SLAM开发环境通常需要以下依赖库Eigen线性代数库几乎所有SLAM代码都会用到。Sophus李代数库用于表示旋转和平移。OpenCV图像处理和特征提取。g2o或Ceres Solver用于后端图优化。Pangolin用于3D可视化和轨迹显示。DBoW2ORB-SLAM中用于回环检测的词袋库。安装时容易遇到的坑是版本不匹配。比如OpenCV 4和某些老版本SLAM代码可能存在接口变化CERES版本太新可能导致编译错误。我自己的习惯是先用Docker或预编译环境比如在NVIDIA Isaac或一些SLAM官方镜像上开发和测试等代码稳定后再装回本机原生环境。调试时几个实用的工具也要掌握用rvizROS的3D可视化工具观察轨迹和地图用evo工具评估ATE绝对轨迹误差和RPE相对位姿误差。EVO是我最常用来对比不同算法效果的评测工具一条指令就能出轨迹对比图和误差指标推荐所有人学会。5.3 经典数据集与评测标准跑SLAM不能光在模拟器里自嗨用公开数据集评价算法性能非常必要。常用的数据集有TUM RGB-D室内手持相机场景提供RGB-D图像和真值轨迹适合评估RGB-D SLAM。KITTI室外自动驾驶场景双目图像激光雷达真值适合双目和单目评估。EuRoC无人机室内场景双目IMU带真值适合视觉惯性SLAM评估。评测指标上最常用的是ATEAbsolute Trajectory Error和RPERelative Pose Error。ATE看的是整体轨迹和真值之间的差距RPE看的是局部短时间内的位移和旋转误差。两者要结合起来看ATE大但RPE小说明系统有回环误差没校正ATE和RPE都大说明前端或后端都有问题。我面试算法岗时经常被问到这些问题。6. 常见问题与排查技巧实录6.1 跟踪丢失特征点扛不住大旋转或快速运动这是视觉SLAM最常见的“当场暴毙”问题。原因往往是相邻帧之间重叠区域太小、特征点数量过少、图像运动模糊。我的处理习惯是开启更高级别的图像金字塔提高尺度变化容忍度。增加特征点提取数量同时用网格法保证分布均匀。降低相机自动曝光和自动白平衡的影响尽量固定曝光参数。在快速运动场景引入IMU做运动预测给前端一个“先验位姿”这样即使视觉短暂丢失系统也能维持一小段时间不崩。这里有个小技巧如果你用ORB-SLAM系列可以打开可视化窗口统计当前跟踪的特征点数量。如果发现特征点稳定在100个以下那就别指望它能扛住剧烈运动了先优化输入图像质量再考虑改算法。6.2 尺度漂移单目SLAM的头号难题单目相机无法直接获得绝对尺度所以单目SLAM的轨迹和地图的“大小”是相对的。机器人沿直线走10米算法可能认为走了9米这就是尺度漂移。尺度漂移最有效的解决办法是引入已知尺度的传感器——IMU、深度相机、轮式里程计都可以。比如ORB-SLAM3和VINS-Mono就是通过视觉/惯性联合初始化恢复尺度。如果你必须纯单目那就只能尽量多回环回环约束能显著抑制尺度漂移但不能完全消除。我在做AR应用时方案基本都是单目IMU因为手机自带IMU融合之后虚拟物体“钉”在现实中的稳定度大幅提升。如果你看到某个AR Demo特别晃大概率是没做视觉惯性融合。6.3 动态物体和重复纹理回环检测的隐形杀手动态物体行人、车辆会给建图和回环检测带来很多“脏数据”。ORB-SLAM2对动态物体是“硬扛”的通过RANSAC和多视图几何剔除大部分动态特征点但效果有限。更彻底的做法是在前端加语义分割比如YOLO或SegNet直接剔除动态物体上的特征点。这个思路叫Dynamic SLAM是近几年研究热点之一。重复纹理比如一模一样的货架、瓷砖墙则会让回环检测误判或特征匹配错误。排查时如果发现地图上出现“重影”或“穿越”大概率就是重复纹理引起的。解决办法是提高关键帧选取阈值减少冗余帧同时提高回环检测的几何一致性校验强度。6.4 性能优化嵌入式平台上的生存之道在Jetson、RK3588这类边缘设备上跑Visual SLAM性能瓶颈通常集中在特征提取、匹配和BA优化。我调优的顺序一般是降分辨率图像从1080p降到640p特征提取耗时能降一半以上。用多线程把前端、回环检测、建图分线程跑避免互相阻塞。限制局部BA的窗口大小和迭代次数换取更稳定的帧率。对回环检测做“先粗筛、再细匹配”避免每一帧都做全库搜索。必要时把特征提取放到GPU或NPU上这一步优化空间很大。说个具体的例子我曾在Jetson Nano上跑ORB-SLAM2单目默认参数下只有8到10帧CPU占用接近满载。后来把图像分辨率从640x480降到320x240特征点数量从1500降到800帧率稳定到20帧以上同时引入IMU预测跟踪丢失率明显下降。对于很多室内机器人来说20帧已经足够稳定了。7. 一些想单独说的学习心得VSLAM这个方向入门的第一道坎是数学基础矩阵、非线性优化、李群李代数。第二道坎是工程能力C、Linux、CMake、调试工具。第三道坎才是算法理解什么时候用本质矩阵、什么是信息矩阵、怎么设计鲁棒核函数。我见过不少人卡在第一步就放弃了原因是直接去啃论文结果被公式吓跑。我的建议是反着来先跑通ORB-SLAM2再回去看高翔的《视觉SLAM十四讲》对应章节带着问题去读效率会高很多。等你跑通两三个开源系统再回头看论文“为什么这么设计”这个问题你自然会找到答案。另外Visual SLAM的算法创新空间其实很大尤其在极端光照、无纹理环境、动态场景、多机协同这几块。如果准备做深入研究建议结合语义分割、神经辐射场这类新工具来突破传统方案的瓶颈。3DGS3D高斯泼溅和NeRF与SLAM的结合目前正是学术界的热点方向之一后面如果有时间我也想单独写一篇做对比实验的笔记。
返回列表