CenterPoint 模型结构与输出语义解析

0 阅读5分钟

本文以地平线 Open Explorer(OE)中的 CenterPoint 参考算法为主线,系统梳理 CenterPoint 的模型结构设计、Head 与 box 语义拆分方式,以及在工具链中从训练、导出到编译部署的完整工程语义。文末通过 nuScenes → KITTI 的一次实际配置修改,作为参考算法“应用级改造”的示例,帮助理解这些设计在真实项目中的影响范围。

一、CenterPoint 是一个怎样的 3D 检测模型?

CenterPoint 是一种 anchor-free、center-based 的 3D 目标检测方法。 它的核心思想并不是“直接预测 3D 框”,而是:

  • BEV 平面 上预测目标中心点(center)
  • 围绕 center 回归目标的几何属性

因此,CenterPoint 的输出天然拆分为多个 ​语义明确的回归分量​,例如:

  • 中心偏移(x, y)
  • 高度(z)
  • 尺寸(w, l, h)
  • 朝向(yaw)
  • 速度(vx, vy,可选)

这也是为什么在 CenterPoint 中,​Head 的设计和 box 语义是强绑定的​,而不是“随便接几个卷积头”。

二、CenterPoint 的 Head 设计与 box 语义拆分

在 OE 的 CenterPoint 配置中,Head 通常通过 common_heads 定义,例如:

common_heads = dict(
    reg=(2, 2),
    height=(1, 2),
    dim=(3, 2),
    rot=(2, 2),
    vel=(2, 2),
)

这里的每一项并不只是“网络输出通道数”,而是​明确对应 box 的几何语义拆分​:

几个关键点需要强调:

  1. rot 使用 sin / cos 表达这是为了避免角度回归的周期不连续问题,因此训练回归维度中,yaw 始终占用 2 个通道。
  2. Head 的 insertion order 很重要 CenterPoint 在 deploy 模式下会按 Head key 的顺序扁平化输出,这直接影响导出 HBIR 和最终推理输出 tensor 的语义顺序。

三、box_size 的真正含义:训练回归 vs 推理输出

在 CenterPoint 中,经常会看到如下配置:

postprocess = dict(
    box_size=9,
)

需要明确的是:

box_size 表示的是 postprocess 之后,最终输出给下游使用的 box 物理维度,而不是训练阶段的回归维度。

3.1 nuScenes 场景:box_size = 9

在 nuScenes 数据集中,CenterPoint 通常开启 velocity,因此最终输出 box 语义为:

[x, y, z, w, l, h, yaw, vx, vy]

这 9 个量分别来自:

  • reg → x, y
  • height → z
  • dim → w, l, h
  • rot → yaw(由 sin/cos 反解)
  • vel → vx, vy

因此,box_size=9 是一个​语义必然结果​,而不是经验值。

3.2 训练回归维度(code_size)与 box_size 并不相同

一个容易混淆的点是:

  • 训练阶段回归维度(code_size)
  • 推理阶段输出维度(box_size)

以 nuScenes 为例:

  • 训练回归维度 = 10
    • reg(2) + height(1) + dim(3) + rot(2) + vel(2)
  • 推理输出维度 = 9
    • yaw 从 sin/cos 合并为 1 个角度

四、deploy 模式下的输入与输出语义(工具链视角)

4.1 is_deploy=False:训练 / 评估路径

在训练或评估阶段:

  • 输入:example["points"](raw point cloud)
  • 处理流程:
pre_process → reader → backbone → neck → head → target/loss 或 postprocess

其中 pre_process 会完成体素化、点云归一化以及 pillar 特征编码,这些过程包含:

  • 动态点数
  • 非规则张量
  • 不适合直接导出为静态 IR

4.2 is_deploy=True:导出 / 部署路径

在导出 HBIR 或部署推理时:

  • 不再接收 raw 点云
  • 输入变为:
features + coors

流程变为:

reader → backbone → neck → head → flatten outputs

原因很明确:

  1. BPU / IR 编译需要 静态、确定形状的输入
  2. 点云体素化包含动态结构,不利于导出
  3. 将点云预处理放到外部流水线(CPU / DSP)更可控,也更利于性能调优

4.3 deploy_inputs 的语义约束

在 OE 示例中,deploy_inputs 通常形如:

  • features: [1, C, P, M]
  • coors: [M, 4],格式为 [batch_idx, z, y, x]

这两者必须与 CenterPointPreProcess 的输出严格一致,否则导出或推理阶段会出现维度或 scatter 错误。

五、编译阶段的输出语义:output_layout 与 transpose_dim

在模型编译配置中,常见如下设置:

compile_cfg = dict(
    output_layout="NHWC",
    transpose_dim=dict(
        outputs={"global": [0, 2, 3, 1]}
    ),
)

这里需要区分两个概念:

  • 真正生效的,是 ​transpose_dim
    • 工具链会在输出节点插入 transpose
  • output_layout 更多是 语义标注

也就是说:

是否从 NCHW 转为 NHWC,取决于 transpose_dim,而不是 output_layout 字符串本身。

工程上建议: output_layout​ 与 ​transpose_dim​​ 保持一致​,避免下游推理或解析代码误读输出格式。

六、参考算法的一次应用修改示例:nuScenes → KITTI

在理解了 CenterPoint 的模型结构与输出语义之后,再来看一个非常典型的应用场景:将 nuScenes 的 CenterPoint 配置修改为 KITTI 使用。

6.1 目标差异

  • KITTI 不定义 velocity
  • 最终推理输出 box 语义应为:
[x, y, z, w, l, h, yaw]

即:

  • 推理 box_size = 7
  • 训练回归维度仍为 8(rot 使用 sin/cos)

6.2 修改不是“改一个字段”,而是系统性同步

这类修改必须同步覆盖以下模块:

  • Head(移除 vel)
  • Target / Loss(不再生成或监督 velocity)
  • PostProcess / BBoxCoder(不再 decode vel)
  • deploy flatten 输出顺序
  • 下游解析与评测逻辑

一个实用的自检原则是:

训练回归维度 = len(code_weights) = target anno_box dim推理输出维度 = ​​**postprocess.box_size**

二者必须分别自洽,否则一定存在隐患。

七、小结

CenterPoint 在地平线工具链中并不是一个“只改配置就能安全迁移”的模型:

  • box 语义来自 Head 设计
  • 训练与推理的维度并不等价
  • deploy 模式下的输入、输出和编译布局都有严格约束
  • 一个看似简单的 with_velocity 开关,实际影响的是 跨模块的系统行为

理解 从模型结构 → 语义拆分 → 工具链部署 的完整链路,才能正确使用参考算法。