0 概述
该示例为参考算法,仅作为在J5上模型部署的设计参考,非量产算法
1 性能精度指标
数据集 | backbone | 精度 | infer+后处理 | 帧率(J5/双核) |
|---|---|---|---|---|
Kitti3D | RPN | 76.69(99.12%) | 25.8ms+ 2.6ms | 116 fps |
点云数量 | 点云范围 | Voxel 尺寸 | 最大点数 | 最大pillar数 | 检测类别 |
|---|---|---|---|---|---|
15W | [0, -39.68, -3, 69.12, 39.68, 1] | [0.16, 0.16, 4.0] | 100 | 12000 | "car" |
2 模型介绍
PointPillars的最大贡献是在VoxelNet中Voxel的基础上提出了一种改进版本的点云表征方法Pillar,可以将点云转换成伪图像,进而通过2D卷积实现目标检测。PointPillars整个网络结构分为三个部分:
Pillar Feature Net:将输入的点云转换为稀疏的Pseudo image;
Backbone:处理Pseudo image得到高层的特征;
Detection Head:检测和回归3D框.

2.1 模型改动点
前处理 point encoder部分,仅使用4维(官方9维),并做归一化处理,耗时减小4ms;精度表现: 浮点相比官方几乎不掉点(官方浮点77.42),但是对量化训练更友好;
PillarFeatutreNet 中的 PFNLayer 使用 Conv2d + BathNorm2d + ReLU,替换原有的 Linear + BatchNorm1d + ReLU,便于模型量化;
PillarFeatutreNet 中的 PFNLayer 使用 MaxPool2d,替换原有的 torch.max,便于性能的提升;
Scatter过程使用horizon_plugin_pytorch的point_pillars_scatter,便于模型推理优化,逻辑与公版相同。
2.2 源码说明
configs/detection/pointpillars/pointpillars_kitti_car.py 为 pointpillars 的配置文件,定义了模型结构、数据集加载,和整套训练流程,所需参数的说明在算子定义中会给出。配置文件主要内容包括:
pc_range = [0, -39.68, -3, 69.12, 39.68, 1] #截取稀疏和未标记数据
voxel_size = [0.16, 0.16, 4.0]
max_points_in_voxel = 100
max_voxels_num = 12000
class_names = ["Car"]
...
model=dict(
type="PointPillarsDetector", #model training structure
feature_map_shape=get_feature_map_size(pc_range, voxel_size),
pre_process=dict(
type="PointPillarsPreProcess",
pc_range=pc_range,
voxel_size=voxel_size,
max_voxels_num=max_voxels_num,
max_points_in_voxel=max_points_in_voxel,
),
reader=dict(
type="PillarFeatureNet",
with_distance=False,
quantize=True,
use_4dim=True, #对应改动点1
pool_size=(1, max_points_in_voxel), #对应改动点3
...
),
backbone=dict(
type="PointPillarScatter",
use_horizon_pillar_scatter=True, #对应改动4
...
),
neck=dict(
type="SequentialBottleNeck",
...
use_tconv=True,
),
head=dict(
type="PointPillarHead",
...
use_direction_classifier=True,
),
anchor_generator=dict(
type="Anchor3DGeneratorStride",
anchor_sizes=[[1.6, 3.9, 1.56]], # noqa B006 Anchor的尺寸
...
unmatch_thresholds=[0.45],
),
targets=dict(
type="LidarTargetAssigner",
...
region_similarity_calculator=dict(type="NearestIouSimilarity"),
),
loss=dict(
type="PointPillarsLoss",
num_classes=len(class_names),
loss_cls=dict(
type="SigmoidFocalLoss",
...
),
loss_bbox=dict(
type="WeightedSmoothL1Loss",
...
),
loss_dir=dict(
type="WeightedSoftmaxClassificationLoss",
...
),
),
postprocess=dict(
type="PointPillarsPostProcess",
...
use_direction_classifier=True,
# test_cfg
use_rotate_nms=False,
nms_pre_max_size=1000,
...
post_center_limit_range=[0, -39.68, -5, 69.12, 39.68, 5],
),
)
#deploy model and input
deploy_model=dict(...)
deploy_inputs = dict(
points=[
torch.randn(150000, 4),
],
)
#train数据处理
train_set=dict(
...
)
dataloader=dict(...)
val_data_loader=dict(...)
#callbacks 定义
stat_callback = dict(...)
ckpt_callback = dict(...)
val_callback = dict(...)
#训练策略配置
float_trainer=dict(...)
calibration_trainer=dict(...)
qat_trainer=dict(...)
int_infer_trainer=dict(...)
#编译设置
compile_cfg = dict(...)
# predictor
float_predictor = dict(...)
calibration_predictor = dict(...)
qat_predictor = dict(...)
int_infer_predictor= dict(...)
注: 如果需要复现精度,config中的训练策略最好不要修改。否则可能会有意外的训练情况出现。
该接口是在horizon-plugin中实现,preprocess实现voxelization过程,主要是将点云数据根据预设size划分为一个个的网格。凡是落入到一个网格的点云数据被视为其处在一个 voxel里,或者理解为它们构成了一个 voxel。voxelization的实现流程见下图:

为了应用 2D 卷积架构,最终要实现将点云(P,N,4)转换为伪图像,整体步骤如下图:

该算法主要实现将点云数据的shape (1,D,P,N)经过pfn_layers后变换为(1,1,P,C)
对应代码:hat/models/task_modules/lidar/pillar_encoder.py
该层实现伪图像转换的最后一个步骤。为了获得伪图片特征,将 P 转化为(W, H),由于预先设定pillar最大值以及去除了一些空pillar,因此P<H*W,最终可以通过映射获得形如(C, H, W) 的伪图像。

Scatter实现代码在horizon_plugin_pytorch下实现,见代码:
该模型的backbone用是SequentialBottleNeck,使用多个 Conv2D + BN + ReLU 的结构将特征进行融合处理,通过多个卷积和反卷积的组合,最后在dim=1维做concat。RPN结构见下图:

pointpillars模型 head 层的输出为box_preds, cls_preds, dir_preds。对应代码路径为:
hat/models/task_modules/pointpillars/head.py
由3部分构成:loss_cls+loss_reg+loss_dir。其中,loss_cls=SigmoidFocalLoss;loss_bbox=WeightedSmoothL1Loss;loss_dir=WeightedSoftmaxClassificationLoss
对应代码:hat/models/losses/pointpillar_loss.py
pointpillars 在infer中前后处理部分主要为以下部分:
前处理:
点云pillar化(具体实现过程见voxelization章节);
对coors 0维添加batch_id,以便scatter时P到H*W的映射;
- 对voxel_feature归一化
对应路径:hat/models/task_modules/pointpillars/pre_process.py
后处理:
生成anchor;
NMS去除低质量高重叠的框;
- 生成camera、image坐标下的box
对应路径:hat/models/task_modules/pointpillars/postprocess.py

3 浮点模型训练
3.1 Before Start
3.1.1 环境部署
release_models获取路径见:horizon_model_train_sample/scripts/configs/detection/pointpillars/README.md
拉取docker环境
如需本地离线安装HAT,我们提供了训练环境的whl包,路径在ddk/package/host/ai_toolchain
3.1.2 数据准备
left color images of object data set,
velodyne point clouds,
camera calibration matrices of object data set,
taining labels of object data set ,
下载上述4个文件后,解压并按照如下方式组织文件夹结构:
为了创建 KITTI 点云数据,首先需要加载原始的点云数据并生成相关的包含目标标签和标注框的数据标注文件,同时还需要为 KITTI 数据集生成每个单独的训练目标的点云数据,并将其存储在 gt_database 的 .bin 格式的文件中,此外,需要为训练数据或者验证数据生成 .pkl 格式的包含数据信息的文件。随后,通过运行下面的命令来创建 KITTI 数据:
该过程的产出物为.pkl文件,生成时间较长
执行上述命令后,生成的文件目录如下:
3.1.3 数据打包
上面两条命令分别对应转换训练数据集和验证数据集(.pkl-->lmdb),打包完成之后,data目录下的文件结构应该如下所示:
train_lmdb 和 val_lmdb 就是打包之后的训练数据集和验证数据集,也是网络最终读取的数据集,
3.1.4 config 配置
在进行模型训练和验证之前,需要对configs文件中的部分参数进行配置,一般情况下,我们需要配置以下参数:
device_ids、batch_size_per_gpu:根据实际硬件配置进行device_ids和每个gpu的batchsize的配置;
ckpt_dir:浮点、calib、量化训练的权重路径配置,权重下载链接在config文件夹下的README中;
base_data_dir:为kitti3d数据集目录,./tmp_data/kitti3d/;
root_path:为kitti3d数据集目录,./tmp_data/kitti3d/;
db_info_path:为生成的 kitti3d_dbinfos_train.pkl 路径
data_path:为打包的lmdb路径
3.2 浮点模型训练
3.3 浮点模型精度验证
4 模型量化和编译
4.1 Calibration
4.2 Calibration 模型精度验证
calibration完成以后,可以使用以下命令验证经过calib后模型的精度:
验证完成后,会在终端输出calib模型在验证集上的检测精度。
4.3 量化模型训练
通过运行下面的脚本就可以开启模型的qat训练:
4.4 量化模型精度验证
量化模型的精度验证,只需要运行以下命令:
qat模型的精度验证对象为插入伪量化节点后的模型(float32);quantize模型的精度验证对象为定点模型(int8),验证的精度是最终的int8模型的真正精度,这两个精度应该是十分接近的。
4.5 仿真上板精度验证
除了上述模型验证之外,我们还提供和上板完全一致的精度验证方法,可以通过下面的方式完成:
4.6 量化模型编译
opt为优化等级,取值范围为0~3,数字越大优化等级越高,运行时间越长;
compile_perf脚本将生成.html文件和.hbm文件(compile文件目录下),.html文件为BPU上的运行性能,.hbm文件为上板实测文件。
5 其他工具
5.1 结果可视化
如果你希望可以看到训练出来的模型对于单帧雷达点云的检测效果,我们的tools文件夹下面同样提供了点云预测及可视化的脚本,你只需要运行以下脚本即可:
由于开发机配置不同,plt.show可能不会正常显像,可以通过plt.savefig在保存的路径中查看
6 板端部署
6.1 上板性能实测
点云模型的板端验证请务必使用真实点云输入
6.2 AIBenchmark示例
可在板端使用以下命令执行做模型评测:


