Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

NVIDIA Jetson Xavier™ NX 組み込みプラットフォームでのグランド プレーンのセグメント化と障害物の検出

この例では、NVIDIA® 組み込みプラットフォームを搭載した車両の 3 次元 LiDAR データについて、グランド プレーンのセグメント化を実行し、近くの障害物を見つける方法を説明します。

この例では、グランド プレーンのセグメント化と障害物検出を使用して、次のことを説明します。

  • MATLAB® Coder™ および GPU Coder™ を使用した、グランド プレーンのセグメント化および障害物検出アルゴリズム用の C++ コードおよび CUDA® コードの生成。

  • プロセッサインザループ (PIL) シミュレーションを使用した、ターゲット プラットフォームでの生成コードの検証。

  • CPU (C++) と GPU (CUDA) でのアプリケーションのパフォーマンス。

サードパーティの必要条件

ターゲット ボード要件

  • NVIDIA Jetson Xavier™ NX 組み込みプラットフォーム。

  • NVIDIA CUDA Toolkit。

  • コンパイラおよびライブラリの環境変数。詳細については、Install and Setup Prerequisites for NVIDIA Boardsを参照してください。

開発用ホスト要件

NVIDIA ターゲット プラットフォームの構成と検証

NVIDIA ハードウェアへの接続

MATLAB Coder Support Package for NVIDIA Jetson and NVIDIA DRIVE™ Platforms は、生成されたコードを Jetson プラットフォームでビルドおよび実行している間、TCP/IP 経由の SSH 接続を使用してコマンドを実行します。ターゲット プラットフォームをホスト コンピューターと同じネットワークに接続します。

NVIDIA ハードウェアと通信するには、関数jetsonを使用してライブ ハードウェア接続オブジェクトを作成します。この例では、最近 Jetson ハードウェアに正常に接続されたときのデバイスのアドレス、ユーザー名、およびパスワードの設定を使用します。

hwobj = jetson;
Checking for CUDA availability on the Target...
Checking for 'nvcc' in the target system path...
Checking for cuDNN library availability on the Target...
Checking for TensorRT library availability on the Target...
Checking for prerequisite libraries is complete.
Gathering hardware details...
Checking for third-party library availability on the Target...
Gathering hardware details is complete.
 Board name              : NVIDIA Jetson Nano Developer Kit
 CUDA Version            : 10.2
 cuDNN Version           : 8.2
 TensorRT Version        : 8.2
 GStreamer Version       : 1.14.5
 V4L2 Version            : 1.14.2-1
 SDL Version             : 1.2
 OpenCV Version          : 4.1.1
 Available Webcams       :  
 Available GPUs          : NVIDIA Tegra X1
 Available Digital Pins  : 7  11  12  13  15  16  18  19  21  22  23  24  26  29  31  32  33  35  36  37  38  40

PIL シミュレーションの構成

この例では、プロセッサインザループ (PIL) シミュレーションを使用して、生成された C++ コードおよび CUDA コードを Jetson ボード上でテストします。入力データの転送とアルゴリズムの計算には時間がかかるため、既定の PIL タイムアウト値を変更して、タイムアウト エラーを回避するようにします。

setPILTimeout(hwobj,100);

GPU 環境の検証

この例を実行するのに必要なコンパイラおよびライブラリが正しく設定されていることを検証するために、関数coder.checkGpuInstallを使用します。

envCfg = coder.gpuEnvConfig('jetson');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
envCfg.HardwareObject = hwobj;
coder.checkGpuInstall(envCfg);

coder.gpuEnvConfig オブジェクトの Quiet プロパティが true に設定されている場合、関数 coder.checkGpuInstall は警告メッセージまたはエラー メッセージのみを返します。

コード生成パラメーターの構成

Jetson ボード上の ARM® CPU で実行される PIL 実行可能ファイルを生成するには、スタティック ライブラリ用のcoder.EmbeddedCodeConfigオブジェクトを作成します。

cfgCpu = coder.config('lib');

生成されたコードのターゲット言語を C++ に設定し、コード構成オブジェクトで PIL の実行を有効にします。次に、PIL を実行するときに実行時プロファイリングを有効にします。実行時プロファイリングでは、生成コードでタスクと関数のメトリクスを生成します。詳細については、Create Execution-Time Profile for Generated Code (Embedded Coder)を参照してください。最後に、Jetson プラットフォーム用のcoder.hardwareオブジェクトを作成し、それをコード構成オブジェクトの Hardware プロパティに割り当てます。

cfgCpu.TargetLang = 'C++';
cfgCpu.VerificationMode = 'PIL';
cfgCpu.CodeExecutionProfiling = true;
cfgCpu.Hardware = coder.hardware('NVIDIA Jetson');

同様に、coder.gpuConfigを使用して、Jetson ボード上に CUDA GPU 用の構成パラメーターを作成します。

cfgGpu = coder.gpuConfig('lib');
cfgGpu.VerificationMode = 'PIL';
cfgGpu.CodeExecutionProfiling = true;
cfgGpu.Hardware = coder.hardware('NVIDIA Jetson');

エントリポイント関数 segmentGroundAndObstacles

エントリポイント関数 segmentGroundAndObstacles は、入力点群の位置からグランド プレーン、エゴ ビークル、および近くの障害物に属する点をセグメント化します。次の図は、エントリポイント関数に実装されているアルゴリズムを示しています。詳細については、LiDAR を使用した地面および障害物の検出 (Automated Driving Toolbox)を参照してください。

segmentGroundAndObstacles.png

type segmentGroundAndObstacles
function [egoPoints, groundPoints, obstaclePoints] = ...
    segmentGroundAndObstacles(ptCloudLocation)
%segmentGroundAndObstacles segments ground and nearby obstacle points from 
%the pointcloud.
%   egoPoints = segmentGroundAndObstacles(ptCloudLocation) segments the 
%   ground points and nearby obstacle points from the input argument 
%   ptCloudLocation. egoPoints are the vehicle points segmented with the 
%   help of helper function helperSegmentEgoFromLidarData. 
%   segmentGroundFromLidarData is used to segment the ground points, 
%   groundPoints. findNeighborsInRadius is used to segment the nearby 
%   obstacles, obstaclePoints.
%
%   [..., groundPoints, obstaclePoints] = segmentGroundAndObstacles(...)
%   additionally returns segmented groundPoints and obstaclePoints.

%   Copyright 2021 The MathWorks, Inc.

%#codegen

% GPU Coder pragma
coder.gpu.kernelfun;

% Create a pointCloud object for point cloud locations
ptCloud = pointCloud(ptCloudLocation);

%% Segment the Ego Vehicle
% The lidar is mounted on top of the vehicle, and the point cloud may 
% contain points belonging to the vehicle itself, such as on the roof or 
% hood. Knowing the dimensions of the vehicle, we can segment out points 
% that are closest to the vehicle.

% Create a vehicleDimensions object for storing dimensions of the vehicle.
% Typical vehicle 4.7m by 1.8m by 1.4m

vehicleDims.Length = 4.7;
vehicleDims.Width = 1.8;
vehicleDims.Height = 1.4;
vehicleDims.RearOverhang = 1.0;

% Specify the mounting location of the lidar in the vehicle coordinate 
% system. The vehicle coordinate system is centered at the center of the 
% rear-axle, on the ground, with positive X direction pointing forward, 
% positive Y towards the left, and positive Z upwards. In this example, 
% the lidar is mounted on the top center of the vehicle, parallel to the 
% ground.

mountLocation = [...
    vehicleDims.Length/2 - vehicleDims.RearOverhang, ... % x
    0, ...                                               % y
    vehicleDims.Height];                                 % z

% Segment the ego vehicle using the helper function 
% |helperSegmentEgoFromLidarData|. This function segments all points within
% the cuboid defined by the ego vehicle. 

egoPoints = helperSegmentEgoFromLidarData(ptCloudLocation, vehicleDims, mountLocation);

%% Segment Ground Plane and Nearby Obstacles
% In order to identify obstacles from the lidar data, first segment the 
% ground plane using the segmentGroundFromLidarData function to accomplish 
% this. This function segments points belonging to the ground from organized 
% lidar data.

elevationDelta = 10;
groundPoints = segmentGroundFromLidarData(ptCloud, 'ElevationAngleDelta', elevationDelta);

% Remove points belonging to the ego vehicle and the ground plane by using 
% the select function on the point cloud. Specify the 'OutputSize' as 
% 'full' to retain the organized nature of the point cloud. For CUDA code 
% generation, an optimized approach for removing the points is considered.

nonEgoGroundPoints = coder.nullcopy(egoPoints);
coder.gpu.kernel;
for iter = 1:numel(egoPoints)
    nonEgoGroundPoints(iter) = ~egoPoints(iter) & ~groundPoints(iter);
end

ptCloudSegmented = select(ptCloud, nonEgoGroundPoints, 'OutputSize', 'full');

% Next, segment nearby obstacles by looking for all points that are not 
% part of the ground or ego vehicle within some radius from the ego
% vehicle. This radius can be determined based on the range of the lidar 
% and area of interest for further processing.

sensorLocation  = [0, 0, 0]; % Sensor is at the center of the coordinate system
radius          = 40; % meters

obstaclePoints = findNeighborsInRadius(ptCloudSegmented, sensorLocation, radius);

end


function egoPoints = helperSegmentEgoFromLidarData(ptCloudLocation, vehicleDims, mountLocation)
%#codegen
%helperSegmentEgoFromLidarData segment ego vehicle points from lidar data
%   egoPoints = helperSegmentEgoFromLidarData(ptCloudLocation,vehicleDims,mountLocation)
%   segments points belonging to the ego vehicle of dimensions vehicleDims
%   from the lidar scan ptCloud. The lidar is mounted at location specified
%   by mountLocation in the vehicle coordinate system. ptCloud is a
%   pointCloud object. vehicleDimensions is a vehicleDimensions object.
%   mountLocation is a 3-element vector specifying XYZ location of the
%   lidar in the vehicle coordinate system.
%
%   This function assumes that the lidar is mounted parallel to the ground
%   plane, with positive X direction pointing ahead of the vehicle,
%   positive Y direction pointing to the left of the vehicle in a
%   right-handed system.

% GPU Coder pragma
coder.gpu.kernelfun;

% Buffer around ego vehicle 
bufferZone = [0.1, 0.1, 0.1]; % in meters

% Define ego vehicle limits in vehicle coordinates
egoXMin = -vehicleDims.RearOverhang - bufferZone(1);
egoXMax = egoXMin + vehicleDims.Length + bufferZone(1);
egoYMin = -vehicleDims.Width/2 - bufferZone(2);
egoYMax = egoYMin + vehicleDims.Width + bufferZone(2);
egoZMin = 0 - bufferZone(3);
egoZMax = egoZMin + vehicleDims.Height + bufferZone(3);

egoXLimits = [egoXMin, egoXMax];
egoYLimits = [egoYMin, egoYMax];
egoZLimits = [egoZMin, egoZMax];

% Transform to lidar coordinates
egoXLimits = egoXLimits - mountLocation(1);
egoYLimits = egoYLimits - mountLocation(2);
egoZLimits = egoZLimits - mountLocation(3);

% Use logical indexing to select points inside ego vehicle cube
egoPoints = ptCloudLocation(:,:,1) > egoXLimits(1) ...
    & ptCloudLocation(:,:,1) < egoXLimits(2) ...
    & ptCloudLocation(:,:,2) > egoYLimits(1) ...
    & ptCloudLocation(:,:,2) < egoYLimits(2) ...
    & ptCloudLocation(:,:,3) > egoZLimits(1) ...
    & ptCloudLocation(:,:,3) < egoZLimits(2);
end

ターゲットでの実行可能ファイルの生成と実行

LIDAR センサーからの点群データのサイズは 32 x 1100 x 3 です。ただし、2 番目の次元に対して上限の 1100 を使用すると、信号の欠落やノイズのために点群データから一部の点が除外される可能性があります。関数 coder.typeof を使用して、2 番目の次元の変数次元を指定します。

codegenArgs = {coder.typeof(single(0),[32,1100,3],[0,1,0])};

C++ 実行可能ファイルの生成と実行

CPU コード構成オブジェクト cfgCpu を使用して C++ コードを生成します。

codegen -config cfgCpu -args codegenArgs segmentGroundAndObstacles -report
### Connectivity configuration for function 'segmentGroundAndObstacles': 'NVIDIA Jetson'
PIL execution is using Port 17725.
PIL execution is using 100 Sec(s) for receive time-out.
Code generation successful: View report

補助関数 obstacleDetectionWrapper は、ストリーミング LiDAR 入力データをフレームごとに処理し、PIL 実行可能ファイルを呼び出し、グランド プレーン、エゴ ビークル、および近くの障害物に属する点をセグメント化した 3 次元点群を表示する実行ラッパーです。この例で使用されている LIDAR データは、車両に取り付けられた Velodyne HDL32E センサーを使用して記録されました。関数 obstacleDetectionWrapper によって実行される処理の説明については、LiDAR を使用した地面および障害物の検出 (Automated Driving Toolbox)を参照してください。

obstacleDetectionWrapper();
### Starting application: 'codegen/lib/segmentGroundAndObstacles/pil/segmentGroundAndObstacles.elf'
    To terminate execution: clear segmentGroundAndObstacles_pil
### Launching application segmentGroundAndObstacles.elf...
    Execution profiling data is available for viewing. Open Simulation Data Inspector.
    Execution profiling report will be available after termination.

cpuFrame.png

C++ 実行可能ファイルの実行プロファイルの収集

PIL 実行可能ファイルをクリアし、関数 getCoderExecutionProfile を使用して実行時間プロファイルを収集します。

clear segmentGroundAndObstacles_pil;
Runtime log on Target:
[sudo] password for ubuntu: 
PIL execution terminated on target.
    Execution profiling report: coder.profile.show(getCoderExecutionProfile('segmentGroundAndObstacles'))
cpuExecutionProfile = getCoderExecutionProfile('segmentGroundAndObstacles');

CUDA 実行可能ファイルの生成と実行

GPU コード構成オブジェクト cfgGpu を使用して CUDA コードを生成します。

codegen -config cfgGpu -args codegenArgs segmentGroundAndObstacles -report
### Connectivity configuration for function 'segmentGroundAndObstacles': 'NVIDIA Jetson'
PIL execution is using Port 17725.
PIL execution is using 100 Sec(s) for receive time-out.
Code generation successful: View report

GPU のパフォーマンスを最大化するには、ボード上で jetson_clocks.sh スクリプトを使用します。詳細については、NVIDIA Xavier - Maximizing Performance (RidgeRun wiki) を参照してください。

ClockFileStatus = system(hwobj, 'test -f l4t_dfs.conf && echo "1" || echo "0"');
if ~str2double(ClockFileStatus)
    system(hwobj,'echo "ubuntu" | sudo -S jetson_clocks --store | printf "y"');
end
system(hwobj,'echo "ubuntu" | sudo -S jetson_clocks');

補助関数 obstacleDetectionWrapper を使用して、ストリーミング LiDAR 入力データを処理し、PIL 実行可能ファイルを呼び出し、グランド プレーン、エゴ ビークル、および近くの障害物に属する点をセグメント化した 3 次元点群を表示します。

obstacleDetectionWrapper();
### Starting application: 'codegen/lib/segmentGroundAndObstacles/pil/segmentGroundAndObstacles.elf'
    To terminate execution: clear segmentGroundAndObstacles_pil
### Launching application segmentGroundAndObstacles.elf...
    Execution profiling data is available for viewing. Open Simulation Data Inspector.
    Execution profiling report will be available after termination.

gpuFrame.png

CUDA 実行可能ファイルの実行プロファイルの収集

Jetson クロック設定を無効にし、PIL 実行可能ファイルをクリアし、関数 getCoderExecutionProfile を使用して実行時間プロファイルを収集します。

system(hwobj,'echo "ubuntu" | sudo -S jetson_clocks --restore');
clear segmentGroundAndObstacles_pil;
Runtime log on Target:
[sudo] password for ubuntu: 
PIL execution terminated on target.
    Execution profiling report: coder.profile.show(getCoderExecutionProfile('segmentGroundAndObstacles'))
gpuExecutionProfile = getCoderExecutionProfile('segmentGroundAndObstacles');

CPU および GPU の実行プロファイルの解析

ExecutionTimeInSeconds プロパティを使用して、実行プロファイルから CPU と GPU のフレームごとの実行時間を取得します。

[~,cpuExecTimePerFrame,~] = cpuExecutionProfile.Sections.ExecutionTimeInSeconds;
[~,gpuExecTimePerFrame,~] = gpuExecutionProfile.Sections.ExecutionTimeInSeconds;

フレームごとの実行時間をプロットするには、次のコードを使用します。

figure;
% Plot CPU execution times.
plot(cpuExecTimePerFrame(2:end)*1000,'r');
hold on;
% Plot GPU execution times.
plot(gpuExecTimePerFrame(2:end)*1000,'b');
grid on;
% Set the title, legend and labels.
title('CPU vs GPU Per-frame Execution times (in ms)');
legend('CPU Timings', 'GPU Timings');
axis([0,40,0,40]);
xlabel('Frame Number');
ylabel('Execution Time (in ms)');

これらの数値は代表的なものです。実際の値はハードウェアの機能によって異なります。

サポート関数

補助関数 obstacleDetectionWrapper は、LiDAR データを処理し、PIL 実行可能ファイルを呼び出し、結果を可視化します。関数 obstacleDetectionWrapper によって実行される処理の説明については、LiDAR を使用した地面および障害物の検出 (Automated Driving Toolbox)を参照してください。

function obstacleDetectionWrapper()
%OBSTACLEDETECTIONWRAPPER process lidar data and visualize results
%   The OBSTACLEDETECTIONWRAPPER is an execution wrapper function that
%   processess the streaming lidar input data frame-by-frame, calls the PIL
%   executable, and displays the 3-D point cloud with segmenting points
%   belonging to the ground plane, the ego vehicle, and nearby obstacles.

fileName    = 'lidarData_ConstructionRoad.pcap';
deviceModel = 'HDL32E';
veloReader = velodyneFileReader(fileName, deviceModel);

% Setup Streaming Point Cloud Display
xlimits = [-25 45];   % Limits of point cloud display, meters
ylimits = [-25 45];
zlimits = [-20 20];

% Create a pcplayer
lidarViewer = pcplayer(xlimits, ylimits, zlimits);
xlabel(lidarViewer.Axes, 'X (m)')
ylabel(lidarViewer.Axes, 'Y (m)')
zlabel(lidarViewer.Axes, 'Z (m)')

% Set the colormap for labeling the ground plane, ego vehicle, and nearby
% obstacles.
colorLabels = [...
    0      0.4470 0.7410; ... % Unlabeled points, specified as [R,G,B]
    0.4660 0.6740 0.1880; ... % Ground points
    0.9290 0.6940 0.1250; ... % Ego points
    0.6350 0.0780 0.1840];    % Obstacle points

% Define indices for each label
colors.Unlabeled = 1;
colors.Ground    = 2;
colors.Ego       = 3;
colors.Obstacle  = 4;

% Set the colormap
colormap(lidarViewer.Axes, colorLabels)

% Stop processing the frame after specified time.
stopTime = veloReader.EndTime;

i = 1;
isPlayerOpen = true;
while hasFrame(veloReader) && veloReader.CurrentTime < stopTime && isPlayerOpen

    % Grab the next lidar scan
    ptCloud = readFrame(veloReader);

    % Segment points belonging to the ego vehicle
    [egoPoints,groundPoints,obstaclePoints] = segmentGroundAndObstacles_pil(ptCloud.Location);

    i = i+1;
    closePlayer = ~hasFrame(veloReader);

    % Update lidar display
    points = struct('EgoPoints',egoPoints, 'GroundPoints',groundPoints, ...
        'ObstaclePoints',obstaclePoints);
    isPlayerOpen = helperUpdateView(lidarViewer, ptCloud, points, colors, closePlayer);
end
snapnow
end

補助関数 helperUpdateView は、最新の点群および関連付けられたカラー ラベルを使用して、ストリーミング点群表示を更新します。

function isPlayerOpen = helperUpdateView(lidarViewer,ptCloud,points,colors,closePlayer)
%HELPERUPDATEVIEW update streaming point cloud display
%   isPlayerOpen =
%   HELPERUPDATEVIEW(lidarViewer,ptCloud,points,colors,closePlayers)
%   updates the pcplayer object specified in lidarViewer with a new point
%   cloud ptCloud. Points specified in the struct points are colored
%   according to the colormap of lidarViewer using the labels specified by
%   the struct colors. closePlayer is a flag indicating whether to close
%   the lidarViewer.

if closePlayer
    hide(lidarViewer);
    isPlayerOpen = false;
    return;
end

scanSize = size(ptCloud.Location);
scanSize = scanSize(1:2);

% Initialize colormap
colormapValues = ones(scanSize, 'like', ptCloud.Location) * colors.Unlabeled;

if isfield(points, 'GroundPoints')
    colormapValues(points.GroundPoints) = colors.Ground;
end

if isfield(points, 'EgoPoints')
    colormapValues(points.EgoPoints) = colors.Ego;
end

if isfield(points, 'ObstaclePoints')
    colormapValues(points.ObstaclePoints) = colors.Obstacle;
end

% Update view
view(lidarViewer, ptCloud.Location, colormapValues)

% Check if player is open
isPlayerOpen = isOpen(lidarViewer);

end

参考

オブジェクト

関連するトピック