源码版本、开发环境与可复现构建¶
开发环境的第一目标不是“在我的电脑上编译成功”,而是能够回答:谁在什么环境中,用哪一份源码、哪些依赖和哪个板级目标,生成了哪一个固件文件。

建立隔离的工作副本¶
不要直接在唯一的稳定源码副本或主分支上实验。基本流程是:
- 从项目官方仓库取得源码;
- 核对仓库地址,避免使用来源不明的镜像;
- 切换到明确的发布标签或提交;
- 初始化并固定子模块;
- 创建本地开发分支;
- 记录工作区是否存在未提交修改。
通用Git记录命令示例:
分支名不能替代提交哈希,因为分支指针会移动。发布标签也应同时记录其最终解析到的提交。
本专题的固定教学基线¶
以下示范固定为PX4 v1.17.0,对应提交d6f12ad1c4f70ad3230afd7d86e971421e02fef4。该版本只用于让本文命令可复查,不表示任何任务都应选择PX4或该版本。
本专题参考主机为Ubuntu 24.04 x86_64。克隆前先检查:
uname -a
df -h .
command -v git curl bash python3
git --version
curl --version
python3 --version
sudo -v
curl -I https://github.com/PX4/PX4-Autopilot
教学环境建议预留至少20GB可用空间,以容纳源码、依赖、主机测试和多个构建目录。任一基础命令缺失、sudo -v失败、网络无法访问官方仓库或磁盘空间不足时,先修复主机环境。
Ubuntu上缺少Git、curl或ripgrep时可以先执行:
git clone --branch v1.17.0 --recursive \
https://github.com/PX4/PX4-Autopilot.git
cd PX4-Autopilot
git rev-parse HEAD
git tag --points-at HEAD
git submodule status --recursive
git status --short
git switch -c learning/read-only-diagnostic
核验结果应满足:
git rev-parse HEAD输出上述完整提交;git tag --points-at HEAD包含v1.17.0;- 子模块每行应以空格开头;
- 新建分支后
git status --short为空。
如果任一结果不同,先记录实际版本并解释差异,不继续套用本专题后面的固定示例。
git submodule status --recursive的首字符含义为:
| 首字符 | 含义 | 处理 |
|---|---|---|
| 空格 | 当前子模块提交与父仓库记录一致 | 通过 |
- |
子模块尚未初始化 | 初始化后复核 |
+ |
当前子模块提交与父仓库记录不一致 | 同步并更新到记录提交 |
U |
子模块存在合并冲突 | 停止,不继续构建 |
对-或+可执行:
git submodule sync --recursive
git submodule update --init --recursive
git submodule status --recursive
若仍出现+,检查是否在子模块中存在主动检出的工作;若出现U或任何不属于自己的修改,不得强制覆盖,应修复冲突或重新取得干净工作副本。
不要混用项目的构建说明¶
不同飞控项目可能使用不同工具:
| 项目 | 常见构建入口 | 必须以什么为准 |
|---|---|---|
| Betaflight | GNU Make及项目提供的工具链安装目标 | 当前分支的开发文档和Makefile |
| INAV | CMake、Ninja或项目包装命令 | 当前分支的开发目录和目标定义 |
| ArduPilot | Waf构建系统 | 当前分支开发文档和waf目标列表 |
| PX4 | CMake及Make包装入口 | 当前分支开发环境、板级目标和仿真文档 |
网上旧文章中的命令可能仍能运行,也可能构建出错误目标或缺少新依赖。命令、目录和工具链版本必须与正在使用的源码分支配套。
工具与产物速查¶
| 名称 | 作用 |
|---|---|
| 工具链 | 编译器、汇编器、链接器、标准库和二进制工具的组合 |
| Make、CMake、Ninja、Waf | 读取项目构建定义并组织编译、链接和测试 |
| 容器镜像摘要 | 标识不可变容器内容,比可移动标签更适合复现 |
| 编译数据库 | 记录每个源文件的实际编译命令,常供编辑器和静态分析使用 |
| 可执行与可链接格式(ELF,Executable and Linkable Format)带符号文件 | 保留代码、数据、符号和调试信息,用于定位崩溃 |
| 映射文件 | 展示符号被链接到的地址和占用空间 |
| 栈预算 | 每个任务调用链和局部变量可使用的栈空间上界 |
| 堆预算 | 运行时动态分配可使用的内存上界 |
构建环境记录¶
建议为每次可验证构建保存:
| 字段 | 示例内容 |
|---|---|
| 主机系统 | 操作系统名称、版本和处理器架构 |
| 容器或虚拟环境 | 镜像名称及不可变摘要 |
| 编译器 | 名称、完整版本和目标架构 |
| 构建工具 | Make、CMake、Ninja、Waf等版本 |
| 脚本 | 项目提供的环境安装脚本提交 |
| 板级目标 | 完整目标名称和硬件版本 |
| 构建选项 | 功能开关、优化级别和自定义定义 |
| 源码 | 主仓库和全部子模块提交 |
| 输出 | 文件名、大小和SHA-256校验值 |
安全散列算法256位(SHA-256,Secure Hash Algorithm 256-bit)校验值可以识别产物是否发生变化,但不能证明产物安全或来源可信。
先构建未修改基线¶
修改任何代码前:
- 按官方文档准备工具链;
- 构建项目支持的仿真目标或主机测试;
- 构建目标飞控板的官方配置;
- 记录编译警告、产物大小和校验值;
- 运行项目已有测试;
- 确认未修改基线可以启动或进入仿真。
如果官方基线不能构建,先修复环境问题。不要同时修改业务代码,否则无法区分环境错误和代码错误。
可执行的PX4软件在环基线¶
以下命令以官方支持的Ubuntu 24.04开发环境为例。安装脚本会修改系统软件包,执行前应先查看脚本,并在专用开发机、虚拟机或容器中运行;macOS和其他受支持环境应改用对应版本的官方安装页。
cd PX4-Autopilot
mkdir -p ../px4-evidence
set -o pipefail
bash ./Tools/setup/ubuntu.sh 2>&1 | \
tee ../px4-evidence/toolchain-install.log
INSTALL_STATUS=${PIPESTATUS[0]}
printf 'installer_exit=%s\n' "$INSTALL_STATUS"
test "$INSTALL_STATUS" -eq 0 || exit "$INSTALL_STATUS"
installer_exit=0才表示安装脚本成功。包下载、网络、权限或空间错误应先根据日志中的第一条错误处理,不要直接继续构建。
安装完成并重新打开终端后核验:
任何命令不存在时,回到对应主机的PX4 v1.17开发环境文档,不使用来源不明的工具链补丁。然后运行:
cd PX4-Autopilot
BASELINE_DIR="../px4-evidence/baseline-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BASELINE_DIR"
set -o pipefail
make tests 2>&1 | tee "$BASELINE_DIR/tests.log"
TEST_STATUS=${PIPESTATUS[0]}
printf 'tests_exit=%s\n' "$TEST_STATUS" | \
tee "$BASELINE_DIR/tests.exit"
test "$TEST_STATUS" -eq 0 || exit "$TEST_STATUS"
make px4_sitl sihsim_quadx 2>&1 | \
tee "$BASELINE_DIR/sitl.log"
SITL_STATUS=${PIPESTATUS[0]}
printf 'sitl_exit=%s\n' "$SITL_STATUS" | \
tee "$BASELINE_DIR/sitl.exit"
test "$SITL_STATUS" -eq 0 || exit "$SITL_STATUS"
sha256sum build/px4_sitl_default/bin/px4 | \
tee "$BASELINE_DIR/px4.sha256"
git rev-parse HEAD > "$BASELINE_DIR/head.txt"
git submodule status --recursive > "$BASELINE_DIR/submodules.txt"
出现pxh>后依次执行:
等待goodbye出现后再执行shutdown。
通过标准:
make tests后的tests_exit为0;- 仿真构建没有编译或链接错误;
- 终端出现
pxh>控制台提示符; - 在控制台执行
ver all能够看到固件与构建信息; - 执行
hello start后出现hello、五次Doing work...和goodbye; - 执行
shutdown后仿真正常退出。
SITL_STATUS和SHA-256命令会在shutdown退出仿真后继续执行。BASELINE_DIR位于源码目录之外,保存了测试、仿真、版本、子模块和产物校验值。
硬件内仿真(SIH,Simulation In Hardware)把物理模型作为PX4内部模块运行。这里使用主机上的px4_sitl构建,是为了避开三维仿真器依赖并先验证源码、工具链和飞控进程;此时仍属于软件在环,不代表真实飞控硬件已经验证。
目标板固件只能在已确定真实板级目标后构建。可先列出当前提交中的目标:
不要为了得到一个固件文件而随意选择名称相近的目标。
目标名称必须来自当前源码¶
板级目标名称可能因版本变化而新增、重命名、合并或移除。应从当前提交的目标列表、构建帮助或板级目录中取得名称,不凭商品名称猜测。
需要同时核对:
- 厂商与型号;
- 硬件版本;
- 处理器和闪存容量;
- 引导加载程序;
- 板级标识;
- 默认传感器;
- 构建功能裁剪;
- 产物格式。
构建一个“相同处理器”的目标,不能证明产物适用于另一块板。
可复现不一定等于逐字节相同¶
构建时间、绝对路径、编译器版本、链接顺序或嵌入式版本信息可能让二进制字节不同。因此要区分:
- 过程可复现:同一记录可以再次完成构建;
- 功能可复现:测试结果和运行行为一致;
- 位级可复现:产物每个字节完全一致。
安全发布应尽量追求位级可复现;做不到时,至少解释差异来源,并保证源码、工具链、选项和测试可追溯。
构建输出不能只保存一个固件文件¶
建议归档:
- 固件镜像;
- 带符号的可执行文件;
- 映射文件;
- 编译数据库;
- 版本清单;
- 构建日志;
- 内存占用报告;
- 测试报告;
- SHA-256校验值;
- 对应参数和硬件清单。
带符号文件和映射文件对崩溃地址解析、栈分析和内存布局检查很重要,不能只保留用于刷写的压缩镜像。
检查资源变化¶
每次改动至少比较:
- 闪存占用;
- 静态随机存取存储器占用;
- 栈和堆预算;
- 中断和任务数量;
- 日志带宽;
- CPU负载;
- 控制周期和截止期限违约;
- 启动时间。
“只增加一个日志字段”也可能改变存储、协议、CPU和带宽。
构建记录模板¶
检查理解¶
- 为什么分支名不能替代提交哈希?
- 为什么修改代码前必须先构建未修改基线?
- 板级目标名称为什么不能根据商品名称猜测?
- 过程可复现和位级可复现有什么区别?
- 为什么应保留带符号文件和映射文件?