主要内容

使用PointPillars深度学习的激光雷达三维目标检测

此示例显示如何在点云中培训用于对象检测的尖峰网络。

激光雷达点云数据可以通过多种激光雷达传感器获取,包括Velodyne®、Pandar和Ouster传感器。这些传感器可以捕捉场景中物体的三维位置信息,这对自动驾驶和增强现实的许多应用都很有用。然而,由于每个目标数据的稀疏性、目标遮挡和传感器噪声,用点云数据训练鲁棒检测器是具有挑战性的。深度学习技术已经被证明可以通过直接从点云数据中学习稳健的特征表示来解决许多这些挑战。PointPillars是一种用于三维物体检测的深度学习技术[1.].PointPillars网络使用与PointNet类似的架构,从称为柱子的稀疏点云中提取密集、健壮的特征,然后使用一个带有改进的SSD对象检测网络的2d深度学习网络来估计联合3d包围盒、方向和类预测。

这个示例使用PandaSet [2.]来自Hesai和Scale的数据集。PANDASET包含使用PANDAR64传感器捕获的各种城市场景的8240个未组织的LIDAR点云扫描。数据集提供了3-D边界框标签,可为18个不同的对象类,包括汽车,卡车和行人。

下载激光雷达数据集

本例使用PandaSet的一个子集,其中包含2560个预处理过的有组织点云。每点云覆盖 360. o ,并指定为64 × 1856矩阵。点云以PCD格式存储,其对应的地面真值数据存储在PandaSetLidarGroundTruth.mat文件。该文件包含三个类的3-D包围盒信息,它们是car、truck和pedestrian。数据集大小为5.2 GB。

属性从给定的URL下载Pandaset数据集HelperdownloadPandasetData.辅助函数,在本例的最后定义。

outputFolder=fullfile(tempdir,“潘达塞特”);lidarURL = ['https://ssd.mathworks.com/金宝appsupportfiles/lidar/data/'...“Pandaset_LidarData.tar.gz”];helperDownloadPandasetData (outputFolder lidarURL);

根据你的网络连接,下载过程可能需要一些时间。代码暂停MATLAB®执行,直到下载过程完成。或者,您可以使用web浏览器将数据集下载到本地磁盘并提取文件。如果您这样做,请更改outputFolder代码中的变量到下载的文件的位置。

下载Pretrained网络

使用帮手DownloadPretrainedPointPillarsNet辅助函数,在本例的最后定义。预先训练的模型允许您运行整个示例,而不必等待训练完成。如果你想训练网络,设置溺爱变量来真的

pretrainedNetURL = ['https://ssd.mathworks.com/金宝appsupportfiles/lidar/data/'...“trainedPointPillarsPandasetNet.zip”];doTraining=false;如果~doTraining helperDownloadPretrainedPointPillarsNet(outputFolder,pretrainedNetURL);结束

加载数据

使用。创建文件数据存储来从指定的路径加载PCD文件pcread函数。

路径= fullfile (outputFolder,激光雷达的);lidardata = filedatastore(路径,“ReadFcn”@ (x) pcread (x));

装载汽车和卡车物体的三维包围盒标签。

gtPath = fullfile (outputFolder,“长方体”,“PandaSetLidarGroundTruth.mat”);data =负载(gtPath“lidarGtLabels”);Labels=timetable2table(data.lidarGtLabels);boxLabels=Labels(:,2:3);

显示全视线云。

图ptld = read(lidarData);ax = pcshow (ptCld.Location);集(ax,“XLim”50 [-50],“YLim”, 40 [-40]);变焦(ax, 2.5);轴

重置(Lidardata);

数据进行预处理

PandaSet数据由全视图点云组成。对于本例,使用标准参数将全视图点云裁剪为前视图点云[1.].这些参数决定了传递给网络的输入的大小。沿着x、y、z轴选择较小范围的点云有助于检测离原点更近的目标,同时也减少了网络的整体训练时间。

xMin=0.0;% x轴上最小值。yMin = -39.68;% y轴最小值。zMin = -5.0;%沿Z轴的最小值。Xmax = 69.12;% x轴上的最大值。yMax=39.68;% y轴上的最大值。zMax = 5.0;沿z轴的%最大值。xStep = 0.16;沿x轴的分辨率。yStep = 0.16;沿y轴的分辨率。dsFactor=2.0;百分比下采样因子。%计算伪图像的尺寸。Xn = round(((xMax - xMin) / xStep));Yn = round(((yMax - yMin) / yStep));%定义柱提取参数。gridparams = {{xmin,ymin,zmin},{xmax,ymax,zmax},{xstep,ystep,dsfactor},{xn,yn}};

使用cropfrontviewfromlidardata.Helper函数,作为支持文件附加到本例中:金宝app

  • 从输入的全视图点云裁剪前视图。

  • 选择指定的ROI内的框标签gridParams

[CROMPEDPOINTCLOUDOBJ,PROCESSEDLABELS] = CROMFRONTVIEWFROMLIDADDATA(...lidarData、boxLabels gridParams);
处理数据100%完成

使用使用的裁剪点云和地面真相标签helperDisplay3DBoxesOverlaidPointCloud在示例的最后定义的辅助函数。

pc=croppedPointCloudObj{1,1};gtLabelsCar=processedLabels.Car{1};gtLabelsTruck=processedLabels.Truck{1};HelperDisplay3BoxesOverlaidPointCloud(pc.Location,gtLabelsCar,...“绿色”gtLabelsTruck,“红色”,“裁剪点云”);

重置(Lidardata);

创建用于培训的数据存储对象

将数据集分为培训和测试集。选择70%的数据以培训网络以及其余的评估。

rng (1);shuffledIndices = randperm(大小(processedLabels, 1));idx = floor(0.7 * length(shuffledIndices));trainData = croppedPointCloudObj (shuffledIndices (1: idx):);testData = croppedPointCloudObj (shuffledIndices (idx + 1:结束):);trainLabels = processedLabels (shuffledIndices (1: idx):);testLabels = processedLabels (shuffledIndices (idx + 1:结束):);

为了方便地访问数据存储,请使用saveptCldToPCD辅助函数,作为支持文件附加到本示例中。金宝app你可以设置writefile假“如果您的培训数据保存在文件夹中并得到支持金宝apppcread函数。

writefile = true;dataLocation = fullfile (outputFolder,“InputData”);[trainData, trainLabels] = saveptCldToPCD (trainData trainLabels,...Datalocation,Writefiles);
处理数据100%完成

使用。创建文件数据存储fileDatastore要加载PCD文件,请使用pcread函数。

lds=文件数据存储(数据位置,“ReadFcn”@ (x) pcread (x));

Createa Box标签数据存储使用Boxlabeldata商店用于加载3-D边界框标签。

bds = boxLabelDatastore (trainLabels);

使用结合函数将点云和3-D包围盒标签合并到一个用于训练的数据存储中。

cd =结合(摩门教,bds);

数据扩充

本例使用了地面真值数据增强和其他一些全局数据增强技术,为训练数据和相应的框添加更多的多样性。有关在使用激光雷达数据的三维物体检测工作流程中使用的典型数据增强技术的更多信息,请参见利用深度学习的LIDAR对象检测的数据增强

在使用增强之前读取并显示点云helperDisplay3DBoxesOverlaidPointCloud辅助函数,在示例的最后定义。

augData =阅读(cds);augptCld = augData {1};augLabels = augData {1,2};augClass = augData {1,3};labelsCar = augLabels (augClass = ='车':);labelsTruck = augLabels (augClass = ='卡车':);helperDisplay3DBoxesOverlaidPointCloud (augptCld。位置、labelsCar“绿色”,...标签车,“红色”,“数据扩充前”);

重置(CD);

使用世代特拉多法悬念辅助函数,作为支持文件附加到本示例中,用于从训练数据中提取所有地面真值边界框。金宝app

gtData = generateGTDataForAugmentation (trainData trainLabels);

使用groundTruthDataAugmentation助手函数,作为支持文件附于本示例,用于向每个点云随机添加固定数量的car和truck类对象金宝app转变函数将地面真实值和自定义数据增强应用到训练数据。

samplesToAdd =结构('车'10,'卡车'10);cdsAugmented = transform(cds,@(x) groundTruthDataAugmenation(x,gtData,samplesToAdd));

此外,对每个点云应用以下数据增强。

  • 沿x轴随机翻转

  • 随机缩放5%

  • 从[-pi/4, pi/4]沿z轴随机旋转

  • 随机平移分别沿X-,Y和Z轴[0.2,0.2,0.1]米[0.2,0.2,0.1]米

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

使用helperDisplay3DBoxesOverlaidPointCloud辅助函数,在示例的最后定义。

奥杜塔=读(Cdsaugmented);augptCld = augData {1};augLabels = augData {1,2};augClass = augData {1,3};labelsCar = augLabels (augClass = ='车':);labelsTruck = augLabels (augClass = ='卡车':);helperDisplay3DBoxesOverlaidPointCloud (augptCld (: 1:3), labelsCar,“绿色”,...标签车,“红色”,数据增加的);

重置(cdsAugmented);

提取支柱信息

您可以将二维卷积架构应用于点云以更快地处理。为此,首先将3-D点云转换为2-D表示。使用转变函数与createpillars.辅助功能,附加到此示例作为支持文件,从点云中创建支柱功能和支柱指数。金宝app辅助功能执行以下操作:

  • 将三维点云离散成x-y平面上均匀间隔的网格,创建一组称为柱子的垂直柱子。

  • 根据每个支柱的点数(n)选择突出的支柱(p)。

  • 计算柱子中所有点的算术平均值的距离。

  • 计算从支柱中心的偏移量。

  • 使用x、y、z位置、强度、距离和偏移值为柱子中的每个点创建一个9维(9-D)向量。

定义突出柱子的数量。P = 12000;定义每根柱子的点数。N=100;cdsTransformed=transform(cdsAugmented,@(x)createPillars(x,gridParams,P,N));

定义网络

PointPillars网络使用PointNet网络的简化版本,它将支柱特性作为输入。对于每个支柱特征,网络应用线性层,然后是批处理归一化和ReLU层。最后,网络在通道上应用最大池操作来获得高级编码特征。这些编码的特征被分散到原来的柱子位置,使用自定义层创建一个伪图像helperscatterLayer,附加到该示例作为支持文件。金宝app然后,网络将伪图像处理有2-D卷积骨干,然后进行各种SSD检测磁头以预测三维边界盒以及其类。

基于要检测的类定义锚框维度。通常,这些维度是训练集中所有边界框值的均值[1.].或者,您也可以使用calculateAnchorBoxes辅助函数,附加到示例中,用于从任何训练集获取适当的锚盒。锚框在格式中定义{长度、宽度、高度、z-中心、偏航角}。

锚具箱=计算锚点支柱(列车标签);numAnchors=尺寸(锚箱,2);classNames=trainLabels.Properties.VariableNames;numClasses=numel(类名);

接下来,使用pointpillarNetwork辅助函数,作为支持文件附加到本示例中。金宝app

lgraph=点柱面网络(numAnchors、gridParams、P、N、NumClass);

指定培训选项

指定以下培训选项。

  • 将时代数量设置为60。

  • 将“小批量大小”设置为2。如果有更多可用内存,可以将“小批量大小”设置为更大的值。

  • 学习率设置为0.0002。

  • 设置学习ropperiod.到15。该参数表示根据公式降低学习率的周期数 learningRate × ( 迭代 % 学习ropperiod. ) × learnRateDropFactor

  • 设置learnRateDropFactor0.8。此参数表示按照每个参数降低学习率的速率学习ropperiod.

  • 将渐变衰减因子设置为0.9。

  • 设置平方梯度衰减因子为0.999。

  • 初始化梯度的平均值为[]。这是Adam优化器使用的。

  • 初始化梯度的平方平均值为[]。这是Adam优化器使用的。

numEpochs = 60;miniBatchSize = 2;learningRate = 0.0002;learnRateDropPeriod = 15;learnRateDropFactor = 0.8;gradientDecayFactor = 0.9;squaredGradientDecayFactor = 0.999;trailingAvg = [];trailingAvgSq = [];

火车模型

使用CPU或GPU训练网络。使用GPU需要并行计算工具箱™和支持CUDA®的NVIDIA®GPU。有关更多信息,请参见GPU支金宝app持情况(并行计算工具箱).若要自动检测您是否有可用的GPU,请设置executionEnvironment“汽车”.如果您没有GPU,或者不想使用一个用于培训,请设置executionEnvironment“cpu”.为了确保使用GPU进行训练,设置executionEnvironment“GPU”

接下来,创建一个小公子(深度学习工具箱)要批量加载数据,请执行以下操作:miniBatchSize在培训。

执行环境=“汽车”如果canUseParallelPool dispatchInBackground = true;其他的Disparctinbackground = false;结束mbq=小型批处理队列(...cdsTransformed 3...“MiniBatchSize”miniBatchSize,...“ouppantenvironment”,execultionenvironment,...“MiniBatchFcn”@(特性指标,盒子,标签)...helperCreateBatchData(特性、指标框、标签类名),...“MiniBatchFormat”,[“SSCB”,“SSCB”,""],...“DispatchInBackground”,真正的);

要使用自定义训练循环训练网络并启用自动微分,请将层图转换为dlnetwork(深度学习工具箱)对象。创建训练进度绘图器helperConfigureTrainingProgressPlotter辅助函数,在本例的最后定义。

最后,指定自定义训练循环。每一次迭代:

  • 读点云和地面真理盒从小公子(深度学习工具箱)使用下一个(深度学习工具箱)函数。

  • 使用dlfeval(深度学习工具箱)modelGradients函数。的modelGradients在示例的最后定义的Helper函数,返回相对于中可学习参数的损失梯度,相应的迷你批量丢失和当前批次的状态。

  • 使用adamupdate(深度学习工具箱)函数。

  • 更新的状态参数

  • 更新培训进度情节。

如果溺爱将层图转换为dlnetwork。net=dlnetwork(lgraph);%初始化情节。无花果=图;lossPlotter = helperConfigureTrainingProgressPlotter(图);迭代= 0;%自定义训练循环。对于epoch = 1:numepochs%重置数据存储。重置(兆贝可);(hasdata(mbq)) iteration = iteration + 1;%读取数据批次。[Pillarfeatures,Pillarindices,Boxlabels] =下一个(MBQ);%使用dlfeval评估模型梯度和损失%和MapeStradients函数。[梯度、损失、状态]=dlfeval(@modelGradients,net,...Pillarfeatures,Pillarindices,Booklabels,Gridparams,...anchorBoxes、numClasses executionEnvironment);%如果是NaN值,则不更新网络可学习参数%以梯度或损失值表示。如果helperCheckForNaN(梯度、损失)继续结束%更新DLNetwork的状态参数。网。=状态;%使用adam更新网络学习参数%的优化器。[net.Learnables,trailingAvg,trailingAvgSq]=...adamupdate(净。可学的、渐变trailingAvg,...跟踪AVGSQ,迭代,学习率,...梯度Dayfactor,SquaredgradientDecayfactor);%使用新点更新训练图。addpoints (lossPlotter、迭代、双(收集(extractdata(损失))));标题(“训练时代”+纪元+“的”+ numEpochs);drawnow;结束%每次learnRateDropPeriod后更新学习率。如果mod(epoch,learnRateDropPeriod) == 0 learningRate = learningRate * learnRateDropFactor;结束结束其他的preTrainedMATFile=fullfile(outputFolder,“trainedPointPillarsPandasetNet.mat”);pretrainedNetwork =负载(preTrainedMATFile,“净”);网= pretrainedNetwork.net;结束

生成检测

使用经过训练的网络检测测试数据中的对象:

  • 从测试数据中读取点云。

  • 使用generatePointPillarDetections辅助函数,作为支持文件附加到本示例中,以获得预测的边界框和置信度得分。金宝app

  • 用边框显示点云helperDisplay3DBoxesOverlaidPointCloud辅助函数,在示例的最后定义。

45 ptCloud = testData {1};gtLabels = testLabels(45岁);%generatePointPillarDetections功能检测%包围框,并为给定的点云得分。confidenceThreshold = 0.5;overlapThreshold = 0.1;(盒、分数、标签)= generatePointPillarDetections(净、ptCloud anchorBoxes,...Gridparams,ClassNames,ConcidenceThreshold,Overlapthreshold,P,N,...执行环境);boxlabelsCar=盒子(标签'=='车':);boxlabelsTruck(标签' = = =框'卡车':);%在点云上显示预测。Helperdisplay3dboxesoverlaidPointCloud(PTCloud.Location,Boxlabelscar,“绿色”,...Boxlabelstruck,“红色”,“预测边界框”);

评估模型

计算机Vision Toolbox™提供对象检测器评估功能,以测量平均精度等常用度量(evaluateDetectionAOS).对于本例,使用平均精度度量。平均精度提供了一个单一的数字,该数字包括探测器进行正确分类的能力(精度)和探测器找到所有相关对象的能力(回忆)。

评估受训人员dlnetwork(深度学习工具箱)对象对测试数据执行以下步骤。

  • 指定置信阈值,以便仅使用置信评分高于此值的检测。

  • 指定重叠阈值以消除重叠检测。

  • 使用generatePointPillarDetections辅助函数,作为支持文件附加到本示例中,以获取边界框、对象置信度得分和类标签。金宝app

  • 称呼evaluateDetectionAOSdetectionResultsgroundTruthData作为论点。

numInputs=numel(测试数据);%从长方体标签生成旋转的矩形。bds = boxLabelDatastore (testLabels);groundTruthData = transform(bds,@(x) createRotRect(x));%设置阈值。nmsPositiveIoUThreshold = 0.5;confidenceThreshold = 0.25;overlapThreshold = 0.1;%SET NUMSAMPLESTOTEST到NUMInputs以评估整个模型%测试数据集。numSamplesToTest = 50;detectionResults =表(“大小”numSamplesToTest [3],...'variabletypes',{“细胞”,“细胞”,“细胞”},...“VariableNames”,{“盒子”,“分数”,“标签”});对于num=1:numSamplesToTest ptCloud=testData{num,1};[box,score,labels]=generatePointPillarDetections(net,ptCloud,anchorBoxes,...Gridparams,ClassNames,ConfidenceThreshold,Orectapthreshold,...P, N, executionEnvironment);%将检测到的框转换为旋转矩形格式。如果detectionResults ~ isempty(盒)。盒子{num} =盒(:,(1、2、4、5、7]);其他的detectionResults。盒子{num} =框;结束detectionResults.Scores{num}=分数;detectionResults.Labels{num}=标签;结束度量=评估的检测AOS(检测结果、地面数据、,...nmsPositiveIoUThreshold);disp(指标(:,1:2))
AOS AP _______ _______汽车0.86746 0.86746卡车0.61463 0.61463

辅助函数

模型梯度

功能modelGradientsdlnetwork(深度学习工具箱)对象以及一小批输入数据柱状特征pillarIndices有相应的地面真值箱、锚箱和网格参数。函数返回损失相对于中可学习参数的梯度,相应的迷你批量丢失和当前批次的状态。

模型梯度函数通过执行这些操作来计算总损失和梯度。

  • 使用向前(深度学习工具箱)函数。

  • 通过使用地面真实数据,网格参数和锚框来生成损耗计算的目标。

  • 计算来自网络的所有六个预测的损失函数。

  • 将总损失计算为所有损失之和。

  • 计算可学习项相对于总损失的梯度。

函数(渐变、损失、状态)= modelGradients (pillarFeatures净,...pillarIndices、boxLabels gridParams anchorBoxes,...numClasses,executionEnvironment) numAnchors = size(锚盒,2);%从网络中提取预测。ypredictions = cell(size(net.outputnames));[ypredictions {:},州] =前进(净,柱,支柱);%从地面真实数据生成预测目标。YTargets = generatePointPillarTargets (YPredictions boxLabels pillarIndices,...Gridparams,锚盒,numcrasses);ytargets = Cellfun(@ Dlarray,Ytargets,'统一输出'、假);如果(execultenvironment ==.“汽车”&& canusegpu)||executionenvironment ==.“GPU”YTargets = cellfun(@ gpuArray,YTargets,'统一输出'、假);结束[盎格鲁人,occLoss locLoss、szLoss hdLoss, clfLoss] =...computePointPillarLoss(预测、目标、网格参数、,...numClasses numAnchors);%计算总损失。loss=angLoss+occLoss+locLoss+szLoss+hdLoss+clfLoss;%在丢失方面计算了学习栏的渐变。梯度= dlgradient(损失、net.Learnables);结束函数[pillarFeatures、PillarDice、labels]=helperCreateBatchData(...特性、指标groundTruthBoxes groundTruthClasses,类名)%沿批次尺寸组合的返回柱特征和索引沿标签中的批量维度连接的%和边界框。沿批处理维度的%连接功能和指标。pillarFeatures =猫(4特性{:1});pillarIndices =猫(4,指标{:1});%从类名获取类id。一会= repmat({分类(类名)},大小(groundTruthClasses));[~, classIndices] = cellfun (@ (a, b) ismember (a、b), groundTruthClasses,...一会,'统一输出'、假);%附加类索引并创建单个响应数组。= cellfun(@(bbox,classid) [bbox,classid],...土拨鼠箱,分类,'统一输出',false);len=max(cellfun(@(x)大小(x,1),组合响应));paddedboxes=cellfun(@(v)焊盘阵列(v,[len大小(v,1),0],0,“职位”),...组合响应,'统一输出'、假);标签=猫(4,paddedBBoxes {: 1});结束函数helperDownloadPandasetData (outputFolder lidarURL)%将数据集从给定URL下载到输出文件夹。lidarDataTarFile = fullfile (outputFolder,“Pandaset_LidarData.tar.gz”);如果〜存在(LidardataTarfile,'文件'mkdir (outputFolder);disp (“正在下载PandaSet激光雷达驱动数据(5.2 GB)……”);Websave(LidardataTarfile,Lidarurl);Untar(LidardataTarfile,Outputfolder);结束%解压文件。如果(~存在(fullfile (outputFolder激光雷达的),“dir”))...&&(〜存在(fullfile(outputfile,“长方体”),“dir”)解压(lidarDataTarFile outputFolder);结束结束函数helperDownloadPretrainedPointPillarsNet (outputFolder pretrainedNetURL)%下载预先训练过的PointPillars网络。preTrainedMATFile=fullfile(outputFolder,“trainedPointPillarsPandasetNet.mat”);preTrainedZipFile=fullfile(outputFolder,“trainedPointPillarsPandasetNet.zip”);如果~exist(preTrainedMATFile,'文件')如果~exist(预训练Zipfile,'文件') disp ('下载掠夺探测器(8.4 MB)......');websave (preTrainedZipFile pretrainedNetURL);结束解压(preTrainedZipFile,outputFolder);结束结束函数lossPlotter = helperConfigureTrainingProgressPlotter (f)%配置各种损耗的训练进度表。图(f);clf ylabel (“全损”);xlabel(“迭代”);lossPlotter = animatedline;结束函数retValue = helperCheckForNaN(梯度、损失)%已知最后一个卷积头'occupancy|conv2d'包含nan%的梯度。这个函数检查梯度值是否包含%纳尼。如果NANS将其他卷积头值添加到条件下%出现在梯度中。gradValue = gradients.Value(梯度。层= =“入住率| conv2d”)&...(渐变。参数= =“偏见”));如果(总和(isnan(提取数据(损失)),“所有”) > 0) ||...(和(isnan (extractdata (gradValue {1 1})),“所有”)>0)retValue=true;其他的etvalue = false;结束结束函数Helperdisplay3dboxesoverlaidPointPlod(PTCLD,Labelscar,Carcolor,...LabelStruck,TruckColor,TitleFutigure)%显示具有不同颜色边界框的点云,以显示不同颜色的点云%班级。数字;AX = PCSHOW(PTCLD);showshape('长方体'labelsCar,'父母'ax,'不透明度',0.1,...“颜色”,arcolor,“线宽”,0.5);保持;showshape('长方体',labelstruck,'父母'ax,'不透明度',0.1,...“颜色”,卡车色,“线宽”, 0.5);标题(titleForFigure);变焦(ax, 1.5);结束

参考文献

[1] Lang,Alex H.,Sourabh Vora,Holger Caesar,Lubing Zhou,Jiong Yang和Oscar Beijbom.“点柱:用于点云目标检测的快速编码器”,摘自2019 IEEE/CVF计算机视觉与模式识别会议(CVPR), 12689 - 12697。美国加州长滩:IEEE, 2019。https://doi.org/10.1109/CVPR.2019.01298

Hesai和Scale。PandaSet。https://scale.com/open-datasets/pandaset