主要内容

单目视觉同步定位与制图

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

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

要加快计算速度,可以从计算机视觉工具箱首选项对话框。要打开计算机视觉工具箱™首选项,请在首页选项卡,环境部分中,点击首选项.然后选择计算机视觉的工具箱。

术语表

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

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

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

  • Covisibility图:由关键帧作为节点组成的图。如果两个关键帧共享公共映射点,则由一条边连接。边的权值是共享映射点的数量。

  • 基本图:共能见度图的一个子图,只包含高权重的边,即更多的共享映射点。

  • 位置识别数据库:用来识别一个地方过去是否被访问过的数据库。数据库存储基于特征输入包的可视化词到图像映射。它用于搜索视觉上与查询图像相似的图像。

概述ORB-SLAM

ORB-SLAM管道包括:

  • 地图初始化: ORB-SLAM首先从两个视频帧初始化3-D点的映射。利用基于二维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(超时=正);tgzFileName = [dataFolder,“fr3_office.tgz”];folderExists =存在(dataFolder,“dir”);在临时目录中创建一个文件夹来保存下载的文件。如果~ folderExists mkdir (dataFolder);disp (“下载fr3_office。tgz (1.38 GB)。下载可能需要几分钟。”) websave(tgzFileName, baseDownloadURL, options);提取下载文件的内容disp (“提取fr3_office。tgz (1.38 GB)…”)解压(tgzFileName dataFolder);结束

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

imageFolder = [dataFolder,“rgbd_dataset_freiburg3_long_office_household / rgb /”];imd = imageDatastore (imageFolder);检查第一张图片currFrameIdx = 1;currI = readimage(imds, currFrameIdx);himage = imshow (currI);

地图初始化

ORB-SLAM管道首先初始化保存3d世界点的地图。这一步是至关重要的,对最终SLAM结果的准确性有重大影响。初始ORB特征点对应使用matchFeatures在一对图像之间。找到对应关系后,使用两个几何变换模型建立映射初始化:

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

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

单应性和基本矩阵可以用estgeotform2d而且estimateFundamentalMatrix,分别。选择重投影误差较小的模型来估计两帧间的相对旋转和平移estrelpose.由于RGB图像是由单目相机拍摄的,不提供深度信息,因此只能恢复到特定的比例因子的相对平移。

根据相机的相对姿态和两幅图像中匹配的特征点,利用由三角形组成的函数。当一个三角化地图点位于两个摄像机的前面,当它的重投影误差较低,且该点的两个视图的视差足够大时,该三角化地图点是有效的。

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

图中包含一个axes对象。坐标轴对象包含4个类型为image, line的对象。

存储初始关键帧和地图点

在使用两帧初始化映射之后,您可以使用imageviewset而且worldpointset存储两个关键帧和对应的映射点:

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

  • worldpointset存储映射点的三维位置和映射点的二维投影对应关系:哪些映射点在一个关键帧中被观察,哪些关键帧观察一个映射点。它还存储映射点的其他属性,例如平均视图方向、代表ORB描述符以及可以观察到映射点的距离范围。

创建一个空的imageviewset对象来存储关键帧vSetKeyFrames = imageviewset;创建一个空的worldpointset对象来存储3-D地图点mapPointSet = worldpointset;添加第一个关键帧。将相机与第一个相关联%关键帧在原点,沿z轴方向preViewId = 1;vSetKeyFrames = addView(vSetKeyFrames, preViewId, rigidtform3d, Points=prePoints,...特点= preFeatures.Features);添加第二个关键帧currViewId = 2;vSetKeyFrames = addView(vSetKeyFrames, currViewId, relPose, Points=currPoints,...特点= currFeatures.Features);在第一个和第二个关键帧之间添加连接vSetKeyFrames = addConnection(vSetKeyFrames, preViewId, currViewId, relPose, Matches=indexPairs);添加3d地图点[mapPointSet, newPointIdx] = addWorldPoints(mapPointSet, xyzWorldPoints);添加观察地图点preLocations = prePoints.Location;currLocations = currPoints.Location;预分频= prePoints.Scale;currScales = currPoints.Scale;在第一个关键帧中添加对应的贴图点mapPointSet = addmappings (mapPointSet, preViewId, newPointIdx, indexPairs(:,1));在第二关键帧中添加对应的贴图点。mapPointSet = addmappings (mapPointSet, currViewId, newPointIdx, indexPairs(:,2));

初始化地点识别数据库

使用单词袋方法执行循环检测。一种视觉词汇表bagOfFeatures对象通过调用从数据集中的大量图像中提取的ORB描述符离线创建:

bag = bagOfFeatures(imds,CustomExtractor=@helperORBFeatureExtractorFunction,TreeProperties=[3,10], stronggestfeatures =1);

在哪里洛桑国际管理发展学院是一个imageDatastore对象存储训练图像和helperORBFeatureExtractorFunction为ORB特征提取函数。看到基于视觉词袋的图像检索为更多的信息。

循环闭包过程增量地构建一个数据库,表示为invertedImageIndex对象,该对象存储基于ORB特性包的可视化字到图像映射。

加载离线创建的特性数据包。bofData =负载(“bagOfFeaturesDataSLAM.mat”);初始化地点识别数据库loopDatabase = invertedImageIndex (bofData.bof SaveFeatureLocations = false);将前两个关键帧的特征添加到数据库中。addImageFeatures (loopDatabase preFeatures preViewId);addImageFeatures (loopDatabase currFeatures currViewId);

细化和可视化初始重构

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

在前两个关键帧上运行完整的bundle调整跟踪= findTracks (vSetKeyFrames);cameraPoses =姿势(vSetKeyFrames);[refinedPoints, refinedAbsPoses] = bundleadjustadjustment (xyzWorldPoints, tracks,...cameraPoses, intrinsic FixedViewIDs = 1,...PointsUndistorted = true, AbsoluteTolerance = 1 e -,...RelativeTolerance = 1 e15汽油,MaxIteration = 20,...解算器=“preconditioned-conjugate-gradient”);缩放地图和相机姿势使用地图点的中位深度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);更新地图点与细化的位置mappointment = updateWorldPoints(mappointment, newPointIdx, refinedPoints);更新视图方向和深度mapPointSet = updateLimitsAndDirection(mapPointSet, newPointIdx, vSetKeyFrames.Views);%更新代表视图mapPointSet = updateRepresentativeView(mapPointSet, newPointIdx, vSetKeyFrames.Views);在当前帧中可视化匹配的特征关闭(hfeature.Parent.Parent);featurePlot = helperVisualizeMatchedFeatures(currI, currPoints(indexPairs(:,2)));

{

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

{

跟踪

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

当前关键帧的% ViewIdcurrKeyFrameId = currViewId;最后一个关键帧的ViewIdlastKeyFrameId = currViewId;输入图像序列中最后一个关键帧的索引lastKeyFrameIdx = currFrameIdx - 1;%输入图像序列中所有关键帧的索引addedFramesIdx = [1;lastKeyFrameIdx];isLoopClosed = false;

每一帧处理如下:

  1. 为每个新帧提取ORB特征,然后进行匹配(使用matchFeatures),最后一个关键帧的特征已经知道对应的三维地图点。

  2. 利用透视-n点算法估计相机的姿态estworldpose

  3. 给定相机姿势,将最后一个关键帧观察到的地图点投影到当前帧,并使用搜索特征对应matchFeaturesInRadius

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

  5. 将局部映射点投影到当前帧中,以搜索更多的特征对应matchFeaturesInRadius并将相机的姿势再完善使用bundleAdjustmentMotion

  6. 跟踪的最后一步是确定当前帧是否是一个新的关键帧。如果当前帧是关键帧,则继续当地的地图的过程。否则,开始跟踪下一帧。

如果跟踪丢失是因为没有足够的特征点可以匹配,尝试插入新的关键帧更频繁。

%主循环isLastFrameKeyFrame = true;~isLoopClosed && currFrameIdx < numel(imds. files) currI = readimage(imds, currFrameIdx);[currFeatures, currPoints] = helperDetectAndExtractFeatures(currI, scaleFactor, numLevels, numPoints);跟踪最后一个关键帧% mapPointsIdx:当前帧中观察到的映射点的索引的对应特征点的索引%当前帧[currPose, mappetsidx, featureIdx] = helperTrackLastKeyFrame(mappetset,...vSetKeyFrames。视图,currFeatures, currPoints, lastKeyFrameId, intrinsic, scaleFactor);跟踪本地地图,检查当前帧是否是关键帧。%满足以下两个条件的帧是关键帧:% 1。从上一个关键帧到现在至少已经过去了20帧%当前帧跟踪少于100个地图点。% 2。当前帧跟踪的地图点小于90%由参考关键帧跟踪的%点。跟踪性能对numPointsKeyFrame的值很敏感。%如果跟踪丢失,请尝试更大的值。% localKeyFrameIds:当前帧连接的关键帧的ViewIdnumSkipFrames = 20;numPointsKeyFrame = 80;[localKeyFrameIds, currPose, mapptsidx, featureIdx, isKeyFrame] =...helperTrackLocalMap (mapPointSet vSetKeyFrames mapPointsIdx,...featureIdx, currPose, currFeatures, currPoints, intrinsic, scaleFactor, numLevels,...isLastFrameKeyFrame, lastKeyFrameIdx, currFrameIdx, numSkipFrames, numPointsKeyFrame);%可视化匹配的特征updatePlot (featurePlot、currI currPoints (featureIdx));如果~isKeyFrame currFrameIdx = currFrameIdx + 1;isLastFrameKeyFrame = false;继续其他的isLastFrameKeyFrame = true;结束更新当前关键帧IDcurrKeyFrameId = currKeyFrameId + 1;

当地的地图

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

通过对当前关键帧及其连接的关键帧中的ORB特征点进行三角化来创建新的映射点。对于当前关键帧中的每一个不匹配的特征点,使用matchFeatures.局部束调整细化当前关键帧的姿态,连接的关键帧的姿态,以及在这些关键帧中观察到的所有映射点。

添加新的关键帧[mappset, vSetKeyFrames] = helperAddNewKeyFrame(mappset, vSetKeyFrames,...currPose, currFeatures, currPoints, mapPointsIdx, featureIdx, localKeyFrameIds);移除在少于3个关键帧内观察到的离群图点mapPointSet = helperCullRecentMapPoints(mapPointSet, mapPointsIdx, newPointIdx);通过三角剖分创建新的地图点minNumMatches = 10;minParallax = 3;[mapPointSet, vSetKeyFrames, newPointIdx] = helperCreateNewMapPoints(mapPointSet, vSetKeyFrames,...currKeyFrameId, intrinsic, scaleFactor, minNumMatches, minParallax);%本地包调整[refinedViews, dist] = connectedViews(vSetKeyFrames, currKeyFrameId, MaxDistance=2);refinedKeyFrameIds = refinedViews.ViewId;fixedViewIds = refinedKeyFrameIds (dist = = 2);fixedViewIds = fixedViewIds(1:min(10, numel(fixedViewIds)));优化本地关键帧和地图点[mapPointSet, vSetKeyFrames, mappoint] = bundleAdjustment(...mapPointSet vSetKeyFrames, [refinedKeyFrameIds;intrinsic currKeyFrameId),...FixedViewIDs = FixedViewIDs PointsUndistorted = true, AbsoluteTolerance = 1 e -,...RelativeTolerance = 1 e-16,解算器=“preconditioned-conjugate-gradient”...MaxIteration = 10);更新视图方向和深度mapPointSet = updateLimitsAndDirection(mapPointSet, mappoint dx, vSetKeyFrames.Views);%更新代表视图mappointment = updateRepresentativeView(mappointment, mappointment, vSetKeyFrames.Views);可视化3D世界点和相机轨迹updatePlot (mapPlot vSetKeyFrames mapPointSet);

循环关闭

闭环检测步骤取本地映射进程处理过的当前关键帧,尝试检测并关闭闭环。通过在数据库中查询与当前关键帧在视觉上相似的图像来识别循环候选帧evaluateImageRetrieval.如果一个候选关键帧没有连接到最后一个关键帧,并且它的三个邻居关键帧都是循环候选关键帧,那么这个候选关键帧就是有效的。

当找到有效的循环候选时,使用estgeotform3d计算循环候选帧和当前关键帧之间的相对位姿。相对位姿表示存储在affinetform3d对象。然后添加与相对姿势和更新的循环连接mapPointSet而且vSetKeyFrames

在创建一些关键帧后检查循环关闭如果currKeyFrameId > 20循环边缘特征匹配的最小个数loopEdgeNumMatches = 50;检测可能的闭环关键帧候选帧[isDetected, validloopcandidate] = helperCheckLoopClosure(vSetKeyFrames, currKeyFrameId,...loopDatabase、currI loopEdgeNumMatches);如果isDetected添加环路闭合连接[isLoopClosed, mapPointSet, vSetKeyFrames] = helperAddLoopConnections(..., validloopcandidate, currKeyFrameId,...currFeatures loopEdgeNumMatches);结束结束%如果未检测到循环关闭,则将当前特性添加到数据库中如果~isLoopClosed addImageFeatures(loopDatabase, currFeatures, currKeyFrameId);结束更新id和索引lastKeyFrameId = currKeyFrameId;lastKeyFrameIdx = currFrameIdx;addedFramesIdx = [addedFramesIdx;currFrameIdx];% #好< AGROW >currFrameIdx = currFrameIdx + 1;结束主循环结束

{

{

在关键帧:2和150之间添加循环边缘

最后,对基本图进行相似位姿图优化vSetKeyFrames校正相机姿势的漂移。基本图是通过删除小于的连接在内部创建的minNumMatches在共可见度图中匹配。在相似位姿图优化后,利用优化后的位姿和相关比例尺更新地图点的三维位置。

%优化姿势minNumMatches = 20;[vSetKeyFramesOptim, poseScales] = optimizepose (vSetKeyFrames, minNumMatches, Tolerance=1e-16);优化姿势后更新地图点mappset = helperUpdateGlobalMap(mappset, vSetKeyFrames, vSetKeyFramesOptim, poseScales);updatePlot (mapPlot vSetKeyFrames mapPointSet);绘制优化后的相机轨迹optimizedPoses =姿势(vSetKeyFramesOptim);plotOptimizedTrajectory (mapPlot optimizedPoses)%更新传奇showLegend (mapPlot);

与基本真理比较

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

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

{

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

最后概述了如何使用ORB-SLAM构建室内环境的地图和估计摄像机的轨迹。通过调优以下参数,您可以使用不同的数据集测试可视化的SLAM管道:

  • numPoints:用于图像分辨率设置为480x640像素numPoints到1000岁。对于更高的分辨率,如720 × 1280,设置为2000。较大的值需要更多的时间进行特征提取。

  • numSkipFrames:当帧率为30fps时,设置numSkipFrames是20。对于较慢的帧速率,将其设置为较小的值。增加numSkipFrames提高跟踪速度,但可能会导致跟踪丢失,当相机运动很快。

支持功能

下面包含了简短的助手函数。更大的函数包含在单独的文件中。

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

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

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

helperCreateNewMapPoints通过三角剖分创建新的地图点。

helperLocalBundleAdjustment细化当前关键帧的姿态和周围场景的地图。

helperORBFeatureExtractorFunction实现bagfeatures中使用的ORB特征提取。

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

helperTrackLocalMap通过跟踪本地地图来改进当前的相机姿势。

helperVisualizeMatchedFeatures在一帧中显示匹配的特征。

helperVisualizeMotionAndStructure显示地图点和相机轨迹。

helperDetectAndExtractFeatures从图像中检测和提取ORB特征。

函数[features, validPoints] = helperDetectAndExtractFeatures(Irgb,...scaleFactor, numLevels, numPoints, varargin)在本例中,图像已经未失真。。在一般%工作流,取消注释以下代码以恢复图像失真。% if nargin > 4% intrinsic = varargin{1};%结束% Irgb = undistortion timage (Irgb, intrinsic);%检测ORB功能Igray = im2gray (Irgb);points = detectORBFeatures(灰度,ScaleFactor= ScaleFactor, NumLevels= NumLevels);选择特征的子集,均匀分布在整个图像。points = selectUniform(points, numPoints, size(灰度,1:2));%提取特征[features, validPoints] = extractFeatures(Igray, points);结束

helperHomographyScore计算单应性和评估重建。

函数[H, score, inliersIndex] = helperComputeHomography(matchedPoints1, matchedPoints2) [H, inliersloicalindex] = estgeotform2d(...matchedPoints1 matchedPoints2,“射影”...MaxNumTrials = 1 e3, MaxDistance = 4,信心= 90);inlierPoints1 = matchedPoints1 (inliersLogicalIndex);inlierPoints2 = matchedPoints2 (inliersLogicalIndex);inliersIndex =找到(inliersLogicalIndex);locations1 = inlierPoints1.Location;locations2 = inlierPoints2.Location;xy1In2 = transformPointsForward(H, location1);xy2In1 = transformPointsInverse(H, locations2);error1in2 = sum((locations2 - xy1In2)。2) ^ 2;error2in1 = sum((location1 - xy2In1)。2) ^ 2; outlierThreshold = 6; score = sum(max(outlierThreshold-error1in2, 0)) +...sum (max (outlierThreshold-error2in1, 0));结束

helperFundamentalMatrixScore计算基本矩阵和评估重建。

函数[F, score, inliersIndex] = helperComputeFundamentalMatrix(matchedPoints1, matchedPoints2) [F, inlierlogicalindex] = estimateFundamentalMatrix(...matchedPoints1 matchedPoints2方法=“RANSAC”...NumTrials = 1 e3, DistanceThreshold = 0.01);inlierPoints1 = matchedPoints1 (inliersLogicalIndex);inlierPoints2 = matchedPoints2 (inliersLogicalIndex);inliersIndex =找到(inliersLogicalIndex);locations1 = inlierPoints1.Location;locations2 = inlierPoints2.Location;点到极线的距离lineIn1 = epipolarLine(F', locations2);Error2in1 = (sum([locations1, ones(size(locations1, 1),1)]。* lineIn1, 2)) ^ 2...。/笔(lineIn1(:, 1:2)。2) ^ 2;lineIn2 = epipolarLine(F, location1);Error1in2 = (sum([locations2, ones(size(locations2, 1),1)].使用实例* lineIn2, 2)) ^ 2...。/笔(lineIn2(:, 1:2)。2) ^ 2;outlierThreshold = 4;score = sum(max(outlierThreshold-error1in2, 0)) +...sum (max (outlierThreshold-error2in1, 0));结束

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

函数[isValid, xyzPoints, inlierIdx] = helperTriangulateTwoFrames(...camMatrix1 = cameraProjection(intrinsic, pose2extr(pose1));camMatrix2 = cameraProjection(intrinsic, pose2extr(pose2));[xyzPoints, reprojectionErrors, isInFront] = triangulate(matchedPoints1, isInFront),...matchedPoints2、camMatrix1 camMatrix2);%根据视图方向和重投影错误筛选点minReprojError = 1;inlierIdx = isInFront & reprojectionErrors < minReprojError;xyzPoints = xyzPoints(inlierIdx,:);一个具有显著视差的良好双视图ray1 = xyzPoints - pose1.Translation;ray2 = xyzPoints - pose2.Translation;cosAngle = sum(ray1 .* ray2, 2) ./ (vecnorm(ray1, 2,2) .* vecnorm(ray2, 2,2));%检查视差isValid = all(cosAngle < cosd(minParallax) & cosAngle>0);结束

helperCullRecentMapPointsCull最近增加了地图点。

函数mapPointSet = helperCullRecentMapPoints(mapPointSet, mapPointsIdx, newPointIdx) outlierIdx = setdiff(newPointIdx, mpointsidx);如果~isempty(outlierIdx) mapPointSet = removeWorldPoints(mapPointSet, outlierIdx);结束结束

helperEstimateTrajectoryError计算跟踪误差。

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

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

函数mapPointSet = helperUpdateGlobalMap (...mappset, vSetKeyFrames, vSetKeyFramesOptim, poseScales)%helperUpdateGlobalMap在位姿图优化后更新映射点posesOld = vSetKeyFrames.Views.AbsolutePose;一直陷于= vSetKeyFramesOptim.Views.AbsolutePose;positionsOld = mapPointSet.WorldPoints;positionsNew = positionsOld;指数= 1:mapPointSet.Count;更新每个地图点的世界位置基于新的绝对姿势。%对应的主要视图i = indexes majorViewIds = mappointment . representativeviewid (i);poseNew =一直陷于(majorViewIds)。;poseNew(1:3, 1:3) = poseNew(1:3, 1:3) * poseScales(majorViewIds);tform = affinetform3d (poseNew / posesOld (majorViewIds)。);positionsNew(i,:) = transformPointsForward(tform, positionsOld(i,:));结束mappointment = updateWorldPoints(mappointment, indexes, positionsNew);结束

参考

[1]穆尔-阿塔尔,劳尔,何塞·玛丽亚·马丁内斯·蒙蒂埃尔,胡安·塔多斯。“ORB-SLAM:一种多功能和精确的单目SLAM系统。”IEEE机器人汇刊31日。5, pp 1147-116, 2015。

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

相关的话题

Baidu
map