第一个安全的固件改动¶
第一次改动只修改PX4官方示例模块的一条只读诊断文字,不接触状态估计、控制器、解锁、失联和执行器输出。目标是完整走通“固定版本→理解构建注册→修改→测试→SITL验证→回滚”,而不是展示复杂算法。

固定实验边界¶
本篇只适用于:
项目:PX4-Autopilot
发布:v1.17.0
提交:d6f12ad1c4f70ad3230afd7d86e971421e02fef4
目标:px4_sitl / sihsim_quadx
改动:hello示例的一条只读控制台文字
硬件:不需要真实飞控
飞行:不进行
如果当前提交、文件内容或构建目标不同,停止照抄,改用该版本官方文档重新确认。
前置条件¶
已经完成源码版本、开发环境与可复现构建,并满足:
make tests通过;make px4_sitl sihsim_quadx能够进入pxh>;- 输入
hello start能够看到hello、五次Doing work...和goodbye; git status --short为空。
基线不通过时,不进入修改。
第一步:确认源码如何进入目标¶
依次检查:
src/examples/hello/hello_main.cpp
src/examples/hello/hello_start.cpp
src/examples/hello/CMakeLists.txt
src/examples/hello/Kconfig
boards/px4/sitl/default.px4board
四者的关系是:
hello_main.cpp定义任务执行时打印的文字;hello_start.cpp定义hello start、hello stop和hello status命令;CMakeLists.txt通过px4_add_module()注册模块和源文件;Kconfig定义EXAMPLES_HELLO开关;- SITL的
default.px4board设置CONFIG_EXAMPLES_HELLO=y; - 构建系统把模块加入目标,运行后在
pxh>注册hello命令。
可以直接验证:
rg "hello_main|EXAMPLES_HELLO|examples__hello" \
src/examples/hello \
boards/px4/sitl/default.px4board
第二步:只修改诊断文字¶
在src/examples/hello/hello_main.cpp中找到:
本次目标差异是把它改为:
期望差异只有这一行:
可以手工编辑,也可以使用下面的补丁方法,二者只能选择一种。推荐把补丁保存在源码目录之外再应用:
EVIDENCE_DIR="../px4-evidence/first-safe-change-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$EVIDENCE_DIR"
cat > "$EVIDENCE_DIR/read-only-diagnostic.patch" <<'PATCH'
diff --git a/src/examples/hello/hello_main.cpp b/src/examples/hello/hello_main.cpp
--- a/src/examples/hello/hello_main.cpp
+++ b/src/examples/hello/hello_main.cpp
@@ -48,7 +48,7 @@ int PX4_MAIN(int argc, char **argv)
{
px4::init(argc, argv, "hello");
- printf("hello\n");
+ printf("learning-build: read-only diagnostic v1\n");
HelloExample hello;
hello.main();
PATCH
git apply --check "$EVIDENCE_DIR/read-only-diagnostic.patch"
git apply "$EVIDENCE_DIR/read-only-diagnostic.patch"
检查:
通过标准:
git diff --check退出码为0;- 只有目标源文件被修改;
- 差异不包含控制、参数、消息定义或板级配置。
第三步:运行主机测试¶
set -o pipefail
make tests 2>&1 | tee "$EVIDENCE_DIR/tests-modified.log"
TEST_STATUS=${PIPESTATUS[0]}
printf 'tests_exit=%s\n' "$TEST_STATUS" | \
tee "$EVIDENCE_DIR/tests-modified.exit"
test "$TEST_STATUS" -eq 0 || exit "$TEST_STATUS"
通过标准:
tests_exit=0;- 没有新增失败测试;
- 若测试输出包含原有跳过项,应记录名称,不能把跳过当成通过。
本次一行文字修改通常不会触发专门的行为测试,但全量主机测试可以发现构建和公共依赖回归。
第四步:重新构建并启动SITL¶
set -o pipefail
make px4_sitl sihsim_quadx 2>&1 | \
tee "$EVIDENCE_DIR/sitl-modified.log"
SIM_STATUS=${PIPESTATUS[0]}
printf 'sitl_exit=%s\n' "$SIM_STATUS" | \
tee "$EVIDENCE_DIR/sitl-modified.exit"
test "$SIM_STATUS" -eq 0 || exit "$SIM_STATUS"
sha256sum build/px4_sitl_default/bin/px4 | \
tee "$EVIDENCE_DIR/px4-modified.sha256"
出现pxh>后执行:
通过标准:
ver all报告的源码版本来自当前工作副本;- 第一条应用输出精确包含
learning-build: read-only diagnostic v1; - 随后仍每两秒输出一次
Doing work...,共五次; - 最后输出
goodbye; - 没有
ERROR、崩溃或无法退出; - 输入
shutdown后仿真正常结束。
仿真退出后脚本会记录make一侧的退出码,不能只采用tee的退出码。
若仍显示hello,说明运行的不是刚构建的产物,或者构建缓存、工作目录和启动命令不一致。
第五步:记录证据¶
git rev-parse HEAD > "$EVIDENCE_DIR/head.txt"
git submodule status --recursive > "$EVIDENCE_DIR/submodules.txt"
git diff --check > "$EVIDENCE_DIR/diff-check.txt"
git diff -- src/examples/hello/hello_main.cpp \
> "$EVIDENCE_DIR/applied.patch"
git status --short > "$EVIDENCE_DIR/status-before-rollback.txt"
同时保存:
make tests结果;- SITL构建和启动结果;
ver all输出;hello start输出;- 主机系统和工具链版本;
- 测试日期。
证据目录位于源码仓库的上一级,不会让git status --short出现额外文件。回滚前先确认applied.patch内容正确且非空。
第六步:回滚并复测¶
先再次确认只有预期文件被修改:
确认后恢复该文件:
再运行:
ROLLBACK_DIR="$EVIDENCE_DIR/rollback"
mkdir -p "$ROLLBACK_DIR"
git status --short | tee "$ROLLBACK_DIR/status.txt"
set -o pipefail
make tests 2>&1 | tee "$ROLLBACK_DIR/tests.log"
ROLLBACK_TEST_STATUS=${PIPESTATUS[0]}
printf 'tests_exit=%s\n' "$ROLLBACK_TEST_STATUS" | \
tee "$ROLLBACK_DIR/tests.exit"
test "$ROLLBACK_TEST_STATUS" -eq 0 || \
exit "$ROLLBACK_TEST_STATUS"
make px4_sitl sihsim_quadx 2>&1 | \
tee "$ROLLBACK_DIR/sitl.log"
ROLLBACK_SITL_STATUS=${PIPESTATUS[0]}
printf 'sitl_exit=%s\n' "$ROLLBACK_SITL_STATUS" | \
tee "$ROLLBACK_DIR/sitl.exit"
test "$ROLLBACK_SITL_STATUS" -eq 0 || \
exit "$ROLLBACK_SITL_STATUS"
sha256sum build/px4_sitl_default/bin/px4 | \
tee "$ROLLBACK_DIR/px4.sha256"
出现pxh>后:
应等待goodbye出现后再执行shutdown。
通过标准:
git status --short恢复为空;make tests退出码为0;ver all再次对应固定提交;hello start再次输出原始hello;- 模块仍能输出五次
Doing work...和goodbye。 - 基线、修改版和回滚版均保存了测试、SITL、版本和SHA-256记录;
- 若基线与回滚产物不是逐字节相同,必须记录构建时间、路径或工具链等差异来源,不能只凭功能输出宣称位级可复现。
不要在存在其他未提交修改时直接执行恢复命令。先核对目标文件和差异,避免删除不属于本练习的工作。
本练习证明了什么¶
完成后可以证明:
- 使用了明确的源码和子模块版本;
- 能找到源文件、构建注册、Kconfig和目标配置之间的关系;
- 能运行主机测试;
- 能构建并启动SITL;
- 能确认运行的是修改后的产物;
- 能恢复基线并再次验证。
它不能证明:
- 真实目标板能够启动;
- 传感器和执行器驱动正确;
- 目标硬件满足实时期限;
- 控制算法发生任何改善;
- 自编译固件已经可以带桨飞行。
进阶练习:只读时间统计¶
只有在完成上述闭环后,才考虑增加时间统计。必须分开记录:
- CPU执行时间;
- 函数开始到返回的墙钟执行时间;
- 实际开始相对计划释放时刻的释放抖动;
- 计划释放时刻到完成时刻的任务响应时间;
- 传感器采样到执行器命令生效的端到端延迟。
截止期限违约应使用:
仅测量函数入口到返回的elapsed_us,无法发现任务在开始执行前已经晚释放。SITL可以验证统计逻辑,但主机调度结果不能替代目标飞控上的实时测量。
硬件扩展的前置门禁¶
本练习在SITL结束。若后续要刷写真实飞控,必须先完成:
- 确认精确硬件型号和板级目标;
- 备份原始参数、校准和已验证固件;
- 阅读该板卡官方上传与恢复说明;
- 准备正确的USB、引导模式或SWD恢复路径;
- 拆除螺旋桨;
- 使用稳定供电;
- 定义上传成功、启动成功和回滚成功标志。
这些步骤属于后续组装与首次配置专题,不能因为SITL通过而跳过。
检查理解¶
- 为什么本练习固定到PX4 v1.17.0和具体提交?
- 哪些源码、构建和目标文件共同让
hello进入SITL目标? - 如何证明运行的是修改后的产物?
- 为什么SITL通过不能证明目标飞控满足实时期限?
- 回滚前为什么必须再次检查工作区差异?