1. 为什么 Apollo 6.0 的安装值得单独写一篇
Apollo 6.0 是百度 Apollo 自动驾驶平台一个比较特殊的版本节点。它不像后面 7.0、8.0 那样把很多依赖打包得更完整,也不像 5.5 那样资料满地都是。6.0 卡在一个"功能已经比较全,但环境依赖还相当原始"的位置上,尤其是它默认绑定的 Ubuntu 18.04 环境,和现在大多数人手头用的 Ubuntu 20.04 之间存在不少差异。
我自己第一次在 Ubuntu 20.04 上装 Apollo 6.0 的时候,前后折腾了差不多两天。官方文档写的是 18.04,社区里关于 20.04 的帖子又零散,很多坑得自己踩一遍才知道。所以这篇东西就是把我踩过的坑、验证过的步骤、以及一些"为什么这么干"的逻辑整理出来,给同样想在 20.04 上跑 Apollo 6.0 的人一个可以直接抄的作业。
这篇文章适合几类人:一是做自动驾驶相关开发、需要本地跑仿真和感知模块的工程师;二是高校里做相关课题、需要搭环境的学生;三是单纯想看看 Apollo 这套东西长什么样、值不值得投入时间的技术爱好者。不管你基础如何,只要你会用 Linux 基本命令,跟着走应该都能装起来。
需要先说明一点:Apollo 6.0 的安装本质上不是"装一个软件",而是"搭一整套开发环境"。它涉及 Docker、GPU 驱动、CUDA、镜像拉取、容器内编译这一整条链路。任何一环出问题,后面都会卡住。所以我会按这条链路的顺序来讲,而不是按官方文档那种"先装这个再装那个"的流水账。
2. 整体方案设计与环境选型思路
2.1 为什么用 Docker 而不是裸机安装
Apollo 官方从很早的版本开始就主推 Docker 方案,这不是偷懒,而是有实际考量的。Apollo 依赖的东西太多了:特定版本的 CUDA、特定版本的 Protobuf、一堆第三方库、还有它自己魔改过的一些组件。如果你在裸机上装,这些依赖会和你系统里已有的东西打架,装完 Apollo 可能你别的开发环境就废了。
Docker 的好处是把这套环境整个封在一个容器里,容器内的 Ubuntu 版本、CUDA 版本、各种库的版本都是 Apollo 自己定好的,和你宿主机的环境隔离。你宿主机是 20.04 还是 22.04,理论上都不影响容器内部。这也是为什么"Ubuntu 20.04 装 Apollo 6.0"这件事本身是可行的——因为真正跑 Apollo 的是容器里的 18.04。
但这里有个关键点:宿主机和容器之间要共享 GPU、要共享显示、要共享文件。这三件事是 Docker 方案里最容易出问题的地方,也是我后面会重点讲的。
2.2 宿主机 Ubuntu 20.04 的版本选择
Ubuntu 20.04 有小版本之分,比如 20.04.1 到 20.04.6。我实测下来,20.04.3 之后的版本对 NVIDIA 驱动的支持更顺一些,因为内核版本相对新。如果你还在用 20.04.0 或者 20.04.1,建议先sudo apt update && sudo apt upgrade把系统更新一下,主要是更新内核和基础库。
桌面版还是服务器版?如果你需要跑 Dreamview 这种可视化工具,那必须用桌面版,因为要有图形界面。如果你只跑命令行下的模块,服务器版也行,但后面看仿真结果会麻烦。我建议直接用桌面版,省事。
2.3 GPU 还是 CPU
Apollo 6.0 支持纯 CPU 运行,但感知模块里很多模型默认是要 GPU 的。如果你只是想让系统跑起来、看看 Dreamview 界面、跑跑规划控制模块,CPU 版本够用。但如果你想跑感知、想用摄像头和激光雷达数据,那 GPU 是必须的。
GPU 的话,NVIDIA 的卡基本都行,但驱动版本有讲究。Apollo 6.0 容器内用的是 CUDA 10.2 或者 11.x(取决于你拉哪个镜像),对应的宿主机驱动不能太老。我建议宿主机驱动版本在 470 以上,这样兼容性最好。太新的驱动(比如 535 以上)有时候反而会有兼容问题,这个后面会讲。
2.4 磁盘和内存的硬性要求
这个必须提前说,因为很多人是装到一半才发现空间不够。Apollo 6.0 的 Docker 镜像解压后大概 20 到 30 GB,加上编译产物、地图数据、日志,整个下来建议预留100 GB 以上的磁盘空间。我见过有人用 50 GB 的虚拟机装,编译到一半磁盘满了,非常痛苦。
内存建议 16 GB 起步,8 GB 会非常卡,编译的时候可能直接 OOM。如果你要跑仿真,32 GB 会更舒服。
3. 核心依赖的安装与配置细节
3.1 Docker 和 NVIDIA Container Toolkit 的安装
Docker 的安装本身不复杂,但有几个细节要注意。首先是不要用 Ubuntu 自带的docker.io包,那个版本太老,对 GPU 支持不好。要用 Docker 官方的仓库装。
# 卸载可能存在的旧版本 sudo apt remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release # 添加 Docker 官方 GPG key sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加仓库 echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin装完之后,把当前用户加到 docker 组,这样不用每次都 sudo:
sudo usermod -aG docker $USER newgrp docker然后是 NVIDIA Container Toolkit,这个是让容器能用上 GPU 的关键:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt update sudo apt install -y nvidia-docker2 sudo systemctl restart docker装完之后一定要验证:
docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi如果能看到显卡信息,说明 GPU 透传没问题。如果报错,八成是驱动版本和 toolkit 版本不匹配,或者 docker 服务没重启。
注意:这一步验证非常重要,不要跳过。很多人后面容器里跑不起来 GPU,就是因为这一步没验证,等到编译完才发现,浪费时间。
3.2 NVIDIA 驱动的版本选择
驱动这块我要单独说,因为它是整个安装里最容易出玄学问题的地方。Apollo 6.0 的容器里 CUDA 版本一般是 10.2 或 11.1,对应的宿主机驱动要求是 440 以上。但驱动不是越新越好。
我实测下来,470 到 495 这个区间最稳。我试过 515 和 525,容器里跑nvidia-smi有时候会报 "Failed to initialize NVML: Driver/library version mismatch",这是因为宿主机驱动和容器内 CUDA 运行时的版本对不上。虽然有时候重启一下就好了,但很烦。
安装驱动最简单的方式是用 Ubuntu 的"附加驱动"工具,图形界面里选一个 470 或 495 的版本装上就行。命令行的话:
ubuntu-drivers devices sudo apt install nvidia-driver-470装完重启,nvidia-smi能正常输出就 OK。
3.3 拉取 Apollo 6.0 的镜像
Apollo 的镜像在它的镜像仓库里,国内拉取速度是个问题。我一般会配置一下 Docker 的镜像加速,但这里不展开讲加速的事,因为各家情况不一样,你自己找适合的源就行。
拉取命令:
docker pull apolloauto/apollo:dev-x86_64-18.04-20210624_2000这个 tag 是 6.0 对应的一个比较稳定的开发镜像。注意 tag 里的日期,不同日期的镜像内容会有差异,我用的这个是验证过能跑通的。
拉完之后docker images看一下,镜像大小应该在 10 GB 以上,如果只有几百 MB,说明拉错了或者拉了个基础镜像。
3.4 克隆 Apollo 源码
镜像只是运行环境,源码还得单独拉。Apollo 6.0 对应的源码分支是v6.0.0:
git clone https://github.com/ApolloAuto/apollo.git cd apollo git checkout v6.0.0这里有个坑:Apollo 的仓库很大,克隆慢不说,有时候还会因为网络问题中断。如果中断了,用git clone --depth 1 -b v6.0.0只拉最新的一次提交,会快很多,但后面如果要切分支就不方便。看你需求。
克隆完之后,源码目录大概 2 GB 左右。这个目录后面会挂载到容器里,所以放在你磁盘空间充足的地方。
4. 容器启动与编译的完整实操
4.1 启动容器的正确姿势
Apollo 源码目录里有个docker/scripts/dev_start.sh脚本,官方推荐用它启动。但这个脚本默认的一些参数在 20.04 上可能不太合适,我一般会手动改一下或者直接用 docker 命令起。
用脚本的话:
cd apollo bash docker/scripts/dev_start.sh这个脚本会做几件事:检查镜像、启动容器、挂载源码目录、配置显示。但它默认可能用的是 CPU 模式,如果你有 GPU,要加参数:
bash docker/scripts/dev_start.sh -gpu启动之后,用docker ps应该能看到一个叫apollo_dev_<username>的容器在跑。
如果脚本报错,我建议直接手动起容器,这样可控性更强:
docker run -it \ --name apollo_dev \ --gpus all \ --privileged \ -v /home/$USER/apollo:/apollo \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -e DISPLAY=$DISPLAY \ -e QT_X11_NO_MITSHM=1 \ --net=host \ apolloauto/apollo:dev-x86_64-18.04-20210624_2000 \ /bin/bash这里几个参数解释一下:--gpus all是 GPU 透传,--privileged是给容器足够权限(Apollo 里有些操作需要),-v是挂载源码和 X11 socket,--net=host是让容器和宿主机共享网络(Dreamview 的 web 界面需要)。
提示:
-e DISPLAY=$DISPLAY和 X11 socket 挂载是为了让容器里的图形程序能显示到宿主机屏幕上。如果你不需要图形界面,这两个可以去掉。
4.2 进入容器后的环境初始化
容器起来之后,你会在容器的 bash 里。第一件事是进到/apollo目录,然后跑环境配置脚本:
cd /apollo bash docker/scripts/dev_into.sh等等,这里要说明一下:dev_into.sh是在宿主机上跑的,用来进入已经启动的容器。如果你已经手动docker run -it进去了,就不用再跑这个。这个脚本主要是给用dev_start.sh启动的场景用的。
进入容器后,先确认一下环境:
nvidia-smi如果能看到显卡,说明 GPU 透传成功。然后:
ls /apollo应该能看到modules、scripts、tools这些目录。
4.3 编译 Apollo 6.0
编译是整个安装里最耗时的一步,也是最容易出问题的一步。Apollo 用的是 Bazel 构建系统,编译命令是:
./apollo.sh build但这个命令会编译所有模块,非常慢,我第一次编译花了将近两个小时。如果你只需要特定模块,可以:
./apollo.sh build_opt_gpu # GPU 优化版本 ./apollo.sh build_cpu # CPU 版本编译过程中常见的几个问题:
问题一:内存不足。Bazel 默认会开很多并行任务,16 GB 内存的机器可能会 OOM。解决办法是限制并行数:
./apollo.sh build --jobs=4问题二:网络超时。Bazel 编译时会下载一些依赖,国内网络可能超时。这个比较麻烦,一般多试几次,或者配置代理(这里不展开)。
问题三:Protobuf 版本冲突。这个在 20.04 宿主机上一般不会遇到,因为编译是在容器里进行的,容器里的 Protobuf 版本是固定的。但如果你之前手动在容器里装过东西,可能会冲突。
编译成功的标志是最后输出Build succeeded或者类似的提示。如果中途报错,仔细看错误信息,大部分是依赖缺失或者版本问题。
4.4 编译产物的验证
编译完之后,可以跑一个简单的测试:
./scripts/bootstrap.sh start这个命令会启动 Dreamview。然后在宿主机浏览器里打开http://localhost:8888,如果能看到 Dreamview 的界面,说明基本环境没问题。
Dreamview 是 Apollo 的可视化工具,能看到车辆模型、地图、模块状态这些。第一次打开可能会提示没有地图,这个正常,后面加载地图数据就行。
5. 常见问题排查与避坑经验
5.1 容器内 GPU 不可用
这是最常见的问题。表现是容器里nvidia-smi报错,或者编译时提示找不到 CUDA。
排查思路:先在宿主机上确认nvidia-smi正常,然后确认nvidia-docker2装了,再确认 docker 服务重启过。如果都正常,试试:
docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi如果这个能跑通,说明是 Apollo 容器启动参数的问题,检查--gpus all有没有加。
5.2 Dreamview 打不开
Dreamview 默认监听 8888 端口。打不开的话,先确认容器是用--net=host启动的,然后确认容器里 Dreamview 进程在跑:
ps aux | grep dreamview如果没有,手动启动:
./scripts/bootstrap.sh start还有一个可能是防火墙拦了 8888 端口,检查一下。
5.3 编译报 "No space left on device"
磁盘满了。Apollo 编译产物很大,加上 Docker 镜像,很容易把磁盘塞满。清理方法:
# 清理 Docker 无用镜像和容器 docker system prune -a # 清理 Bazel 缓存 bazel clean --expunge但最根本的还是提前预留足够空间。
5.4 显示相关的报错
如果容器里跑图形程序报 "cannot connect to X server",检查 X11 socket 挂载和 DISPLAY 环境变量。另外,宿主机上要允许 X11 连接:
xhost +local:docker这个命令每次重启宿主机后都要重新跑一次。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 容器内 nvidia-smi 报错 | GPU 透传失败 | 检查 --gpus 参数和 nvidia-docker2 |
| Dreamview 打不开 | 端口未监听或网络模式不对 | 用 --net=host,检查 8888 端口 |
| 编译 OOM | 内存不足 | 限制 Bazel 并行数 --jobs=4 |
| 磁盘满 | 空间不足 | 清理 Docker 和 Bazel 缓存 |
| 图形程序报 X server 错误 | X11 未配置 | xhost +local:docker,检查 DISPLAY |
| 编译超时 | 网络问题 | 多试几次,或配置依赖源 |
5.6 几个我踩过的坑
第一个坑是驱动版本。我一开始用的是 525 的驱动,容器里跑着跑着就报 NVML 错误,重启容器才好,过一会又报。后来降到 470,就再也没出现过。所以驱动不是越新越好,稳定最重要。
第二个坑是源码目录的权限。如果你用 sudo 跑过一些命令,源码目录里可能会有 root 权限的文件,容器里用普通用户就改不了。解决办法是sudo chown -R $USER:$USER /home/$USER/apollo。
第三个坑是编译时的并行数。我 16 GB 内存的机器,默认并行编译直接 OOM,后来改成--jobs=4才过。如果你内存更小,可能要--jobs=2。
第四个坑是地图数据。Apollo 自带的地图数据在modules/map/data下,但有些版本里是空的,需要单独下载。没有地图的话,Dreamview 里很多功能用不了。
6. 装完之后能干什么
环境装好只是第一步,Apollo 6.0 能做的事情挺多的。你可以跑仿真,用./scripts/bootstrap.sh启动 Dreamview 后,加载一个 demo 地图,然后跑规划控制模块,看车辆在虚拟环境里跑起来。也可以接真实的传感器数据,跑感知模块,看它怎么识别车道线、车辆、行人。
如果你做算法开发,Apollo 的模块化设计让你可以单独替换某个模块,比如把默认的规划算法换成你自己的,然后对比效果。这也是很多人用 Apollo 的原因——它提供了一个完整的框架,你只需要关注自己那部分。
不过要提醒一句,Apollo 6.0 毕竟是几年前的版本了,有些依赖和现在的系统确实有代差。如果你不是非 6.0 不可,其实可以考虑更新的版本,环境配置会省心很多。但如果你就是要在 6.0 上做东西,那这套流程应该是能跑通的。
我个人在实际操作中的体会是,Apollo 这类大型平台的安装,难点从来不在某一条命令,而在于整条链路的连贯性。Docker、驱动、CUDA、编译,每一环都要对,错一环就卡住。所以装的时候不要跳步,每一步都验证一下,比装完再回头找问题要省时间得多。