ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Apollo自动驾驶平台入门实操:从环境搭建到跑通仿真全流程

Apollo自动驾驶平台入门实操:从环境搭建到跑通仿真全流程 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显卡。感知模块的深度学习推理需要CUDAGPU也用于渲染。显存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.047.0以上对应20.049.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的时候建议加上--depth1参数只拉取最近一次提交避免把整个历史都拖下来——这个仓库的完整历史非常大全量clone非常耗时。git clone --depth1 https://github.com/ApolloAuto/apollo.git cd apollo然后切换到目标版本分支。比如我用的5.0版本git checkout v6.0.0这里有个坑如果先拉的最新分支再切到老版本分支代码版本可能和子模块不同步。所以更稳妥做法是直接拉指定分支git clone --depth1 --branch v6.0.0 https://github.com/ApolloAuto/apollo.git3.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引入了aemApollo Environment Manager流程变成了./apollo.sh build aem start aem enteraem把镜像构建和环境管理做成了更统一的工具用起来更省心。但无论哪种方式本质都是一样的让代码目录挂载进一个预装好所有依赖的容器里所有编译运行都在容器内完成。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 --jobs4另外还有一类原因是磁盘空间不够。编译产物非常占空间如果分区快满了也会编到一半报错。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 prune7.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框架。更合理的顺序是先跑通Demo理解整体流程和模块分工阅读Apollo官方技术文档和代码结构文档选一个你最感兴趣的模块我建议先选规划或控制只精读这个模块的代码路径用CyberRT工具观察该模块的输入输出消息对比代码做验证改一个小参数或加一个小逻辑观察仿真效果变化我当年选择从规划模块切入因为规划输出的轨迹在DreamView里是可见的改动参数后效果能直接看到反馈最快。这种改代码-看效果-再改的循环是学习自动驾驶系统最好的正反馈。Apollo的路还很长从编译到跑通只是万里长征第一步。但只要能把这个系统从里到外啃下来你对自动驾驶的认知深度已经超过大多数停留在概念层面的人。
返回列表