长文梳理点云3D目标检:Pillar-Based和Voxel-Based

作者 | Fangzh  编辑 | 自动驾驶之心

原文链接:https://zhuanlan.zhihu.com/p/700072002

点击下方卡片,关注“自动驾驶之心”公众号

戳我-> 领取自动驾驶近15个方向学习路线

>>点击进入→自动驾驶之心3D目标检测技术交流群

本文只做学术分享,如有侵权,联系删文

前言

目前的自动驾驶感知算法的输入数据会涉及到诸多采集传感器,比如激光雷达传感器、环视相机传感器以及毫米波雷达传感器等等。

相比于图像数据信息而言,由于激光雷达传感器采集到的点云数据可以更加准确的表示物体的深度和几何结构信息而被广泛应用于当前的智驾感知技术方案当中。并且在学术界也已经涌现出了很多基于BEV空间的3D点云感知算法。

对于当前绝大多数的基于BEV空间的3D感知算法而言,无论是基于单模态的纯视觉输入或者是纯点云输入,又或者是基于多模态的BEV空间3D感知算法,其整体思路均是将传感器采集的原始数据利用主干网络进行特征提取,并将提取到的特征转换到BEV空间下完成BEV特征的构建过程,得到的BEV特征用于后续的不同感知子任务(比如3D障碍物检测、基于BEV空间的语义分割等等)。

本文重点介绍点云3D目标检测任务中的两大基础主干网络:Voxel-BasedPillar-Based两类主干网络

下面先整体说一下两类主干网络分别是如何处理点云,并得到最终BEV空间特征的整体处理步骤。

【1】Voxel-Based点云提取主干网络

.1 对激光雷达点云进行体素化的处理;
2. 进行体素特征编码(Voxel Feature Encoding,VFE);
3. 利用3D的卷积主干网络(VoxelResBackBone8x)对体素特征编码后的特征进行3D特征提取;
4. 将3D卷积主干网络提取后的特征投影到BEV空间中得到BEV特征;
5. 利用2D的卷积主干网络(ResNet系列等)对BEV特征完成进一步的特征提取;

【2】Pillar-Based点云提取主干网络

  1. 对激光雷达点云进行体素化的处理;

  2. 进行体素特征编码(Voxel Feature Encoding,VFE);

  3. 将体素特征编码后的结果直接投影到BEV空间下得到BEV特征(与Voxel-Based的方法不同,不再使用3D卷积主干网络进行点云特征提取);

  4. 利用2D的卷积主干网络(ResNet系列等)对BEV特征完成进一步的特征提取;

通过上述流程可以看出,Voxel-Based和Pillar-Based的点云特征提取方法主要不同点在于是否存在3D卷积提取的过程,Pillar-Based的方法由于不使用3D卷积,具有更快的推理速度,同时方便部署,是目前工业界主流的点云提取主干网络。

接下来,本文将详细介绍每种点云提取主干网络的细节部分~

Voxel-Based点云提取主干网络

该方法最早是在VoxelNet论文中被提出用于提取点云特征,其整体思路是将激光雷达采集到的点云数据转换成3D体素的形式,然后利用3D卷积主干网络进行特征提取,最终得到BEV空间特征,其整体网络结构如下图所示。

0013f81d601fa0015a72dc219618a684.png
VoxelNet网络整体结构图

(1)点云数据体素化

点云数据体素化的主要作用就是将输入的点云数据分别划分到设定好的体素网格中,方便进行点云特征提取。具体而言,点云体素化模块的输入是一个张量Tensor([N, 4]),N代表输入点云的个数,4代表点云的3D坐标以及反射强度信息。

在具体处理的过程中需要人为设定当前算法模型的感知范围、每个体素所代表的实际空间大小,这样就可以知道每个体素网格中有多少点云,便于后续进行特征提取了。

点云体素化模块的输出有三项,分别如下

  • voxels:Tensor([M, max_num_points, 4]),其中M是根据当前点云中包含的所有点计算得到的体素个数,这里的4依旧代表点云的3D坐标以及反射强度;

  • voxels_coord:Tensor([M, 3]),代表每个体素在体素坐标系下的位置坐标;主要在后续3D卷积的过程中使用;

  • voxel_num_points:Tensor([M]);代表每个体素内有多少实际有效的点;主要在后续体素特征编码(Voxel Feature Encode,VFE)过程中使用;

(2)体素特征编码

Voxel-Based的体素特征编码主要包含两个部分,如下所示

需要注意的是:这部分输出的Tensor[M, 4])中的4就已经不是xyzr的物理含义了,已经是经过编码后的特征了。

  1. 先将每个体素内所有点的值进行求和:Tensor([M, max_points_in_voxel, 4]) → Tensor([M, 4]);

  2. 利用每个体素内包含的点云个数对上一步求和后的特征进行归一化;Tensor([M, 4]) / voxel_num_points;

  3. 整体的代码实现如下:

class MeanVFE(VFETemplate):
    def __init__(self, model_cfg, num_point_features, **kwargs):
        super().__init__(model_cfg=model_cfg)
        self.num_point_features = num_point_features

    def get_output_feature_dim(self):
        return self.num_point_features

    def forward(self, batch_dict, **kwargs):
        """
        Args:
            batch_dict:
                voxels: (num_voxels, max_points_per_voxel, C)
                voxel_num_points: optional (num_voxels)
            **kwargs:

        Returns:
            vfe_features: (num_voxels, C)
            
        """
        voxel_features, voxel_num_points = batch_dict['voxels'], batch_dict['voxel_num_points']
        points_mean = voxel_features[:, :, :].sum(dim=1, keepdim=False) 
        normalizer = torch.clamp_min(voxel_num_points.view(-1, 1), min=1.0).type_as(voxel_features)
        points_mean = points_mean / normalizer 
        batch_dict['voxel_features'] = points_mean.contiguous() # points_mean(12000, 5)

        return batch_dict

(3)利用3D卷积主干网络进行特征提取

这里采用的是VoxelResBackBone8x(3D Backbone主干网络 —— 8x代表下采样8倍)。具体而言,VoxelResBackBone8x主干网络共采用了两种稀疏卷积形式。

  • 一种是spconv.SparseConv3d(regular output definition),就像普通的卷积一样,只要 kernel 覆盖一个 active input site,就可以计算出 output site,主要用于降采样操作

  • 另外一种是spconv.SubMConv3d(submanifold output definition),只有当 kernel 的中心覆盖一个 active input site 时,卷积输出才会被计算,主要用于提取点云的特征信息

  • 其中输入体素不为0的地方称为 active input site;

接下来是VoxelResBackBone8x主干网络的前向过程

def forward(self, voxel_features, voxel_coords, batch_size):
    input_sp_tensor = spconv.SparseConvTensor(
            features=voxel_features,
            indices=voxel_coords.int(),
            spatial_shape=self.sparse_shape,  # 
            batch_size=batch_size,
        )  # 在进行3D卷积前,先进行稀疏化
     
     # SubMConv3d提取特征
     # 1) features.shape: Tensor([M = 102604, 16]) --> Tensor([M = 102604, 16])
     # 2) spatial_shape: Tensor([grid_z + 1 = 41, grid_x = 1504, grid_y = 448]) --> 
     #        Tensor([grid_z + 1, grid_x, grid_y])
     x = self.conv_input(input_sp_tensor)  
      
     # SubMConv3d提取特征
     # 1) features.shape: Tensor([M = 102604, 16]) --> Tensor([M = 102604, 16])
     # 2) spatial_shape: Tensor([grid_z + 1 = 41, grid_x = 448, grid_y = 1504]) --> 
     #        Tensor([grid_z + 1 = 41, grid_x = 448, grid_y = 1504])
     x_conv1 = self.conv1(x) 
     
     # SubMConv3d提取特征 + SparseConv3d降采样
     # 1) features.shape: Tensor([M = 102604, 16] --> Tensor([M = 72218, 32])
     # 2) spatial_shape: Tensor([grid_z + 1 = 41, grid_x = 448, grid_y = 1504]) --> 
     #         Tensor([grid_z + 1 = 21, grid_x = 224, grid_y = 752])
     x_conv2 = self.conv2(x_conv1)  
     
     # SubMConv3d提取特征 + SparseConv3d降采样
     # 1) features.shape: Tensor([M = 72218, 32] --> Tensor([M = 26536, 64])
     # 2) spatial_shape: Tensor([grid_z + 1 = 21, grid_x = 224, grid_y = 752]) -->
     #         Tensor([grid_z + 1 = 11, grid_x = 112, grid_y = 376])
     x_conv3 = self.conv3(x_conv2)  
     
     # SubMConv3d提取特征 + SparseConv3d降采样
     # 1) features.shape: Tensor([M = 26536, 64] --> Tensor([M = 8198, 128])
     # 2) spatial_shape: Tensor([grid_z + 1 = 11, grid_x = 112, grid_y = 376]) -->
     #         Tensor([grid_z + 1 = 5, grid_x = 56, grid_y = 188])
     x_conv4 = self.conv4(x_conv3)  
     
     # SparseConv3d降采样
     # 1) features.shape: Tensor([M = 8198, 128] --> Tensor([M = 6534, 128])
     # 2) spatial_shape: Tensor([grid_z + 1 = 5, grid_x = 56, grid_y = 188]) -->
     #         Tensor([grid_z + 1 = 2, grid_x = 56, grid_y = 188])
     out = self.conv_out(x_conv4)   

     encoded_spconv_tensor = out
     encoded_spconv_tensor_stride = 8
     multi_scale_3d_features = {
         "x_conv1": x_conv1,
         "x_conv2": x_conv2,
         "x_conv3": x_conv3,
         "x_conv4": x_conv4,
      }
    return encoded_spconv_tensor, encoded_spconv_tensor_stride, multi_scale_3d_features

(4)3D特征投影到BEV空间

这一步骤的主要目的就是将3D卷积主干网络提取到的3D特征投影到BEV空间得到BEV特征。

def forward(self, encoded_spconv_tensor, encoded_spconv_tensor_stride):
    # Tensor([bs, C=128, grid_z=2, grid_x = 56, grid_y = 188])
    spatial_features = encoded_spconv_tensor.dense() 
    N, C, D, H, W = spatial_features.shape
    
    # 通过特征.view()的方式将BEV特征的Z轴拍平
    spatial_features = spatial_features.view(N, C * D, H, W)  # Tensor([bs, 256, 56, 188])
    
    # 最终的BEV特征 Tensor([bs, 256, 56, 188])
    return spatial_features, encoded_spconv_tensor_stride

(5)2D主干网络进行BEV特征的进一步提取

这一部分的结构类似ResNet当中的结构,主要是对2D的BEV特征进行进一步的特征提取,其实现代码如下

class BaseBEVBackbone(nn.Module):
    def __init__(self, model_cfg, input_channels):
        super().__init__()   
def forward(self, data_dict):
        """
        Args:
            data_dict:
                spatial_features : (batch, c, W, H)
        Returns:
        """
        spatial_features = data_dict['spatial_features']
        ups = []
        ret_dict = {}
        x = spatial_features

        for i in range(len(self.blocks)):
            x = self.blocks[i](x)

            stride = int(spatial_features.shape[2] / x.shape[2])
            ret_dict['spatial_features_%dx' % stride] = x
  
            if len(self.deblocks) > 0:
                ups.append(self.deblocks[i](x))
            else:
                ups.append(x)

        if len(ups) > 1:
            x = torch.cat(ups, dim=1)
        elif len(ups) == 1:
            x = ups[0]
      
        if len(self.deblocks) > len(self.blocks):
            x = self.deblocks[-1](x)

        data_dict['spatial_features_2d'] = x
        return data_dict

Pillar-Based的点云提取主干网络

该方法最早在PointPillars论文中被提出用于提取点云特征,与VoxelNet算法模型处理点云方式不同,PillarNet在点云数据处理的过程中直接将点云数据处理为BEV特征,取消掉了3D卷积主干网络的特征提取过程,推理速度更快,更方便上车部署,其整体网络结构如下图所示。

1fcf53f49f85ec3307e825137c99c4fd.png
PointPillars整体网络结构图

(1)点云数据体素化

该过程与Voxel-Based的方法是相同的,这里就不展开介绍了

(2)体素特征编码

由于Pillar-Based的方法不再使用3D的卷积主干网络对体素化后的点云进行3D特征提取,所以这部分的体素特征编码相对复杂一些。主要包括两部分:点云增强以及点云特征提取

  • 点云增强部分

原始的激光雷达点云是4维的数据格式(3D坐标以及反射强度),作者对点云数据进行了增强,将其表示为9个维度(x,y,z,r,xc,yc,zc,xp,yp)。其中,xc,yc,zc分别代表划分的Pillar中激光雷达点云相对N个点云中心的偏移量,xp和yp分别表示激光雷达点云相对Pillar坐标的偏移量,r代表反射强度。

  • 点云特征提取部分

这里主要采用了一个全连接网络实现体素特征的编码过程

(3)体素特征投影到BEV空间形成BEV特征

这里的操作主要就根据步骤一中的点云数据体素化后得到的坐标coord以及步骤二体素特征编码后的特征进行按位置填充,从而直接得到BEV空间下的BEV特征。

(4)2D主干网络进行BEV特征的进一步提取

该部分的实现过程与Voxel-Based的实现思路基本一样,这里就不展开介绍了。

投稿作者为『自动驾驶之心知识星球』特邀嘉宾,欢迎加入交流!

① 全网独家视频课程

BEV感知、BEV模型部署、BEV目标跟踪、毫米波雷达视觉融合多传感器标定多传感器融合多模态3D目标检测车道线检测轨迹预测在线高精地图世界模型点云3D目标检测目标跟踪Occupancy、cuda与TensorRT模型部署大模型与自动驾驶Nerf语义分割自动驾驶仿真、传感器部署、决策规划、轨迹预测等多个方向学习视频(扫码即可学习

8f7643baa340b1bb9d411ac2f359e88e.png

网页端官网:www.zdjszx.com

② 国内首个自动驾驶学习社区

国内最大最专业,近3000人的交流社区,已得到大多数自动驾驶公司的认可!涉及30+自动驾驶技术栈学习路线,从0到一带你入门自动驾驶感知2D/3D检测、语义分割、车道线、BEV感知、Occupancy、多传感器融合、多传感器标定、目标跟踪)、自动驾驶定位建图SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案大模型、端到端等,更有行业动态和岗位发布!欢迎扫描下方二维码,加入自动驾驶之心知识星球,这是一个真正有干货的地方,与领域大佬交流入门、学习、工作、跳槽上的各类难题,日常分享论文+代码+视频

4dbaa19eb24850f641fbb7a7f67766e5.png

③【自动驾驶之心】技术交流群

自动驾驶之心是首个自动驾驶开发者社区,聚焦感知、定位、融合、规控、标定、端到端、仿真、产品经理、自动驾驶开发、自动标注与数据闭环多个方向,目前近60+技术交流群,欢迎加入!扫码添加汽车人助理微信邀请入群,备注:学校/公司+方向+昵称(快速入群方式)

7aea5b38795691a95d3d8619b55aca3e.jpeg

④【自动驾驶之心】全平台矩阵

b463230268a80498b943687b23ef997d.png

(base) PS C:\Users\10556> # 创建名为 pyqt_env 的虚拟环境,指定 Python 3.9 版本 (base) PS C:\Users\10556> conda create -n pyqt_env python=3.9 Retrieving notices: ...working... done Collecting package metadata (current_repodata.json): done Solving environment: done ==> WARNING: A newer version of conda exists. <== current version: 22.11.1 latest version: 25.7.0 Please update conda by running $ conda update -n base -c defaults conda Or to minimize the number of packages updated during conda update use conda install conda=25.7.0 ## Package Plan ## environment location: C:\mini\envs\pyqt_env added / updated specs: - python=3.9 The following NEW packages will be INSTALLED: bzip2 pkgs/main/win-64::bzip2-1.0.8-h2bbff1b_6 ca-certificates pkgs/main/win-64::ca-certificates-2025.7.15-haa95532_0 expat pkgs/main/win-64::expat-2.7.1-h8ddb27b_0 libffi pkgs/main/win-64::libffi-3.4.4-hd77b12b_1 openssl pkgs/main/win-64::openssl-3.0.17-h35632f6_0 pip pkgs/main/noarch::pip-25.1-pyhc872135_2 python pkgs/main/win-64::python-3.9.23-h716150d_0 setuptools pkgs/main/win-64::setuptools-78.1.1-py39haa95532_0 sqlite pkgs/main/win-64::sqlite-3.50.2-hda9a48d_1 tk pkgs/main/win-64::tk-8.6.15-hf199647_0 tzdata pkgs/main/noarch::tzdata-2025b-h04d1e81_0 ucrt pkgs/main/win-64::ucrt-10.0.22621.0-haa95532_0 vc pkgs/main/win-64::vc-14.3-h2df5915_10 vc14_runtime pkgs/main/win-64::vc14_runtime-14.44.35208-h4927774_10 vs2015_runtime pkgs/main/win-64::vs2015_runtime-14.44.35208-ha6b5a95_10 wheel pkgs/main/win-64::wheel-0.45.1-py39haa95532_0 xz pkgs/main/win-64::xz-5.6.4-h4754444_1 zlib pkgs/main/win-64::zlib-1.2.13-h8cc25b3_1 Proceed ([y]/n)?
最新发布
08-20
<think>我们被要求回答关于在创建conda虚拟环境时自动安装的依赖包列表以及是否需要确认安装的问题。 根据引用[1]:用户提到conda每次创建环境时都会默认安装一些包,但如果不想要这些默认包,可以使用`--no-deps`选项。 引用[2][3]提供了关于虚拟环境存储位置安装依赖包的一些注意事项,但并没有直接回答默认依赖包列表。 引用[4]是一篇关于conda使用的长文,其中提到创建虚拟环境、管理环境等,但也没有明确列出默认依赖包。 然而,根据conda的官方文档常见实践,当我们使用`conda create -n myenv python=x.x`命令时,conda会自动安装一些基础依赖包,这些包通常包括: - python (指定版本) - pip - setuptools - wheel - sqlite - openssl - ncurses(在Linux/macOS上) - 以及其他一些必要的底层库(如zlib、readline等) 这些包是运行Python环境所必需的,所以conda会默认安装。但是,这个默认列表可能因为操作系统的不同而略有差异。 关于是否需要确认安装:在默认情况下,conda在创建环境时会显示将要安装的包列表,并提示用户确认(即显示“Proceed ([y]/n)?”)。用户可以选择按回车确认(即选择y)或输入n取消。 如果用户不想被提示确认,可以使用`-y`或`--yes`选项,这样conda会直接安装而不提示。 另外,如果用户不想安装这些默认的依赖包,可以使用`--no-deps`选项(如引用[1]所示),但这样创建的环境可能是不完整的,需要用户自己安装必要的依赖。 因此,回答如下: 当使用`conda create -n env_name python=x.x`命令创建虚拟环境时,conda会自动解析并安装一组基础依赖包(包括Python、pip、setuptools等)。在安装前,conda会显示将要安装的包列表,并提示用户确认(即Proceed ([y]/n)?)。用户可以选择输入y(或直接回车)继续安装,或输入n取消。 如果希望跳过确认步骤,可以添加`-y`选项,例如: conda create -n env_name python=x.x -y 如果不想安装任何默认的依赖包(仅安装指定的python,而不安装其依赖),可以使用`--no-deps`选项(但通常不推荐,因为这样环境可能无法正常工作): conda create -n env_name python=x.x --no-deps 注意:使用`--no-deps`创建的环境可能缺少基本工具(如pip),需要用户手动安装所需依赖。 另外,用户可以通过`--dry-run`选项来查看将要安装的包而不实际安装: conda create -n env_name python=x.x --dry-run 这个命令会列出所有将要安装的包,但不会创建环境。 总结:默认情况下,conda会提示用户确认安装,并显示将要安装的包列表。用户可以根据需要选择是否跳过确认或禁止安装默认依赖。 根据引用[1][4],我们可以提供相关的引用标记。 最后,生成相关问题。</think>### Conda 创建虚拟环境时的依赖包管理 #### 默认依赖包列表 当使用 `conda create -n env_name python=x.x` 创建环境时,Conda 会自动安装以下核心依赖包: 1. **Python 解释器**(指定版本) 2. **基础工具集**: - `pip` (Python 包管理器) - `setuptools` (包安装工具) - `wheel` (二进制包构建工具) - `sqlite` (轻量级数据库) - `openssl` (加密通信库) - `certifi` (SSL 证书) - `ca-certificates` (根证书) 完整列表可通过 `--dry-run` 预览: ```bash conda create -n test_env python=3.9 --dry-run ``` 输出示例: ``` The following NEW packages will be INSTALLED: ca-certificates pkgs/main/win-64::ca-certificates-2024.3.11-h56e8100_0 certifi pkgs/main/win-64::certifi-2024.2.2-py39haa95532_0 openssl pkgs/main/win-64::openssl-3.0.13-h2bbff1b_0 pip pkgs/main/win-64::pip-23.3.1-py39haa95532_0 python pkgs/main/win-64::python-3.9.19-h6244533_0 setuptools pkgs/main/win-64::setuptools-68.2.2-py39haa95532_0 sqlite pkgs/main/win-64::sqlite-3.41.2-h2bbff1b_0 wheel pkgs/main/noarch::wheel-0.41.2-pyhd3eb1b0_0 ``` #### 确认安装的三种方式 1. **交互式确认**(默认): ```bash conda create -n py39 python=3.9 ``` 终端显示 `Proceed ([y]/n)?` 需要手动输入 `y` 确认 2. **跳过确认**(强制安装): ```bash conda create -n py39 python=3.9 -y ``` 自动安装所有依赖包不提示[^1] 3. **禁止默认依赖**(高级用法): ```bash conda create -n minimal_env python=3.9 --no-deps ``` 仅安装 Python 解释器,不安装任何额外依赖[^1][^4] #### 依赖包管理建议 | 场景 | 推荐方案 | 注意事项 | |------|----------|----------| | 标准开发 | 使用默认安装 | 包含必要工具链 | | 容器部署 | `--no-deps` + 手动安装 | 减少镜像体积 | | 批量部署 | `-y` 自动确认 | 无需人工干预 | | 依赖冲突 | 单独安装核心包 | 避免版本冲突[^3] #### 环境存储位置 默认路径取决于操作系统: - **Windows**:`C:\Users\<用户名>\Anaconda3\envs\<环境名>` - **Linux/macOS**:`~/anaconda3/envs/<环境名>`[^2] 可通过以下命令验证: ```bash conda info --envs ``` #### 最佳实践 1. 创建环境后查依赖: ```bash conda list -n py39 ``` 2. 导出精确环境配置: ```bash conda env export > environment.yml ``` 3. 重建环境时指定通道优先级: ```bash conda env create -f environment.yml --channel conda-forge --channel defaults ``` > **提示**:若需完全纯净环境,可先创建空环境再手动安装: > ```bash > conda create -n clean_env --no-deps python=3.9 > conda activate clean_env > conda install pip setuptools wheel # 按需添加 > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值