주요 콘텐츠

PointPillars 딥러닝을 사용한 라이다 3차원 객체 검출

이 예제에서는 PointPillars 딥러닝 신경망을 사용하여 라이다에서 객체를 검출하는 방법을 보여줍니다[1]. 이 예제에서는 다음을 수행합니다.

  • PointPillars 객체 검출 신경망의 훈련과 테스트를 위한 데이터셋을 구성합니다. 또한 신경망 효율성을 향상하기 위해 훈련 데이터셋에 대한 데이터 증강을 수행합니다.

  • PointPillars 객체 검출 신경망을 훈련하기 위해 앵커 상자를 훈련 데이터로부터 계산합니다.

  • pointPillarsObjectDetector 함수를 사용하여 PointPillars 객체 검출기를 만들고 trainPointPillarsObjectDetector 함수를 사용하여 검출기를 훈련시킵니다.

이 예제에서는 포인트 클라우드에서 객체를 검출하는 데 사용할 사전 훈련된 PointPillars 객체 검출기도 제공됩니다. 사전 훈련된 모델은 Pandaset 데이터 세트로 훈련된 상태입니다. PointPillars 객체 검출 신경망에 대한 자세한 내용은 Get Started with PointPillars 항목을 참조하십시오.

라이다 데이터 세트 다운로드하기

PandaSet 데이터 세트에서 센서 데이터의 일부가 포함된 ZIP 파일(약 5.2GB 크기)을 다운로드합니다[2]. 다운로드한 후 파일의 압축을 풉니다. 이 파일에는 두 개의 메인 폴더인 LidarCuboids가 있으며, 여기에는 다음의 데이터가 포함되어 있습니다.

  • PointCloud 폴더에는 에고 차량이 양의 y축을 따라 움직이도록 배열된 PCD 형식의 전처리된 정렬 포인트 클라우드 2560개가 포함되어 있습니다. 포인트 클라우드가 이 방향에서 벗어나는 경우 pctransform 함수를 활용하여 필요한 변환을 적용할 수 있습니다.

  • Cuboid 폴더에는 대응하는 ground truth 데이터가 테이블 형식으로 포함되어 있습니다. 이는 PandasetLidarGroundTruth.mat 파일에 저장되어 있습니다. 이 파일은 자동차, 트럭, 보행자라는 세 가지 범주에 대한 3차원 경계 상자 정보를 제공합니다. 포인트 클라우드에 적용된 모든 변환은 bboxwarp 함수를 사용하여 경계 상자에 적용해야 합니다.

이 예제의 끝부분에 정의된 helperDownloadPandasetData 헬퍼 함수를 사용하여, 주어진 URL에서 Pandaset 데이터셋을 다운로드합니다.

outputFolder = fullfile(tempdir,'Pandaset');

lidarURL = ['https://ssd.mathworks.com/supportfiles/lidar/data/' ...
    'Pandaset_LidarData.tar.gz'];
helperDownloadPandasetData(outputFolder,lidarURL);

인터넷 연결에 따라 다운로드가 완료되기까지 얼마간의 시간이 걸릴 수 있습니다. 다운로드 처리가 완료될 때까지 코드는 MATLAB® 실행을 일시 중단합니다. 또는 웹 브라우저를 사용하여 로컬 디스크에 데이터 세트를 다운로드하고 파일을 추출할 수도 있습니다. 이렇게 하려면 코드에서 outputFolder 변수를 다운로드한 파일의 위치로 변경하십시오.

데이터 세트 불러오기

pcread 함수를 사용하여 지정된 경로로부터 PCD 파일을 불러올 파일 데이터저장소를 만듭니다.

path = fullfile(outputFolder,'Lidar');
lidarData = fileDatastore(path,'ReadFcn',@(x) pcread(x));

자동차 객체와 트럭 객체의 3차원 경계 상자 레이블을 불러옵니다.

gtPath = fullfile(outputFolder,'Cuboids','PandaSetLidarGroundTruth.mat');
data = load(gtPath,'lidarGtLabels');
Labels = timetable2table(data.lidarGtLabels);
boxLabels = Labels(:,2:3);

전체 뷰 포인트 클라우드를 표시합니다.

figure
ptCld = preview(lidarData);
ax = pcshow(ptCld.Location);
set(ax,'XLim',[-50 50],'YLim',[-40 40]);
zoom(ax,2.5);
axis off;

Figure contains an axes object. The hidden axes object contains an object of type scatter.

데이터 전처리하기

PandaSet 데이터는 전체 뷰 포인트 클라우드로 구성됩니다. 이 예제의 경우 표준 파라미터를 사용하여 전체 뷰 포인트 클라우드를 정면 뷰 포인트 클라우드로 자릅니다[1]. 이러한 파라미터는 신경망에 전달되는 입력의 크기를 결정합니다. x축, y축, z축을 따라 더 작은 포인트 클라우드 범위를 선택하여 원점에 더 가까운 객체를 검출합니다. 이렇게 하면 신경망의 전체 훈련 시간도 단축됩니다.

xMin = 0.0;     % Minimum value along X-axis.
yMin = -39.68;  % Minimum value along Y-axis.
zMin = -5.0;    % Minimum value along Z-axis.
xMax = 69.12;   % Maximum value along X-axis.
yMax = 39.68;   % Maximum value along Y-axis.
zMax = 5.0;     % Maximum value along Z-axis.
xStep = 0.16;   % Resolution along X-axis.
yStep = 0.16;   % Resolution along Y-axis.

% Define point cloud parameters.
pointCloudRange = [xMin xMax yMin yMax zMin zMax];
voxelSize = [xStep yStep];

이 예제에 지원 파일로 첨부된 cropFrontViewFromLidarData 헬퍼 함수를 사용하여 다음을 수행합니다.

  • 입력 전체 뷰 포인트 클라우드에서 정면 뷰를 자릅니다.

  • gridParams로 지정된 ROI 내에 있는 상자 레이블을 선택합니다.

[croppedPointCloudObj,processedLabels] = cropFrontViewFromLidarData(...
    lidarData,boxLabels,pointCloudRange);
Processing data 100% complete

잘라낸 포인트 클라우드와 ground truth 상자 레이블을 표시합니다.

pc = croppedPointCloudObj{1,1};
bboxes = [processedLabels.Car{1};processedLabels.Truck{1}];

ax = pcshow(pc);
showShape('cuboid',bboxes,'Parent',ax,'Opacity',0.1,...
        'Color','green','LineWidth',0.5);

Figure contains an axes object. The axes object contains an object of type scatter.

reset(lidarData);

Datastore 객체 만들기

데이터 세트를 훈련 세트와 테스트 세트로 나눕니다. 데이터의 70%를 신경망 훈련용으로, 나머지는 평가용으로 선택합니다.

rng(1);
shuffledIndices = randperm(size(processedLabels,1));
idx = floor(0.7 * length(shuffledIndices));

trainData = croppedPointCloudObj(shuffledIndices(1:idx),:);
testData = croppedPointCloudObj(shuffledIndices(idx+1:end),:);

trainLabels = processedLabels(shuffledIndices(1:idx),:);
testLabels = processedLabels(shuffledIndices(idx+1:end),:);

데이터저장소에 쉽게 액세스할 수 있도록 이 예제에 지원 파일로 첨부된 saveptCldToPCD 헬퍼 함수를 사용하여 훈련 데이터를 PCD 파일로 저장합니다. 훈련 데이터가 폴더에 저장되어 있고 pcread 함수에서 지원되는 경우 writeFiles를 "false"로 설정할 수 있습니다.

writeFiles = true;
dataLocation = fullfile(outputFolder,'InputData');
[trainData,trainLabels] = saveptCldToPCD(trainData,trainLabels,...
    dataLocation,writeFiles);
Processing data 100% complete

fileDatastore를 사용하여 파일 데이터저장소를 만들고 pcread 함수를 사용하여 PCD 파일을 불러옵니다.

lds = fileDatastore(dataLocation,'ReadFcn',@(x) pcread(x));

3차원 경계 상자 레이블을 불러오기 위해 boxLabelDatastore를 사용하여 상자 레이블 데이터저장소를 만듭니다.

bds = boxLabelDatastore(trainLabels);

combine 함수를 사용하여 포인트 클라우드와 3차원 경계 상자 레이블을 하나의 훈련용 데이터저장소로 결합합니다.

cds = combine(lds,bds);

데이터 증강 수행하기

이 예제에서는 ground truth 데이터 증강과 다른 전역 데이터 증강 기법을 사용하여 훈련 데이터와 대응하는 상자를 더 다양하게 만듭니다. 라이더 데이터를 사용한 3차원 객체 검출 워크플로에 사용되는 일반적인 데이터 증강 기법에 대한 자세한 내용은 Data Augmentations for Lidar Object Detection Using Deep Learning 항목을 참조하십시오.

이 예제의 끝부분에 정의된 helperShowPointCloudWith3DBoxes 헬퍼 함수를 사용하여 증강 전에 포인트 클라우드를 읽어오고 표시합니다.

augData = preview(cds);
[ptCld,bboxes,labels] = deal(augData{1},augData{2},augData{3});

% Define the classes for object detection.
classNames = {'Car','Truck'};

% Define colors for each class to plot bounding boxes.
colors = {'green','magenta'};

helperShowPointCloudWith3DBoxes(ptCld,bboxes,labels,classNames,colors)

Figure contains an axes object. The axes object contains an object of type scatter.

sampleLidarData 함수를 사용하여 훈련 데이터에서 3차원 경계 상자와 해당 대응점을 샘플링합니다.

sampleLocation = fullfile(outputFolder,'GTsamples');
[ldsSampled,bdsSampled] = sampleLidarData(cds,classNames,'MinPoints',20,...                  
                            'Verbose',false,'WriteLocation',sampleLocation);
cdsSampled = combine(ldsSampled,bdsSampled);

pcBboxOversample 함수를 사용하여 고정된 수의 자동차 클래스 객체와 트럭 클래스 객체를 모든 포인트 클라우드에 무작위로 추가합니다. transform 함수를 사용하여 ground truth와 사용자 지정 데이터 증강을 훈련 데이터에 적용합니다.

numObjects = [10 10];
cdsAugmented = transform(cds,@(x)pcBboxOversample(x,cdsSampled,classNames,numObjects));

이 예제의 끝부분에 정의된 helperAugmentLidarData 헬퍼 함수를 사용하여 다음과 같은 추가 데이터 증강 기법을 모든 포인트 클라우드에 적용합니다.

  • 5% 무작위 스케일링

  • [-pi/4, pi/4]에서 z축을 따라 무작위 회전

  • x축, y축, z축을 따라 각각 [0.2, 0.2, 0.1]미터씩 무작위 평행 이동

cdsAugmented = transform(cdsAugmented,@(x)helperAugmentData(x));

이 예제의 끝부분에 정의된 helperShowPointCloudWith3DBoxes 헬퍼 함수를 사용하여 ground truth 증강된 상자와 함께 증강된 포인트 클라우드를 표시합니다.

augData = preview(cdsAugmented);
[ptCld,bboxes,labels] = deal(augData{1},augData{2},augData{3});
helperShowPointCloudWith3DBoxes(ptCld,bboxes,labels,classNames,colors)

Figure contains an axes object. The axes object contains an object of type scatter.

PointPillars 객체 검출기 만들기

pointPillarsObjectDetector 함수를 사용하여 PointPillars 객체 검출 신경망을 만들 수 있습니다. PointPillars 신경망에 대한 자세한 내용은 Get Started with PointPillars 항목을 참조하십시오.

이 다이어그램은 PointPillars 객체 검출기의 신경망 아키텍처를 보여줍니다. 심층 신경망 디자이너 (Deep Learning Toolbox) 앱을 사용하여 PointPillars 신경망을 만들 수 있습니다.

PPNetwork.png

이 예제에 지원 파일로 첨부된 calculateAnchorsPointPillars 헬퍼 함수를 사용하여 훈련 데이터를 기반으로 앵커 상자를 추정합니다.

anchorBoxes = calculateAnchorsPointPillars(trainLabels);

PointPillars 검출기를 정의합니다.

detector = pointPillarsObjectDetector(pointCloudRange,classNames,anchorBoxes,...
    'VoxelSize',voxelSize); 

훈련 옵션 지정하기

trainingOptions (Deep Learning Toolbox) 함수를 사용하여 신경망 훈련 파라미터를 지정합니다. 훈련이 중단된 경우 저장된 검사 지점에서 훈련을 재개할 수 있습니다.

CPU 또는 GPU를 사용하여 검출기를 훈련시킵니다. GPU를 사용하려면 Parallel Computing Toolbox™와 CUDA® 지원 NVIDIA® GPU가 필요합니다. 자세한 내용은 GPU 연산 요구 사항 (Parallel Computing Toolbox) 항목을 참조하십시오. 사용 가능한 GPU가 있는지 자동으로 감지하려면 executionEnvironment"auto"로 설정합니다. GPU가 없거나 훈련에 GPU를 사용하지 않으려면 executionEnvironment"cpu"로 설정합니다. GPU를 훈련에 사용하려면 executionEnvironment"gpu"로 설정합니다.

executionEnvironment = "auto";

options = trainingOptions('adam',...
    Plots = "training-progress",...
    MaxEpochs = 60,...
    MiniBatchSize = 3,...
    GradientDecayFactor = 0.9,...
    SquaredGradientDecayFactor = 0.999,...
    LearnRateSchedule = "piecewise",...
    InitialLearnRate = 0.0002,...
    LearnRateDropPeriod = 15,...
    LearnRateDropFactor = 0.8,...
    ExecutionEnvironment= executionEnvironment, ...
    PreprocessingEnvironment = 'parallel',...
    BatchNormalizationStatistics = 'moving',...
    ResetInputNormalization = false,...
    CheckpointFrequency = 10, ...
    CheckpointFrequencyUnit = 'epoch', ...
    CheckpointPath = userpath);

PointPillars 객체 검출기 훈련시키기

doTraining이 "true"인 경우 trainPointPillarsObjectDetector 함수를 사용하여 PointPillars 객체 검출기를 훈련시킵니다. 또는 사전 훈련된 검출기를 불러옵니다.

doTraining = false;
if doTraining    
    [detector,info] = trainPointPillarsObjectDetector(cdsAugmented,detector,options);
else
    pretrainedDetector = load('pretrainedPointPillarsDetector.mat','detector');
    detector = pretrainedDetector.detector;
end

참고: 사전 훈련된 신경망 pretrainedPointPillarsDetector.mat은 에고 차량 방향이 양의 y축을 따르는 상황에서 Pandar 64 센서에 의해 캡처된 포인트 클라우드 데이터를 기반으로 훈련된 상태입니다.

사용자 지정 데이터셋에 대해 이 사전 훈련된 신경망을 사용하여 정확한 검출을 생성하려면 다음을 수행합니다.

  1. 에고 차량이 양의 y축을 따라 이동하도록 포인트 클라우드 데이터를 변환합니다.

  2. pcorganize 함수를 사용하여 Pandar 64 파라미터로 데이터를 재구성합니다.

검출 생성하기

훈련된 신경망을 사용하여 테스트 데이터의 객체를 검출합니다.

  • 테스트 데이터에서 포인트 클라우드를 읽어옵니다.

  • 테스트 포인트 클라우드를 대상으로 검출기를 실행하여 예측된 경계 상자와 신뢰 점수를 구합니다.

  • 이 예제의 끝부분에 정의된 helperDisplay3DBoxesOverlaidPointCloud 헬퍼 함수를 사용하여 경계 상자와 함께 포인트 클라우드를 표시합니다.

ptCloud = testData{1,1};

% Run the detector on the test point cloud.
[bboxes,score,labels] = detect(detector,ptCloud);

% Display the predictions on the point cloud.
helperShowPointCloudWith3DBoxes(ptCloud,bboxes,labels,classNames,colors)

Figure contains an axes object. The axes object contains an object of type scatter.

테스트 세트를 사용하여 검출기 평가하기

훈련된 객체 검출기를 대규모 포인트 클라우드 데이터 세트를 대상으로 평가하여 성능을 측정합니다.

numInputs = 50;

% Generate rotated rectangles from the cuboid labels.
bds = boxLabelDatastore(testLabels(1:numInputs,:));
groundTruthData = transform(bds,@(x)createRotRect(x));

detectionResults = detect(detector,testData(1:numInputs,:),...
                         'Threshold',0.25);

% Convert the bounding boxes to rotated rectangles format and calculate
% the evaluation metrics.
for i = 1:height(detectionResults)
    box = detectionResults.Boxes{i};
    detectionResults.Boxes{i} = box(:,[1,2,4,5,9]);
    detectionResults.Labels{i} = detectionResults.Labels{i}';
end

% Evaluate the object detector using average orietation similarity metric
metrics = evaluateObjectDetection(detectionResults,groundTruthData,"AdditionalMetrics","AOS");
[datasetSummary,classSummary] = summarize(metrics)
datasetSummary=1×5 table
    NumObjects    mAPOverlapAvg    mAP0.5     mAOSOverlapAvg    mAOS0.5
    __________    _____________    _______    ______________    _______

       511           0.70556       0.70556       0.68208        0.68208

classSummary=2×5 table
             NumObjects    APOverlapAvg     AP0.5     AOSOverlapAvg    AOS0.5 
             __________    ____________    _______    _____________    _______

    Car         480          0.76271       0.76271       0.74664       0.74664
    Truck        31          0.64841       0.64841       0.61751       0.61751

지원 함수

helperDownloadPandasetData 함수는 Pandaset 데이터를 다운로드합니다.

function helperDownloadPandasetData(outputFolder,lidarURL)
% Download the data set from the given URL to the output folder.

    lidarDataTarFile = fullfile(outputFolder,'Pandaset_LidarData.tar.gz');
    
    if ~exist(lidarDataTarFile,'file')
        mkdir(outputFolder);
        
        disp('Downloading PandaSet Lidar driving data (5.2 GB)...');
        websave(lidarDataTarFile,lidarURL);
        untar(lidarDataTarFile,outputFolder);
    end
    
    % Extract the file.
    if (~exist(fullfile(outputFolder,'Lidar'),'dir'))...
            &&(~exist(fullfile(outputFolder,'Cuboids'),'dir'))
        untar(lidarDataTarFile,outputFolder);
    end

end

helperShowPointCloudWith3DBoxes 함수는 포인트 클라우드를 해당되는 경계 상자와 함께 표시하고, 각 클래스에 다른 색상을 사용하여 쉽게 구분할 수 있도록 합니다.

function helperShowPointCloudWith3DBoxes(ptCld,bboxes,labels,classNames,colors)
    % Validate the length of classNames and colors are the same
    assert(numel(classNames) == numel(colors), 'ClassNames and Colors must have the same number of elements.');
    
    % Get unique categories from labels
    uniqueCategories = categories(labels); 

    % Create a mapping from category to color
    colorMap = containers.Map(uniqueCategories, colors); 
    labelColor = cell(size(labels));

    % Populate labelColor based on the mapping
    for i = 1:length(labels)
        labelColor{i} = colorMap(char(labels(i)));
    end

    figure;
    ax = pcshow(ptCld); 
    showShape('cuboid', bboxes, 'Parent', ax, 'Opacity', 0.1, ...
        'Color', labelColor, 'LineWidth', 0.5);
    zoom(ax,1.5);
end

helperAugmentData 함수는 데이터에 다음과 같은 증강을 적용합니다.

  • 5% 무작위 스케일링.

  • [-pi/4, pi/4]에서 z축을 따라 무작위 회전.

  • x, y, z축을 따라 각각 [0.2, 0.2, 0.1]미터씩 무작위 평행 이동.

function data = helperAugmentData(data)
    % Apply random scaling, rotation and translation.
    pc = data{1};
    
    minAngle = -45;
    maxAngle = 45;
    
    % Define outputView based on the grid-size and XYZ limits.
    outView = imref3d([32,32,32],[-100,100],...
        [-100,100],[-100,100]);
    
    
    theta = minAngle + rand(1,1)*(maxAngle - minAngle);
    tform = randomAffine3d('Rotation',@() deal([0,0,1],theta),...
        'Scale',[0.95,1.05],...
        'XTranslation',[0,0.2],...
        'YTranslation',[0,0.2],...
        'ZTranslation',[0,0.1]);
    
    % Apply the transformation to the point cloud.
    ptCloudTransformed = pctransform(pc,tform);
    
    % Apply the same transformation to the boxes.
    bbox = data{2};
    [bbox,indices] = bboxwarp(bbox,tform,outView);
    if ~isempty(indices)
        data{1} = ptCloudTransformed;
        data{2} = bbox;
        data{3} = data{1,3}(indices,:);
    end

end

참고 문헌

[1] Lang, Alex H., Sourabh Vora, Holger Caesar, Lubing Zhou, Jiong Yang, and Oscar Beijbom. "PointPillars: Fast Encoders for Object Detection From Point Clouds." In 2019 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 12689-12697. Long Beach, CA, USA: IEEE, 2019. https://doi.org/10.1109/CVPR.2019.01298.

[2] Hesai and Scale. PandaSet. https://scale.com/open-datasets/pandaset.

참고 항목

객체

함수

도움말 항목