☰
Apollo自动驾驶平台入门实操:从环境搭建到跑通仿真全流程
2026/10/6 21:18:00 网站建设 项目流程

2017年百度宣布开源Apollo自动驾驶平台的时候,我还在机器人行业搬砖。第一反应是:又一个PPT。直到后来项目需要,我真的把Apollo源码拉下来、按流程编译、在模拟环境里看到那辆虚拟汽车沿着车道线稳稳跑完一圈时,才意识到这是一个货真价实的东西——一套完整、可编译、可运行、几乎开放了主体代码的自动驾驶系统。这篇帖子就把我入门Apollo时走过的完整路径梳理一遍,从硬件准备、环境搭建、代码编译,到跑通第一段仿真数据,再到理解感知、规划、控制这些核心模块的协作逻辑。内容只讲接地气的实操和原理,适合第一次接触Apollo的开发者和学生,也适合想了解自动驾驶系统架构的非算法岗朋友。

1. Apollo是什么:它不是一个Demo,而是一整套自动驾驶技术栈

1.1 从一辆车的角度看Apollo的构成

我们谈Apollo的时候,说的其实不只是GitHub上那一堆C++代码。整个Apollo开放平台覆盖了自动驾驶从硬件到云端的完整链路。

最底层是车辆平台和参考硬件。Apollo官方的车辆平台基于线控底盘,通过CAN总线协议控制转向、油门、刹车。硬件层则给出了传感器建议清单:激光雷达、毫米波雷达、摄像头、GPS/IMU组合惯导。这些不是随便接上就能用,Apollo为每一类传感器都写了驱动和标定工具,这也是新手最容易低估的工作量。

硬件之上是软件平台,通常分几大块:

  • 高精地图和定位:负责告诉车"你在哪、前面是什么路"
  • 感知:负责把相机、雷达的原始数据变成"前方有车、有行人、有车道线"
  • 预测:判断感知出来的障碍物接下来几秒想干什么
  • 规划:计算"从当前位置到目的地,怎么走最安全"
  • 控制:把规划好的轨迹转换成方向盘角度、油门和刹车指令

然后是云端和仿真。Apollo提供DreamView可视化工具、仿真模拟器、高精地图服务等,用于开发调试和测试。很多人第一次接触Apollo就是从DreamView开始的——毕竟看到一个虚拟车辆在屏幕上自己跑起来,比读十篇架构文档都直观。

1.2 版本演进和我对选版本的建议

Apollo从1.0到9.0,演进非常明显:

版本主要变化大致支持能力
1.0平台雏形,封闭场地循迹简单轨迹跟随
1.5加入高精地图、感知、规划固定车道巡航
2.0/2.5引入多传感器融合、夜间行驶简单城市道路
3.0/3.5引入CyberRT通信框架、无GPS区域感知复杂城市道路、雨天夜间
5.0/6.0算法模块重构、规划控制全面升级城市路况+部分高速
7.0-9.0架构持续演进,工具链完善全场景覆盖

如果你是新手上路,我的建议是:先别追最新版本,选一个生态最成熟、教程最多的版本入手,比如Apollo 5.0或6.0。这套版本对应的社区文章、踩坑记录都很多,遇到问题基本能搜到答案。新版本当然更好更完善,但有些API变化大、文档更新慢,对新手反而增加负担。等把老版本的核心逻辑吃透了,再切新版也不迟。

2. 开工前的准备:硬件、系统和Docker,一步都不能省

Apollo不是拿个普通笔记本就能随便跑的东西。它的编译和运行对硬件有硬性要求,这一点刚开始容易忽视。

2.1 硬件环境清单

我自己使用的配置是Intel i7-9700的CPU、32GB内存、NVIDIA GTX 1060 6GB显卡、500GB NVMe固态硬盘。这个配置跑Apollo 5.0编译和模拟回放比较从容。如果配置低一档,也不是不能跑,但体验会差很多:

  • CPU:建议8核16线程以上。Apollo编译时有大量并行编译任务,核心太少编译时间会非常漫长,动辄几个小时。
  • 内存:16GB是底线,32GB才舒服。Docker容器本身、编译缓存、DreamView进程加上中间数据,内存占用很容易顶到十几个G。
  • GPU:推荐NVIDIA显卡。感知模块的深度学习推理需要CUDA,GPU也用于渲染。显存4GB够用,6GB以上更好。没有NVIDIA显卡的话,可以编译CPU版本的感知模型,但效果和速度都会打折。
  • 硬盘:至少留出100GB空闲空间。全部源码+依赖镜像+构建产物几十个G很正常。建议NVMe固态,编译时会频繁读写小文件,机械硬盘会很痛苦。

2.2 为什么Apollo非要Docker

很多第一次接触Apollo的人都会被Docker这层绕来绕去的流程搞烦,忍不住问:为什么不能直接装依赖、直接运行?

原因是Apollo的依赖链太长太复杂了。光编译需要的基础库就有几十个,每个还有特定版本的ABI兼容问题。如果直接装进主机Ubuntu里,不同项目之间很容易产生依赖冲突,而且卸载不干净。Docker的本质相当于把整套编译运行环境"打包成一个集装箱",里面所有依赖版本已经配好,你拎起来在任何机器上都能开箱即用,用坏了删掉重建一个就行,完全不影响宿主机。

这一点我深有体会。早期Apollo还支持用ROS时,我在自己机器上手动配过一次依赖环境,结果把系统搞坏了两次。后来用Docker,所有问题都变成了"环境坏了?删容器重建",成本降低了几个数量级。

2.3 安装Docker和GPU支持的关键点

主机系统我推荐Ubuntu 18.04或20.04。Apollo各版本对系统版本有明确要求,5.0/6.0对应18.04,7.0以上对应20.04,9.0也开始支持22.04。系统版本和Apollo版本不匹配,会引出各种奇怪的问题。

Docker安装本身不难,官方文档很详细,但有一个点容易漏:为了让当前用户不用sudo也能执行docker命令,需要把用户加入docker组:

sudo usermod -aG docker $USER

执行完之后一定要注销重新登录或者重启,不然组权限不生效。

然后是NVIDIA Container Toolkit,这个必须装。它的作用是让Docker容器里能用上宿主机的GPU。没有它,容器里的CUDA程序会报找不到设备错误。装完之后可以用这个命令验证容器内部GPU是否可用:

docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi

如果能看到显卡信息,说明GPU透传成功。

3. 拉代码与编译:从Clone到中控台就位的完整流程

3.1 下载源码和切换到合适的分支

Apollo源码托管在GitHub的ApolloAuto/apollo仓库。clone的时候建议加上--depth=1参数,只拉取最近一次提交,避免把整个历史都拖下来——这个仓库的完整历史非常大,全量clone非常耗时。

git clone --depth=1 https://github.com/ApolloAuto/apollo.git cd apollo

然后切换到目标版本分支。比如我用的5.0版本:

git checkout v6.0.0

这里有个坑:如果先拉的最新分支再切到老版本分支,代码版本可能和子模块不同步。所以更稳妥做法是直接拉指定分支:

git clone --depth=1 --branch v6.0.0 https://github.com/ApolloAuto/apollo.git

3.2 进入Docker开发环境的两种方式

经典版本(5.0/6.0)的启动方式是:

bash docker/scripts/dev_start.sh bash docker/scripts/dev_into.sh

第一次执行dev_start.sh会从Docker Hub拉取Apollo开发镜像,这个镜像是几个G的体量,需要等一段时间。拉取完成后,dev_into.sh会进入容器并挂载当前Apollo目录。

版本不同进入方式也有差异。Apollo 9.0引入了aem(Apollo Environment Manager),流程变成了:

./apollo.sh build aem start aem enter

aem把镜像构建和环境管理做成了更统一的工具,用起来更省心。但无论哪种方式,本质都是一样的:让代码目录挂载进一个预装好所有依赖的容器里,所有编译运行都在容器内完成。

3.3 编译:等待时间很长,但别闲着

在Docker容器内执行编译:

bash apollo.sh build

整个编译过程会输出大量的编译信息,耗时取决于机器配置。我见过有人拿低配笔记本编了四五个小时,所以我一般建议编译前先确认机器散热和电源策略,别让CPU降频,否则越编越慢。

编译时间可以利用起来做点别的事,比如读一读docs/目录里的技术文档,或者去研究一下modules/下的目录结构。新手容易犯的错是编译完成后急急忙忙去跑Demo,结果对代码结构一无所知,出了问题完全不知道去哪查。花半小时看一下目录结构,后面排查问题会轻松很多。

modules/目录下常见模块包括:perception(感知)、prediction(预测)、planning(规划)、control(控制)、localization(定位)、routing(路由)、map(地图)、canbus(车辆底层的CAN总线通信)、dreamview(可视化界面服务)等。一眼扫过去,就能对Apollo的分层有个直观认识。

3.4 编译告警和失败怎么判断

看到大段红色输出不要慌,先判断是warning还是error。Apollo编译输出中偶尔会有warning,不影响生成目标文件;但如果出现ERROR: Build failed或类似的明显失败信息,就需要处理。

最常见的编译失败原因是内存不足。编译过程是并行进行的,多线程同时跑GCC时内存会瞬间飙升,如果机器内存不够直接OOM(内存溢出)。降低并行度可以缓解:

bash apollo.sh build --jobs=4

另外还有一类原因是磁盘空间不够。编译产物非常占空间,如果分区快满了也会编到一半报错。

4. 第一次让Apollo跑起来:DreamView与仿真数据回放

编译通过那一瞬间是很爽的,但真正的成就感来自看到车跑起来。这一节讲主流程。

4.1 启动DreamView可视化平台

在Docker容器内执行:

bash scripts/bootstrap.sh

它做两件事:启动DreamView后台服务,以及启动监控模块。之后打开浏览器访问:

http://localhost:8888

就能看到DreamView界面。注意,如果浏览器在宿主机,要保证Docker使用host网络模式或映射8888端口,否则访问不到。Apollo官方脚本默认做了端口映射,正常情况直接访问即可。

DreamView界面左侧是模块开关面板,右侧是3D视图,中间是车辆状态信息。第一次打开时可能会觉得界面信息量太大,不要慌,核心就几个点:模块状态是否点亮、车辆定位是否正常、规划轨迹是否生成。

4.2 加载Demo数据回放

Apollo源码包带了一段录制好的道路数据(record文件),用于模拟真实传感器输入。在DreamView中可以完成加载和播放。

更直接的方式是在容器里用CyberRT工具播放:

cyber_recorder play -f docs/demo_guide/demo_3.5.record

播放前记得在DreamView左侧打开感知、预测、规划、控制等模块的开关。播放后,你会看到视图中央出现一辆车,周边的障碍物会被实时识别出来并框选,车前会画出一条规划轨迹,车辆沿着轨迹前进。

第一次看到这个场景时,我盯着屏幕看了很久。不是因为这有多酷,而是那个画面意味着:从传感器数据进来,到感知出障碍物、预测出意图、规划出轨迹、下发控制指令,这条完整链路是真真切切在本地机器上运转起来的。对学习自动驾驶来讲,没有比这个更好的起点。

4.3 在DreamView界面里该重点看什么

如果只是在屏幕上看车跑圈,收获有限。我建议每次回放数据时,刻意观察这几个点:

  • 车辆周围的不同颜色的框,对应不同类型的障碍物
  • 车前方的两条平行线,那是规划模块输出的参考轨迹
  • 地图上车辆行驶路径和目标点,对应路由模块的工作
  • 模块面板中Planning、Prediction等模块的运行状态

把这些现象和背后模块对应起来,再去看代码就会有目标感,而不是一头扎进几十万行的代码里迷路。

5. 感知与定位:Apollo如何"看见"自己在哪

5.1 多种传感器各司其职

如果你认为自动驾驶的感知就是把摄像头画面扔给神经网络就完事,那就把问题想简单了。Apollo的感知是一个多传感器融合系统。

激光雷达输出的是三维点云,能精确测距、建模周围环境,但在雨雾天气和远距离小物体上表现不佳。摄像头提供丰富的颜色和纹理信息,适合识别交通灯、交通标志、车道线,但容易受光照影响。毫米波雷达对速度测量非常准,穿透性好,适合检测动态目标。

Apollo通过融合算法把三类传感器结果统一到同一坐标系中,互为补充。这也解释了为什么自动驾驶硬件成本那么高——想在所有场景下都可靠,单靠一种传感器确实做不到。

5.2 感知模块的典型输出

感知模块的输出通常包括:

  • 障碍物列表:每个障碍物的位置、大小、速度、朝向、类型(车辆、行人、自行车等)
  • 车道线信息:车道线的位置、曲率、类型(实线、虚线)
  • 红绿灯状态(如果场景涉及)
  • 路面标志检测结果

这些数据以CyberRT消息的形式,通过Topic广播给下游的预测和规划模块。理解消息结构,对开发调试特别关键。比如你改了感知算法,想知道输出格式变没变,可以直接用cyber_monitor工具查Topic里的消息内容。

5.3 定位:知道自己在哪是一切决策的前提

定位对自动驾驶的重要程度,怎么强调都不为过。一辆车如果连自己在地图上的精确位置都不知道,规划出的路线完全不可信。

Apollo的定位主要依赖三种信号的融合:

  • GPS/RTK(实时动态差分定位),提供厘米级绝对位置信息
  • IMU(惯性测量单元),提供加速度和角速度,用于短时间高频率的位姿推算
  • 激光雷达点云与高精地图的匹配,修正GPS信号丢失时的定位漂移

这套组合在GNSS信号良好的环境下精度很高;在隧道、高楼密集区域,GPS不可用时,IMU和激光雷达匹配会接管定位任务。Apollo在很长一段时间里的定位方案就是基于这三种传感器的融合,这也是定位模块复杂的地方——不是单点定位,而是递推、匹配、融合、修正不断循环的过程。

6. 从路由到控制:车辆如何自己决定怎么走

6.1 路由与参考线:先解决"往哪个方向走"

把导航地图类比成开车导航,Routing模块负责的就是"大方向"——给定起点和终点,在高精地图上找出一条可行路线。

Routing的输出不是一条细到车轮级别的轨迹,而是一条由道路和车道连接而成的路径。规划模块拿到这条路径后,会生成一条基于参考线的更精细的轨迹。参考线是规划模块内部的核心概念,它把三维道路问题转化成相对车道的一维问题,在数学上大为简化。

6.2 规划模块:在安全和效率之间找平衡

规划模块的工作可以理解成:在Routing确定的参考线附近,生成一条位置、速度、朝向都平滑的轨迹,并确保这条轨迹安全、舒适、合法。

Apollo规划采用的思路是采样+优化+决策的组合。一方面基于Frenet坐标系采样候选轨迹,评估每条轨迹的代价;另一方面对轨迹进行平滑优化。规划还需要考虑动态决策,比如前车慢速行驶时应该跟车还是变道,这是一系列复杂的场景决策。

想入门规划,不用一头扎进源码,建议先从DreamView里观察规划轨迹的变化。你会发现前方有障碍物时,规划轨迹会平滑地绕开;前车减速时,规划轨迹的红色速度曲线也会相应下降。有了直观概念再去看planning模块里的代码,会容易得多。

6.3 预测模块:先猜别人想干嘛

只感知到"前方有一个行人"是不够的。规划还需要知道这个行人接下来3秒是要横穿马路还是站在原地。

预测模块的任务就是估计每个障碍物的未来运动轨迹。Apollo的预测包括基于运动学的短时预测、基于语义地图的行为预测、基于学习的轨迹预测等,通常输出各候选轨迹的概率。

这一步对安全至关重要。印象很深的是Apollo文档里强调过:一次失败的预测可能比一次失败的感知更危险,因为车会因为错误的预判而选择错误的应对策略。

6.4 控制模块:把轨迹变成方向盘和踏板

规划模块输出的是轨迹——一条几何与时间相关的曲线。但如果只是把这条曲线当作"理想路径",车是没法执行的。控制模块负责把轨迹转化成具体的油门、刹车和转向指令,保证车实际行驶时尽量贴合规划轨迹。

Apollo的控制以横向控制和纵向控制为核心。横向控制负责方向盘转角,常用MPC(模型预测控制)或LQR;纵向控制负责速度和加速度,对应油门和刹车指令。控制效果的一个重要评估指标是跟踪误差:实际轨迹和规划轨迹的偏差。大量路测中的调参工作,很多都是在降低这个误差。

我建议关注控制模块时多思考一个问题:为什么不能直接把规划轨迹当作真值发下去?因为车辆动力学模型有延迟、轮胎非线性、地面摩擦变化等因素,导致实际执行永远有偏差,控制模块的任务就是不断缩小这个偏差。

7. 新手最容易踩的坑,以及我的排查建议

7.1 Docker权限和镜像问题

最常见的第一道坎是permission denied。症状是执行docker命令报权限错误,原因就是当前用户不在docker组里。解决方法是执行usermod -aG docker后重新登录。

另一个常见问题是镜像拉取失败或超时。Apollo镜像有几个G,国内网络环境下偶发中断很常见。建议多试几次,或者配置Docker镜像加速器。如果中途中断导致镜像半成品,可以清理后重新拉取:

docker image prune

7.2 编译报错却不知道去哪查

很多人第一次编译失败后,习惯把报错信息复制到搜索引擎搜。但Apollo报错信息上下文长、话很多,直接搜往往搜不到有效结果。

我的经验是先看是哪一步报错:是配置阶段(configure)失败、编译阶段失败,还是链接阶段失败。不同阶段问题原因差异很大。配置失败通常是依赖缺失或版本不兼容;编译失败多为语法错误或头文件缺失;链接失败常和库路径有关。定位到阶段后再去翻Apollo的GitHub Issues,往往能找到答案。

7.3 DreamView页面打不开或显示不全

出现这种问题,先检查bootstrap.sh是不是真的启动成功了,日志里有没有报错。最常见的原因是宿主机和Docker容器的端口映射没生效,8888端口没暴露出来。

另外,DreamView对浏览器的支持有差异,我用Chrome和Edge遇到过渲染不一致的情况。建议固定用Chrome,并把硬件加速打开。

7.4 磁盘空间审计

Apollo编译会持续占用磁盘空间,每次重新编译还会产生新的缓存。如果分区的空闲空间不足,编译会在最耗时的阶段突然失败。建议定期清理构建中间产物:

rm -rf /root/.cache/bazel/* # 清理Bazel缓存

这个方法非常管用,能腾出大量空间,但代价是下次编译变慢,需要权衡。

7.5 我的学习路径建议

最后分享我对入门路径的看法。直接啃源码是效率最低的方式,除非你已经很熟C++和ROS/CyberRT框架。更合理的顺序是:

  1. 先跑通Demo,理解整体流程和模块分工
  2. 阅读Apollo官方技术文档和代码结构文档
  3. 选一个你最感兴趣的模块(我建议先选规划或控制),只精读这个模块的代码路径
  4. 用CyberRT工具观察该模块的输入输出消息,对比代码做验证
  5. 改一个小参数或加一个小逻辑,观察仿真效果变化

我当年选择从规划模块切入,因为规划输出的轨迹在DreamView里是可见的,改动参数后效果能直接看到,反馈最快。这种"改代码-看效果-再改"的循环,是学习自动驾驶系统最好的正反馈。

Apollo的路还很长,从编译到跑通只是万里长征第一步。但只要能把这个系统从里到外啃下来,你对自动驾驶的认知深度,已经超过大多数停留在概念层面的人。

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

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

立即咨询