В этом примере показано, как преобразовать неорганизованные облака точек в организованный формат с помощью сферической проекции.
3D облако пункта лидара обычно представляется как ряд Декартовских координат (x, y, z). Облако точек может также содержать дополнительную информацию, такую как интенсивность и значения RGB. В отличие от распределения пикселей изображения, распределение облака точек лидара обычно разреженное и нерегулярное. Обработка таких разреженных данных неэффективна. Чтобы получить компактное представление, проецируйте облака лидарных точек на сферу, чтобы создать плотное, основанное на сетке представление, известное как организованное представление [1]. Дополнительные сведения о различиях между организованными и неорганизованными облаками точек см. в разделе Обзор обработки Lidar. Методы извлечения нулевой плоскости и детектора ключевых точек требуют организованных точечных облаков. Кроме того, необходимо преобразовать облако точек в организованный формат, если требуется использовать большинство сетей сегментации глубокого обучения, включая SqueezeSegV1, SqueezeSegV2, RureNet++ [2] и SalsunNext [3]. Пример использования глубокого обучения с организованным облаком точек см. в разделе Семантическая сегментация облака точек Лидара SqueezeSegV2 примере сети глубокого обучения.
Чтобы преобразовать неорганизованное облако точек в организованный формат с помощью сферической проекции, необходимо указать параметры лидарного датчика, используемого для создания облака точек. Определите параметры, которые требуется указать, с помощью таблицы данных для датчика. Можно указать следующие параметры.
Конфигурация балки - 'uniform«или»gradient'. Укажите 'uniform«если балки имеют равные интервалы». Укажите 'gradientесли пучки на горизонте плотно упакованы, а пучки к верхней и нижней частям поля зрения датчика расположены на большем расстоянии друг от друга.
Вертикальное разрешение - количество каналов в вертикальном направлении, то есть количество лазеров. Типичными значениями являются 32 и 64.
Углы вертикальной балки - угловое положение каждого вертикального канала. Необходимо указать этот параметр, если конфигурация балки 'gradient'.
Вертикальное поле зрения вверх - поле зрения в вертикальном направлении над горизонтом (в градусах).
Вертикальное поле зрения вниз - поле зрения в вертикальном направлении ниже горизонта (в градусах).
Горизонтальное разрешение - количество каналов в горизонтальном направлении. Типичными значениями являются 512 и 1024.
Горизонтальное угловое разрешение - угловое разрешение между каждым каналом вдоль горизонтального направления. Необходимо указать этот параметр, если разрешение по горизонтали не указано в таблице.
Горизонтальное поле зрения - Поле зрения, перекрытое в горизонтальном направлении (в градусах). В большинстве случаев это значение равно 360 градусам.

Считывание облака точек с помощью pcread функция.
fileName = fullfile(matlabroot,'examples','deeplearning_shared','data','ousterLidarDrivingData.pcd'); ptCloud = pcread(fileName);
Проверьте размер облака точек выборки. Если координаты облака точек находятся в форме, M-by-N-by-3, облако точек является организованным облаком точек.
disp(size(ptCloud.Location))
64 1024 3
Преобразование облака точек в неорганизованный формат с помощью removeInvalidPoints функция. Координаты неорганизованного облака точек имеют вид M-by-3.
ptCloudUnOrg = removeInvalidPoints(ptCloud); disp(size(ptCloudUnOrg.Location))
65536 3
Данные облака точек были собраны с датчика Ouster OS1 Gen1. Укажите следующие параметры, заданные в спецификации устройства [4].
vResolution = 64;
hResolution = 1024;
vFOVUp = 16.6;
vFOVDown = -16.6;
hFOV = 360;
beamConfig = 'Uniform';Рассчитайте углы балки в горизонтальном и вертикальном направлениях.
if strcmp(beamConfig,'Uniform') vbeamAngles = linspace(vFOVUp,vFOVDown,vResolution); end hbeamAngles = linspace(0,hFOV,hResolution);
Преобразуйте неорганизованное облако точек в организованный формат с помощью вспомогательной функции ConvertUncedToOrg, определенной в конце примера.
ptCloudOrg = convertUnorgToOrg(ptCloudUnOrg,vResolution,hResolution,vbeamAngles,hbeamAngles);
Отображение канала интенсивности исходных и реконструированных организованных точечных облаков.
figure
montage({uint8(ptCloud.Intensity),uint8(ptCloudOrg.Intensity)});
title("Intensity Channel of Original Point Cloud(Top) vs. Reconstructed Organized Point Cloud(Bottom)")
Отображение исходного организованного облака точек и реконструированного организованного облака точек с помощью helperShowUnorgAndOrgPair вспомогательная функция, присоединенная к этому примеру в качестве вспомогательного файла.
display1 = helperShowUnorgAndOrgPair(); display1.plotLidarScan(ptCloudUnOrg,ptCloudOrg,3.5);

Считывание облака точек с помощью pcread функция.
ptCloudUnOrg = pcread('HDL64LidarData.pcd');Данные облака точек собираются с датчика Velodine HDL-64. Укажите следующие параметры, указанные в техническом описании устройства [5].
vResolution = 64;
hResolution = 1024;
vFOVUp = 2;
vFOVDown = -24.9;
hFOV = 360;
beamConfig = 'Uniform';Рассчитайте углы балки в горизонтальном и вертикальном направлениях.
if strcmp(beamConfig,'Uniform') vbeamAngles = linspace(vFOVUp,vFOVDown,vResolution); end hbeamAngles = linspace(0,hFOV,hResolution);
Преобразуйте неорганизованное облако точек в организованный формат с помощью вспомогательной функции ConvertUningToOrg, определенной в конце примера.
ptCloudOrg = convertUnorgToOrg(ptCloudUnOrg,vResolution,hResolution,vbeamAngles,hbeamAngles);
Отображение канала интенсивности реконструированного организованного облака точек. Замените NaNs нулями и измените размер изображения для улучшения визуализации.
intensityChannel = ptCloudOrg.Intensity;
intensityChannel(isnan(intensityChannel)) = 0;
figure
intensityChannel = imresize(intensityChannel,'Scale',[3 1]);
imshow(intensityChannel);
Отображение исходного организованного облака точек и реконструированного организованного облака точек с помощью helperShowUnorgAndOrgPair вспомогательная функция, присоединенная к этому примеру в качестве вспомогательного файла.
display2 = helperShowUnorgAndOrgPair(); display2.plotLidarScan(ptCloudUnOrg,ptCloudOrg,2.5);

Считывание облака точек с помощью pcread функция. Облако точек получено из [6].
ptCloudUnOrg = pcread('Pandar64LidarData.pcd');Данные облака точек собираются с помощью датчика Pandar-64. Укажите следующие параметры, заданные в спецификации устройства [7].
vResolution = 64;
hAngResolution = 0.2;
hFOV = 360;
beamConfig = 'gradient';Конфигурация балки 'gradient', что означает, что расстояние между балками неравномерно. Задайте значения углов балки в вертикальном направлении, заданные в спецификации.
vbeamAngles = [15.0000 11.0000 8.0000 5.0000 3.0000 2.0000 1.8333 1.6667 1.5000 1.3333 1.1667 1.0000 0.8333 0.6667 ... 0.5000 0.3333 0.1667 0 -0.1667 -0.3333 -0.5000 -0.6667 -0.8333 -1.0000 -1.1667 -1.3333 -1.5000 -1.6667 ... -1.8333 -2.0000 -2.1667 -2.3333 -2.5000 -2.6667 -2.8333 -3.0000 -3.1667 -3.3333 -3.5000 -3.6667 -3.8333 -4.0000 ... -4.1667 -4.3333 -4.5000 -4.6667 -4.8333 -5.0000 -5.1667 -5.3333 -5.5000 -5.6667 -5.8333 -6.0000 -7.0000 -8.0000 ... -9.0000 -10.0000 -11.0000 -12.0000 -13.0000 -14.0000 -19.0000 -25.0000];
Рассчитайте углы горизонтальной балки.
hResolution = round(360/hAngResolution); hbeamAngles = linspace(0,hFOV,hResolution);
Преобразуйте неорганизованное облако точек в организованный формат с помощью вспомогательной функции ConvertUncedToOrg, определенной в конце примера.
ptCloudOrg = convertUnorgToOrg(ptCloudUnOrg,vResolution,hResolution,vbeamAngles,hbeamAngles);
Отображение канала интенсивности реконструированного организованного облака точек. Замените NaNs нулями и измените размер изображения. Использовать histeq для лучшей визуализации.
intensityChannel = ptCloudOrg.Intensity;
intensityChannel(isnan(intensityChannel)) = 0;
figure
intensityChannel = imresize(intensityChannel,'Scale',[3 1]);
histeq(intensityChannel./max(intensityChannel(:)));
Отображение исходного организованного облака точек и реконструированного организованного облака точек с помощью helperShowUnorgAndOrgPair вспомогательная функция, присоединенная к этому примеру в качестве вспомогательного файла.
display3 = helperShowUnorgAndOrgPair(); display3.plotLidarScan(ptCloudUnOrg,ptCloudOrg,4);

Используйте convertUnorgToOrg для поиска пиксельных координат проекционного изображения для всех (x, y, z) точек. Функция выполняет следующие действия.
Рассчитайте углы наклона и рыскания для каждой точки облака точек.
Рассчитайте индексы строк и столбцов для каждой точки на основе угла балки, шага и угла рыскания.
Создание организованного облака точек на основе индексов строк и столбцов.
function ptCloudOrganized = convertUnorgToOrg(ptCloud,vResolution,hResolution,vbeamAngles,hbeamAngles) locations = ptCloud.Location; if ~isempty(ptCloud.Intensity) intensity = ptCloud.Intensity; else intensity = zeros(size(ptCloud.Location,1),1); end % Calculate the radial distance for every point. r = sqrt(locations(:,1).^2 + locations(:,2).^2); r(r==0) = 1e-6; % Calculate the pitch and yaw angles for each point in the point cloud. pitch = atan2d(locations(:,3),r); yaw = atan2d(locations(:,2),locations(:,1)); % Shift the range of the yaw angle from [-pi,pi] to [0,2*pi]. yaw = 180-yaw; % Calculate the row indices for all points based on the bin in which the pitch angle for each point falls into. [~,~,rowIdx] = histcounts(pitch,flip(vbeamAngles)); rowIdx(rowIdx == 0) = NaN; rowIdx = vResolution - rowIdx; % Calculate the column indices for all points based on the bin in which the yaw angle for each point falls into. [~,~,colIdx] = histcounts(yaw,hbeamAngles); colIdx(colIdx == 0) = NaN; % Create a pseudo image and fill in the values with the corresponding location and intensity values. pseduoImage = NaN(vResolution,hResolution,4); for i = 1:size(locations,1) if ~isnan(rowIdx(i,1)) && ~isnan(colIdx(i,1)) pseduoImage(rowIdx(i,1),colIdx(i,1),1) = locations(i,1); pseduoImage(rowIdx(i,1),colIdx(i,1),2) = locations(i,2); pseduoImage(rowIdx(i,1),colIdx(i,1),3) = locations(i,3); pseduoImage(rowIdx(i,1),colIdx(i,1),4) = intensity(i,1); end end % Create a point cloud object from the locations and intensity. if ~isempty(ptCloud.Intensity) ptCloudOrganized = pointCloud(pseduoImage(:,:,1:3), 'Intensity', pseduoImage(:,:,4)); else ptCloudOrganized = pointCloud(pseduoImage(:,:,1:3)); end end
[1] У, Бичэнь, Элвин Вань, Сянъю Юэ и Курт Кеутцер. «SqueezeSeg: сверточные нейронные сети с текущим CRF для сегментации Дорожного Объекта в реальном времени от 3D облака пункта LiDAR». В 2018 году Международная конференция IEEE по робототехнике и автоматизации (ICRA), 1887-93. Брисбен, QLD: IEEE, 2018. https://doi.org/10.1109/ICRA.2018.8462926.
[2] Милиото, Андрес, Игнасио Виццо, Йенс Бехли и Сирил Стахнисс. «RureNet++: быстрая и точная семантическая сегментация LiDAR». В 2019 году IEEE/RSJ Международная конференция по интеллектуальным роботам и системам (IROS), 4213-20. Макао, Китай: IEEE, 2019. https://doi.org/10.1109/IROS40897.2019.8967762.
[3] Кортиньяль, Тьяго, Георгий Целепис и Эрен Эрдал Аксой. «SalsaNext: быстрая, учитывающая неопределенность семантическая сегментация точечных облаков LiDAR для автономного вождения». ArXiv:2003.03653 [Cs], 9 июля 2020 года. https://arxiv.org/abs/2003.03653.
[4] Ким, Джейден. «ПСК - удаленное LiDAR». Доступно 22 декабря 2020 года. https://data.ouster.io/downloads/datasheets/datasheet-gen1-v1p13-os1.pdf.
[5] Велодин Лидар. «HDL-64E Прочный объемный датчик Лидара». Доступно 22 декабря 2020 года. https://velodynelidar.com/products/hdl-64e/.
[6] «Набор открытых наборов данных PandaSet - масштаб». Доступно 22 декабря 2020 года. https://scale.com/open-datasets/pandaset.
[7] «Руководство пользователя Pandar64». Доступно 22 декабря 2020 года. https://hesaiweb2019.blob.core.chinacloudapi.cn/uploads/Pandar64_User's_Manual.pdf.