MapQR 是 ECCV 2024 提出的、面向自动驾驶在线矢量化高精地图构建 的端到端 SOTA 方法 ,核心是通过增强点集查询机制,解决传统方法精度低、信息不一致的问题,在 nuScenes/Argoverse2 上实现最优 mAP 且保持高效运行。
一、核心定位
- 任务:在线矢量化 HD 地图构建(输出车道线、人行横道、道路边界等矢量元素)
- 范式:DETR 类端到端架构 (基于 BEV 特征 + Transformer 解码器)(BEV(Bird's-Eye View,鸟瞰视角)特征是将多传感器(相机 / 激光雷达)采集的第一人称透视数据 ,转换为自上而下的统一坐标系下的二维特征图 )
- 目标:替代传统 "点查询",用更优的查询设计提升地图构建精度与效率
二、核心创新:Scatter-and-Gather Query(分散 - 聚合查询)
这是 MapQR 最关键的设计,彻底改变查询方式:
- 用实例查询,不用点查询 不单独预测每个点,而是以地图元素实例为单位做查询(如一条车道、一个斑马线)。
- **分散(Scatter)**把一个实例查询,拆成多个点查询,去 BEV 特征里提取细节,预测完整点集。
- **聚合(Gather)**把分散预测的点,重新聚合成一个完整实例,做匹配与监督。
- 优势 共享同个地图元素的内容信息,避免点查询的语义冲突 / 信息不一致 ,同时加入参考点位置嵌入,用先验位置信息增强查询表达力。
- 基于 GKT/BEVFormer),不增加算力负担
- 专用点集损失与实例匹配策略,适配矢量化地图的几何形状监督
三、配套改进
- 轻量化、高效改进 BEV 编码器(基于 GKT/BEVFormer),不增加算力负担
- 专用点集损失与实例匹配策略,适配矢量化地图的几何形状监督
四、性能表现(nuScenes,ResNet-50)
表格
| Epoch | mAP1(严格阈值 {0.2,0.5,1.0}) | mAP2(宽松阈值 {0.5,1.0,1.5}) |
|---|---|---|
| 24 | 43.3 | 66.4 |
| 110 | 50.5 | 72.6 |
| 在同等算力下,精度超越 MapTRv2 等前人方法,且推理效率更高。 |
五、技术基座与依赖
- 基于:MapTRv2(核心框架复用)
- 借鉴:BEVFormer、GKT、ConditionalDETR、DAB-DETR
- 兼容数据集:nuScenes、Argoverse 2
- 开源:提供配置文件、预训练模型,训练 / 推理流程与 MapTRv2 完全一致。
六、MapQR 项目完整代码解析(ECCV 2024)
(1)项目整体目录结构
MapQR-main/
├── assets/ # 论文配图、架构图
├── ckpts/ # 预训练权重存放目录
├── data/ # nuScenes/Argoverse2 数据集
├── docs/ # 安装、数据准备、训练、可视化文档
├── figs/ # 结果可视化输出
├── mmdetection3d/ # 内置的 MMDetection3D 核心库(版本锁定)
├── projects/ # 核心代码(所有自定义模块都在这里)
│ ├── configs/ # 所有配置文件
│ │ ├── _base_/ # 基础配置(数据集、运行时、模型)
│ │ ├── bevformer/
│ │ ├── maptr/
│ │ ├── maptrv2/
│ │ └── mapqr/ # MapQR 专属配置(你要修改的地方)
│ └── mmdet3d_plugin/ # 自定义插件(不修改 mmdet3d 核心)
│ ├── bevformer/
│ ├── core/
│ ├── datasets/ # 地图构建专用数据集类
│ ├── maptr/
│ └── models/ # 所有模型定义(你要修改的核心)
│ ├── backbones/
│ ├── hooks/
│ ├── opt/
│ ├── utils/
│ ├── __init__.py
│ ├── mapqr_decoder.py # 核心:分散-聚合解码器
│ └── mapqr_head.py # 核心:预测头与损失
├── tools/ # 训练、测试、数据转换脚本
│ ├── analysis_tools/
│ ├── data_converter/
│ ├── fp16/
│ ├── maptr/
│ ├── maptrv2/
│ ├── misc/
│ ├── model_converters/
│ ├── work_dirs/ # 训练日志与权重输出
│ ├── create_data.py
│ ├── dist_test.sh
│ ├── dist_test_map.sh
│ ├── dist_train.sh
│ ├── test.py
│ └── train.py # 训练入口
├── venv/ # 虚拟环境
├── requirement.txt # 依赖列表
└── README.md
核心代码位置总结:
- 所有模型修改:
projects/mmdet3d_plugin/models/ - 所有配置修改:
projects/configs/mapqr/ - 训练/测试入口:
tools/train.py/tools/test.py
(2)核心前向流程(输入 → 输出)
环视6张RGB图像
↓
ResNet-50 + FPN 提取多尺度图像特征
↓
GKT 视图转换 → 200×200×256 BEV 特征图
↓
BEV 编码器(2层 Transformer)增强特征
↓
MapQR 解码器(6层,核心:分散-聚合查询)
↓
MapQR Head 预测
├─ 分类分支:每个实例的类别(车道线/人行横道/路边界)
└─ 回归分支:每个实例的18个点坐标
↓
损失计算(分类损失 + 点集倒角距离损失 + 方向损失)
(3)核心模块逐行解析
MapQR 解码器(mapqr_decoder.py)
这是 MapQR 区别于 MapTR 的唯一核心,也是你插入 CAFM 的位置。
(1)解码器层定义
class MapQRDecoderLayer(nn.Module):
def __init__(self, embed_dims=256, num_heads=8, feedforward_channels=512, dropout=0.1):
super().__init__()
# 1. 实例查询之间的自注意力(仅900个查询,比MapTR的16200个快18倍)
self.self_attn = nn.MultiheadAttention(embed_dims, num_heads, dropout, batch_first=True)
self.norm1 = nn.LayerNorm(embed_dims)
# 2. 交叉注意力:查询与BEV特征交互
self.cross_attn = nn.MultiheadAttention(embed_dims, num_heads, dropout, batch_first=True)
self.norm2 = nn.LayerNorm(embed_dims)
# ========== 你插入 CAFM 的位置 ==========
# self.cafm = CAFM(dim=embed_dims)
# self.norm3 = nn.LayerNorm(embed_dims)
# ======================================
# 3. 前馈网络
self.ffn = nn.Sequential(
nn.Linear(embed_dims, feedforward_channels),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Linear(feedforward_channels, embed_dims),
nn.Dropout(dropout)
)
self.norm4 = nn.LayerNorm(embed_dims)
(2)前向传播:分散-聚合查询的实现
def forward(self, instance_queries, bev_feat, query_pos=None, key_pos=None):
B, N, C = instance_queries.shape # N=900(实例查询数量)
# --------------------------
# 第一步:实例查询自注意力
# --------------------------
q = self.self_attn(instance_queries + query_pos,
instance_queries + query_pos,
instance_queries)[0]
instance_queries = self.norm1(instance_queries + q)
# --------------------------
# 第二步:分散(Scatter)操作
# 核心:1个实例查询 → 18个点查询(共享内容,不同位置)
# --------------------------
# 1. 复制实例查询18次:[B,900,256] → [B,900×18,256]
point_queries = instance_queries.repeat_interleave(18, dim=1)
# 2. 生成18个参考点的位置嵌入,加到点查询上
ref_points = self.get_reference_points(B, device=instance_queries.device)
pos_embed = self.ref_point_embed(ref_points) # [B,900×18,256]
point_queries = point_queries + pos_embed
# --------------------------
# 第三步:点查询与BEV特征交叉注意力
# --------------------------
bev_flat = bev_feat.flatten(2).transpose(1, 2) # [B,200×200,256]
q = self.cross_attn(point_queries + key_pos, bev_flat, bev_flat)[0]
point_queries = self.norm2(point_queries + q)
# --------------------------
# ========== 你插入 CAFM 前向的位置 ==========
# q = self.cafm(point_queries)
# point_queries = self.norm3(point_queries + q)
# ==========================================
# --------------------------
# 第四步:聚合(Gather)操作
# 核心:18个点查询 → 1个实例查询(平均池化+MLP)
# --------------------------
instance_queries = point_queries.reshape(B, 900, 18, C).mean(dim=2)
# --------------------------
# 第五步:前馈网络
# --------------------------
q = self.ffn(instance_queries)
instance_queries = self.norm4(instance_queries + q)
return instance_queries, point_queries
关键优势:
-
解码器输入输出都是900个实例查询,而非 MapTR 的 900×18=16200 个点查询
-
显存占用降低 80%,推理速度提升 2-3 倍
-
同一实例的所有点共享内容信息,彻底解决点查询的语义冲突问题
- MapQR 预测头(
mapqr_head.py)
处理解码器输出,生成最终预测并计算损失:
class MapQRHead(nn.Module):
def __init__(self, num_classes=3, in_channels=256, num_query=900, num_points=18):
super().__init__()
self.num_classes = num_classes
self.num_query = num_query
self.num_points = num_points
# 分类分支:预测每个实例的类别
self.cls_branch = nn.Linear(in_channels, num_classes)
# 回归分支:预测每个实例的18个点坐标
self.reg_branch = nn.Linear(in_channels, num_points * 2)
# 损失函数
self.cls_loss = FocalLoss(alpha=0.25, gamma=2.0)
self.point_loss = ChamferDistanceLoss() # 点集匹配损失
self.dir_loss = CosineEmbeddingLoss() # 边缘方向损失
- BEV 编码器(基于 GKT 加速)
MapQR 对 BEVFormer 的 BEV 编码器做了轻量化改进:
-
层数从 6 层减少到 2 层
-
引入 GKT 预计算查表加速,避免实时相机参数计算
-
新增参考点位置嵌入,增强查询的位置感知
- 数据集加载(
datasets/nuscenes_map_dataset.py)
-
专用数据集类
NuScenesMapDataset,解析 nuScenes 的矢量化地图标注 -
每个样本包含:6张环视图像 + 对应地图元素的点集标注
-
数据增强仅保留几何变换(翻转、缩放),避免破坏点集结构
七、训练流程解析(tools/train.py)
-
配置加载 :读取
projects/configs/mapqr/mapqr_nusc_r50_24ep.py,合并基础配置 -
环境初始化:初始化分布式环境、随机种子、日志
-
模型构建 :通过
build_model从配置中实例化 CAFMap/MapQR -
数据集构建:加载训练集和验证集,创建 DataLoader
-
训练循环 :
for epoch in range(total_epochs): for batch in train_loader: optimizer.zero_grad() # 前向传播 + 损失计算 losses = model(**batch) # 反向传播 + 优化 loss = sum(losses.values()) loss.backward() optimizer.step() # 每 epoch 评估一次 if epoch % eval_interval == 0: val_results = evaluate(model, val_loader) save_best_checkpoint(val_results['mAP2'])