面向 KITTI 3D 目标检测的 PointPillars 部署工程,支持从 PyTorch checkpoint 拆分导出 ONNX、ONNXRuntime/MNN 推理、结果诊断、BEV/相机图可视化与输出对齐。
CSDN 资源下载链接:https://download.csdn.net/download/m0_60827485/92963979
1. 项目定位
本工程面向 KITTI 3D 目标检测 场景,围绕 zhulf0804/PointPillars 的 epoch_160.pth checkpoint,构建一套可部署、可验证、可视化、可对齐的 PointPillars 推理工程。
工程目标不是单纯训练模型,而是完成从 PyTorch 参考模型 → 拆分 ONNX → ONNXRuntime/MNN 推理 → 后处理 → BEV/相机图可视化 → 结果对齐诊断 的完整部署链路。
2. 资源说明
2.1 CSDN 资源下载
资源下载地址:
text
https://download.csdn.net/download/m0_60827485/92963979
建议资源内容包括:
- PointPillars 部署工程源码;
- 示例 KITTI 输入数据;
- PyTorch checkpoint 配置说明;
- 拆分 ONNX 模型或导出脚本;
- ONNXRuntime/MNN 推理脚本;
- BEV 与相机图可视化结果;
- 部署说明与调试命令。
2.2 推荐目录结构
text
PointPillars_deploy/
├── configs/ # KITTI/运行时配置
├── data/ # 示例 KITTI 输入:点云、图像、标定、标签
├── models/ # checkpoint、ONNX、MNN 与 manifest
│ ├── checkpoints/ # PyTorch checkpoint
│ ├── pfn.onnx
│ ├── backbone_head.onnx
│ ├── pfn_sim.onnx
│ ├── backbone_head_sim.onnx
│ ├── pfn.mnn
│ └── backbone_head.mnn
├── outputs_demo/ # 示例推理与可视化输出
├── pointpillars/ # PointPillars 模型、ops、IO、后处理、可视化工具
├── ThirdParty/ # ONNXRuntime C/C++ 等第三方库
└── tools/ # 模型准备、导出、推理、验证、可视化、对比脚本
3. 总体部署架构
PointPillars 原始 PyTorch 模型包含点云体素化、Pillar 特征编码、BEV 特征散射、Backbone/Neck、检测头、anchor decode、NMS、坐标投影等步骤。部署时不建议直接整体导出,而是采用 拆分模型 + C++/Python 前后处理 的方式。
推荐拆分方式如下:
text
KITTI 点云 .bin
│
▼
点云读取 / 范围过滤 / Pillarization
│
├── pillar_features
├── pillar_mask
└── coords
│
▼
PFN ONNX/MNN
│
▼
pillar_embed
│
▼
Scatter BEV
│
▼
bev_feature
│
▼
Backbone + Neck + Head ONNX/MNN
│
├── cls_preds
├── box_preds
└── dir_cls_preds
│
▼
Anchor Decode / Direction 修正 / Rotated NMS
│
▼
3D 检测框 JSON
│
├── BEV 可视化
└── 相机图投影可视化
当前工程的模型拆分方式:
| 模块 | 输入 | 输出 | 部署位置 |
|---|---|---|---|
| Pillarization | 原始点云 | pillar_features / pillar_mask / coords |
Python/C++ 前处理 |
| PFN | pillar_features + pillar_mask |
pillar_embed |
ONNX/MNN |
| Scatter | pillar_embed + coords |
bev_feature |
Python/C++ 前处理 |
| Backbone/Neck/Head | bev_feature |
cls_preds / box_preds / dir_cls_preds |
ONNX/MNN |
| Decode/NMS | Head 输出 | 3D boxes | Python/C++ 后处理 |
| Visualization | boxes + calib + image | BEV/相机图 | Python/C++ 可视化 |
4. 关键配置
4.1 点云范围
yaml
point_cloud_range: [0.0, -39.68, -3.0, 69.12, 39.68, 1.0]
含义:
text
x: [0.0, 69.12]
y: [-39.68, 39.68]
z: [-3.0, 1.0]
4.2 Voxel 参数
yaml
voxel_size: [0.16, 0.16, 4.0]
grid_size: [432, 496, 1]
max_points_per_pillar: 32
max_pillars: 12000
4.3 类别顺序
必须保持与源码一致:
text
0 -> Pedestrian
1 -> Cyclist
2 -> Car
如果类别顺序错误,会出现 Car 被画成 Pedestrian、Pedestrian anchor 解成 Car box 等问题。
4.4 Anchor 参数
推荐使用源码顺序:
text
Pedestrian: [w=0.6, l=0.8, h=1.73], z=-0.60
Cyclist: [w=0.6, l=1.76, h=1.73], z=-0.60
Car: [w=1.6, l=3.9, h=1.56], z=-1.78
rotations: [0, 1.57]
5. 环境部署
5.1 Python 环境
建议 Python 3.9+:
bash
python3 -m pip install numpy opencv-python pyyaml onnx onnxruntime onnxsim torch
依赖说明:
| 依赖 | 作用 |
|---|---|
torch |
PyTorch checkpoint 加载与参考推理 |
onnx |
ONNX 导出与模型检查 |
onnxruntime |
ONNX 推理验证 |
onnxsim |
ONNX 简化 |
opencv-python |
BEV/相机图可视化 |
pyyaml |
配置文件读取 |
numpy |
前后处理与数据格式转换 |
5.2 MNN 环境
MNN 部署需要准备:
text
MNNConvert
MNN Python 包,或 C++ 推理库
ONNX 转 MNN 示例:
bash
tools/convert_mnn.sh --mnnconvert /path/to/MNNConvert
6. 模型准备流程
6.1 一键准备
bash
tools/prepare_models.sh
只导出/验证 ONNX,不转换 MNN:
bash
tools/prepare_models.sh --skip-mnn
只做工具链冒烟测试:
bash
tools/prepare_models.sh --dummy-export --skip-mnn
6.2 分步执行
bash
python tools/download_models.py --manifest models/model_manifest.json
python tools/export_pointpillars_onnx.py \
--manifest models/model_manifest.json
python tools/verify_onnx.py \
--manifest models/model_manifest.json
tools/simplify_onnx.sh
tools/convert_mnn.sh
6.3 从 checkpoint 导出拆分 ONNX
bash
python tools/export_pointpillars_split.py \
--ckpt models/checkpoints/epoch_160.pth \
--config configs/pointpillars_kitti.yaml \
--pfn-out models/pfn.onnx \
--backbone-out models/backbone_head.onnx \
--opset 17
导出后建议固定输入尺寸并简化:
bash
tools/simplify_onnx.sh
输出:
text
models/pfn_sim.onnx
models/backbone_head_sim.onnx
7. PyTorch 参考推理
PyTorch 参考推理用于确认 checkpoint、输入数据、标定文件和可视化流程是否正确。
bash
python tools/infer_pointpillars_pytorch.py \
--ckpt models/checkpoints/epoch_160.pth \
--pc-path data/sample.bin \
--calib-path data/sample_calib.txt \
--img-path data/sample.png \
--gt-path data/sample_label.txt \
--device cpu \
--save-dir outputs/pytorch_ref \
--save-bev \
--save-image \
--show-gt
输出示例:
text
outputs/pytorch_ref/detections_pytorch_refactor.json
outputs/pytorch_ref/vis_bev.png
outputs/pytorch_ref/vis_image.png
PyTorch 结果应作为 ONNX/MNN 对齐的基准。
8. ONNX 推理流程
bash
python tools/infer_pointpillars_onnx.py \
--pc-path data/sample.bin \
--calib-path data/sample_calib.txt \
--img-path data/sample.png \
--gt-path data/sample_label.txt \
--pfn models/pfn_sim.onnx \
--backbone models/backbone_head_sim.onnx \
--backend onnx \
--save-dir outputs/onnx \
--visualize \
--score-thr 0.3 \
--nms-thr 0.01 \
--nms-pre 100 \
--max-num 50
输出:
text
outputs/onnx/detections_runtime.json
outputs/onnx/vis_bev.png
outputs/onnx/vis_image.png
8.1 ONNX 对齐重点
ONNX 侧容易出现以下问题:
| 问题 | 现象 | 修复方向 |
|---|---|---|
| PFN 输入特征维度不一致 | 检测框位置大幅偏移 | 确认 pillar_features 最后一维与导出模型一致 |
| scatter y 方向不一致 | BEV 上检测整体上下/左右偏移 | 对齐源码 scatter 坐标定义 |
| anchor stride 错误 | y 方向集中到半幅区域 | 使用 head 输出 H/W 覆盖完整 point cloud range |
| 类别/anchor 顺序错误 | 类别错乱、尺寸异常 | 固定 Pedestrian/Cyclist/Car 顺序 |
| NMS 不一致 | 同一目标重复框很多 | 使用 rotated NMS,按 score 排序后映射索引 |
| 图像投影未过滤 | 相机图出现长射线 | 过滤相机后方、深度过小、投影跨度异常框 |
9. MNN 推理流程
MNN 推荐沿用 ONNX 拆分结构:
text
pfn.onnx -> pfn.mnn
backbone_head.onnx -> backbone_head.mnn
转换命令:
bash
tools/convert_mnn.sh --mnnconvert /path/to/MNNConvert
启用 FP16:
bash
tools/convert_mnn.sh --mnnconvert /path/to/MNNConvert --fp16
MNN 推理命令示例:
bash
python tools/infer_pointpillars_onnx.py \
--pc-path data/sample.bin \
--calib-path data/sample_calib.txt \
--img-path data/sample.png \
--gt-path data/sample_label.txt \
--pfn models/pfn.mnn \
--backbone models/backbone_head.mnn \
--backend mnn \
--save-dir outputs/mnn \
--visualize
10. C++ 部署设计
10.1 推荐模块划分
text
cpp/
├── include/
│ ├── PointPillarsPipeline.hpp
│ ├── VoxelGenerator.hpp
│ ├── PillarFeatureBuilder.hpp
│ ├── ScatterBEV.hpp
│ ├── RuntimeEngine.hpp
│ ├── OnnxRuntimeEngine.hpp
│ ├── MNNRuntimeEngine.hpp
│ ├── AnchorDecoder.hpp
│ ├── RotatedNMS.hpp
│ ├── Calibration.hpp
│ └── Visualizer.hpp
├── src/
│ ├── PointPillarsPipeline.cpp
│ ├── VoxelGenerator.cpp
│ ├── ScatterBEV.cpp
│ ├── OnnxRuntimeEngine.cpp
│ ├── MNNRuntimeEngine.cpp
│ ├── AnchorDecoder.cpp
│ ├── RotatedNMS.cpp
│ ├── Calibration.cpp
│ └── Visualizer.cpp
└── demo/
└── demo_pointpillars.cpp
10.2 Pipeline 接口
cpp
struct Detection3D {
float x;
float y;
float z;
float w;
float l;
float h;
float yaw;
float score;
int cls_id;
};
class PointPillarsPipeline {
public:
bool Init(const std::string& runtime_config);
std::vector<Detection3D> Infer(const std::vector<float>& point_cloud_xyzi);
};
10.3 RuntimeEngine 抽象
cpp
class IRuntimeEngine {
public:
virtual ~IRuntimeEngine() = default;
virtual bool LoadModel(const std::string& model_path) = 0;
virtual bool Infer(const std::vector<Tensor>& inputs,
std::vector<Tensor>& outputs) = 0;
};
ONNXRuntime 与 MNN 分别实现:
text
OnnxRuntimeEngine : IRuntimeEngine
MNNRuntimeEngine : IRuntimeEngine
这样 C++ 端可以通过配置切换后端:
yaml
runtime_backend: onnx # onnx / mnn
pfn_model: models/pfn_sim.onnx
backbone_model: models/backbone_head_sim.onnx
11. 数据格式定义
11.1 输入点云
KITTI .bin 点云格式:
text
float32 x, y, z, intensity
读取后形状:
text
[N, 4]
11.2 PFN 输入
text
pillar_features: [1, max_pillars, max_points_per_pillar, feature_dim]
pillar_mask: [1, max_pillars, max_points_per_pillar, 1]
coords: [max_pillars, 4]
11.3 Backbone 输入
text
bev_feature: [1, 64, H, W]
11.4 Head 输出
text
cls_preds: [1, 18, H, W]
box_preds: [1, 42, H, W]
dir_cls_preds: [1, 12, H, W]
11.5 输出 JSON
json
{
"x": 10.3070,
"y": 0.0077,
"z": -1.7240,
"w": 1.6548,
"l": 3.5103,
"h": 1.5760,
"yaw": -1.6265,
"score": 0.9654,
"cls_id": 2,
"class_name": "Car"
}
12. 结果验证与对齐
12.1 PyTorch vs ONNX 对比
bash
python tools/compare_outputs.py \
--ref outputs/pytorch_ref/detections_pytorch_refactor.json \
--test outputs/onnx/detections_runtime.json \
--tolerance 1e-3
12.2 诊断检测结果
bash
python tools/diagnose_pointpillars_output.py outputs/onnx/detections_runtime.json
重点检查:
text
box 数量
类别分布
score 分布
x/y/z 范围
w/l/h 是否符合类别尺寸
yaw 是否异常
重复框数量
是否存在投影异常框
12.3 重新可视化
bash
python tools/visualize_detection.py \
--points data/sample.bin \
--detections outputs/onnx/detections_runtime.json \
--image data/sample.png \
--calib data/sample_calib.txt \
--label data/sample_label.txt \
--bev-output outputs/onnx/vis_bev_replot.png \
--image-output outputs/onnx/vis_image_replot.png \
--bev-reference-style
13. 常见问题与修复建议
13.1 BEV 中 GT 与 Pred 旋转角度不一致
原因:
text
GT camera box 维度顺序错误
bbox_camera2lidar yaw 被额外转换
BEV corner 公式与源码不一致
修复:
text
GT camera box 使用 [x, y, z, l, h, w, ry]
GT lidar box 仅用于 BEV
图像 GT 直接 camera box -> image
BEV GT/Pred 统一使用 bbox3d2bevcorners
13.2 图像中出现异常长射线
原因:
text
NMS 未生效
投影时存在相机后方或近深度 corner
box decode 异常导致尺寸过大
calib 与点云/图像不匹配
修复:
text
使用 rotated NMS
过滤 camera z <= min_depth 的 box
过滤投影跨度过大的 box
确认 calib/image/point cloud 同帧
13.3 ONNX 与 PyTorch 结果不一致
优先检查:
text
PFN 输入 feature_dim
pillar 坐标顺序
scatter y 方向
decode anchor_y stride
anchor size/class 顺序
head 输出顺序
NMS 实现
13.4 检测框集中到 y 负方向或正方向
原因通常不是简单镜像,而是:
text
scatter 坐标解释与 decode anchor_y 不一致
head stride 使用 voxel_size=0.16,而不是 point_cloud_range / head feature size
修复:
text
anchor_x = x_min + (col + 0.5) * ((x_max - x_min) / W)
anchor_y = y_min + (row + 0.5) * ((y_max - y_min) / H)