В этом примере показано, как обнаруживать полосы движения в облаках опорных точек. Для обнаружения полос движения транспортных средств ego можно использовать значения интенсивности, возвращаемые облаками точек lidar. Можно дополнительно улучшить обнаружение полосы движения с помощью алгоритма подгонки кривой и отслеживания параметров кривой. Обнаружение полосы движения Lidar позволяет создавать сложные рабочие процессы, такие как поддержка по поддержанию полосы движения, предупреждение о выходе из полосы движения и адаптивный круиз-контроль для автономного вождения. Испытываемое транспортное средство собирает данные лидара с помощью датчика лидара, установленного на его крыше.
Обнаружение полосы движения в лидаре включает в себя обнаружение непосредственной левой и правой полос движения, также известных как полосы движения эго-транспортного средства, относительно датчика лидара. Она включает в себя следующие этапы:
Регион извлечения процентов
Сегментация наземной плоскости
Обнаружение пиковой интенсивности
Определение полосы движения с помощью поиска в окне
Параболическая полиномиальная подгонка
Фитинг параллельной полосы движения
Отслеживание полосы движения
На этой блок-схеме представлен обзор рабочего процесса, представленного в этом примере.

Преимущества использования лидарных данных для обнаружения дорожек:
Точечные облака Lidar дают лучшее 3-D представление поверхности дороги, чем данные изображения, таким образом уменьшая требуемые калибровочные параметры для поиска вида с высоты птичьего полета.
Лидар более устойчив к неблагоприятным климатическим условиям, чем обнаружение на основе изображений.
Данные Лидара имеют сантиметровый уровень точности, приводящий к точной локализации полосы движения.
Данные лидара, использованные в этом примере, были собраны с помощью датчика лидара канала Ouster OS1-64, создающего облака точек высокого разрешения. Этот набор данных содержит облака точек, хранящиеся как массив ячеек pointCloud объект. Каждое облако точек 3-D состоит из местоположений XYZ вместе с информацией об интенсивности.
Примечание.Время загрузки данных зависит от вашего подключения к Интернету. MATLAB будет временно не отвечать во время выполнения этого блока кода.
% Download lidar data
lidarData = helperGetDataset;Выбор первого кадра набора данных для дальнейшей обработки.
% Select first frame ptCloud = lidarData{1}; % Visualize input point cloud figure pcshow(ptCloud) title('Input Lidar Point Cloud') axis([0 50 -15 15 -5 5]) view([-42 35])

Чтобы оценить точки полосы движения, сначала выполните предварительную обработку облаков лидарных точек. Предварительная обработка включает в себя следующие шаги:
Регион извлечения процентов
Сегментация наземной плоскости
% Define ROI in meters xlim = [5 55]; ylim = [-3 3]; zlim = [-4 1]; roi = [xlim ylim zlim]; % Crop point cloud using ROI indices = findPointsInROI(ptCloud,roi); croppedPtCloud = select(ptCloud,indices); % Remove ground plane maxDistance = 0.1; referenceVector = [0 0 1]; [model,inliers,outliers] = pcfitplane(croppedPtCloud,maxDistance,referenceVector); groundPts = select(croppedPtCloud,inliers); figure pcshow(groundPts) title('Ground Plane') view(3)

Обнаружение точек полосы движения с помощью поиска скользящего окна, где начальные оценки для скользящих окон выполняются с использованием гистограммы на основе интенсивности.
Обнаружение точки полосы движения состоит главным образом из следующих двух этапов:
Обнаружение пиковой интенсивности
Поиск в окне
Точки полосы движения в облаке точек лидара имеют четкое распределение интенсивностей. Обычно эти интенсивности занимают верхнюю область в распределении гистограммы и проявляются как высокие пики. Вычислите гистограмму интенсивностей от обнаруженной нулевой плоскости вдоль оси эго транспортного средства (положительная ось X). helperComputeHistogram вспомогательная функция создает гистограмму точек интенсивности. Управление количеством ячеек для гистограммы путем указания разрешения ячейки.
histBinResolution = 0.2; [histVal,yvals] = helperComputeHistogram(groundPts,histBinResolution); figure plot(yvals,histVal,'--k') set(gca,'XDir','reverse') hold on
Получение пиков в гистограмме с помощью helperfindpeaks функция помощника. Дальнейшая фильтрация возможных точек полосы на основе ширины полосы с помощью helperInitialWindow функция помощника.
[peaks,locs] = helperfindpeaks(histVal); startYs = yvals(locs); laneWidth = 4; [startLanePoints,detectedPeaks] = helperInitialWindow(startYs,peaks,laneWidth); plot(startYs,peaks,'*r') plot(startLanePoints,detectedPeaks,'og') legend('Histogram','Peak','Detected Peaks','Location','North') title('Peak Detection') hold off

Поиск в окне используется для определения точек полосы движения путем перемещения окон вдоль направления кривизны полосы движения. Поиск в окне состоит из двух шагов:
Инициализация окна
Скользящее окно
Обнаруженные пики помогают в инициализации окна поиска. Инициализируйте окна поиска как несколько ячеек с красным и синим цветами для левой и правой полос соответственно.
vBinRes = 1; hBinRes = 0.8; numVerticalBins = ceil((groundPts.XLimits(2) - groundPts.XLimits(1))/vBinRes); laneStartX = linspace(groundPts.XLimits(1),groundPts.XLimits(2),numVerticalBins); % Display bin windows figure pcshow(groundPts) view(2) helperDisplayBins(laneStartX,startLanePoints(1),hBinRes/2,groundPts,'red'); helperDisplayBins(laneStartX,startLanePoints(2),hBinRes/2,groundPts,'blue'); title('Initialized Sliding Windows')

Сдвижные бункеры инициализируются из мест расположения бункеров и итеративно перемещаются в направлении эго-транспортного средства (положительная ось X). helperDetectLanes функция помощника определяет точки полосы движения и визуализирует скользящие бункеры. Последовательные бункеры скользят вдоль оси Y на основе значений интенсивности внутри предыдущего бункера.
В областях, где отсутствуют точки полосы движения, функция предсказывает скользящие ячейки с помощью многочлена второй степени. Это условие обычно возникает, когда движущийся объект пересекает полосы движения. Скользящие ячейки желтые, а ячейки, предсказанные с помощью полинома, зеленые.
display = true;
lanes = helperDetectLanes(groundPts,hBinRes, ...
numVerticalBins,startLanePoints,display);
% Plot final lane points lane1 = lanes{1}; lane2 = lanes{2}; figure pcshow(groundPts) title('Detected Lane Points') hold on p1 = plot3(lane1(:, 1),lane1(:, 2),lane1(:, 3),'*y'); p2 = plot3(lane2(:, 1),lane2(:, 2),lane2(:, 3),'*r'); hold off view(2) lgnd = legend([p1 p2],{'Left Lane Points','Right Lane Points'}); set(lgnd,'color','White','Location','southoutside')

Фитинг полосы движения включает в себя оценку полиномиальной кривой в обнаруженных точках полосы движения. Эти многочлены используются вместе с зависимостью параллельной полосы движения для фитинга полосы движения.
Многочлен устанавливается на точках X-Y с использованием 2-градусного многочлена, представленного как + c, где a, b и c - параметры многочлена. Для выполнения фитинга кривой используйте командуhelperFitPolynomial вспомогательная функция, которая также обрабатывает отклонения с помощью алгоритма консенсуса случайных выборок (RANSAC). Чтобы оценить 3-D точки полосы движения, извлеките параметры из модели плоскости, созданной во время этапа предварительной обработки. Модель плоскости представляется как + d = 0, где координата Z неизвестна. Оцените координату Z, подставив координаты X и Y в уравнение плоскости.
[P1,error1] = helperFitPolynomial(lane1(:,1:2),2,0.1); [P2,error2] = helperFitPolynomial(lane2(:,1:2),2,0.1); xval = linspace(5,40,80); yval1 = polyval(P1,xval); yval2 = polyval(P2,xval); % Z-coordinate estimation modelParams = model.Parameters; zWorld1 = (-modelParams(1)*xval - modelParams(2)*yval1 - modelParams(4))/modelParams(3); zWorld2 = (-modelParams(1)*xval - modelParams(2)*yval2 - modelParams(4))/modelParams(3); % Visualize fitted lane figure pcshow(croppedPtCloud) title('Fitted Lane Polynomial') hold on p1 = plot3(xval,yval1,zWorld1,'y','LineWidth',0.2); p2 = plot3(xval,yval2,zWorld2,'r','LineWidth',0.2); lgnd = legend([p1 p2],{'Left Lane Points','Right Lane Points'}); set(lgnd,'color','White','Location','southoutside') view(2) hold off

Полосы движения обычно параллельны друг другу вдоль дороги. Чтобы сделать фитинг полосы надежным, используйте эту зависимость параллельности. При подгонке многочленов helperFitPolynomial вспомогательная функция также вычисляет ошибку фитинга. Обновите полосы с ошибочными точками с помощью нового полинома. Обновите этот многочлен, сдвинув его вдоль оси Y.
lane3d1 = [xval' yval1' zWorld1']; lane3d2 = [xval' yval2' zWorld2']; % Shift the polynomial with a high score along the Y-axis towards % the polynomial with a low score if error1 > error2 lanePolynomial = P2; if lane3d1(1,2) > 0 lanePolynomial(3) = lane3d2(1,2) + laneWidth; else lanePolynomial(3) = lane3d2(1,2) - laneWidth; end lane3d1(:,2) = polyval(lanePolynomial,lane3d1(:,1)); lanePolynomials = [lanePolynomial; P2]; else lanePolynomial = P1; if lane3d2(1,2) > 0 lanePolynomial(3) = lane3d1(1,2) + laneWidth; else lanePolynomial(3) = lane3d1(1,2) - laneWidth; end lane3d2(:,2) = polyval(lanePolynomial,lane3d2(:,1)); lanePolynomials = [P1; lanePolynomial] end % Visualize lanes after parallel fitting figure pcshow(ptCloud) axis([0 50 -15 15 -5 5]) hold on p1 = plot3(lane3d1(:,1),lane3d1(:,2),lane3d1(:,3),'y','LineWidth',0.2); p2 = plot3(lane3d2(:,1),lane3d2(:,2),lane3d2(:,3),'r','LineWidth',0.2); view([-90 90]) title('Fitted Lanes') lgnd = legend([p1 p2],{'Left Lane Points','Right Lane Points'}); set(lgnd,'color','White',"Location","southoutside") hold off

Отслеживание полосы помогает стабилизировать кривизну полосы, вызванную внезапными рывками и заносами. Эти изменения могут произойти из-за отсутствия точек полосы движения, движения транспортных средств по полосам движения и ошибочного обнаружения полосы движения. Отслеживание полосы движения представляет собой двухэтапный процесс:
Отслеживание параметров многочлена полосы ) для управления кривизной многочлена.
Отслеживание начальных точек, полученных при обнаружении пика. Этот параметр обозначается в многочлене как c.
Эти параметры обновляются с помощью фильтра Калмана с моделью движения с постоянным ускорением. Чтобы инициировать фильтр Калмана, используйте параметр configureKalmanFilter.
% Initial values curveInitialParameters = lanePolynomials(1,1:2); driftInitialParameters = lanePolynomials(:,3)'; initialEstimateError = [1 1 1] * 1e-1; motionNoise = [1 1 1] * 1e-7; measurementNoise = 10; % Configure Kalman filter curveFilter = configureKalmanFilter('ConstantAcceleration', ... curveInitialParameters,initialEstimateError,motionNoise,measurementNoise); driftFilter = configureKalmanFilter('ConstantAcceleration', ... driftInitialParameters,initialEstimateError,motionNoise,measurementNoise);
Закольцовывание и обработка данных лидара с помощью helperLaneDetector класс помощника. Этот вспомогательный класс реализует все предыдущие шаги, а также выполняет дополнительную предварительную обработку для удаления транспортных средств из облака точек. Это гарантирует, что обнаруженные точки заземления являются плоскими и модель плоскости является точной. Метод класса detectLanes обнаруживает и извлекает точки полосы для левой и правой полосы в виде двухэлементного массива ячеек, где первый элемент соответствует левой полосе, а второй элемент - правой полосе.
% Initialize the random number generator rng(2020) numFrames = numel(lidarData); detector = helperLaneDetector('ROI',[5 40 -3 3 -4 1]); % Turn on display player = pcplayer([0 50],[-15 15],[-5 5]); drift = zeros(numFrames,1); filteredDrift = zeros(numFrames,1); curveSmoothness = zeros(numFrames,1); filteredCurveSmoothness = zeros(numFrames,1); for i = 1:numFrames ptCloud = lidarData{i}; % Detect lanes detectLanes(detector,ptCloud); % Predict polynomial from Kalman filter predict(curveFilter); predict(driftFilter); % Correct polynomial using Kalman filter lanePolynomials = detector.LanePolynomial; drift(i) = mean(lanePolynomials(:,3)); curveSmoothness(i) = mean(lanePolynomials(:,1)); updatedCurveParams = correct(curveFilter,lanePolynomials(1,1:2)); updatedDriftParams = correct(driftFilter,lanePolynomials(:,3)'); % Update lane polynomials updatedLanePolynomials = [repmat(updatedCurveParams,[2 1]),updatedDriftParams']; % Estimate new lane points with updated polynomial lanes = updateLanePolynomial(detector,updatedLanePolynomials); filteredDrift(i) = mean(updatedLanePolynomials(:,3)); filteredCurveSmoothness(i) = mean(updatedLanePolynomials(:,1)); % Visualize lanes after parallel fitting ptCloud.Color = uint8(repmat([0 0 255],ptCloud.Count,1)); lane3dPc1 = pointCloud(lanes{1}); lane3dPc1.Color = uint8(repmat([255 0 0],lane3dPc1.Count,1)); lanePc = pccat([ptCloud lane3dPc1]); lane3dPc2 = pointCloud(lanes{2}); lane3dPc2.Color = uint8(repmat([255 255 0],lane3dPc2.Count,1)); lanePc = pccat([lanePc lane3dPc2]); view(player,lanePc) end

Чтобы проанализировать результаты обнаружения полосы движения, сравните их с отслеживаемыми многочленами полосы движения, выведя их на рисунки. Каждый график сравнивает параметры с фильтром Калмана и без него. Первый график сравнивает дрейф полос вдоль оси Y, а второй график сравнивает гладкость многочленов полос. Гладкость определяется как скорость изменения уклона кривой полосы движения.
figure plot(drift) hold on plot(filteredDrift) hold off title('Lane Drift Along Y-axis') legend('Drift Values','Filtered Drift Values')

figure plot(curveSmoothness) hold on plot(filteredCurveSmoothness) hold off title('Curve Smoothness') legend('Curve Smoothness','Filtered Curve Smoothness')

В этом примере показано, как обнаруживать полосы на канале интенсивности точечных облаков, поступающих от лидарного датчика. Также вы научились подгонять многочлен 2-D к обнаруженным точкам полосы движения и использовать модель нулевой плоскости для оценки 3-D точек полосы движения. Вы также использовали отслеживание фильтра Калмана для дальнейшего улучшения обнаружения полос движения.
helperLoadData вспомогательная функция загружает набор данных лидара в рабочую область MATLAB.
function reflidarData = helperGetDataset() outputFolder = fullfile(tempdir,'WPI'); url = 'https://www.mathworks.com/supportfiles/lidar/data/WPI_LidarData.tar.gz'; lidarDataTarFile = fullfile(outputFolder,'WPI_LidarData.tar.gz'); if ~exist(lidarDataTarFile,'file') mkdir(outputFolder); disp('Downloading WPI Lidar driving data (760 MB)...'); 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 load(fullfile(outputFolder,'WPI_LidarData.mat'), 'lidarData'); % Select region with a prominent intensity value reflidarData = cell(300,1); count = 1; roi = [-50 50 -30 30 -inf inf]; for i = 81:380 pc = lidarData{i}; ind = findPointsInROI(pc,roi); reflidarData{count} = select(pc,ind); count = count + 1; end end
Это helperInitialWindow вспомогательная функция вычисляет начальные точки окна поиска с помощью обнаруженных пиков гистограммы.
function [yval,detectedPeaks] = helperInitialWindow(yvals,peaks,laneWidth) leftLanesIndices = yvals >= 0; rightLanesIndices = yvals < 0; leftLaneYs = yvals(leftLanesIndices); rightLaneYs = yvals(rightLanesIndices); peaksLeft = peaks(leftLanesIndices); peaksRight = peaks(rightLanesIndices); diff = zeros(sum(leftLanesIndices),sum(rightLanesIndices)); for i = 1:sum(leftLanesIndices) for j = 1:sum(rightLanesIndices) diff(i,j) = abs(laneWidth - (leftLaneYs(i) - rightLaneYs(j))); end end [~,minIndex] = min(diff(:)); [row,col] = ind2sub(size(diff),minIndex); yval = [leftLaneYs(row) rightLaneYs(col)]; detectedPeaks = [peaksLeft(row) peaksRight(col)]; estimatedLaneWidth = leftLaneYs(row) - rightLaneYs(col); % If the calculated lane width is not within the bounds, % return the lane with highest peak if abs(estimatedLaneWidth - laneWidth) > 0.5 if max(peaksLeft) > max(peaksRight) yval = [leftLaneYs(maxLeftInd) NaN]; detectedPeaks = [peaksLeft(maxLeftInd) NaN]; else yval = [NaN rightLaneYs(maxRightInd)]; detectedPeaks = [NaN rightLaneYs(maxRightInd)]; end end end
Это helperFitPolynomial вспомогательная функция подгоняет многочлен на основе RANSAC к обнаруженным точкам полосы движения и вычисляет оценку фитинга.
function [P,score] = helperFitPolynomial(pts,degree,resolution) P = fitPolynomialRANSAC(pts,degree,resolution); ptsSquare = (polyval(P,pts(:,1)) - pts(:,2)).^2; score = sqrt(mean(ptsSquare)); end
Это helperComputeHistogram вспомогательная функция вычисляет гистограмму значений интенсивности точечных облаков.
function [histVal,yvals] = helperComputeHistogram(ptCloud,histogramBinResolution) numBins = ceil((ptCloud.YLimits(2) - ptCloud.YLimits(1))/histogramBinResolution); histVal = zeros(1,numBins-1); binStartY = linspace(ptCloud.YLimits(1),ptCloud.YLimits(2),numBins); yvals = zeros(1,numBins-1); for i = 1:numBins-1 roi = [-inf 15 binStartY(i) binStartY(i+1) -inf inf]; ind = findPointsInROI(ptCloud,roi); subPc = select(ptCloud,ind); if subPc.Count histVal(i) = sum(subPc.Intensity); yvals(i) = (binStartY(i) + binStartY(i+1))/2; end end end
Это helperfindpeaks вспомогательная функция извлекает пики из значений гистограммы.
function [pkHistVal,pkIdx] = helperfindpeaks(histVal) pkIdxTemp = (1:size(histVal,2))'; histValTemp = [NaN; histVal'; NaN]; tempIdx = (1:length(histValTemp)).'; % keep only the first of any adjacent pairs of equal values (including NaN) yFinite = ~isnan(histValTemp); iNeq = [1; 1 + find((histValTemp(1:end-1) ~= histValTemp(2:end)) & ... (yFinite(1:end-1) | yFinite(2:end)))]; tempIdx = tempIdx(iNeq); % Take the sign of the first sample derivative s = sign(diff(histValTemp(tempIdx))); % Find local maxima maxIdx = 1 + find(diff(s)<0); % Index into the original index vector without the NaN bookend pkIdx = tempIdx(maxIdx) - 1; % Fetch the coordinates of the peak pkHistVal = histVal(pkIdx); pkIdx = pkIdxTemp(pkIdx)'; end
[1] Галлаби, Фарук, Фавзи Нашашиби, Гайатх Эль-Хадж-Шаде и Мари-Анн Митте. «Обнаружение разметки полосы движения на основе LIDAR для позиционирования транспортного средства в карте высокой четкости». В 2018 году 21-я Международная конференция по интеллектуальным транспортным системам (ITSC), 2209-14. Мауи: IEEE, 2018. https://doi.org/10.1109/ITSC.2018.8569951.
[2] Туи, Микаэль и Фернандо Леон. «Обнаружение и отслеживание полосы движения на основе данных Лидара». Метрологические и измерительные системы 17, № 3 (2010): 311-322. https://doi.org/10.2478/v10178-010-0027-3.