bev-project/BEV_FEATURE_SCALE_ANALYSIS.md

346 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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