第2章:点云数据结构 — PCL中的PointCloud核心字段与组织方式

说实话,刚接触PCL那会儿,我对着PointCloud这个模板类发了好一会儿呆。看着代码里一堆字段,心里直犯嘀咕:这玩意儿到底怎么用?后来踩了几个坑才明白,理解这个结构,是玩转点云滤波的第一步。

今天咱们就把它彻底掰开揉碎。你想想看,点云说白了就是一堆三维点的集合。但怎么组织这些点,PCL给出了两种方式:有序和无序。这两种方式直接决定了你后续处理算法的选择。

2.1 PointCloud的核心字段

先看一个典型的点云对象长什么样:

pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.points;      // 存储所有点的容器
cloud.width;       // 宽度
cloud.height;      // 高度
cloud.is_dense;    // 是否稠密
cloud.sensor_origin_; // 传感器原点
cloud.sensor_orientation_; // 传感器朝向

这几个字段,我一个个说。

2.1.1 points — 灵魂所在

这是点云数据的本体。一个std::vector<T>,里面装着所有点。每个点可以是PointXYZ(只有xyz)、PointXYZRGB(带颜色)、PointNormal(带法向量)等等。我个人习惯用PointXYZRGB,因为调试时能看到颜色,方便定位问题。

2.1.2 width 和 height — 组织方式的开关

这两个字段决定了点云是有序还是无序。规则很简单:

  • 无序点云:height = 1,width = 点的总数
  • 有序点云:height > 1,width = 每行点数,总点数 = width * height

我在项目中遇到过有人把有序点云当成无序处理,结果滤波算法跑出来的结果完全不对。嗯,这里要注意:有序点云的行列关系一旦被破坏,你就再也恢复不了原始的空间结构了。

2.1.3 is_dense — 坑点预警

这个字段表示点云中是否包含无效点(比如NaN或Inf)。true表示所有点都有效,false表示存在无效点。我曾经在读取Kinect数据时,没检查is_dense就直接做滤波,结果程序崩溃了。后来养成习惯:处理前先调用cloud.is_dense检查,必要时用pcl::removeNaNFromPointCloud清理一下。

注意:很多滤波算法(比如统计滤波)对NaN点非常敏感。如果is_dense为false,建议先做一步去NaN处理,否则结果可能让你怀疑人生。

2.2 有序点云 vs 无序点云

说白了,有序点云就像一张深度图,每个像素对应一个三维点。无序点云就是一锅粥,点与点之间没有空间邻居关系。

2.2.1 有序点云

通常来自RGB-D相机(如Kinect、RealSense)或激光雷达的扫描线数据。它的好处是:

  • 天然支持邻域搜索(直接通过行列索引找邻居)
  • 可以结合图像处理算法(比如用OpenCV处理深度图)
  • 滤波效率高,因为不需要构建KD-Tree

举个例子,从有序点云中取某个点的上下左右邻居:

// 假设cloud是有序点云,width=640, height=480
int row = 200, col = 300;
int idx = row * cloud.width + col;

// 左邻居
pcl::PointXYZ left = cloud.points[idx - 1];
// 右邻居
pcl::PointXYZ right = cloud.points[idx + 1];
// 上邻居
pcl::PointXYZ up = cloud.points[idx - cloud.width];
// 下邻居
pcl::PointXYZ down = cloud.points[idx + cloud.width];

你看,多直接。但要注意边界检查,我刚开始写的时候没注意,程序跑着跑着就崩了。

2.2.2 无序点云

这是更常见的情况。激光雷达扫描一圈,或者多帧拼接后的点云,基本都是无序的。它的特点是:

  • 点与点之间没有空间顺序
  • 要搜索邻居必须构建KD-Tree或Octree
  • 滤波算法更通用,但计算开销更大
小技巧:如果你不确定点云是否有序,直接打印width和height。height=1就是无序,否则有序。我经常在调试代码时加一行std::cout << "height: " << cloud.height << std::endl;,一目了然。

2.3 核心逻辑结构图

下面这张图帮你理清PointCloud的核心字段与组织方式的关系:

PointCloud<T> 核心结构 PointCloud<T> points: vector<T> width / height is_dense: bool height == 1 → 无序点云 height > 1 → 有序点云 无序点云特点 • 点间无空间顺序 • 需构建KD-Tree/Octree 有序点云特点 • 行列索引直接访问邻居 • 可结合图像处理算法

2.4 实际项目中的选择建议

我做了这么多年点云处理,总结了几条经验:

场景 推荐组织方式 原因
RGB-D相机实时处理 有序点云 利用行列关系加速邻域搜索,滤波效率高
激光雷达多帧拼接 无序点云 点云密度不均匀,有序结构无意义
SLAM建图后的全局地图 无序点云 多帧融合后行列关系已丢失
需要快速体素滤波 无序点云 体素滤波不依赖点云顺序

核心要点:有序点云是「有结构的宝藏」,无序点云是「散落的珍珠」。前者适合快速邻域操作,后者更通用灵活。选择哪种,取决于你的传感器和算法需求。

最后说一句,别被这些概念吓到。你只要记住:height=1就是无序,height>1就是有序。剩下的,用多了自然就熟了。

可可精品资料库