作者 | 点云PCL
点击下方卡片,关注“自动驾驶之心”公众号
ADAS巨卷干货,即可获取
点击进入→自动驾驶之心【点云处理】技术交流群
本文只做学术分享,如有侵权,联系删文
前言
在这篇文章将介绍如何使用CUDA-PCL处理点云来获得最佳性能,由于PCL无法充分利用Jetson上的CUDA,NVIDIA开发了一些具有与PCL相同功能的基于CUDA的库。代码地址:https://github.com/NVIDIA-AI-IOT/cuPCL.git(只有动态库和头文件,作者说源码将在未来开源)。
cuPCL包含一些用于使用CUDA处理点云的库,以及用于它们的使用示例。项目中有几个子文件夹,每个子文件夹都包含:由CUDA实现的库以及库用法并通过将其输出与PCL的输出进行比较来检查性能和准确性的示例代码,该库支持Xavier、Orin和Linux x86。
作者介绍
Lei Fan,NVIDIA的高级CUDA软件工程师。他目前与NVIDIA的中国技术支持工程师团队合作,开发通过CUDA优化软件性能的解决方案。
Lily Li,正在NVIDIA的机器人团队担任开发者关系工作。她目前致力于开发Jetson生态系统中的机器人解决方案,以帮助创建最佳实践。
主要内容
许多Jetson用户选择激光雷达用于定位和感知的主要传感器,激光雷达将车辆周围的空间环境描述为一组三维点,称为点云,点云对周围对象的表面进行采样,具有远距离和高精度的特点,非常适合用于高级障碍物感知、地图制作、定位和规划算法。
在这篇文章中介绍了CUDA-PCL 1.0,其这里主要介绍三个CUDA加速的PCL库:
1.CUDA-ICP
2.CUDA-Segmentation
3.CUDA-Filter
CUDA-ICP
在迭代最近点(ICP)中,一个点云目标或参考点云被固定,而源点被变换以最佳匹配参考点。该算法迭代地计算所需的变换矩阵,以最小化误差度量,这通常是来自源点到参考点云的距离,例如匹配对坐标之间的平方差之和。ICP是在给定刚性变换所需的初始猜测位姿的情况下对齐三维模型的广泛使用的算法之一。ICP的优点包括高精度匹配结果,对不同初始化具有强鲁棒性等。然而它消耗大量计算资源。为了改进Jetson上的ICP性能,NVIDIA发布了基于CUDA的ICP,它可以替代点云库(PCL)中的原始ICP版本。以下代码示例是CUDA-ICP示例。可以实例化该类,然后直接执行cudaICP.icp()。
cudaICP icpTest(nPCountM, nQCountM, stream);
icpTest.icp(cloud_source, nPCount,
float *cloud_target, int nQCount,
int Maxiterate, double threshold,
Eigen::Matrix4f &transformation_matrix, stream);
ICP计算两个点云之间的变换矩阵:
source(P)* transformation =target(Q)
因为激光雷达提供了具有固定数量的点云,我们可以得到最大点数。nPCountM和nQCountM都用于为ICP分配缓存。
class cudaICP
{
public:
/* nPCountM and nQCountM are the maximum of count for input clouds.
They are used to pre-allocate memory.
*/
cudaICP(int nPCountM, int nQCountM, cudaStream_t stream = 0);
~cudaICP(void);
/*
cloud_target = transformation_matrix *cloud_source
When the Epsilon of the transformation_matrix is less than threshold,
the function returns transformation_matrix.
Input:
cloud_source, cloud_target: Data pointer for the point cloud.
nPCount: Point number of the cloud_source.
nQCount: Point number of the cloud_target.
Maxiterate: Threshold for iterations.
threshold: When the Epsilon of the transformation_matrix is less than
threshold, the function returns transformation_matrix.
Output:
transformation_matrix
*/
void icp(float *cloud_source, int nPCount,
float *cloud_target, int nQCount,
int Maxiterate, double threshold,
Eigen::Matrix4f &transformation_matrix,
cudaStream_t stream = 0);
void *m_handle = NULL;
};
表2展示了CUDA-ICP与PCL-ICP的性能对比结果
在ICP之前两帧点云的状态
在ICP之后两帧点云的状态
CUDA-Segmentation
点云地图包含许多地面点,这不仅使整个地图看起来凌乱,还给后续障碍点云的分类、识别和跟踪带来了麻烦,因此需要首先将其删除。通过点云分割可以实现去除地面。该库使用随机抽样一致性(Ransac)拟合和非线性优化来实现这一目标。以下是CUDA-Segmentation的示例代码。首先实例化该类,初始化参数,然后直接调用 cudaSeg.segment 来执行地面去除。
//Now Just support: SAC_RANSAC + SACMODEL_PLANE
std::vectorindexV;
cudaSegmentation cudaSeg(SACMODEL_PLANE, SAC_RANSAC, stream);
segParam_t setP;
setP.distanceThreshold = 0.01;
setP.maxIterations = 50;
setP.probability = 0.99;
setP.optimizeCoefficients = true;
cudaSeg.set(setP);
cudaSeg.segment(input, nCount, index, modelCoefficients);
for(int i = 0; i < nCount; i++)
{
if(index[i] == 1)
indexV.push_back(i);
}
CUDA-Segmentation对具有nCount点数的输入进行分割,使用一些参数,index是输入的索引,代表目标平面,而modelCoefficients是平面的系数组。
typedef struct {
double distanceThreshold;
int maxIterations;
double probability;
bool optimizeCoefficients;
} segParam_t;
class cudaSegmentation
{
public:
//Now Just support: SAC_RANSAC + SACMODEL_PLANE
cudaSegmentation(int ModelType, int MethodType, cudaStream_t stream = 0);
~cudaSegmentation(void);
/*
Input:
cloud_in: Data pointer for point cloud
nCount: Count of points in cloud_in
Output:
Index: Data pointer that has the index of points in a plane from input
modelCoefficients: Data pointer that has the group of coefficients of the plane
*/
int set(segParam_t param);
void segment(float *cloud_in, int nCount,
int *index, float *modelCoefficients);
private:
void *m_handle = NULL;
};
表3.展示了CUDA-Segmentation与PCL-Segmentation的性能对比。
图3和图4显示了原始点云数据,然后是仅保留障碍相关点云的处理版本。这个示例在点云处理中很典型,包括去除地面,删除一些点云和提取特征,以及对一些点云进行聚类。
图3. CUDA-Segmentation的原始点云
图4. 由CUDA-Segmentation处理的点云
CUDA-Filter
在点云进行分割、检测、识别等处理之前,滤波是最重要的预处理操作之一。通过滤波可以实现对点云的坐标约束,直接过滤点云的X、Y和Z轴,点云过滤可以仅对Z轴或三个坐标轴X、Y和Z进行约束。CUDA-Filter目前仅支持PassThrough,但以后将支持更多的方法。以下是CUDA-Filter示例的代码示例,创建该类的实例,初始化参数,然后直接调用cudaFilter.filter函数。
cudaFilter filterTest(stream);
FilterParam_t setP;
FilterType_t type = PASSTHROUGH;
setP.type = type;
setP.dim = 2;
setP.upFilterLimits = 1.0;
setP.downFilterLimits = 0.0;
setP.limitsNegative = false;
filterTest.set(setP);
filterTest.filter(output, &countLeft, input, nCount);
CUDA-Filter使用参数筛选输入的nCount个点,然后通过CUDA过滤后,输出具有countLeft个点的结果。
typedef struct {
FilterType_t type;
//0=x,1=y,2=z
int dim;
float upFilterLimits;
float downFilterLimits;
bool limitsNegative;
} FilterParam_t;
class cudaFilter
{
public:
cudaFilter(cudaStream_t stream = 0);
~cudaFilter(void);
int set(FilterParam_t param);
/*
Input:
source: data pointer for point cloud
nCount: count of points in cloud_in
Output:
output: data pointer which has points filtered by CUDA
countLeft: count of points in output
*/
int filter(void *output, unsigned int *countLeft, void *source, unsigned int nCount);
void *m_handle = NULL;
};
表4.展示了CUDA-Filter与PCL-Filter性能比较。
图5和图6显示了通过在X轴上进行约束的PassThrough滤波器示例。
图6. 原始点云。
图6. 通过在X轴上进行约束滤波的点云。
其他模块对比
VoxelGrid
cuOctree
cuCluster
cuNDT
相关链接
https://github.com/NVIDIA-AI-IOT/cuPCL.git
https://developer.nvidia.com/blog/accelerating-lidar-for-robotics-with-cuda-based-pcl/
https://developer.nvidia.com/blog/detecting-objects-in-point-clouds-with-cuda-pointpillars/
① 全网独家视频课程
BEV感知、毫米波雷达视觉融合、多传感器标定、多传感器融合、多模态3D目标检测、点云3D目标检测、目标跟踪、Occupancy、cuda与TensorRT模型部署、协同感知、语义分割、自动驾驶仿真、传感器部署、决策规划、轨迹预测等多个方向学习视频(扫码即可学习)

② 国内首个自动驾驶学习社区
近2000人的交流社区,涉及30+自动驾驶技术栈学习路线,想要了解更多自动驾驶感知(2D检测、分割、2D/3D车道线、BEV感知、3D目标检测、Occupancy、多传感器融合、多传感器标定、目标跟踪、光流估计)、自动驾驶定位建图(SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案、AI模型部署落地实战、行业动态、岗位发布,欢迎扫描下方二维码,加入自动驾驶之心知识星球,这是一个真正有干货的地方,与领域大佬交流入门、学习、工作、跳槽上的各类难题,日常分享论文+代码+视频,期待交流!

③【自动驾驶之心】技术交流群
自动驾驶之心是首个自动驾驶开发者社区,聚焦目标检测、语义分割、全景分割、实例分割、关键点检测、车道线、目标跟踪、3D目标检测、BEV感知、多模态感知、Occupancy、多传感器融合、transformer、大模型、点云处理、端到端自动驾驶、SLAM、光流估计、深度估计、轨迹预测、高精地图、NeRF、规划控制、模型部署落地、自动驾驶仿真测试、产品经理、硬件配置、AI求职交流等方向。扫码添加汽车人助理微信邀请入群,备注:学校/公司+方向+昵称(快速入群方式)
④【自动驾驶之心】平台矩阵,欢迎联系我们!