[RGB-D (depth camera)๋ฅผ ์ด์ฉํ vSLAM]
๐RGB-D vSLAM Example in matlab official
๋ชฉํ: RGB-D(=๋์ค์นด๋ฉ๋ผ)๋ฅผ ์ด์ฉํ 3D Slam์ ๋งคํ๋ฉ์์ ๊ตฌํ. ์์ ๋ฐ๋ผ๊ฐ๊ธฐ
1.Methodology of 3D Slam by RGB-D camera

SLAM flow๋ ์คํ
๋ ์ค ์นด๋ฉ๋ผ์ ์ ์ฌํ ๊ตฌ์กฐ์ด๋ค.
1.Map initialize: RGB image + Depth image๋ก 3D ๊ณต๊ฐ๊ตฌ์ฑ ์์
2.Tracking: ์ฌ๋ฌ์ฅ์ ์ฌ์ง์ ์ฐ์ผ๋ฉฐ, ํคํ๋ ์๊ฐ์ ํน์ง์ ์ ๋ณํ๋ฅผ ๊ณ์ฐ
3.Local Mapping: ์๋ก์ด ํคํ๋ ์์ด ๋ค์ด์ฌ๋๋ง๋ค, map point๋ฅผ ๊ณ์ฐํ์ฌ mapping
4.Loop closure: ๊ฐ์ ๊ณต๊ฐ์ ๋ค์ ๋์์ฌ๋, ์ด์ ์ ์๋ ๊ณต๊ฐ์์ ์ธ์งํ์ฌ, ์ค์ฐจ(drift)๋ณด์
โ ๏ธ์์ ๋ฅผ ๋ถ๋ฌ์๋ ํฌํผํจ์๊ฐ ๋ฑ๋ก์ด ์๋๋ ๊ฒฝ์ฐ๊ฐ ์์๋ค. ์ด ๊ฒฝ์ฐ ๋น ํจ์ํ์ผ์ ๋ง๋ค์ด์ฃผ๊ณ , ํฌํผํจ์ ๋ด์ฉ์ ์ง์ด๋ฃ์ด์ฃผ์.
1. ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋ ๋ฐ ๋๋ ํ ๋ฆฌ ์ค์
baseDownloadURL = "https://cvg.cit.tum.de/rgbd/dataset/freiburg3/rgbd_dataset_freiburg3_long_office_household.tgz";
dataFolder = fullfile(tempdir, 'tum_rgbd_dataset', filesep);
options = weboptions('Timeout', Inf);
tgzFileName = [dataFolder, 'fr3_office.tgz'];
folderExists = exist(dataFolder, 'dir');
% Create a folder in a temporary directory to save the downloaded file
if ~folderExists
mkdir(dataFolder);
disp('Downloading fr3_office.tgz (1.38 GB). This download can take a few minutes.')
websave(tgzFileName, baseDownloadURL, options);
% Extract contents of the downloaded file
disp('Extracting fr3_office.tgz (1.38 GB) ...')
untar(tgzFileName, dataFolder);
end
imageFolder = [dataFolder, 'rgbd_dataset_freiburg3_long_office_household/'];
imgFolderColor = [imageFolder,'rgb/'];
imgFolderDepth = [imageFolder,'depth/'];
imdsColor = imageDatastore(imgFolderColor);
imdsDepth = imageDatastore(imgFolderDepth);
2. image๋ง๋ค (RGB์ ๋ณด, Depth์ ๋ณด) ๋๊ธฐํ ๋ฐ ํ
์คํธ ์ด๋ฏธ์ง ์ถ๋ ฅ
๐ข ์์์ ๋ณด์ ๊น์ด์ ๋ณด๋ ์ด๋ฏธ์ง์ ๋๊ธฐํ๋์ด์ ์ ์ฅ๋๋๊ฒ์ด ์๋๋ผ, ๋ถ๋ฆฌ๋์ด ์ ์ฅ๋์ด์๋ค. Timestamp ๊ธฐ์ค์ผ๋ก ์ด๋ฏธ์ง์ (์์,๊น์ด) ์ ๋ณด๋ฅผ ๋๊ธฐํ ์์ผ์ฃผ์.

% Load time stamp data of color images
timeColor = helperImportTimestampFile([imageFolder, 'rgb.txt']);
% Load time stamp data of depth images
timeDepth = helperImportTimestampFile([imageFolder, 'depth.txt']);
% Align the time stamp
indexPairs = helperAlignTimestamp(timeColor, timeDepth);
% Select the synchronized image data
imdsColor = subset(imdsColor, indexPairs(:, 1));
imdsDepth = subset(imdsDepth, indexPairs(:, 2));
% Inspect the first RGB-D image
currFrameIdx = 1;
currIcolor = readimage(imdsColor, currFrameIdx);
currIdepth = readimage(imdsDepth, currFrameIdx);
imshowpair(currIcolor, currIdepth, "montage");
3.Map initialize
๐ขMap์์ SLAM์ ํ๊ธฐ์ํ ์ด๊ธฐ ์ค์ ์์น์ด๋ค. ํน์ง์ ์ถ์ถ๊ณผ ๊ด๋ จ๋ ํ๋ผ๋ฏธํฐ๋ค(scaleFactor, numLevels, numpoints๋ฑ)์ ์ค์ ํ๋ค.
% Set random seed for reproducibility
rng(0);
% Create a cameraIntrinsics object to store the camera intrinsic parameters.
% The intrinsics for the dataset can be found at the following page:
% https://vision.in.tum.de/data/datasets/rgbd-dataset/file_formats
focalLength = [535.4, 539.2]; % in units of pixels
principalPoint = [320.1, 247.6]; % in units of pixels
imageSize = size(currIcolor,[1,2]); % in pixels [mrows, ncols]
depthFactor = 5e3;
intrinsics = cameraIntrinsics(focalLength,principalPoint,imageSize);
% Detect and extract ORB features from the color image
scaleFactor = 1.2;
numLevels = 8;
numPoints = 1000;
[currFeatures, currPoints] = helperDetectAndExtractFeatures(currIcolor, scaleFactor, numLevels, numPoints);
initialPose = rigidtform3d();
[xyzPoints, validIndex] = helperReconstructFromRGBD(currPoints, currIdepth, intrinsics, initialPose, depthFactor);
4.Loop closure ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์ถ
๐ข๊ฐ์ ์์น์ ๋๋์์์๋, ์๋ก์ด๊ณณ์ด๋ผ๊ณ ์ธ์งํ์ง ์์์ผ SLAM์ด ์ ํํ๋ค. ์ด๋ bof๋ผ๋ ์๊ฐ์ ์ดํ ๊ธฐ๋ฒ(๋น์ : ์ด๊ณณ์ ๋๋ฌด,์์์ ๋ฑ ํน์ง์ด์๋ ํน์ง์ ๋ค์ ๊ธฐ์ตํ๊ณ ๋ฉ๋ชจํด๋์!)์ ์ด์ฉํ๋ค.
์ด bof์ ๋ณด๋ค์ ์ ์ฅํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ตฌ์ถํ๋ ๊ณผ์ ์ด๋ค.
% Load the bag of features data created offline
bofData = load("bagOfFeaturesDataSLAM.mat");
% Initialize the place recognition database
loopDatabase = invertedImageIndex(bofData.bof, SaveFeatureLocations=false);
% Add features of the first key frame to the database
currKeyFrameId = 1;
addImageFeatures(loopDatabase, currFeatures, currKeyFrameId);
5.3D Map point ๋ฐ RGB์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ ๋๋ ํ ๋ฆฌ ๊ตฌ์ถ ๋ฐ SLAMํ
์คํธ
๐ข3D Map point, keyFrame์ ์ ์ฅํ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ค๋ค.
Keyframe์์ 3D Map point๋ฅผ ์ถ์ถํ์ฌ ์ ์ฅํ๋ค.(Keyframe๋ ์ ์ฅ)
ํน์ 3D Map point ์ ๊ตฐ๋ง๋ค ๊น์ด์ ๋ณด ๋ฐ ์นด๋ฉ๋ผ๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ฐฉํฅ๋ ์ ์ฅํ๋ค.
ํ
์คํธ์ฉ์ผ๋ก ์ฒซ ํคํ๋ ์์ 3D SLAM ๊ฒฐ๊ณผ๋ฅผ ์๊ฐํํด๋ณธ๋ค.
3D Mapoint : SLAM๊ฒฐ๊ณผ๋ก์ ๋ณด์ด๋ ์ ๋ค์ ์งํฉ
Key Frame : ํน์ง์ ์ด ๊ฐ์ฅ ๋ง์ด ๋ณด์ด๋ RGB์ด๋ฏธ์ง. SLAM์์ ์ฐธ๊ณ ํ๋ ์ด๋ฏธ์ง๋ค

% Create an empty imageviewset object to store key frames
vSetKeyFrames = imageviewset;
% Create an empty worldpointset object to store 3-D map points
mapPointSet = worldpointset;
% Add the first key frame
vSetKeyFrames = addView(vSetKeyFrames, currKeyFrameId, initialPose, Points=currPoints,...
Features=currFeatures.Features);
% Add 3-D map points
[mapPointSet, rgbdMapPointsIdx] = addWorldPoints(mapPointSet, xyzPoints);
% Add observations of the map points
mapPointSet = addCorrespondences(mapPointSet, currKeyFrameId, rgbdMapPointsIdx, validIndex);
% Update view direction and depth
mapPointSet = updateLimitsAndDirection(mapPointSet, rgbdMapPointsIdx, vSetKeyFrames.Views);
% Update representative view
mapPointSet = updateRepresentativeView(mapPointSet, rgbdMapPointsIdx, vSetKeyFrames.Views);
% Visualize matched features in the first key frame
featurePlot = helperVisualizeMatchedFeaturesRGBD(currIcolor, currIdepth, currPoints(validIndex));
% Visualize initial map points and camera trajectory
xLim = [-4 4];
yLim = [-3 1];
zLim = [-1 6];
mapPlot = helperVisualizeMotionAndStructure(vSetKeyFrames, mapPointSet, xLim, yLim, zLim);
% Show legend
showLegend(mapPlot);
6.Tracking
๐ข์นด๋ฉ๋ผ๊ฐ ์์ง์ด๋ฉด์ ์๋ก์ด ์ฌ์ง์ด ์ฐํ๋ฉด, ์ด์ ํคํ๋ ์์ ์๋ ํน์ง์ ๋ค๊ณผ ๋น๊ตํ๋ฉฐ, ์นด๋ฉ๋ผ์ ์๋์์น์ ํฌ์ฆ(orientation)์ ์ถ์ ํ๋ค.
๋ํ, ์ถ๊ฐ์ ์ผ๋ก ํน์ง์ ๋ค์ ํ์
ํ์ฌ ๊ฒฐ๊ณผ๊ฐ์ ๋ฏธ์ธ์กฐ์ ๋ ํ๋ค.
Kanade-Lucas-Tomasi(vision.PointTracker) : ํน์ง์ ์ ๋ณํ๋ก ์นด๋ฉ๋ผ ์ด๋ ์ถ์ ํ๋ ์๊ณ ๋ฆฌ์ฆ
PnP : ์๋ก ๋์๋๋ (2D ์ขํ - 3D์ขํ) ์์ ์ด์ฉํ์ฌ ์นด๋ฉ๋ผ์ ์์น์ ๋ฐฉํฅ(ํฌ์ฆ) ์ถ์
BundleAdjustmentMotion: SLAM๊ฒฐ๊ณผ๊ฐ ๋ณด์
% ViewId of the last key frame
lastKeyFrameId = currKeyFrameId;
% Index of the last key frame in the input image sequence
lastKeyFrameIdx = currFrameIdx;
% Indices of all the key frames in the input image sequence
addedFramesIdx = lastKeyFrameIdx;
currFrameIdx = 2;
isLoopClosed = false;
% Main loop
isLastFrameKeyFrame = true;
% Create and initialize the KLT tracker
tracker = vision.PointTracker(MaxBidirectionalError = 5);
initialize(tracker, currPoints.Location(validIndex, :), currIcolor);
while currFrameIdx < numel(imdsColor.Files)
currIcolor = readimage(imdsColor, currFrameIdx);
currIdepth = readimage(imdsDepth, currFrameIdx);
[currFeatures, currPoints] = helperDetectAndExtractFeatures(currIcolor, scaleFactor, numLevels, numPoints);
% Track the last key frame
% trackedMapPointsIdx: Indices of the map points observed in the current left frame
% trackedFeatureIdx: Indices of the corresponding feature points in the current left frame
[currPose, trackedMapPointsIdx, trackedFeatureIdx] = helperTrackLastKeyFrameKLT(tracker, currIcolor, mapPointSet, ...
vSetKeyFrames.Views, currFeatures, currPoints, lastKeyFrameId, intrinsics);
if isempty(currPose) || numel(trackedMapPointsIdx) < 20
currFrameIdx = currFrameIdx + 1;
continue
end
% Track the local map and check if the current frame is a key frame.
% A frame is a key frame if both of the following conditions are satisfied:
%
% 1. At least 20 frames have passed since the last key frame or the
% current frame tracks fewer than 120 map points.
% 2. The map points tracked by the current frame are fewer than 90% of
% points tracked by the reference key frame.
%
% localKeyFrameIds: ViewId of the connected key frames of the current frame
numSkipFrames = 20;
numPointsKeyFrame = 120;
[localKeyFrameIds, currPose, trackedMapPointsIdx, trackedFeatureIdx, isKeyFrame] = ...
helperTrackLocalMap(mapPointSet, vSetKeyFrames, trackedMapPointsIdx, ...
trackedFeatureIdx, currPose, currFeatures, currPoints, intrinsics, scaleFactor, ...
isLastFrameKeyFrame, lastKeyFrameIdx, currFrameIdx, numSkipFrames, numPointsKeyFrame);
% Visualize matched features
updatePlot(featurePlot, currIcolor, currIdepth, currPoints(trackedFeatureIdx));
if ~isKeyFrame
currFrameIdx = currFrameIdx + 1;
isLastFrameKeyFrame = false;
continue
else
% Match feature points between the stereo images and get the 3-D world positions
[xyzPoints, validIndex] = helperReconstructFromRGBD(currPoints, currIdepth, ...
intrinsics, currPose, depthFactor);
[untrackedFeatureIdx, ia] = setdiff(validIndex, trackedFeatureIdx);
xyzPoints = xyzPoints(ia, :);
isLastFrameKeyFrame = true;
end
% Update current key frame ID
currKeyFrameId = currKeyFrameId + 1;
7.Local Mapping
๐ขMapping(Map point ์์ฑ)์ ์ํํ๋ ๊ธฐ์ค์ ์ค๋ช
. Mapping์ ์๋ก์ด ํคํ๋ ์์ด ์ถ๊ฐ๋ ๋๋ง๋ค ์ํ๋จ. ์๋ก์ด ํค ํ๋ ์์ ์ด์ ์ ํคํ๋ ์์ ํน์ง์ ๊ณผ ํ์ฌ ์ด๋ฏธ์ง์ ํน์ง์ ์ฌ์ด์ ์ฐจ์ด๊ฐ ์ปค์ง๋ฉด ์์ฑ์ด๋จ.
% Add the new key frame
[mapPointSet, vSetKeyFrames] = helperAddNewKeyFrame(mapPointSet, vSetKeyFrames, ...
currPose, currFeatures, currPoints, trackedMapPointsIdx, trackedFeatureIdx, localKeyFrameIds);
% Remove outlier map points that are observed in fewer than 3 key frames
if currKeyFrameId == 2
triangulatedMapPointsIdx = [];
end
[mapPointSet, trackedMapPointsIdx] = ...
helperCullRecentMapPointsRGBD(mapPointSet, trackedMapPointsIdx, triangulatedMapPointsIdx, ...
rgbdMapPointsIdx);
% Add new map points computed from disparity
[mapPointSet, rgbdMapPointsIdx] = addWorldPoints(mapPointSet, xyzPoints);
mapPointSet = addCorrespondences(mapPointSet, currKeyFrameId, rgbdMapPointsIdx, ...
untrackedFeatureIdx);
% Create new map points by triangulation
minNumMatches = 10;
minParallax = 1;
[mapPointSet, vSetKeyFrames, triangulatedMapPointsIdx, rgbdMapPointsIdx] = helperCreateNewMapPointsStereo( ...
mapPointSet, vSetKeyFrames, currKeyFrameId, intrinsics, scaleFactor, minNumMatches, minParallax, ...
untrackedFeatureIdx, rgbdMapPointsIdx);
% Update view direction and depth
mapPointSet = updateLimitsAndDirection(mapPointSet, [triangulatedMapPointsIdx; rgbdMapPointsIdx], ...
vSetKeyFrames.Views);
% Update representative view
mapPointSet = updateRepresentativeView(mapPointSet, [triangulatedMapPointsIdx; rgbdMapPointsIdx], ...
vSetKeyFrames.Views);
% Local bundle adjustment
[mapPointSet, vSetKeyFrames, triangulatedMapPointsIdx, rgbdMapPointsIdx] = ...
helperLocalBundleAdjustmentStereo(mapPointSet, vSetKeyFrames, ...
currKeyFrameId, intrinsics, triangulatedMapPointsIdx, rgbdMapPointsIdx);
% Visualize 3-D world points and camera trajectory
updatePlot(mapPlot, vSetKeyFrames, mapPointSet);
% Set the feature points to be tracked
[~, index2d] = findWorldPointsInView(mapPointSet, currKeyFrameId);
setPoints(tracker, currPoints.Location(index2d, :));
8.Loop Closure
๐ข๋ฐฉ๋ฌธํ๋ ์์น๋ก ๋์์์๋, ์๋ก์ด ์์น๋ก ์ธ์ํ์ง ์๋๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ์ธํด์ SLAM๊ฒฐ๊ณผ๊ฐ ๋ณด์ .
์ธ์ ํ ํ๋ ์๊ฐ ๋น๋งค์นญ๋ ํฌ์ธํธ๋ค๋ ์ฐ๊ฒฐํ์ฌ SLAM ๊ณผ์ ์ ์ด์ฉ.
๋งค์นญ๋ ํฌ์ธํธ : ํคํ๋ ์ ์๋ค๋ก 1๊ฐ์ ํ๋ ์(์ด 3๊ฐ์ ํ๋ ์๊ทธ๋ฃน)์์ ํน์ง์ ์์น๊ฐ ํฌ๊ฒ ๋ณํ์ง ์์๊ฒ๋ค์ ๋งค์นญ๋ ํฌ์ธํธ์ด๋ค.
% Check loop closure after some key frames have been created
if currKeyFrameId > 20
% Minimum number of feature matches of loop edges
loopEdgeNumMatches = 120;
% Detect possible loop closure key frame candidates
[isDetected, validLoopCandidates] = helperCheckLoopClosureT(vSetKeyFrames, currKeyFrameId, ...
loopDatabase, currIcolor, loopEdgeNumMatches);
if isDetected
% Add loop closure connections
maxDistance = 0.1;
[isLoopClosed, mapPointSet, vSetKeyFrames] = helperAddLoopConnectionsStereo(...
mapPointSet, vSetKeyFrames, validLoopCandidates, currKeyFrameId, ...
currFeatures, currPoints, loopEdgeNumMatches, maxDistance);
end
end
% If no loop closure is detected, add current features into the database
if ~isLoopClosed
addImageFeatures(loopDatabase, currFeatures, currKeyFrameId);
end
% Update IDs and indices
lastKeyFrameId = currKeyFrameId;
lastKeyFrameIdx = currFrameIdx;
addedFramesIdx = [addedFramesIdx; currFrameIdx]; %#ok<AGROW>
currFrameIdx = currFrameIdx + 1;
end % End of main loop
9.Optimization of Pose Graph
๐ข์ต์ ๋งค์นญ ํฌ์ธํธ์ (minNumMatches) ๋ณด๋ค ์ ์ ๋งค์นญ์ ๊ฐ์ง ํ๋ ์์ ์ ๊ฑฐํ์ฌ ํ์ ๊ทธ๋ํ(๋
ธ์ด์ฆ์ ๊ฑฐํ๋ ๊ณผ์ )
์ด ํ์๊ทธ๋ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ฌ SLAM๊ฒฐ๊ณผ๋ฅผ ๋ฏธ์ธํ๊ฒ ์กฐ์ .
์ต์ ํ๋ 3D๋งตํฌ์ธํธ์ ์์น๋ฅผ ์
๋ฐ์ดํธํจ.(์ค์ SLAM๊ณผ์ ์์ ๋งจ์ฒ์์๋ ํน์ง์ ๋ง์ด ์ฐํ๋ค๊ฐ OPG๊ณผ์ ์ ํตํด ํต์ฌ ํน์ง์ ๋ง ๋จ๊ฒ๋จ.)
% Optimize the poses
minNumMatches = 50;
vSetKeyFramesOptim = optimizePoses(vSetKeyFrames, minNumMatches, Tolerance=1e-16);
% Update map points after optimizing the poses
mapPointSet = helperUpdateGlobalMap(mapPointSet, vSetKeyFrames, vSetKeyFramesOptim);
updatePlot(mapPlot, vSetKeyFrames, mapPointSet);
% Plot the optimized camera trajectory
optimizedPoses = poses(vSetKeyFramesOptim);
plotOptimizedTrajectory(mapPlot, optimizedPoses)
% Update legend
showLegend(mapPlot);
10. SLAM๊ฒฐ๊ณผ


11. GroundTruth์ SLAM๊ฒฐ๊ณผ ๋น๊ต
๐ขํ๋ ์๋ง๋ค ์นด๋ฉ๋ผ์ ์์น์ ๋ฐฉํฅ ์ ๋ณด๋ฅผ ๋ด์ ์ค์ ์ ๋ณด์, SLAM์ ํตํด ์ถ์ ๋ ์นด๋ฉ๋ผ์ ์์น, ๋ฐฉํฅ์ ๋ณด๋ฅผ ๋น๊ตํ์ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ ํ๋๋ฅผ ํ๊ฐํ๋ค.

% Load ground truth
gTruthData = load("orbslamGroundTruth.mat");
gTruth = gTruthData.gTruth;
% Plot the actual camera trajectory
plotActualTrajectory(mapPlot, gTruth(indexPairs(addedFramesIdx, 1)), optimizedPoses);
% Show legend
showLegend(mapPlot);
12. Dense Reconstruction
๐ข์นด๋ฉ๋ผ ๋๋ ์ผ์๋ก ์บก์ฒํ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ์ฌ ๊ณ ๋ฐ๋ 3D ๋ชจ๋ธ์ ์์ฑํ๋ ๊ณผ์ . ์ด ๊ณผ์ ์ ํ๊ฒฝ์ ์ธ๋ฐํ 3D ๊ตฌ์กฐ๋ฅผ ์ฌ๊ตฌ์ฑํ๋ ๋ฐ ์ค์ ์ ๋๋ฉฐ, ํ๋ฉด์ ๋ชจ๋ ์์ ์ธ๋ถ ์ฌํญ์ ํฌํจ. SLAM์ผ๋ก ์์ฑ๋ ์ ๋ณด๋ค์ ๊ธฐ๋ฐ์ผ๋ก ๊ณต๊ฐ์ ์ฐ๋ฆฌ๊ฐ ๋ณผ ์ ์๋ ํํ๋ก ์ฌ๊ตฌ์ฑํ๋ ๋จ๊ณ

% Create an array of pointCloud objects to store the world points constructed
% from the key frames
ptClouds = repmat(pointCloud(zeros(1, 3)), numel(addedFramesIdx), 1);
% Ignore image points at the boundary
offset = 40;
[X, Y] = meshgrid(offset:2:imageSize(2)-offset, offset:2:imageSize(1)-offset);
for i = 1: numel(addedFramesIdx)
Icolor = readimage(imdsColor, addedFramesIdx(i));
Idepth = readimage(imdsDepth, addedFramesIdx(i));
[xyzPoints, validIndex] = helperReconstructFromRGBD([X(:), Y(:)], ...
Idepth, intrinsics, optimizedPoses.AbsolutePose(i), depthFactor);
colors = zeros(numel(X), 1, 'like', Icolor);
for j = 1:numel(X)
colors(j, 1:3) = Icolor(Y(j), X(j), :);
end
ptClouds(i) = pointCloud(xyzPoints, Color=colors(validIndex, :));
end
% Concatenate the point clouds
pointCloudsAll = pccat(ptClouds);
figure
pcshow(pointCloudsAll,VerticalAxis="y", VerticalAxisDir="down");
xlabel('X')
ylabel('Y')
zlabel('Z')
๐ก๋ง๋ฌด๋ฆฌ
RGB-D (DepthCamera๋ฅผ ํตํ 3D SLAM ๊ณผ์ ์ ์๊ฒ๋์๋ค.)
์์๋ณธ ๊ฒฐ๊ณผ, ์นด๋ฉ๋ผ์ ์ด๋ ๋ฐ ์์ธ์ถ์ ๋จ๊ณ(Tracking)์์ ๋จ์ ํน์ง์ ์ฌ์ด ์ด๋๋ง์ ์ด์ฉํ๋๊ฒ์ด ์๋, IMU๊ฐ ์ฌ๊ธฐ์ ํจ์ ๋๋ฉด ํจ์ฌ ์ ํํ ์์น ๋ฐ ์์ธ์ถ์ ์ด ๋๋ค๋ ์ฌ์ค๋ ์ฐพ์๋ณด๋ ์๊ฒ๋์๋ค.
(โโํ์ฌ ํฌ์คํธ์์๋ IMU๋ฅผ ์ด์ฉํ์ง ์๊ณ ์๋คโโ)
์ถํ์ 3D์นด๋ฉ๋ผ์ IMU๋ฅผ ์ผ์ํจ์ ํ์ฌ SLAMํ๋ ๋ฐฉ์์ Matlab์ ํตํด ํด๋ด์ผ๊ฒ ๋ค.
๋ฐ์ดํฐ์ ์ ์ฐ๋ฆฌ๊ป๋ก ๋ฐ๊ฟ์ฃผ๋ฉด, ๋ด๊ฐ ์ํ๋ ๊ณณ์ SLAM์ ํด๋ณผ์ ์๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.