4. 开发环境搭建
Cyber RT 是 Apollo 自动驾驶平台的核心中间件,负责节点通信、调度、服务发现与数据融合。要真正进入 Cyber RT 的工程实践,第一步不是写代码,而是把一套可复现、可调试、可编译的开发环境搭起来。本章围绕“源码获取 → 容器化环境 → Bazel 构建 → 编译运行 → 调试准备”这条主线,给出可落地的操作路径与常见问题排查方法。
一、核心原理:为什么 Apollo 选择 Docker + Bazel
Apollo 的依赖体系非常庞大:Protobuf、gRPC、Fast RTPS、OpenCV、PCL、CUDA、Eigen、Boost 等版本互相耦合,直接安装在宿主机上极易出现“版本地狱”。Docker 提供隔离的根文件系统,把 Ubuntu 版本、编译器版本、第三方库版本全部锁定在镜像里;Bazel 则负责增量编译、依赖图管理和跨语言构建。两者结合,使“同一份源码在不同机器上编译出相同结果”成为可能。
Cyber RT 的构建目标通常以 //cyber/... 形式组织,Bazel 会解析 BUILD 文件中的 cc_library、cc_binary、proto_library 等规则,生成依赖 DAG,再按拓扑顺序编译。理解这一点,就能明白为什么“改一个头文件会触发大量重编译”,也能理解缓存目录 ~/.cache/bazel 的重要性。
二、方法流程:从零到可编译的五步操作
- 获取源码:从 Apollo 官方仓库克隆,并切换到与 Cyber RT 匹配的分支。建议使用
--depth=1减少下载量。 - 启动 Docker 容器:使用 Apollo 提供的
apolloauto/apollo镜像,挂载源码目录,并映射显示与调试端口。 - 配置 Bazel:确认
WORKSPACE存在,检查.bazelrc中的编译选项与缓存路径。 - 编译 Cyber RT:执行
bazel build //cyber/...,观察依赖解析与编译输出。 - 运行与调试:启动示例节点,使用
cyber_monitor、cyber_visualizer和 GDB 进行验证。
# 1. 获取源码
git clone --depth=1 -b master https://github.com/ApolloAuto/apollo.git
cd apollo
# 2. 启动 Docker 容器
docker run -it --name apollo_dev \
-v $(pwd):/apollo \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=$DISPLAY \
apolloauto/apollo:dev-latest bash
# 3. 进入容器后配置 Bazel
cd /apollo
bazel version
# 4. 编译 Cyber RT
bazel build //cyber/... --jobs=4
# 5. 运行示例
bazel run //cyber/examples:talker
bazel run //cyber/examples:listener
三、案例场景:编译一个 Cyber RT 最小节点
假设我们要在 cyber/examples 下新增一个最小发布者节点。首先编写 BUILD 文件,声明 cc_binary 并依赖 //cyber;然后编写 talker.cc,使用 cyber::Init、CreateWriter 和 Node 完成消息发布。编译命令如下:
bazel build //cyber/examples:talker
bazel run //cyber/examples:talker
如果编译成功但运行时报“channel not found”,通常是因为 CYBER_IP 或 CYBER_DOMAIN_ID 未设置,导致节点无法加入同一通信域。此时可在容器内执行:
export CYBER_IP=127.0.0.1
export CYBER_DOMAIN_ID=80
bazel run //cyber/examples:talker
CYBER_IP、CYBER_DOMAIN_ID 写入 ~/.bashrc,避免每次手动设置。调试阶段还可以打开 GLOG_v=4 查看详细日志。四、误区与复盘:环境搭建中最容易踩的坑
| 误区 | 现象 | 正确做法 |
|---|---|---|
| 直接在宿主机安装所有依赖 | 版本冲突、编译失败、难以复现 | 使用 Apollo 官方 Docker 镜像,锁定环境 |
| 忽略 Bazel 缓存目录权限 | Permission denied,编译中断 | 确保容器内 UID 与宿主机一致,或使用 --output_user_root |
| 不设置 CYBER_DOMAIN_ID | 节点之间无法通信 | 统一设置 CYBER_DOMAIN_ID 与 CYBER_IP |
| 编译并发过高导致 OOM | Bazel 进程被 kill | 使用 --jobs=2 或 --local_ram_resources=2048 |
| 调试时未保留符号信息 | GDB 无法定位源码行 | 编译时使用 -c dbg,并保留 .dwp 文件 |
bazel clean --expunge 后仍然编译失败,优先检查 WORKSPACE 中的外部依赖 URL 是否可访问。国内网络环境下,建议配置镜像源或使用离线依赖包,否则会在拉取 com_github_grpc_grpc 等外部仓库时长时间卡住。