点云数据基础:数据结构、常见格式与读写方法
点云数据是三维感知与处理的核心载体,无论是激光雷达扫描的室外场景,还是结构光相机获取的物体表面,最终都会以离散点的集合形式呈现。理解点云的数据结构、文件格式以及读写机制,是进行滤波、配准、分割与重建的前提。本章将系统拆解点云的属性维度、主流格式的差异,并给出工程实践中的读写策略与踩坑指南。
核心原理:点云属性维度与内存布局
点云本质上是一组在三维空间中离散分布的点的集合,每个点除了必须的几何坐标外,还可以携带多种附加属性。从数据结构角度看,点云可以视为一个 N×D 的矩阵,其中 N 为点的数量,D 为每个点的属性维度。最常见的属性包括:
- XYZ(几何坐标):每个点的三维位置,通常使用 float32 或 float64 存储。这是所有点云处理的基础,决定了点云的空间形状。
- RGB(颜色):红绿蓝三通道颜色值,通常使用 uint8(0-255)或 uint16(0-65535)存储。颜色信息来源于相机采集或人工着色,常用于语义分割、可视化与纹理映射。
- 强度(Intensity):激光雷达回波信号的强度值,反映目标表面的反射率。强度值通常为 float32 或 uint16,在道路标线提取、地物分类中具有重要价值。
- 法向量(Normal):每个点所在局部表面的法线方向,通常由邻域点拟合平面计算得出。法向量是曲面重建、点云配准和特征描述的关键输入。
在内存布局上,点云数据通常采用 结构体数组(SoA) 或 数组结构体(AoS) 两种方式。PCL 库默认使用 AoS 方式,即每个点是一个包含所有属性的结构体,连续存储;而 Open3D 则更倾向于 SoA 方式,将不同属性分别存储为独立数组。选择哪种布局取决于算法对缓存友好性的需求:AoS 适合逐点遍历,SoA 适合按属性批量计算。
pcl::PointXYZ、pcl::PointXYZRGB、pcl::PointXYZI 等预定义类型,避免自定义结构体导致的内存对齐问题。在 Python 中,Open3D 的 PointCloud 对象通过 points、colors、normals 属性分别管理不同维度,操作更直观。
常见文件格式:PCD、PLY、LAS、XYZ 的对比与选型
点云数据的存储格式多种多样,不同格式在压缩率、可扩展性、行业适配性上各有优劣。下表对比了四种最常用的格式:
| 格式 | 全称 | 数据组织 | 支持属性 | 典型应用 | 优缺点 |
|---|---|---|---|---|---|
| PCD | Point Cloud Data | ASCII / 二进制 | XYZ、RGB、强度、法向量、自定义字段 | PCL 生态、算法开发 | 灵活性强,支持自定义字段;文件头信息丰富 |
| PLY | Polygon File Format | ASCII / 二进制 | XYZ、RGB、法向量、面片信息 | 三维扫描、网格处理 | 支持多边形网格;可扩展性好 |
| LAS | Lidar Data Exchange | 二进制(压缩/非压缩) | XYZ、强度、回波数、分类、GPS 时间 | 测绘、地理信息系统 | 行业标准,支持大规模点云;压缩率高 |
| XYZ | Plain Text | ASCII | 仅 XYZ(可扩展为 XYZRGB 等) | 简单交换、调试 | 格式简单,可读性强;文件体积大,无压缩 |
PCD 格式详解
PCD 是 PCL 库的原生格式,文件头包含版本号、字段定义、尺寸、类型、点数等元信息。其最大的优势是支持自定义字段,例如可以添加 temperature 或 confidence 等业务属性。PCD 支持 ASCII 和二进制两种编码,二进制模式在读写速度上显著优于 ASCII,但文件不可直接阅读。
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z rgb
SIZE 4 4 4 4
TYPE F F F F
COUNT 1 1 1 1
WIDTH 5
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 5
DATA ascii
0.1 0.2 0.3 4294967296
0.5 0.6 0.7 4294967296
...
PLY 格式详解
PLY 格式起源于斯坦福图形实验室,设计初衷是存储三维扫描仪获取的几何数据。它既可以存储点云,也可以存储三角网格。PLY 文件头使用关键字 element 和 property 定义数据布局,支持 vertex 和 face 两种元素类型。在工业级扫描场景中,PLY 常用于保存带有法向量和颜色的高精度模型。
LAS 格式详解
LAS 是 ASPRS(美国摄影测量与遥感学会)制定的激光雷达数据标准,广泛应用于测绘、林业、城市规划等领域。LAS 文件采用二进制存储,包含公共头块、变长记录和点数据记录三部分。点数据记录中除了 XYZ 和强度外,还包含回波次数、分类标签、扫描角度、GPS 时间等专业属性。LAS 1.4 版本支持最多 15 个额外字节的用户自定义字段。
XYZ 格式详解
XYZ 是最简单的点云格式,每行三个浮点数,以空格或逗号分隔。虽然它不支持元数据,但因其极简性,常用于程序间快速交换数据或调试。实际工程中,常扩展为 XYZRGB 或 XYZI 格式,即在每行末尾追加颜色或强度值。
读写方法:PCL 与 Open3D 实战操作
点云文件的读写是数据处理的第一步,不同库提供了差异化的 API。以下分别介绍 PCL(C++)和 Open3D(Python)的典型读写方式。
PCL 读写示例
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
// 读取 PCD 文件
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
if (pcl::io::loadPCDFile<pcl::PointXYZRGB>("scene.pcd", *cloud) == -1) {
PCL_ERROR("Couldn't read file.\n");
return -1;
}
// 写入 PLY 文件
pcl::io::savePLYFileBinary("output.ply", *cloud);
// 自定义字段的 PCD 写入
pcl::PointCloud<pcl::PointXYZI> cloud_xyzi;
cloud_xyzi.points.resize(100);
for (auto& p : cloud_xyzi.points) {
p.x = rand() % 100 / 100.0;
p.y = rand() % 100 / 100.0;
p.z = rand() % 100 / 100.0;
p.intensity = rand() % 255;
}
pcl::io::savePCDFileBinary("synthetic.pcd", cloud_xyzi);
Open3D 读写示例
import open3d as o3d
import numpy as np
# 读取点云
pcd = o3d.io.read_point_cloud("scene.ply")
print(f"Loaded {len(pcd.points)} points")
# 访问属性
points = np.asarray(pcd.points) # (N, 3)
colors = np.asarray(pcd.colors) # (N, 3) 范围 [0, 1]
normals = np.asarray(pcd.normals) # (N, 3)
# 写入 PCD 格式
o3d.io.write_point_cloud("output.pcd", pcd, write_ascii=False)
# 从 numpy 数组创建点云
xyz = np.random.rand(1000, 3).astype(np.float64)
pcd_new = o3d.geometry.PointCloud()
pcd_new.points = o3d.utility.Vector3dVector(xyz)
o3d.io.write_point_cloud("random.xyz", pcd_new)
案例拆解:多传感器点云格式统一与属性保留
在某自动驾驶数据采集项目中,团队需要融合 64 线激光雷达(输出 PCD 格式)和工业相机(输出 PLY 格式)的数据。初始阶段,算法工程师发现两个问题:一是 PCD 文件中的强度值范围是 0-255,而 PLY 文件中的颜色值范围是 0-1,直接拼接导致特征不一致;二是 LAS 格式的测绘数据需要导入同一套处理流程,但 LAS 中的 GPS 时间戳在 PCD 中无法直接存储。
针对上述问题,团队制定了以下处理策略:
- 统一属性规范:定义内部标准点云格式为 PCD 二进制,字段固定为
x y z intensity rgb,其中 intensity 归一化到 [0,1],rgb 使用 uint8 三通道。 - 格式转换脚本:使用 Python 编写转换工具,读取 PLY 时自动将颜色从 [0,1] 映射到 [0,255];读取 LAS 时提取 XYZ、强度、分类字段,丢弃 GPS 时间(或存入自定义字段
gps_time)。 - 内存优化:对于超过 5000 万点的 LAS 文件,采用分块读取策略,每次处理 100 万点,避免内存溢出。
- 验证机制:转换后随机抽取 1000 个点,对比原始文件的坐标和属性值,确保误差小于 1e-6。
最终,统一后的数据格式使得后续的滤波和分割算法无需关心数据来源,处理效率提升了约 30%。复盘时发现,最容易被忽视的是 强度值的位深差异——部分传感器输出 16 位强度,而 PCD 默认使用 32 位浮点,直接转换会丢失精度。因此,在格式转换时必须明确位深并做相应缩放。
常见误区与总结复盘
- 误区一:混淆 RGB 与强度。RGB 是相机采集的颜色信息,强度是激光雷达的回波能量,两者物理意义完全不同,不能互相替代。在融合处理时,应分别归一化。
- 误区二:忽略文件头信息。PCD 和 PLY 的文件头中包含了点数、字段类型、视角等关键信息,直接跳过文件头解析会导致数据错位。
- 误区三:ASCII 与二进制混用。ASCII 格式可读但体积大、读取慢;二进制格式紧凑但不可读。在性能敏感的生产环境中,应统一使用二进制格式。
- 误区四:法向量未归一化。许多算法假设法向量为单位向量,如果从文件读取的法向量未归一化,会导致配准或重建结果异常。