4、点云数据基础:点云数据结构、滤波与可视化

点云这东西,说白了就是一堆三维坐标点的集合。每个点带着 (x, y, z) 信息,有时候还带上颜色、强度什么的。我刚开始接触点云时,觉得不就是个数组嘛,有什么好讲的?后来踩了不少坑才明白——数据结构选不对,后面所有算法都得重写。

4.1 点云数据结构:PCL vs Open3D

目前工业界最常用的两个库,一个是 PCL(Point Cloud Library),一个是 Open3D。我个人习惯是:做底层算法、嵌入式部署时用 PCL;做快速原型、可视化验证时用 Open3D。为什么?往下看。

4.1.1 PCL 的点云结构

PCL 的核心数据结构是 pcl::PointCloud<T>。T 可以是多种点类型,比如:

  • pcl::PointXYZ — 只有 x, y, z
  • pcl::PointXYZRGB — 带 RGB 颜色
  • pcl::PointXYZI — 带强度值(激光雷达常用)
  • pcl::PointNormal — 带法向量

我在项目中遇到过一个问题:用 PointXYZ 存了 500 万个点,后面想加颜色信息,结果发现类型不兼容,只能全部重读。嗯,这里要注意——一开始就选好点类型,后面能省很多事

// PCL 点云创建与基本操作
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

// 填充点云
cloud->width = 1000;
cloud->height = 1;  // 无序点云
cloud->points.resize(cloud->width * cloud->height);

for (size_t i = 0; i < cloud->points.size(); ++i) {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
}
小技巧:PCL 里 height == 1 表示无序点云,height > 1 表示有序点云(比如深度图转过来的)。有序点云可以用 at(row, col) 快速索引,性能好很多。

4.1.2 Open3D 的点云结构

Open3D 就简洁多了。它的核心是 open3d::geometry::PointCloud,所有数据都存在 numpy 数组里。你想想看,这意味着什么?意味着你可以直接用 Python 的切片、广播、矩阵运算来操作点云,开发效率极高。

import open3d as o3d
import numpy as np

# 创建点云
pcd = o3d.geometry.PointCloud()

# 从 numpy 数组创建
points = np.random.rand(1000, 3)
pcd.points = o3d.utility.Vector3dVector(points)

# 添加颜色
colors = np.random.rand(1000, 3)
pcd.colors = o3d.utility.Vector3dVector(colors)

# 添加法向量
normals = np.random.rand(1000, 3)
pcd.normals = o3d.utility.Vector3dVector(normals)
核心区别:PCL 是 C++ 原生,性能好但开发慢;Open3D 是 Python 友好,适合快速迭代。我一般这样选——算法验证用 Open3D,产品化用 PCL。

4.2 点云滤波:去噪与降采样

原始点云数据通常很「脏」。传感器噪声、环境干扰、离群点……不处理的话,后面做配准、分割、重建全是错的。我见过有人拿原始点云直接跑 ICP,结果收敛到不知道哪里去了。

4.2.1 体素滤波(Voxel Grid Filter)

体素滤波说白了就是「三维像素化」。你把空间切成一个个小立方体(体素),每个体素里只保留一个点(通常是重心)。这样点云数量就大大减少了。

为什么要做体素滤波?两个原因:

  • 降采样:1000 万个点降到 100 万个,计算量直接少一个数量级
  • 均匀化:消除传感器近密远疏的问题,让点云分布更均匀
// PCL 体素滤波
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;
voxel_filter.setInputCloud(cloud);
voxel_filter.setLeafSize(0.01f, 0.01f, 0.01f);  // 1cm 体素
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
voxel_filter.filter(*cloud_filtered);
# Open3D 体素滤波
voxel_size = 0.01  # 1cm
downpcd = pcd.voxel_down_sample(voxel_size)
注意:体素大小不是越小越好。设太小了降采样效果不明显;设太大了会丢失细节。我一般从 0.01m(1cm)开始试,根据实际场景调整。室内场景 0.01-0.02m,室外场景 0.05-0.1m。

4.2.2 统计滤波(Statistical Outlier Removal)

统计滤波是用来去除离群点的。它的原理很简单:计算每个点到它 k 个最近邻的平均距离,如果这个距离超过全局均值 + n 倍标准差,就认为是离群点。

我曾经在一个激光雷达项目里,点云里总有几根「飞线」——就是莫名其妙飘在空中的点。统计滤波一跑,干干净净。

// PCL 统计滤波
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50);          // 考虑 50 个近邻
sor.setStddevMulThresh(1.0); // 标准差倍数
sor.filter(*cloud_filtered);
# Open3D 统计滤波
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=50, std_ratio=1.0)
pcd_filtered = pcd.select_by_index(ind)
参数调优:meanK 一般设 20-50,stddevMulThresh 设 1.0-2.0。设太小会把正常点也删掉,设太大又滤不干净。我习惯先设 1.0,看效果再微调。

4.3 点云可视化

可视化是点云开发里最容易被忽视的一环。很多人觉得「能看就行」,但实际调试时,一个好的可视化工具能帮你一眼看出问题。

4.3.1 PCL 可视化

PCL 的 pcl::visualization::CloudViewer 是最简单的,但功能有限。我推荐用 PCLVisualizer,支持多窗口、添加文字、交互操作。

// PCL 可视化
pcl::visualization::PCLVisualizer viewer("点云查看器");
viewer.setBackgroundColor(0, 0, 0);  // 黑色背景

// 添加点云
viewer.addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");

// 设置点大小
viewer.setPointCloudRenderingProperties(
    pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");

// 添加坐标轴
viewer.addCoordinateSystem(1.0);

// 循环显示
while (!viewer.wasStopped()) {
    viewer.spinOnce(100);
}

4.3.2 Open3D 可视化

Open3D 的可视化就优雅多了。一行代码就能看,而且支持鼠标交互(旋转、平移、缩放)。

# Open3D 可视化
o3d.visualization.draw_geometries([pcd], 
                                  window_name="点云查看",
                                  width=800, 
                                  height=600)

# 更高级的用法:添加多个几何体
o3d.visualization.draw_geometries([pcd, mesh, lineset],
                                  point_show_normal=True)
我的建议:开发阶段用 Open3D 可视化,调试效率高;产品化阶段用 PCL 可视化,可以嵌入到 C++ 管线里。两个都学,不亏。

4.4 本章知识体系

下面这张图总结了点云数据基础的核心内容,从数据结构到滤波再到可视化,一条线串起来。

点云数据基础:知识体系 点云数据结构 PCL: pcl::PointCloud<T> Open3D: geometry.PointCloud 点云滤波 体素滤波 (Voxel Grid) 降采样、均匀化、控制点密度 统计滤波 (SOR) 去除离群点、基于邻域统计 点云可视化 PCLVisualizer (C++ 集成) draw_geometries (快速调试)

这张图把本章的三个核心模块串起来了。从上到下,先选好数据结构,再做滤波处理,最后可视化验证。每一步都有对应的工具和参数,实际项目中按这个流程走,基本不会出大问题。

一句话总结:数据结构选对,滤波参数调好,可视化工具用好——点云处理的第一步就稳了。

公众号:蓝海资料掘金营,微信deep3321