pointpillars deployment 学习

pointpillar网络结构组成

  1. voxelizatio(pillarization), 把3d点云划分,得到pillar,并关联pillar的坐标和点云中的点
  2. pfe(pillar feature extracation),通过简单的pointnet网络,提取pillar内点的特征
  3. scatter to bev, 把pillar的特征,重新分布到2d的image上,得到2d sudo image
  4. 2d backbone + detector(ssd or centernet), 2d的目标检测网络,得到中间结果
  5. postprocess(decode + nms),后处理中间结果,得到最终的结果

目前网上开源的部署有:

1 apollo
https://github.com/ApolloAuto/apollo/tree/master/modules/perception/lidar
2 autoware
https://github.com/Autoware-AI/core_perception/tree/master/lidar_point_pillars
3 carkuls(centerpoint Open3D框架)
https://github.com/CarkusL/CenterPoint
4 abraham(centerpoint Open3D框架)
https://github.com/Abraham423/CenterPointTensorRT
5 nvidia(openpcdet框架)
https://github.com/NVIDIA-AI-IOT/CUDA-PointPillars
https://developer.nvidia.com/zh-cn/blog/detecting-objects-in-point-clouds-with-cuda-pointpillars/

对比如上开源的部署方式:
1 apollo
如其代码,分为5个部分进行部署

Preprocess(in_points_array, in_num_points);
  
auto pfe_output =
      pfe_net_
          .forward({tensor_pillar_point_feature, tensor_num_points_per_pillar,
                    tensor_pillar_coors})
          .toTensor();

auto scattered_feature =
      scattered_net_
          .forward({pfe_output, tensor_pillar_coors, scattered_batch_size})
          .toTensor();

auto backbone_feature = backbone_net_.forward({scattered_feature});
auto fpn_feature = fpn_net_.forward({backbone_feature});
auto bbox_head_output = bbox_head_net_.forward({fpn_feature}).toTuple();
  
postprocess_cuda_ptr_->DoPostprocessCuda(
      bbox_pred.data_ptr<float>(),
      cls_score.data_ptr<float>(),
      dir_cls_preds.data_ptr<float>(),
      dev_anchor_mask_, dev_anchors_px_, dev_anchors_py_, dev_anchors_pz_,
      dev_anchors_dx_, dev_anchors_dy_, dev_anchors_dz_, dev_anchors_ro_,
      dev_filtered_box_, dev_filtered_score_, dev_filtered_label_,
      dev_filtered_dir_, dev_box_for_nms_, dev_filter_count_, out_detections,
      out_labels);

2 autoware
如其代码,部署分为5部分,apollo和autoware很类似,不知道谁参考谁的

preprocess(in_points_array, in_num_points);

std::vector<float> pfe_out = pfe_engine_ptr_->schedule(pfe_net_input_);

scatter_cuda_ptr_->doScatterCuda(host_pillar_count_[0], dev_x_coors_,
                                    dev_y_coors_, (float*)pfe_net_output,
                                    dev_scattered_feature_);

RPN_Net_Output rpn_out = rpn_engine_ptr_->schedule(rpn_net_input_);

postprocess_cuda_ptr_->doPostprocessCuda((float*)rpn_buffers_[0],
                                           (float*)rpn_buffers_[1],
                                           (float*)rpn_buffers_[2],
                                           dev_anchor_mask_,
                                           dev_anchors_px_,
                                           dev_anchors_py_,
                                           dev_anchors_pz_,
                                           dev_anchors_dx_,
                                           dev_anchors_dy_,
                                           dev_anchors_dz_,
                                           dev_anchors_ro_,
                                           dev_filtered_box_,
                                           dev_filtered_score_,
                                           dev_filtered_dir_,
                                           dev_box_for_nms_,
                                           dev_filter_count_,
                                           out_detections);

5 nvidia
如下为流程图和代码,分为4步,前两步为预处理,中间为TRT推理engine,最后为后处理。这里和其他的网络还是有很大不同的,中间的一步,涵盖了其他网络的pfe, scatter to bev, net infer三步。这里的关键就是,scatter to bev的这个trt的插件,把前后两个网络串联起来了。带导出模型的script。
pointpillars deployment 学习_第1张图片
pointpillars deployment 学习_第2张图片

pre_->generateVoxels((float*)points_data, points_size,
        params_input_,
        voxel_features_, 
        voxel_num_points_,
        coords_);
        
pre_->generateFeatures(voxel_features_,
      voxel_num_points_,
      coords_,
      params_input_,
      features_input_);

trt_->doinfer(buffers);

post_->doPostprocessCuda(cls_output_, box_output_, dir_cls_output_,
                          bndbox_output_);

3 carkusl
如代码,基本分为3步,preprocess, net infer, postprocess, 其中net infer在onnx导出时,分为三部分,pfe_onnx, rpn_onnx,通过scatterND,连接起来了,成为一个。这一点和nvidia的类似。其中包含导出模型的script。

preprocess(points, hostPillars, hostIndex, pointNum);
bool status = context->executeV2(buffers.getDeviceBindings().data());
postprocess(buffers, predResult);

4 abraham
如流程图,分为5步,同apollo和autoware.

preprocessGPU(dev_points, devicePillars,deviceIndices, 
        _PMask, _PBEVIdxs,  _PPointNumAssigned,  _BEVVoxelIdx, _VPointSum,  _VRange,  _VPointNum,
         pointNum, POINT_DIM);
         
bool status = context->executeV2(buffers.getDeviceBindings().data());

scatter_cuda_ptr_->doScatterCuda(MAX_PILLARS, deviceIndices,static_cast<float*>(buffers.getDeviceBuffer(mParams.pfeOutputTensorNames[0])), 
                                                                //   static_cast(buffersRPN.getDeviceBuffer(mParamsRPN.inputTensorNames[0]) )) ;
                                                                dev_scattered_feature_);

status = contextRPN->executeV2( buffersRPN.getDeviceBindings().data());

postprocessGPU(buffersRPN, predResult, mParams.rpnOutputTensorNames,
                                                dev_score_indexs_,
                                                mask_cpu,
                                                remv_cpu,
                                                host_score_indexs_,
                                                host_keep_data_,
                                                host_boxes_,
                                                host_label_);

结论
可以分为5步或者3步进行,其中的三部分,voxelization, scatter to bev, post process,有现成的c++,cuda实现,可以直接用。
pfe/scatternd/rpn, 这三部分通过pytorch->onnx->trt engine,也有相关的转化代码,可以参考,然后修改,生成自己的模型导出script。 这部分export模型的代码,应该通pytorch官方的模型导出的方法类似。
最后把分解的几部分的模型,串联起来,导入模型,运行engine。

最近学到的心得:

  1. torch保存模型,只会保存网络中带有参数的那部分参数
  2. 为了提速,一般会把网络预处理,后处理,中间特殊结构,单独拎出来,c++,cuda直接编程处理
  3. 如何拿出网络中的特殊结构,有两种办法:在pytorch export到onnx模型时,分部分建立网络,分部分导出onnx模型;导出完整的onnx模型后,再对onnx模型进行处理,删减,替换(替换的化,需要trt engine能够处理,特殊的得自己添加trt的plugin)

更进一步
4. 跑通了pointpillar的网络模型的导出,先完整的导出模型,然后再对onnx模型进行删减,替换,修改;
5. 安装tensorrt,并完成编译,可以运行demo
6. 为了转化centerpoint,更改点如下:
a. rpn head,需要优化,这部分,在导出onnx的时候,可以直接导出
b. post process,需要重新更改一下,之前的pointpillar是anchor based的输出进行decode,现在要基于center based的方法进行decode。

你可能感兴趣的:(目标检测,pytorch,自动驾驶,深度学习)