exponenta event banner

Постройте карту занятия из 3D данных о лидаре Используя ХЛОПОК

Этот пример демонстрирует, как построить 2-ю карту занятия из 3D данных о Лидаре, используя алгоритм одновременной локализации и отображения (SLAM). Эта карта занятости полезна для локализации и планирования маршрута для навигации транспортного средства. Эту карту можно также использовать в качестве предварительно созданной карты для включения информации о датчиках.

В этом примере выполняется инкрементная обработка точечных облаков для оценки траектории транспортного средства с установленным лидарным датчиком. Эти оценки склонны к накоплению дрейфа с течением времени, что снижает точность построенной карты. Чтобы исправить дрейф, используйте обнаружение замыкания контура и оптимизацию графика позы. Наконец, используйте оптимизированные позы для построения карты занятости. Данные облака точек лидара в этом примере были собраны из сцены в среде моделирования, построенной с использованием Unreal Engine ® от Epic Games ®.

В этом примере вы научитесь:

  1. Настройка сценария в среде моделирования с различными сценами, транспортными средствами, конфигурациями датчиков и сбор данных.

  2. Оценка траектории транспортного средства с использованием метода регистрации фазовой корреляции.

  3. Используйте алгоритм SLAM для обнаружения замыкания цикла и оптимизации графика позы.

  4. Используйте предполагаемые позы для построения и визуализации карты заполняемости.

Настройка сценария в среде моделирования

Загрузите предварительно созданную сцену большой парковки. Пример «Выбор ППМ для моделирования нереального двигателя» описывает интерактивный выбор последовательности ППМ из сцены и формирование траектории опорного транспортного средства. Этот подход используется для выбора последовательности ППМ и создания опорной траектории для эго-транспортного средства. Добавить к сцене дополнительные транспортные средства, указав их парковочные места. Визуализируйте опорную траекторию и припаркованные транспортные средства на 2-D виде сцены с высоты птичьего полета.

% Load reference path
data = load('parkingLotReferenceData.mat');

% Set reference trajectory of the ego vehicle
refPosesX = data.refPosesX;
refPosesY = data.refPosesY;
refPosesT = data.refPosesT;

% Set poses of the parked vehicles
parkedPoses = data.parkedPoses([18 21],:);

% Display the reference path and the parked vehicle locations
sceneName = 'LargeParkingLot';
hScene = figure('Name', 'Large Parking Lot', 'NumberTitle', 'off');
helperShowSceneImage(sceneName);
hold on
plot(refPosesX(:,2),refPosesY(:,2),'LineWidth',2,'DisplayName','Reference Path');
scatter(parkedPoses(:,1),parkedPoses(:,2),[],'filled','DisplayName','Parked Vehicles');
xlim([-60 40])
ylim([10 60])
hScene.Position = [100,100,1000,500]; % Resize figure
legend
hold off

Откройте модель и добавьте припаркованные транспортные средства с помощью helperAddParkedVehicle функция.

modelName = 'GenerateLidarDataOfParkingLot';
open_system(modelName)
snapnow

helperAddParkedVehicles(modelName,parkedPoses);

Запись и визуализация данных

Настройте простую модель с транспортным средством хэтчбека, проходящим указанный справочный путь при помощи Моделирования 3D Транспортное средство с Землей После блока. Установите лидар в центре крыши транспортного средства с помощью блока Simulation 3D Lidar. Запишите и визуализируйте данные датчика. Используйте записанные данные для разработки алгоритма локализации.

close(hScene)

if ~ispc
    error("Unreal Engine Simulation is supported only on Microsoft" + char(174) + " Windows" + char(174) + ".");
end

% Run simulation
simOut = sim(modelName);

close_system(modelName,0);

% Extract lidar data
ptCloudArr = helperGetPointClouds(simOut);

% Extract ground truth for the lidar data as an array of rigid3d objects
groundTruthPosesLidar = helperGetLidarGroundTruth(simOut);

Одометрия транспортного средства с использованием алгоритма фазовой корреляции

Чтобы построить карту с использованием собранных облаков точек, оцените относительные положения между последовательными облаками точек. По этим позициям можно оценить траекторию эго-транспортного средства. Этот подход инкрементной оценки траектории называется одометрией.

Для построения 2-D карты заполняемости достаточно 2-D оценок позы. Следовательно, преобразуйте облака точек в 2-D изображения сетки занятости, проецируя точки на плоскость земли. Используйте алгоритм регистрации фазовой корреляции для вычисления 2-D относительного преобразования между двумя изображениями. При последовательном преобразовании каждое облако точек преобразуется обратно в опорную рамку первого облака точек. Этот метод также используется pcregistercorr [1].

Таким образом, для расчета одометрии транспортного средства используются следующие шаги:

  1. Предварительная обработка облака точек.

  2. Создайте изображение сетки занятости облака точек путем определения занятости на основе значений высоты (оси Z) точек.

  3. Зарегистрируйте два изображения сетки занятости, созданных из облаков точек, которые соответствуют одной и той же сцене. Используйте imregcorr функция для регистрации изображений сетки и оценки позы.

  4. Повторите предыдущие шаги для последовательных облаков точек, чтобы оценить относительные положения между ними.

Предварительная обработка облака точек

Этап предварительной обработки включает в себя следующие операции:

  1. Удалите отклонения.

  2. Сегментируйте и удалите эго-транспортное средство.

  3. Закрепите облако точек, чтобы сконцентрироваться на интересующей области.

ptCloud = ptCloudArr(1);

% Remove outliers in the point cloud
ptCloudDenoised = pcdenoise(ptCloud);

% Clip the point cloud. This is done to improve the processing performance
% and also to include only the areas of interest

% Set the limits to select the point cloud
selectLimitX = [-40 40];
selectLimitY = [-40 40];
selectLimitZ = [0 5];

roi = [selectLimitX selectLimitY selectLimitZ];
indices = findPointsInROI(ptCloudDenoised,roi);
ptCloudClipped = select(ptCloudDenoised,indices);

% Segment and remove ego vehicle

% Set location of the sensor and vehicle radius
sensorLocation = [0 0 0];
vehicleRadius = 3.5;

% Find the indices of the points other than the ego vehicle and create a
% point cloud with these points
egoIndices = findNeighborsInRadius(ptCloudClipped,sensorLocation,vehicleRadius);

egoFixed = false(ptCloudClipped.Count,1);
egoFixed(egoIndices) = true;
ptCloudNonEgo = select(ptCloudClipped,~egoFixed);

% Since you use the Z-values to determine the occupancy in a grid image,
% move the point cloud by sensor height so that more points are included to
% calculate occupancy

% Set the sensor height. This information can be obtained from the lidar
% point cloud
sensorHeight = groundTruthPosesLidar(1).Translation(3);

locationPts = ptCloudNonEgo.Location;
locationPts(:,3) = locationPts(:,3) + sensorHeight;
ptCloudProcessed = pointCloud(locationPts);

% Visualize and compare the point cloud before and after preprocessing.
figure('Name', 'Processed Point Clouds', 'NumberTitle','off')
pcViewAxes = pcshowpair(ptCloud, ptCloudProcessed);
title('Point cloud before and after preprocessing')
xlabel(pcViewAxes, 'X (m)')
ylabel(pcViewAxes, 'Y (m)')
zlabel(pcViewAxes, 'Z (m)')

Создать изображение сетки занятости

Выполните следующие действия для создания изображений сетки занятости из облаков точек путем проецирования точек на плоскость земли.

  1. Определение сетки на плоскости X-Y с соответствующим разрешением

  2. Вычислите значение вероятности для каждой точки в облаке точек на основе его Z-значения. Эта вероятность определяется нижним и верхним пределом, при этом все значения Z между пределами масштабируются в диапазоне [0,1]. Любые точки с Z-значениями ниже нижней границы или выше верхней границы сопоставляются со значениями вероятности 0 и 1 соответственно.

  3. Накапливайте вероятности в каждой ячейке сетки, соответствующей точкам, и вычисляйте среднее значение. Использовать pcbin для дискретизации сетки и получения индексов ячеек сетки для обработки.

  4. Визуализируйте изображение сетки занятости, представляющее вид сцены сверху.

% Set the occupancy grid size to 100 m with a resolution of 0.2 m
gridSize = 100;
gridStep = 0.2;

% The occupancy grid is created by scaling the points from 0-5 m in
% height to the probability values of [0 1]
zLimits = [0 5];

% Calculate the number of bins
spatialLimits = [-gridSize/2 gridSize/2; -gridSize/2 gridSize/2; ptCloudProcessed.ZLimits];

gridXBinSize = round(abs(spatialLimits(1,2) - spatialLimits(1,1)) / gridStep);
gridYBinSize = round(abs(spatialLimits(2,2) - spatialLimits(2,1)) / gridStep);

numBins = [gridXBinSize gridYBinSize 1];

% Find bin indices
binIndices = pcbin(ptCloudProcessed,numBins,spatialLimits,'BinOutput',false);

% Preallocate occupancy grid
occupancyGrid = zeros(gridXBinSize,gridYBinSize,'like',ptCloudProcessed.Location);
gridCount = zeros(gridXBinSize,gridYBinSize);

% Scale the Z values of the points to the probability range [0 1]
zValues = rescale(ptCloudProcessed.Location(:,3),'InputMin',zLimits(1),'InputMax',zLimits(2));

for idx = 1:numel(binIndices)
    binIdx = binIndices(idx);
    if ~isnan(binIdx)
        occupancyGrid(binIdx) = occupancyGrid(binIdx) + zValues(idx);
        gridCount(binIdx) = gridCount(binIdx) + 1;
    end
end

gridCount(gridCount == 0) = 1;

occupancyGrid = occupancyGrid ./ gridCount;

% Visualize the created occupancy grid
figure
subplot(1,2,1)
pcshow(ptCloudProcessed)
view(2)
title('Point Cloud Birds Eye View')
subplot(1,2,2)
imshow(imrotate(occupancyGrid,90))
tHandle = title('Occupancy Grid Image');
tHandle.Color = [1 1 1];

Проецирование точек на плоскость земли хорошо работает, если плоскость земли плоская. Данные, собранные в этом примере, имеют относительно плоскую плоскость заземления. Если плоскость заземления не плоская, преобразуйте ее так, чтобы она была параллельна плоскости X-Y. Дополнительные сведения см. в примере «Регистрация Lidar Moving Point Cloud to Fixed Point Cloud» и в разделе «Советы» pcregistercorr.

Регистрация и оценка позиций

Используйте poseGraph(Панель инструментов навигации) для хранения расчетных поз.

% Pose graph for registration poses
regEstPoseGraph = poseGraph;
relTrans = groundTruthPosesLidar(1).T(4,1:2);
relYaw = -atan2(groundTruthPosesLidar(1).T(2,1),groundTruthPosesLidar(1).T(1,1));

Используйте imregcorr для оценки относительного преобразования. Добавьте позы, полученные на графике позы с помощью addRelativePose(Панель инструментов навигации). Визуализируйте позы, которые хранятся в графе позы по мере продвижения алгоритма.

% Get ground truth pose graph
gTPoseGraph = helperGetGTPoseGraph(groundTruthPosesLidar);

% Obtain the nodes from the pose graphs
gTNodes = gTPoseGraph.nodes();

% Plot the ground truth trajectory
figure('Name', 'Vehicle Trajectory', 'NumberTitle',"off")
compareTrajAxes = axes;
compareTrajAxes.XLim = [-10 60];
compareTrajAxes.YLim = [-20 5];
compareTrajAxes.XGrid = 'on';
compareTrajAxes.YGrid = 'on';
title('Compare Trajectories')

hold on
plot(gTNodes(:,1),gTNodes(:,2),'Color','blue','LineWidth',3,'DisplayName','Ground Truth Trajectory')

% Visualize the estimated trajectory and the location of the vehicle when
% running the algorithm
estimatedTraj = plot(gTNodes(1,1),gTNodes(1,2),'Color','green','LineWidth',3 ,'DisplayName','Estimated Trajectory');
currentLoc = scatter(gTNodes(1,1),gTNodes(1,2),50,'red','filled','DisplayName','Vehicle Location');

legend;

% Process every fourth frame. This is done to speed up the processing
% without affecting much accuracy.
skipFrames = 3;

numFrames = numel(groundTruthPosesLidar);

occGridFixed = occupancyGrid';

for frameIdx = 1+skipFrames:skipFrames:numFrames
    
    ptCloud = ptCloudArr(frameIdx);
    
    ptCloudProcessed = helperPreprocessPtCloud(ptCloud);
    
    occGridMoving = helperCreateOccupancyGrid(ptCloudProcessed,gridSize,gridStep,zLimits);
    
    % Registration
    
    % Because imregcorr reports the transformation from the origin, you must
    % provide a reference such that the sensor is at the center of each image
    Rgrid = imref2d(size(occGridMoving));
    offsetX = mean(Rgrid.XWorldLimits);
    Rgrid.XWorldLimits = Rgrid.XWorldLimits - offsetX;
    offsetY = mean(Rgrid.YWorldLimits);
    Rgrid.YWorldLimits = Rgrid.YWorldLimits - offsetY;    
    
    % Find relative pose in image coordinates
    [relPoseImage, peak] = imregcorr(occGridMoving,Rgrid,occGridFixed,Rgrid);
    
    % Convert translation to grid coordinates
    transInGrid = relPoseImage.T(3,1:2) .* gridStep;
    
    % The tranformation is a rigid transformation. Since relative pose is
    % an affine2d object, convert to rigid2d object
    rotations = relPoseImage.T(1:2,1:2);
    [u,~,v] = svd(rotations);
    relPose = rigid2d(u*v',transInGrid);
    
    % Add pose to registration estimate pose graph
    relTrans = relPose.Translation(1:2);
    relYaw = -atan2(relPose.T(2,1),relPose.T(1,1));     
    regEstPoseGraph.addRelativePose([relTrans relYaw]);  
    
    occGridFixed = occGridMoving;
    
    % Update the estimated trajectory and vehicle location
    estimatedNode = regEstPoseGraph.nodes(regEstPoseGraph.NumNodes);
    estimatedTraj.XData(end + 1) = estimatedNode(1);
    estimatedTraj.YData(end + 1) = estimatedNode(2);
    set(currentLoc,'XData',estimatedNode(1),'YData',estimatedNode(2));
    drawnow
end
hold off

Заметим, что есть дрейф, который накопился с течением времени, что заставляет траекторию отклоняться от истинного положения земли и заканчиваться в другой конечной точке. Этот дрейф происходит из-за ошибок, накопленных в оценках позы.

Обнаружение петель и исправление дрейфа

Метод Graph SLAM может быть использован для коррекции накопленного дрейфа и получения точной карты. Используйте lidarSLAMОбъект (панель инструментов навигации) и его методы, использующие оптимизацию графика позы для обнаружения контуров и коррекции дрейфа в траектории. При добавлении нового сканирования в lidarSLAM(Панель инструментов навигации), выполняются следующие шаги.

  1. Оценка позы: объект использует imregcorr для регистрации.

  2. Построить график позы: объект хранит позы в poseGraph(Панель инструментов навигации).

  3. Обнаружение замыкания контура: Контуры - это места, которые были ранее посещены. lidarSLAM(Панель инструментов навигации) использует функции регистрации сканирования, matchScansGrid(Панель инструментов навигации) и matchScans(Панель инструментов навигации) для обнаружения контуров. Этот процесс называется обнаружением замыкания контура. Объект выполняет обнаружение замыкания контура путем сопоставления текущего сканирования с предыдущим сканированием в пределах небольшого радиуса вокруг текущего местоположения робота. Обнаруженные циклы добавляются к графу позы.

  4. Оптимизация графика позы: На основе обнаруженных циклов объект оптимизирует график позы для поиска нового набора поз транспортного средства с уменьшенным дрейфом.

  5. Постройте карту.

Установите lidarSLAM эмпирически основанные на данных свойства.

% Set lidarSLAM properties
mapResolution = 2;
maxLidarRange = 50;
loopClosureThreshold = 2450;
loopClosureRadius = 3;

% Initialize lidarSLAM object
slamAlg = lidarSLAM(mapResolution,maxLidarRange);
slamAlg.ScanRegistrationMethod = 'PhaseCorrelation';
slamAlg.LoopClosureThreshold = loopClosureThreshold;
slamAlg.LoopClosureSearchRadius = loopClosureRadius;

Используйте addScan(Панель инструментов навигации) объектная функция lidarSLAM Объект (Navigation Toolbox) для инкрементного предоставления данных для регистрации и обнаружения замыкания цикла. Потому что addScanФункция (Панель инструментов навигации) принимает только lidarScan(Панель инструментов навигации), необходимо преобразовать каждое изображение сетки в lidarScan(Панель инструментов навигации).

occGridFixed = occupancyGrid';

% Create a lidarScan object from an occupancy grid image
[rows,cols,values] = find(occGridFixed);
xLocs = -gridSize/2 + rows * gridStep;
yLocs = -gridSize/2 + cols * gridStep;

scan = lidarScan([yLocs xLocs]);

% Visualize the grid image and lidar scan
figure('Name','Occupancy image and lidar scan','NumberTitle','off')
subplot(1,2,1)
imshow(imrotate(occupancyGrid,-180))
title('Occupancy Grid Image')
subplot(1,2,2)
plot(scan)

Пошаговое добавление сканирований лидара в lidarSLAM(Панель инструментов навигации).

% Plot the ground truth trajectory
figure('Name','Trajectory traversal','NumberTitle','off') 
slamAlgAxes = axes;
plot(gTNodes(:,1),gTNodes(:,2),'Color','blue','LineWidth',3,'DisplayName','Ground Truth Trajectory')
hold on 
slamAlgAxes.XLim = [-10 60];
slamAlgAxes.YLim = [-20 5];
slamAlgAxes.XGrid = 'on';
slamAlgAxes.YGrid = 'on';
title('Trajectory Traversal and Loop Closure Detections')
legend

% Plot the current location of the vehicle as the algorithm progresses.
currentLoc = scatter(gTNodes(1,1),gTNodes(1,2),50,'red','filled','DisplayName','Vehicle Location');

% Also plot any location where a loop closure was detected.
loopLocation = scatter([],[],50,'black','filled','DisplayName','Loop Detected');

for frameIdx = 1:skipFrames:numFrames
    
    ptCloud = ptCloudArr(frameIdx);
    ptCloudProcessed = helperPreprocessPtCloud(ptCloud);
    occGridMoving = helperCreateOccupancyGrid(ptCloudProcessed,gridSize,gridStep,zLimits);
    
    [rows,cols,values] = find(occGridMoving);
    xLocs = -gridSize/2 + rows * gridStep;
    yLocs = -gridSize/2 + cols * gridStep;
    
    scan = lidarScan([yLocs xLocs]);  
    
    % Add lidar scan to algorithm
    [isScanAccepted,loopClosureInfo] = addScan(slamAlg,scan); 
    
    % Update the loop closure detected location
    if isScanAccepted && size(loopClosureInfo.EdgeIDs,1) > 0
        loopLocation.XData(end+1) = gTNodes(frameIdx,1); 
        loopLocation.YData(end+1) = gTNodes(frameIdx,2);
    end
    
    % Update the vehicle location
    set(currentLoc,'XData',gTNodes(frameIdx,1),'YData',gTNodes(frameIdx,2));
    drawnow
end
hold off

Визуализация траектории, полученной с помощью оптимизации графика позы. Следует отметить, что дрейф был уменьшен. Дополнительные сведения о вычислении ошибок для оценок позы см. в примере Lidar Localization with Unreal Engine Simulation.

% Obtain the poses from the pose graph
slamAlgPoses = slamAlg.PoseGraph.nodes();
estNodes = regEstPoseGraph.nodes();

% Plot the trajectories from the nodes
figure('Name','Vehicle Trajectory','NumberTitle',"off"); 
slamTrajAxes = axes;
slamTrajAxes.XLim = [-10 60];
slamTrajAxes.YLim = [-20 5];
slamTrajAxes.XGrid = 'on';
slamTrajAxes.YGrid = 'on';
title('Compare Trajectories')
legend

hold on; 

plot(gTNodes(:,1),gTNodes(:,2),'Color','blue','LineWidth',3,'DisplayName','Ground Truth Trajectory'); 
plot(estNodes(:,1),estNodes(:,2),'Color','green','LineWidth',3 ,'DisplayName','Estimated Trajectory');
plot(slamAlgPoses(:,1),slamAlgPoses(:,2),'Color','magenta','LineWidth',3 ,'DisplayName','Optimized Trajectory');

hold off;

Просмотр и визуализация обнаруженных узлов замыкания контура, добавленных в slamAlg объект.

slamAlg.PoseGraph.LoopClosureEdgeIDs
ans = 1×4

   197   199   201   203

figure('Name','Loop Closure Results','NumberTitle',"off")
pGraphAxes = slamAlg.PoseGraph.show('IDs','off');
pGraphAxes.XLim = [0 10];
pGraphAxes.YLim = [-5 3];
title('Loop Closure Edges')

Построение и визуализация карты

Используйте buildMap(Панель инструментов навигации) для построения карты сетки занятости. Получение необходимых сканирований и поз с помощью scansAndPoses(Панель инструментов навигации) объектная функция lidarSLAM(Панель инструментов навигации).

[scans,optimizedPoses]  = scansAndPoses(slamAlg);

map = buildMap(scans,optimizedPoses,1,50);
figure('Name','Occupancy Map','NumberTitle',"off") 
show(map);
hold on
show(slamAlg.PoseGraph,'IDs','off');
xlim([-20 100])
ylim([-50 50])
hold off;

Сшейте облака точек, используя информацию из графика позы, чтобы создать карту облака точек.

sensorHeight = groundTruthPosesLidar(1).Translation(3);
ptCloudAccum = ptCloud.empty;

% Configure display
xlimits = [ -30  100];
ylimits = [-50  60];
zlimits = [-3  30];
player = pcplayer(xlimits,ylimits,zlimits);

% Define rigid transformation between the lidar sensor mounting position
% and the vehicle reference point. 
lidarToVehicleTform = helperPoseToRigidTransform(single([0 0 -1.57 0 0 0]));

% Specify vehicle dimensions
centerToFront = 1.104;
centerToRear  = 1.343;
frontOverhang = 0.828;
rearOverhang  = 0.589;
vehicleWidth  = 1.653;
vehicleHeight = 1.513;
vehicleLength = centerToFront + centerToRear + frontOverhang + rearOverhang;
hatchbackDims = vehicleDimensions(vehicleLength,vehicleWidth,vehicleHeight,...
'FrontOverhang',frontOverhang,'RearOverhang',rearOverhang);

vehicleDims   = [hatchbackDims.Length,hatchbackDims.Width,hatchbackDims.Height];
vehicleColor  = [0.85 0.325 0.098];

% Estimation trajectory handle
markerSize = 3;
hold(player.Axes,'on')
estTrajHandle   = scatter3(player.Axes,NaN,NaN,NaN,markerSize,vehicleColor,'filled');
hold(player.Axes,'off')

for frameIdx = 1:skipFrames:numFrames
    
    % Obtain the point clouds
    ptCloud = ptCloudArr(frameIdx);
    ptCloudProcessed = helperPreprocessPtCloud(ptCloud);
    
    % Obtain the pose
    poseIdx = floor((frameIdx-1)/skipFrames) + 1;
    pose = optimizedPoses(poseIdx,:);
    
    % Construct the rigid3d object required to concatenate point clouds.
    % This object holds the rigid transformation
    yaw = pose(3);
    rot = [cos(yaw) sin(yaw) 0;...
       -sin(yaw) cos(yaw) 0;...
       0 0 1];
    trans = [pose(1:2) sensorHeight];
    
    absPose = rigid3d(rot,trans);
    
    % Accumulated point cloud map
    ptCloudAccum = pccat([ptCloudAccum,pctransform(ptCloudProcessed,absPose)]);
    
    % Update accumulated point cloud map
    view(player,ptCloudAccum);
    
    % Set viewing angle to top view
    view(player.Axes,2);
    
    % Convert current absolute pose of sensor to vehicle frame 
    absVehiclePose = rigid3d( lidarToVehicleTform.T * absPose.T );
    
    % Draw vehicle at current absolute pose
    helperDrawVehicle(player.Axes,absVehiclePose,vehicleDims,'Color',vehicleColor);
    
    estTrajHandle.XData(end+1) = trans(1);
    estTrajHandle.YData(end+1) = trans(2);
    estTrajHandle.ZData(end+1) = trans(3);
    
end

hold(player.Axes,'off')

Визуализируйте сцену с припаркованными транспортными средствами и сравните ее с построенной картой.

% Display the scene and the parked vehicle locations
figure;
sceneAxes = axes;
sceneName = 'LargeParkingLot';
[sceneImage, sceneRef] = helperGetSceneImage(sceneName);
helperShowSceneImage(imrotate(sceneImage, 180), sceneRef);
hold on
parkedPoses = [-17.255 -44.789 90; -0.7848, -44.6161, 90];
scatter(parkedPoses(:,1), parkedPoses(:,2), [], [0.8500 0.3250 0.0980], 'filled', 'DisplayName', 'Parked Vehicles');
ylim([-90 20])
sceneAxes.XTick = [];
sceneAxes.YTick = [];
sceneAxes.XLabel = [];
sceneAxes.YLabel = [];
legend
hold off;

Ссылки

[1] Димитриевский, Мартин, Дэвид Ван Хамм, Питер Веелаерт и Вильфрид Филипс. «Надежное сопоставление карт занятости для одометрии в автономных транспортных средствах». В трудах 11-й Совместной конференции по компьютерному зрению, визуализации и теории и приложениям компьютерной графики, 626-33. Рим, Италия: SCITEPRESS - Научно-технические публикации, 2016.

Вспомогательные функции

helPerTransform преобразует позу в жесткое преобразование.

helPerSunImagedImage отображает изображение сцены.

helPerAddParkedVehicles добавляет припаркованные транспортные средства к месту стоянки.

helperPreprocessPtCloud выполняет предварительную обработку облака точек.

helperCreateOccupancyGrid создает изображение сетки занятости из облака точек.

helperDrawVehicle рисует транспортное средство на карте.

helperGetImagedImage извлекает изображение сцены и пространственную ссылку.

helperGetStartClouds извлекает массив объектов startCloud, содержащих данные датчика лидара.

function ptCloudArr = helperGetPointClouds(simOut)

% Extract signal
ptCloudData = simOut.ptCloudData.signals.values;

% Create a pointCloud array
ptCloudArr = pointCloud(ptCloudData(:,:,:,2)); % Ignore first frame
for n = 3 : size(ptCloudData,4)
    ptCloudArr(end+1) = pointCloud(ptCloudData(:,:,:,n));  %#ok<AGROW>
end

end

helperGetLidarGroundTruth извлекает местоположение и ориентацию истинного основания.

function gTruth = helperGetLidarGroundTruth(simOut)
numFrames = size(simOut.lidarLocation.time,1);

gTruth = repmat(rigid3d,numFrames-1,1);

for i = 2:numFrames
    gTruth(i-1).Translation = squeeze(simOut.lidarLocation.signals.values(:,:,i));
    % Ignore the roll and the pitch rotations since the ground is flat
    yaw = double(simOut.lidarOrientation.signals.values(:,3,i));
    gTruth(i-1).Rotation = [cos(yaw),sin(yaw), 0; ...
        -sin(yaw),cos(yaw),0; ...
        0,         0,    1];
end
end

helperGetGTERoseGraph получает график позы из достоверной информации о основе лидара.

function gTPoseGraph = helperGetGTPoseGraph(gTruthLidar)

gTPoseGraph = poseGraph;
gTPoseGraph.addRelativePose([0 0 0]);

for idx = 3:numel(gTruthLidar)
    relTform = gTruthLidar(idx).T * inv(gTruthLidar(idx-1).T);
    
    relTrans = relTform(4,1:2);
    relYaw = -atan2(relTform(2,1),relTform(1,1));     
    
    gTPoseGraph.addRelativePose([relTrans relYaw]);
    
end

end

См. также

Функции

Объекты

Связанные темы

Внешние веб-сайты