本文以地平线 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 的几何语义拆分:
几个关键点需要强调:
- rot 使用 sin / cos 表达这是为了避免角度回归的周期不连续问题,因此训练回归维度中,yaw 始终占用 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
原因很明确:
- BPU / IR 编译需要 静态、确定形状的输入
- 点云体素化包含动态结构,不利于导出
- 将点云预处理放到外部流水线(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 开关,实际影响的是 跨模块的系统行为
理解 从模型结构 → 语义拆分 → 工具链部署 的完整链路,才能正确使用参考算法。