深夜仓库里散落着一千张会发光的纸片,每张纸片都写着一组三维坐标,代表激光雷达从周围物体上弹回来的信号。这些纸片没有行列、没有顺序,也不均匀——这就是点云。想从里面找出藏着的车辆和行人,必须先给它们建立秩序。VoxelNet 像一位认真细致的整理员,搬来立体书架:把仓库切成成千上万个等大的小格子,纸片按坐标扫进格子;PointPillars 则像另一位麻利的整理员,他竖起一排排贯穿屋顶的竖档文件柜,纸片只按地面 XY 位置进档,高度则作为备注写在纸片上。一个精细但笨重,一个轻快但略粗。这篇文章会用这个仓库隐喻,带你弄懂两种经典方案的结构差异、速度与精度的权衡,以及工程上该如何选型。
学完这个对比,你可以在机器人 3D 感知项目里快速判断“要精度优先”还是“要速度与易部署优先”,直接为点云检测方案选型提供决策依据。
可视化演示
动态演示VoxelNet与Point的核心机制,通过源头/管道/节点/终端四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
VoxelNet与Point:D目标检 3D目标检测问题与点云挑战 VoxelNet体素化网络架构 核心机制可视化 VoxelNet与Point整体架构与关键组件
点云与检测任务
回到深夜仓库:激光雷达每秒钟发出几十万条激光束,碰到物体后反射回来,形成一堆带坐标的“纸星”。纸星之间没有顺序,同一面墙可能收到几千颗,远处的物体却只有几颗。3D 目标检测的任务,就是在这堆纸星里找出“哪里有车、哪里有人、哪里有自行车”,并画一个立体的方框把它框住。
点云的每个点通常由 \((x,y,z,r)\) 构成,其中 \(r\) 是反射强度。它与图片最大的不同是:图片有规整的像素矩阵,点云却没有行列顺序。点云天然稀疏,距离越远越疏。如果直接把原始点云塞给全连接网络,网络不知道哪些点属于同一个物体;必须先做离散化或结构化。这一步的选择,直接决定了后面模型的速度和精度。
3D 检测框不是普通的 2D 矩形,而是带朝向的立体框。一辆停在斜方向的车,如果不标注朝向,两个矩形框虽然重合,却无法描述车头指向哪里。因此,业界通常用 7 个数字来表示一个框:
就像整理仓库前先确认要找什么:如果只想找兔子的大概位置,一个大筐就够;但自动驾驶需要知道车头朝向,才能预测它会不会撞上来。检测框就是这张清单,后面的体素或柱体结构化方式,都是在为这张清单服务。
- 中心坐标检测框的几何中心在三维空间中的位置,由三个数字组成。
- 长宽高物体在三维空间中占据的包围盒尺寸。
- 朝向角物体在地面平面上的旋转角度,使框贴合车辆的实际方向。
- 完整表示这七个数字合起来,就是一个可旋转的 3D 检测框。
VoxelNet架构
VoxelNet 选择把仓库从地面到天空切成固定大小的小格子,像立体书架一样。每一格的宽、深、高都是预先定好的,散落的纸星按照三维坐标被扫进对应格子里。如果某个格子一颗星都没有,就让它空着,但书架本身依然存在。这种以三维立方体为单位组织数据的方式,就是“体素化”。
体素化之后,原始点云变成稀疏的三维网格。VoxelNet 引入 VFE(Voxel Feature Encoding)层来处理每个非空体素:先把每个点的坐标、反射率与它相对体素中心的偏移拼在一起;再通过一个 MLP 生成高维特征;最后在体素内部做最大值池化,形成这个格子的代表特征。最大值池化会保留最显著响应,而不是简单平均,因此对局部几何更敏感。
所有非空体素特征会组成一个四维张量,进入 3D 卷积中间层,让邻近体素交换信息。之后接一个区域提议网络(RPN)输出检测框。3D 卷积能捕捉局部几何关系,但也是主要性能瓶颈:三维空间里的邻居数量比二维多得多,计算量自然更大。立体书架的好处是细致,坏处是空书架也占地方。
- 点特征第 \(i\) 个激光点包含坐标和反射率,是体素处理的基本单位。
- 中心偏移用点的坐标减去第 \(k\) 个体素的中心,得到局部位置关系。
- 特征变换把原始点和偏移拼成一个向量,送入 MLP 学习更高层特征。
- 最大池化对一个体素内所有点做最大值池化,得到该体素的代表特征。
PointPillars架构
PointPillars 换了一种整理思路:它把仓库改成竖档文件柜,每个档案格从地面一直通到天花板,只按 XY 坐标决定纸星进哪个竖档,高度不单独开格子,而是作为备注写进纸星里。这种“柱体”划分大幅减少了格子总数,是 PointPillars 速度优势的根本来源。
每个柱体内的点不会直接被丢弃,而是构造一个 9 维特征向量:包含原始坐标 \(x,y,z\)、反射率 \(r\)、到柱体中心的三维距离,以及到所在 XY 栅格中心的偏移。这个丰富的特征让网络即使不做 Z 方向切分,也能感知垂直结构。然后使用 PointNet 对柱体内所有点做特征编码,再取最大值池化,得到柱体级别的代表特征。
得到每个柱体的特征后,PointPillars 会按 XY 坐标把这些特征散布到一个二维网格上,形成一张“伪图像”。伪图像仍然保留高度和反射率信息,但组织方式变成了规则的二维图像,后续可以直接使用成熟的 2D 卷积检测网络。竖档文件柜舍弃了 Z 方向切分,却没有丢掉垂直信息,这正是它又快又不明显掉精度的关键。
- 原始点信息先保留每个点的三维坐标和反射率。
- 柱体中心偏移计算点到它所在柱体中心的三维距离。
- 栅格中心偏移计算点到 XY 网格中心的二维偏移,帮助定位点在该柱体中的相对位置。
- PointNet 编码把上述信息拼成 9 维向量,用 PointNet 对每个点做非线性变换。
- 最大池化在柱体内部做最大值池化,得到这个竖档的代表特征。
核心差异对比
如果把体素和柱体看成两种整理账本:VoxelNet 的账本按三维格子逐层记账,PointPillars 的账本只按 XY 柜台记账,Z 信息作为备注写在每张纸星上。两者本质区别不在于“谁更先进”,而在于中间表示从 3D 张量变成 2D 伪图像后,整个网络的计算模式发生了变化。
VoxelNet 需要维护 \(N_x \times N_y \times N_z\) 个潜在体素,即使只对非空体素做计算,检索和稀疏处理依然复杂;PointPillars 最多只有 \(N_x \times N_y\) 个柱体。假设 Z 方向被切成 \(N_z\) 层,那么柱体表示在格子数量上会少一个数量级:
这两种方式在特征编码上也不同:VoxelNet 用 VFE 加 3D 卷积,适合直接学习三维局部结构;PointPillars 用 PointNet 加 2D 卷积,先压缩再检测。计算复杂度的差异最终表现为:体素方案需要更多算力换精度,柱体方案用更轻量的网络换取实时性。
- 体素总数三维体素把空间切成 X、Y、Z 三个方向上的立方体,总数为三者乘积。
- 柱体总数柱体只在 XY 平面划分,没有 Z 方向切分,因此总数少乘一个 \(N_z\)。
- 比值柱体数量是体素数量的 \(1/N_z\),Z 分层越多,节省越明显。
性能与精度权衡
立体书架精细,竖档文件柜迅速。VoxelNet 对空间分层更细,能更好地区分上下重叠的物体;PointPillars 走鸟瞰图路线,丢失了精细的高度切分,但换来了速度和更稳定的训练过程。两者在 KITTI 等公开数据集上的表现也印证了这种权衡。
在相同条件下,PointPillars 的推理速度通常比 VoxelNet 快数倍到十余倍,显存占用也更低。精度上,PointPillars 的车体检测 AP 往往比 VoxelNet 低两到四个百分点,但很多工业场景中这个差距可以接受,尤其是当传感器线数较低、点云稀疏时,体素方案的优势并不明显。
| 对比维度 | VoxelNet | PointPillars |
| --- | --- | --- |
| 中间表示 | 稀疏三维体素 | 柱体伪图像 |
| 主要卷积 | 3D 卷积 | 2D 卷积 |
| 相对速度 | 慢 | 快 |
| 小目标/遮挡精度 | 较高 | 略低 |
| 显存占用 | 高 | 低 |
| 部署优化难度 | 高 | 低 |
但速度与精度的权衡不是固定的。稀疏卷积、TensorRT、FP16 和专用算子都能同时优化两者。PointPillars 在低算力平台上更容易达到实时;VoxelNet 经过工程优化后也能改善,但投入的精力更大。选型不是问哪个绝对好,而是问你的仓库需要几点前清点完:夜间巡逻要求 30Hz 响应,就选竖档;离线制图愿意多等 0.1 秒,就选精细书架。
工程落地与选型
工程落地首先要看生态。PointPillars 因为生成的是伪图像,可以直接复用 2D 检测领域的成熟优化:TensorRT、FP16、NPP 库、CUDA 核都有现成方案;VoxelNet 需要自定义 3D 卷积算子,算子优化高度依赖框架和硬件厂商支持,部署成本明显更高。
在机器人场景中,算力通常是 Jetson 这类嵌入式平台,点云密度有限,PointPillars 往往成为默认第一版方案。如果场景需要识别密集重叠的行人与自行车,并且允许更高功耗,VoxelNet 或混合方案会更合适。选型建议如下:
1. 对延迟有硬指标,例如自动驾驶 30Hz 推理,优先 PointPillars。
2. 离线处理高密度点云,例如高精地图自动标注,选 VoxelNet。
3. 想要快速迭代验证算法,先用 PointPillars,后续再针对难点优化。
4. 需要同时完成 BEV 分割、路径规划等多任务,伪图像结构更方便共享特征。
最终,所有方案都要依靠数据闭环和评测指标验证。没有万能的仓库整理方式,只有适合当前货物、仓库高度和限时要求的方案。多做几次对照实验,比在纸面上争论“谁更强”更有价值。
AI 应用场景
自动驾驶实时感知
阶段:模型选型与推理部署
解决的问题:传统 2D 检测无法直接处理雷达点云,而 VoxelNet 的 3D 卷积在车规级计算平台上难以达到实时。PointPillars 先把点云变成竖档柱体,再散布成伪图像,用 2D 卷积完成检测,延迟低且部署链路成熟。
具体案例:某 Robotaxi 项目使用 32 线激光雷达,感知模块要求每帧 80ms 内输出车辆和行人框。工程师以 PointPillars 为骨干网络,搭配 TensorRT FP16,在 Orin 平台达到 25FPS,并将模型量化误差控制在 1% 以内。
低速物流 AGV 避障
阶段:模型训练与边缘部署
解决的问题:AGV 上算力有限,常见 16 线激光雷达点云稀疏,直接上 VoxelNet 容易出现大量空体素,浪费算力。PointPillars 只按 XY 分竖档,对稀疏点更稳定,也更符合室内低速场景的实时避障需求。
具体案例:一个工厂仓储 AGV 项目需要识别前方 5 米内的人、托盘和叉车。算法团队在 Jeston Orin Nano 上部署 PointPillars,达到 20FPS,误检率低于 2%,成功支撑了自动急停与绕障。
离线高精地图自动标注
阶段:离线数据处理与自动化标注
解决的问题:高精地图制图车使用 64 线激光雷达,点云密集、数据量大,不需要实时推理,却需要尽可能高的检测精度。VoxelNet 的三维体素和 3D 卷积能更好还原物体轮廓,适合离线批量生成候选框。
具体案例:某图商使用 VoxelNet 对城市道路点云自动标注路杆、路口和车辆,后期人工只修正边缘框,标注效率提升了约 60%,尤其改善了高层遮挡场景下的召回率。
Python 演示
import numpy as np def voxel_encode(points, voxel_size=0.4, max_groups=64): indices = np.floor(points[:, :3] / voxel_size).astype(int) groups = {} for i, idx in enumerate(indices): key = tuple(idx) if key in groups: groups[key].append(points[i]) elif len(groups) < max_groups: groups[key] = [points[i]] feats = [] for pts in groups.values(): pts = np.array(pts) center = pts[:, :3].mean(axis=0) aug = np.hstack([pts, pts[:, :3] - center]) feats.append(aug.max(axis=0)) return np.array(feats) def pillar_encode(points, grid_size=0.4, max_groups=64): xy_idx = np.floor(points[:, :2] / grid_size).astype(int) groups = {} for i, idx in enumerate(xy_idx): key = tuple(idx) if key in groups: groups[key].append(points[i]) elif len(groups) < max_groups: groups[key] = [points[i]] feats = [] for pts in groups.values(): pts = np.array(pts) center_xy = pts[:, :2].mean(axis=0) center_xyz = np.append(center_xy, 0.0) pfeat = np.hstack([pts, pts[:, :3] - center_xyz, pts[:, :2] - center_xy]) feats.append(pfeat.max(axis=0)) return np.array(feats) def main(): rng = np.random.default_rng(0) points = rng.random((500, 4)) voxel_feats = voxel_encode(points) pillar_feats = pillar_encode(points) print("voxel groups:", voxel_feats.shape[0]) print("pillar groups:", pillar_feats.shape[0]) print("voxel feature dim:", voxel_feats.shape[1]) print("pillar feature dim:", pillar_feats.shape[1]) if __name__ == "__main__": main()
indices = np.floor(points[:, :3] / voxel_size).astype(int)用整数索引把连续空间切成体素格子。groups[key].append(points[i])同一格子的点聚到一起,天然处理无序点云。center = pts[:, :3].mean(axis=0)`; `aug = np.hstack([pts, pts[:, :3] - center])加入相对中心偏移,让特征带位置上下文。feats.append(aug.max(axis=0))最大池化,保留体素内最显著特征。xy_idx = np.floor(points[:, :2] / grid_size).astype(int)柱体只看 XY,所以 Z 不参与分组。pfeat = np.hstack([pts, pts[:, :3] - center_xyz, pts[:, :2] - center_xy])9 维柱体特征,高度仍保留在数值中。
常见误区
误区我觉得点云有坐标,把它填成一个固定大小的三维数组不就行了,把二维卷积改成三维卷积,就跟图像检测一样了。
正解点云不像图像每个像素都有值,大部分位置是空的。直接建稠密网格会浪费算力和显存,而且点云的点数、顺序不固定。需要先用体素或柱体这样的稀疏表示,再用 PointNet 或稀疏卷积聚合。好比不能要求仓库里每个书架格子都塞满纸星,只有非空格才值得读。
为何常见:学 2D 检测时习惯了规则像素,下意识把坐标离散化想象成填数组。
误区为了不漏掉小物体,我把体素设成 1 厘米,分辨率越高精度一定越高。
正解格子越小,空体素数量会指数增长,3D 卷积和显存开销都会爆炸,反而导致训练变慢甚至跑不起来。体素大小要与点云密度匹配,激光雷达点云通常使用几十厘米量级的体素,并配合稀疏化技术。
为何常见:直觉认为分辨率越高越好,忽略了稀疏性和算力约束。
误区柱子只按 XY 分组,高度信息完全丢了,所以它一定比 VoxelNet 差很多。
正解PointPillars 只是不按 Z 方向切格,但每个点仍然把 \(z\)、到柱体中心的三维偏移写进特征里,后续网络可以恢复高度。就像竖档文件柜不把文件按层分格,但每张纸上都写了层高备注。
为何常见:从示意图上看柱体是二维网格,容易被误读成丢失高度。
误区既然算力差了十几倍,检测精度肯定也差十个百分点,所以不能用在精确任务上。
正解在 KITTI 等基准上,PointPillars 与 VoxelNet 的 AP 差距通常只有几个点,经过数据增强和工程调优后差距还会缩小。速度收益主要来自去掉 Z 方向空体素和 3D 卷积,而不是粗暴丢信息。
为何常见:把速度与精度看成简单线性对立,忽略了特征编码保留了大量关键信息。
误区我选了 PointPillars,部署时应该能直接 20FPS 跑在 Jetson 上,不需要再调。
正解工程落地还要处理点云预处理、张量形状、TensorRT 算子支持、多雷达融合、后处理、类别不平衡等问题。选型只是第一步,PointPillars 的 2D 伪图像确实更容易复用成熟优化,但依然需要大量适配工作。
为何常见:算法工程师常低估部署优化成本,以为网络轻就一定快。
面试题
Q1. PointPillars 把点云划分成哪种单元?
- A.三维立方体体素
- B.垂直方向的柱体← 正确答案
- C.二维灰度像素
- D.完全无序的点列表
答案:B。PointPillars 只在 XY 平面划分柱体,Z 方向不单独切格,所有高度信息通过特征编码保留。A 是 VoxelNet 的做法,C 和 D 都不符合柱体化思路。
Q2. VoxelNet 在一个体素内聚合点特征时,主要使用哪个操作?
- A.最大值池化← 正确答案
- B.全局平均池化
- C.随机采样
- D.中值滤波
答案:A。VFE 层在 MLP 变换后使用最大值池化,保留体素内最显著的点特征。平均池化会稀释峰值响应,随机采样和信息丢失有关,中值滤波不是 VFE 的标准操作。
Q3. 一个用于水平路面物体的 3D 检测框通常用几个参数表示?
- A.5
- B.6
- C.7← 正确答案
- D.9
答案:C。3D 检测框一般用中心坐标 \((x,y,z)\)、长宽高 \((l,w,h)\) 和朝向角 \(\theta\),共 7 个参数。5 或 6 个参数通常无法描述旋转框,9 维通常是 PointPillars 的点特征,不是框参数。
Q4. PointPillars 的伪图像是如何生成的?
- A.柱体特征按 XY 坐标散布到二维网格← 正确答案
- B.体素特征直接拼接成三维张量
- C.激光点云按距离排序成图片
- D.RPN 输出的边界框画成图像
答案:A。PointPillars 先把每个柱体编码成特征向量,再根据柱体所在的 XY 坐标把它们放到二维网格上,形成类似图像的伪图像。这个过程是特征散布,不是点云排序,也不是把检测框画出来。
Q5. 在相同条件下,下列哪个说法通常正确?
- A.VoxelNet 推理速度低于 PointPillars← 正确答案
- B.PointPillars 推理速度低于 VoxelNet
- C.两者推理速度完全相同
- D.两者都不能用于激光雷达
答案:A。VoxelNet 使用 3D 卷积和三维体素,计算量更大,通常在同等条件下比 PointPillars 慢。PointPillars 用伪图像和 2D 卷积,速度更快,因此 B 和 C 错误。
Q6. PointPillars 比 VoxelNet 轻量的原因包括?
- A.没有 Z 方向体素划分← 正确答案
- B.主要使用 2D 卷积← 正确答案
- C.柱体特征可散布成伪图像← 正确答案
- D.完全不用点云原始特征
答案:ABC。PointPillars 不做 Z 方向切格,减少空格数量;伪图像使后续卷积变成 2D,计算更高效。它没有丢掉点云原始特征,点的坐标、反射率和中心偏移仍然参与编码,因此 D 错误。
Q7. VFE 层把哪两部分特征拼接后再送入 MLP?
- A.点特征与体素中心偏移← 正确答案
- B.检测框与类别概率
- C.伪图像与灰度图
- D.时间戳与雷达 ID
答案:A。VFE 层拼接原始点特征和相对体素中心的偏移,使网络知道每个点在自己的格子里处于什么位置。检测框、伪图像和时间戳都出现得更晚,不属于 VFE 输入。
Q8. 在 16 线稀疏点云和低功耗 Jetson 平台上,通常优先选择哪个方案?
- A.高分辨率 VoxelNet
- B.PointPillars← 正确答案
- C.纯 3D 卷积网络
- D.先补点云再检测
答案:B。稀疏点云下,高分辨率体素会产生大量空体素,3D 卷积在低功耗平台上难以实时。PointPillars 的柱体结构对稀疏点更稳定,部署更轻量。补点云会引入伪信息,不是首选。
Q9. VoxelNet 在密集点云上的主要劣势包括?
- A.体素数量巨大← 正确答案
- B.3D 卷积计算开销高← 正确答案
- C.显存占用偏高← 正确答案
- D.无法输出三维朝向
答案:ABC。密集点云下,非空体素增多,三维网格规模变大,3D 卷积和显存开销也会上升。VoxelNet 完全可以输出带朝向的 3D 框,因此 D 错误。
Q10. 关于 VoxelNet 与 PointPillars 在 KITTI 上车体检测的对比,合理说法是?
- A.二者 AP 差距通常不大但速度量级不同← 正确答案
- B.VoxelNet 的 AP 比 PointPillars 高约 30 个点
- C.PointPillars 的速度比 VoxelNet 还慢
- D.PointPillars 完全不检测车辆
答案:A。在同一数据集上,VoxelNet 的精度通常略高,但差距一般只有几个百分点,而 PointPillars 在速度上往往能快数倍到十余倍。高 30 个点、速度更慢或完全不检测车辆都不符合实际。