简介:面向自动驾驶感知与多模态融合学习者的技术文档,共33页PDF,大小2.31MB,系统讲解Transformer架构、注意力机制、激光雷达与视觉特征提取、多模态融合算法设计及实验评估。内容覆盖摄像头/激光雷达/毫米波雷达感知原理与挑战,并给出城市道路、高速、园区、港口、矿山等场景应用案例,适合算法工程师、研究生及自动驾驶爱好者从理论到实践建立完整知识框架。已有102人学习下载。文档支持目录章节跳转,阅读器左侧显示大纲,便于快速定位关键模块;所有文字、图表显示正常,可放心查阅。资源仅供学习参考,勿作商业用途。
1. 多模态Transformer融合激光雷达与视觉数据,解决的不只是“精度”
自动驾驶感知要落地,最困难的部分往往不是单传感器的模型有多强,而是激光雷达和摄像头各说各话。同一辆车,同一个瞬间,LiDAR在自车坐标系里给了稀疏但精确的距离点云,相机给了密集的纹理和色彩,却缺少深度。要让“看”和“测”合流,传统做法是把点云投影成图像,或者直接把两者拼进一个网络。但这类fusion方案有个共通问题:模态间隙(modality gap)没有被建模,投影过程还把3D结构压扁了。
多模态Transformer这条路之所以在近两年成了主流选项,是因为它把融合从“特征相加”升级成“序列交互”。图像被切成patch token,点云被体素化或BEV栅格化后也变成token,然后用注意力机制自动决定:哪个3D区域最需要参考哪块图像纹理。这个设计不依赖人工指定的投影规则,也让模型天然具备在远距离、遮挡和夜间场景下选择信任哪种传感器的能力。适合的读者是已经在跑单纯视觉或纯点云感知、想把两路数据真正揉在一起并控制推理开销的工程师和算法研究员。
2. 为什么Transformer适合做跨模态融合:从体素、patch到注意力交互
2.1 早期融合、后期融合和深度融合的边界在哪
在进入Transformer之前,先厘清一个基本矛盾:两个模态的数据形态完全不同,点云是无序集合,图像是规则栅格,融合发生在哪个阶段直接决定信息流走向。
- 早期融合:点云投影到图像平面,生成深度图,再与RGB图在通道维度concat。实现简单,但投影丢掉的3D结构不可恢复,且在外参标定不准时引入系统性偏差。
- 后期融合:感知分支各自独立推理,BEV框或语义分割结果再做NMS和逻辑合并。两路错误互不干扰,但信息交互太少,丢掉了低层特征的互补性。
- 深度融合:点云特征和图像特征在多个中间层交互,这正是多模态Transformer的位置。不再“投影一次了事”,而是让每个模态的token通过注意力去“查询”另一个模态的信息。
多模态Transformer把深度融合做成了可微的、逐token的动态交互。它的假设是:图像中提取的纹理特征和点云中提取的几何特征,在语义空间里是可对齐的,注意力矩阵就是对齐关系本身。
2.2 Transformer fusion的核心设计选项:cross-attention还是自注意力
常见的多模态Transformer结构有三类,区别在注意力作用域。
| 方案名称 | token来源 | 注意力范围 | 典型做法 |
|---|---|---|---|
| 全局自注意力 | 点云token + 图像token拼接 | 所有token两两交互 | 简单直接,计算量随token数平方增长 |
| 分模态自注意力 | 点云、图像各自独立encoder | 模态内交互 | 双塔结构,后期再加融合层 |
| cross-attention | 一个模态做query,另一个模态做key/value | 跨模态交互 | 显式建模“谁去查谁”,计算量可控 |
我在实际项目中更倾向于cross-attention。原因很直接:场景中90%的融合需求是“图像特征去丰富点云特征”,而不是对称互查。点云本身已经提供了可靠的几何骨架,图像负责给骨架填纹理和语义。
2.2.1 cross-attention的公式拆解
跨模态注意力的标准形式与自注意力几乎一致:
Attention(Q_lidar, K_camera, V_camera) = softmax(Q_lidar · K_camera^T / sqrt(d_k)) · V_camera区别在于Q、K、V的出处。Q_lidar由点云特征经线性投影得到,K_camera、V_camera由图像特征投影得到。这样每个点云token都会在图像token集合里“检索”与自己语义最相关的区域。稀疏的BEV柱子可以聚合来自图像不同空间位置的语义线索。
2.3 点云token化:从无序点集到规则序列
Transformer天然吃有序序列,点云必须结构化。两种主流做法:
- 体素化(voxelization):将3D空间划分为规则网格,每个非空体素用一个MLP或PointNet聚合内部点云特征,再展平成token序列。空间分辨率高,但体素数量可能上万。
- BEV栅格化(BEV grid):直接把高度维压平,生成2D鸟瞰特征图,每个栅格即一个token。计算量小,但丢失了高度信息,对立体障碍物(如天桥、隧道入口)容易产生歧义。
我在设计里优先推荐体素化,但会配合高度编码曲线。原因在下一章展开:BEV丢失的高度信息可以通过图像特征回补,但前提是图像的分辨率足够高、空间对齐足够准。
3. 算法实现:多模态Transformer融合的模型设计与最小可跑代码
3.1 输入编码:图像分支和点云分支各自做什么
先说图像分支。输入一张H×W×3的RGB图,先用2D骨干网络(常见ResNet-50或轻量Swin-T)提取多尺度特征,再取最后一层特征图生成patch token。每个token代表图像上一块局部区域,感受野范围由骨干网络的stage决定。
点云分支更讲究。原始点云是N×4(x, y, z, intensity),不能直接进Transformer。我按以下顺序处理:
- 去除离群点:统计滤波器或半径滤波,排除漂浮噪声。
- 体素化:设置体素边长0.1m到0.2m,每个体素内部点云取均值或MaxPooling。
- 坐标编码:保留体素中心坐标,加上相对自车位置的归一化坐标。
- 高度压缩可选:如果后续要接BEV检测头,可以沿高度维做加权求和。
3.2 融合网络的PyTorch实现
下面这段代码演示最核心的融合模块:点云token作为query,图像token作为key/value,做一次cross-attention。
import torch import torch.nn as nn import torch.nn.functional as F class CrossModalAttention(nn.Module): def __init__(self, d_model=256, n_heads=8, dropout=0.1): super().__init__() assert d_model % n_heads == 0, "d_model必须能被n_heads整除" self.d_model = d_model self.n_heads = n_heads self.head_dim = d_model // n_heads # 点云分支作为query,图像分支作为key/value self.q_proj = nn.Linear(d_model, d_model) self.k_proj = nn.Linear(d_model, d_model) self.v_proj = nn.Linear(d_model, d_model) self.out_proj = nn.Linear(d_model, d_model) def forward(self, lidar_tokens, img_tokens, lidar_mask=None, img_mask=None): # lidar_tokens: [B, N_lidar, d_model] 点云token序列 # img_tokens: [B, N_img, d_model] 图像token序列 B, N_lidar, _ = lidar_tokens.shape N_img = img_tokens.shape[1] # 线性投影并分头,形状变为 [B, n_heads, seq_len, head_dim] Q = self.q_proj(lidar_tokens).view(B, N_lidar, self.n_heads, self.head_dim).transpose(1, 2) K = self.k_proj(img_tokens).view(B, N_img, self.n_heads, self.head_dim).transpose(1, 2) V = self.v_proj(img_tokens).view(B, N_img, self.n_heads, self.head_dim).transpose(1, 2) # 缩放点积注意力 scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.head_dim ** 0.5) if img_mask is not None: # img_mask: [B, N_img],1表示有效token scores = scores.masked_fill(img_mask.unsqueeze(1).unsqueeze(2) == 0, float('-inf')) attn_weights = F.softmax(scores, dim=-1) attn_weights = F.dropout(attn_weights, p=0.1, training=self.training) out = torch.matmul(attn_weights, V) out = out.transpose(1, 2).contiguous().view(B, N_lidar, self.d_model) return self.out_proj(out)这段代码有三个值得说明的细节。scores计算时除以head_dim开根号,是为了防止softmax后梯度消失,这是标准Transformer的点积缩放。img_mask的作用是把图像padding区域对应的attention分数置为负无穷,保证点云token不会从无效图像区域学东西。attn_weights在训练时加dropout,推理时deopout自动关闭,避免融合结果过度依赖某几个图像patch。
3.3 可选的增强:可变形注意力拿到更灵活的感受野
普通cross-attention让每个点云token看全场图像token,计算量是N_lidar × N_img,图像分辨率高时很容易爆显存。一个常见做法是把注意力改成deformable形式,只让query采样周边若干偏移位置的特征:
class DeformableCrossAttention(nn.Module): def __init__(self, d_model=256, n_points=8): super().__init__() self.n_points = n_points # 预测采样点偏移量,输出 n_points 组2D偏移 self.offset_predictor = nn.Linear(d_model, n_points * 2) def forward(self, lidar_tokens, img_feat_map, ref_points): # img_feat_map: [B, C, H, W] 图像特征图,未展平 # ref_points: [B, N_lidar, 2] 每个点云token在图像上对应的参考点(归一化坐标) batch, _, _, W = img_feat_map.shape offsets = self.offset_predictor(lidar_tokens).view(batch, -1, self.n_points, 2) sample_points = ref_points.unsqueeze(2) + offsets * 0.1 # 限制偏移范围 sample_points = sample_points.clamp(0, 1) # 双线性采样(简化示意) sampled_features = F.grid_sample( img_feat_map, sample_points.view(batch, -1, 1, 2), mode='bilinear', align_corners=True ) return sampled_features这里有一个关键的参数:offsets * 0.1。0.1是限制采样点距参考点的最大距离,实际调参时我会先设0.2,观察注意力是否发散,再收紧到0.05~0.1。偏移范围太小会让融合退化成局部特征拼接,太大则引入无关区域噪声。
3.4 从融合模块到完整感知头
融合输出的是增强后的点云token,要变成可用的感知结果还要接任务头。常见两种:
- BEV检测头:把点云token重整为BEV特征图,接CenterHead或DETR类目标检测头,输出3D框和类别。
- 点级分割头:沿用原始点云坐标,在融合后的token特征上做语义分割或前景点分类。
4. 训练的关键工程细节:数据配准、Loss设计与超参调试
4.1 点云与图像的空间对齐是融合的基石
任何跨模态融合,最容易被忽略也最能毁掉结果的就是“对外参”。Transformer的注意力可以学到复杂的多模态关系,但前提是模型能看到正确的对应关系。如果标定外参偏差超过1度,50米处目标的投影误差就超过0.87米,注意力就算想对齐也对不齐。
我一般会在训练前做一遍离线配准检查:挑20帧典型场景,人工确认投影到图像上的3D框是否贴合目标边缘。代码上用nuscenes或自己的bag包都行,核心是检查几步:
python tools/visualize_pcd_on_image.py \ --pcd_path /data/samples/000123.bin \ --img_path /data/samples/000123.jpg \ --calib_path /data/calib/000123.json \ --output_prefix /tmp/fusion_check # 输出:每帧点云投影到图像上的叠加图,用于人工目检参数含义:--calib_path里应包含相机内参、畸变系数、LiDAR到相机的外参(旋转矩阵R和平移向量t)。投影公式是uv = K * (R * xyz + t),注意点云坐标是3D齐次坐标。这个步骤如果出错,后面所有的训练都是白做。
4.2 数据集选择与增强策略
公开数据集方面,nuScenes是目前多模态融合最常用的:它有6个相机、5个激光雷达,且标注了3D框,适合做BEV检测和跟踪研究。Waymo Open Dataset也有同步校准好的LiDAR和相机数据,但采集场景和交通规则偏向北美,迁移到国内道路时要做大量re-training。KITTI现在只适合做单模态baseline,数据量太少,Transformer学不稳定。
增强策略上,图像和点云必须在同一空间变换下同步增强。比如对图像做随机翻转时,点云也要做对应的水平镜像;对图像做缩放时,外参的内参矩阵要做相应缩放。很多多模态融合论文跑不赢baseline,就是因为在增强阶段把空间对应关系破坏了。
4.3 Loss函数设计:检测、分割、对偶监督怎么配
多模态Transformer的融合模块不是直接监督目标,通常用任务头间接指导。检测头常用:
- 分类:focal loss,维度为类别数
- 回归:Smooth L1或IoU loss,预测中心点、尺寸、朝向角
训练时我会加一个辅助loss:对点云token和融合后token分别计算分类结果,强制融合后的特征不能比原始点云特征差。这个“对偶监督”能显著加快训练收敛,特别是融合模块初期不稳定时。
loss_total = cls_loss_fusion + 0.7 * reg_loss_fusion \ + 0.3 * cls_loss_lidar_only + 0.3 * reg_loss_lidar_only这里的系数0.7和0.3是经验值。融合分支loss权重更高,引导模型优先优化融合效果;单模态分支是保底信号,防止融合模块退化。
4.4 超参表格与排查方法
| 超参数 | 推荐值 | 影响 | 排查方向 |
|---|---|---|---|
| 体素边长voxel size | 0.1m(城市场景)/ 0.2m(高速) | 小体素保留细节但token暴增 | 显存溢出先上调体素 |
| 注意力头数n_heads | 8 | 太少学不到多模态对应关系,太多稳定性差 | 看注意力权重是否稀疏到单头 |
| 采样点数n_points | 8~16 | 与图像局部细节密度相关 | 小目标漏检适当加n_points |
| 点云token数量 | 2048~8192 | 直接决定计算量 | 用k-means做空间均匀采样 |
| 图像输入分辨率 | 640×480 到 1280×720 | 高分辨率小目标更有利 | 过低时远端目标模糊 |
训练时如果loss出现震荡,第一步不是改学习率,而是看点云和图像是否对齐。我遇到过最隐蔽的问题:某个batch里点云是32线、图像是1920×1080,但数据加载时图像被resize到640×480而没有同步改内参,导致投影偏移,整个注意力都在学无效信息。
5. 验证与上板前的最后三件事
5.1 用小模型先跑通意图,再放大模型
多模态Transformer的瓶颈常常不在精度而在显存。我在项目里固定只用80%的GPU容量训练,剩余留给验证和预研。初始阶段把Loss只保留分类分支,融合模块只保留一层cross-attention,跑通整个流程后再逐步添加回归分支和更深融合层。好处是快速暴露数据流bug,而不是被训练时间消耗掉耐心。
5.2 四类退化样本必须手动验证
- 雨雾天:激光点云被吸收和散射,比图像退化更严重,观察注意力是否会加大图像权重。
- 远距离小目标(>80m):点云稀疏,图像分辨率不够,常见做法是加一个图像特征金字塔,让BEV token能访问多尺度图像token。
- 运动模糊:车辆颠簸时图像模糊,但点云几何完整,验证融合特征是否不过度信任图像。
- 相机过曝或夜间:图像纹理失效,点云强度信息接管,观察强度通道是否在注意力中占主导。
5.3 量化部署时的注意力易碎点
上板部署是另一个坑。常规做法是转TensorRT int8或FP16,注意力层在FP16下精度容易掉,尤其softmax的指数计算容易溢出。建议保留FP16不动,重点量化标定时的校准集要包含上述四类退化样本,否则融合模块在校准后loss可能从0.9暴涨到3.5。此外,偏置项在int8量化时常被跳过,注意力QKV投影的偏置会造成位置偏移,部署时要把偏置也纳入量化范围,或者直接去掉融合层的偏置。做完这一步,再回到nuScenes验证集跑一轮,确认mAP下降幅度在可接受范围内。
本文还有配套的精品资源,点击获取