mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
591 字
2 分钟
用体素法计算点云体积
2026-04-21

算法原理#

体素法(Voxelization-based Volume Estimation)将连续三维空间离散化为等边立方体网格(体素,Voxel),通过统计被点云占据的体素数量来估算目标体积。其核心思想源于三维版本的”像素化”:当体素尺寸足够小时,被占据体素的总体积可近似为物体实际体积。

与三维凸包/凹包法不同,体素法不依赖表面重建,而是直接对原始点云进行空间量化,因此对表面不封闭、存在遮挡空洞、边缘不规则的场景具有天然鲁棒性。每个体素的占据状态通常采用”至少包含一个点即占据”的0-1判定策略,避免了对点云密度不均匀性的过度敏感。

适用场景#

场景特征体素法表现
表面凹凸不平、存在大量局部空洞★★★★★ 不依赖表面闭合
需要快速估算且精度要求适中★★★★★ 时间复杂度O(n),仅需一次遍历
点云密度分布不均★★★★☆ 单点即可标记体素,弱化密度差异
存在大量离群噪点★★★☆☆ 需前置滤波,否则单点噪点会误占体素
需要精确到厘米级的体积测量★★☆☆☆ 受限于体素尺寸离散化误差

算法步骤#

1.计算点云的包围盒(最大/最小 X,Y,Z)

2.根据voxel_size,把包围盒划分为体素网格

3.把每个点分配到对应的体素中

4.统计被占据的体素数量

5.体积 = 占据体素数 × voxel_size

相关参数建议#

体素尺寸精度表现计算开销适用场景
0.02m高,离散化误差小体素数多,内存占用大精细物料、小体积货物
0.05m中等,误差约 ±0.125%均衡煤炭/矿石车厢
0.10m低,棱角处阶梯感明显体素数少,极快土方量粗略估算

注:该参数为AI估算,尚未实测。

具体实现代码#

方案 A:基于 NumPy(轻量、无额外依赖)#

import numpy as np
def voxel_volume_numpy(points: np.ndarray, voxel_size: float = 0.05) -> dict:
"""
基于 NumPy 的体素体积计算。
Args:
points: (N, 3) 点云数组
voxel_size: 体素边长(单位:米)
Returns:
dict: 包含体积、占据体素数、总网格数等信息
"""
if points.size == 0:
return {"volume": 0.0, "occupied_voxels": 0, "total_voxels": 0}
# 1. 计算 AABB 包围盒
min_bound = points.min(axis=0)
max_bound = points.max(axis=0)
# 2. 计算体素索引(向量化操作,无需显式循环)
voxel_indices = np.floor((points - min_bound) / voxel_size).astype(np.int32)
# 3. 去重统计占据体素
unique_voxels = np.unique(voxel_indices, axis=0)
occupied = unique_voxels.shape[0]
# 4. 计算理论网格总数(用于参考)
grid_dims = np.floor((max_bound - min_bound) / voxel_size).astype(np.int32) + 1
total_grid = np.prod(grid_dims)
# 5. 体积计算
volume = occupied * (voxel_size ** 3)
return {
"volume": volume,
"occupied_voxels": occupied,
"total_voxels": total_grid,
"voxel_size": voxel_size,
"bbox": {"min": min_bound.tolist(), "max": max_bound.tolist()},
"grid_dims": grid_dims.tolist()
}
# 使用示例
# result = voxel_volume_numpy(points_array, voxel_size=0.05)
# print(f"估算体积: {result['volume']:.3f} m³")

方案 B:基于 Open3D(推荐,支持预处理流水线)#

import open3d as o3d
import numpy as np
def voxel_volume_o3d(pcd: o3d.geometry.PointCloud,
voxel_size: float = 0.05,
outlier_nb_neighbors: int = 20,
outlier_std_ratio: float = 2.0) -> dict:
"""
基于 Open3D 的体素体积计算,内置统计滤波去噪。
Args:
pcd: Open3D 点云对象
voxel_size: 体素边长(米)
outlier_nb_neighbors: 统计滤波邻域点数
outlier_std_ratio: 统计滤波标准差倍数阈值
Returns:
dict: 体积及体素化信息
"""
# 前置:统计滤波去除离群噪点(防止单点飞点误占体素)
if len(pcd.points) > outlier_nb_neighbors:
pcd_filtered, _ = pcd.remove_statistical_outlier(
nb_neighbors=outlier_nb_neighbors,
std_ratio=outlier_std_ratio
)
else:
pcd_filtered = pcd
# 体素化:Open3D 的 VoxelGrid 会自动处理坐标映射
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(
pcd_filtered, voxel_size=voxel_size
)
# 获取所有体素(Open3D 0.16+)
voxels = voxel_grid.get_voxels()
occupied = len(voxels)
volume = occupied * (voxel_size ** 3)
return {
"volume": volume,
"occupied_voxels": occupied,
"voxel_size": voxel_size,
"points_before_filter": len(pcd.points),
"points_after_filter": len(pcd_filtered.points)
}
# 使用示例
# pcd = o3d.io.read_point_cloud("carriage.ply")
# result = voxel_volume_o3d(pcd, voxel_size=0.05)
# print(f"车厢体积: {result['volume']:.3f} m³")
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

用体素法计算点云体积
https://fredsblog-2dc.pages.dev/posts/note-pc-voxel/
作者
Fredzhe
发布于
2026-04-21
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时