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