[代码实践] 自动驾驶中常见的激光雷达点云去噪处理 您所在的位置:网站首页 激光雷达点云处理 [代码实践] 自动驾驶中常见的激光雷达点云去噪处理

[代码实践] 自动驾驶中常见的激光雷达点云去噪处理

2024-04-17 19:40| 来源: 网络整理| 查看: 265

简介

在 [代码实践] 自动驾驶中常见的激光雷达点云滤波处理 中整理了自动驾驶常用的几种降低点云规模的预处理方法,那些方法除了能够去除无关区域的点以及降低点云规模以外,或多或少也能起到点云去噪的作用。这篇博客尝试整理一下基于激光雷达常见的点云去噪方法。博客用到的代码在:noise_removal

基于点云分布密度进行去噪

第一个直观的感受是可以根据点云中每个点所在区域判断其是否是噪声,一般来说噪声点所在区域都比较稀疏。

PCL 自带滤波器

PCL 内部包含了一个 RadiusOutlierRemoval 可以进行此种方法滤波,使用时需要设定每个点的搜索半径,已经需要满足的最少邻近点数,使用方法如下:

pcl::RadiusOutlierRemoval::Ptr radius_outlier_removal( new pcl::RadiusOutlierRemoval(true)); radius_outlier_removal->setInputCloud(cloud_ptr); radius_outlier_removal->setRadiusSearch(1.0); radius_outlier_removal->setMinNeighborsInRadius(10); radius_outlier_removal->filter(*filtered_cloud_ptr); 自己实现版本

函数比较简单,自己实现起来也比较方便,只需要构建一个 KD 树,对每个点进行范围搜索(最近邻搜索),最后判断邻近点数(最大距离)是否满足要求即可,如下所示:

template void radius_outlier_removal(boost::shared_ptr& src_cloud_ptr, boost::shared_ptr& dst_cloud_ptr, double search_radius, int min_neighbors, boost::shared_ptr removed_indices = nullptr) { boost::shared_ptr kdtree(new pcl::KdTreeFLANN()); kdtree->setInputCloud(src_cloud_ptr); boost::shared_ptr cloud_ptr(new pcl::PointCloud()); cloud_ptr->points.reserve(src_cloud_ptr->points.size()); if (removed_indices) removed_indices->reserve(src_cloud_ptr->points.size()); std::vector neighbors(min_neighbors); std::vector distances(min_neighbors); for (size_t i = 0; i points.size(); ++i) { if (kdtree->nearestKSearch(i, min_neighbors, neighbors, distances) > 0 && distances.back() * distances.back() points.push_back(src_cloud_ptr->points[i]); } else { if (removed_indices) removed_indices->push_back(i); } } }

这里我用的是 PCL 的 KD 树实现,但是它的范围搜索速度比最近邻搜索速度慢很多,目前没有看实现细节所以不太清楚原因,因此这里使用最近邻搜索来进行搜索。在使用时可以考虑使用其他的 KD 树实现。

效果

两种方法的效果差不多,如下所示。红色的点表示被去除的点,可以看到,除了一些很明显的噪声点被去除以外,由于激光雷达点云的特性,较远处分布比较稀疏的点也被去除了。

基于点云所在区域分布规律进行去噪

除了可以根据点云中每个点所在区域是否足够稠密的情况下,还可以根据该点与其他周围点能否形成一个较好的分布来作为筛选标准。具体来说,针对每个点计算其到周围若干点的平均距离,如果这个平均距离相对于整体点云中所有点的平均距离较近,则认为其不是噪点。

PCL 自带滤波器及源码

同样,PCL 提供了相关滤波器的实现,首先来看一下使用方法:

// PCL built-in radius removal pcl::StatisticalOutlierRemoval::Ptr statistical_outlier_removal( new pcl::StatisticalOutlierRemoval(true)); // set to true if we want to extract removed indices statistical_outlier_removal->setInputCloud(cloud_ptr); statistical_outlier_removal->setMeanK(20); statistical_outlier_removal->setStddevMulThresh(2.0); statistical_outlier_removal->filter(*filtered_cloud_ptr);

由于实现过程和 RadiusOutlierRemoval 大同小异,都是对每个点搜索 k 个最近点,只是在判断标准有些许不一样,所以这次没有自己实现,而是直接来看一下 PCL 是怎么进行距离判断的,以下为源码的核心部分:

template void pcl::StatisticalOutlierRemoval::applyFilterIndices (std::vector &indices) { // Initialize the search class -- 初始化需要用到的搜索树结构 if (!searcher_) { if (input_->isOrganized ()) searcher_.reset (new pcl::search::OrganizedNeighbor ()); else searcher_.reset (new pcl::search::KdTree (false)); } searcher_->setInputCloud (input_); // The arrays to be used -- 提前分配搜索结果需要的空间 std::vector nn_indices (mean_k_); std::vector nn_dists (mean_k_); std::vector distances (indices_->size ()); indices.resize (indices_->size ()); removed_indices_->resize (indices_->size ()); int oii = 0, rii = 0; // oii = output indices iterator, rii = removed indices iterator // First pass: Compute the mean distances for all points with respect to their k nearest neighbors // 一次遍历,对每个点搜索到附近若干个点的平均距离 int valid_distances = 0; for (int iii = 0; iii size ()); ++iii) // iii = input indices iterator { if (!pcl_isfinite (input_->points[(*indices_)[iii]].x) || !pcl_isfinite (input_->points[(*indices_)[iii]].y) || !pcl_isfinite (input_->points[(*indices_)[iii]].z)) { distances[iii] = 0.0; continue; } // Perform the nearest k search if (searcher_->nearestKSearch ((*indices_)[iii], mean_k_ + 1, nn_indices, nn_dists) == 0) { distances[iii] = 0.0; PCL_WARN ("[pcl::%s::applyFilter] Searching for the closest %d neighbors failed.\n", getClassName ().c_str (), mean_k_); continue; } // Calculate the mean distance to its neighbors double dist_sum = 0.0; for (int k = 1; k


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有