共计 2808 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
传统 3D 目标检测方法(如 PointPillars、VoxelNet)通常需要预定义锚框(anchor boxes),这种设计存在两个主要问题:

- 计算开销大 :需要枚举大量候选框进行匹配
- 参数敏感 :锚框尺寸和比例需要针对不同数据集精心调整
CenterPoint 创新性地采用关键点检测范式,将 3D 物体建模为点云中的中心点,配合简单的回归头预测物体尺寸和朝向。这种设计天然解决了锚框方法的痛点,但带来了新的挑战——如何稳定训练关键点预测任务?
数学原理
Heatmap 生成
对于每个物体类别,生成一个二维 heatmap 表示中心点存在概率。设真实中心点为 $(u,v)$,heatmap 值按高斯核分布:
$$ H_{ij} = \exp\left(-\frac{(i-u)^2 + (j-v)^2}{2\sigma^2}\right) $$
其中 $\sigma$ 是自适应半径,与物体尺寸成正比:
$$ \sigma = \max(f(w), f(h)) $$
$f(\cdot)$ 通常取线性函数,将物理尺寸映射到特征图尺度。
损失函数构成
1. 分类损失(Focal Loss)
解决正负样本不平衡问题:
$$ L_{cls} = -\frac{1}{N}\sum_{ij}\begin{cases}
(1-\hat{H}{ij})^\alpha \log(\hat{H}=1 \
\hat{H}}) & \text{if} H_{ij{ij}^\beta (1-H
\end{cases} $$})^\gamma \log(1-\hat{H}_{ij}) & \text{otherwise
- $\alpha,\beta$:调节难易样本权重(通常取 $\alpha=2,\beta=4$)
- $\gamma$:聚焦系数(通常取 2)
2. 回归损失(L1 Loss)
预测中心点偏移量和 3D 尺寸:
$$ L_{reg} = \sum_{k\in{x,y,z,w,l,h,\theta}} | \hat{t}_k – t_k | $$
其中 $t_z$ 需归一化处理避免数值不稳定。
代码实现
Heatmap 目标生成
def generate_heatmap(feature_map_size, gt_boxes, sigma_ratio=0.2):
"""
Args:
feature_map_size: (W, H)
gt_boxes: [N, 7] (x,y,z,w,l,h,theta)
sigma_ratio: 高斯核半径与物体尺寸的比例系数
Returns:
heatmap: [H, W] 值域 [0,1]
"""
heatmap = torch.zeros(feature_map_size[1], feature_map_size[0])
for box in gt_boxes:
# 将 3D 中心点投影到特征图坐标
x, y = project_3d_to_feature(box[:2], feature_map_size)
w, l = box[3:5]
# 自适应高斯半径
sigma = max(w, l) * sigma_ratio
radius = int(sigma * 3) # 覆盖 99.7% 区域
# 生成二维高斯核
gaussian = create_2d_gaussian(radius, sigma)
# 将高斯核叠加到 heatmap
left, top = max(0, x-radius), max(0, y-radius)
right, bottom = min(feature_map_size[0], x+radius+1), min(feature_map_size[1], y+radius+1)
heatmap[top:bottom, left:right] = torch.maximum(heatmap[top:bottom, left:right],
gaussian[top-y+radius:bottom-y+radius, left-x+radius:right-x+radius]
)
return heatmap
完整损失函数
class CenterPointLoss(nn.Module):
def __init__(self, alpha=2.0, beta=4.0):
super().__init__()
self.focal_loss = FocalLoss(alpha, beta)
self.reg_loss = nn.L1Loss(reduction='sum')
def forward(self, pred_heatmap, pred_reg, target_heatmap, target_reg, mask):
"""
Args:
mask: [N] 标识哪些位置需要计算回归损失
"""
# 分类损失
cls_loss = self.focal_loss(pred_heatmap, target_heatmap)
# 回归损失(只计算正样本位置)pred_reg = pred_reg.permute(0,2,3,1) # [B,C,H,W] -> [B,H,W,C]
pred_reg = pred_reg[mask > 0]
target_reg = target_reg[mask > 0]
reg_loss = self.reg_loss(pred_reg, target_reg) / max(1, mask.sum())
return cls_loss + 0.1 * reg_loss # 需平衡权重
调优技巧
Heatmap 阈值设置
- 过高 :模型对弱特征不敏感,漏检增多
- 过低 :产生大量误检
建议策略:
- 初始训练使用较低阈值(如 0.1)
- 微调阶段逐步提高到 0.3-0.5
- 测试时采用动态阈值(如取 top 100 得分点)
点云密度处理
- 密集区域 :适当增大高斯核 $\sigma$ 避免过拟合
- 稀疏区域 :对负样本施加更大惩罚权重
多任务平衡
- 初始阶段给分类损失更高权重(如 10:1)
- 使用动态权重调整(参考 GradNorm 算法)
- 监控各任务梯度幅值差异
避坑指南
数值溢出问题
- 症状 :训练初期出现 NaN
- 解决方案 :
- 对 z 坐标做 log 缩放:$\hat{z} = \log(z + \epsilon)$
- 限制 heatmap 最小值:$\max(H_{ij}, 1e-6)$
- 使用混合精度训练
显存优化
- 采用梯度累积(accumulation_steps=4)
- 使用稀疏卷积处理大场景
- 降低 heatmap 分辨率(1/ 4 原图)
指标与效果差异
- 原因 :验证指标(如 AP)与训练损失不对齐
- 对策 :
- 添加中心点距离惩罚项
- 在验证集上微调 heatmap 阈值
- 监控假阳性样本的分布特征
延伸思考
对于旋转物体检测(如停车场车辆),可改进损失函数:
- 将方向预测从标量改为向量形式:
$$ L_\theta = 1 – \cos(\hat{\theta} – \theta) $$ - 添加 IoU-aware 分支:
$$ L_{iou} = -\log(\text{IoU}(\hat{b}, b)) $$ - 使用解耦回归头(separate heads for orientation and size)
这些改进能显著提升旋转物体的检测精度,但同时会增加训练复杂度。建议先验证基础版本效果,再逐步引入高级特性。
