自动驾驶-第五阶段-工程化 + 作品集打磨 + 面试准备

第五阶段:工程化 + 作品集打磨 + 面试准备

把前四阶段的学习成果转化为拿到Offer的武器

时间规划: 4周(第11-14周)
目标: 产出一个可展示的项目Portfolio + 掌握模型部署技能 + 准备面试


本阶段的核心原则

前四阶段你学会了"做",这个阶段你要学会"说"和"卖"。

做得好 × 说不好 = 没人知道你的能力
做得好 × 说得好 = 拿到面试机会 + 通过面试

Week 1(第11周):整合项目——打造一个完整的Demo

Day 1-2:定义你的核心展示项目

项目名称建议:E2E-Drive-Portfolio

一个从感知到规划的完整端到端自动驾驶系统,包含BEV感知、扩散模型规划、VLA推理、CARLA闭环评测。

项目结构:
E2E-Drive-Portfolio/
├── README.md                    # ← 最重要!面试官第一个看这个
├── docs/
│   ├── architecture.png         # 系统架构图
│   ├── results_table.md         # 评测结果对比表
│   └── demo_video.mp4           # 闭环驾驶演示视频
├── perception/
│   ├── bevformer_inference.py   # BEV感知推理脚本
│   └── visualize_bev.py         # BEV可视化
├── planning/
│   ├── diffusion_planner.py     # 扩散模型规划器
│   ├── pid_controller.py        # PID轨迹跟踪
│   └── evaluate_navsim.py       # NAVSIM评测脚本
├── vla/
│   ├── mini_vla.py              # 轻量VLA模型
│   ├── cot_inference.py         # CoT推理演示
│   └── grpo_training.py         # GRPO微调脚本
├── deployment/
│   ├── export_onnx.py           # ONNX导出
│   ├── trt_benchmark.py         # TensorRT推理测速
│   └── quantize_int8.py         # INT8量化
├── carla_demo/
│   ├── collect_data.py          # 数据采集(第一阶段)
│   ├── closed_loop_test.py      # 闭环测试
│   └── record_video.py          # 录制演示视频
└── configs/
    └── model_configs.yaml       # 统一配置文件

Day 3-4:撰写一个有说服力的README

README是你的"简历首页"——面试官打开你的GitHub,第一眼看的就是它。

# E2E-Drive-Portfolio: 端到端自动驾驶全栈实现

## 项目亮点
- 覆盖 **感知→预测→规划→VLA** 完整技术栈
- 在 **NAVSIM** 基准上实现端到端评测,PDMS达到XX分
- 基于 **DiffusionDrive** 的多模态扩散规划器
- 基于 **Qwen2.5-VL** 的轻量VLA驾驶场景推理
- **CARLA** 闭环驾驶演示,碰撞率低于X%
- **TensorRT** 部署加速,推理延迟从XXms降至XXms

## 技术栈
| 模块 | 方法 | 论文 |
|------|------|------|
| BEV感知 | BEVFormer | ECCV 2022 |
| 端到端规划 | VAD / SparseDrive | ICCV 2023 / 2024 |
| 扩散规划 | DiffusionDrive | CVPR 2025 |
| VLA推理 | ORION + Qwen2.5-VL | ICCV 2025 |
| 闭环评测 | NAVSIM + CARLA | NeurIPS 2024 |
| 模型部署 | TensorRT + ONNX | - |

## 快速开始
[安装、运行说明...]

## 评测结果
[结果表格 + 可视化图片...]

## 演示视频
[CARLA闭环驾驶视频截图/GIF...]

关键原则:面试官的时间不超过30秒。 README的前5行必须让他知道"这个项目做了什么、用了什么技术、效果如何"。把最有说服力的数字(PDMS分数、碰撞率、推理速度)放在最前面。

Day 5:录制CARLA闭环演示视频

"""
record_demo_video.py
在CARLA中运行你的模型并录制一段高质量的演示视频
"""
import carla
import cv2
import numpy as np
import torch

# === 录制配置 ===
VIDEO_WIDTH, VIDEO_HEIGHT = 1280, 720
FPS = 20
OUTPUT_FILE = 'demo_video.mp4'

# 创建视频写入器
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video = cv2.VideoWriter(OUTPUT_FILE, fourcc, FPS, (VIDEO_WIDTH, VIDEO_HEIGHT))

# 在每一帧上叠加信息面板
def create_info_overlay(frame, info):
    """在画面上叠加模型状态信息"""
    overlay = frame.copy()

    # 半透明黑色背景
    cv2.rectangle(overlay, (10, 10), (400, 180), (0, 0, 0), -1)
    frame = cv2.addWeighted(overlay, 0.6, frame, 0.4, 0)

    # 文字信息
    texts = [
        f"Speed: {info['speed']:.1f} m/s",
        f"Steer: {info['steer']:.3f}",
        f"Model: {info['model_name']}",
        f"Inference: {info['inference_ms']:.1f} ms",
        f"Distance: {info['distance']:.0f} m",
        f"Collisions: {info['collisions']}",
    ]
    for i, text in enumerate(texts):
        cv2.putText(frame, text, (20, 35 + i * 25),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)

    return frame

# 在闭环测试循环中:
# frame = create_info_overlay(camera_image, info_dict)
# video.write(frame)

# 结束时:
# video.release()

视频要展示什么?

  1. 正常驾驶场景(直行、转弯、跟车)——证明基础能力
  2. 挑战性场景(急弯、避障、路口)——证明鲁棒性
  3. 画面上要有实时的模型状态信息——证明工程能力
  4. 视频时长控制在1-3分钟——太长面试官不会看完

Week 2(第12周):模型部署工程化

Day 6-7:ONNX导出与TensorRT加速

什么是ONNX?什么是TensorRT?

PyTorch模型(研究用)
    ↓ torch.onnx.export()
ONNX模型(跨平台中间格式)
    ↓ trtexec
TensorRT引擎(车端部署用)

PyTorch → 灵活但慢(研究阶段用)
ONNX → 标准化但一般快(跨框架交换用)
TensorRT → 极快但不灵活(部署阶段用)

概念解释:

ONNX(Open Neural Network Exchange): 一种开放的模型格式标准。就像PDF是文档的通用格式一样,ONNX是神经网络的通用格式。任何框架训练的模型都可以导出为ONNX,然后在任何支持ONNX的平台上运行。

TensorRT: NVIDIA专门为GPU推理优化的加速引擎。它做的事情包括:算子融合(把多个小操作合并成一个大操作)、精度优化(FP32→FP16→INT8)、内存优化(减少显存占用)。通常能把推理速度提升3-10倍。

完整的导出和加速流程

"""
deployment_pipeline.py
完整的模型部署Pipeline:PyTorch → ONNX → TensorRT → Benchmark
"""
import torch
import torch.onnx
import time
import numpy as np

# ========== Step 1: PyTorch → ONNX ==========
def export_to_onnx(model, save_path="model.onnx"):
    """导出PyTorch模型为ONNX格式"""
    model.eval()

    # 创建dummy输入(要和实际推理时的输入shape一致)
    dummy_input = torch.randn(1, 3, 300, 400).cuda()  # 假设输入是图像

    torch.onnx.export(
        model,
        dummy_input,
        save_path,
        input_names=['image'],
        output_names=['trajectory'],
        dynamic_axes={
            'image': {0: 'batch_size'},      # 支持动态batch
            'trajectory': {0: 'batch_size'}
        },
        opset_version=17,  # ONNX算子版本,越新支持的操作越多
        do_constant_folding=True  # 常量折叠优化
    )
    print(f"ONNX模型已导出到 {save_path}")

    # 验证ONNX模型
    import onnx
    onnx_model = onnx.load(save_path)
    onnx.checker.check_model(onnx_model)
    print("ONNX模型验证通过")

# ========== Step 2: ONNX → TensorRT ==========
# 命令行方式(最简单):
# trtexec --onnx=model.onnx \
#          --saveEngine=model.trt \
#          --fp16 \                    # 半精度加速
#          --workspace=4096 \           # 工作空间4GB
#          --minShapes=image:1x3x300x400 \
#          --optShapes=image:4x3x300x400 \
#          --maxShapes=image:8x3x300x400

# ========== Step 3: TensorRT推理 ==========
def trt_inference(engine_path, input_data):
    """用TensorRT引擎做推理"""
    import tensorrt as trt
    import pycuda.driver as cuda
    import pycuda.autoinit

    # 加载引擎
    logger = trt.Logger(trt.Logger.WARNING)
    with open(engine_path, 'rb') as f:
        engine = trt.Runtime(logger).deserialize_cuda_engine(f.read())

    context = engine.create_execution_context()

    # 分配GPU内存
    input_shape = input_data.shape
    d_input = cuda.mem_alloc(input_data.nbytes)
    output_shape = (input_shape[0], 6, 2)  # 假设输出6个waypoints
    output_data = np.empty(output_shape, dtype=np.float32)
    d_output = cuda.mem_alloc(output_data.nbytes)

    # 拷贝数据到GPU → 推理 → 拷贝结果回CPU
    cuda.memcpy_htod(d_input, input_data)
    context.execute_v2(bindings=[int(d_input), int(d_output)])
    cuda.memcpy_dtoh(output_data, d_output)

    return output_data

# ========== Step 4: Benchmark对比 ==========
def benchmark(model_pytorch, onnx_path, trt_path, input_shape=(1,3,300,400)):
    """对比三种推理方式的速度"""
    dummy = torch.randn(*input_shape).cuda()
    num_runs = 100

    # PyTorch FP32
    model_pytorch.eval().cuda()
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(num_runs):
        with torch.no_grad():
            _ = model_pytorch(dummy)
        torch.cuda.synchronize()
    pytorch_time = (time.time() - start) / num_runs * 1000

    # PyTorch FP16
    model_fp16 = model_pytorch.half()
    dummy_fp16 = dummy.half()
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(num_runs):
        with torch.no_grad():
            _ = model_fp16(dummy_fp16)
        torch.cuda.synchronize()
    pytorch_fp16_time = (time.time() - start) / num_runs * 1000

    # TensorRT FP16
    dummy_np = dummy.cpu().numpy().astype(np.float32)
    start = time.time()
    for _ in range(num_runs):
        _ = trt_inference(trt_path, dummy_np)
    trt_time = (time.time() - start) / num_runs * 1000

    print(f"{'方式':<20} {'延迟(ms)':<15} {'FPS':<10}")
    print(f"{'='*45}")
    print(f"{'PyTorch FP32':<20} {pytorch_time:<15.2f} {1000/pytorch_time:<10.1f}")
    print(f"{'PyTorch FP16':<20} {pytorch_fp16_time:<15.2f} {1000/pytorch_fp16_time:<10.1f}")
    print(f"{'TensorRT FP16':<20} {trt_time:<15.2f} {1000/trt_time:<10.1f}")

面试中一定会问的: "你的模型推理延迟是多少?能达到实时吗?"

你需要准备的回答模板:
"我的规划模型在RTX 3090上,PyTorch FP32推理约15ms,转TensorRT FP16后降到约3ms,远低于实时要求的100ms(10Hz)。BEV编码器的推理时间较长,约50ms,但通过图像分辨率降采样和特征缓存可以优化到30ms以内。"

Day 8-9:INT8量化

什么是量化?为什么要做?

FP32(32位浮点):精度最高,但最慢最费显存
  每个参数占4字节 → 1亿参数 = 400MB

FP16(16位浮点):精度略降,速度翻倍
  每个参数占2字节 → 1亿参数 = 200MB

INT8(8位整数):精度再降,速度再翻倍
  每个参数占1字节 → 1亿参数 = 100MB

车端芯片(如Orin-X)的INT8算力远高于FP16/FP32
所以量化到INT8是车端部署的标准操作

概念解释:量化的精度损失

FP32用32位表示一个数,能表示非常精确的小数(如3.14159265)。INT8只用8位,只能表示-128到127之间的整数。量化就是把FP32映射到INT8的过程——需要找到一个合适的"缩放因子"(scale),使得映射后的误差最小。

好的量化方法能把精度损失控制在1%以内,但推理速度提升2-4倍。这就是车企部署时必须掌握的技能。

"""
quantize_int8.py
PTQ(训练后量化)示例
"""
import torch
from torch.quantization import quantize_dynamic, prepare, convert

# 方法1:动态量化(最简单,适合MLP类层)
model_int8 = quantize_dynamic(
    model,
    {torch.nn.Linear},  # 只量化Linear层
    dtype=torch.qint8
)

# 方法2:TensorRT INT8量化(需要校准数据集)
# trtexec --onnx=model.onnx \
#          --saveEngine=model_int8.trt \
#          --int8 \
#          --calib=calibration_cache.bin  # 校准缓存

# 校准数据集:用100-500张真实数据跑一遍,
# TensorRT会统计每一层的数值分布,找到最佳的量化参数

Week 3(第13周):面试准备——技术深度 + 系统思维

Day 10-11:必须准备的50个面试问题

第一类:基础概念(10题)

Q1: BEV感知的核心思想是什么?BEVFormer是怎么实现的?
A: BEV把多视角图像统一到俯视图空间。BEVFormer用空间交叉注意力——
   在BEV网格上预设查询点,通过相机内外参投影到各摄像头像素位置取特征,
   再用时序自注意力融合历史帧信息。

Q2: 为什么VAD比UniAD更高效?
A: VAD用向量化表示替代栅格化表示——车道线用折线段而非像素图表示,
   信息密度更高、计算量更小。推理速度提升2.5倍,碰撞率降低29%。

Q3: 一段式和两段式端到端的区别?
A: 两段式先训练感知再训练规划(如UniAD/VAD),感知误差无法被规划纠正。
   一段式所有模块同时训练(如SparseDrive),规划的loss可以反传到感知,
   让感知学会关注对规划有用的信息。

Q4: 什么是Mode Collapse?扩散模型怎么解决?
A: MSE回归只能输出一条轨迹,遇到多种合理选择时会输出"平均轨迹"
   (不合理)。扩散模型能生成多条候选轨迹,每条对应一种驾驶模式,
   再用碰撞检测等约束选最优的。

Q5: VLA和传统端到端的根本区别是什么?
A: 传统端到端只能模仿训练数据中的模式,遇到没见过的场景就懵了。
   VLA引入了大语言模型的"世界知识"和推理能力,能像人一样分析
   "前方有施工→应该绕行"——这种推理不需要在训练数据中见过施工场景。

第二类:技术深度(15题)

Q6: BEVFormer的空间交叉注意力和标准交叉注意力有什么区别?
Q7: Deformable Attention的核心创新是什么?为什么用它?
Q8: nuScenes的标注频率是多少?为什么选2Hz而不是更高?
Q9: NAVSIM的PDMS指标里EP为什么是乘法关系而不是加法?
Q10: DiffusionDrive的截断扩散策略为什么只需要2步去噪?
Q11: DDPM和Flow Matching的核心区别是什么?
Q12: GRPO和PPO的区别?GRPO的优势在哪?
Q13: VLA中的CoT SFT和普通SFT的区别?
Q14: QT-Former是怎么压缩多帧视觉信息的?
Q15: 为什么ORION用VAE而不是直接让VLM输出坐标?
Q16: Transformer的自注意力计算复杂度是多少?怎么优化?
Q17: ResNet的残差连接为什么有效?
Q18: Adam优化器和SGD的区别?什么时候用什么?
Q19: Batch Normalization和Layer Normalization的区别?
Q20: 什么是梯度消失/爆炸?怎么解决?

第三类:工程化(10题)

Q21: PyTorch模型转ONNX时常见的坑有哪些?
A: 动态shape支持(需要设dynamic_axes)、不支持的算子(需要自定义)、
   控制流(if/else不能直接导出,需要用torch.jit.script)。

Q22: TensorRT的FP16加速原理是什么?
A: 把FP32的权重和激活值截断到FP16,减少一半的内存带宽和计算量。
   现代GPU的FP16算力是FP32的2倍(Tensor Core),所以速度翻倍。

Q23: INT8量化的校准过程是什么?
A: 用一批有代表性的真实数据(100-500张),跑一遍前向传播,
   统计每一层的激活值分布(min/max或直方图),
   找到最优的scale和zero_point使量化误差最小。

Q24: 多GPU训练时,数据并行和模型并行的区别?
Q25: 如何处理训练中的OOM(显存溢出)问题?
Q26: 混合精度训练(AMP)的原理和使用方法?
Q27: 什么是梯度累积?什么时候用?
Q28: 分布式训练中的AllReduce操作是什么?
Q29: Docker在自动驾驶开发中的作用?
Q30: CI/CD在自动驾驶算法迭代中怎么用?

第四类:系统设计(10题)

Q31: 如果让你从零设计一个端到端自动驾驶系统,你会怎么做?
Q32: 你怎么看小鹏VLA和华为WA路线的区别?
Q33: 端到端系统的安全性怎么保证?
Q34: 数据闭环是什么?怎么设计?
Q35: 如果模型在某个corner case上表现差,你怎么排查和解决?
Q36: 训练数据中的标注噪声怎么处理?
Q37: 如何评估一个自动驾驶系统的泛化能力?
Q38: 模型上车后出了问题怎么回滚?
Q39: 仿真测试和实车测试的关系是什么?
Q40: 你对自动驾驶的未来技术方向怎么看?

第五类:你的项目(10题)

Q41: 介绍一下你的项目?(必问,准备2分钟版和5分钟版)
Q42: 你在项目中遇到的最大挑战是什么?怎么解决的?
Q43: 你的模型的推理延迟是多少?怎么优化的?
Q44: 你在NAVSIM上的PDMS是多少?和SOTA差多少?
Q45: 如果给你更多时间,你会怎么改进这个项目?
Q46: 你为什么选择DiffusionDrive而不是其他方法?
Q47: 你的VLA用的什么VLM?为什么选它?
Q48: 你在训练中遇到过loss不降的情况吗?怎么排查的?
Q49: 你做过哪些消融实验?结论是什么?
Q50: 你觉得现在端到端自动驾驶最大的瓶颈是什么?

Day 12-13:准备技术路线图讲解

面试中经常被问"你怎么理解自动驾驶的技术演进"。准备一个清晰的叙述:

自动驾驶技术演进路线(面试时画在白板上):

2020-2022: 模块化时代
  感知(PointPillars/CenterPoint) → 预测(VectorNet) → 规划(Rule-based)
  问题:模块间信息损失、累积误差

2022-2023: 端到端1.0——两段式
  UniAD(CVPR23 Best Paper) → VAD(ICCV23)
  创新:统一框架、Query-based架构
  问题:BEV Dense计算量大、两段训练不能联合优化

2024: 端到端2.0——一段式
  SparseDrive → DiffusionDrive(CVPR25)
  创新:稀疏表示、扩散模型多模态规划
  问题:纯模仿学习有mode collapse、缺乏推理能力

2025-2026: VLA时代
  ORION(ICCV25) → 小鹏VLA2.0 → 理想MindVLA
  创新:VLM推理能力 + 强化学习 + 世界模型
  当前挑战:车端算力限制、推理延迟、安全验证

未来方向:
  ① 世界模型(生成式仿真)替代传统仿真器
  ② 强化学习(GRPO/PPO)超越模仿学习上限
  ③ 端云协同(云端大模型+车端小模型)

Week 4(第14周):简历打磨 + 模拟面试 + 投递

Day 14:简历撰写

简历中的项目描述模板

端到端自动驾驶系统全栈实现    2026.01 - 2026.03
────────────────────────────────────────────────
技术栈:PyTorch / BEVFormer / VAD / DiffusionDrive / Qwen2.5-VL / 
        TensorRT / CARLA / NAVSIM / nuScenes

• 复现BEVFormer多视角BEV感知算法,在nuScenes mini数据集上完成
  3D目标检测推理和可视化,深入理解空间交叉注意力机制

• 复现VAD端到端自动驾驶框架,实现向量化场景表示和waypoints规划,
  在nuScenes上完成开环评测

• 基于DiffusionDrive截断扩散策略,实现多模态轨迹规划器,
  在NAVSIM基准上完成端到端评测

• 构建轻量VLA系统,基于Qwen2.5-VL-3B实现驾驶场景CoT推理
  和轨迹预测,探索GRPO强化学习微调

• 完成CARLA仿真器闭环驾驶测试,录制演示视频,
  实现TensorRT FP16加速(推理延迟降低X倍)

关键原则:用数字说话。 "完成了端到端系统"不如"在NAVSIM上PDMS达到XX分"。"做了模型加速"不如"TensorRT FP16将推理延迟从15ms降至3ms"。面试官需要量化的信息来判断你的水平。

Day 15-17:投递与面试

目标公司和岗位

第一梯队(算法深度要求高):
├── 小鹏汽车 — 端到端算法 / VLA算法
├── 华为智驾 — 端到端感知 / 规划算法
├── 理想汽车 — VLA算法 / 世界模型
├── 地平线 — 端到端算法 / 模型部署
└── 蔚来 — 感知算法 / 规划算法

第二梯队(工程化要求高,适合你的背景):
├── 比亚迪 — 智驾算法工程师
├── 元戎启行 — 端到端算法 / 数据闭环
├── 商汤/Momenta — 感知算法 / 部署
├── 德赛西威 — 智驾方案工程师
└── 纵目科技 — 泊车算法

投递渠道:
├── 公司官网(最正式)
├── Boss直聘(最快得到回复)
├── 内推(成功率最高——去自动驾驶之心社区找内推码)
└── 猎头(适合社招高级岗位)

Day 18-19:模拟面试

找一个同样在学自动驾驶的朋友,互相做模拟面试。如果找不到,对着镜子讲(或者录视频回看)。

2分钟自我介绍模板:

"面试官好,我是[名字],有10年Java/Python开发经验,过去也做过机器学习和计算机视觉的项目。最近3个月系统学习了自动驾驶端到端技术栈,从CARLA仿真入手,依次复现了BEVFormer、VAD、SparseDrive和DiffusionDrive,在NAVSIM基准上做了端到端评测。我还研究了VLA方向,跑通了小米的ORION框架,自己实现了一个基于Qwen2.5-VL的轻量VLA。工程化方面,我完成了TensorRT模型加速和INT8量化的实践。我的核心优势是既理解算法原理,又有很强的工程落地能力,这是我10年开发经验带来的。我对贵公司的[具体岗位]非常感兴趣,因为[具体原因]。"

Day 20-21:持续学习计划

拿到Offer不是终点,入职后还需要持续跟进前沿。建议关注:

必关注的信息源:
├── 论文:arXiv cs.CV + cs.RO 每周浏览
├── GitHub:OpenDriveLab、hustvl、autonomousvision
├── 社区:自动驾驶之心(知识星球/公众号)
├── 会议:CVPR / ICCV / NeurIPS / CoRL / ICRA
└── 公司技术博客:小鹏/理想/地平线的技术文章

需要持续深入的方向:
├── 世界模型(World Model)— 用于仿真和数据生成
├── 3DGS/NeRF — 用于场景重建和仿真渲染
├── 强化学习(PPO/GRPO)— 超越模仿学习的上限
├── 模型压缩与蒸馏 — 大模型上车的关键技术
└── 安全验证与形式化方法 — 自动驾驶量产的最后一关

恭喜!你已经完成了14周的完整学习路径

Week 1:     CARLA环境搭建 + 第一辆自动驾驶车
Week 2:     数据采集 + 模仿学习 + 闭环测试
Week 3-4:   BEV感知(BEVFormer)+ nuScenes
Week 5-6:   端到端(VAD)+ waypoints规划
Week 7-8:   一段式端到端(SparseDrive)+ 扩散模型(DiffusionDrive)
Week 9:     NAVSIM评测基准
Week 10:    VLA(ORION + Qwen2.5-VL + GRPO)
Week 11:    项目整合 + 演示视频
Week 12:    模型部署(ONNX + TensorRT + INT8)
Week 13:    面试题准备 + 技术路线图
Week 14:    简历 + 投递 + 模拟面试

你现在的能力:
✅ 理解从感知到规划的完整端到端技术栈
✅ 能复现BEVFormer/VAD/SparseDrive/DiffusionDrive等前沿算法
✅ 掌握VLA(当前最热门方向)的核心原理和实践
✅ 有NAVSIM + CARLA的标准化评测结果
✅ 具备TensorRT模型部署的工程化能力
✅ 有一个完整可展示的GitHub项目Portfolio
✅ 10年工程经验 + 自动驾驶算法理解 = 稀缺人才

去拿你的Offer吧。

为者常成,行者常至