前阵子有个朋友问我,想用STM32H7自己画一块飞控板来跑PX4,网上教程东一篇西一篇,不是版本对不上,就是写到“编译通过”就戛然而止,实际移植时却卡在各种诡异现象里。我前后在F4、F7、H7上折腾过PX4,从拿开发板点灯到把自研板飞起来,踩过的坑确实不少。这篇专门针对STM32H7自制飞控,把从零开始移植PX4固件的完整过程写透,尤其把编译、板级配置、传感器枚举、烧录启动这些最容易翻车的环节单独拎出来讲。不管你是刚接触飞控开发的学生,还是想把手里的H7板子跑上PX4的工程师,这篇都能给你一条可以对照执行的路。
1. 为什么非STM32H7不可:选型逻辑与资源门槛
1.1 换到H7,到底图什么
很多人在选型时纠结一个问题:F4已经能被PX4官方支持,为什么非要费劲用H7?我的看法是,如果你的目标是自研飞控并且打算长期迭代,H7不是炫技,是刚需。
STM32H7系列的核心优势首先是算力。以H743为例,Cortex-M7内核跑到480MHz,带双精度FPU,还能开L1缓存,单核性能大概是F427的2到3倍。PX4的EKF2扩展卡尔曼滤波、惯性导航解算、多传感器融合都吃CPU,F4在日志全开、外设挂满的时候帧率会往下掉,H7却能留出不少余量。
其次是存储和外设资源。H743内部就有2MB Flash和1MB SRAM,跑一整套PX4(NuttX内核+驱动+中间件)压力不大。外设方面,多路UART、SPI、I2C、FDCAN、USB OTG都齐全,适合同时接两个IMU、两三个气压计、磁力计和外部数传,这也符合现在飞控“传感器冗余”的设计趋势。
还有一个现实因素:PX4官方对H7板卡的适配已经非常成熟,Pixhawk 6、Pixhawk 6C这些主流参考设计用的都是H7系列,这意味着你在移植时不是“开荒”,而是“有参考答案地做题”。相比之下,F4官方支持链路的代码维护正在边缘化,长期看不如直接上车H7。
1.2 选H743还是H750,要提前定下来
H7家族里最常被拿来DIY的型号是H743和H750。两者引脚兼容,但内部Flash差别很大:H743有2MB,H750只有128KB。PX4完整固件编译出来通常在1MB上下,所以选H750基本就必须外挂QSPI Flash,让固件在外部存储器上运行。
这就带来一连串麻烦:启动时需要先执行一段引导程序完成外部Flash初始化,再跳转到应用;烧录流程也更复杂,片内bootloader和分布式加载的配置都得改。如果你不是对H7外部存储启动机制非常熟悉,第一次移植就选H750,大概率会卡在上电不跑、烧录不进这类问题上。
我的建议是,自制飞控优先选H743,封装选LQFP100或者LQFP144,焊接难度和引脚数量比较平衡,而且2MB内部Flash可以直接沿用官方Pixhawk 6的启动加载方案。先用H743把PX4跑起来、把飞行流程走通,再考虑H750的优化,这是最省精力的路线。
硬件设计上有一点要提醒:H7的电源域和复位时序比F4复杂,板子设计时必须认真参考芯片手册里的上电时序要求,不能照搬F4的电源方案。供电纹波太大、复位信号毛刺,都会导致PX4启动到一半随机死机,这类硬件问题在软件层几乎无解。
2. 环境搭建与源码准备:这一趴几乎是翻车重灾区
2.1 操作系统与工具链版本,先定死再动手
PX4在Linux下开发最省心,我用的是Ubuntu 22.04 LTS。这里必须先说一个容易踩的坑:PX4不同版本对工具链的要求差别很大,网上的教程可能来自老版本,你拿新版本固件去跑旧教程的命令,经常会碰到莫名其妙的编译错误。
不要自己手动去apt安装arm-none-eabi-gcc,因为系统源里的版本往往很新,而PX4/NuttX对编译器版本非常敏感,版本不对会报一堆“internal compiler error”或者链接失败。正确做法是先用官方脚本:
git clone --recursive https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilot bash ./Tools/setup/ubuntu.sh这个脚本会帮你装好所有依赖,包括NuttX工具链、Python依赖、CMake、Ninja等。装完之后确认一下工具链版本:
arm-none-eabi-gcc --version如果最终编译有问题,建议对照官方文档里列出的版本号,尽量用脚本固定的工具链路径,不要自己折腾。
2.2 拉取源码和子模块,训心耐心检查
PX4仓库有很多git子模块,常见的坑是clone时加上了--recursive,但网络波动导致子模块没拉全,编译时才发现少了某个组件。这个没法回避,只能多检查:
git submodule update --init --recursive在你准备移植的板卡之前,先编译一个官方H7目标验证环境是否正常。比如Pixhawk 6对应的目标:
make px4_fmu-v6x_default第一次编译会下载NuttX相关组件,耗时比较长,耐心等。如果这一步能顺利产出固件,说明你的环境没问题,后续移植出的问题都可以去自己的板级配置里找;如果这一步都过不了,问题大概率在环境或工具链上,不用急着碰板级代码。
2.3 版本选择建议:不要盲目追新
PX4迭代很快,我个人的经验是:第一次搞移植选1.14或1.15的稳定分支,不要用main开发分支。main分支代码变动频繁,可能一个函数原型说改就改,你在网上搜到的写法根本对不上。用老一点的版本虽然界面和配置格式老,但它资料多、讨论多,踩坑时有迹可循。
要注意的是,1.13之前的板级配置使用default.cmake,1.14之后改成了default.px4board,目录结构也有变化。如果你看着老教程操作,发现文件找不到,先确认版本差异,别急着怀疑自己。
3. Board移植:让PX4认识你的自研硬件
3.1 从复制官方H7板卡开始
PX4对“一块新板子”的抽象很明确:所有板级信息都放在boards/<厂商>/<板名>目录下。移植不需要从零写,最稳妥的方式是复制一块官方H7板卡作为基底,再改成你自己的引脚定义。
以1.14/1.15版本为例:
cp -r boards/px4/fmu-v6x boards/mycompany/myh7board复制之后,目录下重点文件有这么几个:
default.px4board:编译配置,决定要编译哪些驱动和模块,有点像“菜单”。src/board_config.h:引脚复用定义,是移植的核心。src/board.c:板级初始化,包括时钟、电源、LED等。ROMFS/px4fmu_common/init.d/rc.board_start:启动脚本,负责挂载传感器、开启外设。
这些文件要和你的原理图一一对应,接下来的工作基本就是在这些文件里改改删删。
3.2 引脚复用、时钟和链接脚本:耐心对原理图
board_config.h里最容易出问题的是GPIO定义。你需要对照自己的原理图,把SCK、MISO、MOSI、CS这些SPI引脚,UART的TX/RX,I2C的SDA/SCL,以及PWM输出通道的定时器映射全部理清楚。
这里分享一个我自己的笨办法:把原理图导出成PDF,在board_config.h旁边放一个窗口,一个引脚一个引脚对照,改一行注释一行。千万不要凭记忆批量替换,我曾经因为把SPI2和SPI3的片选搞混,导致两个IMU在系统里反复“探测不到-偶尔出现-又掉线”,排查了两天才发现是脚本里传感器驱动的挂载bus和实际硬件接线不一致。
时钟配置也要检查。H7的时钟树比F4复杂,默认板卡可能用了外部高速晶振,而你的板子上用的可能是不同的晶振频率。如果晶振频率和PLL配置不匹配,系统启动时串口会输出乱码或干脆没反应。板子的实际主频也建议通过mavlink status或控制台命令确认,不要理所当然觉得“我用的H7就是480M”。
链接脚本的调整主要集中在Flash和RAM地址上。如果沿用H743内部Flash,一般可以直接沿用官方Pixhawk 6的布局:bootloader占起始地址,固件应用区从偏移地址开始。如果你改了外部Flash启动,那就要同步调整链接脚本里的FLASH_START等宏,这一步出错的表现通常是“烧录成功但上电没反应”。
3.3 编译目标命名与固件产出
PX4的编译目标名和目录名强相关,boards/mycompany/myh7board对应的编译命令就是:
make mycompany_myh7board_default编译完成后,固件通常位于build/mycompany_myh7board_default/目录下,常见的有.px4格式(给QGroundControl用)和.bin格式(给烧录工具用)。如果你是第一次编译自研板,建议把生成的所有文件列表截图或保存,后续烧录用得着。
还有一个坑:在default.px4board里裁剪驱动时,不要为了“缩小固件”乱删传感器和串口驱动。PX4的很多模块是运行时动态注册的,删掉一个你以为没用的驱动,可能在启动时导致依赖它的模块初始化失败,而且这种失败在日志里往往只是很隐晦的一行。
4. 传感器接线、驱动与枚举:让固件“看见”陀螺仪和气压计
4.1 SPI/I2C总线和设备挂载逻辑
飞控上最常见的传感器组合是:IMU走SPI,气压计和磁力计走I2C或SPI。PX4识别传感器的过程,可以简单理解成“内核驱动在总线上问一遍‘有设备吗?’,设备给出正确的ID,驱动就认为找到了”。
所以移植的第一步,是在board_config.h或启动脚本里把总线和设备挂载关系配好。比如你用的是BMI088作为主IMU、MS5611作为气压计,那你需要确保:
- BMI088的SPI总线和片选引脚在板级配置里存在;
- MS5611的I2C总线地址没有和别的设备冲突;
rc.board_start脚本里,对应驱动的启动命令使用正确的总线号和设备地址。
这块不要跳步。我的经验是第一次移植时,先用逻辑分析仪直接量SPI的波形,确认在系统启动阶段主机发出的读写请求能收到设备的回应。只要硬件链路是通的,后面PX4的软件配置只是“找对参数”的问题;如果硬件链路不通,你写再多驱动配置都是在沙滩上盖楼。
4.2 驱动编译开关:default.px4board里的“菜单”
在PX4的新版本里,传感器驱动是否被编译进固件,由default.px4board里的配置项控制。不同传感器的驱动宏名称不一样,比如IMU的BMI088、ICM42688,气压计的MS5611、BMP388,都在这个文件里能找到对应的开关。
改完配置后重新编译,你会看到固件大小发生变化。把不用的传感器驱动关掉,可以稍微减小固件体积,但前提是你必须清楚自己板上到底有哪些传感器。我自己就干过一件蠢事:把气压计驱动全关掉,结果气压计在系统里找不到设备,高度数据一直是NaN,解锁直接拒绝。后来重新打开驱动编译,设备马上枚举成功。
4.3 传感器校准和方向参数
即使传感器能被驱动读取,如果你安装的IMU方向和PX4默认坐标轴方向不一致,飞控的表现会非常“诡异”:机体不动,数据却显示在持续旋转,或者飞起来往一个方向猛偏。
PX4里可以通过参数设置传感器安装方向,比如调整IMU的旋转偏移参数。QGroundControl的地面站校准页面也有图形化指示,它会让你把飞机头朝前、左侧朝下等,根据读出数据来确定安装方向。这一步一定别跳过,尤其是用了非官方参考姿势摆放的板子。
校准完成后,建议在地面站看一遍传感器数据流:加速度计静止时读数为1g左右,陀螺仪静止时接近0,气压计高度变化平滑。如果数据有异常跳变或者长期漂移,先查硬件,不要急着调软件滤波。
5. 烧录、启动和首次调试:从黑屏到控制台输出
5.1 Bootloader和固件,分开烧不要混
PX4在STM32上的启动方式,和安卓手机有点像:先有bootloader,再通过它加载APP固件。自制板第一次烧录时,必须先把PX4 bootloader烧进芯片的起始地址区域,然后才能通过bootloader烧APP。
如果你有ST-Link,可以先用STM32CubeProgrammer把bootloader烧进去。烧录前确认一下bootloader的链接地址和你的板级配置是否匹配,特别是H743板卡,地址不对会出现“能进DFU模式但烧不进固件”的诡异问题。
之后固件的烧录有两种常见方式:一是通过USB DFU,让bootloader接管后用QGroundControl的固件升级页面选择本地固件烧录;二是直接用ST-Link把生成的.bin文件烧到APP区域。第二种方式如果偏移地址写错,一样会“看起来烧成功了,但上电没有任何输出”。
5.2 启动控制台:这是排查问题的主入口
板子第一次上电,最先要看的是NuttX控制台的串口输出。不同板卡的console串口号和波特率不同,不要在默认参数上瞎猜,直接在board_config.h里找到控制台UART定义确认波特率。我遇到过好几次“没输出”的情况,最后都发现是我用错波特率,或者接错串口。
正常启动时,控制台会打印NuttX版本号、PX4版本号,然后逐条加载驱动。如果卡在某个驱动初始化处,基本就是对应外设没回包,回到第4节的“先量波形”思路去查。如果整个日志缺失,第一步检查电源和时钟;第二步检查bootloader有没有正确运行;第三步确认你编译的固件是不是“这一个”板子的配置。
启动顺利的标志是出现pxh>提示符(NuttX shell)。在这个提示符下手动执行list_devices、top等命令,可以快速确认设备节点和任务是否正常。这一步的日志建议全部保存,后续分析问题会经常翻。
5.3 MAVLink连接与地面站的第一次握手
有了pxh>,再确认USB连接之后QGroundControl能不能识别到飞控。插上USB线,地面站如果提示固件不匹配,可以选择自定义固件文件刷入你编译出来的.px4格式固件。这里有个体验上的避坑:在刷固件界面,要开启“高级设置”才能选择本地固件文件,第一次用的人经常找不到这个入口。
连接之后检查两个信息:一是遥测/数传的MAVLink消息是否稳定,二是SD卡日志是否正常。SD卡不是必须的,但试飞和调试时没有日志就很难定位“刚才为什么突然翻了一下”这类问题。建议从第一步开始就把日志存储解决掉。
6. 装机试飞前的地面站校准与安全检查
6.1 遥控器、电调和模式映射,先弄完再谈解锁
飞控能在桌面上“看起来正常”并不代表能上天。在QGroundControl里,有几项校准必须在装桨之前做完:
- 加速度计校准:按地面站图形提示摆六个姿态;
- 磁力计校准:需要把机架拿在手里转各个方向,避开金属物体和电机磁场;
- 遥控器校准:摇杆打到各个极限位,确认PPM/SBUS信号映射正常;
- 电调校准:如果用的是普通电调,需要先拆桨,按电调说明进入行程校准模式,让飞控输出满油门和最小油门完成行程标定。
模式映射也很关键:至少要把“自稳/定高/位置”模式放到一个你熟悉的三挡开关上,并且确认安全开关能够正常解锁和上锁。如果你没有接外部安全开关,就要提前决定是设置成“忽略安全开关”还是用一个按钮来完成。我的态度是:宁可多做一个物理安全开关,也不要为了省事把安全逻辑完全跳过。
6.2 解锁条件检查:参数别乱改
第一次解锁时,QGroundControl会给出拒绝解锁的原因,比如“传感器未校准”“电池电压异常”“GPS定位未就绪”。新手最容易犯的错是一看到拒绝解锁就直接去搜“禁用安全检查参数”,然后一股脑把几项关键保护全关掉。
我理解想快点飞的心情,但安全检查参数是最后一道防线。自制飞控本来就没有大厂产品的可靠性兜底,你至少要把电压检测、传感器健康检查这几项保持有效。真要临时跳过某些检查,建议只针对“本次调试明确知道没问题”的项目,并且做完测试后立刻恢复。
6.3 第一次手持测试和悬停:别急着推油门
所有校准做完之后,先不要在腿上或桌上解锁推高速。正确做法是:拆掉螺旋桨,解锁后轻轻推油门,观察电机转速是否随油门外环变化平滑,机架有没有明显振动;然后在手持机架的状态(务必小心,不要握桨盘位置)感受飞控的姿态响应,看看机体倾斜时电机是否有对应的修正动作。
感受到修正动作正常后,再装桨去户外开阔场地试飞。第一次悬停时油门要缓推,随时准备切回自稳模式并上锁。如果飞机有高频抖动,先检查螺旋桨动平衡和机架振动,很多时候不是PID问题,而是物理振动传导到IMU导致的。
最后分享一个我自己的习惯:拿到新板子,先不急着跑PX4,而是先用ST-Link配合一个最简单的裸机例程,点亮LED、回读传感器ID,把硬件底子验一遍,再开始移植固件。这一步看起来慢,但能在后面省出几天的排查时间。真正决定移植顺利与否的,往往不是你写代码的能力,而是你是不是足够熟悉自己手上的这块板子。希望这篇能帮你少踩几个我踩过的坑,祝飞行顺利。