SSR:Navigation-Guided Sparse Scene Representation for End-to-End Autonomous Driving 代码解读

文章目录

数据流向

  1. 输入: 从nuscenes_vad_dataset.py接收输入数据:多视角图像、3D标注、地图标注、ego信息
  2. 特征提取: ResNet backbone + FPN 提取多尺度特征
  3. BEV编码: 提取历史和当前BEV特征
  4. 感知预测: 检测物体HD地图元素
  5. 规划预测: 基于感知结果和BEV特征进行轨迹规划
  6. Latent预测: 预测未来场景状态
  7. 输出: 检测框、地图元素、规划轨迹、场景预测结果

1、从dataset中准备训练数据(nuscenes_vad_dataset.py)与预处理(datasets/pipelines/transform_3d.py);
2、SSR/projects/mmdet3d_plugin/SSR/SSR.py,模型前向传播阶段;
3、SSR/projects/mmdet3d_plugin/SSR/SSR_head.py

完整流程:

调用 build_dataset → 创建数据集 nuscenes_vad_dataset.py:VADCustomNuScenesDataset  数据
调用 build_model → 创建模型 SSR.py:SSR   模型
调用 custom_train_model → 启动训练(projects/mmdet3d_plugin/SSR/apis/train.py)  训练

1、输入 dataset([VADCustomNuScenesDataset])和 model(SSR)直接传递给 projects/mmdet3d_plugin/SSR/apis/train.py (custom_train_detector)

2、从custom_train_detector → projects/mmdet3d_plugin/SSR/apis/mmdet_train.py
【/apis/mmdet_train.py 其中包括:数据加载器构建 data_loaders模型并行化 distributed优化器 build_optimizerbuild_runner构建以及训练循环启动runner.run

2.1 **数据加载器构建:输入: dataset[0](VADCustomNuScenesDataset)经过 函数: build_dataloader(projects/mmdet3d_plugin/datasets/builder.py)输出: data_loaders[0],一个 PyTorch DataLoader 对象,生成批次数据

2.2 模型并行化 : 将模型放到 GPU 上,支持分布式或单机多卡训练

2.3 优化器和 Runner 构建: 优化器根据 cfg.optimizer(如 AdamW)构建; Runner: 默认 EpochBasedRunner(mmcv.runner),但可能被 cfg.runner 覆盖为 EpochBasedRunner_video

2.4 训练循环启动: 输入: data_loaderscfg.workflow(通常为 [(‘train’, 1)])开始按 epoch 迭代训练

3、mmdet_train.py(runner.run(data_loaders, cfg.workflow)) → SSR/projects/mmdet3d_plugin/SSR/runner/epoch_based_runner.pymodel.train_step) → SSR.py (SSR.forward(return_loss=True, **data_list[-1]))
self.model 是 SSR 的实例,train_step训练的核心步骤
【按理来说,train_step 应该在 SSR.py 中,但是 SSR.py 却没有定义 。原因如下:SSR 继承自 MVXTwoStageDetector(mmdet3d/models/detectors/mvx_two_stage.py)。
MVXTwoStageDetector 又继承自 Base3DDetector(mmdet3d/models/detectors/base.py)。
Base3DDetector 中,定义了一个通用的 train_step 方法。】
train_step 调用了 self(data),即 SSR.forward(return_loss=True, **data)
SSR
重写了 forward 方法**,根据 return_loss=True 调用 forward_train

一、数据预处理

1. 数据加载入口
# 从 /tools/train.py 开始:
datasets = [build_dataset(cfg.data.train)]
2. 数据集构建
# mmdetection3d/mmdet3d/datasets/builder.py
# 调用builder.py中注册的数据集构建器
def build_dataset(cfg, default_args=None):
    # 根据配置构建数据集
    dataset = build_from_cfg(cfg, DATASETS, default_args)  # 通过注册器找到VADCustomNuScenesDataset
    return dataset
3、数据集类实例化
# 通过注册器创建  VADCustomNuScenesDataset 实例
@DATASETS.register_module()
class VADCustomNuScenesDataset(NuScenesDataset):
    def __init__(self, queue_length=4, bev_size=(200, 200), ...):
        # 初始化数据集参数
4、数据加载器构建
# SSR/projects/mmdet3d_plugin/datasets/builder.py
def build_dataloader(dataset,
                    samples_per_gpu,
                    workers_per_gpu,
                    num_gpus=1,
                    dist=True,
                    shuffle=True,
                    **kwargs):
    # 构建数据加载器
    # 1. 构建采样器
    sampler = build_sampler(...)
    
    # 2. 创建DataLoader
    data_loader = DataLoader(
        dataset,
        batch_size=batch_size,
        sampler=sampler,
        num_workers=num_workers,
        collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),
        **kwargs)

数据加载过程中,会调用 VADCustomNuScenesDatasetprepare_train_data 方法

5、数据预处理流水线

/projects/mmdet3d_plugin/datasets/pipelines/ 下的处理步骤

# 图像加载和处理 (transform_3d.py)
@PIPELINES.register_module()
class PadMultiViewImage:
    """填充多视角图像"""
    def _pad_img(self, results):
        # 根据size或size_divisor填充图像
        
@PIPELINES.register_module()
class NormalizeMultiviewImage:
    """归一化多视角图像"""
    def __call__(self, results):
        # 对每张图像进行归一化处理
        
@PIPELINES.register_module() 
class PhotoMetricDistortionMultiViewImage:
    """图像光度扭曲增强"""
# 格式化处理 (formating.py)
@PIPELINES.register_module()
class CustomDefaultFormatBundle3D:
    """格式化各种数据为模型可用的张量格式"""
    def __call__(self, results):
        # 处理ego信息
        if self.with_ego:
            results['ego_his_trajs'] = DC(to_tensor(...))
            results['ego_fut_trajs'] = DC(to_tensor(...))
            results['ego_fut_cmd'] = DC(to_tensor(...))

numpy数组转换为PyTorch张量
使用DataContainer封装数据,便于分布式训练
统一数据格式,确保模型输入的一致性

# 点云处理 (loading.py)
@PIPELINES.register_module()
class CustomLoadPointsFromFile:
    """加载点云数据"""
    
@PIPELINES.register_module()
class CustomLoadPointsFromMultiSweeps:
    """加载多帧点云数据"""

加载原始点云数据
处理多帧点云数据的融合
进行点云预处理(如降采样、坐标转换等)

# 数据收集
@PIPELINES.register_module()
class CustomCollect3D:
    """收集并组织最终的训练数据"""
    def __call__(self, results):
        data = {}
        
        # 1. 收集图像元信息
        for key in self.meta_keys:
            data['img_metas'] = DC(img_metas, cpu_only=True)
            
        # 2. 收集训练所需的所有数据
        for key in self.keys:
            data[key] = results[key]

整合所有预处理后的数据
按照模型需要的格式组织数据
区分CPUGPU数据

最终的数据

完整调用链路:

  1. /tools/train.py 调用 mmdetection3dbuild_dataset
  2. mmdetection3dbuild_dataset 通过注册器找
    VADCustomNuScenesDataset
  3. 创建 VADCustomNuScenesDataset 实例
  4. 调用 SSR 中的 build_dataloader 构建数据加载器
  5. 在数据加载过程中,会调用 VADCustomNuScenesDatasetprepare_train_data 方法
  6. prepare_train_data 中会执行预处理pipeline
  7. prepare_train_data 中执行以下步骤:
    调用 get_data_info 获取原始数据
    使用 pre_pipeline 进行预处理
    执行 pipeline 处理流程处理图像等数据
    调用 vectormap_pipeline 处理地图数据
    处理历史帧数据合并
{
    # 1. 图像元信息
    "img_metas": DataContainer {
        "filename": [6个相机图像路径],  # 6个相机视角
        "ori_shape": [360, 640, 3],     # 原始图像尺寸
        "img_shape": [384, 640, 3],     # 处理后图像尺寸
        "lidar2img": [64x4矩阵],      # LiDAR到图像的转换矩阵
        "pad_shape": [384, 640, 3],     # padding后尺寸
        "scale_factor": 1.0,            # 缩放因子
        "box_mode_3d": 0,               # 3D框模式
        "img_norm_cfg": {               # 图像归一化配置
            "mean": [123.675, 116.28, 103.53],
            "std": [58.395, 57.12, 57.375],
            "to_rgb": true
        }
    },

    # 2. 3D检测框标注
    "gt_bboxes_3d": DataContainer {
        LiDARInstance3DBoxes,           # LiDAR坐标系下的3D框
        box_dim: 9                      # 每个框9个参数(x,y,z,w,l,h,θ,vx,vy)
    },
    
    # 3. 标签
    "gt_labels_3d": DataContainer {     # 3D目标类别标签
        torch.Tensor                    # 类别索引
    },

    # 4. 图像数据
    "img": DataContainer {              # 多视角图像数据
        torch.Tensor                    # shape: [B,6,3,384,640]
    },

    # 5. Ego车辆信息
    "ego_his_trajs": DataContainer,     # 历史轨迹
    "ego_fut_trajs": DataContainer,     # 未来轨迹
    "ego_fut_masks": DataContainer,     # 未来轨迹mask
    "ego_fut_cmd": DataContainer,       # 导航指令
    "ego_lcf_feat": DataContainer,      # 局部特征

    # 6. 地图信息
    "map_gt_labels_3d": DataContainer,  # 地图元素标签
    "map_gt_bboxes_3d": DataContainer { # 地图元素框
        LiDARInstanceLines {            # 实例化的线段
            "patch_size": [60.0, 30.0], # 区域大小
            "max_x": 15.0,              # x轴最大范围
            "max_y": 30.0,              # y轴最大范围
            "instance_list": [          # 线段列表
                "LINESTRING (...)",     # 每条线段的坐标序列
                ...
            ]
        }
    }
}

多模态数据: 6个相机视角的图像、LiDAR点云信息、地图矢量信息、车辆状态信息
时序信息: 历史轨迹、未来轨迹、轨迹mask
坐标系转换: LiDAR到图像的变换矩阵、不同坐标系下的3D框表示
地图表示: 矢量化的地图元素(车道线、路口等)、实例化的线段表示
数据增强配置: 图像归一化参数、数据padding和缩放信息

训练流程

tools/train.py -> SSR/apis/train.py -> SSR/apis/mmdet_train.py -> epoch_based_runner.pymodel.train_step) -> SSR.py(注册在 @DETECTORS.register_module() 中)

训练主流程(mmdet_train.py

def custom_train_detector(model, dataset, cfg, distributed=False, ...):
    
    # 1. 构建数据加载器
    data_loaders = [
        build_dataloader(
            ds,
            cfg.data.samples_per_gpu,
            cfg.data.workers_per_gpu,
            len(cfg.gpu_ids),
            dist=distributed,
            ...
        ) for ds in dataset
    ]

    # 2. 模型放到GPU上
    if distributed:
        model = MMDistributedDataParallel(
            model.cuda(),
            device_ids=[torch.cuda.current_device()],
            ...
        )
    
    # 3. 构建优化器
    optimizer = build_optimizer(model, cfg.optimizer)

    # 4. 构建runner控制训练流程
    runner = build_runner(
        cfg.runner,		# 这里cfg.runner中指定了runner类型为EpochBasedRunner
        default_args=dict(
            model=model,	# 传入SSR模型实例
            optimizer=optimizer,
            work_dir=cfg.work_dir,
            logger=logger,
            meta=meta
        ))

    # 5. 注册训练相关的hooks
    runner.register_training_hooks(
        cfg.lr_config,          # 学习率配置
        optimizer_config,       # 优化器配置
        cfg.checkpoint_config,  # 检查点配置
        cfg.log_config,        # 日志配置
        ...
    )

配置文件中: runner = dict(type='EpochBasedRunner', max_epochs=total_epochs) 通过RUNNERS注册器找到我们的自定义Runner

数据经过SSR模型(SSR.py

SSR.forward_train -> SSR.obtain_history_bev -> SSR.obtain_next_bev -> SSR.extract_feat -> SSR.forward_pts_train -> SSRHead.forward -> SSRHead.loss

SSR.forward_train
forward_train
├── obtain_history_bev (获取历史BEV特征)
│   ├── extract_feat (提取图像特征)
│   └── pts_bbox_head (生成BEV特征)  # 实际上是在调用 SSRHead 类的 forward 函数
│
├── obtain_next_bev (获取未来BEV特征)
│   ├── extract_feat
│   └── pts_bbox_head
│
├── extract_feat (提取当前帧特征)
│   ├──extract_img_feat
│
└── forward_pts_train (计算损失)
    └── pts_bbox_head (处理当前帧)  # 实际上是在调用 SSRHead 类的 forward 函数
SSR_head.py
pts_bbox_head (SSRHead)
├── transformer.get_bev_features   # (生成BEV特征)
├── tokenlearner 			#  (提取关键tokens)
├── latent_decoder 			# (场景理解)
├── way_decoder 			# (轨迹规划)
└── loss 					# (损失计算)
SSR_transformer.py
get_bev_features
├── encoder 					# (特征编码)
│   └── spatial_cross_attention
├── decoder 					# (特征解码)
│   └── temporal_self_attention
└── positional_encoding 		# (位置编码)
tokenlearner.py
TokenLearnerV11
├── MlpBlock 		# (特征变换)
└── TokenFuser 		# (特征融合)

训练日志

这里加载配置文件以及训练信息

配置文件 : 涵盖了数据集、数据处理流程、模型结构、优化器等信息。

全局参数:点云范围和类别

point_cloud_range = [-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]
class_names = [
    'car', 'truck', 'construction_vehicle', 'bus', 'trailer', 'barrier',
    'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
]

点云范围point_cloud_range 定义了 3D 空间的有效范围(单位:米),格式为 [x_min, y_min, z_min, x_max, y_max, z_max]。这里是 X: [-15, 15], Y: [-30, 30], Z: [-2, 2],表示模型关注的空间范围。

数据处理流程(Pipelines)

训练流程(train_pipeline
train_pipeline = [
    dict(type='LoadMultiViewImageFromFiles', to_float32=True),
    dict(type='PhotoMetricDistortionMultiViewImage'),
    dict(
        type='LoadAnnotations3D',
        with_bbox_3d=True,
        with_label_3d=True,
        with_attr_label=True),
    dict(
        type='CustomObjectRangeFilter',
        point_cloud_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]),
    dict(
        type='CustomObjectNameFilter',
        classes=[
            'car', 'truck', 'construction_vehicle', 'bus', 'trailer',
            'barrier', 'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
        ]),
    dict(
        type='NormalizeMultiviewImage',
        mean=[123.675, 116.28, 103.53],
        std=[58.395, 57.12, 57.375],
        to_rgb=True),
    dict(type='RandomScaleImageMultiViewImage', scales=[0.4]),
    dict(type='PadMultiViewImage', size_divisor=32),
    dict(
        type='CustomDefaultFormatBundle3D',
        class_names=[
            'car', 'truck', 'construction_vehicle', 'bus', 'trailer',
            'barrier', 'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
        ],
        with_ego=True),
    dict(
        type='CustomCollect3D',
        keys=[
            'gt_bboxes_3d', 'gt_labels_3d', 'img', 'ego_his_trajs',
            'ego_fut_trajs', 'ego_fut_masks', 'ego_fut_cmd', 'ego_lcf_feat',
            'gt_attr_labels'
        ])
]

加载多视角图像: LoadMultiViewImageFromFiles 从文件中加载多视角相机图像,转换为 32 位浮点数。
图像增强(光度畸变): PhotoMetricDistortionMultiViewImage 应用数据增强(如亮度、对比度调整)。
加载 3D 标注: LoadAnnotations3D 加载 3D 边界框标签属性
范围过滤(基于点云范围): CustomObjectRangeFilter 过滤超出指定范围的目标。
类别过滤(基于类别范围): CustomObjectNameFilter 保留指定类别的目标。
图像归一化: NormalizeMultiviewImage 按均值和标准差归一化图像(RGB 通道)。
随机缩放被(: RandomScaleImageMultiViewImage 随机缩放图像,比例为 0.4
填充(保证尺寸是32的倍数): PadMultiViewImage 填充图像,使尺寸能被 32 整除
格式化: CustomDefaultFormatBundle3D数据格式化为模型输入,包含自车(ego)信息
收集数据: CustomCollect3D 收集指定**键(key)**的数据(如 3D 边界框、图像、自车轨迹等)。

测试流程(test_pipeline

测试流程增加了点云加载多尺度增强,适用于推理阶段:

test_pipeline = [
    dict(type='LoadMultiViewImageFromFiles', to_float32=True),
    dict(type='LoadPointsFromFile', coord_type='LIDAR', ...),
    ...
    dict(type='MultiScaleFlipAug3D', img_scale=(1600, 900), ...)
]

加载点云: LoadPointsFromFile 加载激光雷达点云数据(但是use_lidar=False ??)。
多尺度增强: MultiScaleFlipAug3D 在测试时使用多尺度输入(图像尺寸 1600x900)。

评估流程eval_pipeline
eval_pipeline = [
    dict(type='LoadPointsFromFile', ...),
    dict(type='LoadPointsFromMultiSweeps', sweeps_num=10, ...),
    ...
]

多帧点云: LoadPointsFromMultiSweeps 加载 10 帧的点云数据,用于评估。

数据配置(data)

data = dict(
    samples_per_gpu=1,
    workers_per_gpu=4,
    train=dict(...),
    val=dict(...),
    test=dict(...),
    shuffler_sampler=dict(type='DistributedGroupSampler'),
    nonshuffler_sampler=dict(type='DistributedSampler'))

批大小: samples_per_gpu=1 表示每个 GPU 处理 1 个样本。
工作线程: workers_per_gpu=4 表示每个 GPU 使用 4 个线程加载数据。
训练/验证/测试集: 分别定义了 trainvaltest 数据集的参数,包括路径、管道、模态等。
采样器: DistributedGroupSamplerDistributedSampler 用于分布式训练的数据采样。

小结

数据处理流程(train_pipelinetest_pipelineeval_pipeline),Pipelines 是全局模板,而data 是具体实例化。
data 是具体任务的“实例化”,它不仅指定了数据处理流程(通过引用 pipeline),还包括数据集的具体信息:数据集类型(type='VADCustomNuScenesDataset')数据根目录 (data_root)标注文件路径 (ann_file)其他参数 (如 classes、modality、test_mode)
data字典中的pipeline会覆盖之前定义的pipeline

中间配置参数(evaluation、checkpoint_config、log_config等)

评估配置(evaluation)
evaluation = dict(
    interval=24,
    pipeline=[...]
)

评估间隔:interval=24表示每 24 个 epoch 进行一次评估。
评估流程: pipeline 定义了评估时的数据处理流程,主要加载点云数据(LoadPointsFromFileLoadPointsFromMultiSweeps),格式化后收集点云(points)。这与 eval_pipeline 一致,用于计算指标

检查点配置(checkpoint_config)
checkpoint_config = dict(interval=1, max_keep_ckpts=12)

保存间隔: interval=1 表示每 1 epoch 保存一次模型检查点。
最大保留数量: max_keep_ckpts=12 表示最多保留 12 个检查点,旧的会被覆盖。

日志配置(log_config)
log_config = dict(
    interval=100,
    hooks=[dict(type='TextLoggerHook'), dict(type='TensorboardLoggerHook')]
)

日志间隔: interval=100 表示每 100 次迭代记录一次日志。
日志钩子:
TextLoggerHook: 将日志输出到文本文件
TensorboardLoggerHook: 将日志输出到 TensorBoard 可视化工具。

分布式参数(dist_params)
dist_params = dict(backend='nccl')

分布式后端: backend='nccl'表示使用 NVIDIA NCCL 库进行分布式通信,适用于多 GPU 训练

日志级别和工作目录
log_level = 'INFO'
work_dir = './save/outputs/'

日志级别: INFO 表示记录信息级别的日志。
工作目录: work_dir 指定了输出文件(日志检查点等)的保存路径。

加载和恢复
load_from = None
resume_from = None

加载预训练模型: load_from=None 表示不从预训练模型加载
恢复训练: resume_from=None 表示不从已有检查点恢复训练

工作流程(workflow)
workflow = [('train', 1)]

训练流程: 表示只执行训练阶段(train),循环 1 次。通常可以包括 val 或 test,但这里仅训练。

插件(plugin)和体素大小
plugin = True
plugin_dir = 'projects/mmdet3d_plugin/'
voxel_size = [0.15, 0.15, 4]

插件支持: plugin=True 表示启用自定义插件,路径为 plugin_dir
体素大小: voxel_size定义了点云体素化的分辨率(X: 0.15m, Y: 0.15m, Z: 4m)

图像归一化和类别
img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
num_classes = 10
map_classes = ['divider', 'ped_crossing', 'boundary']

图像归一化: 定义了 RGB 图像的均值和标准差,用于标准化。
目标类别数: num_classes=10 对应 10 个检测类别
地图类别: map_classes 定义了 3 个地图元素类别

其他参数
map_num_vec = 100
map_fixed_ptsnum_per_gt_line = 20
map_fixed_ptsnum_per_pred_line = 20
map_eval_use_same_gt_sample_num_flag = True
map_num_classes = 3
_dim_ = 256
_pos_dim_ = 128
_ffn_dim_ = 512
_num_levels_ = 1
bev_h_ = 100
bev_w_ = 100
queue_length = 3
total_epochs = 12

地图参数: 定义了map_num_vec = 100(地图向量的数量,例如,检测 100 条车道线或路沿)、map_fixed_ptsnum_per_gt_line = 20 (每个地图向量(如一条车道线)用 20 个点来表示)、评估设置、map_num_classes=3: 地图元素的类别数量(例如,车道线、分隔带、路沿)。等。
模型维度: _dim_=256(嵌入维度)、_pos_dim_=128(位置编码维度)、_ffn_dim_=512(前馈网络维度)。
BEV 和队列: bev_h_bev_w_ 定义了BEV分辨率(100x100)queue_length=3 表示时间队列长度
总 epoch 数: total_epochs=12 表示训练 12 个 epoch。

模型配置(model)

整体流程:

(多摄像头图像 + 车辆状态(can_bus)) ---->
[ResNet + FPN] 提取各视角图像特征 ---->
[BEVFormer Encoder] 融合成统一的 BEV 特征图 (包含时空信息) ---->
[SSR Head (含 Decoder 和任务头)] 使用query向量BEV 图上进行 “查找”“解码”,并行地进行 3D 物体检测未来轨迹预测高清地图元素(如车道线)的矢量化构建 ---->
同时输出 { (物体检测结果 + 轨迹), (地图元素向量), (规划路径) }

模型采用多种损失函数分类、回归、IoU、点损失、方向损失、规划约束损失等)进行端到端的训练,并使用匈牙利算法进行预测与 GT 之间的匹配。配置中还包括了数据增强、预训练权重加载、特定模块(如 BEV 特征、潜在世界模型)的监督等细节

解码器和任务头:查询向量BEV 特征图进行交互(通过 Transformer 解码器层)。查询向量会“关注”BEV 特征图中的相关区域,并逐渐吸收信息,变得特定化。例如,某个obejct query会逐渐学会代表场景中的一辆特定汽车
经过解码器精炼后的查询向量,会被送入各自的、小的预测网络这就是任务头,通常是几层全连接层)

图像特征提取
img_backbone=dict(
    type='ResNet',
    depth=50,
    num_stages=4,
    out_indices=(3, ),
    frozen_stages=1,
    ...)
img_neck=dict(
    type='FPN',
    in_channels=[2048],
    out_channels=256,
    ...)

输出:只输出一个尺度的特征图 [B, N, 256, H/16, W/16]只处理 conv4 的输出,并通过额外卷积(add_extra_convs=‘on_output’)将通道数降至 256】

核心处理头:pts_bbox_head 【处理转换后的 BEV 特征并输出最终结果(如物体框、轨迹、地图元素)的核心模块】
pts_bbox_head=dict(
    type='SSRHead',
    map_thresh=0.5,
    dis_thresh=0.2,
    pe_normalization=True,
    tot_epoch=12,
    use_traj_lr_warmup=False,
    query_thresh=0.0,
    query_use_fix_pad=False,
    ego_his_encoder=None,
    ego_lcf_feat_idx=None,
    valid_fut_ts=6,
    latent_decoder=dict(...)# MultiheadAttention
    way_decoder=dict(...),   # cross_attn 用于路径或轨迹的生成/解码
    use_pe=True,   # 使用位置编码
    bev_h=100,
    bev_w=100,
    num_query=300,     # 用于物体检测/预测的查询向量数量。
    num_classes=10,	   # 物体检测任务需要区分的类别数量(例如,汽车、行人、自行车等)
    in_channels=256,
    sync_cls_avg_factor=True,  # 在使用多 GPU 进行分布式训练时,同步分类损失的归一化因子,确保不同 GPU 上的损失计算一致。
    with_box_refine=True,   # 在 Transformer 解码器的不同层之间迭代地优化预测的边界框
    as_two_stage=False,
    map_num_vec=100,     # 用于地图构建的查询向量数量(例如,检测 100 条车道线或路沿)
    map_num_classes=3,	# 地图元素的类别数量(例如,车道线、分隔带、路沿)
    map_num_pts_per_vec=20,	 #  每个地图向量(如一条车道线)用 20 个点来表示
    map_num_pts_per_gt_vec=20,	# 真实标注(Ground Truth)的地图向量也用 20 个点表示
    map_query_embed_type='instance_pts',	# 地图查询向量的嵌入方式,基于实例的点。
    map_transform_method='minmax',	# 地图点坐标的归一化方法
    map_gt_shift_pts_pattern='v2',	# 对 GT 地图点进行扰动的方式(用于数据增强或匹配)
    map_dir_interval=1,		# 计算地图向量方向时使用的点间隔
    map_code_size=2,		#  地图向量编码的大小(可能指每个点的 x, y 坐标)
    map_code_weights=[1.0, 1.0, 1.0, 1.0],
    transformer=dict(...),
    bbox_coder=dict(...),
    map_bbox_coder=dict(...),
    positional_encoding=dict(...),
    loss_all=dict(...)  # 各类损失【】
    ...)

SSRHead 是自定义检测头,负责 3D 目标检测和地图预测。
阈值: map_thresh=0.5地图预测的分类阈值。dis_thresh=0.2距离阈值,可能用于匹配。
位置编码: pe_normalization=True 表示对位置编码进行归一化。
训练参数: tot_epoch=12 epoch 一致use_traj_lr_warmup=False 表示不使用轨迹学习率预热
查询参数: query_thresh=0.0query_use_fix_pad=False 控制查询生成。
自车特征 : ego_his_encoder=Noneego_lcf_feat_idx=None 表示不使用特定的自车历史编码器
未来时间步**: valid_fut_ts=6 表示预测 未来 6 帧

BEVFormer编码器
transformer=dict(
    type='SSRPerceptionTransformer',
    map_num_vec=100,
    map_num_pts_per_vec=20,
    rotate_prev_bev=False,
    use_shift=True,
    use_can_bus=True,
    embed_dims=256,
    encoder=dict(
        type='BEVFormerEncoder',
        num_layers=3,
        pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
        num_points_in_pillar=4,
        return_intermediate=False,
        transformerlayers=dict(...)),
    ...)

3层编码器,将多视角图像特征转换为BEV表示

map_bbox_coder (地图边界框编码/解码器)
map_bbox_coder=dict(
    type='MapNMSFreeCoder',
    post_center_range=[-20, -35, -20, -35, 20, 35, 20, 35],
    pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
    max_num=50,
    voxel_size=[0.15, 0.15, 4],
    num_classes=3),

类似 bbox_coder,但用于处理地图元素的输出。参数类似,但 post_center_rangemax_num 不同,适配地图元素的特性
type='MapNMSFreeCoder': 针对地图元素的无 NMS 解码器。

positional_encoding (位置编码)
positional_encoding=dict(
    type='LearnedPositionalEncoding',
    num_feats=128,	# 位置编码的特征维度的一半(总维度是 256,因为通常 x 和 y 方向分开编码再拼接)
    row_num_embed=100,
    col_num_embed=100),	# BEV 特征图的高度和宽度,用于生成对应的可学习编码
Loss Functions (损失函数): 定义了模型训练时使用的各种损失函数及其权重
 loss_cls=dict(
     type='FocalLoss',
     use_sigmoid=True,
     gamma=2.0,
     alpha=0.25,
     loss_weight=2.0),
 loss_bbox=dict(type='L1Loss', loss_weight=0.25),
 loss_traj=dict(type='L1Loss', loss_weight=0.2),
 loss_traj_cls=dict(
     type='FocalLoss',
     use_sigmoid=True,
     gamma=2.0,
     alpha=0.25,
     loss_weight=0.2),
 loss_iou=dict(type='GIoULoss', loss_weight=0.0),
 loss_map_cls=dict(
     type='FocalLoss',
     use_sigmoid=True,
     gamma=2.0,
     alpha=0.25,
     loss_weight=2.0),
 loss_map_bbox=dict(type='L1Loss', loss_weight=0.0),
 loss_map_iou=dict(type='GIoULoss', loss_weight=0.0),
 loss_map_pts=dict(type='PtsL1Loss', loss_weight=1.0),
 loss_map_dir=dict(type='PtsDirCosLoss', loss_weight=0.005),
 loss_plan_reg=dict(type='L1Loss', loss_weight=1.0),
 loss_plan_bound=dict(
     type='PlanMapBoundLoss', loss_weight=1.0, dis_thresh=1.0),
 loss_plan_col=dict(type='PlanCollisionLoss', loss_weight=1.0),
 loss_plan_dir=dict(type='PlanMapDirectionLoss', loss_weight=0.5)),

loss_cls: 物体分类损失,使用 Focal Loss(解决类别不平衡问题)。权重为 2.0
loss_bbox: 物体边界框回归损失,使用 L1 Loss(预测框和 GT 框的 L1 距离)。权重为 0.25
loss_traj: 物体未来轨迹点回归损失,使用 L1 Loss。权重为 0.2。
loss_traj_cls: 物体未来轨迹点的分类损失(可能用于预测轨迹是否存在或轨迹点的有效性),使用 Focal Loss。权重为 0.2。
loss_iou: 物体边界框的 IoU 损失,使用 GIoU Loss权重为 0.0,表示未使用
loss_map_cls: 地图元素分类损失,使用 Focal Loss权重为 2.0
loss_map_bbox: 地图元素边界框回归损失(如果地图元素用框表示),使用 L1 Loss。权重为 0.0,表示未使用
loss_map_iou: 地图元素 IoU 损失,使用 GIoU Loss权重为 0.0,表示未使用
loss_map_pts: 地图元素上的点坐标回归损失,使用 PtsL1Loss(可能是 L1 Loss 的变种)。权重为 1.0
loss_map_dir: 地图元素方向损失,使用 PtsDirCosLoss(基于点计算方向向量,然后用余弦相似度计算损失)。权重为 0.005
loss_plan_reg: 规划轨迹点回归损失,使用 L1 Loss权重为 1.0
loss_plan_bound: 规划轨迹边界约束损失 (PlanMapBoundLoss),惩罚轨迹超出地图边界(如车道线)权重为 1.0,dis_thresh=1.0 是距离阈值。
loss_plan_col: 规划轨迹碰撞损失 (PlanCollisionLoss),惩罚规划轨迹与检测到的物体发生碰撞。权重为 1.0
loss_plan_dir: 规划轨迹方向损失 (PlanMapDirectionLoss),鼓励规划轨迹与地图方向(如车道线方向)保持一致。权重为 0.5

latent_world_model (潜在世界模型)
latent_world_model=dict(
   type='CustomTransformerDecoder',
   num_layers=2,
   return_intermediate=False,
   transformerlayers=dict(
       type='BaseTransformerLayer',
       attn_cfgs=[
           dict(type='MultiheadAttention', embed_dims=256, num_heads=8)
       ],
       feedforward_channels=512,
       operation_order=('self_attn', 'norm', 'ffn', 'norm'))),

接收当前的潜在状态(如 BEV 特征或查询向量)并预测未来的潜在状态

loss_bev (BEV 损失)
loss_bev=dict(type='MSELoss', loss_weight=1.0),

使用均方误差损失 (MSELoss),权重为 1.0。一种辅助损失,用于监督中间生成的 BEV 特征图,让它更接近某个目标(例如,通过某种方式生成的 GT BEV 表示),以帮助模型学习更好的 BEV 表征。

train_cfg (训练配置)
train_cfg=dict(
   pts=dict( # 再次定义了 BEV 空间和体素化参数,用于训练时的 GT 处理和匹配。
       grid_size=[512, 512, 1],
       voxel_size=[0.15, 0.15, 4],
       point_cloud_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
       out_size_factor=4,  # 输出特征图相对于输入图像的尺寸缩小因子
       assigner=dict(  # 预测物体
           type='HungarianAssigner3D',  # 使用匈牙利算法进行最优匹配,将预测的物体框(来自 num_query 个查询)与 GT 物体框进行一对一匹配
           cls_cost=dict(type='FocalLossCost', weight=2.0), # 分类匹配成本,使用 Focal Loss 计算。权重 2.0。
           reg_cost=dict(type='BBox3DL1Cost', weight=0.25),	# 边界框回归匹配成本,使用 3D BBox L1 Cost 计算。权重 0.25
           iou_cost=dict(type='IoUCost', weight=0.0),	# IoU 匹配成本,使用 IoU Cost 计算。权重 0.0 (未使用)
           pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]), # 匹配时使用的坐标范围
       map_assigner=dict( # 预测地图
           type='MapHungarianAssigner3D',
           cls_cost=dict(type='FocalLossCost', weight=2.0),
           reg_cost=dict(
               type='BBoxL1Cost', weight=0.0, box_format='xywh'),
           iou_cost=dict(type='IoUCost', iou_mode='giou', weight=0.0),
           pts_cost=dict(type='OrderedPtsL1Cost', weight=1.0),
           pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]))))

这部分包含了只在训练阶段使用的参数,特别是关于如何将模型预测与真实标签 GT 进行匹配以计算损失(即 Assigners

训练策略(优化器和学习率)

optimizer = dict(
    type='AdamW',
    lr=5e-05,
    paramwise_cfg=dict(custom_keys=dict(img_backbone=dict(lr_mult=0.1))),
    weight_decay=0.01)
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))

# 学习率在训练过程中动态调整(余弦退火 (Cosine Annealing))
lr_config = dict(
    policy='CosineAnnealing',
    warmup='linear',
    warmup_iters=500, # 预热阶段持续 500 次迭代 (iterations/steps)
    warmup_ratio=0.3333333333333333,  # 预热阶段的起始学习率是目标基础学习率 (optimizer['lr']) 的 1/3 
    min_lr_ratio=0.001)  # 当余弦退火结束时(即训练结束时),学习率达到的最小值是基础学习率的 0.001 倍。这确保学习率不会完全降到 0。

# 训练循环的类型和总时长
runner = dict(type='EpochBasedRunner', max_epochs=12)
find_unused_parameters = True   # 查找未使用的参数---PyTorch 的 DDP (分布式训练) 功能会在反向传播(计算梯度)时检查模型中是否存在某些参数在前向传播中没有被用到(因此没有梯度)


'''
钩子 (Hook) 是一种机制,允许在训练/评估过程中的特定时间点(如每个 Epoch 开始/结束,每次迭代之前/之后)插入并执行自定义的操作
'''
custom_hooks = [
    dict(type='CustomSetEpochInfoHook'),
    dict(type='MEGVIIEMAHook', init_updates=10560, priority='NORMAL')  # 指定在训练开始的前 10560 次参数更新 (updates/iterations) 内不计算 EMA,之后才开始
]
gpu_ids = range(0, 4)

paramwise_cfg=dict(custom_keys=dict(img_backbone=dict(lr_mult=0.1))): 参数化配置。这允许为模型的不同部分设置不同的学习率custom_keys: 定义自定义规则。
img_backbone=dict(lr_mult=0.1): 这条规则表示,对于模型中名字包含 “img_backbone” 的参数,其实际学习率将是基础学习率 lr 乘以 lr_mult (0.1)。也就是图像主干网络的学习率只有 0.00005 * 0.1 = 0.000005。这么做的原因是图像主干网络加载了预训练权重,通常只需要进行微调 (fine-tuning),使用较小的学习率可以避免破坏已经学到的知识
weight_decay=0.01: 设置权重衰减系数为 0.01。权重衰减是一种正则化技术,通过在损失函数中添加一个惩罚项来限制模型权重的大小,有助于防止模型过拟合

整体架构流程

流程总结
  1. 输入: [B, N, 3, H, W] → img_backbone → [B, N, 2048, H/32, W/32].
  2. 特征融合: img_neck → [B, N, 256, H/32, W/32].
  3. BEV 转换: transformer.encoder → [B, 256, 100, 100].
  4. 查询生成: latent_decoder 和 way_decoder → [B, 300, 256](目标)+ [B, 100, 256](地图)
  5. 预测: 分支输出分类和回归结果。
  6. 损失: 使用 FocalLossL1Loss 等计算误差。
顶层结构:SSR
SSR(
  (pts_bbox_head): SSRHead(...),
  (img_backbone): ResNet(...),
  (img_neck): FPN(...),  
  (grid_mask): GridMask(),   # 数据增强模块
  (tokenfuser): TokenFuser(...),   # Token融合模块
  (latent_world_model): CustomTransformerDecoder(...),
  (loss_bev): MSELoss()     # BEV 特征的均方误差损失
)
latent_decoder 查询生成与解码
(latent_decoder): CustomTransformerDecoder(
  (layers): ModuleList(
    (0-2): BaseTransformerLayer(
      (attentions): MultiheadAttention(...),
      (ffns): FFN(...),
      (norms): LayerNorm(...)
    )
  )
),
(way_decoder): CustomTransformerDecoder(
  (layers): ModuleList(
    (0): BaseTransformerLayer(...)
  )
)

输入: BEV 特征 [B, 256, 100, 100] + 查询嵌入(query_embedding, map_instance_embedding)。
输出:
目标查询特征 [B, 300, 256]
地图查询特征 [B, 100, 256]

latent_decoder: 3 层 Transformer,生成 300 个目标查询num_query=300)。
way_decoder: 1 层 Transformer,生成 100 个地图查询map_num_vec=100)。

预测分支
(cls_branches): ModuleList(
  (0): Sequential(
    (0-5): Linear → LayerNorm → ReLU,
    (6): Linear(in_features=256, out_features=10)
  )
),
(reg_branches): ModuleList(
  (0): Sequential(
    (0-3): Linear → ReLU,
    (4): Linear(in_features=256, out_features=10)
  )
),
(map_cls_branches): ModuleList(...out_features=3),
(map_reg_branches): ModuleList(...out_features=2)

输入: 查询特征 [B, 300, 256](物体)和 [B, 100, 256](地图)。
输出:
目标预测:[B, 300, 10](分类)+ [B, 300, 10](回归)。
地图预测:[B, 100, 3](分类)+ [B, 100, 20, 2](回归,每向量 20 个点)。

cls_branches: 目标分类,输出 10 类 logits(num_classes=10)。
reg_branches: 目标回归,输出 10 维边界框参数x, y, z, w, l, h, yaw 等)。
map_cls_branches: 地图分类,输出 3 类 logitsmap_num_classes=3)。
map_reg_branches: 地图回归,输出 2 维坐标(x, y)

技术名词解释

提示:这里可以添加技术名词解释

例如:

  • Bert
  • GPT 初代
  • GPT-2
  • GPT-3
  • ChatGPT

技术细节

提示:这里可以添加技术细节

例如:

  • API
  • 支持模型类型

小结

提示:这里可以添加总结

例如:

提供先进的推理,复杂的指令,更多的创造力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值