В этом примере показано, как обнаруживать, классифицировать и отслеживать транспортные средства с помощью данных облака точек лидара, захваченных датчиком лидара, установленным на эго-транспортном средстве. Лидарные данные, используемые в этом примере, записываются из сценария вождения шоссе. В этом примере данные облака точек сегментируются для определения класса объектов с помощью PointSeg сеть. Для отслеживания обнаруженных транспортных средств используется трекер совместной вероятностной ассоциации данных (JPDA) с интерактивным множественным модельным фильтром.
Модуль восприятия играет важную роль в достижении полной автономности транспортных средств с системой ADAS. Лидар и камера являются важными датчиками в процессе восприятия. Лидар хорошо извлекает точную информацию о глубине объектов, в то время как камера создает богатую и подробную информацию об окружающей среде, которая полезна для классификации объектов.
Этот пример включает в себя главным образом следующие части:
Сегментация наземной плоскости
Семантическая сегментация
Ориентированный фитинг ограничивающей рамки
Отслеживание ориентированных ограничивающих рамок
На блок-схеме представлен обзор всей системы.

Лидарный датчик генерирует данные облака точек либо в организованном формате, либо в неорганизованном формате. Данные, используемые в этом примере, собираются с помощью датчика Ouster OS1 lidar. Этот лидар создает организованное облако точек с 64 горизонтальными линиями сканирования. Данные облака точек состоят из трех каналов, представляющих координаты точек x, y и z. Каждый канал имеет размер 64 на 1024. Использовать функцию помощника helperDownloadData для загрузки данных и их загрузки в рабочую область MATLAB ®.
Примечание: Загрузка может занять несколько минут.
[ptClouds,pretrainedModel] = helperDownloadData;
В этом примере используется гибридный подход, который использует segmentGroundFromLidarData (инструментарий компьютерного зрения) и pcfitplane(Панель инструментов компьютерного зрения). Сначала оцените параметры нулевой плоскости с помощью segmentGroundFromLidarData функция. Расчетная плоскость земли разделена на полосы вдоль направления транспортного средства, чтобы подогнать плоскость, используя pcfitplane функция на каждой полосе. Этот гибридный подход надежно подходит к плоскости земли кусочно и обрабатывает изменения в облаке точек.
% Load point cloud ptCloud = ptClouds{1}; % Define ROI for cropping point cloud xLimit = [-30,30]; yLimit = [-12,12]; zLimit = [-3,15]; roi = [xLimit,yLimit,zLimit]; % Extract ground plane [nonGround,ground] = helperExtractGround(ptCloud,roi); figure; pcshowpair(nonGround,ground); legend({'\color{white} Nonground','\color{white} Ground'},'Location','northeastoutside');

В этом примере используется предварительно обученный PointSeg сетевая модель. PointSeg - сквозная сеть семантической сегментации в реальном времени, обученная для таких классов объектов, как легковые автомобили, грузовики и фон. Вывод из сети представляет собой маскированное изображение с каждым пикселем, помеченным в соответствии с классом. Эта маска используется для фильтрации различных типов объектов в облаке точек. Вход в сеть представляет собой пятиканальное изображение, то есть x, y, z, интенсивность и диапазон. Дополнительные сведения о сети или о том, как обучить сеть, см. в примере семантической сегментации облака точек Lidar с использованием Deep Learning Network (Lidar Toolbox).
helperPrepareData функция генерирует пятиканальные данные из загруженных данных облака точек.
% Load and visualize a sample frame frame = helperPrepareData(ptCloud); figure; subplot(5,1,1); imagesc(frame(:,:,1)); title('X channel'); subplot(5,1,2); imagesc(frame(:,:,2)); title('Y channel'); subplot(5,1,3); imagesc(frame(:,:,3)); title('Z channel'); subplot(5,1,4); imagesc(frame(:,:,4)); title('Intensity channel'); subplot(5,1,5); imagesc(frame(:,:,5)); title('Range channel');

Выполните вывод вперед на одном кадре из загруженной предварительно обученной сети.
if ~exist('net','var') net = pretrainedModel.net; end % Define classes classes = ["background","car","truck"]; % Define color map lidarColorMap = [ 0.98 0.98 0.00 % unknown 0.01 0.98 0.01 % green color for car 0.01 0.01 0.98 % blue color for motorcycle ]; % Run forward pass pxdsResults = semanticseg(frame,net); % Overlay intensity image with segmented output segmentedImage = labeloverlay(uint8(frame(:,:,4)),pxdsResults,'Colormap',lidarColorMap,'Transparency',0.5); % Display results figure; imshow(segmentedImage); helperPixelLabelColorbar(lidarColorMap,classes);

Созданная семантическая маска используется для фильтрации облаков точек, содержащих грузовики. Аналогично, фильтровать облака точек для других классов.
truckIndices = pxdsResults == 'truck'; truckPointCloud = select(nonGround,truckIndices,'OutputSize','full'); % Crop point cloud for better display croppedPtCloud = select(ptCloud,findPointsInROI(ptCloud,roi)); croppedTruckPtCloud = select(truckPointCloud,findPointsInROI(truckPointCloud,roi)); % Display ground and nonground points figure; pcshowpair(croppedPtCloud,croppedTruckPtCloud); legend({'\color{white} Nonvehicle','\color{white} Vehicle'},'Location','northeastoutside');

После извлечения облаков точек различных классов объектов объекты группируются с помощью евклидовой кластеризации с использованием pcsegdist (Панель инструментов компьютерного зрения). Чтобы сгруппировать все точки, принадлежащие одному кластеру, облако точек, полученное как кластер, используется в качестве начальных точек для выращивания области в некруглых точках. Используйте findNearestNeighbors(Панель инструментов компьютерного зрения) функция, чтобы закольцевать все точки для роста региона. Извлеченный кластер устанавливается в L-образную ограничивающую рамку с использованием pcfitcuboid(Панель инструментов Lidar). Эти скопления транспортных средств напоминают форму буквы L, если смотреть сверху вниз. Эта функция помогает оценить ориентацию транспортного средства. Ориентированная аппроксимация ограничительной рамки помогает оценить угол наклона объектов, что полезно в таких приложениях, как планирование путей и управление трафиком.
Кубовидные границы кластеров также можно вычислить, найдя минимальную и максимальную пространственные границы в каждом направлении. Однако этот способ не позволяет оценить ориентацию обнаруженных транспортных средств. Разница между этими двумя методами показана на рисунке.

[labels,numClusters] = pcsegdist(croppedTruckPtCloud,1); % Define cuboid parameters params = zeros(0,9); for clusterIndex = 1:numClusters ptsInCluster = labels == clusterIndex; pc = select(croppedTruckPtCloud,ptsInCluster); location = pc.Location; xl = (max(location(:,1)) - min(location(:,1))); yl = (max(location(:,2)) - min(location(:,2))); zl = (max(location(:,3)) - min(location(:,3))); % Filter small bounding boxes if size(location,1)*size(location,2) > 20 && any(any(pc.Location)) && xl > 1 && yl > 1 indices = zeros(0,1); objectPtCloud = pointCloud(location); for i = 1:size(location,1) seedPoint = location(i,:); indices(end+1) = findNearestNeighbors(nonGround,seedPoint,1); end % Remove overlapping indices indices = unique(indices); % Fit oriented bounding box model = pcfitcuboid(select(nonGround,indices)); params(end+1,:) = model.Parameters; end end % Display point cloud and detected bounding box figure; pcshow(croppedPtCloud.Location,croppedPtCloud.Location(:,3)); showShape('cuboid',params,"Color","red","Label","Truck");

Используйте helperLidarObjectDetectionDisplay для визуализации полного рабочего процесса в одном окне. Макет окна визуализации разделен на следующие разделы:
Изображение диапазона Lidar: изображение облака точек в 2-D как изображение диапазона
Сегментированное изображение: Обнаруженные метки, созданные из семантической сети сегментации, наложенные на изображение интенсивности или четвертый канал данных
Обнаружение ориентированной ограничивающей рамки: 3-D облака точек с ориентированными ограничивающими рамками
Вид сверху: вид сверху облака точек с ориентированными ограничивающими прямоугольниками
display = helperLidarObjectDetectionDisplay;
helperLidarObjectDetection класс является оболочкой, инкапсулирующей все шаги сегментации, кластеризации и подгонки ограничивающей рамки, упомянутые в вышеприведенных разделах. Используйте findDetections для извлечения обнаруженных объектов.
% Initialize lidar object detector lidarDetector = helperLidarObjecDetector('Model',net,'XLimits',xLimit,... 'YLimit',yLimit,'ZLimit',zLimit); % Prepare 5-D lidar data inputData = helperPrepareData(ptClouds); % Set random number generator for reproducible results. S = rng(2018); % Initialize the display initializeDisplay(display); numFrames = numel(inputData); for count = 1:numFrames % Get current data input = inputData{count}; rangeImage = input(:,:,5); % Extact bounding boxes from lidar data [boundingBox,coloredPtCloud,pointLabels] = detectBbox(lidarDetector,input); % Update display with colored point cloud updatePointCloud(display,coloredPtCloud); % Update bounding boxes updateBoundingBox(display,boundingBox); % Update segmented image updateSegmentedImage(display,pointLabels,rangeImage); drawnow('limitrate'); end

В этом примере используется трекер совместной вероятностной связи данных (JPDA). Временной шаг dt устанавливается в 0,1 секунды, так как набор данных захватывается при частоте 10 Гц. Модель пространства состояний, используемая в трекере, основана на кубовидной модели с параметрами, ]. Дополнительные сведения о том, как отслеживать ограничивающие прямоугольники в данных лидара, см. в примере «Отслеживание транспортных средств с использованием лидара: из облака точек в список дорожек». В этом примере информация о классе предоставляется с помощью ObjectAttributes имущества objectDetection object. При создании новых дорожек функция инициализации фильтра, определяется с помощью функции помощника helperMultiClassInitIMMFilter использует класс обнаружения для настройки начальных размеров объекта. Это помогает трекеру отрегулировать модель измерения ограничивающей рамки с соответствующими размерами дорожки.
Настройте объект JPDA-трекера с этими параметрами.
assignmentGate = [10 100]; % Assignment threshold; confThreshold = [7 10]; % Confirmation threshold for history logi delThreshold = [2 3]; % Deletion threshold for history logic Kc = 1e-5; % False-alarm rate per unit volume % IMM filter initialization function filterInitFcn = @helperMultiClassInitIMMFilter; % A joint probabilistic data association tracker with IMM filter tracker = trackerJPDA('FilterInitializationFcn',filterInitFcn,... 'TrackLogic','History',... 'AssignmentThreshold',assignmentGate,... 'ClutterDensity',Kc,... 'ConfirmationThreshold',confThreshold,... 'DeletionThreshold',delThreshold,'InitializationThreshold',0); allTracks = struct([]); time = 0; dt = 0.1; % Define Measurement Noise measNoise = blkdiag(0.25*eye(3),25,eye(3)); numTracks = zeros(numFrames,2);
Обнаруженные объекты собираются как массив ячеек objectDetection (Automated Driving Toolbox) объекты с использованием helperAssembleDetections функция.
display = helperLidarObjectDetectionDisplay; initializeDisplay(display); for count = 1:numFrames time = time + dt; % Get current data input = inputData{count}; rangeImage = input(:,:,5); % Extact bounding boxes from lidar data [boundingBox,coloredPtCloud,pointLabels] = detectBbox(lidarDetector,input); % Assemble bounding boxes into objectDetections detections = helperAssembleDetections(boundingBox,measNoise,time); % Pass detections to tracker if ~isempty(detections) % Update the tracker [confirmedTracks,tentativeTracks,allTracks,info] = tracker(detections,time); numTracks(count,1) = numel(confirmedTracks); end % Update display with colored point cloud updatePointCloud(display,coloredPtCloud); % Update segmented image updateSegmentedImage(display,pointLabels,rangeImage); % Update the display if the tracks are not empty if ~isempty(confirmedTracks) updateTracks(display,confirmedTracks); end drawnow('limitrate'); end

В этом примере показано, как обнаруживать и классифицировать транспортные средства, оснащенные ориентированной ограничивающей рамкой, на основе данных лидара. Также вы научились использовать фильтр IMM для отслеживания объектов с информацией о нескольких классах. Результаты семантической сегментации могут быть дополнительно улучшены путем добавления дополнительных данных обучения.
function multiChannelData = helperPrepareData(input) % Create 5-channel data as x, y, z, intensity and range % of size 64-by-1024-by-5 from pointCloud. if isa(input, 'cell') numFrames = numel(input); multiChannelData = cell(1, numFrames); for i = 1:numFrames inputData = input{i}; x = inputData.Location(:,:,1); y = inputData.Location(:,:,2); z = inputData.Location(:,:,3); intensity = inputData.Intensity; range = sqrt(x.^2 + y.^2 + z.^2); multiChannelData{i} = cat(3, x, y, z, intensity, range); end else x = input.Location(:,:,1); y = input.Location(:,:,2); z = input.Location(:,:,3); intensity = input.Intensity; range = sqrt(x.^2 + y.^2 + z.^2); multiChannelData = cat(3, x, y, z, intensity, range); end end
function helperPixelLabelColorbar(cmap, classNames) % Add a colorbar to the current axis. The colorbar is formatted % to display the class names with the color. colormap(gca,cmap) % Add colorbar to current figure. c = colorbar('peer', gca); % Use class names for tick marks. c.TickLabels = classNames; numClasses = size(cmap,1); % Center tick labels. c.Ticks = 1/(numClasses*2):1/numClasses:1; % Remove tick mark. c.TickLength = 0; end
function [ptCloudNonGround,ptCloudGround] = helperExtractGround(ptCloudIn,roi) % Crop the point cloud idx = findPointsInROI(ptCloudIn,roi); pc = select(ptCloudIn,idx,'OutputSize','full'); % Get the ground plane the indices using piecewise plane fitting [ptCloudGround,idx] = piecewisePlaneFitting(pc,roi); nonGroundIdx = true(size(pc.Location,[1,2])); nonGroundIdx(idx) = false; ptCloudNonGround = select(pc,nonGroundIdx,'OutputSize','full'); end function [groundPlane,idx] = piecewisePlaneFitting(ptCloudIn,roi) groundPtsIdx = ... segmentGroundFromLidarData(ptCloudIn, ... 'ElevationAngleDelta',5,'InitialElevationAngle',15); groundPC = select(ptCloudIn,groundPtsIdx,'OutputSize','full'); % Divide x-axis in 3 regions segmentLength = (roi(2) - roi(1))/3; x1 = [roi(1),roi(1) + segmentLength]; x2 = [x1(2),x1(2) + segmentLength]; x3 = [x2(2),x2(2) + segmentLength]; roi1 = [x1,roi(3:end)]; roi2 = [x2,roi(3:end)]; roi3 = [x3,roi(3:end)]; idxBack = findPointsInROI(groundPC,roi1); idxCenter = findPointsInROI(groundPC,roi2); idxForward = findPointsInROI(groundPC,roi3); % Break the point clouds in front and back ptBack = select(groundPC,idxBack,'OutputSize','full'); ptForward = select(groundPC,idxForward,'OutputSize','full'); [~,inliersForward] = planeFit(ptForward); [~,inliersBack] = planeFit(ptBack); idx = [inliersForward; idxCenter; inliersBack]; groundPlane = select(ptCloudIn, idx,'OutputSize','full'); end function [plane,inlinersIdx] = planeFit(ptCloudIn) [~,inlinersIdx, ~] = pcfitplane(ptCloudIn,1,[0, 0, 1]); plane = select(ptCloudIn,inlinersIdx,'OutputSize','full'); end
function mydetections = helperAssembleDetections(bboxes,measNoise,timestamp) % Assemble bounding boxes as cell array of objectDetection mydetections = cell(size(bboxes,1),1); for i = 1:size(bboxes,1) classid = bboxes(i,end); lidarModel = [bboxes(i,1:3), bboxes(i,end-1), bboxes(i,4:6)]; % To avoid direct confirmation by the tracker, the ClassID is passed as % ObjectAttributes. mydetections{i} = objectDetection(timestamp, ... lidarModel','MeasurementNoise',... measNoise,'ObjectAttributes',struct('ClassID',classid)); end end
function [lidarData, pretrainedModel] = helperDownloadData outputFolder = fullfile(tempdir,'WPI'); url = 'https://ssd.mathworks.com/supportfiles/lidar/data/lidarSegmentationAndTrackingData.tar.gz'; lidarDataTarFile = fullfile(outputFolder,'lidarSegmentationAndTrackingData.tar.gz'); if ~exist(lidarDataTarFile,'file') mkdir(outputFolder); websave(lidarDataTarFile,url); untar(lidarDataTarFile,outputFolder); end % Check if tar.gz file is downloaded, but not uncompressed if ~exist(fullfile(outputFolder,'WPI_LidarData.mat'),'file') untar(lidarDataTarFile,outputFolder); end % Load lidar data data = load(fullfile(outputFolder,'highwayData.mat')); lidarData = data.ptCloudData; % Download pretrained model url = 'https://ssd.mathworks.com/supportfiles/lidar/data/pretrainedPointSegModel.mat'; modelFile = fullfile(outputFolder,'pretrainedPointSegModel.mat'); if ~exist(modelFile,'file') websave(modelFile,url); end pretrainedModel = load(fullfile(outputFolder,'pretrainedPointSegModel.mat')); end
[1] Сяо Чжан, Венда Сюй, Чию Дун и Джон М. Долан, «Эффективный L-образный фитинг для обнаружения транспортных средств с помощью лазерных сканеров», IEEE Intelligent Vehicles Symposium, июнь 2017
[2] Я. Ван, Т. Ши, П. Юн, Л. Тай и М. Лю, «Пуантсег: семантическая сегментация в реальном времени на основе облака точек 3d-лидара», arXiv препринт arXiv:1807.06288, 2018.