1. 进阶篇开篇为什么感知是无人驾驶里最难啃的骨头很多人以为做无人驾驶重点在于控制算法和路径规划车辆只要能感知到前方有东西就能躲开。实际真正上手跑过 Apollo 仿真、或者在实车上盯过感知模块输出的人都会明白一件事感知是整个自动驾驶链路里不确定性最大、最容易翻车、也最依赖工程沉淀的部分。我在学完 Apollo 入门篇之后对它的总体架构已经有了基本印象——高精地图、定位、感知、预测、规划、控制、底盘通信每个模块各司其职。但进入进阶篇的感知部分之后才发现之前对感知的理解太浅了。入门篇里传感器只需要在仿真环境里给出干净的障碍物坐标到了进阶篇你要处理的是真实世界里的点云噪音、曝光过度的相机画面、拉链式并排行驶的重型卡车、黄昏时逆光下的行人……每一个案例都在提醒你感知不是识别出物体那么简单而是要在一帧一帧不完美的数据里逼近一个可用的世界模型。这篇笔记我重点整理了感知部分的几个核心模块从传感器数据到障碍物输出的整体链路激光雷达感知的预处理、分割与检测视觉感知的红绿灯、车道线、可行驶区域多传感器融合的匹配逻辑与ID管理还有部署过程中真正折磨人的工程细节。如果你也在跟 Apollo 课程这篇笔记比较适合作为你的第二遍阅读提纲——第一遍跟着课程视频走第二遍再带着笔记里的问题去翻源码和调试工具。2. 感知整体链路拆解从传感器原始数据到下游可用的障碍物结果2.1 感知模块在整套架构里的位置在 Apollo 的软件框架里感知模块位于传感器驱动和下游预测/规划模块之间。你可以把它想象成一个流水线的质检工位传感器激光雷达、相机、毫米波雷达负责采集物理世界的信息感知模块负责把这些信息加工成统一的、有时间戳的、带语义的道路物体清单交给预测模块去推测这些物体接下来要干什么再由规划模块决定车辆该怎么开。这里关键是统一两个字。三个传感器的数据格式完全不一样激光雷达输出的是点云——一堆带有空间坐标和反射强度的点相机输出的是图像——二维像素矩阵毫米波雷达输出的是目标级数据——直接给出目标的距离、速度、角度。感知模块要做的第一件事并不是识别而是把不同传感器得到的信息对应到同一个物理目标上。如果这一步没做好下游收到的可能是一份自相矛盾的障碍物清单激光雷达说前方5米有静止物体视觉说前方5米有行人融合模块如果不处理这种冲突就会导致决策模块无所适从。2.2 感知输出的核心数据结构障碍物与红绿灯状态课程里反复强调一个概念感知模块的产出不是一个画面而是一个结构化描述。用 Apollo 里的术语来说就是障碍物Obstacle和交通信号灯状态TrafficLight。一个障碍物条目包含的内容很直接唯一ID跟踪用确保是同一个物体类型小轿车、卡车、行人、自行车、未知三维框的位置、大小、朝向速度与加速度与自车的相对距离置信度。红绿灯状态则包含灯组的位置、颜色状态红/黄/绿/黑/未知、以及对应的置信度。注意这里的位置不是相机图像上的像素位置而是地图坐标系下的三维位置——这一点特别容易被新手忽略感知不仅要识别出前方有红灯还要告诉规划模块这个红灯在哪条车道线旁、距离多远。2.3 相机、激光雷达、毫米波雷达的职责分配进阶篇课程没有让人只依赖某一种传感器而是强调各传感器各司其职、互为冗余。我整理了一下它们的分工定位传感器优势劣势在Apollo感知里的主要职责激光雷达距离准、三维信息直接、不受光照影响雨天/雾天性能下降、成本高、点云稀疏时目标容易中断三维目标检测、可行驶区域边界、目标测距相机语义信息丰富、能识别颜色和纹理强光/逆光/暗光容易失效、单目距离估算误差大车道线、红绿灯、行人/交通标志、2D检测毫米波雷达全天候工作、直接测速、成本低角度分辨率低、点迹稀疏、难以识别静止目标类别目标测速、远距离感知、雨雾天气兜底我在实车调试时有个体会三个传感器同时丢掉同一个近距离目标的情况极少但任意两个同时失效就很容易出问题。所以 Apollo 感知模块在设计时并没有简单地把所有数据丢进一个大网络里而是分别做检测、再做融合这样可以在单个传感器失效时平滑降级。3. 激光雷达感知的核心链路从点云预处理到三维目标检测3.1 预处理的第一步地面滤除不是可选项激光雷达一帧点云里绝大多数点都落在地面上。如果不对地面点做处理目标检测和聚类会面临两个灾难性问题地面点和障碍物点混在一起聚类算法会把路面和路沿连成一片远处低矮物体的点云会被地面点淹没目标根本分割不出来。Apollo 里经典的做法是基于高度和平整度做地面滤除。课程里提到的思路很朴素一个点如果在一个局部区域内比周围的点低很多它大概率属于地面如果相邻点的高度差突变剧烈那这附近大概率有障碍物边界。实际操作中不同雷达线束密度差异很大——64线雷达地面点非常密集4线雷达则稀疏很多。Apollo 的做法并不是只在某一个固定高度阈值上切一刀而是通过动态估计地面平面模型来分离地面点。你会发现同样的路面在上下坡时如果还用一个固定高度阈值结果会非常难看。3.2 ROI 区域过滤算力很贵不管不该管的点激光雷达能看到的范围很广但车辆真正需要关心的区域主要集中在自车周围的一定范围。Apollo 课程中专门提到了ROIRegion of Interest过滤这一步也就是根据高精地图信息把点云里位于道路边界之外的点直接裁掉。这一步的作用有两个减少计算量——与其让检测网络对所有点做推理不如先裁掉大部分无关点减少误检——路边的树木、护栏、行人道上的非机动车如果不在当前车道的ROI里优先级就会被降低。你还得注意 ROI 不能设置得太紧。窄路、施工改道、临时路障都会让车辆实际运行区域偏离高精地图标定范围。课程里给的建议是ROI 范围应该是动态的低速复杂场景下适当扩大高速结构化道路下可以收紧。我在自己跑仿真时对这一点感受很深ROI 范围过小车辆在临近路口的右转场景经常会丢掉路沿上的锥桶导致规划模块觉得无路可走。3.3 点云分割与目标检测聚类、PointPillars、CenterPoint在点云完成了地面滤除和 ROI 过滤之后就进入了核心检测阶段。Apollo 提供了两条技术路线课程里都有涉及第一条是传统聚类路线。对点云做密度聚类类似欧几里得聚类把空间上聚集的点归为一类再给每个聚类拟合一个三维包围框。这条路线的优点是简单、可控、CPU友好缺点是聚类参数对场景敏感——密度阈值设大了会把一辆车和旁边的行人合成一团设小了又容易把一个卡车拆成好几块。第二条是基于深度学习的点云检测路线。Apollo 里集成过 PointPillars 这类基于体素的检测网络后来也逐步演进到 CenterPoint 这类 anchor-free 方案。PointPillars 的思路是把三维点云切成一堆竖直柱体pillar每个 pillar 内聚合点云特征再丢给 2D 卷积网络处理最终输出三维框。CenterPoint 则更进一步不依赖预设 anchor 框而是直接预测物体中心点再回归其他属性。这里我要多说一句课程里点云检测部分的重点不是让你从零训练模型而是帮你理解检测结果如何映射回真实坐标。网络输出的三维框中心通常是在体素坐标或自车坐标下要转换到世界坐标系必须串联外参、内参、时间戳对齐等一整套变换。很多人刚开始调试时觉得模型识别率够了一上车就崩多半是坐标系转换在某个环节出了问题。3.4 点云感知的边界情况稀疏、遮挡、雨雾激光雷达并不是万能的。课程里有几个专门讨论点云感知失效的场景远距离目标点云稀疏一辆 50 米外的小轿车可能只扫到几个点聚类算法很难把它和噪声区分开深度模型也容易漏检。这时候就需要靠视觉融合或者毫米波雷达补位。近距离大目标遮挡一辆卡车斜停在路边它旁边的行人可能完全被遮挡点云里根本没有任何信息。感知模块输出不了看不见的东西只能通过预测模块的轨迹推断来补。雨雾天气雨滴和雾霾会产生大量噪声点地面滤除和聚类都会变得不稳定。Apollo 的做法通常是降低对激光雷达远距离输出的信任度更多依赖毫米波雷达。我的建议是不要试图用单一传感器解决所有问题学会看传感器可信度这个隐藏维度。融合模块之后的输出里每种传感器对目标的检测置信度其实是分开保留的这为下游决策提供了重要的判断依据。4. 视觉感知不仅仅是认出物体4.1 2D 检测与 3D 检测的配合相机在 Apollo 感知体系里的作用绝不仅仅是看到什么就输出什么。它主要负责几类任务2D目标检测图像上画出障碍物的框3D目标检测从单目或多目图像回归障碍物的三维尺寸和位置车道线检测红绿灯检测与识别可行驶区域分割2D 检测目前已经比较成熟。Apollo 里常用的做法是基于深度学习的检测网络比如 YOLO 系列或其变体在图像上框出目标然后通过相机的外参和地面几何约束把 2D 框投影到三维空间。但注意单目 3D 检测的精度天然有限。同一个 2D 框物体可能是近处的小轿车也可能是远处的卡车。课程里明确指出了这一点单目 3D 检测主要用来提供语义类别和粗略位置精确测距还是要靠激光雷达或双目视差。4.2 红绿灯识别的难点定位、时序、颜色红绿灯模块是进阶篇里一个很有意思的案例因为它的难点不在识别红色还是绿色而在时空关联。首先是定位问题相机图像里的红绿灯只是一个很小的色块而且同一个路口的红绿灯可能有多组分别对应不同车道。感知模块需要结合高精地图先预测当前车道上哪个红绿灯跟自己相关再在图像里找到它。然后是时序问题红绿灯有周期相机每一帧都在变如果某一帧识别成了黄色下一帧识别成了红色这中间到底是切换还是误检Apollo 的做法是引入一个小的状态机要求连续多帧确认同一个状态才输出切换结果避免单帧误检导致决策模块急刹车。最后是颜色识别本身红色在夜晚会偏暗黄色在强光下会偏白不同相机的自动白平衡还会影响颜色输出。课程里的经验是在训练数据里要故意加入大量不同天气、不同时长、不同相机的红绿灯样本否则模型在实车上会给你惊喜。我自己跑仿真时遇到过一种情况路口的红绿灯在画面里只占很小区域视觉模型漏检了但地图上的信号灯位置是已知的。Apollo 的做法在这种情况下会把该信号灯状态标成未知宁可让规划模块保守处理也不会硬猜一个颜色。这种不确定就说不确定的设计哲学我觉得是感知工程里特别重要的一课。4.3 车道线与可行驶区域给规划一条路权边界车道线检测也是视觉感知的核心任务。它不像障碍物检测那样输出一个框而是输出一系列曲线点集描述车道线的位置、类型实线、虚线、双黄线和置信度。可行驶区域分割则更偏向图像分割任务——把画面里的道路区域和路牙、绿化带区分开输出一个区域掩码再投影到自车坐标系。课程里提醒了一个细节可行驶区域的边界不能直接当作障碍物边界用。道路边缘可能有一小段草皮也可能有一块低矮路缘石图像分割模型看到的是非路面就认为是边界但实际车辆可能压过去也没事。所以规划模块通常会把可行驶区域边界往里缩一段距离预留安全裕量。5. 多传感器融合ID跟踪与置信度管理的较量5.1 为什么需要融合单传感器的盲区各不相同融合Fusion模块是感知部分的总集成商。它的输入是激光雷达检测结果、视觉检测结果、雷达目标数据输出是一份统一且一致的障碍物列表。融合的难点不在同一个物体被三个传感器都检测到时取平均值而在一个物体只被其中一个传感器检测到时怎么办。比如视觉检测到行人但激光雷达因为遮挡没看到毫米波雷达检测到远处快速接近的车辆但视觉因为逆光没看到激光雷达检测到静止的交通锥但视觉因为目标太小没看到。每一种情况都意味着如果直接丢弃单一传感器结果就可能漏掉关键目标如果直接采信单一传感器结果又可能把误检带进决策链路。5.2 融合策略从匹配到跟踪的生命周期管理Apollo 的融合模块大体上做了三件事匹配Association判断两帧之间的障碍物是否属于同一个物理目标。常用的方法包括基于距离、基于 IOU交并比、基于特征相似度的匹配。状态估计Estimation对匹配成功的障碍物用滤波算法比如卡尔曼滤波融合多传感器的测量值估计出更稳定的位置、速度、加速度。生命周期管理Track Management维护每个障碍物的 ID知道它什么时候出生、什么时候消失。课程里专门提到ID 稳定性是融合模块的重要指标。规划模块在做出避让决策时依赖的是障碍物的一段历史轨迹如果 ID 频繁跳变——同一辆车一会儿是 ID12 一会儿是 ID57——预测模块就无法建立稳定轨迹决策自然就会抖动。5.3 传感器失效与降级策略实车里传感器一定会失效。镜头被泥点遮住、雷达通信偶发丢包、激光雷达电机故障……Apollo 是怎么处理的课程里讲了几个原则。降级而不是中断一个传感器失效时融合模块不会报感知故障退出而是自动切换到剩余传感器的结果并降低整体置信度。超时判定同一个目标如果连续多帧没有被任何传感器更新就会进入推测队友状态位置速度按照上一帧的趋势外推一段短时间再标记为消失。这里的时间窗口很关键太短会导致目标忽隐忽现太长会掩盖真正的危险。异常状态上报感知模块自身无法处理的情况比如主机算力过载、传感器时间不同步会上报到系统层面由安全模块决定是否降速靠边。我在这部分最大的收获是意识转变感知不是识别得越多越好而是在不确定中维持一份尽量可信的共识。融合模块的真正价值是让决策模块始终有一份可用的数据哪怕这份数据带上了置信度不高的标签。6. 实际调试感知模块时容易踩的坑6.1 坐标系和时间戳是万恶之源我接触 Apollo 感知以后踩过最大的坑就是坐标系。至少遇到过三次检测结果看起来对、但实际上在乱跳的问题最后定位到都是因为某个坐标系用错了。先说坐标系。感知模块内部至少存在四类坐标传感器坐标、车辆坐标、世界坐标、地图坐标。传感器外参的作用就是把点云从激光雷达坐标系转到车辆坐标系。外参标定的微小误差在远处会被放大成很大的位置偏差。你拿一个 10 米外的路障验证外参可能只差了 2 厘米同样的外参误差放到 80 米外的车辆上位置可能偏了半米。再说时间戳。不同传感器、不同处理链路的数据到达融合模块的时间都不一样。如果直接拿一帧 100ms 前的激光雷达数据和一帧 20ms 前的视觉数据做匹配高速场景下的同一辆障碍车可能已经在空间上移动了 1~2 米。所以 Apollo 里才有一大堆时间对齐的机制。每次调试感知问题先检查时间戳是不是对齐的通常能排除一半的诡异现象。6.2 可视化调试别只盯着数值课程里花了不少时间讲可视化工具的使用我当时觉得不就是画几个框么后来发现可视化对于感知调试是不可替代的。一个推荐的做法是在回放数据里同时显示点云俯视图、相机图像、检测输出的 3D 框和 ID 号、以及速度矢量。这样你能一眼看出问题到底出现在哪个环节如果点云里目标很清晰但网络没检测出来——检查模型部署和输入预处理如果网络检测出了目标但 3D 框位置偏移——检查坐标变换和外参如果 3D 框位置正确但 ID 一直在跳——检查融合匹配阈值和跟踪生命周期管理。很多问题靠读代码是看不出来的但看到可视化的一瞬间就明白了。6.3 参数调优的顺序先链路后算法初学者拿到感知模块特别喜欢直接调模型阈值。但我自己的经验是先把整个链路跑通、跑稳再回头调单点参数。比如检测网络输出的置信度阈值调高了会漏检调低了会误检。但在实际项目里这个阈值往往不是最先要调的——你应该先确认数据流一遍畅通、时间戳对齐、坐标系没问题然后才轮到它。如果数据流没跑通就算把模型调到最优融合模块输出的障碍物列表也照样是乱的。6.4 真车测试和仿真测试的差异最后提醒一下仿真环境里跑得再好实车测试仍然会发现新问题。仿真数据没有传感器噪声、没有曝光问题、没有震动和延迟感知算法在仿真环境里的表现通常会比实车好看。真正让我觉得涨经验的还是在实车或者描述精确的回放数据上反复看感知模块在各种边缘场景下的表现。7. 感知进阶的学习路线建议如果你跟我一样在入门篇之后打算深入感知部分我的建议是按照广度优先、深度随后的顺序学先建立全景图——弄明白数据从传感器到融合输出的每一步重点是数据的走向和模块间的接口再挑一个方向深入——要么做点云感知要么做视觉感知要么做融合把一个方向的核心算法学透而不是每个方向都浅尝辄止多看感知失败案例——课程里讲的每个失效场景都值得单独写篇笔记分析因为这些才是感知工程真正要解决的问题。我在实际学习中的体会是感知模块没有一个终极答案。点云检测、视觉识别、传感器融合每个方向上都有无数人在迭代更新。更重要的能力是形成自己的调试方法论出了问题知道往哪个环节查、用什么工具验证、怎么通过对比实验缩小范围。下一篇笔记我会接着写预测与规划部分把感知到世界模型之后车辆如何决策下一步动作这条链路补全。如果你也在跟这套课程欢迎一起交流学习中遇到的坑。