4、点云降采样:体素降采样、随机降采样、均匀降采样

大家好,我是你们的老朋友。今天我们来聊聊点云处理里一个绕不开的话题——降采样。

你想想看,一个激光雷达扫一圈,动辄几十万甚至上百万个点。这么多点,直接拿去跑算法,电脑风扇能转得飞起。我刚开始做SLAM那会儿,就吃过这个亏。有一次在园区跑数据,笔记本直接卡死,重启后一看,点云文件1.2个G。嗯,从那以后,我养成了一个习惯:拿到点云,先降采样。

降采样说白了,就是用更少的点,保留尽可能多的几何结构信息。今天咱们聊三种最常用的方法:体素降采样、随机降采样、均匀降采样。

4.1 体素降采样(Voxel Downsampling)

这是我最常用的方法,没有之一。体素降采样的思路很直观:把三维空间切成一个个小立方体(体素),每个体素里只保留一个点。这个点通常是体素内所有点的重心,或者中心点。

为什么我偏爱它?因为体素降采样能很好地保持点云的几何形状。你想想看,如果点云密度不均匀,随机降采样会把稀疏的地方采得更稀疏,但体素降采样不会。每个体素一视同仁,该留几个点就留几个点。

核心参数:体素大小(voxel_size)

体素大小直接决定了降采样后的点云密度。体素越大,点越少;体素越小,点越多。我一般从0.01米开始试,根据场景调整。

在Open3D里,实现起来就几行代码:

import open3d as o3d

# 读取点云
pcd = o3d.io.read_point_cloud("scene.pcd")

# 体素降采样,体素大小0.05米
downsampled = pcd.voxel_down_sample(voxel_size=0.05)

# 可视化对比
o3d.visualization.draw_geometries([pcd], window_name="原始点云")
o3d.visualization.draw_geometries([downsampled], window_name="体素降采样后")

我在项目中遇到过一个问题:体素大小设得太小,降采样效果不明显;设得太大,细节全丢了。后来我总结了一个经验——体素大小取点云平均间距的2-3倍,效果比较稳妥。

小技巧:如果你用的是PCL库,体素降采样函数叫pcl::VoxelGrid。Open3D里叫voxel_down_sample。名字不同,原理一样。

4.2 随机降采样(Random Downsampling)

随机降采样,顾名思义,就是从原始点云里随机抽取一部分点。这个方法简单粗暴,但有个致命缺点——它不考虑点的空间分布。

举个例子,如果点云里某个区域点特别密,随机降采样后这个区域还是密;如果某个区域点特别稀疏,随机降采样后可能就没了。我早期做地面分割时用过随机降采样,结果地面点被采得七零八落,分割效果一塌糊涂。

不过,随机降采样也不是一无是处。它速度快,适合对实时性要求高的场景。比如在SLAM前端,如果点云量太大,可以先随机降采样到一定数量,再去做特征匹配。

import numpy as np
import open3d as o3d

pcd = o3d.io.read_point_cloud("scene.pcd")
points = np.asarray(pcd.points)

# 随机降采样,保留30%的点
sample_ratio = 0.3
indices = np.random.choice(len(points), size=int(len(points) * sample_ratio), replace=False)
sampled_points = points[indices]

# 构建新的点云
sampled_pcd = o3d.geometry.PointCloud()
sampled_pcd.points = o3d.utility.Vector3dVector(sampled_points)

注意:随机降采样时,replace=False表示不重复采样。如果点云量特别大,可以设replace=True,但这样会有重复点,一般不建议。

4.3 均匀降采样(Uniform Downsampling)

均匀降采样,也叫最远点采样(Farthest Point Sampling, FPS)。它的思路是:每次选一个离已选点集最远的点加入,直到达到目标点数。

这样做的好处是,采样后的点能均匀分布在三维空间里,不会出现局部过密或过疏的情况。我在做点云配准(ICP)时,经常先用均匀降采样,因为均匀分布的点能提供更稳定的匹配约束。

不过,均匀降采样有个缺点——慢。它的时间复杂度是O(n²),点云量一大,跑起来很吃力。我一般只在点云量小于10万时用均匀降采样。

import open3d as o3d

pcd = o3d.io.read_point_cloud("scene.pcd")

# 均匀降采样,采样到5000个点
uniform_sampled = pcd.uniform_down_sample(every_k_points=10)

# 注意:uniform_down_sample是每隔k个点取一个,不是最远点采样
# 真正的FPS在Open3D里需要自己实现,或者用PCL的pcl::RandomSample

补充说明:Open3D的uniform_down_sample其实是均匀间隔采样,不是严格意义上的最远点采样。如果你需要真正的FPS,建议用PCL或者自己写一个。

4.4 降采样对点云密度的影响

聊完三种方法,咱们来看看降采样到底怎么影响点云密度。我画了一张图,帮你直观理解:

降采样方法对比:点云密度变化 原始点云 点数:100,000 密度:不均匀 体素降采样 点数:10,000 密度:均匀分布 随机降采样 点数:10,000 密度:不均匀 体素降采样保持密度均匀,随机降采样保留原始密度分布

从这张图你能看到:

  • 体素降采样:点云密度变得均匀,每个体素内只有一个点。适合做特征提取、配准等需要均匀分布的场景。
  • 随机降采样:密度分布和原始点云一样,密的地方还是密,疏的地方还是疏。适合快速预览、实时处理。
  • 均匀降采样:点与点之间的间距尽量相等,密度最均匀,但计算代价高。

我个人的建议是:

  1. 做SLAM前端:用体素降采样,体素大小0.05-0.1米,平衡速度和精度。
  2. 做点云分类/分割:用体素降采样,体素大小根据物体尺寸调整。比如汽车用0.2米,行人用0.05米。
  3. 做可视化预览:用随机降采样,保留5%-10%的点就够了。
  4. 做高精度配准:用均匀降采样(FPS),采样到5000-10000个点。

避坑指南:我曾经在做一个室内重建项目时,用了随机降采样,结果墙角处的点被采没了,导致重建出来的墙角是圆的。后来换成体素降采样,问题就解决了。所以,如果你的场景里有明显的几何特征(墙角、边缘、平面),尽量用体素降采样。

好了,降采样这块就聊到这儿。三种方法各有千秋,选哪个取决于你的应用场景。记住一句话:降采样不是越少越好,而是够用就好。

专注资料整理