第二章 源码获取与编译:Git仓库管理、分支选择策略、waf编译系统详解、常见编译错误及解决
好,咱们直接进入正题。源码获取与编译,是每个ArduPilot开发者都要过的第一关。我见过太多新手在这上面卡好几天,甚至有人因为分支选错,飞控刷上去直接炸机。嗯,这节课我就把这几年的经验掰开揉碎了讲给你听。
2.1 Git仓库管理:别把仓库搞乱了
ArduPilot的官方仓库在GitHub上,地址是 https://github.com/ArduPilot/ardupilot。但直接 git clone 完事?没那么简单。
我个人习惯的克隆方式是这样的:
git clone --recursive https://github.com/ArduPilot/ardupilot.git
cd ardupilot
git submodule update --init --recursive --force
为什么要加 --recursive?因为ArduPilot依赖很多第三方库,比如 mavlink、uavcan,它们都是以子模块形式存在的。如果你忘了这一步,编译时会报一堆找不到头文件的错误。
git clone --depth=1 只拉取最新版本。但注意,深度克隆后切换分支会比较麻烦。
2.2 分支选择策略:选错了真会炸机
ArduPilot的分支策略,说白了就是「稳定优先,尝鲜谨慎」。我见过有人直接拿 master 分支去飞,结果遇到一个未修复的bug,飞机直接翻跟头。
| 分支名称 | 用途 | 风险等级 | 我的建议 |
|---|---|---|---|
master |
开发主线,每天都有新提交 | 高 | 仅用于开发测试,别上真机 |
Copter-4.5.x |
稳定版本分支,经过充分测试 | 低 | 量产或正式飞行首选 |
Plane-4.5.x |
固定翼稳定分支 | 低 | 固定翼项目用这个 |
Sub-4.5.x |
水下机器人稳定分支 | 低 | 水下项目专用 |
beta |
候选发布版,接近正式版 | 中 | 可以测试新功能,但要做好回退准备 |
我的选择策略很简单:
- 正式项目: 用最新的稳定分支,比如
Copter-4.5.x。别追新,稳定第一。 - 功能验证: 用
beta分支,但一定要在仿真环境里跑几天。 - 二次开发: 基于稳定分支创建自己的特性分支,比如
my-feature-from-4.5。
master 上做二次开发!你想想看,master每天都有变动,你今天改的代码,明天可能就跟上游冲突了。我曾经有个同事,直接在master上改了一个月,结果合并时发现冲突多到无法解决,最后只能重来。
2.3 waf编译系统详解:别怕,它没那么神秘
ArduPilot用waf作为编译系统,而不是常见的CMake或Makefile。我第一次接触waf时也觉得奇怪,但用久了发现,它其实更适合嵌入式交叉编译的场景。
waf的核心命令就几个,我列出来:
# 配置编译环境,选择板卡
./waf configure --board Pixhawk1
# 编译指定固件
./waf copter
# 编译并上传
./waf --upload copter
# 清理编译产物
./waf clean
# 查看所有可用板卡
./waf list_boards
这里有个坑:./waf configure 只需要执行一次,除非你换了板卡或者改了工具链。每次编译前不需要重新configure,直接 ./waf copter 就行。
-j 参数,比如 ./waf -j8 copter。我一般设为核心数+1,8核CPU就用 -j9。
waf的编译流程其实很清晰:
- configure阶段: 检查工具链、依赖库,生成编译配置。
- build阶段: 根据配置编译源码,生成 .elf 和 .bin 文件。
- upload阶段: 通过串口或SD卡将固件刷写到飞控板。
下面这张图能帮你理解waf的整个工作流程:
2.4 常见编译错误及解决:这些坑我都踩过
编译错误是家常便饭。我把最常见的几种列出来,你遇到了直接对照解决就行。
错误1:找不到Python模块
ModuleNotFoundError: No module named 'future'
原因: waf依赖一些Python库,比如 future、lxml。
解决:
pip install future lxml pyserial
如果你用的是Python3,记得用 pip3。
错误2:工具链找不到
arm-none-eabi-g++: command not found
原因: 没装ARM交叉编译工具链。
解决:
# Ubuntu/Debian
sudo apt-get install gcc-arm-none-eabi
# macOS
brew install arm-none-eabi-gcc
我个人习惯用 gcc-arm-none-eabi-9-2020-q2-update 这个版本,太新或太旧都可能出问题。
错误3:子模块未更新
fatal: 'mavlink' does not exist
原因: 克隆时忘了 --recursive,或者子模块版本不匹配。
解决:
git submodule update --init --recursive --force
如果还不行,试试:
git submodule foreach --recursive git checkout --force
错误4:编译时内存不足
virtual memory exhausted: Cannot allocate memory
原因: 多核编译时,每个编译线程都会占用内存。如果你用 -j16 但只有8GB内存,很容易爆。
解决: 减少并行数,比如 ./waf -j4 copter。或者加swap空间。
错误5:链接时符号未定义
undefined reference to 'AP_Param::setup_object_defaults'
原因: 通常是代码版本与库版本不匹配。比如你从master分支拉了新代码,但子模块还是旧的。
解决: 先更新子模块,然后 ./waf clean 再重新编译。
git pull 和 git submodule update --recursive,确保代码是最新的。然后 ./waf clean 清理旧产物,再开始编译。虽然多花几十秒,但能避免很多莫名其妙的问题。
2.5 避坑指南:我踩过的那些坑
嗯,最后分享几个我亲身经历过的坑,希望能帮你省点时间。
- 坑1: 我曾经在
master分支上直接改代码,结果第二天上游合并了一个大改动,我的代码全冲突了。从那以后,我每次开发都从稳定分支拉一个特性分支。 - 坑2: 有一次编译通过了,但飞控刷上去后GPS一直搜不到星。查了两天,发现是编译时选错了板卡配置,导致GPS的UART引脚没初始化。所以,
./waf configure --board一定要选对。 - 坑3: 用Windows的Git Bash克隆仓库,结果子模块下载到一半报错,因为路径太长了。后来我改用WSL2,再也没出过这个问题。
好了,这一章的内容就这些。源码获取和编译是基础中的基础,但基础打牢了,后面才能走得稳。记住:分支选稳定,编译前更新,遇到错误先看子模块。做到这三点,你就能避开80%的坑。