В этом примере показано, как обнаружить маршруты в облаках точек лидара. Можно использовать значения интенсивности, возвращенные от облаков точек лидара, чтобы обнаружить маршруты автомобиля, оборудованного датчиком. Можно далее улучшить обнаружение маршрута при помощи алгоритма подбора кривой и отслеживание параметров кривой. Обнаружение маршрута лидара включает, вы, чтобы создать комплексные рабочие процессы как маршрут сохраняете, помогают, предупреждение о сходе с полосы и адаптивный круиз-контроль для автономного управления автомобилем. Тестовое транспортное средство собирает данные о лидаре с помощью датчика лидара, смонтированного на его крыше.
Обнаружение маршрута в лидаре включает обнаружение мгновенных левых и правых маршрутов, также известных как маршруты автомобиля, оборудованного датчиком, относительно датчика лидара. Это включает следующие шаги:
Экстракция необходимой области
Оснуйте плоскую сегментацию
Пиковое обнаружение интенсивности
Поиск окна использования обнаружения маршрута
Параболический полиномиальный подбор кривой
Параллельный подбор кривой маршрута
Отслеживание маршрута
Эта блок-схема дает обзор рабочего процесса, представленного в этом примере.
Преимущества использования данных о лидаре для обнаружения маршрута:
Облака точек лидара дают лучшее 3-D представление дорожного покрытия, чем данные изображения, таким образом уменьшая необходимые калибровочные параметры, чтобы найти вид с высоты птичьего полета.
Лидар более устойчив против неблагоприятных климатических условий, чем основанное на изображении обнаружение.
Данные о лидаре имеют уровень сантиметра точности, ведя к точной локализации маршрута.
Данные о лидаре, используемые в этом примере, были собраны с помощью Изгнания датчик лидара канала 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)
Обнаружьте точки маршрута при помощи поиска раздвижного окна, где первоначальные оценки для раздвижных окон сделаны с помощью основанной на интенсивности гистограммы.
Обнаружение точки маршрута состоит, в основном, из этих двух шагов:
Пиковое обнаружение интенсивности
Поиск окна
Точки маршрута в облаке точек лидара имеют отличное распределение интенсивности. Обычно, эта интенсивность занимает верхнюю область в распределении гистограммы и появляется как высокий peaks. Вычислите гистограмму интенсивности от обнаруженной наземной плоскости вдоль оси автомобиля, оборудованного датчиком (положительная Ось X). helperComputeHistogram
функция помощника создает гистограмму точек интенсивности. Управляйте количеством интервалов для гистограммы путем определения разрешения интервала.
histBinResolution = 0.2; [histVal,yvals] = helperComputeHistogram(groundPts,histBinResolution); figure plot(yvals,histVal,'--k') set(gca,'XDir','reverse') hold on
Получите peaks в гистограмме при помощи 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
Поиск окна используется, чтобы обнаружить точки маршрута путем скольжения окон вдоль направления искривления маршрута. Поиск окна состоит из двух шагов:
Инициализация окна
Раздвижное окно
Обнаруженный peaks помогает в инициализации окна поиска. Инициализируйте окна поиска как несколько интервалов с красными и синими цветами для левых и правых маршрутов соответственно.
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 степенями, представленного как , где a, b, и c являются полиномиальными параметрами. Чтобы выполнить аппроксимирование кривыми, используйте helperFitPolynomial
функция помощника, которая также обрабатывает выбросы с помощью согласия случайной выборки (RANSAC) алгоритм. Чтобы оценить 3-D точки маршрута, извлеките параметры из плоской модели, созданной во время шага предварительной обработки. Плоская модель представлена как , где 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
Отслеживание маршрута помогает в стабилизации искривления маршрута, вызванного внезапными толчками и дрейфами. Эти изменения могут произойти из-за упущения сути маршрута, транспортные средства, отодвигающиеся маршруты и ошибочное обнаружение маршрута. Отслеживание маршрута является двухступенчатым процессом:
Отследите параметры полинома маршрутауправлять искривлением полинома.
Отследите стартовые точки, прибывающие из пикового обнаружения. Этот параметр обозначается как в полиноме.
Эти параметры обновляются с помощью Фильтра Калмана с постоянной ускоряющей моделью движения. Чтобы инициировать Фильтр Калмана, используйте 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')
Этот пример показал вам, как обнаружить маршруты на канале интенсивности облаков точек, прибывающих из датчика лидара. Вы также изучили, как соответствовать 2D полиному на обнаруженных точках маршрута, и рычаги основывают плоскую модель, чтобы оценить 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
функция помощника вычисляет начальные точки окна поиска с помощью обнаруженного peaks гистограммы.
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
функция помощника извлекает peaks из значений гистограммы.
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] Ghallabi, Фарук, Fawzi Nashashibi, Гаят Эль-Хэдж-Шхэйд и Мари-Энн Миттет. "Обнаружение отмечающего Маршрута LIDAR-Based для Расположения Транспортного средства в Карту HD". На 2 018 21-х Международных конференциях по вопросам Интеллектуальных Систем Транспортировки (ITSC), 2209-14. Мауи: IEEE, 2018. https://doi.org/10.1109/ITSC.2018.8569951.
[2] Thuy, Майкл и Фернандо Леон. "Обнаружение маршрута и Отслеживающий На основе Данных о Лидаре". Метрология и Системы измерения 17, № 3 (2010): 311-322. https://doi.org/10.2478/v10178-010-0027-3.