☰
百度Apollo自动驾驶学习笔记:架构、模块与仿真实践
2026/10/18 6:45:06 网站建设 项目流程

我会严格按照要求,直接开始撰写一篇围绕“百度Apollo自动驾驶学习笔记”的深度技术分享博文,不包含任何前置说明或元信息,直接从正文开始。


百度Apollo自动驾驶学习笔记

从第一次在仿真环境里看着车辆自动完成变道、绕障、跟停,到逐步理解每个模块在背后干了什么,这个过程我走了大半年。坦白说,自动驾驶是个很容易让人“从入门到放弃”的领域,因为涉及的东西太杂:感知、定位、规划、控制、高精地图、操作系统、并行计算,随便拎一个出来都能撑起一门课。但如果你有一个像Apollo这样开源的完整系统可以跑、可以改、可以看实时日志,学习曲线一下子就被拉平了不少。

这篇文章是我自己学习Apollo过程中沉淀下来的核心笔记,不是官方文档的复述,更多是站在一个学习者的角度,讲清楚Apollo这套东西到底怎么组织起来的,每个核心模块在解决什么问题,以及我在实际动手跑仿真、读源码、调参数时踩过的坑和总结的经验。无论你是有一些ROS基础、想往自动驾驶方向转的开发者,还是对智能驾驶感兴趣、想搞明白“车为什么自己能开”的产品和技术爱好者,这篇文章都能帮你节省不少摸索时间。

1. 入坑Apollo之前:自动驾驶到底长什么样

1.1 自动驾驶的“眼睛、大脑、手脚”

很多人一开始搞混一个概念:自动驾驶不等于“一个很聪明的算法”。它更像一个完整的闭环系统,每一环都不能出问题。

我们可以用人类驾驶员来类比。人开车的时候,眼睛看到路况、耳朵听到鸣笛,这是感知;大脑根据看到的和听懂的,判断前车可能会减速、旁边行人可能要横穿,这是预测;然后决定是加速超车还是减速让行、走哪条车道,这是规划;最后手脚去踩油门、打方向盘,精准执行这个决定,这是控制。

Apollo的架构本质上也是这样划分的。它有感知模块处理摄像头、激光雷达、毫米波雷达的数据,有定位模块解决“我在哪里”,有预测模块推断其他交通参与者接下来要干什么,有规划模块输出一条能走的轨迹,最后由控制模块把轨迹变成油门、刹车和转向指令。再加上高精地图提供先验信息,整个系统才能像一个老司机一样在路上跑。

我第一次看Apollo源码时,最先做的事不是去读某个算法的具体代码,而是先把模块之间的关系画出来。这样做有两个好处:一是避免一头扎进代码细节里出不来,二是后面调试问题的时候,你知道日志报错来自哪一块,能快速定位问题范围。

1.2 不同层级的学习者该从哪里入手

Apollo这套系统不是只有一种学法,关键看你的基础和目标。

如果你对ROS有一定的了解,那恭喜你,上手会顺利很多,因为Apollo的模块通信机制和ROS非常接近,都是基于Topic的发布订阅模式。你只需要掌握Apollo自己的框架(比如Cyber RT,这是Apollo自研的通信中间件),就能很快把各个模块串起来。

如果你是刚接触自动驾驶、编程基础也一般,那建议先从仿真环境入手,先把整个系统跑起来,看到一辆车在电脑屏幕里自己开,建立直观感受,再逐步深入到某一个模块里去学算法。不建议一上来就啃源码,那样容易迷失在代码的海洋里。

如果你是做某个垂直方向应用的(比如做视觉感知、做路径规划),那更建议聚焦在对应模块上,把上下游的数据格式和依赖摸清楚,然后横向对比Apollo的实现和业界主流方案的差异,会比自己从零搭一个系统高效得多。

我自己属于中间那种,有一点ROS基础,但对自动驾驶整体架构很陌生。我的路径是先跑通仿真Demo,再逐个模块深入,最后再尝试做了一些小的二次开发。这个路径还算平滑,后面会展开讲讲具体每一步怎么走。

2. Apollo平台的整体架构拆解

2.1 模块划分与应用层的对应关系

Apollo的源码目录结构,其实已经暗示了一辆自动驾驶车需要哪些“器官”。打开代码仓库,你会看到很多以模块名命名的目录:perception(感知)、prediction(预测)、planning(规划)、control(控制)、localization(定位)、map(高精地图)、routing(路由)等等。

这个划分不是随便分的,每一层都有清晰的上下游关系。数据从传感器进来,先过感知,感知的结果和定位的结果一起交给预测和规划,规划算出一条轨迹,再交给控制去执行。在Apollo的仿真界面(Dreamview)里,你能很直观地看到这个过程:车辆在路网中行驶,周围障碍物被不同的框标注出来,绿色的是可行区域,红色的是障碍物,车前方有一条彩色的曲线,那就是规划出来的轨迹。

理解这个模块划分还有一个额外的好处:当你去参加一些自动驾驶相关的职位面试或者做项目汇报时,能够用清晰的架构语言讲清楚一个系统是如何工作的,这比背一堆算法名词要有说服力得多。Apollo在这一点上做得很好,它本身就是一个教学级的架构范本。

2.2 消息通信机制:为什么全程都在聊Topic

Apollo早期版本用的是ROS,后来由于性能和实时性方面的考虑,自研了Cyber RT作为中间件。如果你看过Apollo的代码或配置文件,会频繁见到“topic”这个词。简单理解,Topic就是一个数据通道,某个模块往这个通道里发数据,其他模块从通道里订阅数据。

举个例子,感知模块识别到前方50米有一辆车,就会把这条消息发到某个Topic上,规划模块订阅了该Topic,就能拿到这辆车的坐标、速度等信息,然后再结合其他数据决定怎么走。

这种方式的好处是解耦。各个模块不需要知道对方的具体实现细节,只需要约定好数据格式,就能独立开发和升级。这也是Apollo能被这么多人拿来学习和二次开发的原因之一,你完全可以只替换其中的某一个模块,比如把Apollo默认的感知算法换成你自己训练的模型,其他模块基本不用动。

实际动手时,我建议大家花点时间学会用Cyber RT提供的命令行工具去查看Topic列表、打印Topic里的数据内容。这就像调试后端服务时看接口返回一样,是定位问题的基本功。

2.3 从源码目录快速定位核心代码

Apollo的代码量非常大,刚开始看容易觉得无从下手。我自己的方法是先看每个模块的main函数或者组件入口,搞清楚这个模块是在什么时候被启动的,然后再顺着数据流去看处理函数。

以规划模块为例,它的核心代码在planning目录下,入口是PlanningComponent,配置了要订阅哪些Topic、要发布哪些Topic。顺着代码往下走,会走到规划器的核心逻辑,比如OnLanePlanning(车道内规划),里面包含了决策、路径规划、速度规划等子模块。

另外一个很实用的技巧是,Apollo的代码注释和文档质量总体是比较高的,特别是一些关键的算法说明,可以直接当成论文的补充材料来看。如果某个函数看不懂,先去看看它对应的注释和设计文档,往往能省掉很多猜谜的时间。

这里也要提一个建议:代码不要从头到尾逐行读,那样效率极低。正确的方式是带着问题去读。比如你想搞明白“Apollo是如何做变道决策的”,就从决策模块的代码入手,追踪它读取了什么输入、输出什么结果,分析关键函数的逻辑。

3. 核心模块逐个过一遍:感知、预测、规划、控制

3.1 感知:多传感器融合的取舍

感知是自动驾驶最直观、也最“热闹”的部分。Apollo的感知模块支持摄像头、激光雷达、毫米波雷达三种主要传感器。但很多人不知道的是,真实量产车上,未必三种传感器都要用,因为成本、功耗、算力都是约束。

摄像头能提供丰富的颜色和纹理信息,能识别车道线、交通标志、行人,但受光照和天气影响大;激光雷达测距精准、不受光照影响,但成本高,雨雾天气性能也会衰减;毫米波雷达对速度测量非常擅长,但分辨率低,很难识别物体类别。所以Apollo的做法是融合,取长补短。

在代码层面,感知模块做了好几层融合。底层是单传感器的检测,比如用深度学习模型在图像上框出障碍物,把激光雷达的点云聚类成一堆障碍物;上层是融合层,把不同传感器的检测结果映射到统一坐标系里,做关联和匹配,输出最终的目标列表,包括位置、速度、加速度、类别、朝向等信息。

学习感知模块时,一个值得深入钻研的点是“传感器标定”。想让不同传感器的数据融合得好,前提是它们在空间和时间上是对齐的。一颗摄像头检测到的“前方有车”,要换算到以车身为原点的坐标系里,才能和激光雷达检测到的“前方障碍物”对应上。标定就是为了解决这个问题,包括内参标定和外参标定。Apollo在标定工具链上做了不少工作,初学者如果有条件,最好找一套标定工具实际操作一遍,这会极大提升你对融合原理的理解。

3.2 预测:给未来留一点提前量

预测模块是很多人初学时容易忽略的,但它恰恰是自动驾驶“拟人化”的关键。感知模块告诉你前方有一辆车,但如果你不知道这辆车下一秒是要直行还是要变道,规划模块就没法安全地决策。

Apollo的预测模块主要做两件事:一是分析交通参与者的行为意图,比如判断前车是否在准备变道;二是根据意图预测其未来几秒钟的轨迹。

预测算法里有个核心概念叫“交通规则约束”。我们的人脑在预测别人开车意图时,会默认对方会遵守交通规则,比如车道线是实线就不会跨线变道。Apollo也采用了类似思路,把地图信息和交通规则引入预测模型,大大提高了预测的合理性。我在学习过程中对此印象很深刻,因为纯数据驱动的轨迹预测往往会预测出一些“违反物理规则”的诡异轨迹,而在规则约束下,预测结果要可信得多。

想深入理解预测模块,建议去翻翻prediction目录下的代码,重点看不同预测器(比如基于车道的预测器、基于运动的预测器)是如何被选择调用的。这里还有个小细节:预测模块的输入不仅有障碍物信息,还包括车流信息、路况信息、信号灯状态等,信息维度越多,预测结果越准。

3.3 规划:横纵向联合的“老司机”思路

规划模块是Apollo里最复杂的模块之一,它的职责是综合感知、预测、定位、地图信息,生成一条安全、合理、可执行的轨迹。这条轨迹不仅包含“往哪儿走”,还包含“以多快的速度走”,也就是横向规划和纵向规划要联合最优。

横向规划主要解决“在车道内怎么避障、怎么变道”的问题,Apollo里常用的是基于参考线和平滑优化的方法。简单说,它先找到一条路线上的参考路径,然后在这个参考路径附近进行优化,生成一条平滑的、满足车辆运动学约束的轨迹。

纵向规划解决的是“什么时候加速、什么时候减速、什么时候停下”。这背后其实是在做速度剖面规划。Apollo会综合考虑前车距离、限速、弯道曲率、红绿灯状态等因素,生成一条速度曲线。

这里要专门提一下Apollo里非常经典的“决策”概念:整套规划是在先决策后规划的逻辑下进行的,也就是说,先决定“我要干什么”(比如是跟车、停车还是绕行),再具体规划“怎么干”。这种机制让系统逻辑符合直觉,也便于在复杂环境中做安全兜底。学习这部分时,建议重点关注EM Planner和Lattice Planner这两个经典规划器,虽然是几年前的方案,但背后的数学建模思路到今天依然很值得学习。

规划模块的输出是一个包含了多个轨迹点的轨迹消息,每个点包含位置、曲率、相对时间、速度、加速度等信息。在仿真界面里,你会看到车前面有一条平滑的曲线,那就是规划模块的“作品”。

3.4 控制:最后一步的稳与准

控制模块是连接算法和车辆物理执行的桥梁。如果说规划问题是“怎么走”,控制问题就是“怎么让车按这个轨迹走”。Apollo的控制模块主要用到了PID控制和LQR控制等经典算法,虽然听起来没有深度学习那么“新潮”,但它在实际工程中的重要性完全不输给任何一个算法模块。

我自己的直观感受是,控制模块是最能体现实车与仿真差异的地方。在仿真环境里,车辆模型是理想化的,控制参数调得再激进,车一般也不会出太大问题。但在实车上,有转向系统延迟、轮胎非线性、路面坡度、风阻等一堆乱七八糟的干扰,控制参数调不好,车辆就会蛇形、抖动,严重的甚至会造成安全隐患。

Apollo的控制模块支持纵向控制和横向控制分离。纵向控制管油门刹车,目标是让车速精准跟踪规划的速度曲线;横向控制管方向盘转角,目标是让车辆的航向和位置贴合规划的轨迹。在研究控制模块时,可以把重点放在控制器的参数整定上,试着调整不同增益系数,观察跟踪效果的变化,这是特别直观的学习方式,也是我强烈建议新手去做的实验。

4. 高精地图与定位:容易被忽略但极其关键的一环

4.1 高精地图为什么不是普通导航地图

我在学习时一度觉得高精地图没那么重要,毕竟感知模块已经能看到路了。但后来深入接触规划模块才发现,没有高精地图,规划模块根本无从下手。

普通导航地图是给人看的,有路名、有POI、有道路级别,误差按米级算。高精地图则是给车用的,它详细到厘米级,包含车道中心线、车道边界、车道宽度、曲率、坡度、交通标志、信号灯等极其精细的信息。而且高精地图里存的不只是空间位置,还有很多语义信息,比如每条车道的通行规则、可行驶区域。

为什么自动驾驶离不开高精地图?因为感知是有盲区的,在视野受限或距离较远的情况下,感知模块很难对道路结构做出准确判断,而高精地图提供的先验信息正好弥补了这个不足。规划模块在生成轨迹时,也是在以高精地图为底座的“虚拟道路”上进行运算的,而不是凭空想象。

Apollo里有一套完整的高精地图工具链,包括地图数据格式定义、地图生成工具、地图查询接口等。学习这部分内容时,建议去拆解一个高精地图数据文件,理解里面不同元素(车道、停止线、交叉口)是如何关联起来的。这对于后续理解规划、预测模块的逻辑很有帮助。

4.2 定位方案:RTK与点云配准的配合

定位模块负责回答“车到底在哪里”的问题,而且这个“哪里”的精度要求是厘米级,不是普通手机GPS能比的。Apollo里主要用到的是RTK(载波相位差分定位)加激光点云与高精地图配准的定位方案。

RTK可以理解成一种“增强版GPS”技术,它利用地面基准站的观测数据来消除普通GPS的误差,在开阔场景下能实现厘米级定位。但RTK也有自己的问题,比如在高架桥下、隧道里、高楼密集区,卫星信号差甚至完全丢失,这时候定位就没办法单独依赖RTK。

Apollo的做法是融合。当RTK信号好的时候,用它来约束车辆位置;当RTK信号差的时候,利用激光雷达实时扫描到的点云与高精地图上预先存储的点云做匹配,通过配准算法反算出车辆当前的位置,这就是点云定位。两种方案互相配合,保证车辆在不同环境下都能获得可靠的定位结果。

学习定位模块时,建议先搞清楚Apollo里坐标系的概念。自动驾驶涉及多个坐标系,比如全球坐标系、车身坐标系、地图坐标系等。模块之间传递数据时,坐标系变换做得对不对,会直接影响最终结果。我为此专门做过一个小实验,故意把坐标系参数改错,观察车辆的定位结果瞬间漂移到了其他街道上,效果非常直观,也让我彻底理解了坐标系变换的价值。

5. 从零跑通仿真:最值得先做的事

5.1 环境准备与版本选择的坑

学Apollo的第一步其实不是学算法,而是把系统跑起来。这里首先要面对的是硬件环境和版本选择问题。

Apollo官方提供了Docker镜像来统一开发环境,这是个很聪明的做法,免去了安装一堆依赖库的麻烦。不过Docker对硬件配置是有要求的,尤其是运行包含感知、规划、控制全链路的仿真时,对CPU、内存和显卡都有要求。我自己第一次在一台配置较老的笔记本上跑,结果图形界面卡顿严重,根本无法流畅操作。如果条件允许,建议至少配备16GB内存和一块独立显卡,体验会好很多。

版本选择上,Apollo有多个分支版本,比如不同推出了很多版本号(比如3.0、5.0、6.0、7.0等),功能侧重点不同。建议初学者选择一个稳定、文档齐全的版本作为起点,比如早期比较经典的5.0或6.0版本,因为这类版本有大量现成的学习资料和博客案例可以参考,遇到问题也更容易找到答案。不需要追求最新版本,最新的版本往往改动大,资料相对少,容易劝退新手。

5.2 启动仿真、加载地图与车辆

跑通仿真的完整流程,大体上是这样几步:启动容器、编译代码、启动Dreamview、进入仿真模式、加载地图、打开仿真场景、然后车辆就会开始自主行驶。

Dreamview是Apollo的可视化调试平台,在浏览器里打开一个网页即可看到整个车辆运行的状态。你可以在上面选择地图,加载一辆车,让车辆在仿真环境中自动行驶。这里的仿真有两种模式:一种是普通的路测仿真,车辆可以在一个相对完备的路网里自由行驶;另一种是场景仿真,里面内置了一些特定的交通场景,比如前车突然切入、行人横穿马路等,专门用来测试特定能力。

我在第一次成功看到仿真车辆在路上跑起来时,说实话还是挺兴奋的。但兴奋过后要冷静下来,因为那只代表系统跑通了,离理解技术还有很长的路要走。

5.3 用Scenario工具跑通感知到规划的闭环

跑通基础仿真之后,我强烈建议去了解一下Apollo里的Scenario(场景)机制。它是Apollo为特定测试场景设计的工具,可以让你自由设定自车的起点、终点,还可以配置障碍物的行为。

我当时花了一个下午,专门在仿真环境里搭建了一个“前车急刹”的场景,观察自车是怎么从巡航状态平稳减速到停车,并保持一个安全距离的。通过反复修改前车的速度、减速度、切入时机等参数,我能很直观地看到规划和控制模块是如何响应这些变化的。

这个过程的收获很大,既加深了对模块职责的理解,也积累了一些调参和调试的经验。很多时候,源码读十遍,不如实际把一个场景跑一遍来得明白。

5.4 理解输出的关键数据

仿真跑起来以后不能光看热闹,一定要学会看数据。在Dreamview里,你可以选择显示不同的数据层,比如障碍物、规划轨迹、高精地图、定位结果等。把这些图层层层打开,你就能看到传感器数据是怎么被处理成上层模块能用的信息的。

举个例子,打开感知输出的目标列表,你会看到每个障碍物的ID、位置、速度、类型(车辆、行人、自行车)、置信度等属性。对比实际场景里障碍物的真实位置与感知输出的结果,你能直观感受到感知算法的性能,比如有没有漏检、有没有误检、位置误差有多大。

我养成了一个习惯:每次跑一个场景,都会把关键话题的数据保存下来做分析,比如规划轨迹的数据、控制指令的数据。通过查看这些数据,我可以推断出系统当时在做“加减速”还是“转向”决策,再回看场景里的实际交通状况,就能判断系统表现是否合理。这比单纯肉眼看画面更能深入理解系统的行为。

6. 常见问题与排查技巧实录

6.1 启动阶段的常见问题

我在学习和帮助其他同学上手的过程中,遇到过不少典型问题,这里挑几个最常见的分享一下。

第一个常见问题是编译报错。Apollo代码量大,编译一次需要比较长的时间,新手第一次编译容易因为网络问题拉取依赖失败,导致编译中断。解决方案通常是配置好软件源,并在编译前做一次环境检查,确认所有依赖项没问题。另外,建议不要频繁改动代码后全量编译,而是做好增量编译,可以大大节约时间。

第二个常见问题是Docker容器启动失败。这往往是显卡驱动或权限问题导致的,需要确认Docker权限配置正确,必要时重新安装nvidia-container-toolkit。如果启动容器后看不到图形界面,大概率是浏览器版本不兼容Dreamview的某些前端特性,或者端口没有映射正确。

第三个问题是仿真地图加载不出来。通常是因为地图包未完整下载、数据路径配置不对,或者进程内存不足导致加载失败。检查日志是最快的排查方式,直接看启动时有没有报错信息。

6.2 运行过程中的异常排查思路

仿真跑起来后,最容易遇到的现象是车辆行为异常,比如该减速不减速、该变道不变道、方向盘抖动等。这些问题可能来自任何一个模块,排查思路通常是先定位再排查。

Apollo的日志系统很完善,模块运行时会输出大量日志。遇到异常时,第一步是看日志有没有Error级别的报错;第二步是根据时间戳去对应查看各个模块的输出数据是否合理,比如感知目标是否丢失、预测轨迹是否跳变、规划轨迹是否平滑等。多数情况下,通过数据比对就能锁定出问题的模块。

一个有代表性的案例是:某次仿真中车辆在行驶时突然急刹。一开始我以为是控制模块参数问题,后来查看了规划输出,发现规划轨迹里速度曲线出现了剧烈跳变,而速度跳变的原因是感知模块对前方障碍物的检测出现了闪烁,几帧有、几帧没有。定位到问题后,再去改进感知的置信度阈值和过滤策略,问题就解决了。这个排查过程也印证了理解系统架构和数据流的重要性。

6.3 实操中的避坑心得

最后分享几个我个人的经验和避坑心得。

第一,多看日志,少猜问题。Apollo的日志写得非常详细,遇到问题先看日志是最快的方式,不要盲目地改参数。很多新手喜欢一上来就怀疑某个模块有问题,结果调了半天发现根本不是那个原因。

第二,善用利器。Cyber RT自带的调试工具很大程度上简化了故障定位的过程,比如打印话题数据、查看模块状态等。熟练掌握这些工具,会让你的调试效率翻倍。

第三,保持一个“最小改动”的实验习惯。学习和二次开发时,不要一次改很多地方,一次只改一个变量,跑一轮对比一次效果。否则当结果变差时,你根本不知道是哪一步导致的。这个习惯不仅适用于学Apollo,也适用于所有工程学习。

我在实际使用中还有一个很个人的体会:学Apollo不要太急于求成,这是一个体系庞大的工程。最好建立自己的笔记库,把每个模块的原理、关键配置、踩坑过程都记录下来。我自己的笔记从最初只有几行命令,慢慢积累到了包含图、表格和大量代码注释的完整文档,回头翻看时收获很大。这套学习方式后来也帮助到了一些同样在学Apollo的朋友。

这个内容后续还可以往很多方向扩展,比如把自己训练的目标检测模型接入感知链路,或者尝试修改规划器里的代价函数来改变车辆的行为风格,又或者将仿真中累积的经验迁移到自己的研究课题里。总之,Apollo是一个值得慢慢挖掘的系统,希望这篇笔记能帮你快速跨过第一道坎,体会到自动驾驶技术从算法原型到工程落地的有趣之处。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询