主要内容

单目视觉同步定位与绘图

视觉同步定位与测绘(Visual simultaneous localization and mapping, vSLAM),是指在同时测绘环境的同时,计算相机相对于其周围环境的位置和方向的过程。这个过程只使用摄像机的视觉输入。vSLAM的应用包括增强现实、机器人技术和自动驾驶。

这个例子展示了如何处理来自单目相机的图像数据来建立一个室内环境的地图和估计相机的轨迹。该示例使用ORB-SLAM[1],是一种基于特征的vSLAM算法。

要加快计算,您可以从中启用并行计算计算机视觉工具箱首选项对话框。要打开计算机视觉工具箱™首选项,请在首页标签,在环境部分中,点击首选项.然后选择电脑视觉工具箱。

术语表

以下术语在这个例子中经常使用:

  • 关键帧:包含定位和跟踪线索的视频帧子集。两个连续的关键帧通常包含足够的视觉变化。

  • 地图上分:一个表示从关键帧重建的环境地图的三维点的列表。

  • Covisibility图:以关键帧为节点的图。如果两个关键帧共享相同的地图点,则它们通过一条边连接。一条边的权值是共享地图点的数目。

  • 基本图:仅包含高权重的边缘的可粘物图的子图,即更多共享地图点。

  • 识别数据库:用于识别过去已访问过地面的数据库。数据库存储基于特征的输入袋的视觉字到图像映射。它用于搜索视觉上类似于查询图像的图像。

概述ORB-SLAM

ORB-SLAM管道包括:

  • 地图初始化: ORB-SLAM首先从两个视频帧初始化三维点的地图。采用基于二维ORB特征对应的三角剖分方法计算三维点和相对相机位姿。

  • 追踪一旦地图被初始化,对于每一个新的帧,相机的姿态是通过匹配当前帧的特征和最后一个关键帧的特征来估计的。通过跟踪局部地图,对估计的相机姿态进行优化。

  • 本地映射:如果当前帧被识别为关键帧,则当前帧用于创建新的3-D地图点。在这个阶段,通过调整相机的姿态和3-D点来最小化重投影误差。

  • 循环关闭:通过使用特征包方法将每个关键帧与之前的所有关键帧进行比较,从而检测出每个关键帧的循环。一旦检测到一个闭环,姿态图将被优化,以细化所有关键帧的相机姿态。

下载并探索输入图像序列

本例中使用的数据来自TUM RGB-D基准[2].您可以使用Web浏览器将数据下载到临时目录或运行以下代码:

baseDownloadURL ='https://vision.in.tum.de/rgbd/dataset/freiburg3/rgbd_dataset_freiburg3_long_office_household.tgz';dataFolder = fullfile (tempdir,'tum_rgbd_dataset', filesep);选择= weboptions (“超时”,INF);tgzfilename = [datafolder,'fr3_office.tgz'];folderExists =存在(dataFolder,'dir');%在临时目录中创建一个文件夹以保存下载的文件如果~ folderExists mkdir (dataFolder);disp (“下载fr3_office。tgz (1.38 GB)。这次下载可能需要几分钟。”) websave(tgzFileName, baseDownloadURL,选项);%提取下载文件的内容disp (“提取fr3_office。tgz (1.38 GB)…)解压(tgzFileName dataFolder);结束

创建一个imageageAtastore.对象来检查RGB图像。

imagefolder = [datafolder,“rgbd_dataset_freiburg3_long_office_household / rgb /”];imd = imageDatastore (imageFolder);%检查第一个图像currFrameIdx = 1;currI = readimage(imds, currFrameIdx);himage = imshow (currI);

地图初始化

ORB-SLAM管道首先初始化包含3-D世界点的地图。这一步至关重要,对最终SLAM结果的准确性有重要影响。使用以下方法可以找到初始ORB特性点对应matchFeatures在一对图像之间。找到对应后,使用两个几何变换模型来建立地图初始化:

  • 单应性:如果场景是平面的,单应投影变换是描述特征点对应关系的更好选择。

  • 基本矩阵:如果场景是非平面的,则必须使用基本矩阵。

单应性和基本矩阵可以用estimateGeometricTransform2d.estimateFundamentalMatrix, 分别。选择导致较小的重注错误的模型来估计两个帧之间的相对旋转和转换relativecamerapose..由于RGB图像是由单目相机拍摄的,不提供深度信息,相对平移只能恢复到特定的比例因子。

在给定相机相对位姿和两幅图像中匹配的特征点的情况下,使用三角化功能。当重点误差低时,三角形地图点在两个相机的前面时有效,并且当点的两个视图的视差足够大时。

%设置随机种子以进行再现性RNG(0);%创建一个cameraIntrinsics对象来存储相机的内部参数。%数据集的内在功能可以在下页找到:% https://vision.in.tum.de/data/datasets/rgbd-dataset/file_formats%注意,数据集中的图像已经是未变形的,因此存在%不需要指定失真系数。焦点= [535.4,539.2];%,单位为像素principalPoint = [320.1, 247.6];%,单位为像素图像智能=尺寸(CURI,[1 2]);%,单位为像素intrinsics = cameraIntrinsics(focalLength, principalPoint, imageSize);%检测和提取ORB特征scaleFactor = 1.2;numLevels = 8;[preFeatures, prePoints] = helperDetectAndExtractFeatures(currI, scaleFactor, numLevels);currFrameIdx = currFrameIdx + 1;firstI = currI;%保留第一帧isMapInitialized = false;% Map初始化循环尽管~isMapInitialized && currFrameIdx < numel(imds. files) currI = readimage(imds, currFrameIdx);[currFeatures, currPoints] = helperDetectAndExtractFeatures(currI, scaleFactor, numLevels);currFrameIdx = currFrameIdx + 1;找到假定的特征匹配indexPairs = matchFeatures(preFeatures, currFeatures,'独特', 真的,...“MaxRatio”, 0.9,'matchthreshold', 40);preMatchedPoints = prePoints (indexPairs (: 1):);currMatchedPoints = currPoints (indexPairs (:, 2):);%如果没有找到足够的匹配,检查下一帧minmatches = 100;如果尺寸(indexpaire,1)继续结束preMatchedPoints = prePoints (indexPairs (: 1):);currMatchedPoints = currPoints (indexPairs (:, 2):);计算单应性并评估重建[tformH, scoreH, inliersIdxH] = helperComputeHomography(preMatchedPoints, currMatchedPoints);计算基本矩阵和评估重建[tformF, scoreF, inliersIdxF] = helperComputeFundamentalMatrix(preMatchedPoints, currMatchedPoints);%基于启发式选择模型ratio = scoreH/(scoreH + scoreF);ratioThreshold = 0.45;如果ratio > ratioThreshold inliertorigx = inliersIdxH;tform = tformH;其他的inlierTformIdx = inliersIdxF;tform = tformF;结束%按比例计算摄像机位置。用一半%点数减少计算inlierPrePoints = preMatchedPoints (inlierTformIdx);inlierCurrPoints = currMatchedPoints (inlierTformIdx);[relOrient, relLoc, validFraction] = relativeCameraPose(tform, intrinsics, validFraction),...inlierPrePoints(1:2)结束,inlierCurrPoints(1:2:结束));%如果没有找到足够的嵌线,移动到下一帧如果有效期<0.9 ||numel(尺寸(Relorient))== 3继续结束对两个视图进行三角测量以获得三维地图点relPose = rigid3d(relOrient, relLoc);minParallax = 1;%的度[isValid, xyzWorldPoints, inlierTriangulationIdx] = helperTriangulateTwoFrames(...rigid3d, relPose, inlierPrePoints, inlierCurrPoints, intrinsics, minParallax);如果~ isValid继续结束%获取两个关键帧中特征的原始索引indexPairs = indexPairs (inlierTformIdx (inlierTriangulationIdx):);isMapInitialized = true;disp (['Map被帧1和帧初始化',num2str(currframeidx-1)])结束%映射初始化循环结束
用帧1和帧26初始化的映射
如果isMapInitialized关闭(himage.Parent.Parent);%关闭前面的数字%显示匹配的功能hfeature = showMatchedFeatures(firstI, currI, prePoints(indexPairs(:,1)),...currPoints (indexPairs (:, 2)),“蒙太奇”);其他的错误('无法初始化地图。'结束

存储初始关键帧和映射点

使用两个帧初始化映射后,可以使用imageviewsetworldpointsethelperViewDirectionAndDepth存储两个关键帧和对应的映射点:

  • imageviewset存储关键帧及其属性,如ORB描述符、特征点和相机姿态,以及关键帧之间的连接,如特征点匹配和相对相机姿态。它还构建和更新一个姿势图。里程计边缘的绝对相机位姿和相对相机位姿存储为rigid3d对象。闭合环边的相对相机位姿存储为affine3d对象。

  • worldpointset将地图点和3-D存储到二维投影对应关系的3-D位置:在键帧中观察到哪个地图点,并且该键帧观察地图点。

  • helperViewDirectionAndDepth存储地图点的其他属性,例如平均视图,代表性的ORB描述符和可以观察地图点的距离范围。

%创建一个空的imageviewset对象来存储关键帧vSetKeyFrames = imageviewset;%创建一个空的worldpointset对象以存储3-D映射点mappointset = worldpointset;%创建一个升迁virectionanddepth对象以存储视图方向和深度directionAndDepth = helperViewDirectionAndDepth(size(xyzWorldPoints, 1));添加第一个关键帧。放置与第一个相关联的相机%关键帧在原点,沿z轴方向preViewId = 1;vSetKeyFrames = addView(vSetKeyFrames, preViewId, rigid3d,“点”prePoints,...“特性”,装饰.Features);添加第二个关键帧currViewId = 2;vSetKeyFrames = addView(vSetKeyFrames, currViewId, relPose,“点”currPoints,...“特性”, currFeatures.Features);在第一和第二关键帧之间增加连接vsetkeyframs = addConnection(vsetKeyFrames,PreviewID,CurviewID,有关,'火柴', indexPairs);%添加3d地图点[mappointet,newpointidx] = addworldpoints(mappointset,xyzworldpoints);增加地图点的观察值prelocations = prepoints.location;CRUCLOCations = CurrPoints.Location;prescales = prepoints.scale;currcales = currpoints.cale;在第一个关键帧中添加与地图点对应的图像点mapPointSet = addements (mapPointSet, preViewId, newPointIdx, indexPairs(:,1));%在第二个关键帧中添加与地图点对应的图像点mapPointSet = addements (mapPointSet, currViewId, newPointIdx, indexPairs(:,2));

改进和可视化初始重建

使用bundleAdjustment,它优化了相机姿态和世界点,以最小化整体重投影误差。细化后,更新地图点的属性,包括三维位置、视图方向和深度范围。您可以使用helperVisualizeMotionAndStructure可视化地图点和摄像机位置。

%在前两个关键帧上运行完整捆绑调整跟踪= findTracks (vSetKeyFrames);cameraPoses =姿势(vSetKeyFrames);[refinedPoints, refinedAbsPoses] = bundleAdjustment(xyzWorldPoints, tracks,...cameraPoses intrinsic,'filedviewids', 1...“PointsUndistorted”, 真的,“AbsoluteTolerance”1 e -...“RelativeTolerance”1 e15汽油,“MaxIteration”, 50);%使用地图点的中值深度缩放地图和相机姿态medianDepth =值(vecnorm (refinedPoints。');refinedPoints = refinedPoints / medianDepth;refinedAbsPoses.AbsolutePose (currViewId)。翻译=...refinedAbsPoses.AbsolutePose (currViewId)。翻译/ medianDepth;relPose。翻译= relPose.Translation / medianDepth;%用优化的姿势更新关键帧vSetKeyFrames = updateView(vSetKeyFrames, refinedAbsPoses);vSetKeyFrames = updateConnection(vSetKeyFrames, preViewId, currViewId, relPose);%更新地图点与精炼的位置mapPointSet = updateWorldPoints(mapPointSet, newPointIdx, refinedPoints);%更新视图方向和深度directionanddepth =更新(directionanddepth,mappointset,vsetkeyframes.views,newPointIdx, true);%在当前帧中可视化匹配的特征关闭(hfeature.Parent.Parent);featurePlot = helperVisualizeMatchedFeatures(currI, currPoints(indexPairs(:,2)));

%可视化初始映射点和相机轨迹mapPlot = helperVisualizeMotionAndStructure(vSetKeyFrames, mapPointSet);%显示传奇showLegend (mapPlot);

追踪

跟踪过程使用每一帧执行,并决定何时插入新的关键帧。为了简化这个示例,我们将在找到循环闭包后终止跟踪过程。

当前关键帧的% ViewIdCurrkeyFrameID = CurviewId;上一个关键帧的% ViewIdlastKeyFrameId = currViewId;具有最可观的参考密钥帧的%视角%映射点与当前关键框架refKeyFrameId = currViewId;%输入图像序列中最后一个关键帧的索引LastKeyFrameIdx = CurrFrameIDX  -  1;%输入图像序列中所有关键帧的索引addedFramesIdx = [1;lastKeyFrameIdx];isLoopClosed = false;

每个帧都处理如下:

  1. 为每个新帧提取ORB特性,然后进行匹配(使用matchFeatures),具有已知对应的3-D映射点的最后一个关键帧中的功能。

  2. 使用Perspective-n-Point算法估计相机的姿态estimateWorldCameraPose

  3. 给定相机姿态,将最后一帧观察到的地图点投影到当前帧中,并使用matchFeaturesInRadius

  4. 在当前帧中使用3-D到2-D对应,通过执行仅运动束调整来优化相机的姿态bundleAdjustmentMotion

  5. 将局部地图点投影到当前框架中,以搜索更多的特征对应matchFeaturesInRadius再次调整相机的姿势bundleAdjustmentMotion

  6. 跟踪的最后一步是判断当前帧是否是新的关键帧。如果当前帧是关键帧,则继续本地映射过程。否则,开始追踪下一帧。

%主循环尽管~isLoopClosed && currFrameIdx < numel(imds. files) currI = readimage(imds, currFrameIdx);[currFeatures, currPoints] = helperDetectAndExtractFeatures(currI, scaleFactor, numLevels);%跟踪最后一个关键帧% mapPointsIdx:当前帧中观察到的映射点的索引% featureIdx:文件中相应特征点的索引%当前帧[省略mappointsidx,featureidx] = helpertracklastkeyframe(mappointset,...vSetKeyFrames。视图,currFeatures, currPoints, lastKeyFrameId, intrinsics, scaleFactor);%跟踪本地地图% refKeyFrameId:引用关键帧的ViewId与当前帧共可见的地图点% localKeyFrameIds:当前帧连接的关键帧的ViewId[refKeyFrameId, localkeyframeid, currPose, mapPointsIdx, featureIdx] = .使用实例...HelperTrackLocalMap(Mappointset,DirectionAndDepth,VsetKeyFrames,Mappointsidx,...featureIdx, currPose, currFeatures, currPoints, intrinsics, scaleFactor, numLevels);%检查当前帧是否是关键帧。如果满足以下两个条件,%帧是关键帧:% 1。从上一个关键帧到现在已经过去了至少20帧%当前帧跟踪少于100个地图点% 2。当前帧跟踪的地图点不到90%由参考关键帧跟踪的%点isKeyFrame = helperIsKeyFrame(mapPointSet, refKeyFrameId, lastKeyFrameIdx,...currframeidx,mappointsidx);可视化匹配特征updatePlot (featurePlot、currI currPoints (featureIdx));如果~isKeyFrame currFrameIdx = currFrameIdx + 1;继续结束%更新当前关键帧IDcurrKeyFrameId = currKeyFrameId + 1;

本地映射

对每个关键帧进行局部映射。当确定一个新的关键帧时,将其添加到关键帧中,更新新关键帧观察到的地图点的属性。以确保mapPointSet包含尽可能几个异常值,必须在至少3个关键帧中观察到有效地图点。

通过对当前关键帧及其连接的关键帧中的ORB特征点进行三角化来创建新的映射点。对于当前关键帧中的每一个未匹配的特征点,利用该方法在已连接的关键帧中与其他未匹配的特征点进行匹配matchFeatures.局部束调整改进了当前关键帧的姿势,连接关键帧的姿势,以及在这些关键帧中观察到的所有地图点。

%添加新的关键帧[mappointment set, vSetKeyFrames] = helperAddNewKeyFrame(mappointment set, vSetKeyFrames,...currform, currFeatures, currPoints, mapPointsIdx, featureIdx, localKeyFrameIds);%删除在关键帧内观察到的离群点[mapPointSet, directionAndDepth, mapPointsIdx] = helperCullRecentMapPoints(mapPointSet,...directionanddepth,mappointsidx,newpointidx);通过三角测量创建新的地图点minnummatches = 20;minparallax = 3;[mappointet,vsetkeyframes,newpointIdx] = helpercreateNewmappoints(mappointset,vsetkeyframes,...currKeyFrameId, intrinsics, scaleFactor, minNumMatches, minParallax);%更新视图方向和深度directionanddepth =更新(directionanddepth,mappointset,vsetkeyframes.views,...[mapPointsIdx;newPointIdx),真正的);局部束平差[mapPointSet, directionAndDepth, vSetKeyFrames, newPointIdx] = helperLocalBundleAdjustment(...mapPointSet、directionAndDepth vSetKeyFrames,...currKeyFrameId, intrinsic newPointIdx);%可视化3D世界点和相机轨迹updatePlot (mapPlot vSetKeyFrames mapPointSet);

循环关闭

循环结束步骤采用局部映射过程处理的当前关键帧,并尝试检测和关闭循环。环路检测是使用词袋方法进行的。用A表示的视觉词汇Bagoffeature对象使用SURF描述符离线创建,该描述符从数据集中的大量图像中提取,调用:

包= bagfeatures (imds,'CustomExtractor', @helperSURFFeatureExtractorFunction);

在哪里洛桑国际管理发展学院是一个imageageAtastore.对象存储训练图像和elplersurfeepureextractorfunctionSURF功能提取器功能是否。看图像检索与袋子的视觉词为更多的信息。

循环关闭过程递增地构建数据库,表示为一个数据库invertedImageIndex对象,基于冲浪功能的袋子存储视觉字到图像映射。通过在视觉上类似于当前密钥帧的数据库中查询图像中的图像来识别循环候选evaluateImageRetrieval.如果一个候选关键帧没有连接到最后一个关键帧,并且它的三个邻居关键帧是循环候选帧,那么这个候选关键帧是有效的。

当找到一个有效的循环候选时,使用estimationGeometricTransform3d.计算循环候选帧与当前关键帧之间的相对位姿。相对位姿表示存储在affine3d目的。然后使用相对姿势和更新添加循环连接mapPointSetvSetKeyFrames

初始化循环闭包数据库如果CurryFrameID == 3.%加载离线创建的特性数据包bofData =负载(“bagOfFeaturesData.mat”);初始化位置识别数据库LoopCandIdates = [1;2];LoopDatabase = IndexImages(子集(IMDS,LoopCandIdates),BOFData.BOF);%在一些关键帧被创建后检查循环关闭elseifCurrkeyFrameID> 20.%循环边的最小特征匹配数loopEdgeNumMatches = 50;%检测可能的循环关闭关键帧候选[Isdetected,ValidLoopCandIdates] = HelperCheckLoopClosure(vsetKeyFrames,CurrkeyFrameID,...loopDatabase, currI, loopCandidates, loopEdgeNumMatches);如果isDetected%添加循环闭包连接[isLoopClosed, mapPointSet, vSetKeyFrames] = helperAddLoopConnections(...mappointset,vsetkeyframes,validloopcandIdates,currkeyFrameID,...currFeatures、currPoints loopEdgeNumMatches);结束结束%如果未检测到循环关闭,则将图像添加到数据库中如果~isLoopClosed addImages(loopDatabase,子集(imds, currFrameIdx)),“详细”、假);loopCandidates = [loopCandidates;currKeyFrameId];%#OK 结束%更新id和索引lastKeyFrameId = currKeyFrameId;lastKeyFrameIdx = currFrameIdx;addedFramesIdx = [addedFramesIdx;currFrameIdx];%#OK currFrameIdx = currFrameIdx + 1;结束%主循环结束
使用Bag-Of-Features创建倒置的图像索引。------------------------------------------------------- 使用Bag-Of-Features编码图像。-------------------------------------- * 编码2图片…。完成图像索引的创建。

关键框架之间添加了循环边缘:6和204

最后,对中基本图进行相似位姿图优化vSetKeyFrames来纠正相机姿势的漂移。本质图是通过去除小于的连接而在内部创建的minnummatches.匹配在可执行性图中。在相似性姿态图优化之后,使用优化的姿势和分配量表更新地图点的三维位置。

%优化姿势minNumMatches = 30;[vSetKeyFramesOptim, poseScales] = optimizePoses(vSetKeyFrames, minNumMatches,'宽容'1 e-16);%在优化姿态后更新地图点mappoint = helperUpdateGlobalMap(mappoint, directionAndDepth,...vsetkeyframes,vsetkeyframesoptim,posescales);updatePlot (mapPlot vSetKeyFrames mapPointSet);%绘制优化的摄像机轨迹optimizedPoses =姿势(vSetKeyFramesOptim);plotOptimizedTrajectory (mapPlot optimizedPoses)%更新传奇showLegend (mapPlot);

与地面真理比较

可以将优化后的相机轨迹与地面真实值进行比较,以评估ORB-SLAM的精度。下载的数据包含groundtruth.txt存储每帧相机姿态的地面真实的文件。数据已以mat文件的形式保存。您还可以计算轨迹估计的均方根误差(RMSE)。

负载地面真实值gtruthdata = load(“orbslamGroundTruth.mat”);gTruth = gTruthData.gTruth;%绘制实际的相机轨迹plotActualTrajectory (mapPlot gTruth (addedFramesIdx) optimizedPoses);%显示传奇showLegend (mapPlot);

评估跟踪精度helperEstimateTrajectoryError (gTruth (addedFramesIdx) optimizedPoses);
关键帧轨迹的绝对RMSE (m): 0.25809

本文概述了如何利用ORB-SLAM来构建室内环境的地图和估计相机的轨迹。

万博1manbetx支持功能

下面包括一些简短的辅助函数。较大的函数包含在单独的文件中。

helperAddLoopConnections在当前关键帧和有效循环候选帧之间添加连接。

helperAddNewKeyFrame添加关键帧到关键帧集。

helperCheckLoopClosure通过从数据库中检索视觉上相似的图像来检测循环候选关键帧。

helperCreateNewMapPoints通过三角测量创建新的地图点。

Helperlocalbundleadjustment.优化当前关键帧的姿态和周围场景的地图。

elplersurfeepureextractorfunction实现BagoffecateS中使用的冲浪功能提取。

helperTrackLastKeyFrame通过跟踪最后一个关键帧估计当前相机的姿态。

helperTrackLocalMap通过跟踪本地地图优化当前的相机姿态。

helperViewDirectionAndDepth存储地图点的平均视图方向和预测深度

HelpervisualizeMatchedFeatures.显示框架中的匹配功能。

helperVisualizeMotionAndStructure显示地图点和摄像机轨迹。

helperDetectAndExtractFeatures从图像中检测和提取和解组件。

功能[特征,有效点] = HelperDetectAndExtractFeatures(IRGB,...scaleFactor, numLevels, varargin)%在此示例中,图像已不变置。一般%工作流程,取消注释以下代码以贴变图像。%如果nargin> 3% intrinsics = varargin{1};%结束% Irgb = undistortion timage (Irgb, intrinsics);%检测ORB功能Igray = im2gray (Irgb);点= detectORBFeatures (Igray,“ScaleFactor”scaleFactor,“NumLevels”, numLevels);%选择一个特征子集,均匀分布在整个图像中点= SelectUniform(点,Numpoints,大小(Igray,1:2));%提取特征[功能,有效点] =提取物(IGRAI,点);结束

helperHomographyScore计算众同类和评估重建。

功能[H, score, inlierindex] = helperComputeHomography(matchedPoints1, matchedPoints2) [H, inliersLogicalIndex] = estimateGeometricTransform2D(...matchedPoints1 matchedPoints2,“射影”...“MaxNumTrials”,1e3,“MaxDistance”4'置信度',90);Inlierpoints1 = matchedpoints1(InlierslogicalIndex);Inlierpoints2 = matchedpoints2(InlierslogicalIndex);InliersIndex = find(InlierslogicalIndex);Locations1 = Inlierpoints1.Location;locations2 = Inlierpoints2.Location;XY1in2 = TransformPointSforward(H,Locations1);XY2IN1 = TransformPointsInverse(H,Locations2);error1in2 = sum((locations2  -  xy1in2)。^ 2,2);ERROR2IN1 = SUM((LOCATIONS1  -  XY2IN1)。^ 2,2); outlierThreshold = 6; score = sum(max(outlierThreshold-error1in2, 0)) +...SUM(MAX(OutlierThreshold-error2in1,0));结束

HelperfundamentalMatrixScore.计算基本矩阵并评估重建。

功能[F, score, inlierindex] = helperComputeFundamentalMatrix(matchedpoint1, matchedpoint2) [F, inliersLogicalIndex] = estimateFundamentalMatrix(...matchedPoints1 matchedPoints2,“方法”“RANSAC”...'numtrials',1e3,“DistanceThreshold”,0.01);Inlierpoints1 = matchedpoints1(InlierslogicalIndex);Inlierpoints2 = matchedpoints2(InlierslogicalIndex);InliersIndex = find(InlierslogicalIndex);Locations1 = Inlierpoints1.Location;locations2 = Inlierpoints2.Location;从点到ePipolar线的距离距离lineIn1 = epipolarLine(F', locations2);Error2in1 = (sum([locations1, ones(size(locations1, 1),1)])。* lineIn1, 2)) ^ 2...。/笔(lineIn1(:, 1:2)。2) ^ 2;lineIn2 = epipolarLine(F, locations1);Error1in2 = (sum([locations2, ones(size(locations2, 1),1)])。* lineIn2, 2)) ^ 2...。/笔(lineIn2(:, 1:2)。2) ^ 2;outlierThreshold = 4;得分= sum(max(OutlierThreshold-error1in2,0))+...SUM(MAX(OutlierThreshold-error2in1,0));结束

helperTriangulateTwoFrames对两个帧进行三角定位来初始化地图。

功能[isValid, xyzPoints, inlierIdx] = helperTriangulateTwoFrames(...pose1、pose2 matchedPoints1、matchedPoints2 intrinsic minParallax) (R1, t1) = cameraPoseToExtrinsics (pose1。旋转,pose1.Translation);camMatrix1 = cameraMatrix(intrinsics, R1, t1);[R2, t2] = cameraPoseToExtrinsics(pose2. p);旋转,pose2.Translation);camMatrix2 = cameraMatrix(intrinsics, R2, t2);[xyzPoints, reprojectionErrors, isInFront] = triangulate(matchedPoints1,...matchedPoints2、camMatrix1 camMatrix2);%通过视图方向和重投影错误过滤点minReprojError = 1;inlierIdx = isInFront & reprojectionErrors < minReprojError;xyzPoints = xyzPoints(inlierIdx,:);%具有显著视差的良好双视点RAY1 = XYZPOINTS  -  POSE1.TRANSLATION;RAY2 = XYZPOINTS  -  POSE2.TRANSLATION;casangle = sum(ray1。* ray2,2)./(vecnorm(ray1,2,2)。* vecnorm(Ray2,2,2));%检查视差IsValid =全部(Casangle  0);结束

helperIsKeyFrame检查帧是否是关键帧。

功能isKeyFrame = helperIsKeyFrame (mappoint,...numPointsRefKeyFrame = nummel (findWorldPointsInView(mapPoints, refKeyFrameId));%从上次关键帧插入已经传递了超过20帧tooManyNonKeyFrames = currFrameIndex > lastKeyFrameIndex + 20;%追踪少于100个地图点tooFewMapPoints = nummel (mapPointsIndices) < 100;%追踪的地图点数不到90%的点%参考关键帧toofewtrackkedpoints = nummel (mapPointsIndices) < 0.9 * numPointsRefKeyFrame;isKeyFrame = (tooManyNonKeyFrames || tooFewMapPoints) && toofewtrackkedpoints;结束

helperCullRecentMapPointsCull最近增加了地图点。

功能[mappointset,directionanddepth,mappointsidx] = helpercullrecentmoppoints(mappointset,directionanddepth,mappointsidx,newpointidx)outlieridx = setdiff(newpointidx,mappointsidx);如果~isempty(outlierIdx) mapPointSet = removeWorldPoints(mapPointSet, outlierIdx);directionAndDepth = remove(directionAndDepth, outlierIdx);mappointment sidx = mappointment sidx - arrayfun(@(x) nnz(x>outlierIdx), mappointment sidx);结束结束

helperEstimateTrajectoryError计算跟踪错误。

功能rmse = helperEstimateTrajectoryError(gTruth, cameraPoses) locations = vertcat(cameraPoses. absolutepose . translation);gLocations = vertcat (gTruth.Translation);scale =中位数(vecnorm(gLocations, 2,2))/中位数(vecnorm(locations, 2,2));scaledLocations =位置*比例;rmse = sqrt(mean(sum((scaledLocations - gLocations))。^ 2, 2)));disp ([关键帧轨迹的绝对RMSE (m):,num2str(RMSE)]);结束

helperUpdateGlobalMap在姿态图优化后更新地图点的三维位置

功能[mapPointSet, directionAndDepth] = helperUpdateGlobalMap(...mapPointSet, directionAndDepth, vSetKeyFrames, vSetKeyFramesOptim, poseScales)%helperUpdateGlobalMap在姿态图优化后更新映射点poseold = vsetkeyframes.views.absolutepose;posenew = vsetkeyframesoptim.views.absolutepose;positysold = mappointet.worldpoints;positynew = positysold;indices = 1:mappointet.count;%更新每个地图点的世界位置基于新的绝对姿态%对应的主要观点i = 1:mappointet.count majorviewIds = diventionanddepth.majorviewId(i);posenew = posenew(majorviewids).t;POSENEW(1:3,1:3)= POSENED(1:3,1:3)* PoseScales(MajorViewID);tform = poseold(majorviewids).t \ posenew;Positednew(I,:)= Positysold(I,:) * Tform(1:3,1:3)+ Tform(4,1:3);结束mapPointSet = updateWorldPoints(mapPointSet, indices, positionsNew);结束

参考

murr - artal, Raul, Jose Maria Martinez Montiel和Juan D. Tardos。ORB-SLAM:一种多功能、精确的单目SLAM系统。机器人上的IEEE交易31,不。5,2015年PP 1147-116。

[2] Sturm,Jürgen,尼古拉斯Engelhard,Felix Endres,Wolfram Burgard和Daniel Cremers。“RGB-D SLAM系统评估的基准。在IEEE/RSJ智能机器人与系统国际会议论文集,页573- 580,2012。

相关的话题