2. 3D空间表示方法:体素、点云、网格、隐式表示

各位同学,今天我们来聊聊3D空间表示。说实话,这个问题我当年刚入行时也困惑了很久。自动驾驶场景里,障碍物是三维的,但我们的传感器(摄像头、激光雷达)采集到的数据,要么是二维图像,要么是稀疏的点。怎么把这些数据变成计算机能理解的3D表示?

我个人的经验是:没有完美的表示方法,只有最适合场景的表示方法。下面我把四种主流方法掰开揉碎了讲清楚。

2.1 体素(Voxel)—— 3D世界的像素

体素就是3D版本的像素。你把空间切成一个个小立方体,每个立方体要么被占据(1),要么空闲(0)。

核心思想:用离散的网格去近似连续的3D空间。

我在做激光雷达障碍物检测时,最早用的就是体素。为什么呢?因为它的数据结构太直观了——就是一个3D数组,形状是 (W, H, D),每个元素是0或1。

# 一个简单的体素表示示例
import numpy as np

# 假设我们有一个 10x10x10 的体素空间
voxel_grid = np.zeros((10, 10, 10), dtype=np.int8)

# 在位置 (3, 4, 5) 处放置一个障碍物
voxel_grid[3, 4, 5] = 1

# 检查某个位置是否被占据
if voxel_grid[3, 4, 5] == 1:
    print("该位置有障碍物")

优点:

  • 数据结构简单,适合GPU并行计算
  • 空间局部性好,邻居关系天然存在
  • 适合做3D卷积(3D CNN)

缺点:

  • 内存爆炸:分辨率每翻一倍,内存占用变成8倍
  • 稀疏性:大部分体素是空的,浪费严重
  • 精度受限:体素大小决定了最小可检测物体

我曾经踩过的坑:在项目中用0.1m分辨率的体素覆盖100m×100m×5m的区域,结果内存直接爆了。后来改用稀疏体素(只存储被占据的体素),才解决了问题。

2.2 点云(Point Cloud)—— 最接近传感器原始数据

点云就是一堆三维点的集合。每个点有 (x, y, z) 坐标,有时还带强度、颜色等信息。

你想想看,激光雷达扫一下,出来的就是点云。所以点云是自动驾驶里最直接的3D表示。

# 点云数据示例
point_cloud = [
    [1.2, 3.4, 5.6],  # 第一个点
    [2.3, 4.5, 6.7],  # 第二个点
    [3.4, 5.6, 7.8],  # 第三个点
]

优点:

  • 数据紧凑:只存储有意义的点,不浪费空间
  • 精度高:点的坐标可以是浮点数,理论上无限精度
  • 与传感器天然对齐:不需要额外的转换

缺点:

  • 无序性:点的顺序不影响表示,这让神经网络很难处理
  • 稀疏性:远距离点很稀疏,近距离点很密集
  • 缺乏拓扑信息:点与点之间没有连接关系

我的建议:如果你要做点云处理,一定要理解PointNet和PointNet++。它们解决了点云的无序性问题。我当年花了两周才真正搞懂PointNet的对称函数设计,嗯,值得的。

2.3 网格(Mesh)—— 表面表示之王

网格用三角形(或四边形)来逼近物体表面。每个三角形由三个顶点和一条法线组成。

说白了,网格就是3D建模里最常用的格式。你玩的游戏里的角色、汽车,都是网格。

# 一个简单的三角形网格
# 顶点列表
vertices = [
    [0.0, 0.0, 0.0],  # v0
    [1.0, 0.0, 0.0],  # v1
    [0.0, 1.0, 0.0],  # v2
]

# 三角形面片(用顶点索引表示)
faces = [
    [0, 1, 2],  # 一个三角形
]

优点:

  • 表面表示精确:适合渲染和可视化
  • 数据量可控:用少量三角形就能表示复杂形状
  • 拓扑信息丰富:知道哪些点连在一起

缺点:

  • 不适合深度学习:网格的拓扑结构不固定,很难输入到神经网络
  • 生成困难:从点云生成网格需要复杂的重建算法
  • 不擅长表示内部结构:网格只描述表面

注意:在自动驾驶中,网格主要用于高精地图3D重建。实时障碍物检测很少直接用网格,因为计算量太大。我见过有人尝试用网格做检测,结果一帧处理了500毫秒...嗯,显然不现实。

2.4 隐式表示(Implicit Representation)—— 最近的大热门

隐式表示不直接存储几何信息,而是用一个函数来描述空间。比如:

  • 有符号距离函数(SDF): f(x, y, z) = 到最近表面的距离(带符号)
  • 占据场(Occupancy Field): f(x, y, z) = 该点被占据的概率

这就是Occupancy Network的核心思想!

# 隐式表示示例:一个球的SDF
def sphere_sdf(x, y, z, radius=1.0, center=(0, 0, 0)):
    cx, cy, cz = center
    distance = np.sqrt((x-cx)**2 + (y-cy)**2 + (z-cz)**2)
    return distance - radius  # 负值表示内部,正值表示外部

# 查询点 (0.5, 0.5, 0.5) 是否在球内
sdf_value = sphere_sdf(0.5, 0.5, 0.5)
if sdf_value < 0:
    print("点在球内部")
else:
    print("点在球外部")

优点:

  • 无限分辨率:你可以以任意精度查询空间中的任意点
  • 内存高效:只需要存储一个神经网络(或函数参数)
  • 拓扑灵活:可以表示任意形状,包括有孔洞的物体
  • 适合学习:用神经网络拟合隐式函数,端到端训练

缺点:

  • 推理速度慢:每个点都要通过网络前向传播
  • 难以可视化:需要先提取等值面(用Marching Cubes算法)
  • 训练不稳定:隐式函数的优化比较困难

为什么Occupancy Network选择隐式表示?

因为隐式表示可以连续地、高精度地描述3D空间。你想想看,体素是离散的,点云是稀疏的,网格是表面的。而隐式表示可以告诉你空间中每一个点的占据情况——这正是障碍物检测需要的。

2.5 四种方法的对比总结

特性 体素 点云 网格 隐式表示
数据结构 3D数组 点集 顶点+面片 函数/网络
内存占用 高(随分辨率立方增长) 低(只存有效点) 中(取决于三角形数量) 低(只存网络参数)
精度 受分辨率限制 高(浮点坐标) 高(可细化) 理论上无限
深度学习友好度 高(3D CNN) 中(需特殊网络) 低(拓扑不固定) 高(NeRF, Occupancy Network)
推理速度 慢(逐点查询)
适用场景 语义分割、检测 检测、跟踪 重建、渲染 新视图合成、占用预测

我的个人经验:在实际项目中,我通常混合使用多种表示。比如用体素做粗检测(快速定位障碍物),然后用隐式表示做精细形状重建。这样既保证了速度,又保证了精度。你想想看,这不就是取长补短吗?

2.6 小结

好了,四种3D表示方法讲完了。我再说说我的理解:

  • 体素:简单粗暴,适合做3D卷积,但内存是硬伤
  • 点云:最接近传感器,数据紧凑,但无序性让网络设计头疼
  • 网格:渲染和建模的王者,但深度学习不太待见它
  • 隐式表示:新宠儿,灵活且强大,但速度是瓶颈

下一章我们会深入Occupancy Network,看看它如何用隐式表示来做障碍物检测。到时候你会看到,这些基础概念是怎么串起来的。

嗯,今天就到这里。有问题随时问我。