基于体素的3D目标检测网络:VoxelNet

P粉084495128
发布: 2025-07-17 16:59:23
原创
1162人浏览过
本文基于PaddlePaddle框架复现了VoxelNet算法,这是一种基于体素的3D目标检测算法,在KITTI数据集上开展实验并提供预训练模型和在线体验。VoxelNet含特征学习网络、卷积中间层和区域候选网络,通过划分点云为体素、提取特征等实现检测。复现过程参考相关改进项目,解决了内存泄漏等问题,取得一定检测精度。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

基于体素的3d目标检测网络:voxelnet - php中文网

VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection

简介

基于体素的3D目标检测网络:VoxelNet - php中文网

本项目基于PaddlePaddle框架复现了基于体素的3D目标检测算法VoxelNet,在KITTI据集上进行了实验。 项目提供预训练模型和AiStudio在线体验NoteBook。

背景

3D检测广泛用于自主导航、家政机器人以及AR/VR。LIDAR提供可靠的深度信息用于准确定位目标并表征其形状。

现有方法

  • 将LIDAR投影到某个视角作为输入;
  • 采用3D体素,提取人工设计的体素特征;
  • 虽然有PointNet和Pointnet++这类点云学习网络,但还无法处理大规模点云数据。

这篇文章利用网络学习体素点特征,只使用点云实现了快速高效的3D目标检测。

算法解释

VoxelNet由三个功能块组成:特征学习网络、卷积中间层和区域候选网络。下面一一介绍:

  • 特征学习网络

基于体素的3D目标检测网络:VoxelNet - php中文网

首先将三维点云划分为一定数量的Voxel(就是将空间划分为一个一个栅格,用格子表示格子里的点云)并对这些voxel进行分组,再经过点的随机采样(每个格子的最大点云采样数量这里是T=35)以及归一化后,对每一个非空Voxel使用若干个VFE(Voxel Feature Encoding)层进行局部特征提取,得到Voxel-wise Feature。这里的VFE模型其实就是FC全连接模型。最后的输出形状为128×10×400×352.

  • 卷积中间层

为了聚合周围环境voxels的信息,使用3D卷积对4D tensor进行卷积,并进行reshape到3D tensor。每个卷积中间层顺序应用3D卷积、BN层和ReLU层。举例:输入尺寸(4D tensor)是128 × 10 × 400 × 352,输出尺寸(经过Convolutional Middle Layers之后)是64 × 2 × 400 × 352,然后reshape到 128 × 400 × 352变成3D tensor(注意到128 × 400 × 352正是BEV视图上的栅格尺寸)。

  • 区域候选网络

基于体素的3D目标检测网络:VoxelNet - php中文网

在提取到特征后,利用RPN模块预测候选检测框。如图所示,该网络包含三个全卷积层块(Block),每个块的第一层通过步长为2的卷积将特征图采样为一半,之后是三个步长为1的卷积层,每个卷积层都包含BN层和ReLU操作。将每一个块的输出都上采样到一个固定的尺寸并串联构造高分辨率的特征图。最后,该特征图通过两种二维卷积被输出到期望的学习目标:概率评分图(Probability Score Map)和回归图(Regression Map)

  • 损失函数

Probability Score Map的输出通道是2,分别对应positive和negative的分数,Regression map输出通道为14维,对于每个回归的Bounding box都用7维来表示,也就是中心位置 、候选框的长宽高和航向角,另外两个旋转轴默认为0,原因是地面水平。同理,假设预测的anchor用小标a表示,因此可定义如下的残差: 基于体素的3D目标检测网络:VoxelNet - php中文网

其中,是anchor框底部的对角线长度,采用的目的是用对角线齐次归一化和。然后定义可以最终的损失函数: 基于体素的3D目标检测网络:VoxelNet - php中文网

损失函数前面两项是正则化分类损失,其中和分别表示softmax层对正锚和负锚的分数,采用的是交叉熵表示,和为正定平衡系数。最后一项是回归损失,和是正锚的回归输出和ground truth,采用的是Smooth L1损失。

详细的参数设定需要查看论文才能更好理解~

论文:

  • [1] Yin Zhou, Oncel Tuzel. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2018. VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection

博客参考:

  • VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection
  • 【3D目标检测】VoxelNet:End-to-End Learning for Point Cloud Based 3D Object Detection解读

项目参考:

  • https://github.com/qianguih/voxelnet

    github repo实现精度 easy: 53.43 moderate:48.78 hard:48.06

  • https://github.com/traveller59/second.pytorch

由于该论文并未提供开源的代码,目前也找不到能够复现其论文中指标的项目。 因此本项目根据参考项目(voxelnet-tensorflow)和该论文后续的算法改进版本(second)进行了复现。

复现精度

指标解释:>机器学习算法评估指标——3D目标检测

评价3D目标检测结果的指标主要是3D AP 和Bev AP。其含义是当预测框与真值框的交并比(IOU)大于一定阈值时,认为预测框正确的数量与所有真值框的比例。

IOU即两个框的相交范围与并集范围的比例。

在KITTI val数据集(50/50 split as paper)的测试效果如下表。

NetWork epochs opt lr batch_size dataset config
VoxelNet 160 SGD 0.0015 2 * 1(V100 card) KITTI config
Car AP@0.70, 0.70, 0.70:bbox AP:90.26, 86.24, 79.26bev  AP:89.92, 86.04, 79.143d   AP:77.00, 66.40, 63.24aos  AP:38.34, 37.30, 33.19Car AP@0.70, 0.50, 0.50:bbox AP:90.26, 86.24, 79.26bev  AP:90.80, 89.84, 88.883d   AP:90.75, 89.32, 87.84aos  AP:38.34, 37.30, 33.19Car coco AP@0.50:0.05:0.95:bbox AP:67.72, 63.70, 61.10bev  AP:67.13, 63.44, 61.153d   AP:53.45, 48.92, 46.34aos  AP:28.82, 27.54, 25.55
登录后复制

预训练权重和日志:百度网盘 | AiStudio存储

2、当将分类损失改为FocalLoss以及加入针对aos的direction分类损失时(后续实验表明direction损失只对aos起作用,可不用)

Replit Ghostwrite
Replit Ghostwrite

一种基于 ML 的工具,可提供代码完成、生成、转换和编辑器内搜索功能。

Replit Ghostwrite 93
查看详情 Replit Ghostwrite
NetWork epochs opt lr batch_size dataset config
VoxelNet 160 SGD 0.005 2 * 4 (V100 card) KITTI configFix
Car AP@0.70, 0.70, 0.70:bbox AP:90.19, 85.78, 79.38bev  AP:89.79, 85.26, 78.933d   AP:81.78, 66.88, 63.51aos  AP:89.81, 84.55, 77.71Car AP@0.70, 0.50, 0.50:bbox AP:90.19, 85.78, 79.38bev  AP:96.51, 89.53, 88.593d   AP:90.65, 89.08, 87.52aos  AP:89.81, 84.55, 77.71Car coco AP@0.50:0.05:0.95:bbox AP:67.15, 63.05, 60.58bev  AP:68.90, 63.78, 61.083d   AP:54.88, 49.42, 46.82aos  AP:66.89, 62.19, 59.23
登录后复制

预训练权重和训练日志:百度网盘 | AiStudio存储

  • 另外,论文中没提及的细节,本项目均参考Second项目的实施。

  • 仓库内的log文件夹下存放有两个训练日志和可视化曲线日志。

开始

数据集解压

约15分钟.

In [1]
%cd /home/aistudio/
!rm -rf kitti/
!mkdir -p kitti/training/velodyne_reduced
!mkdir -p kitti/testing/velodyne_reduced
!unzip data/data50186/data_object_calib.zip -d kitti/
!unzip data/data50186/image_training.zip -d kitti/training/
!unzip data/data50186/data_object_label_2.zip -d kitti/training/
!unzip data/data50186/velodyne_training_1.zip -d kitti/training/
!unzip data/data50186/velodyne_training_2.zip -d kitti//training/
!unzip data/data50186/velodyne_training_3.zip -d kitti/training/
!unzip data/data50186/image_testing.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_1.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_2.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_3.zip -d kitti/testing/
!mv kitti/training/training/* kitti/training/
!rm -rf kitti/training/training/
!mv kitti/testing/testing/* kitti/testing/
!rm -rf kitti/testing/testing/
!mkdir kitti/training/velodyne
!mv kitti/training/velodyne_training_1/* kitti/training/velodyne/
!mv kitti/training/velodyne_training_2/* kitti/training/velodyne/
!mv kitti/training/velodyne_training_3/* kitti/training/velodyne/
!rm -rf kitti/training/velodyne_training_1
!rm -rf kitti/training/velodyne_training_2
!rm -rf kitti/training/velodyne_training_3
!mkdir kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_1/* kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_2/* kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_3/* kitti/testing/velodyne
!rm -rf kitti/testing/velodyne_testing_1
!rm -rf kitti/testing/velodyne_testing_2
!rm -rf kitti/testing/velodyne_testing_3
!mv kitti data/
登录后复制

至此,数据集的结构:

└── KITTI_DATASET_ROOT
       ├── training    <-- 7481 train data
       |   ├── image_2 <-- for visualization
       |   ├── calib
       |   ├── label_2
       |   ├── velodyne
       |   └── velodyne_reduced <-- empty directory
       └── testing     <-- 7580 test data
           ├── image_2 <-- for visualization
           ├── calib
           ├── velodyne
           └── velodyne_reduced <-- empty directory
登录后复制

3,712 data samples fortraining and 3,769 data samples for validation

安装依赖

最适合的环境配置:

  • python版本:3.7.4
  • PaddlePaddle框架版本:2.2.1
  • CUDA 版本: NVIDIA-SMI 450.51.06 Driver Version: 450.51.06 CUDA Version: 11.0 cuDNN:7.6

注意: 由于PaddlePaddle/cuDNN本身的BUG,CUDA 10.1版本当batch size > 2时会报如下错误:

OSError: (External) CUDNN error(7), CUDNN_STATUS_MAPPING_ERROR. 
  [Hint: 'CUDNN_STATUS_MAPPING_ERROR'.  An access to GPU memory space failed, which is usually caused by a failure to bind a texture.  To correct, prior to the function call, unbind any previously bound textures.  Otherwise, this may indicate an internal error/bug in the library.  ] (at /paddle/paddle/fluid/operators/conv_cudnn_op.cu:758)
登录后复制

因此单卡如果环境不是CUDA 11.0以上,config文件中batch size设置为2即可,后续通过训练的accum_step参数开启梯度累加起到增大bs的效果。设置accum_step=8即表示bs=16,并做相应config文件的初始学习率调整。

In [ ]
!pip install distro shapely pybind11 pillow fire memory_profiler psutil scikit-image==0.14.2!pip install numpy==1.17.0!pip install numba==0.48.0
登录后复制

(由于Notebook不支持导入当前整个项目到Python环境,以下操作在终端命令行执行)

准备部分

1. 为numba设置cuda环境

export NUMBAPRO_CUDA_DRIVER=/usr/lib/x86_64-linux-gnu/libcuda.soexport NUMBAPRO_NVVM=/usr/local/cuda/nvvm/lib64/libnvvm.soexport NUMBAPRO_LIBDEVICE=/usr/local/cuda/nvvm/libdevice
登录后复制

2. 将当前项目加到环境中

export PYTHONPATH=$PYTHONPATH:/home/aistudio/VoxelNet
登录后复制

3. 数据预处理

从label中分类别抽取真值信息以及对点云进行降采样。(约7分钟)

cd /home/aistudio/VoxelNet/voxelnet/
python create_data.py create_kitti_info_file --data_path=/home/aistudio/data/kitti  #  Create kitti infospython create_data.py create_reduced_point_cloud --data_path=/home/aistudio/data/kitti # Create kitti reduced pointpython create_data.py create_groundtruth_database --data_path=/home/aistudio/data/kitti # Create kitti gt
登录后复制

打印信息如下:

Generate info. this may take several minutes.
Kitti info train file is saved to /home/aistudio/data/kitti/kitti_infos_train.pkl
Kitti info val file is saved to /home/aistudio/data/kitti/kitti_infos_val.pkl
Kitti info trainval file is saved to /home/aistudio/data/kitti/kitti_infos_trainval.pkl
Kitti info test file is saved to /home/aistudio/data/kitti/kitti_infos_test.pkl
[100.0%][===================>][40.86it/s][01:44>00:00]   
[100.0%][===================>][35.31it/s][01:47>00:00]   
[100.0%][===================>][39.13it/s][03:49>00:00] 
[100.0%][===================>][28.71it/s][01:53>00:00]     
load 14357 Car database infosload 2207 Pedestrian database infosload 734 Cyclist database infosload 1297 Van database infosload 56 Person_sitting database infosload 488 Truck database infosload 224 Tram database infosload 337 Misc database infos
登录后复制

4. 修改配置文件

voxelnet/configs/car.configs

train_input_reader: {
  ...
  database_sampler {
    database_info_path: "/home/aistudio/data/kitti/kitti_dbinfos_train.pkl"
    ...
  }
  kitti_info_path: "/home/aistudio/data/kitti/kitti_infos_train.pkl"
  kitti_root_path: "/home/aistudio/data/kitti"}
...
eval_input_reader: {
  ...
  kitti_info_path: "/home/aistudio/data/kitti/kitti_infos_val.pkl"
  kitti_root_path: "/home/aistudio/data/kitti"}
登录后复制

设置注意事项:

1、若训练要开启梯度累加选项,则:

  • 学习率的decay_steps按照梯度累加后的batch size对应的总steps来设置。
  • train_config.steps则按未梯度累加时对应的初始batch size对应的总steps来设置

2、 配置文件需放置于voxelnet/configs/***.py

快速开始

1. 训练

训练一个epoch, V100 16G大约15分钟。显存占用11G左右。

python ./pypaddle/train.py train --config_path=./configs/config.py --model_dir=./output
登录后复制

2. 评估

V100 16G 大约5分钟

python ./pypaddle/train.py evaluate --config_path=./configs/config.py --model_dir=./output --ckpt_path=./output/voxelnet-278400.ckpt
登录后复制

3. 可视化预测

3D可视化需要GUI,Notebook环境不支持动态GUI调用显示。需在本地测试。 详细查看README.md。

4. 一个简单的BEV视角可视化例子

为了方便查看预测结果,下面的cell提供了一个在notebook中查看二维bev视角的可视化例子,可以在notebook执行。 由于只保留了相机视角范围内的结果(Points that are projectedoutside of image boundaries are removed(in Paper Section 3.1)),所以车身后面没有检测框。

In [3]
%cd /home/aistudio/VoxelNet/
!export PYTHONPATH=$PYTHONPATH:/home/aistudio/VoxelNetimport paddleimport numpy as npimport matplotlib.pyplot as pltimport picklefrom pathlib import Pathimport voxelnet.pypaddle.builder.voxelnet_builder as voxelnet_builderimport voxelnet.builder.voxel_builder as voxel_builderimport voxelnet.builder.target_assigner_builder as target_assigner_builderimport voxelnet.pypaddle.builder.box_coder_builder as box_coder_builderfrom voxelnet.data.preprocess import merge_voxelnet_batchfrom voxelnet.configs import cfg_from_config_py_filefrom voxelnet.utils import visdef example_convert_to_paddle(example, dtype=paddle.float32,                             ) -> dict:
    example_paddle = {}
    float_names = [        "voxels", "anchors", "reg_targets", "reg_weights", "bev_map", "rect",        "Trv2c", "P2"
    ]    for k, v in example.items():        if k in float_names:
            example_paddle[k] = paddle.to_tensor(v, dtype=dtype)        elif k in ["coordinates", "labels", "num_points"]:
            example_paddle[k] = paddle.to_tensor(
                v, dtype=paddle.int32)        elif k in ["anchors_mask"]:
            example_paddle[k] = paddle.to_tensor(
                v, dtype=paddle.uint8)        else:
            example_paddle[k] = v    return example_paddle

paddle.set_device('gpu') # 设置cpu/gpuconfig_path = "home/aistudio/VoxelNet/voxelnet/configs/config.py"config = cfg_from_config_py_file(config_path)
input_cfg = config.eval_input_reader
model_cfg = config.model.voxelnet

ckpt_path = "/home/aistudio/VoxelNet/voxelnet/output/voxelnet-278400.ckpt"model_cfg.voxel_generator.point_cloud_range = [0, -40, -3, 70.4, 40, 1]
voxel_generator = voxel_builder.build(model_cfg.voxel_generator)####################### BUILD TARGET ASSIGNER######################bv_range = voxel_generator.point_cloud_range[[0, 1, 3, 4]]
box_coder = box_coder_builder.build(model_cfg.box_coder)
target_assigner_cfg = model_cfg.target_assigner
target_assigner = target_assigner_builder.build(target_assigner_cfg,
                                                bv_range, box_coder)
net = voxelnet_builder.build(model_cfg, voxel_generator, target_assigner)
net.eval()

state = paddle.load(ckpt_path)
net.set_state_dict(state)

out_size_factor = model_cfg.rpn.layer_strides[0] // model_cfg.rpn.upsample_strides[0]
grid_size = voxel_generator.grid_size
feature_map_size = grid_size[:2] // out_size_factor
feature_map_size = [*feature_map_size, 1][::-1]

anchors = target_assigner.generate_anchors(feature_map_size)["anchors"]
anchors = anchors.reshape((1, -1, 7))

info_path = input_cfg.kitti_info_path
root_path = Path(input_cfg.kitti_root_path)with open(info_path, 'rb') as f:
    infos = pickle.load(f)

info = infos[564] # 测试目标点云# print(info)v_path = info['velodyne_path']
v_path = str(root_path / v_path)
points = np.fromfile(
    v_path, dtype=np.float32, count=-1).reshape([-1, 4])
voxels, coords, num_points = voxel_generator.generate(points, max_voxels=40000)print(voxels.shape)# add batch idx to coords# coords = np.pad(coords, ((0, 0), (1, 0)), mode='constant', constant_values=0)image_idx = info['image_idx']
rect = info['calib/R0_rect'].astype(np.float32)
Trv2c = info['calib/Tr_velo_to_cam'].astype(np.float32)
P2 = info['calib/P2'].astype(np.float32)

example = {    "anchors": anchors,    "voxels": voxels,    "num_points": num_points,    "num_voxels": np.array([voxels.shape[0]], dtype=np.int64),    "coordinates": coords,    "rect": rect,    "P2": P2,    "Trv2c":Trv2c,    'image_idx': image_idx

}
batch_example = [example]
examples = merge_voxelnet_batch(batch_example)
examples = example_convert_to_paddle(examples)with paddle.no_grad():
    pred = net(examples)[0]

boxes_lidar = pred["box3d_lidar"].detach().cpu().numpy()
vis_voxel_size = [0.1, 0.1, 0.1]
vis_point_range = [-50, -30, -3, 50, 30, 1]
bev_map = vis.point_to_vis_bev(points, vis_voxel_size, vis_point_range)
bev_map = vis.draw_box_in_bev(bev_map, vis_point_range, boxes_lidar, [0, 255, 0], 2)# plt.savefig('/home/aistudio/val564.png')plt.imshow(bev_map)
paddle.device.cuda.empty_cache()
登录后复制
/home/aistudio/VoxelNet
cfg_file must be located in ./configs/***.py...
load config from home/aistudio/VoxelNet/voxelnet/configs/config.py...
(13282, 35, 4)
登录后复制
<Figure size 432x288 with 1 Axes>
登录后复制

结语

复现心得:

这篇论文在第四届论文复现赛的时候我就进行了复现,并未成功,相差甚远。由于论文没有开源代码,网络上也没有达到论文精度的项目,很难从头开始自己全部重写。于是我换了一个思路,既然这篇论文是非常经典的论文,后续肯定有人基于这个思路进行改进。果然找到了second。second这篇论文几乎重现了voxelnet的所有方法(但据作者说并没有完全复现原始的voxelnet),不过加入了稀疏卷积使得速度和精度得到了巨大提升。于是我的思路就变成了从second中去掉它改进的部分内容,使其复原原始的voxelnet,来减少自己重写代码的工作量。

由于之前第四届比赛时的经验,这次遇到的问题都不多,或者说当时已经遇到了,直接拿我当时的代码进行替换,一个函数一个函数输入输出对齐测试即可。并且只需要对照X2Paddle进行对齐即可。

第四届和这一次遇到的主要问题都是内存泄漏问题。不过这一次,找到了问题所在。

1.这里总结一下几个常犯的内存泄漏错误原因。

  • loss在用于取值打印或者用于其他计算时,没有.detach()或者.numpy()
  • 如果model本身内部要存一些每一次前向计算后的指标,指标一定也要在前向计算后detach再赋值到model内的变量(这是我找了超级久的泄漏问题)。

2.enisum函数。这个函数虽然paddle2.2提供了,但是还有bug。用的时候报错了。

我在paddlenlp(https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/ops/einsum.py) 中找到了可以正常使用的版本。

3.mask赋值。问题如下: 写了一个mask(bool类型)的赋值函数:

def mask_slice_v1(data, mask):
    """
    问题:
    data.shape = [x,y], type: float...
    mask.shape = [x] ,type: bool
    in torch, can do it by data[mask] to get shape:[x,y] result.
    但是padlde目前还不行。
    """
    data_shape = data.shape
    mask = mask.unsqueeze(-1) # [x,1]
    mask = paddle.tile(mask,[1,data_shape[1]]) # [x,y]
    slice = paddle.masked_select(data,mask) # [x*y]
    return slice.reshape([-1,data_shape[1]]) # [x,y]
登录后复制

相关信息:

信息 描述
作者 xbchen
日期 2021年1月
框架版本 PaddlePaddle>=2.2.1
应用场景 3D目标检测
硬件支持 GPU
在线体验 Notebook
多卡脚本 Shell

引用

  • Thanks for yan yan's second.pytorch project.
@inproceedings{Yin2018voxelnet,
    author={Yin Zhou, Oncel Tuzel},
    title={VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection},
    booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},
    year = {2018}
}
登录后复制

以上就是基于体素的3D目标检测网络:VoxelNet的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号