主要内容

立体视觉同步定位与映射

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

vSLAM只需要使用单目相机就可以完成。然而,由于无法用一台相机精确计算深度,所以地图的比例尺和估计的轨迹是未知的,而且会随着时间推移而漂移。此外,为了引导系统,需要多个视图来生成初始映射,因为它不能从第一帧开始进行三角定位。使用立体摄像机解决了这些问题,并提供了更可靠的vSLAM解决方案。

这个例子展示了如何处理来自立体相机的图像数据,以建立一个户外环境的地图和估计相机的轨迹。本例使用了ORB-SLAM2的一个版本[1]算法,它是基于特征的,并支持立体相机。金宝app

加工管道概述

立体vSLAM的管道与单目vSLAM的管道非常相似单目视觉同步定位与制图的例子。主要的区别是地图初始化阶段三维地图点是由同一立体对的一对立体图像而不是不同帧的两个图像创建的。

monocularMappingStereo_example.png

  • 地图初始化:管道首先使用视差图从一对立体图像初始化3-D点的映射。左边的图像被存储为第一个关键帧。

  • 跟踪:一旦映射初始化,对于每个新的立体对,通过匹配左边图像中的特征和最后一个关键帧中的特征来估计相机的姿态。通过跟踪局部地图,对估计的相机姿态进行细化。

  • 当地的地图:如果当前左侧图像被识别为关键帧,则根据立体对的视差计算新的三维映射点。在这个阶段,通过调整相机的姿态和3-D点,使用束调整来最小化重投影误差。

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

下载和探索输入立体声图像序列

本例中使用的数据来自长期定位和映射数据集由多伦多大学航空航天研究所提供。您可以使用网页浏览器或运行以下代码将数据下载到目录中:

dataFolder = [fullfile(pwd), filesep,“stereoImageData”];zipFileName = [dataFolder, filesep,“run_000005.zip”];folderExists =存在(dataFolder,“dir”);在当前目录下创建一个文件夹来保存下载的文件。如果~ folderExists mkdir (dataFolder);disp (正在下载run_000005.zip (818 MB)。下载可能需要几分钟。”get ftp://asrl3.utias.utoronto.ca/2020-vtr-dataset/UTIAS-In-The-Dark/run_000005.zip -P ./stereoImageData提取下载文件的内容disp (正在提取run_000005.zip (818 MB)…)解压缩(zipFileName dataFolder);结束

使用两个imageDatastore对象来存储立体图像。

imgFolderLeft = [dataFolder,“/图片/左/”];imgFolderRight = [dataFolder,“/图片/正确/”];imdsLeft = imageDatastore (imgFolderLeft);imdsRight = imageDatastore (imgFolderRight);检查第一对图像currFrameIdx = 1;currILeft = readimage(imdsLeft, currFrameIdx);currIRight = readimage(imdsRight, currFrameIdx);imshowpair (currILeft currIRight,“蒙太奇”);

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

地图初始化

ORB-SLAM管道首先初始化保存3d世界点的地图。这一步是至关重要的,对最终SLAM结果的准确性有重大影响。初始ORB特征点对应使用matchFeatures在两个立体图像之间。匹配的对应满足以下约束条件:

  • 校正后的立体对图像中两个对应特征点之间的水平位移小于最大视差。您可以从立体对图像的立体浮雕中确定近似的最大视差值。有关更多信息,请参见视差范围的选择

  • 校正后的立体声对图像中两个对应特征点之间的垂直位移小于阈值。

  • 匹配特征的尺度几乎完全相同。

匹配特征点对应的三维世界位置确定如下:

  • 使用视差范围的选择利用半全局匹配(SGM)方法计算每对立体图像的视差图。

  • 使用reconstructScene根据视差图计算三维世界点坐标。

  • 在视差图中找出与特征点及其三维世界位置相对应的位置。

为再现性设置随机种子rng (0);加载初始相机姿势。在此基础上推导出初始相机姿态%关于相机和车辆之间的转换:% http://asrl.utias.utoronto.ca/datasets/2020-vtr-dataset/text_files/transform_camera_vehicle.txinitialPoseData =负载(“initialPose.mat”);initialPose = initialPoseData.initialPose;%构造用于三维重建的重投影矩阵。数据集的intrinsic可以在以下页面中找到:% http://asrl.utias.utoronto.ca/datasets/2020-vtr-dataset/text_files/camera_parameters.txtfocalLength = [387.777 387.777];%(像素)principalPoint = [257.446 197.718];以像素为单位的百分比[x, y]基线= 0.239965;%以米为单位图象尺寸大小= (currILeft [1, 2]);%(像素)[mrows, ncols]intrinsic = cameraIntrinsics(focalLength, principalPoint, imageSize);reprojectionMatrix = [1,0,0, -principalPoint(1);0, 1,0, -principalPoint(2);0, 0, 0, focalLength(1);0,0,1 /baseline, 0];在本例中,图像已经未失真和校正。。在一般的工作流程中,% un注释以下代码,以恢复和校正图像。% currILeft = undistortion timage (currILeft, intrinsic);% currIRight = undistortion timage (currIRight, intrinsic);% stereooparams = stereoparameter (intrinsic, intrinsic, eye(3), [-baseline, 0 0]);% [currILeft, currIRight] = rectifyStereoImages(currILeft, currIRight, stereooparams, OutputView="full");从校正后的立体图像中检测并提取ORB特征scaleFactor = 1.2;numLevels = 8;[currFeaturesLeft, currPointsLeft] = helperDetectAndExtractFeatures(im2gray(currILeft), scaleFactor, numLevels);[currFeaturesRight, currPointsRight] = helperDetectAndExtractFeatures(im2gray(currright), scaleFactor, numLevels);匹配立体图像之间的特征点,得到三维世界的位置。disparityRange = [0 48];%(像素)[xyzPoints, matchedPairs] = helperReconstructFromStereo(im2gray(currILeft), im2gray(currIRight),...currFeaturesLeft, currFeaturesRight, currPointsLeft, currPointsRight, reprojectionMatrix, initialPose, disparityRange);

数据管理和可视化

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

创建一个空的imageviewset对象来存储关键帧vSetKeyFrames = imageviewset;创建一个空的worldpointset对象来存储3-D地图点mapPointSet = worldpointset;添加第一个关键帧currKeyFrameId = 1;vSetKeyFrames = addView(vSetKeyFrames, currKeyFrameId, initialPose, Points=currPointsLeft,...特点= currFeaturesLeft.Features);添加3d地图点[mapPointSet, stereoMapPointsIdx] = addWorldPoints(mapPointSet, xyzPoints);添加观察地图点mapPointSet = addanswences (mapPointSet, currKeyFrameId, stereomappointment sidx, matchedPairs(:, 1));更新视图方向和深度mappointment = updateLimitsAndDirection(mappointment, stereomappointment sidx, vSetKeyFrames.Views);%更新代表视图mappointment = updateRepresentativeView(mappointment, stereomappointment sidx, vSetKeyFrames.Views);在第一个关键帧中可视化匹配的特征featurePlot = helperVisualizeMatchedFeaturesStereo(currILeft, currIRight, currPointsLeft,...currPointsRight matchedPairs);可视化初始地图点和相机轨迹mapPlot = helperVisualizeMotionAndStructureStereo(vSetKeyFrames, mapptset);%显示传奇showLegend (mapPlot);

初始化地点识别数据库

使用单词袋方法执行循环检测。一种视觉词汇表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 currFeaturesLeft currKeyFrameId);

跟踪

跟踪过程使用每个对执行,并确定何时插入新的关键帧。

最后一个关键帧的ViewIdlastKeyFrameId = currKeyFrameId;输入图像序列中最后一个关键帧的索引lastKeyFrameIdx = currFrameIdx;%输入图像序列中所有关键帧的索引addedFramesIdx = lastKeyFrameIdx;currFrameIdx = 2;isLoopClosed = false;

每一帧处理如下:

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

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

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

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

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

  3. 跟踪的最后一步是决定当前帧是否应该是一个新的关键帧。当满足以下两个条件时,一帧就是关键帧:

  • 从上一个关键帧到现在已经经过了至少5帧,或者当前帧跟踪不到80个地图点。

  • 当前帧跟踪的映射点小于参考关键帧跟踪的90%。

如果当前帧要成为关键帧,则继续当地的地图的过程。否则,开始跟踪下一帧。

%主循环isLastFrameKeyFrame = true;~isLoopClosed && currFrameIdx <= numel(imdsLeft. files) currILeft = readimage(imdsLeft, currFrameIdx);currIRight = readimage(imdsRight, currFrameIdx);currILeftGray = im2gray (currILeft);currIRightGray = im2gray (currIRight);[currFeaturesLeft, currPointsLeft] = helperDetectAndExtractFeatures(currILeftGray, scaleFactor, numLevels);[currFeaturesRight, currPointsRight] = helperDetectAndExtractFeatures(currIRightGray, scaleFactor, numLevels);跟踪最后一个关键帧% trackedmappointment sidx:当前左帧中观察到的映射点的索引% trackedFeatureIdx:当前左帧对应特征点的索引[currPose, trackedMapPointsIdx, trackedFeatureIdx] = helperTrackLastKeyFrame(mapPointSet,...vSetKeyFrames。视图,currFeaturesLeft, currPointsLeft, lastKeyFrameId, intrinsic, scaleFactor);如果isempty(currPose) || numl (trackedMapPointsIdx) < 30 currFrameIdx = currFrameIdx + 1;继续结束跟踪本地地图,检查当前帧是否是关键帧。% localKeyFrameIds:当前帧连接的关键帧的ViewIdnumSkipFrames = 5;numPointsKeyFrame = 80;[localKeyFrameIds, currPose, trackedMapPointsIdx, trackedFeatureIdx, isKeyFrame] =...helperTrackLocalMap (mapPointSet vSetKeyFrames trackedMapPointsIdx,...trackedFeatureIdx, currPose, currFeaturesLeft, currPointsLeft, intrinsic, scaleFactor, numLevels,...isLastFrameKeyFrame, lastKeyFrameIdx, currFrameIdx, numSkipFrames, numPointsKeyFrame);匹配立体图像之间的特征点,得到三维世界的位置。[xyzPoints, matchedPairs] = helperReconstructFromStereo(currILeftGray, currIRightGray, currFeaturesLeft,...currFeaturesRight, currPointsLeft, currPointsRight, reprojectionMatrix, currPose, disparityRange);在立体图像中可视化匹配的特征updatePlot(featurePlot, currILeft, currIRight, currPointsLeft, currPointsRight, trackedFeatureIdx, matchedPairs);如果~isKeyFrame && currFrameIdx < numel(imdsLeft.Files) currFrameIdx = currFrameIdx + 1;isLastFrameKeyFrame = false;继续其他的[untrackedFeatureIdx, ia] = setdiff(matchedPairs(:, 1), trackedFeatureIdx);xyzPoints = xyzPoints(ia,:);isLastFrameKeyFrame = true;结束更新当前关键帧IDcurrKeyFrameId = currKeyFrameId + 1;

当地的地图

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

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

添加新的关键帧[mappset, vSetKeyFrames] = helperAddNewKeyFrame(mappset, vSetKeyFrames,...currPose, currFeaturesLeft, currPointsLeft, trackedMapPointsIdx, trackedFeatureIdx, localKeyFrameIds);移除在少于3个关键帧内观察到的离群图点如果currKeyFrameId == 2 triangulatedMapPointsIdx = [];结束mapPointSet = helperCullRecentMapPoints (mapPointSet,...trackedMapPointsIdx、triangulatedMapPointsIdx stereoMapPointsIdx);添加从视差计算的新地图点[mapPointSet, stereoMapPointsIdx] = addWorldPoints(mapPointSet, xyzPoints);mapPointSet = addanswences (mapPointSet, currKeyFrameId, stereomappointment sidx,...untrackedFeatureIdx);通过三角剖分创建新的地图点minNumMatches = 20;minParallax = 0.35;[mappointment set, vSetKeyFrames, triangulatedmappointment sidx, stereomappointment sidx] = helpercreatenewmappointment stereo (...mappset, vSetKeyFrames, currKeyFrameId, intrinsic, scaleFactor, minNumMatches, minParallax,...untrackedFeatureIdx stereoMapPointsIdx);%本地包调整[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);%更新代表视图mapPointSet = updateRepresentativeView(mapPointSet, mappoint dx,...vSetKeyFrames.Views);可视化3-D世界点和相机轨迹updatePlot (mapPlot vSetKeyFrames mapPointSet);

循环关闭

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

当找到一个有效的循环闭合候选帧时,计算循环闭合候选帧和当前关键帧之间的相对位姿estgeotform3d.然后添加与相对姿势和更新的循环连接mapPointSet而且vSetKeyFrames

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

{

{

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

最后,对基本图进行位姿图优化vSetKeyFrames纠正偏差。基本图是通过删除小于的连接在内部创建的minNumMatches在共可见度图中匹配。位姿图优化后,利用优化后的位姿更新地图点的三维位置。

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

与基本真理比较

你可以比较优化的相机轨迹与地面真相,以评估解决的准确性。下载的数据中包含gps.txt该文件存储了每一帧的GPS位置。使用将GPS位置从地理坐标转换为本地笛卡尔坐标latlon2local(自动驾驶工具箱)从自动驾驶工具箱或geodetic2enu(映射工具箱)从映射工具。在本例中,您可以简单地从m文件加载转换后的GPS数据。

载入GPS数据gpsData =负载(“gpsLocation.mat”);gpsLocation = gpsData.gpsLocation;将GPS位置转换为参考坐标系统gTruth = helperTransformGPSLocations(gpsLocation, optimizedposed);绘制GPS定位图plotActualTrajectory (mapPlot gTruth (addedFramesIdx:));%显示传奇showLegend (mapPlot);

{

立体图像的密集重建

给定了精致的相机姿态,您可以从关键帧对应的立体图像执行密集重建。

创建一个pointCloud对象数组来存储3d世界点。%与关键帧相关联ptClouds = repmat(pointCloud(0 (1,3)), numel(addedFramesIdx), 1);i = 1: numel(addedFramesIdx) ILeft = readimage(imdsLeft, addedFramesIdx(i));IRight = readimage(imdsRight, addedFramesIdx(i));从视差中重建场景distitymap = distitysgm (im2gray(ILeft), im2gray(IRight), distityrange = distityrange);xyzPoints = reconstructScene(distitymap, reprojectionMatrix);忽略图像的上半部分,它主要显示的是天空xyzPoints(1:floor(imageSize(1)/2),:,:) = NaN;忽略图片下方显示车辆的部分。xyzPoints(imageSize(1)-50:end,::) = NaN;xyzPoints =重整(xyzPoints, [imageSize(1)*imageSize(2), 3]);从彩色图像中获取颜色colors =重整(ILeft, [imageSize(1)*imageSize(2), 3]);移除离镜头太远的世界点。validIndex = xyzPoints(:, 3) > 0 & xyzPoints(:, 3) < 100/reprojectionMatrix(4,3);xyzPoints = xyzPoints(validIndex,:);colors = colors(validIndex,:);将世界点转换为世界坐标currPose = optimizedPoses.AbsolutePose(我);xyzPoints = transformPointsForward(currPose, xyzPoints);ptCloud = pointCloud(xyzPoints, Color=colors);%对点云进行下采样ptClouds(i) = pcdownsample(ptCloud, random=0.5);结束连接点云pointCloudsAll = pccat (ptClouds);形象化点云图ax = pcshow(pointCloudsAll,VerticalAxis=“y”VerticalAxisDir =“向下”);包含(“X”) ylabel (“Y”) zlabel (“Z”)标题(“密集的重建”显示鸟瞰图的场景视图(ax, [0 0 1]);camroll (ax, 90);

金宝app支持功能

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

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

函数[features, validPoints] = helperDetectAndExtractFeatures(Igray, scaleFactor, numLevels) numPoints = 600;%检测ORB功能points = detectORBFeatures(灰度,ScaleFactor= ScaleFactor, NumLevels= NumLevels);选择特征的子集,均匀分布在整个图像。points = selectUniform(points, numPoints, size(灰度,1:2));%提取特征[features, validPoints] = extractFeatures(Igray, points);结束

helperReconstructFromStereo利用视差图从立体图像中重建场景

函数[xyzPoints, indexPairs] = helperReconstructFromStereo(I1, I2,...features1, features2, points1, points2, reprojectionMatrix, currPose, disparityRange) indexPairs = helperfindvalidfeatufixes (features1, features2, points1, points2, disparityRange);计算左边图像中所有像素的视差。实际上,它更多%常用来计算匹配特征点的视差。disitymap = disityysgm (I1, I2, disityrange = disityrange);xyzPointsAll = reconstructScene(distitymap, reprojectionMatrix);查找匹配特征点对应的世界点。位置=地板(里。Location(indexPairs(:, 1), [2 1]));xyzPoints = [];isPointFound = false(大小(里));i = 1:size(locations, 1) point3d = squeeze(xyzPointsAll(locations(i,1), locations(i, 2),:))';isPointValid = point3d(3) > 0 & point3d(3) < 200/reprojectionMatrix(4,3);如果isPointValid xyzPoints = [xyzPoints;point3d];% #好< * AGROW >isPointFound (i) = true;结束结束indexPairs = indexPairs(isPointFound,:);xyzPoints = xyzPoints * currPose。旋转+ currPose.Translation;结束

helperFindValidFeaturePairs匹配一对立体图像之间的特征

函数indexPairs = helperfindvalidfeatufixes (features1, features2, points1, points2, disparityRange) indexPairs = matchFeatures(features1, features2, features2,...独特的= true, MaxRatio = 1, MatchThreshold = 40);matchedPoints1 = points1.Location(indexPairs(:,1),:);matchedPoints2 = points2.Location(indexPairs(:,2),:);scales1 = points1.Scale(indexPairs(:,1),:);scales2 = points2.Scale(indexPairs(:,2),:);dist2EpipolarLine = abs(matchedPoints2(:, 2) - matchedPoints1(:, 2));shiftDist = matchedPoints1(:, 1) - matchedPoints2(:, 1);isCloseToEpipolarline = dist2EpipolarLine < 2*scales2;isDisparityValid = shiftDist > 0 & shiftDist < disparityRange(2);isscalesame = scales1 == scales2; indexPairs = indexPairs(isCloseToEpipolarline & isDisparityValid & isScaleIdentical, :);结束

helperCullRecentMapPointsCull最近增加了地图点。

函数mappointment = helperCullRecentMapPoints(mappointment set, mappointment sidx, newPointIdx, stereomappointment sindices) outlierIdx = setdiff([newPointIdx;stereoMapPointsIndices], mapPointsIdx);如果~isempty(outlierIdx) mapPointSet = removeWorldPoints(mapPointSet, outlierIdx);结束结束

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

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

helperTransformGPSLocations将GPS位置转换为参考坐标系统

函数gTruth = helpertransformgppslocations (gppslocations, optimizedposed) initialYawGPS = atan((gppslocations (100,2) - gppslocations (1,2)) /...(gpsLocations(100, 1) - gpsLocations(1,1)));initialYawSLAM = atan((optimizedposesabsolutepose (50).Translation(2) -...optimizedPoses.AbsolutePose (1) .Translation) / (2)...(optimizedPoses.AbsolutePose (59) .Translation (1) -...optimizedPoses.AbsolutePose (1) .Translation (1)));relYaw = initialYawGPS - initialYawSLAM;relTranslation = optimizedPoses.AbsolutePose (1) .Translation;initialTform = rotationVectorToMatrix([0 0 relYaw]);i = 1:size(gpsLocations, 1) gTruth(i,:) = initialTform * gpsLocations(i,:)' + relTranslation';结束结束

参考文献

[1]穆尔-阿尔塔尔,劳尔和胡安·D. Tardós。ORB-SLAM2:一种用于单眼、立体和RGB-D相机的开源SLAM系统。IEEE机器人汇刊33岁的没有。5(2017): 1255 - 1262。