小言_互联网的博客

动手学无人驾驶(4):基于激光雷达点云数据3D目标检测

363人阅读  评论(0)

上一篇文章《动手学无人驾驶(3):基于激光雷达3D多目标追踪》介绍了3D多目标追踪,多目标追踪里使用的传感器数据为激光雷达Lidar检测到的数据,本文就介绍如何基于激光雷达点云数据进行目标检测。

本文介绍论文:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》

Github项目地址:https://github.com/sshaoshuai/PointRCNN

KITTI数据集百度云下载地址:https://pan.baidu.com/s/1qDzslt5mwI_JkEbumGJSUA 提取码: ct4q


 

目录

1.KITTI数据集

2.PointRCNN

3.目标检测结果

参考资料


1.KITTI数据集

KITTI目标检测数据集(http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)中共包含7481张训练图片和7518张测试图片,以及与之相对应的点云数据和label以及calib文件。这里以训练集编号为000008的场景为例介绍KITTI数据集中calib和label文件所含信息。

                                                                   (training/image_2/000008.png)

Calib文件:在KITTI数据采集过程中使用到了摄像头和激光雷达,采集到的数据坐标分别为摄像头坐标和激光雷达坐标下的测量值,这时就需要calib文件将摄像头与激光雷达进行标定,calib文件通过txt格式来保存。

velodyne:velodyne中存储着激光雷达点云采集到数据,数据以2进制格式存储(.bin)点云数据存储格式为(N,4)。N为激光线束反射点个数,4代表着:(x,y,z,r),分别返回在3个坐标轴上的位置和反射率

label文件:label文件为标注数据,以txt格式保存,000008.txt中标注的object内容如下:

Car 0.88 3 -0.69 0.00 192.37 402.31 374.00 1.60 1.57 3.23 -2.70 1.74 3.68 -1.29

Car 0.00 1 2.04 334.85 178.94 624.50 372.04 1.57 1.50 3.68 -1.17 1.65 7.86 1.90

Car 0.34 3 -1.84 937.29 197.39 1241.00 374.00 1.39 1.44 3.08 3.81 1.64 6.15 -1.31

Car 0.00 1 -1.33 597.59 176.18 720.90 261.14 1.47 1.60 3.66 1.07 1.55 14.44 -1.25

Car 0.00 0 1.74 741.18 168.83 792.25 208.43 1.70 1.63 4.08 7.24 1.55 33.20 1.95

Car 0.00 0 -1.65 884.52 178.31 956.41 240.18 1.59 1.59 2.47 8.48 1.75 19 -1.25

DontCare -1 -1 -10 800.38 163.67 825.45 184.07 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 859.58 172.34 886.26 194.51 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 801.81 163.96 825.20 183.59 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 826.87 162.28 845.84 178.86 -1 -1 -1 -1000 -1000 -1000 -10

关于label文件的理解可以参考下面的代码:


  
  1. class Object3d(object):
  2. ''' 3d object label '''
  3. def __init__(self, label_file_line):
  4. data = label_file_line.split( ' ')
  5. data[ 1:] = [float(x) for x in data[ 1:]]
  6. # extract label, truncation, occlusion
  7. self.type = data[ 0] # 'Car', 'Pedestrian', ...
  8. self.truncation = data[ 1] # truncated pixel ratio [0..1]
  9. self.occlusion = int(data[ 2]) # 0=visible, 1=partly occluded, 2=fully occluded, 3=unknown
  10. self.alpha = data[ 3] # object observation angle [-pi..pi]
  11. # extract 2d bounding box in 0-based coordinates
  12. self.xmin = data[ 4] # left
  13. self.ymin = data[ 5] # top
  14. self.xmax = data[ 6] # right
  15. self.ymax = data[ 7] # bottom
  16. self.box2d = np.array([self.xmin,self.ymin,self.xmax,self.ymax])
  17. # extract 3d bounding box information
  18. self.h = data[ 8] # box height
  19. self.w = data[ 9] # box width
  20. self.l = data[ 10] # box length (in meters)
  21. self.t = (data[ 11],data[ 12],data[ 13]) # location (x,y,z) in camera coord.
  22. self.ry = data[ 14] # yaw angle (around Y-axis in camera coordinates) [-pi..pi]
  23. def print_object(self):
  24. print( 'Type, truncation, occlusion, alpha: %s, %d, %d, %f' % \
  25. (self.type, self.truncation, self.occlusion, self.alpha))
  26. print( '2d bbox (x0,y0,x1,y1): %f, %f, %f, %f' % \
  27. (self.xmin, self.ymin, self.xmax, self.ymax))
  28. print( '3d bbox h,w,l: %f, %f, %f' % \
  29. (self.h, self.w, self.l))
  30. print( '3d bbox location, ry: (%f, %f, %f), %f' % \
  31. (self.t[ 0],self.t[ 1],self.t[ 2],self.ry))

2.PointRCNN

3D目标检测模型PointRCNN借鉴了PointNet++和RCNN的思想,提出了自底向上的生成和调整候选检测区域的算法,网络结构如下图所示:

PointRCNN的网络结构分为两个阶段:第一阶段自底向上生成3D候选预测框;第二阶段在规范坐标中对候选预测框进行搜索和微调,得到更为精确的预测框作为检测结果。

第一阶段:对3D点云数据进行语义分割和前背景划分,生成候选预测框,有如下三个关键步骤:

  • 点云特征提取:通过PointNet++对点云数据进行编码和解码,提取点云特征向量。

  • 前景点分割:根据提取的点云特征向量,使用focal loss区分前景点和背景点。focal loss能有效地平衡前景点和背景点比例失衡问题,从而得到更为准确的分类效果。

  • 生成候选框:采用候选框箱模型(bin)的方法,将前背景点分割信息生成预测候选框。

举例来说,将候选框定义为参数(x,y,z,h,w,l,θ)表征的空间中的箱体,其中(x,y,z)为箱体中心坐标,( h,w,l)为箱体在中心坐标方向上的大小,θ为鸟瞰视角上(y方向从上往下看)箱体在x-z平面的角度。

bin的执行方式为:先根据前景点的分割信息粗分其所属的箱体;再在箱体内部对其做回归,得到箱体参数作为预测框;最后对预测框做NMS(Non-Max Suppress,非极大值抑制),得到最终预测候选框。

第二阶段:在规范坐标中微调候选预测框,获得最终的检测结果,有如下四个关键部分:

  • 区域池化:对候选框内每个点的特征进行池化。

  • 坐标转化:为了更好地获取局部信息,需要将多个候选区域中的前景点坐标(同一个坐标系)转化为局域坐标系中的规范坐标(以预测框为中心点的多个坐标系),如下图所示:

  • 特征编码:将规范坐标时丢失的深度信息、规范后的坐标信息、前后背景语义信息等经过多层感知机提取特征,作为每个点的编码特征。

  • 微调预测框:经过上一步编码后的特征,经PointNet++网络进行特征提取,最后回归得到局部坐标系下的3D预测框。

PointRCNN网络代码如下:


  
  1. class PointRCNN(nn.Module):
  2. def __init__(self, num_classes, use_xyz=True, mode='TRAIN'):
  3. super().__init__()
  4. assert cfg.RPN.ENABLED or cfg.RCNN.ENABLED
  5. if cfg.RPN.ENABLED:
  6. self.rpn = RPN(use_xyz=use_xyz, mode=mode)
  7. if cfg.RCNN.ENABLED:
  8. rcnn_input_channels = 128 # channels of rpn features
  9. if cfg.RCNN.BACKBONE == 'pointnet':
  10. self.rcnn_net = RCNNNet(num_classes=num_classes, input_channels=rcnn_input_channels, use_xyz=use_xyz)
  11. elif cfg.RCNN.BACKBONE == 'pointsift':
  12. pass
  13. else:
  14. raise NotImplementedError
  15. def forward(self, input_data):
  16. if cfg.RPN.ENABLED:
  17. output = {}
  18. # rpn inference
  19. with torch.set_grad_enabled(( not cfg.RPN.FIXED) and self.training):
  20. if cfg.RPN.FIXED:
  21. self.rpn.eval()
  22. rpn_output = self.rpn(input_data)
  23. output.update(rpn_output)
  24. # rcnn inference
  25. if cfg.RCNN.ENABLED:
  26. with torch.no_grad():
  27. rpn_cls, rpn_reg = rpn_output[ 'rpn_cls'], rpn_output[ 'rpn_reg']
  28. backbone_xyz, backbone_features = rpn_output[ 'backbone_xyz'], rpn_output[ 'backbone_features']
  29. rpn_scores_raw = rpn_cls[:, :, 0]
  30. rpn_scores_norm = torch.sigmoid(rpn_scores_raw)
  31. seg_mask = (rpn_scores_norm > cfg.RPN.SCORE_THRESH).float()
  32. pts_depth = torch.norm(backbone_xyz, p= 2, dim= 2)
  33. # proposal layer
  34. rois, roi_scores_raw = self.rpn.proposal_layer(rpn_scores_raw, rpn_reg, backbone_xyz) # (B, M, 7)
  35. output[ 'rois'] = rois
  36. output[ 'roi_scores_raw'] = roi_scores_raw
  37. output[ 'seg_result'] = seg_mask
  38. rcnn_input_info = { 'rpn_xyz': backbone_xyz,
  39. 'rpn_features': backbone_features.permute(( 0, 2, 1)),
  40. 'seg_mask': seg_mask,
  41. 'roi_boxes3d': rois,
  42. 'pts_depth': pts_depth}
  43. if self.training:
  44. rcnn_input_info[ 'gt_boxes3d'] = input_data[ 'gt_boxes3d']
  45. rcnn_output = self.rcnn_net(rcnn_input_info)
  46. output.update(rcnn_output)
  47. elif cfg.RCNN.ENABLED:
  48. output = self.rcnn_net(input_data)
  49. else:
  50. raise NotImplementedError
  51. return output

3.目标检测结果

介绍完KITTI数据集和PointRCNN模型后,现在用作者预训练好的模型进行目标预测。

1)首先是准备数据,从百度网盘里下载KITTI数据后按如下方式排放数据:


  
  1. PointRCNN
  2. ├── data
  3. │ ├── KITTI
  4. │ │ ├── ImageSets
  5. │ │ ├── object
  6. │ │ │ ├──training
  7. │ │ │ ├──calib & velodyne & label_2 & image_2
  8. │ │ │ ├──testing
  9. │ │ │ ├──calib & velodyne & image_2
  10. ├── lib
  11. ├── pointnet2_lib
  12. ├── tools

2)将预训练好的模型放入/tools文件夹下,执行如下命令,此时会对验证集进行预测:

python eval_rcnn.py --cfg_file cfgs/default.yaml --ckpt PointRCNN.pth --batch_size 1 --eval_mode rcnn --set RPN.LOC_XZ_FINE False

预测结果如下,这里预测的平均准确率略小于原作者预测的。


   
  1. # 原作者预测结果
  2. Car AP@ 0. 70, 0.70, 0.70:
  3. bbox AP: 96.91, 89.53, 88.74
  4. bev AP: 90.21, 87.89, 85.51
  5. 3d AP: 89.19, 78.85, 77.91
  6. aos AP: 96.90, 89.41, 88.54

   
  1. # 使用预训练模型输出的结果
  2. Car AP@ 0. 70, 0.70, 0.70:
  3. bbox AP: 90.3697, 78.9661, 76.0439
  4. bev AP: 88.8698, 75.5572, 69.7311
  5. 3d AP: 83.3413, 66.9504, 60.1443
  6. aos AP: 90.30, 78.51, 75.45

参考资料

飞桨火力全开,重磅上线3D模型:PointNet++、PointRCNN!

PointRCNN地址:https://github.com/sshaoshuai/PointRCNN

论文:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》

 


转载:https://blog.csdn.net/cg129054036/article/details/105372783
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场