ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Ubuntu 20.04下Apollo源码编译与GPU环境配置全攻略

Ubuntu 20.04下Apollo源码编译与GPU环境配置全攻略 直接进入正题。如果你手头正好有一台Ubuntu 20.04的机器又想从源码把Apollo自动驾驶平台完整编译一遍并且顺便把GPU环境一次配好这篇教程就是给你写的。我从环境规划、驱动安装到编译命令、踩坑记录全部按自己实际操作过的流程来梳理尽量做到每一步都能直接对着敲。Apollo这个平台官方是建议直接拉Docker镜像用的日常跑仿真、做算法验证都没问题。但只要你打算改感知模块里的模型推理代码、自定义传感器驱动或者需要针对自己的硬件调优编译参数就必须从源码编译。源码编译能拿到完整的中层接口和底层依赖关系二次开发心里才真正有底。这个教程适合有一定Linux基础、准备入坑自动驾驶开发或者已经在做相关项目的同学GPU环境配置也会分步骤讲清楚。1. 动手之前先想清楚Apollo源码编译的整体思路1.1 为什么推荐在Ubuntu 20.04上做源码编译Apollo官方文档里明确支持Ubuntu 20.04 LTS并且从Apollo 6.0之后的版本基本都以20.04作为主力开发和测试环境。用20.04不只是“能用”而是整个依赖链都卡在这个版本的系统库上。比如Apollo内置的Cyber RT通信框架依赖特定版本的glog、protobuf、abseil-cpp这些库如果系统版本太新或者太旧编译期会出现各种ABI不兼容的报错。Ubuntu 20.04刚好把这些基础依赖锁在了一个稳定区间内。另外20.04的生命周期到2025年官方源和第三方源都还在正常维护驱动和编译工具链的兼容性问题少很多。如果你手头是22.04也不是完全不能搞但需要自己处理不少补丁适合有经验的人折腾。新手入门直接上20.04是最省心的选择。1.2 源码编译的完整流程拆解Apollo源码编译并不是直接在当前系统里make它的做法是把所有编译工具和依赖都封装在一个Docker容器里这个容器叫做dev container。你在宿主机上做的只是准备环境、启动容器、进入容器然后在容器内部执行编译脚本。所以整个流程分为四大块宿主机环境准备Ubuntu 20.04系统、基础软件包、Docker环境。GPU环境配置安装NVIDIA驱动、配置nvidia-container-toolkit保证容器里能调用显卡。源码获取与容器启动下载Apollo源码用官方脚本启动开发容器。在容器内编译执行./apollo.sh build_gpu之类的编译命令等编译完成。这个设计的好处是宿主机上不需要手动安装CUDA、CUDNN、TensorRT这些重量级依赖容器镜像里全都带好了。你只需要保证宿主机显卡驱动够新让容器里的CUDA能访问到GPU就行。很多人在这一步被卡住后面会详细说明。1.3 硬件配置建议与磁盘规划Apollo编译是个比较吃资源的事情尤其是感知、预测这类模块包含大量模板代码和深度学习算子编译时会同时起多个编译任务内存和CPU占用都相当高。我实测建议的配置是这样的CPU8核16线程以上编译时间能压到可接受范围。如果只有4核编译过程会非常痛苦耗时可能翻两三倍。内存至少16GB建议32GB。编译时十几个g进程同时跑每个占据1-2GB内存很常见16GB以下容易出现OOM强行杀进程。磁盘源码编译缓存至少需要80GB可用空间建议预留120GB。Apollo的编译缓存和Docker镜像体积都不小尤其是build目录里的中间文件动辄几十GB。显卡如果你只是编译不做训练显卡要求不高但GPU环境配置最好还是做好。NVIDIA驱动建议450以上CUDA版本由容器决定一般是11.1或11.4。磁盘规划这里要特别提醒一句不要把源码放在空间不够的分区。我见过不少人在默认的home分区只有50GB的机器上硬解压源码编译到一半报磁盘满。建议提前用df -h检查一下磁盘空间必要时把源码放到独立挂载的数据盘上并且给Docker的data-root也留够空间。2. Ubuntu 20.04系统与GPU环境配置2.1 系统安装与基础软件包配置如果你已经装好了Ubuntu 20.04可以跳过系统安装部分但有几个基础项必须确认。首先是系统更新。刚装完的系统建议先跑一遍sudo apt update sudo apt upgrade -y把内核、基础库全部更新到最新避免后续因为系统库太老导致驱动编译或者容器运行出问题。然后是几个编译和下载必需的软件包sudo apt install -y git vim curl wget build-essential \ net-tools gnupg2 ca-certificates lsb-release \ software-properties-common这些包里面build-essential在宿主机编译阶段不一定用到但后续排查问题、装一些辅助工具时经常需要。software-properties-common则用于添加PPA软件源比如后续安装Docker时就会用到。另外建议把系统python环境理清楚。Ubuntu 20.04自带python3.8Apollo的脚本会调用这个默认版本不要去系统里乱装Python 3.10或者3.11也不要随意修改/usr/bin/python3的软链指向。我在实际维护环境时遇到过用户把python3软链改到别的版本结果Apollo的启动脚本直接报语法错误排查了很久才发现是这个原因。如果机器需要远程开发顺手把SSHD配置好sudo apt install -y openssh-server sudo systemctl enable ssh sudo systemctl start ssh2.2 安装NVIDIA驱动别装错版本也别慌这一步是整个GPU环境配置里最容易出问题的地方。Apollo容器内部已经打包好了CUDA和CUDNN宿主机不需要安装完整CUDA工具包但要有一个足够新、能识别你显卡的NVIDIA驱动。我推荐的安装方式是用ubuntu-drivers自动检测并安装推荐版而不是去官网手动下载runfile。自动方式装出来的驱动和内核模块配合得更好重启后不容易出现模块加载失败的问题。sudo ubuntu-drivers autoinstall这条命令会根据你的显卡型号自动安装合适的驱动。装完重启sudo reboot重启后用nvidia-smi验证nvidia-smi输出里能看到驱动的版本号比如520.61.05并且能看到显卡型号和显存信息说明驱动已经正常工作了。这里说说版本这事。热搜词里提到nvidia 520这个版本它是支持Ubuntu 20.04的而且对RTX 30系、40系显卡支持都挺好。如果你是老显卡比如GTX 1080 Ti、1660这类520驱动也能兼容但如果你遇到驱动版本过新导致的老卡显存识别异常可以考虑装回460或470系列这些版本在自动驾驶社区里验证得最多。还有一个非常常见的坑驱动装好了nvidia-smi正常但Docker容器里跑nvidia-smi却提示找不到设备。这个一般不是驱动问题而是后面要说的nvidia-container-toolkit没有装或者配置不对。建议在宿主机验证驱动之后再确认一下内核模块lsmod | grep nvidia输出中应该能看到nvidia、nvidia_uvm、nvidia_drm等模块如果只有nvidia没有nvidia_uvmDocker容器里基本上没法用GPU。2.3 Docker与nvidia-container-toolkit配置Apollo的整个编译环境都依赖Docker所以这一步必须仔细。Docker安装可以直接套用官方源curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo deb [archamd64 signed-by/usr/share/keyrings/docker-archive-keyring.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组这样后面执行Apollo脚本时不用每次都加sudosudo usermod -aG docker $USER newgrp docker注意执行newgrp docker后当前终端就生效了不需要重启。如果终端重新登录后依然报权限问题就重新开一个终端或者注销再登录一次。然后安装nvidia-container-toolkit。这个是让Docker容器能够访问宿主GPU的桥梁没有它即使驱动装得再好容器里的Apollo也感知不到显卡。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-container-toolkit sudo systemctl restart docker这里详细说一下为什么需要这个toolkit。Docker容器和宿主机共享内核但设备访问是靠cgroup和设备映射来管理的。nvidia-container-toolkit做的事情就是在你启动容器时自动检测NVIDIA_VISIBLE_DEVICES环境变量然后把宿主机上的GPU设备和相关驱动库挂载进容器。没有这层挂载容器里即使有CUDA库也找不到物理设备。为了验证配置成功可以跑一个测试容器docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi如果能看到类似宿主机一样的nvidia-smi输出说明GPU环境已经打通了。3. Apollo源码获取与编译实操3.1 获取Apollo源码与目录结构说明源码获取在Apollo GitHub仓库页面上可以直接clone但国内网络克隆大仓库经常不稳定我建议优先用两个方式方式一git clone官方仓库git clone https://github.com/ApolloAuto/apollo.git这个仓库包含完整历史体积比较大。如果只需要指定版本可以加--branch v7.0.0 --depth 1只拉取该版本的单层commit能省不少时间和流量。方式二如果github速度太慢可以考虑从镜像仓库拉代码或者下载官方发布的源码压缩包。我在实际下载中觉得镜像站更稳但要注意选择与目标版本一致的标签别拉错版本。源码目录结构需要简单梳理一下这样后续编译遇到问题时能快速定位modules/核心功能模块包括perception、planning、control、prediction、localization等。cyber/Apollo自研的通信中间件Cyber RT整个系统的消息通信和调度都依赖它。docker/Docker脚本和镜像配置文件。scripts/常用启动和部署脚本。apollo.sh编译入口脚本所有编译操作都通过它完成。WORKSPACEBazel编译系统的顶层配置里面定义了外部依赖库的下载地址和版本。建议clone完成后先切换到你想用的版本。我这里的示例以Apollo 7.0.0为主因为它在感知、规划模块上有较多更新并且社区资料最丰富。cd apollo git checkout v7.0.0如果你需要旧版本或者更新的版本注意对应好编译脚本的差异。Apollo 7.0和Apollo 6.0在构建流程上区别不大但Apollo 9.0之后的版本改用了新的构建工具链命令会不一样。本文主要针对7.0/8.0这一类经典版本。3.2 启动Apollo开发容器先理解dev_start前后发生了什么Apollo官方封装了一个专门用于开发和编译的Docker镜像里面预装好了所有编译依赖包括Bazel、CUDA、CUDNN、TensorRT等。不用手动去挨个安装这些重依赖只要用脚本启动容器即可。进入源码目录后执行bash docker/scripts/dev_start.sh -g这里-g参数非常重要它表示把GPU参数传给Docker容器Docker会通过--gpus all把宿主机显卡透传到容器内部。如果漏掉-g即使宿主机驱动正常容器里也无法使用GPU。这个脚本实际做这么几件事测试Docker环境是否可用。拉取Apollo开发镜像比如apolloauto/apollo:dev-x86_64-20.04-7.0.0如果本地没有会先从远程拉取。创建一个名为apollo_dev_username的容器并把源码目录挂载到容器内的/apollo。配置容器的网络模式和数据卷方便共享编译缓存。设置--gpus all参数让容器能分配GPU资源。启动成功会看到[OK] Apollo Development Environment Ready之类的提示。如果报镜像拉取失败一般是网络问题可以多尝试几次或者配置好镜像加速。启动之后是进入容器bash docker/scripts/dev_into.sh执行后终端提示符会变成类似apolloin_dev_docker:/apollo$的形式说明你已经进入容器内部了。值得提醒的是源码目录是宿主机目录直接挂载进容器的所以你在容器里修改代码宿主机上也会同步改变。反过来也一样在宿主机上直接改动源码目录容器里也能看到。开发调试非常方便不需要频繁地拷贝文件。3.3 执行源码编译GPU版与CPU版的取舍进入容器后第一次编译前需要执行资源检查和依赖配置脚本。Apollo提供了一个一键初始化脚本bash setup.sh这个脚本会设置环境变量APOLLO_ROOT_DIR、CYBER_PATH等并且把Cyber RT的Python模块路径加入PYTHONPATH。如果不执行这一步后续运行测试程序时会找不到Cyber模块。真正的编译命令是./apollo.sh build_gpu执行这个命令后Apollo会用Bazel来构建整个工程。第一次编译耗时很长我实测在16核32GB内存的机器上编译Apollo 7.0大概需要40到60分钟CPU核心少的话可能要两小时以上。中间会有大量日志输出如果出现红色ERROR不要慌大多数是可以针对性解决的。如果你不需要GPU加速跑感知模型或者宿主机没有NVIDIA显卡可以执行./apollo.sh build这个版本会把GPU相关的代码如下掉编译时间稍短一些但后续感知模型推理就做不到实时了。对于只是验证编译流程、做规划控制方向开发的用户CPU版其实够用。Apollo还提供了几个更细分的编译目标./apollo.sh build_opt编译release版本启用了编译优化生成的二进制体积更小运行更快。./apollo.sh build_opt_gpurelease版并启用GPU适合部署使用。./apollo.sh build_cpu纯CPU版本。./apollo.sh build_teleop编译远程遥控相关模块。对于日常开发调试建议先编译build_gpu保证所有模块都能正常链接到GPU相关库。编译完成后的输出会放在/apollo/bazel-bin目录下这个是Bazel的默认输出目录所有的可执行文件、共享库都会生成在这里。后续启动DreamView或者跑感知模块时系统默认会从这些目录加载编译产物。编译完成后可以顺手验证一下编译结果ls -l /apollo/bazel-bin/modules/perception/production/如果能列出感知模块的可执行文件和.so库文件说明编译基本成功了。3.4 启动DreamView与运行Demo验证编译成功只是第一步更重要的是验证整个平台能正常跑起来。Apollo提供了一个可视化交互界面DreamView启动方式如下在容器内先启动Apollo后台进程bash scripts/apollo_neo.sh start这个脚本会启动Cyber RT框架、DreamView后台服务和需要的依赖模块。启动后看到类似[OK] Apollo is running的提示说明后台正常。然后在另一个终端中进入容器启动DreamView前端bash scripts/dreamview.sh start之后浏览器访问http://localhost:8888就能看到DreamView的界面了。在界面左侧选择Sim_Control模式再选择地图比如Sunnyvale或Borrowdale加载对应的场景包就可以开始跑仿真了。我第一次启动DreamView时遇到过页面打不开的情况后来发现是8080端口和8888端口问题。Apollo 7.0默认前端端口是8888如果被占用可以改端口或者检查容器网络是否映射正确。可以用netstat -tlnp | grep 8888来确认端口是否在监听。跑通DreamView后整个源码编译加上环境配置的链路就完全打通了后续就可以根据自己的需求修改代码重新通过./apollo.sh build_gpu增量编译需要的模块。4. 常见问题排查与避坑技巧4.1 问题速查表编译和运行过程中遇到的坑一半以上都是环境问题。我把最常见的整理成一张表方便直接对照排查现象可能原因解决办法容器内无法显示nvidia-smi没有安装nvidia-container-toolkit或Docker未重启安装toolkit后执行sudo systemctl restart dockerdev_start.sh拉取镜像超时网络问题配置镜像加速或者挂代理仅用于拉取镜像反复重试编译时提示disk quota exceeded磁盘空间不足清理Bazel缓存或源码目录至少预留80GB编译时g: internal compiler error: Killed内存不足编译进程被OOM杀死减少并行编译任务数./apollo.sh build_gpu --configopt --jobs4Bazel下载依赖失败网络无法访问部分源码仓库多试几次或者提前下载依赖包放到/apollo/.cache目录运行dev_into.sh报容器不存在dev_start没成功或容器被删除重新执行dev_start.sh并确认没有同名容器残留编译后启动DreamView页面空白前端端口未映射或资源包未下载检查Docker端口映射-p 8888:8888用--map参数重新dev_start4.2 网络问题的处理心得Apollo源码编译过程中最大变数其实是网络。Bazel在编译时会根据WORKSPACE文件去下载各种外部依赖包括boost、eigen、opencv、protobuf等源码或二进制包。这些依赖托管在GitHub、Maven、Bazel中央仓库等多个地方国内网络环境下载这些文件经常出现超时。我的经验是分几步处理第一先配置Bazel的下载镜像。可以设置环境变量HTTP_PROXY和HTTPS_PROXY指向可用的内网代理或者直接修改/etc/hosts把GitHub相关的域名IP解析到更快的地址。对于团队开发场景更推荐在内网搭建一个Bazel缓存服务统一管理依赖下载能省掉每个人重复踩坑的时间。第二利用Bazel的分布式缓存。如果之前成功编译过编译中间产物会缓存在~/.cache/bazel目录。重装系统或换机器后可以把这个缓存目录整体拷贝到新机器上能跳过大量重复编译。第三多试几次。听起来像废话但Bazel下载失败之后直接重新执行同样的编译命令很多情况下能恢复下载并继续。不要一失败就清理缓存先重跑一次看看。4.3 关于资源不足与OOM的处理内存不足导致的编译失败非常典型。错误日志里通常能看到g: internal compiler error: Killed (program cc1plus)这个Killed就是内存耗尽后系统主动杀掉了编译进程。遇到这种问题最直接的办法是降低编译并行度。Apollo的编译脚本支持通过--jobs参数控制并行任务数./apollo.sh build_gpu --jobs4这样同时只跑4个编译任务内存压力会小很多代价是编译时间变长。如果觉得自己机器配置还可以但依然被杀可以检查一下是不是Docker容器可用的内存太少。Apollo在dev_start.sh里默认不会对容器做内存限制但如果你的Docker版本较老或者自己在启动容器时手动指定了--memory参数就可能会限制容器内可用内存。检查方式docker stats确认容器内存使用率和限制值如果限制存在可以用docker update --memory value container调整。4.4 我踩过的坑GPU环境最隐蔽的问题说了这么多最后分享一个很容易被忽略的细节。我在一次重装环境后驱动也装了toolkit也装了nvidia-smi宿主机显示正常容器内也显示正常但编译完跑感知模型时发现GPU利用率始终为0推理速度慢得离谱。后来排查下来发现是Apollo容器内的CUDA版本和宿主机驱动版本不完全匹配。Apollo 7.0容器内默认CUDA是11.4它要求宿主机驱动至少是470以上。我当时装的是450驱动功能上能识别GPU但CUDA 11.4的运行时在旧驱动上跑不起来只能走CPU回退。所以有个简单的经验如果你用的Apollo版本比较新7.0以上宿主机NVIDIA驱动尽量装到470以上最好直接上nvidia-driver-520或者530一步到位。驱动版本太低虽然不报错但性能会打折。另外还有一个关于环境变量的坑。dev_into.sh进入容器后如果当前shell没有正确加载Apollo的环境变量编译时可能会出现找不到cyber模块的情况。解决办法就是每次进入容器后先跑bash setup.sh这个脚本负责设置APOLLO_ROOT_DIR、LD_LIBRARY_PATH和PYTHONPATH。我个人在实际操作中的体会是Apollo源码编译最考验人的不是编译本身而是环境准备阶段的细节。驱动、Docker、toolkit、磁盘、网络每个环节都得稳任何一环出问题都会在容器启动或编译早期暴露出来。但只要把第二步的宿主环境配置仔细做完后面源码编译和二次开发就会非常顺畅。按照这篇教程的顺序走一遍基本可以避免绝大多数新手会踩的坑。编译通过只是个开始后面改代码、加传感器驱动、训练自己的模型每一步都会比直接拿官方镜像做二次开发要自如得多。
返回列表