简介:一份聚焦自动驾驶环境感知技术的教学课件,系统梳理感知系统在车辆智能决策中的基础地位,并围绕道路、静态物体、动态物体三类感知对象,介绍激光测距仪、视频摄像头、车载雷达等传感器融合方案,以及卷积神经网络在图像识别与目标检测中的应用。内容还涉及近目标优先、大尺度优先、动目标优先等感知原则,并针对恶劣天气、连续视频流处理等现实挑战与未来发展趋势展开讨论,适合自动驾驶初学者、算法工程师及高校相关专业师生用作入门概览或教学辅助。资源为单个演示文稿(pptx)文件,大小约10.06MB,已有294人学习。课件结构完整,包含环境感知概述、传感器原理、网络层级、挑战与展望等模块,便于读者快速建立自动驾驶感知技术的整体框架,并为进一步学习深浅层模型与工程落地打下基础。
1. 自动驾驶感知技术为什么先看数据、再看模型
如果一个自动驾驶团队只给你看模型的 mAP,说明它还处在“能跑 demo”的阶段。真正做感知的人,打开一个“autopilot perception”类的技术分享,第一眼看的往往是传感器布局、标注规范和评测集的划分——这三样决定了一套感知系统的上限,模型只是把上限兑现多少的问题。这篇博文从“感知技术”这个词能拆出的三个层面展开:传感器和数据如何对齐,检测、分割、跟踪任务的分工与融合,以及数据集和仿真工具链怎么帮你把边界踩出来。适合正在搭感知团队、或准备把算法往车上部署的工程师读,内容尽量落到能直接照做的程度。
2. 自动驾驶感知的传感器选型与数据对齐
2.1 三项主流传感器的信息边界与失效模式
感知技术的第一步不是挑最贵的传感器,而是明确“每一类传感器骗你的时候长什么样”。相机有稠密纹理和颜色,但它是被动传感器,逆光、隧道口、夜间无照明时信噪比急剧下降;激光雷达测距准、不受光照影响,但点云稀疏,对反射率低的黑色车辆和雨雾天气会“丢点”;毫米波雷达对速度敏感、穿透性好,但角分辨率差,静止目标容易漏检或误报。实际量产方案里,这三者不是替补关系,而是互补关系:视觉负责语义,激光负责几何,毫米波负责速度和全天候底线。
选型时还需要看安装位置和视野重叠度。常见做法是前向三目或四目相机加一个前向激光雷达,再加四个角毫米波雷达。装完之后要立刻做两件事:外参标定和视场角对齐。很多团队在实车上跑感知,发现目标在相机里有、激光点云里没有,往往不是模型问题,而是激光雷达的上方视场角没把路牌和高架桥包进去。感知工程师拿到车的第一周,就应该画出每个传感器的FOV投影图,确认覆盖盲区在哪。
2.2 坐标系标定与时间同步的最小步骤
多传感器融合的基础是统一坐标系。感知里通常以自车后轴中心为车体坐标系原点,x 轴向前,y 轴向左,z 轴向上。相机和激光雷达各自有独立坐标系,通过外参矩阵把点云或图像投影到车体坐标。标定分静态和动态两类:静态用标定板或标定间,动态用 SLAM 或直接法对配准。小团队没条件做专业标定间,可以用开源工具在场地里跑一组包含直行、转弯、过坡道的轨迹,解算传感器间的相对位姿。
时间同步同样容易被低估。相机曝光时刻和激光雷达扫描时刻如果相差 30 毫秒,在 72 km/h 车速下目标会平移 0.6 米。感知融合前必须先做时间对齐,常见做法是把激光雷达的扫描时间作为主时钟,其他传感器的数据按时间戳插值或取最近邻。
import numpy as np def align_lidar_camera(lidar_ts, cam_ts, cam_data, max_offset_ms=20): """将相机数据向激光雷达时间戳对齐(最近邻)。 lidar_ts: 激光雷达时间戳数组 (N,) cam_ts: 相机时间戳数组 (M,) cam_data: 原始图像或检测结果列表,长度 M 返回 aligned: 与 lidar_ts 对齐后的数据列表 """ lidar_time = np.asarray(lidar_ts) cam_time = np.asarray(cam_ts) aligned = [] for t in lidar_time: idx = np.argmin(np.abs(cam_time - t)) offset = abs(cam_time[idx] - t) * 1000.0 # 转为毫秒 if offset > max_offset_ms: # 超出阈值直接丢弃,避免把错位帧送入融合模块 aligned.append(None) else: aligned.append(cam_data[idx]) return aligned这段代码做的事情很简单:对每个激光雷达时间戳,找到最近的一帧相机数据,并检查时间差是否在 20 毫秒以内。超过阈值返回None,让上层逻辑决定是丢弃还是做运动补偿。参数max_offset_ms不是拍脑袋定的,它需要根据车速和目标的动态性来调,高速场景可以收紧到 10 毫秒,城区拥堵场景可以放宽到 30 毫秒。
2.3 标定误差多大,模型性能稳不住
很多团队在数据集上训练模型效果不错,一到实车就露馅,问题多半出在标定。外参误差 0.2 度看起来很小,但投射到 50 米外就会带来约 17 厘米的位置偏移,而相机与激光雷达融合时,这个偏移直接决定了目标框是“贴得紧”还是“漂在半空”。毫米波雷达输出的目标点存在距离向和方位向的系统性误差,融合之前要先做标定补偿,否则卡尔曼滤波会一直收敛不到稳值。
建议每个自然周做一次静态标定验证,方法是把车停在停车场,采集一段静态数据,检查激光点云投影到图像后是否稳定压在地面车道上。投影结果可以用一张图看,也可以用像素偏移的均值和方差做数值监控。感知代码里对外参做一层抽象,标定更新时不需要改融合逻辑,这能省下大量重复调试时间。
3. 感知任务的分工与部署约束
3.1 检测、分割、跟踪分别解决什么
检测回答“哪里有什么”,分割回答“目标的精确轮廓”,跟踪回答“同一辆车在连续帧里是谁”。三个任务不是并列关系,而是递进关系:检测框不稳定的车辆,跟踪很难保持 ID 一致;分割结果可以反过来优化检测框的边界,让框贴近车头车尾而不是包住整个车身。量产感知系统一般把三者拆成独立模块,各自优化再融合,端到端方案目前还很难满足确定性要求。
检测模型选型上,视觉用 YOLO 系列或 DETR 类的 transformer 方案;激光点云用 PointPillars、CenterPoint 这类基于体素的模型。生产环境中我更倾向 CenterPoint 的思路:把检测建模成点云中心点预测,后处理简单,且在稀疏点云上召回率更稳。分割模型负责给出可行驶区域和车道线,语义分割用轻量级 DeepLab 或 STDC,实例分割一般只在近距离用,中远距离算力消耗太大。
跟踪模块值得单独说。基于规则的跟踪,也就是卡尔曼滤波加匈牙利匹配,依然是量产最可靠的方案,可解释、可调参、不会突然学坏。3D 跟踪常见做法是使用扩展卡尔曼滤波(EKF)维护目标状态,状态量包含 x、y、z、朝向和速度,匹配时用 3D IoU 或中心距离作为代价。车辆近距离遮挡后重新出现时,需要保留一个“游离轨迹”列表,至少 10 帧,避免同一个目标产生两个 ID。
3.2 BEV视角下如何把多传感器融合成一张图
BEV(鸟瞰视角)是当前感知技术里的核心思路:把相机图像和激光点云重新投影到一个以自车为中心、俯视的网格上,再做检测和预测。对比在图像平面上做感知,BEV 的优势在于目标尺度不随距离剧烈变化,而且做轨迹预测天然就是平面坐标,省掉透视逆变换带来的误差。
实现 BEV 融合有两条路。一条是几何方法,把相机图像用逆透视映射(IPM)投影到地面,再把激光雷达点云按高度切层填入同一个网格。另一条是端到端方法,用 transformer 的 cross-attention 直接把图像特征变换到 BEV 空间。工程落地我推荐几何方法起步,它可视化直观、出问题好定位,端到端留给后续迭代。
import numpy as np def lidar_to_bev(points, grid_size=0.1, x_range=(-50, 50), y_range=(-50, 50)): """将激光点云投影到 BEV 栅格,返回 2D 强度图。 points: (N, 3) 点云,列为 x, y, z(已变换到车体坐标) grid_size: 每个栅格的物理边长,单位米 x_range, y_range: 投影范围 """ x_min, x_max = x_range y_min, y_max = y_range nx = int((x_max - x_min) / grid_size) ny = int((y_max - y_min) / grid_size) bev = np.zeros((ny, nx), dtype=np.float32) # 过滤范围外的点,降低无效计算 mask = ( (points[:, 0] >= x_min) & (points[:, 0] < x_max) & (points[:, 1] >= y_min) & (points[:, 1] < y_max) ) pts = points[mask] # 只保留每个栅格中 z 值最高的点,避免地面点遮挡目标 xs = ((pts[:, 0] - x_min) / grid_size).astype(np.int32) ys = ((pts[:, 1] - y_min) / grid_size).astype(np.int32) zs = pts[:, 2] for xi, yi, zi in zip(xs, ys, zs): if zi > bev[yi, xi]: bev[yi, xi] = zi return bev这段代码把每个栅格内 z 值最高的点写到 BEV 图上,等价于保留目标的顶部轮廓。grid_size=0.1意味着 100 米见方的范围会产生 100 万格,分辨率太高会影响存储和推理速度;城区场景用 0.2 米更合理,高速场景可以再降到 0.4。投影完成后,可以用 matplotlib 的imshow快速看一张图,确认车道线、路沿和目标轮廓是否清晰。
3.3 模型推理上车的算力分配
感知模型训练时用多大的显卡都不心疼,部署时就要精打细算。量产域控制器一般需要同时跑 6 到 12 路相机、1 到 2 个激光雷达和多个毫米波雷达,整个感知栈的延迟预算通常在 100 毫秒以内。常见分配方式是:检测模型占 40% 算力,分割模型占 25%,跟踪和融合占 10%,剩下的留给预处理和系统开销。
模型轻量化有几个固定动作:用 INT8 量化替代 FP16,感知模型一般可以接受;把 backbone 从 ResNet 换成 MobileNet 或 EfficientNet-Lite;用 TensorRT 或 ONNX Runtime 做图优化,顺带融合 BatchNorm 层。量化后的模型必须在夜间、雨天、逆光三组场景下重新跑一遍评测集,因为量化对暗光特征的精度损失最大。
4. 自动驾驶数据集与仿真验证怎么配合
4.1 开源数据集的标注格式与适用场景
训练感知模型不能只有自己的路测数据,开源数据集的价值在于提供一个可复现的基线。感知领域公认的三件套是 KITTI、nuScenes 和 Waymo Open Dataset。KITTI 是经典中的经典,适合做 3D 检测和立体匹配验证;nuScenes 带了完整的传感器套件和 40 秒的连续帧,适合做跟踪和多传感器融合;Waymo 数据量最大,场景含高速和密集交通流,适合做大规模训练的底座。
数据集的标注格式决定了你写数据加载器的成本。KITTI 的标注是平文本文件,每行是一个目标框,字段依次是类别、截断度、遮挡等级、2D 框和 3D 框参数;nuScenes 用 JSON,每个 sample 关联六个方向的相机和五个激光雷达,还带 object velocity。工程上建议统一转成中间格式,比如 JSON Lines,每行一个对象,字段固定为timestamp, sensor_id, category, bbox_3d, velocity,这样内部工具链就与上游数据集完全解耦。
import json from pyquaternion import Quaternion from nuscenes import NuScenes def load_one_sample(nusc, sample_token): """从 nuScenes 数据集中加载一个 sample 的关键信息""" sample = nusc.get("sample", sample_token) lidar_data = nusc.get("sample_data", sample["data"]["LIDAR_TOP"]) # 拿到当前帧所有标注的 token ann_tokens = sample["anns"] anns = [nusc.get("sample_annotation", t) for t in ann_tokens] records = [] for ann in anns: # 3D 框中心位置和尺寸(单位:米) translation = ann["translation"] size = ann["size"] # width, length, height rot = Quaternion(ann["rotation"]) records.append({ "category": ann["category_name"], "x": translation[0], "y": translation[1], "z": translation[2], "w": size[0], "l": size[1], "h": size[2], "q": [rot.w, rot.x, rot.y, rot.z] }) return { "timestamp": lidar_data["timestamp"], "lidar_path": lidar_data["filename"], "anns": records }代码里sample["data"]["LIDAR_TOP"]取的是当前帧激光雷达的数据 token,标注全部以车体坐标系为基准。nuScenes 的标注框是水平放置的,没有俯仰和横滚,所以四元数只描述偏航角。转成自己格式时注意单位统一,KITTI 的 3D 框尺寸是 height、width、length 的顺序,nuScenes 是 width、length、height,顺序颠倒会让模型训练直接崩掉。
4.2 Carsim、NI与VTD联合仿真:场景怎么进、传感器怎么仿
仿真在感知技术里的位置是“数据补充”和“回归验证”,不是替代路测。Carsim、NI 和 VTD 的联合仿真是一种比较成熟的硬件在环(HIL)测试方案:Carsim 负责高精度车辆动力学模型,输出车辆的位姿、速度和加速度;NI 实时系统负责运行控制算法和注入故障信号,比如模拟某个传感器断线或数据延迟;VTD(Virtual Test Drive)负责构建虚拟道路、交通流和传感器模型,生成“感知层看到的世界”。
三者的数据流是单向闭环:VTD 生成场景和传感器仿真结果,送给感知系统;感知系统输出目标列表,交给控制算法;控制算法在 Carsim 里算出车辆的下一帧状态;再把车辆位置反馈给 VTD 更新场景。搭建这套环境时,变量位映射是最容易出错的地方。Carsim 输出的车辆位置是惯性系下的坐标,VTD 需要的是相对车体的坐标,中间要做一个坐标变换,否则仿真里的目标位置和车辆轨迹对不上。
表:联合仿真里三个工具的分工和常用接口
| 工具 | 模块定位 | 输出内容 | 常见接口 |
|---|---|---|---|
| Carsim | 车辆动力学 | 车速、横摆角速度、经纬度/坐标 | Simulink 或 TCP 协议 |
| NI | 实时控制和故障注入 | 控制指令、传感器故障状态 | PXI/VeriStand,共享内存或 CAN |
| VTD | 场景与传感器仿真 | 真值目标、合成点云、虚拟相机图像 | RDB(运行时数据总线) |
坐标系变换建议在系统集成文档里用一张表写清楚,直接复制到代码注释里,避免后期联调时两个人各查各的。仿真里的传感器真值不能直接当模型输入,要做“降质处理”:给激光雷达点云加高斯噪声和随机丢点,给相机图像加运动模糊和曝光波动,这样训练出来的模型在仿真到实车迁移时差距才小。
4.3 仿真数据多久能替代真实路测数据
说结论:短期不能替代,长期能补足长尾。感知模型对数据的需求是“高频场景要有量,低频场景要有样”。仿真数据最大的价值是生成真实路测里极难遇到、但一旦遇到就可能出事故的场景,比如前车急刹加旁车并线、施工区突然收窄、动物横穿。这些场景在数据集中占比可以到 20% 到 30%,但仿真数据的标注是自动生成的,不存在人工标注的一致性问题。
用仿真数据训练时要注意 domain gap:合成图像和真实图像的纹理、光照差别会传导到模型的特征层。常见的缓解方法是 domain randomize,即在仿真渲染时随机改变光照角度、地面纹理、目标颜色,让模型学到的不是特定渲染风格而是泛化特征。评估时用“仿真训练 + 真实数据微调”的模型,对比纯真实数据训练的模型,如果前者的长尾场景表现更稳,就说明仿真数据起作用了。
5. 评估指标的坑与可落地的验证技巧
5.1 mAP、NLE、ATE 怎么看
感知技术文档里最常见的指标是 mAP(mean Average Precision),但 mAP 有一个天然短板:它只衡量检测框和真值框的 IoU 是否达标,不衡量时间一致性。一个目标这一帧检测到、下一帧丢掉,mAP 可能依然很高。跟踪指标里,NLE(NuScenes 使用的平均位移误差)和 AMOTA 能反映轨迹稳定性,ATE(Absolute Trajectory Error)更多用于定位而非感知。评估时不能只看一个指标,至少要同时看检测的 mAP 和跟踪的 AMOTA,前者管“找得到”,后者管“跟得住”。
计算 mAP 时,3D 检测的 IoU 阈值通常取 0.5 或 0.7,Waymo 对车辆取 0.7、行人取 0.5。这个阈值不是越高越好,阈值高会低估近距离小尺寸车辆的性能,阈值低会让远距离误检漏进来。建议分距离段评估:0 到 30 米、30 到 50 米、50 米以上各算一个 mAP,这样优化时能定位到是哪一段拖了后腿。
def compute_iou_3d(box_a, box_b): """简化版 3D IoU:只比较水平框的重叠,忽略高度旋转差异。 返回 0~1 之间的 IoU 值。 """ # 取水平面 x、y 坐标和边长 ax1, ay1 = box_a[0] - box_a[3] / 2, box_a[1] - box_a[4] / 2 ax2, ay2 = box_a[0] + box_a[3] / 2, box_a[1] + box_a[4] / 2 bx1, by1 = box_b[0] - box_b[3] / 2, box_b[1] - box_b[4] / 2 bx2, by2 = box_b[0] + box_b[3] / 2, box_b[1] + box_b[4] / 2 ix1, iy1 = max(ax1, bx1), max(ay1, by1) ix2, iy2 = min(ax2, bx2), min(ay2, by2) iw = max(0, ix2 - ix1) ih = max(0, iy2 - iy1) inter = iw * ih area_a = box_a[3] * box_a[4] area_b = box_b[3] * box_b[4] union = area_a + area_b - inter return inter / union if union > 0 else 0这段代码假设两个框都没有偏航角旋转,只计算轴对齐矩形的重叠,适合快速排查问题。完整评估时,请用 Open3D 或自己实现带旋转的 IoU 计算,因为车辆在弯道和变道时偏航角很大,忽略旋转会高估误差。评估代码要固化到一个脚本里,每次模型更新都跑同一份测试集,并输出每个类别的分距离段指标,形成日报。
5.2 负责人要做的一次离线回归验证
假设你刚改完融合算法,想确认没有引入竞态条件或时间延迟问题。常见的做法是拿一段 10 分钟的真实路测数据回放,跑完整的感知感知栈,对比回归前后的目标列表。回放时要注意:如果原数据里相机和激光雷达的时间戳相差超过一个阈值,回放结果可能和车上表现不一致。
验证脚本建议分三步:第一步检查每帧是否正常产生输出,统计无输出的帧数;第二步对比相同时间戳下前后两个版本的目标数量,差异超过 20% 就要警觉;第三步随机抽 50 帧,人工可视化对比检测框位置。三步通过后再上实车,减少无意义的试车时间。
5.3 量化感知系统上线的三个底线
第一个底线是“感知输出必须有协方差或置信度”,下游规控模块需要知道目标位置不确定度,才能决定是全力制动还是缓行通过。第二个底线是“传感器故障时功能要降级而不是崩溃”,激光雷达丢了就切到视觉和毫米波融合,视觉被污损就只保留雷达目标,这套降级逻辑必须在仿真里反复注入故障验证。第三个底线是“所有指标必须有对应数据版本和代码版本”,否则三个月后复现不出结果,任何对比都是空的。把这三个底线写进团队评审清单,比多调一个模型参数更值得。
本文还有配套的精品资源,点击获取