Files
fusion_LCD/network_learning/LEARNING_GUIDE.md
2026-05-09 17:03:40 +08:00

420 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 网络结构学习指南
> 论文:[Cross Fusion of Point Cloud and Learned Image for Loop Closure Detection](../Cross_Fusion_of_Point_Cloud_and_Learned_Image_for_Loop_Closure_Detection.pdf)
---
## 目录
1. [项目总览](#1-项目总览)
2. [网络结构全景图](#2-网络结构全景图)
3. [ALNet — 图像特征提取器](#3-alnet--图像特征提取器)
4. [RICNN — 旋转不变CNN](#4-ricnn--旋转不变cnn)
5. [EncodePosition — 位置编码](#5-encodeposition--位置编码)
6. [Converter — 跨模态特征转换器](#6-converter--跨模态特征转换器)
7. [Generator & FusionHead — 特征生成与融合](#7-generator--fusionhead--特征生成与融合)
8. [LocalPool — 局部特征聚合](#8-localpool--局部特征聚合)
9. [NetVLAD — 全局描述子聚合](#9-netvlad--全局描述子聚合)
10. [UOTHead — 最优传输位姿估计](#10-uothead--最优传输位姿估计)
11. [完整数据流](#11-完整数据流)
12. [学习路线建议](#12-学习路线建议)
---
## 1. 项目总览
本项目实现**点云-图像跨模态融合的闭环检测**系统,共包含 **9 个网络结构**
| # | 网络 | 源文件 | 作用 |
|---|------|------|------|
| 1 | **ALNet** | `ALIKE/alnet.py` | 图像特征提取(关键点+描述子) |
| 2 | **RICNN** | `BEVNet.py` | BEV点云特征提取旋转不变 |
| 3 | **EncodePosition** | `BEVNet.py` | 关键点空间位置编码 |
| 4 | **Converter** | `net.py` | 跨模态特征空间转换 |
| 5 | **Generator** | `net.py` | 变长特征→固定大小 |
| 6 | **FusionHead** | `net.py` | 多来源特征Attention融合 |
| 7 | **LocalPool** | `net.py` | 多像素特征→单体素聚合 |
| 8 | **NetVLAD** | `netvlad.py` | 局部特征→全局描述子 |
| 9 | **UOTHead** | `uot.py` | 最优传输→位姿估计 |
### 运行模式
| flag | 含义 | 包含模块 |
|------|------|---------|
| `bev` | 仅点云 | 2, 3, 8 |
| `img` | 仅图像 | 1 |
| `fusion` | 完整融合 | 全部 1-9 |
### 关键维度
| 参数 | 值 |
|------|-----|
| BEV图尺寸 (H×W) | 320×320 |
| BEV输入通道 | 7 (max_z, intensity, density, cx, cy, cz, ci) |
| 图像尺寸 (H×W) | 192×576 |
| 关键点数量 (BEV/Img) | 150 |
| 特征维度 | 128 |
| VLAD聚类数 | 16 |
| VLAD输出维度 | 2048 (=16×128) |
---
## 2. 网络结构全景图
```
┌─────────────────────────┐
│ 输入 img + bev + relation│
└──────┬──────────┬───────┘
│ │
┌──────────┘ └──────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ImgHead │ │ BEVHead │
│ (ALNet) │ │ (RICNN) │
│ │ │ │
│ score_img │ │ score_bev │
│ fea_img │ │ fea_bev │
│ fea_kpl │ │ fea_kpt_orig │
│ key_pixels │ │ key_points │
└──────┬───────┘ │ vlad_bev │
│ └──────┬───────┘
│ │
│ ┌─────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────┐
│ FusionHead │
│ │
│ LocalPool → Converter(cvt_bev) │
│ GridSample → Converter(cvt_img) │
│ Generator → FusionHead(Attention) │
│ │
│ fea_kpt_fusion (B, 128, 150) │
└─────────────┬───────────────────────┘
┌───────▼────────┐
│ NetVLAD │
│ vlad_fusion │
└───────┬────────┘
┌───────▼────────┐
│ VLAD 融合 │
│ w*fusion + │
│ (1-w)*bev │
└───────┬────────┘
┌───────▼────────┐
│ UOTHead │
│ (仅训练时) │
│→ transformation│
└────────────────┘
```
---
## 3. ALNet — 图像特征提取器
**源码**: `ALIKE/alnet.py` | **Demo**: `python 01_alnet_demo.py`
### 结构
```
输入: (B, 3, 192, 576)
block1: ConvBlock(3→16) → (B, 16, 192, 576)
↓ MaxPool2d(2)
block2: ResBlock(16→32) → (B, 32, 96, 288)
↓ MaxPool2d(4)
block3: ResBlock(32→64) → (B, 64, 24, 72)
↓ MaxPool2d(4)
block4: ResBlock(64→128) → (B, 128, 6, 18)
特征聚合: 4尺度concat + 上采样 → (B, 128, 192, 576)
↓ Conv1x1(128→129)
输出: score(B,1,192,576) + desc(B,128,192,576)
```
### 设计要点
- **多尺度特征聚合**: 4阶段特征通过1x1conv压缩后上采样拼接兼顾浅层定位精度和深层语义
- **共享检测+描述**: 单一骨干同时输出关键点得分和密集描述子
- **配置(alike-n)**: c1=16, c2=32, c3=64, c4=128, dim=128
- **关键点选择**: NMS (radius=2, 2轮) + Top-K=150
### 各阶段含义
| 阶段 | 分辨率 | 学习内容 |
|------|-------|---------|
| block1 | 原始 | 边缘、角点等低级特征 |
| block2 | 1/2 | 纹理、局部形状 |
| block3 | 1/8 | 物体部件、语义信息 |
| block4 | 1/32 | 全局上下文、场景级信息 |
---
## 4. RICNN — 旋转不变CNN
**源码**: `BEVNet.py` | **Demo**: `python 02_ricnn_demo.py`
### 结构
```
输入: BEV图像 (B, 3, 320, 320)
block1: RIConvBlock(3→16) → (B, 16, 320, 320)
↓ RIMaxpool2d(2)
block2: RIResBlock(16→32) → (B, 32, 160, 160)
↓ RIMaxpool2d(5, stride=4)
block3: RIResBlock(32→64) → (B, 64, 40, 40)
↓ RIMaxpool2d(5, stride=4)
block4: RIResBlock(64→128) → (B, 128, 10, 10)
特征聚合 (同ALNet) → (B, 128, 320, 320)
↓ Conv1x1(128→129)
输出: score(B,1,320,320) + desc(B,128,320,320)
```
### 旋转不变性原理(核心创新)
BEV图像中车辆旋转时点云投影会旋转。RICNN通过以下机制保持特征不变
**RIConv2d**: 根据kernel位置到中心的**欧氏距离**分组,同距离共享权重
```
标准 5×5 kernel (25个独立权重): RI kernel (3组共享权重):
[0 1 2 3 4] [0 1 1 1 0]
[1 2 3 4 5] [1 2 2 2 1]
[2 3 4 5 6] [1 2 3 2 1] ← 3组: dis=0,1,2
[1 2 3 4 5] [1 2 2 2 1]
[0 1 2 3 4] [0 1 1 1 0]
```
**RIMaxpool2d / RIAvgpool2d**: 只取圆形区域内像素,排除对角线角点(旋转不一致)
**推理优化**: `disable_ri()` 可将RI层转为标准CNN层
---
## 5. EncodePosition — 位置编码
**源码**: `BEVNet.py` | **Demo**: 包含在 `02_ricnn_demo.py`
```
输入: kpts (B, 150, 4), fea (B, 128, 150)
1. 计算150×150关键点欧氏距离矩阵
2. 距离直方图 (16 bins, range=[1,80]m)
3. 直方图归一化
4. MLP: 16→64→64→128
5. fea_out = fea + MLP(hist) (残差连接)
```
将关键点间空间关系编码到特征中,帮助网络理解"哪些关键点在物理空间中相邻"。
---
## 6. Converter — 跨模态特征转换器
**源码**: `net.py` | **Demo**: `python 03_converter_demo.py`
```
输入: x (B, 128, N) N个特征点
├─ 路径1: Self-Attention(MHA) → x2 (B, 128, N)
├─ 路径2: Conv1d瓶颈(128→32→16→32→128) → x3 (B, 128, N)
└─ concat([x2,x3]) → Conv1d(256→128) → 输出 (B, 128, N)
```
### 两种使用
| 转换器 | 输入 → 输出 | 含义 |
|--------|------------|------|
| `cvt_bev` | 图像特征 → BEV空间 | 让图像特征"理解"BEV几何 |
| `cvt_img` | BEV特征 → 图像空间 | 让BEV特征"理解"图像语义 |
双路径设计MHA捕获全局关系Conv1d做逐点变换互补增强。
---
## 7. Generator & FusionHead — 特征生成与融合
**源码**: `net.py` | **Demo**: `python 04_generator_fusion_demo.py`
### Generator (全景特征生成器)
```
输入: (B, 128, N) N可变
↓ Self-Attention
↓ ConvTranspose1d(k3, s3) → 上采样扩展
↓ AdaptiveMaxPool1d(150)
输出: (B, 128, 150) 固定K=150
```
将可变数量的匹配点特征压缩为固定150个与BEV关键点对齐。
### FusionHead (跨模态融合头)
```
输入: (B, 128, 150, 4) ← [original, gen, gen_gen, kpl_gen]
Step 1: 对前3对做 Self-Attn → max聚合
Step 2: Cross-Attn with kpl_gen (图像空间特征)
concat(original, cross_out) → Conv1d(256→128)
输出: (B, 128, 150) 融合特征
```
4种特征来源
| 特征 | 来源 | 空间 |
|------|------|------|
| `original` | RICNN直接提取 | BEV |
| `gen` | Generator从图像特征生成 | 图像→BEV |
| `gen_gen` | cvt_bev(cvt_img(original)) | BEV→图像→BEV循环 |
| `kpl_gen` | cvt_img(original) | BEV→图像残留 |
---
## 8. LocalPool — 局部特征聚合
**源码**: `net.py` | 轻量级模块无独立demo
```
输入: (B, 128, N, K) N个体素每体素K个像素(K≤100)
↓ Conv2d(100→10, k=1) + MaxPool2d((1,10))
输出: (B, 128, N, 1) → squeeze → (B, 128, N)
```
一个BEV体素对应图像上多个像素需聚合为单个体素特征1x1 Conv降维 + MaxPool取最显著响应。
---
## 9. NetVLAD — 全局描述子聚合
**源码**: `netvlad.py` | **Demo**: `python 05_netvlad_demo.py`
```
输入: (B, 128, 150, 1)
1. Soft Assignment: Softmax(Conv2d(128→16)(x)) → (B, 16, 150, 1)
2. Residual: x - centroids[16,128] → (B, 16, 150, 128)
3. VLAD Core: Σ(soft_assign × residual) → (B, 16, 128)
4. 归一化: per-cluster L2 → flatten → global L2
输出: (B, 2048)
```
### 为什么用VLAD
| 方法 | 问题 |
|------|------|
| 平均池化 | 丢失空间分布信息 |
| VLAD | 通过聚类保留"哪些类型特征在哪里"的结构信息 |
### VLAD融合
```python
vlads = sigmoid(w) * vlad_fusion + (1 - sigmoid(w)) * vlad_bev
```
---
## 10. UOTHead — 最优传输位姿估计
**源码**: `uot.py` | **Demo**: `python 06_uot_demo.py`
```
输入: feat1,feat2 (B,150,128), kpts1,kpts2 (B,150,3)
1. Cost Matrix: C = 1 - cosine_sim(feat1, feat2) → (B, 150, 150)
2. Sinkhorn Unbalanced OT (5 iterations):
K = exp(-C/ε) where ε = exp(ε_raw)+0.03
a, b 交替更新,γ 控制质量正则
T = diag(a)·K·diag(b) → (B, 150, 150)
3. 投影: project_kpts = T @ kpts2 / ΣT → (B, 150, 3)
4. Weighted SVD → R, t → transformation (B, 3, 4)
```
### 两个可学习参数
| 参数 | 含义 | 效果 |
|------|------|------|
| ε | 熵正则化 | 大→平滑匹配, 小→稀疏匹配 |
| γ | 质量正则化 | 大→质量守恒, 小→允许不匹配 |
非平衡OT允许部分点不匹配对有遮挡的真实场景更鲁棒。
---
## 11. 完整数据流
### 训练时
```
img → ALNet → fea_img, fea_kpl
bev → RICNN → fea_bev, fea_kpt_original, vlad_bev
relation → grid_sample(fea_img) → fea_pl_dual
→ LocalPool → cvt_bev → fea_pt_dual_gen
→ Generator → fea_kpt_original_gen
grid_sample(fea_bev) → fea_pt_dual (训练时)
→ cvt_img → fea_pl_dual_gen (训练时)
fea_kpt_original → cvt_img → fea_kpl_gen
→ cvt_bev → fea_kpt_gen_gen
FusionHead([original, gen, gen_gen, kpl_gen]) → fea_kpt_fusion
→ NetVLAD → vlad_fusion
vlads = sigmoid(w)*vlad_fusion + (1-sigmoid(w))*vlad_bev
UOTHead(fea_kpt_original) → transformation_original
UOTHead(fea_kpt_fusion) → transformation_fusion
```
### 推理时简化
不执行 UOTHead 和 BEV→图像采样`pose_to_frame`),只输出 `vlads` + 局部特征。
---
## 12. 学习路线建议
### 入门约2-3小时
```bash
# 1. 全流程概览
python 08_full_pipeline_demo.py --mode all
# 2. 独立分支
python 01_alnet_demo.py # 图像分支
python 02_ricnn_demo.py # BEV分支含旋转不变性测试 + 位置编码)
# 3. 融合机制
python 03_converter_demo.py # 跨模态转换
python 04_generator_fusion_demo.py # 特征生成 + 融合
# 4. 全局描述子与位姿
python 05_netvlad_demo.py # VLAD聚合
python 06_uot_demo.py # 最优传输位姿估计
```
### 深入
1. 阅读论文 Section 3 (Methodology)
2. 对照代码看每个模块的 `forward` 函数
3. 修改demo中的参数关键点数量、VLAD聚类数观察变化
4. 加载真实checkpoint运行推理
### 运行环境
```bash
conda activate fusion_cyy
cd network_learning
```
所有可视化图像输出在 `network_learning/output/` 目录。
---
*基于代码版本: commit c3d268f*