Cartographer源码阅读2D&3D-前端体素滤波-VoxelFilter

本文介绍Cartographer SLAM系统中的2D前端VoxelFilter模块。该模块通过体素滤波去除重复点云数据,提高处理效率。文章详细解析了其核心算法原理,包括如何使用32位整数高效表示体素位置,以及如何利用哈希集合快速判断点云是否已存在于当前体素中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Cartographer源码阅读-2D前端体素滤波-VoxelFilter

基本原理:
将xyz分别除以分辨率,并取整,得到xyz所在cell的id(x,y,z),再将该三个整数分别放入3*32个字节的数中(很好的技巧,避免了大量的遍历和比较),插入voxelFileter::voxel_set_中,后面如果是第一次插入该数,则保留该点,如果之前存在该数,即意味着有其他点落在该网格内,不再保留该点。

class VoxelFilter {
 public:
  // 'size' is the length of a voxel edge.
  // 构造函数传入体素大小
  explicit VoxelFilter(float size) : resolution_(size) {}

  VoxelFilter(const VoxelFilter&) = delete;
  VoxelFilter& operator=(const VoxelFilter&) = delete;

  // Returns a voxel filtered copy of 'point_cloud'.
  PointCloud Filter(const PointCloud& point_cloud);

  // Same for TimedPointCloud.
  TimedPointCloud Filter(const TimedPointCloud& timed_point_cloud);

  // Same for RangeMeasurement.
  std::vector<sensor::TimedPointCloudOriginData::RangeMeasurement> Filter(
      const std::vector<sensor::TimedPointCloudOriginData::RangeMeasurement>&
          range_measurements);

 private:
  using KeyType = std::bitset<3 * 32>;

  static KeyType IndexToKey(const Eigen::Array3i& index);

  Eigen::Array3i GetCellIndex(const Eigen::Vector3f& point) const;

  float resolution_;
  std::unordered_set<KeyType> voxel_set_;
};

接口:

PointCloud VoxelFilter::Filter(const PointCloud& point_cloud) {
  PointCloud results;
  for (const Eigen::Vector3f& point : point_cloud) {
    auto it_inserted = voxel_set_.insert(IndexToKey(GetCellIndex(point)));
    // 该体素之前没有没占据过,则插入,否则,不插入
    if (it_inserted.second) {
      results.push_back(point);
    }
  }
  return results;
}

TimedPointCloud VoxelFilter::Filter(const TimedPointCloud& timed_point_cloud) {
  TimedPointCloud results;
  for (const Eigen::Vector4f& point : timed_point_cloud) {
   // 该体素之前没有没占据过,则插入,否则,不插入
    auto it_inserted =
        voxel_set_.insert(IndexToKey(GetCellIndex(point.head<3>())));
    if (it_inserted.second) {
      results.push_back(point);
    }
  }
  return results;
}
// 将cell_index转换为一个32字节的数
VoxelFilter::KeyType VoxelFilter::IndexToKey(const Eigen::Array3i& index) {
  KeyType k_0(static_cast<uint32>(index[0]));
  KeyType k_1(static_cast<uint32>(index[1]));
  KeyType k_2(static_cast<uint32>(index[2]));
  return (k_0 << 2 * 32) | (k_1 << 1 * 32) | k_2;
}
// 3D点除以体素大小,得到所在的cell_index
Eigen::Array3i VoxelFilter::GetCellIndex(const Eigen::Vector3f& point) const {
  Eigen::Array3f index = point.array() / resolution_;
  return Eigen::Array3i(common::RoundToInt(index.x()),
                        common::RoundToInt(index.y()),
                        common::RoundToInt(index.z()));
}
~~
### 使用 k-d 树滤波替代滤波Cartographer 中实现 k-d 树滤波来替换现有的滤波涉及多个方面。当前版本的 Cartographer 主要依赖于 `VoxelFilter` 类来进行点云数据降采样处理[^2]。 为了引入基于 k-d 树的滤波方法,可以考虑以下方案: #### 修改现有代码结构 对于二维局部轨迹构建器中的点云过滤部分,在文件 `local_trajectory_builder_2d.cc` 的函数 `TransformToGravityAlignedFrameAndFilter()` 内部存在对原始点云应用滤波的操作[^3]。具来说就是这段代码: ```cpp return sensor::RangeData{ cropped.origin, sensor::VoxelFilter(cropped.returns, options_.voxel_filter_size()), sensor::VoxelFilter(cropped.misses, options_.voxel_filter_size()) }; ``` 这里可以通过创建一个新的类或修改原有逻辑以支持 k-d 树滤波算法代替原有的 `sensor::VoxelFilter` 调用。 #### 实现新的 K-D Tree Filter 类 定义一个名为 `KDTreeFilter` 的新类用于执行基于 k-d 树的空间下采样操作。此过程可能涉及到 PCL (Point Cloud Library) 库或其他第三方库的支持。下面是一个简单的 C++ 示例展示如何利用 PCL 来完成这项工作: ```cpp #include <pcl/filters/statistical_outlier_removal.h> #include <pcl/kdtree/kdtree_flann.h> class KDTreeFilter { public: explicit KDTreeFilter(double leafSize):leaf_size_(leafSize){} pcl::PointCloud&lt;pcl::PointXYZ>::Ptr Apply(const pcl::PointCloud&lt;pcl::PointXYZ>& input){ pcl::KdTreeFLANN<pcl::PointXYZ> tree; tree.setInputCloud(input.makeShared()); std::vector<int> indices; std::vector<float> distances; auto output = boost::make_shared&lt;pcl::PointCloud&lt;pcl::PointXYZ>>(); for(size_t i=0;i<input.size();i++){ int numNeighbors = tree.radiusSearch(i, leaf_size_,indices,distances); if(numNeighbors==1 || numNeighbors==0){ (*output).push_back(input[i]); } } return output; } private: double leaf_size_; }; ``` 请注意上述例子仅作为概念验证用途,并未完全优化适用于生产环境下的性能需求;实际项目开发时还需要进一步调整和完善。 #### 更新配置选项并集成到系统中 为了让用户能够方便地切换不同的滤波方式,可以在配置参数里增加一项用来指定采用哪种类型的滤波器(即还是 k-d 树)。这通常是在 `.lua` 配置文件内完成设置。例如: ```lua TRAJECTORY_BUILDER.pure_localization_trimmer.min_num_submaps = 2 -- Add this line to choose between filters. TRAJECTORY_BUILDER.use_kdtree_filtering = true/false ``` 最后一步是确保所有调用旧版 `VoxelFilter` 方法的地方都被更新为条件判断语句,以便根据设定的选择不同而加载相应的滤波实例。 通过以上步骤就可以实现在 Cartographer 中使用 k-d 树滤波替代传统的滤波功能了。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值