外观
9.6 从零实现三维占据网格预测 (Occupancy from Scratch)
在自动驾驶感知技术的演进历程中,早期的算法几乎全部聚焦于**三维三维边界框(3D Bounding Boxes)**的回归。算法假定街道上的所有交通参与者都可以被简化为一个长方体盒子(如轿车、货车、行人、骑行者)。
然而,当自动驾驶车辆驶入复杂的非结构化道路时,这一简单的长方体假设却屡屡引发严重的感知漏检:
- 道路施工区域随意堆放的不规则沙石堆、散落一地的施工警示水马;
- 翻倒在高速公路中央的异形货车与散落的货物;
- 倾斜下垂伸入车道内部的树木枝叶。
这些“无法用标准长方体框定”的通用任意障碍物(General Obstacles),构成了自动驾驶感知中最致命的盲区。
为了实现对物理世界几何结构的终极刻画,**三维语义占据网格预测(3D Semantic Occupancy Prediction)**应运而生。它将空间切割为数百万个细小的微观体素立方体,直接判定每一个体素的空间占有状态。
本节我们将从零推导 2D 到 3D 的特征投影机制(FLoSP)、样本极度不平衡下的 Focal 损失,并使用纯底层 PyTorch 从零手写一个完整的三维占据预测网络。

图 9.6-1:MonoScene 从单张道路图像恢复可见与遮挡区域的稠密三维语义占据,展示占据预测的直接输出。 出处:MonoScene: Monocular 3D Semantic Scene Completion,Anh-Quan Cao et al.,2022。
9.6.1 物理与几何基石:从离散边界框到连续体素场
要理解三维占据网格的优越性,我们首先需要从离散几何学对三维空间的表征范式讲起。
1. 乐高积木式空间离散化
想象我们用无数个极小的正方体乐高积木去拼搭整个街道:
- 设自车周围的三维物理空间范围为
; - 我们将每个体素(Voxel)的物理分辨率设为
; - 整个物理空间被离散化为一个分辨率为
个体素的三维立方体网格 。
对于每一个体素坐标
2. 空间空旷性的“稀疏性诅咒”
在真实的道路环境中,整个三维立体空间有超过

图 9.6-2:MonoScene 的 FLoSP 把二维多尺度特征沿相机视线投影到三维体素查询,处理 2D 到 3D 的特征提升。 出处:MonoScene: Monocular 3D Semantic Scene Completion,Anh-Quan Cao et al.,2022。
9.6.2 核心数学推导一:特征沿光线采样(FLoSP)与双线性插值
如何从二维图像特征图生成初始的三维体素特征?MonoScene 提出了**视线特征采样(Feature-Line of Sight Projection, FLoSP)**算法。

图 9.6-3:深度概率向量与像素特征向量做外积,每个深度概率缩放整条通道特征。
1. 三维体素中心反投影
对于三维体素网格中的每一个体素中心点
2. 双线性插值采样(Bilinear Interpolation)
由于投影出的
手算代入算例: 设某体素投影得到的像素坐标为
- 左上
特征值为 ;权重为 ; - 右上
特征值为 ;权重为 ; - 左下
特征值为 ;权重为 ; - 右下
特征值为 ;权重为 。
我们计算插值后的体素特征值:
四项权重之和恰好为
深入推导:三维体素多尺度反投影(FLoSP)插值核函数的全微分求导推导(点击展开查看完整推导)
设插值核函数为
当体素在三维物理空间移动微小位移
该连续可微性保证了 3D 几何特征可以通过反向传播直接优化底层的 2D 视觉主干网络。
9.6.3 核心数学推导二:极度类别不平衡下的 Focal 损失
由于空旷空气体素占据了

图 9.6-4:SurroundOcc 对比三维体素查询与 BEV 查询的跨视图注意力,说明高度维度何时被显式保留。 出处:SurroundOcc: Multi-Camera 3D Occupancy Prediction for Autonomous Driving,Yi Wei et al.,2023。

图 9.6-5:OccFormer 在局部与全局路径中更新三维体素特征,再解码为稠密语义占据。 出处:OccFormer: Dual-path Transformer for 3D Semantic Occupancy Prediction,Xiaofeng Wang et al.,2023。
为了解决该痛点,系统采用了三维 Focal 损失(Focal Loss):
其中:
为模型对真实类别的预测概率; 称为聚焦参数(Focusing Parameter),通常取 ; 为类别平衡权重。
初等代数直觉:
- 当面对极其容易判断的空气体素时,网络预测概率极高(如
),调制因子 ,使其损失被压低了 10000 倍,几乎不产生梯度干扰; - 当面对罕见的障碍物时,网络往往信心不足(如
),调制因子 ,其损失被重点放大,迫使网络把全部注意力集中在攻克长尾障碍物上!
深入推导:Lovász-Softmax 损失对三维交并比(mIoU)次模扩展的数学证明(点击展开查看完整推导)
在语义占据评估中,最终指标为平均交并比(mIoU
其中
9.6.4 纯底层 PyTorch 代码实现:从零搭建三维占据网格预测网络
下面我们使用纯底层 PyTorch 算子实现一个完整的 2D 到 3D 占据网格预测网络,包括双线性体素反投影采样、三维轻量卷积解码器与 Focal 损失计算。
python
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleOccupancyNetwork(nn.Module):
"""
轻量级三维语义占据网格预测网络
"""
def __init__(self, in_channels: int = 32, num_classes: int = 4, voxel_res: tuple = (16, 16, 8)):
super().__init__()
self.voxel_d, self.voxel_h, self.voxel_w = voxel_res
self.num_classes = num_classes
# 3D 卷积编码器-解码器
self.conv3d_block = nn.Sequential(
nn.Conv3d(in_channels, 32, kernel_size=3, padding=1),
nn.BatchNorm3d(32),
nn.ReLU(),
nn.Conv3d(32, 32, kernel_size=3, padding=1),
nn.BatchNorm3d(32),
nn.ReLU(),
nn.Conv3d(32, num_classes, kernel_size=1) # 输出各类别 Logits
)
def sample_2d_to_3d(self, img_feat: torch.Tensor, grid_norm: torch.Tensor) -> torch.Tensor:
"""
利用 grid_sample 将 2D 图像特征双线性插值采样到 3D 体素网格中
:param img_feat: (B, C, H, W) 2D 图像特征
:param grid_norm: (B, D, H_v, W_v, 2) 归一化在 [-1, 1] 的 2D 投影采样坐标
:return: (B, C, D, H_v, W_v) 3D 体素初始特征
"""
B, C, H, W = img_feat.shape
# grid_sample 要求 4D 或 5D 输入,此处将 D 展平到批次执行采样
B, D_v, H_v, W_v, _ = grid_norm.shape
flat_grid = grid_norm.view(B, D_v * H_v, W_v, 2)
# 双线性采样
sampled_2d = F.grid_sample(
img_feat, flat_grid, mode="bilinear", padding_mode="zeros", align_corners=True
) # (B, C, D*H, W)
sampled_3d = sampled_2d.view(B, C, D_v, H_v, W_v)
return sampled_3d
def forward(self, img_feat: torch.Tensor, grid_norm: torch.Tensor) -> torch.Tensor:
"""
前向计算
:return: (B, num_classes, D, H_v, W_v) 体素类别 Logits
"""
init_voxel_feat = self.sample_2d_to_3d(img_feat, grid_norm)
occupancy_logits = self.conv3d_block(init_voxel_feat)
return occupancy_logits
def focal_loss_3d(logits: torch.Tensor, targets: torch.Tensor, gamma: float = 2.0) -> torch.Tensor:
"""
3D 体素 Focal 损失函数
:param logits: (B, num_classes, D, H, W)
:param targets: (B, D, H, W) 整数类别标签
:return: 标量损失
"""
ce_loss = F.cross_entropy(logits, targets, reduction="none") # (B, D, H, W)
p_t = torch.exp(-ce_loss) # 获取真实类别的预测概率
focal_weight = (1.0 - p_t) ** gamma
return (focal_weight * ce_loss).mean()
# ===================================================================
# 单元测试与 3D 卷积前向推理校验
# ===================================================================
if __name__ == "__main__":
batch_size = 2
in_channels = 16
num_classes = 4 # 0: 空气, 1: 道路, 2: 车辆, 3: 障碍物
img_h, img_w = 32, 32
voxel_dim = (8, 16, 16) # (D, H, W)
model = SimpleOccupancyNetwork(
in_channels=in_channels, num_classes=num_classes, voxel_res=voxel_dim
)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
dummy_2d_feat = torch.randn(batch_size, in_channels, img_h, img_w)
# 构造归一化的 2D 采样网格 [-1, 1]
dummy_grid = torch.rand(batch_size, voxel_dim[0], voxel_dim[1], voxel_dim[2], 2) * 2.0 - 1.0
# 模拟真实 3D 标签 (绝大多数为 0: 空气)
dummy_targets = torch.zeros(batch_size, voxel_dim[0], voxel_dim[1], voxel_dim[2], dtype=torch.long)
dummy_targets[:, 2:4, 5:8, 5:8] = 2 # 局部的车辆体素
# 1. 前向推理
logits = model(dummy_2d_feat, dummy_grid)
loss = focal_loss_3d(logits, dummy_targets, gamma=2.0)
# 2. 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"[Occupancy Test] 3D 体素预测输出形状: {logits.shape}")
print(f"[Occupancy Test] 3D Focal 训练损失: {loss.item():.4f}")
assert logits.shape == (batch_size, num_classes, *voxel_dim), "3D 体素输出张量形状不符!"
assert not torch.isnan(loss), "训练损失计算出现 NaN!"
print("✓ 从零实现三维占据网格预测网络与 3D Focal 损失单测全部通过!")9.6.5 本节小结
回顾本节内容,我们建立了三维语义占据网格预测的完整技术图谱:
- 几何范式跃迁:从简化的 3D 长方体检测框走向稠密三维体素网格,彻底消除了未知异形障碍物的漏检盲区;
- 2D 到 3D 的视线反投影:利用双线性插值采样将二维高维图像特征无缝铺设至三维体素网格中;
- 稀疏不平衡优化:通过 Focal 损失动态抑制占据
以上体积的简单空气背景,使模型集中算力攻坚长尾障碍物。

