2. 点云数据结构:PCL点云类型、XYZ、XYZRGB、PointXYZI

点云数据长什么样?说白了,就是一堆三维空间里的点。每个点带着自己的坐标 (x, y, z),有时候还带着颜色、强度之类的附加信息。PCL(Point Cloud Library)把这些点组织成了不同的结构体,也就是我们今天要聊的点云类型。

我个人习惯把点云类型比作「集装箱」。不同类型的集装箱,装的货不一样。有的只装坐标,有的还装颜色,有的装反射强度。你选哪种,取决于你的传感器和算法需求。

2.1 最基础的点:pcl::PointXYZ

这是最常用的点类型。它只包含三个浮点数:x、y、z。没有别的了。简单、纯粹、省内存。

我在项目中遇到过一个问题:用 Velodyne 激光雷达采集数据,默认输出就是 XYZ 格式。如果你只需要做几何处理(比如体素滤波、平面分割),用这个类型就够了。没必要加载颜色信息,白白浪费带宽和内存。

// 定义一个 XYZ 点
pcl::PointXYZ point;
point.x = 1.23f;
point.y = 4.56f;
point.z = 7.89f;

// 定义点云
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.push_back(point);
小技巧: 如果你用 PCL 的 I/O 模块读取 PCD 文件,默认会尝试保留所有字段。但如果你明确知道只需要 XYZ,可以指定加载字段,能省不少时间。

2.2 带颜色的点:pcl::PointXYZRGB

这个类型在 XYZ 的基础上,加了一个 RGB 字段。注意,这里的 RGB 是打包在一个 32 位整数里的。每个颜色通道占 8 位,顺序是 BGR(不是 RGB,容易搞混)。

为什么会这样?嗯,这跟 PCL 底层用的 RGB 编码方式有关。你赋值的时候,最好用现成的函数,别自己手动拼。

pcl::PointXYZRGB point;
point.x = 1.0f;
point.y = 2.0f;
point.z = 3.0f;

// 正确赋值方式
uint8_t r = 255, g = 128, b = 64;
uint32_t rgb = (static_cast<uint32_t>(r) << 16 |
                static_cast<uint32_t>(g) << 8  |
                static_cast<uint32_t>(b));
point.rgb = *reinterpret_cast<float*>(&rgb);

// 或者用 PCL 提供的工具函数
point.rgb = pcl::RGB(255, 128, 64).rgba;
我曾经踩过的坑: 直接给 point.rgb 赋一个 float 值,比如 255.0f。结果颜色完全不对。后来才发现,RGB 字段本质上是把整数当 float 存,必须用 reinterpret_cast 或者 PCL 的辅助函数。千万别偷懒。

PointXYZRGB 常用于 RGB-D 相机(比如 Kinect、RealSense)或者多传感器融合场景。颜色信息对语义分割、目标识别很有帮助。但注意,如果你只是做几何配准,颜色反而可能引入噪声。

2.3 带强度的点:pcl::PointXYZI

这个类型在 XYZ 的基础上,加了一个 intensity 字段。强度值通常表示激光雷达回波的反射强度。不同材质的物体,反射强度不一样。比如,路面的强度通常比树叶低。

我记得有一次做地面分割,单纯用高度信息效果不好。后来加了强度阈值,把低强度的点(比如沥青路面)和高强度的点(比如白色车道线)分开处理,效果立竿见影。

pcl::PointXYZI point;
point.x = 0.0f;
point.y = 0.0f;
point.z = 0.0f;
point.intensity = 150.0f;  // 强度值范围通常 0-255
关键点: 强度值不是绝对的。不同激光雷达的强度标定方式不同。同一台设备,不同距离、不同入射角,强度也会变化。所以,用强度做分类时,最好做归一化或者相对比较。

2.4 其他常见点类型

除了上面三种,PCL 还提供了很多变体。我列个表,方便你快速查阅:

点类型 字段 典型用途
PointXYZ x, y, z 纯几何处理
PointXYZRGB x, y, z, rgb RGB-D 相机、语义分割
PointXYZI x, y, z, intensity 激光雷达、强度滤波
PointXYZRGBA x, y, z, rgb, alpha 带透明度的彩色点云
PointXYZRGBL x, y, z, rgb, label 语义标注数据
PointNormal x, y, z, normal_x, normal_y, normal_z, curvature 法线估计、曲面重建

你想想看,实际项目中,我们经常需要把不同传感器的数据融合到一起。比如,激光雷达给 XYZI,相机给 RGB。这时候,你就得把数据转成统一的点类型。我个人习惯用 PointXYZRGB,把强度映射成灰度颜色,或者用 PointXYZRGBL 保存语义标签。

2.5 点云数据结构的核心逻辑

下面这张图,是我自己总结的点云类型选择逻辑。你可以把它当作一个决策树:

点云类型选择逻辑 传感器数据 只有几何信息? 需要颜色/强度? PointXYZ 具体是什么? PointXYZRGB PointXYZI 根据传感器类型和算法需求,选择合适的点类型

说白了,选点类型就是选数据结构。选对了,代码简洁、运行快。选错了,要么浪费内存,要么丢失关键信息。我个人建议:

  • 纯激光雷达几何处理:用 PointXYZ,别犹豫。
  • 需要强度信息:用 PointXYZI,比如做地面分割、路沿检测。
  • 融合相机数据:用 PointXYZRGB,颜色对目标识别帮助很大。
  • 做语义标注:用 PointXYZRGBL,标签字段能省去很多数据管理的麻烦。
避坑指南: 我曾经在一个项目里,把所有点都转成 PointXYZRGB,哪怕只有激光雷达数据。结果内存占用翻了三倍,处理速度也慢了。后来改成 PointXYZ,问题迎刃而解。记住:够用就好,别过度设计。

嗯,关于点云数据结构,今天就聊这么多。你只要记住:点云类型就是数据的「容器」,选对容器,事半功倍。