420 lines
14 KiB
Markdown
420 lines
14 KiB
Markdown
# 网络结构学习指南
|
||
|
||
> 论文:[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*
|