346 lines
10 KiB
Markdown
346 lines
10 KiB
Markdown
|
|
# BEV特征尺度详解 - Phase 4A Stage 1配置
|
|||
|
|
|
|||
|
|
## 📋 目录
|
|||
|
|
1. [BEV特征生成流程](#bev特征生成流程)
|
|||
|
|
2. [多尺度处理细节](#多尺度处理细节)
|
|||
|
|
3. [任务头输入分析](#任务头输入分析)
|
|||
|
|
4. [关键问题解答](#关键问题解答)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 BEV特征生成流程
|
|||
|
|
|
|||
|
|
### 阶段1: 多模态编码 → 单尺度BEV
|
|||
|
|
|
|||
|
|
#### **Camera分支** (多尺度图像 → 单尺度BEV)
|
|||
|
|
```
|
|||
|
|
输入图像 (6×3×256×704)
|
|||
|
|
↓
|
|||
|
|
[SwinTransformer Backbone]
|
|||
|
|
├─ out_indices: [1, 2, 3] # 输出3个尺度
|
|||
|
|
├─ Stage 1: (B, 192, H/4, W/4)
|
|||
|
|
├─ Stage 2: (B, 384, H/8, W/8)
|
|||
|
|
└─ Stage 3: (B, 768, H/16, W/16)
|
|||
|
|
↓
|
|||
|
|
[GeneralizedLSSFPN Neck]
|
|||
|
|
├─ 将3个尺度融合到统一通道
|
|||
|
|
└─ 输出: 3个尺度×256通道
|
|||
|
|
↓
|
|||
|
|
[DepthLSSTransform]
|
|||
|
|
├─ 3D几何变换 (Lift-Splat-Shoot)
|
|||
|
|
├─ xbound: [-54.0, 54.0, 0.2] # Stage 1: 0.2m分辨率
|
|||
|
|
├─ ybound: [-54.0, 54.0, 0.2]
|
|||
|
|
├─ downsample: 2
|
|||
|
|
└─ 输出: **单个BEV特征** (B, 80, 360, 360)
|
|||
|
|
^^^^^^^^^^^^^^^^
|
|||
|
|
关键:多尺度图像特征被投影到单一BEV平面
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### **LiDAR分支** (3D点云 → 单尺度BEV)
|
|||
|
|
```
|
|||
|
|
点云输入
|
|||
|
|
↓
|
|||
|
|
[Voxelization]
|
|||
|
|
└─ Voxel Size: [0.075, 0.075, 0.2]
|
|||
|
|
↓
|
|||
|
|
[SparseEncoder Backbone]
|
|||
|
|
├─ 多尺度稀疏卷积
|
|||
|
|
├─ encoder_channels: [[16,16,32], [32,32,64], [64,64,128], [128,128]]
|
|||
|
|
└─ 输出: **单个BEV特征** (B, 256, 360, 360)
|
|||
|
|
^^^^^^^^^^^^^^^^
|
|||
|
|
关键:3D稀疏特征被压缩到2D BEV
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### **融合阶段** (多模态 → 统一BEV)
|
|||
|
|
```
|
|||
|
|
Camera BEV (B, 80, 360, 360) ┐
|
|||
|
|
├─→ [ConvFuser] → (B, 256, 360, 360)
|
|||
|
|
LiDAR BEV (B, 256, 360, 360) ┘ ^^^^^^^^^^^^^^
|
|||
|
|
统一的单尺度BEV特征
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重要结论1️⃣**:
|
|||
|
|
> **从骨干网络多尺度形成的BEV是【单个尺度】**
|
|||
|
|
> 虽然Camera和LiDAR编码器内部使用多尺度特征提取,但最终输出的BEV特征是统一的单一尺度 (360×360 @ 0.2m分辨率)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 阶段2: Decoder处理 → 多尺度再生成
|
|||
|
|
|
|||
|
|
#### **SECOND Backbone** (单尺度输入 → 多尺度输出)
|
|||
|
|
```yaml
|
|||
|
|
配置:
|
|||
|
|
in_channels: 256 # 输入单尺度BEV
|
|||
|
|
out_channels: [128, 256] # 输出2个尺度
|
|||
|
|
layer_nums: [5, 5]
|
|||
|
|
layer_strides: [1, 2] # 第1层stride=1,第2层stride=2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
融合BEV (B, 256, 360, 360)
|
|||
|
|
↓
|
|||
|
|
[Block 1: stride=1]
|
|||
|
|
├─ 5层卷积
|
|||
|
|
└─ 输出: (B, 128, 360, 360) ← Scale 1 (原分辨率)
|
|||
|
|
↓
|
|||
|
|
[Block 2: stride=2]
|
|||
|
|
├─ 5层卷积 + 下采样
|
|||
|
|
└─ 输出: (B, 256, 180, 180) ← Scale 2 (1/2分辨率)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### **SECONDFPN Neck** (多尺度融合 → 单尺度输出)
|
|||
|
|
```yaml
|
|||
|
|
配置:
|
|||
|
|
in_channels: [128, 256] # 输入2个尺度
|
|||
|
|
out_channels: [256, 256] # 每个尺度输出256通道
|
|||
|
|
upsample_strides: [1, 2] # Scale1不变,Scale2上采样2倍
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Scale 1: (B, 128, 360, 360) → Conv → (B, 256, 360, 360)
|
|||
|
|
↓
|
|||
|
|
Scale 2: (B, 256, 180, 180) → Deconv(×2) → (B, 256, 360, 360)
|
|||
|
|
↓
|
|||
|
|
Concat
|
|||
|
|
↓
|
|||
|
|
最终输出: (B, 512, 360, 360)
|
|||
|
|
^^^^^^^^^^^^^^^^^^^^
|
|||
|
|
单尺度BEV特征 (通道拼接)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重要结论2️⃣**:
|
|||
|
|
> **Decoder输出【单个尺度】的BEV特征**
|
|||
|
|
> SECONDFPN将多尺度特征上采样到统一分辨率后concat,输出512通道的单尺度BEV。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 任务头输入分析
|
|||
|
|
|
|||
|
|
### **3D检测头 (TransFusionHead)**
|
|||
|
|
```python
|
|||
|
|
# bevfusion.py L346
|
|||
|
|
pred_dict = head(x, metas) # x是单个tensor
|
|||
|
|
|
|||
|
|
# 配置
|
|||
|
|
heads:
|
|||
|
|
object:
|
|||
|
|
type: TransFusionHead
|
|||
|
|
in_channels: 512 # ← 接收512通道的单尺度BEV
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**输入**: `(B, 512, 360, 360)` - **单个尺度**的BEV特征
|
|||
|
|
|
|||
|
|
**处理流程**:
|
|||
|
|
```
|
|||
|
|
(B, 512, 360, 360)
|
|||
|
|
↓
|
|||
|
|
[Heatmap Branch]
|
|||
|
|
└─ 生成检测热图 (B, num_classes, H, W)
|
|||
|
|
↓
|
|||
|
|
[Transformer Decoder]
|
|||
|
|
├─ Query-based检测
|
|||
|
|
└─ 精细化Bbox预测
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **BEV分割头 (EnhancedBEVSegmentationHead)**
|
|||
|
|
```python
|
|||
|
|
# bevfusion.py L349
|
|||
|
|
losses = head(x, gt_masks_bev) # x是单个tensor
|
|||
|
|
|
|||
|
|
# 配置
|
|||
|
|
heads:
|
|||
|
|
map:
|
|||
|
|
type: EnhancedBEVSegmentationHead
|
|||
|
|
in_channels: 512 # ← 接收512通道的单尺度BEV
|
|||
|
|
decoder_channels: [256, 256, 128, 128] # 内部4层decoder
|
|||
|
|
grid_transform:
|
|||
|
|
input_scope: [[-54.0, 54.0, 0.75], [-54.0, 54.0, 0.75]] # 360×360
|
|||
|
|
output_scope: [[-50, 50, 0.167], [-50, 50, 0.167]] # 600×600
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**输入**: `(B, 512, 360, 360)` - **单个尺度**的BEV特征
|
|||
|
|
|
|||
|
|
**处理流程**:
|
|||
|
|
```
|
|||
|
|
(B, 512, 360, 360)
|
|||
|
|
↓
|
|||
|
|
[Grid Transform] # 双线性插值
|
|||
|
|
└─ 从360×360上采样到384×384 (padding到540×540范围)
|
|||
|
|
↓
|
|||
|
|
[ASPP多尺度特征提取]
|
|||
|
|
├─ 1×1卷积
|
|||
|
|
├─ 3×3空洞卷积 (rate=6)
|
|||
|
|
├─ 3×3空洞卷积 (rate=12)
|
|||
|
|
└─ 3×3空洞卷积 (rate=18)
|
|||
|
|
↓
|
|||
|
|
[注意力机制]
|
|||
|
|
├─ Channel Attention (SE)
|
|||
|
|
└─ Spatial Attention
|
|||
|
|
↓
|
|||
|
|
[4层Decoder上采样]
|
|||
|
|
├─ Layer 1: 256通道 → 上采样
|
|||
|
|
├─ Layer 2: 256通道 → 上采样
|
|||
|
|
├─ Layer 3: 128通道 → 上采样
|
|||
|
|
└─ Layer 4: 128通道 → 上采样到600×600
|
|||
|
|
↓
|
|||
|
|
[Per-class分类器]
|
|||
|
|
└─ 输出: (B, 6, 600, 600) # 6个类别的分割结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重要结论3️⃣**:
|
|||
|
|
> **检测头和分割头都接收【单个尺度】的BEV特征**
|
|||
|
|
> 两个任务头共享同一个512通道的BEV特征 (360×360),然后各自进行任务特定的处理。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ❓ 关键问题解答
|
|||
|
|
|
|||
|
|
### Q1: 从骨干多尺度形成的BEV是一个尺度还是多个?
|
|||
|
|
|
|||
|
|
**答案:单个尺度 (360×360)**
|
|||
|
|
|
|||
|
|
**详细解释**:
|
|||
|
|
1. **Camera编码器内部**使用多尺度特征 (SwinTransformer的3个输出)
|
|||
|
|
2. **LiDAR编码器内部**使用多尺度稀疏卷积
|
|||
|
|
3. 但**最终投影到BEV空间时**,都被统一到单一分辨率:
|
|||
|
|
- Camera: 通过DepthLSSTransform投影 → 360×360 @ 0.2m
|
|||
|
|
- LiDAR: 通过Voxelize+压缩 → 360×360 @ 0.2m
|
|||
|
|
4. **融合后**仍是单尺度:256通道 × 360×360
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- BEV空间的物理分辨率是统一的 (xbound/ybound定义)
|
|||
|
|
- 多模态融合需要空间对齐,必须是同一尺度
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Q2: 3D检测头和分割头的输入是单个BEV尺寸还是多个?
|
|||
|
|
|
|||
|
|
**答案:单个尺寸 (512×360×360)**
|
|||
|
|
|
|||
|
|
**详细解释**:
|
|||
|
|
1. **输入统一**:两个头都接收decoder输出的同一个tensor
|
|||
|
|
```python
|
|||
|
|
x = self.decoder["neck"](x) # (B, 512, 360, 360)
|
|||
|
|
|
|||
|
|
# 检测头
|
|||
|
|
pred_dict = self.heads["object"](x, metas)
|
|||
|
|
|
|||
|
|
# 分割头
|
|||
|
|
losses = self.heads["map"](x, gt_masks_bev)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **内部处理不同**:
|
|||
|
|
- **检测头**:保持360×360,在这个尺度上做heatmap和transformer
|
|||
|
|
- **分割头**:上采样到600×600 (通过grid_transform)
|
|||
|
|
|
|||
|
|
3. **为什么不直接输入多尺度?**
|
|||
|
|
- 架构设计简洁:统一的BEV特征更易于多任务学习
|
|||
|
|
- 计算效率:避免多尺度特征的重复计算
|
|||
|
|
- 特征共享:两个任务共享前面提取的高层语义
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📐 尺度变化全流程总结
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
阶段 | 特征尺度数 | 具体尺寸 | 说明
|
|||
|
|
------------------|-----------|--------------------------|-------------------
|
|||
|
|
Camera Backbone | 3个尺度 | H/4, H/8, H/16 | 多尺度图像特征
|
|||
|
|
LiDAR Backbone | 1个尺度 | 1440×1440×41 (3D) | 稀疏体素特征
|
|||
|
|
BEV投影 | 1个尺度 | 360×360 @ 0.2m | ★统一到BEV平面
|
|||
|
|
Fuser融合 | 1个尺度 | 256×360×360 | ★多模态统一
|
|||
|
|
Decoder Backbone | 2个尺度 | 360×360, 180×180 | 短暂的多尺度
|
|||
|
|
Decoder Neck | 1个尺度 | 512×360×360 | ★上采样+concat
|
|||
|
|
检测头输入 | 1个尺度 | 512×360×360 | ★共享BEV特征
|
|||
|
|
分割头输入 | 1个尺度 | 512×360×360 | ★共享BEV特征
|
|||
|
|
分割头输出 | 1个尺度 | 6×600×600 | 上采样到GT分辨率
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**核心要点**:
|
|||
|
|
1. ✅ **BEV空间是单尺度的**:由xbound/ybound统一定义
|
|||
|
|
2. ✅ **多尺度只存在于Decoder内部**:SECOND backbone输出2个尺度,但SECONDFPN立即融合回单尺度
|
|||
|
|
3. ✅ **任务头共享单一BEV特征**:都是512×360×360
|
|||
|
|
4. ✅ **分割头内部有多尺度decoder**:4层逐步上采样到600×600
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 Phase 4A配置验证
|
|||
|
|
|
|||
|
|
### **当前训练配置摘要**
|
|||
|
|
```yaml
|
|||
|
|
# Camera BEV生成
|
|||
|
|
vtransform:
|
|||
|
|
xbound: [-54.0, 54.0, 0.2] # 360×360
|
|||
|
|
ybound: [-54.0, 54.0, 0.2]
|
|||
|
|
downsample: 2
|
|||
|
|
|
|||
|
|
# Decoder
|
|||
|
|
decoder:
|
|||
|
|
backbone:
|
|||
|
|
out_channels: [128, 256] # 2个尺度
|
|||
|
|
neck:
|
|||
|
|
in_channels: [128, 256] # 输入2个尺度
|
|||
|
|
out_channels: [256, 256] # 输出拼接→512
|
|||
|
|
|
|||
|
|
# 任务头
|
|||
|
|
heads:
|
|||
|
|
object:
|
|||
|
|
in_channels: 512 # 单尺度输入
|
|||
|
|
map:
|
|||
|
|
in_channels: 512 # 单尺度输入
|
|||
|
|
grid_transform:
|
|||
|
|
output_scope: [[-50, 50, 0.167], [-50, 50, 0.167]] # 600×600
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **特征尺寸验证**
|
|||
|
|
```python
|
|||
|
|
# 从训练日志验证
|
|||
|
|
Epoch [1][10700/15448]
|
|||
|
|
├─ memory: 18893 MB
|
|||
|
|
├─ loss/map/...: 正常训练 ✅
|
|||
|
|
└─ loss/object/...: 正常训练 ✅
|
|||
|
|
|
|||
|
|
# 说明:
|
|||
|
|
# 1. 两个任务头都正常工作 → 输入尺寸正确
|
|||
|
|
# 2. 显存占用18.9GB → 符合单尺度BEV特征的预期
|
|||
|
|
# 3. 360×360的BEV特征足够支持600×600的分割输出
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📚 参考代码位置
|
|||
|
|
|
|||
|
|
| 组件 | 文件路径 | 关键代码行 |
|
|||
|
|
|------|----------|----------|
|
|||
|
|
| BEVFusion主流程 | `mmdet3d/models/fusion_models/bevfusion.py` | L332-340 (fusion+decoder) |
|
|||
|
|
| SECOND Backbone | `mmdet3d/models/backbones/second.py` | L84-97 (输出多尺度) |
|
|||
|
|
| SECONDFPN Neck | `mmdet3d/models/necks/second.py` | L83-99 (concat成单尺度) |
|
|||
|
|
| 检测头forward | `mmdet3d/models/fusion_models/bevfusion.py` | L346 |
|
|||
|
|
| 分割头forward | `mmdet3d/models/fusion_models/bevfusion.py` | L349 |
|
|||
|
|
| 分割头实现 | `mmdet3d/models/heads/segm/enhanced.py` | L192-246 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔬 实验建议
|
|||
|
|
|
|||
|
|
如果未来想要尝试多尺度BEV方案,可以考虑:
|
|||
|
|
|
|||
|
|
1. **方案A:多尺度BEV生成**
|
|||
|
|
- 在DepthLSSTransform阶段生成多个分辨率的BEV (如180×180, 360×360)
|
|||
|
|
- 但需要修改融合逻辑,增加显存和计算量
|
|||
|
|
|
|||
|
|
2. **方案B:保持当前单尺度设计**
|
|||
|
|
- 优点:架构简洁,特征共享好,显存友好
|
|||
|
|
- 当前设计已经能支持600×600的高分辨率分割
|
|||
|
|
- **推荐方案** ✅
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**文档创建时间**: 2025-11-03
|
|||
|
|
**当前训练阶段**: Phase 4A Stage 1 (FP32, Epoch 1, Iter 10700/15448)
|
|||
|
|
**BEV分辨率**: 360×360 @ 0.2m (Stage 1)
|
|||
|
|
**目标分割分辨率**: 600×600 @ 0.167m
|
|||
|
|
|