1. 为什么版本对比要从安装环节就开始较真
很多人看源码学习笔记,习惯性跳过安装部署这一章,觉得“环境搭起来就行了,重点在代码逻辑”。我一开始也是这个心态,结果在Apollo 5.0和5.5之间来回切换测试的时候,被环境差异坑了整整两天。后来我才意识到,安装过程本身就是版本差异的第一手线索——依赖版本、目录结构、启动脚本、默认配置,这些东西的变化直接反映了架构演进的方向。
这篇笔记的核心,是把我实际搭建Apollo 5.0和Apollo 5.5两套环境、跑通基础测试的完整过程记录下来。Apollo是面向自动驾驶领域的开源平台,5.0和5.5虽然只差一个小版本号,但在安装依赖、编译方式、测试工具链上都有不少值得注意的区别。如果你正在做版本迁移评估,或者想同时保留两套环境做对比学习,这篇内容应该能帮你省下不少折腾的时间。
需要提前说明的是,我用的基础环境是Ubuntu 18.04,这是两个版本官方都比较推荐的系统版本。硬件方面,CPU编译对内存要求比较高,建议至少16GB,硬盘预留50GB以上空间。显卡方面,如果只是跑基础测试和源码阅读,不跑感知模型的GPU推理,其实可以不依赖独立显卡,但涉及CUDA相关的模块编译时需要提前装好驱动。
下面我会从环境准备、依赖安装、编译差异、测试验证、常见报错这几个维度,把两个版本的安装过程拆开来讲。每个步骤我都会说明“为什么这么做”,以及“不做会怎样”,方便你根据自己的实际情况做取舍。
2. 两个版本的基础环境准备与依赖差异
2.1 系统层面的前置检查
在动手装任何东西之前,我习惯先把系统状态摸清楚。这一步看起来简单,但能避免后面很多莫名其妙的错误。
首先是确认Ubuntu版本和内核版本:
lsb_release -a uname -rApollo 5.0对内核版本的要求相对宽松,4.15以上基本都能跑。Apollo 5.5在官方文档里虽然没有强制要求更高内核,但实际测试下来,如果内核低于4.15,Docker相关的网络配置会出现一些奇怪的问题。我建议直接用Ubuntu 18.04自带的4.15内核,省心。
然后是检查CPU是否支持AVX指令集:
grep -o 'avx[^ ]*' /proc/cpuinfo | sort -u这个检查很关键。Apollo的很多计算模块编译时会用到AVX指令集优化,如果CPU不支持,编译到一半会报“illegal instruction”直接崩掉。我遇到过一台老服务器,CPU是早期至强系列,不支持AVX2,结果编译Apollo 5.5的时候在某个数学库那里卡死了。后来换了台支持AVX2的机器才顺利通过。
内存和硬盘的检查也不能省:
free -h df -h编译Apollo 5.5的时候,峰值内存占用能到12GB左右,如果内存不够,编译过程会被系统OOM killer直接杀掉,而且报错信息很不明显,你可能以为是代码问题,其实是内存不够。硬盘方面,两个版本的Docker镜像加上编译产物,50GB是比较稳妥的预留。
2.2 Docker安装的版本选择
Apollo从5.0开始就全面转向Docker化部署了,所以Docker的安装是绕不开的一步。这里有个细节值得注意:Apollo 5.0和5.5对Docker版本的要求略有不同。
Apollo 5.0官方推荐Docker 19.03,而Apollo 5.5在文档里提到了对Docker 20.10的兼容性更好。我实际测试下来,Docker 19.03在两个版本上都能跑,但如果你打算用5.5的一些新特性,比如更灵活的容器网络配置,建议直接上20.10。
安装Docker的步骤网上很多,我就不逐行贴了,重点说几个容易出问题的地方:
第一,安装完成后一定要把当前用户加入docker组:
sudo usermod -aG docker $USER然后必须重新登录才能生效。我见过有人加完组之后直接在当前终端继续操作,结果一直报权限错误,折腾了半天才发现是没重新登录。
第二,配置国内镜像加速。Apollo的Docker镜像体积不小,5.0的基础镜像大概2GB多,5.5因为集成了更多工具,接近3GB。如果不配加速,拉取过程可能会非常慢甚至超时。
第三,检查Docker的存储驱动。默认的overlay2在大多数情况下没问题,但如果你的系统之前装过其他容器工具,可能会残留一些配置导致冲突。用下面的命令确认:
docker info | grep "Storage Driver"2.3 依赖库的版本锁定
Apollo 5.0和5.5在依赖库上的差异,是我在安装过程中感受最明显的地方。5.0的依赖相对“干净”,很多库的版本要求比较宽松;5.5则明显收紧了不少,尤其是和感知、预测相关的模块,对某些数学库和通信库的版本有明确要求。
我整理了一个关键依赖的对比表格,方便你直观看到差异:
| 依赖项 | Apollo 5.0 要求 | Apollo 5.5 要求 | 备注 |
|---|---|---|---|
| Bazel | 0.5.4 或更高 | 0.5.4 或更高 | 实际编译建议用0.5.4 |
| Protobuf | 3.6.1 | 3.6.1 | 两个版本一致 |
| Eigen | 3.3.4 | 3.3.7 | 5.5对Eigen版本更敏感 |
| OpenCV | 3.4.2 | 3.4.5 | 5.5的感知模块依赖更高版本 |
| CUDA | 9.0 | 10.0 | 如果启用GPU推理 |
| PCL | 1.8.1 | 1.9.1 | 点云库版本有差异 |
这个表格里的信息,一部分来自官方文档,一部分是我在实际编译报错后反推出来的。比如Eigen的版本差异,我在编译5.5的某个规划模块时遇到了模板报错,查了半天才发现是Eigen 3.3.4和3.3.7在某个矩阵运算的API上有细微变化。
注意:如果你打算在同一台机器上同时保留5.0和5.5的编译环境,强烈建议用Docker隔离,不要试图在宿主机上装两套依赖。我试过在宿主机上手动切换依赖版本,结果把系统里其他项目的环境搞乱了,得不偿失。
3. 源码获取与Docker镜像构建的实操细节
3.1 源码下载的两种方式及选择建议
Apollo的源码托管在代码托管平台上,获取方式有两种:直接克隆主仓库,或者下载特定版本的发布包。
对于5.0和5.5的对比学习,我建议分别克隆两个版本的分支,而不是只克隆主分支再切换。原因是主分支的代码变动很快,有些中间状态的提交可能编译不过,而发布分支相对稳定。
克隆的时候有个技巧:加上--depth 1参数只拉取最新的一次提交,可以大幅减少下载时间和磁盘占用。因为源码仓库的历史提交非常多,完整克隆可能要下载好几个GB的数据。
git clone --depth 1 -b v5.0.0 <仓库地址> apollo-5.0 git clone --depth 1 -b v5.5.0 <仓库地址> apollo-5.5下载完成后,检查一下目录结构。5.0和5.5在顶层目录上基本一致,但5.5多了一个tools目录下的一些新脚本,后面会提到。
3.2 Docker镜像构建的差异与加速技巧
Apollo的Docker镜像构建脚本在docker/目录下,两个版本都提供了build_docker.sh脚本。但5.5的脚本里增加了一些参数选项,比如可以指定是否包含GPU支持、是否启用某些调试工具。
构建5.0镜像的命令:
cd apollo-5.0 bash docker/build_docker.sh构建5.5镜像的命令类似,但如果你需要GPU支持,要加参数:
cd apollo-5.5 bash docker/build_docker.sh --gpu这里有个非常关键的坑:构建过程中会从网络下载大量依赖包,如果网络不稳定,构建到一半失败,重新构建时Docker的缓存机制可能会导致一些奇怪的问题。我的经验是,如果构建失败超过两次,先执行docker system prune清理掉悬空镜像和缓存,再重新构建。
另外,构建时间方面,5.0在16核CPU的机器上大约需要25分钟,5.5因为依赖更多,大约需要35分钟。如果超过这个时间还没完成,大概率是卡在某个网络下载环节了,可以另开终端用docker stats看看容器的资源占用情况。
构建完成后,用下面的命令确认镜像存在:
docker images | grep apollo你应该能看到类似apollo:5.0.0和apollo:5.5.0的镜像标签。
3.3 进入容器后的初始化操作
镜像构建好之后,用官方提供的脚本进入容器:
bash docker/scripts/dev_start.sh bash docker/scripts/dev_into.sh进入容器后,第一件事是执行环境初始化脚本。5.0和5.5在这一步的差异不大,都是运行:
./apollo.sh build但注意,这个命令默认是完整编译,耗时很长。如果你只是想先跑通测试,可以用更轻量的方式:
./apollo.sh build_opt或者只编译特定模块:
./apollo.sh build_planning我建议第一次先完整编译一次,确保环境没问题,之后再按需编译单个模块。
提示:编译过程中如果看到大量警告信息,不用太紧张,Apollo的代码库比较大,有些第三方库的警告是正常的。真正需要关注的是“error”级别的信息,尤其是链接阶段的错误。
4. 编译过程中的版本差异与典型报错处理
4.1 Bazel编译参数的调整
Apollo使用Bazel作为构建工具,5.0和5.5的.bazelrc配置文件有一些差异。5.5增加了一些编译优化选项,同时也对某些模块的编译并发度做了限制。
如果你在编译5.5时遇到内存不足的问题,可以手动调整并发度:
./apollo.sh build --jobs 4默认情况下Bazel会根据CPU核心数自动设置并发度,但内存不够的时候,降低并发度反而能避免OOM。
5.0的编译相对“温和”,一般不需要调整参数。但如果你在编译5.0时遇到Protobuf相关的报错,检查一下是不是系统里装了多个版本的Protobuf,导致Bazel找到了错误的版本。
4.2 常见报错及排查思路
我在编译两个版本的过程中,遇到了几个典型的报错,这里把排查过程记录下来,方便你对照。
报错一:Eigen版本不兼容
在编译5.5的某个规划模块时,出现了大量的模板实例化错误,报错信息里提到了Eigen的某个矩阵运算函数。排查后发现是容器内的Eigen版本和代码期望的不一致。解决办法是在Dockerfile里明确指定Eigen版本,或者手动在容器内安装正确版本。
报错二:CUDA相关模块编译失败
如果你启用了GPU支持,但宿主机的CUDA驱动版本和容器内的CUDA版本不匹配,编译时会报“no CUDA-capable device detected”。这时候需要确认宿主机的驱动版本是否满足容器内CUDA版本的最低要求。5.5用的是CUDA 10.0,要求宿主机驱动版本不低于410。
报错三:链接阶段找不到某个库
这种报错通常是因为依赖库没有正确安装,或者Bazel的缓存里残留了旧版本的库文件。解决办法是先清理Bazel缓存:
bazel clean --expunge然后重新编译。
报错四:Python依赖冲突
Apollo的某些工具脚本依赖Python,5.0默认用Python 2.7,5.5开始逐步向Python 3迁移。如果你在容器里手动装了一些Python包,可能会和系统自带的版本冲突。建议不要在容器内随意用pip安装包,而是通过修改Dockerfile来添加依赖。
4.3 编译产物的目录结构对比
编译完成后,两个版本的产物目录结构基本一致,都在bazel-bin/下。但5.5增加了一些新的模块目录,比如和预测、仿真相关的工具。
我对比了一下两个版本的bazel-bin/modules/目录,5.0的模块数量相对少一些,5.5增加了几个新的子模块。这个差异在后续做功能对比的时候会很有用,你可以直观看到哪些模块是5.5新增的。
5. 测试验证:从基础功能到模块级验证
5.1 基础环境测试
编译完成后,先跑一下基础的环境测试,确认核心库都能正常工作。
Apollo提供了一个简单的测试脚本:
./apollo.sh test这个命令会运行所有模块的单元测试,耗时比较长。如果只是想快速验证环境,可以只跑某个模块的测试:
./bazel-bin/modules/common/common_test5.0和5.5在测试脚本的命名上基本一致,但5.5增加了一些新的测试用例,尤其是和感知、预测相关的模块。
5.2 模块级功能验证
对于源码学习来说,跑通单元测试只是第一步。更重要的是验证具体模块的功能是否正常。
以规划模块为例,5.0和5.5都提供了离线测试工具。你可以用录制好的数据包回放,观察规划模块的输出。
5.0的规划模块测试命令:
./bazel-bin/modules/planning/planning_component_test5.5的对应命令类似,但增加了一些参数选项,比如可以指定不同的规划场景。
我在测试过程中发现,5.5的规划模块在默认配置下,对某些场景的处理逻辑和5.0有明显差异。这个差异在源码层面也能找到对应,5.5的规划器增加了一些新的场景决策逻辑。
5.3 可视化工具的使用
Apollo提供了一个基于Web的可视化工具,可以实时查看各个模块的输出。5.0和5.5都支持这个工具,但5.5的界面做了一些优化,增加了更多的调试信息。
启动可视化工具的命令:
./scripts/bootstrap.sh start然后在浏览器里访问本地端口。注意,如果你是在远程服务器上跑Apollo,需要做端口转发才能访问。
可视化工具对于理解模块之间的数据流非常有帮助。我建议在跑测试的时候同时打开可视化界面,观察数据在各个模块之间的流转过程。
6. 两套环境共存时的资源管理与切换技巧
6.1 Docker容器的命名与隔离
如果你像我一样,在同一台机器上同时保留5.0和5.5的环境,Docker容器的命名就很重要。默认情况下,两个版本的启动脚本都会创建名为apollo_dev的容器,后启动的会覆盖先启动的。
解决办法是在启动脚本里指定不同的容器名称。你可以手动修改dev_start.sh脚本,或者在启动时通过环境变量指定。
我自己的做法是,给两个版本的启动脚本分别做了副本,修改了容器名称和端口映射,这样两套环境可以同时运行,互不干扰。
6.2 端口冲突的处理
Apollo的某些工具会占用固定端口,比如可视化工具默认用8888端口。如果两套环境同时运行,端口会冲突。
解决办法是修改其中一套环境的端口配置。在5.5的配置文件里,端口设置比5.0更灵活,可以通过命令行参数直接指定。
6.3 磁盘空间与镜像清理
两套环境加上各自的编译产物,磁盘占用很容易超过80GB。定期清理不用的镜像和缓存是必要的。
清理Docker悬空镜像:
docker image prune清理Bazel缓存:
bazel clean但注意,清理Bazel缓存后,下次编译需要重新编译所有依赖,耗时很长。建议在磁盘空间充足的情况下保留缓存。
7. 从安装差异看两个版本的架构演进
7.1 依赖变化反映的模块重构
回过头来看,5.0到5.5在安装依赖上的变化,其实反映了底层模块的一些重构。比如Eigen版本的提升,对应的是感知和规划模块中矩阵运算的优化;OpenCV版本的升级,对应的是图像处理管线的调整。
这些变化在源码层面都能找到对应。如果你在做版本对比学习,建议把安装差异作为一个切入点,顺着依赖变化去追踪对应的代码改动,往往能发现一些官方文档里没提到的细节。
7.2 工具链完善带来的调试便利
5.5在工具链上的完善是我感受比较深的。5.0的调试工具相对基础,很多问题需要自己加日志来排查。5.5增加了一些新的调试工具和可视化选项,对于理解模块内部状态帮助很大。
比如5.5的规划模块增加了一个轨迹对比工具,可以同时显示多个规划器的输出结果。这个工具在5.0里是没有的,需要自己手动实现类似功能。
7.3 对源码学习路径的影响
从安装和测试的角度来看,如果你是第一次接触Apollo,我建议从5.0开始。5.0的依赖更少,编译更快,报错信息也相对简单,适合先建立整体认知。等你对核心模块有了基本了解,再切换到5.5,对比新增的功能和优化。
如果你已经有了一定的Apollo使用经验,直接上5.5也没问题,但要做好心理准备,编译和调试过程中可能会遇到更多依赖相关的问题。
我个人在实际操作中的体会是,安装过程虽然繁琐,但每一步的报错和解决过程,其实都是在帮你理解这个系统的架构。那些依赖关系、编译选项、模块划分,都是架构设计的外在表现。把安装过程中遇到的问题记录下来,回头再看源码的时候,很多设计决策就变得顺理成章了。
最后分享一个小技巧:在容器里编译的时候,可以用tee命令把编译日志同时输出到文件,方便事后排查:
./apollo.sh build 2>&1 | tee build.log这样即使终端滚屏错过了报错信息,也能在日志文件里找到。