
简介基于STM32单片机设计的无线四轴飞行器与飞控遥控器系统分成飞行器和遥控器两大部分面向电子竞赛、无人机入门及嵌入式开发者。以STM32为核心配合NRF24L01无线模块实现遥控指令传输采用MPU6050姿态传感器完成飞行姿态检测与稳定控制并附有详细操作说明涵盖螺旋桨安装、飞控初始化、遥控器解锁及IMU校准等实际使用环节。资源包共673个文件约10.96MB主要包含208个.h头文件、195个.c源文件、原理图、PCB图及hex固件等既有完整工程源码也有硬件设计参考适合从软硬件两方面学习。目前已有172人浏览学习。通过这套资料可快速搭建四轴飞行器原型理解STM32在无人机飞控、无线通信和传感器融合中的典型应用也为后续扩展遥控器功能提供良好基础。1. 基于 STM32 单片机实现无线四轴飞行器飞控与遥控器系统的设计骨架很多人在四轴飞行器项目里栽跟头并不是因为 PID 调不出来而是被飞控和遥控器这两个字带偏了方向。基于 STM32 单片机设计的无线四轴飞行器、飞控遥控器系统拆开看其实是两套独立的嵌入式设备机身上的飞控板负责姿态解算和电机混控手持的遥控器板负责摇杆采集和无线数据下发两者靠一条 2.4G 无线链路维持 50Hz 到 100Hz 的控制周期。对刚接触四轴的工程师来说最常踩的坑是先把姿态解算写得天花乱坠结果电机混控输出和无线帧结构完全没对齐飞机在地面就翻肚皮。这个项目的正确做法是从系统顶层画两条数据流遥控器 → 无线模块 → 飞控 → 电机以及飞控 → 无线模块 → 遥控器回传姿态和电量再顺着数据流把硬件原理图、PCB 和源码逐层填满。适合的读者是准备毕业设计、电子竞赛或者第一次接触 IMU 传感器和串级 PID 的嵌入式从业者。2. 系统架构与核心器件选型在原理图阶段定死通信和供电边界2.1 飞控板与遥控器的 MCU 选型为什么都用 STM32 而不是 STC 或 51常见做飞控遥控器系统的选型方案是机载飞控用 STM32F103C8T6遥控器端也用同型号因为两者需要的资源基本对等飞控需要读取 MPU6050I2C、接收遥控数据SPI 接 NRF24L01、输出 4 路 PWM遥控器需要读取 4 到 6 路摇杆 ADC、按键输入、SPI 无线收发、OLED 显示。F103C8T6 的 64KB Flash 和 20KB RAM 放过姿态解算和通信协议完全够用而且两边的工程可以共用一套 NRF24L01 驱动和通信帧代码仓库里维护成本直接减半。提示如果你的设计里还想加超声波定高、光流定点或者 OSD 图传叠加建议把飞控换成 STM32F405RGT6预算多 10 元左右但浮点运算和 DMA 资源会宽裕很多。选 STM32 而不是 51 单片机还有一个核心原因硬件除法器和乘法器对 PID 控制的实时性影响。四轴飞控的内环控制周期通常做到 1kHz也就是 1 毫秒内要读完 IMU、执行完互补滤波加上串级 PID 计算。51 单片机在 24MHz 主频下跑浮点运算会明显吃紧而 STM32F103 在 72MHz 下用硬件浮点配合整型优化的 PID 可以轻松跑满 1kHz 甚至 2kHz留出余量给无线数据接收和电机更新。2.2 原理图设计的三个关键节点MPU6050 去耦、NRF24L01 电源滤波、电机供电隔离原理图设计常犯的错误是把 MPU6050 直接挂在 MCU 的 3.3V 上不处理。MPU6050 内部是 MEMS 振动结构对电源纹波非常敏感纹波大了数据会随机漂移飞控会表现为地面不动但姿态角自己在缓慢旋转。我一般做法是给 MPU6050 的 VDD 单独加一颗 10Ω 电阻和 10uF 0.1uF 电容组成 π 型滤波模拟地和数字地在主芯片下方单点汇合。NRF24L01 的峰值电流在发射瞬间会跳到 110mA 左右如果和 MCU 共用一条细走线射频发射时刻会造成 MCU 供电跌落导致无线数据收发的关键时序被干扰。正确做法是在 NRF24L01 模块座子旁边放一颗 100uF 电解电容和 0.1uF 陶瓷电容并且电源线从 3.3V LDO 单独引出不要和 MCU 的电源走线共用一条低压差路径。电机供电隔离是原理图里最容易被忽视的四个直流空心杯电机在启动瞬间电流是稳态的 2 到 3 倍如果飞控板和电机驱动共用电源层大电流冲击会直接串进传感器和 MCU。常见的设计是 1S 锂电池3.7V先经过主电源开关进入电机驱动 MOS 管同时分一路经过 LDO如 ME6211降到 3.3V 给 MCU 和传感器用LDO 输入端的 100uF 电容要贴着电池接口放。电机的 VCC 走线要用 1mm 以上宽度或铺铜避免过孔限制电流导致压降过大飞机大油门时出现复位重启。2.2.1 PCB 布局IMU 位置决定飞控的机械噪声PCB 图的布局核心是 MPU6050 必须放在 PCB 物理中心附近而且要避开电机座螺丝孔位。电机高速旋转时螺旋桨的机械振动会从机臂传导到机身如果 MPU6050 离螺丝孔太近或者处于 PCB 边缘加速度计采集的振动噪声会被放大配合互补滤波的高通特性会引入明显的姿态抖动。另外 MCU 的 8MHz 晶振要靠近 BOOT0 和 OSC_IN/OSC_OUT 引脚放置晶振下方铺地铜并打过孔包围这是配套源代码能在硬件上稳定运行的前提——晶振不起振软件层面的工作全都白做。2.2.2 8MHz 晶振电容计算匹配负载电容而不是随便选 20pF热词里经常出现stm32 晶振电容计算这里给一个可以直接套的公式。ST 官方规范中STM32F103 的 8MHz 晶振要求并联负载电容常见匹配公式是CL (C1 * C2) / (C1 C2) Cs其中CL是晶振规格书里标的负载电容常见 8MHz 晶振标 12pF 或 20pFC1和C2是晶振两脚对地的匹配电容Cs是 PCB 引脚的寄生电容经验值取 2 到 5pF。要让晶振工作在标称频率附近应当使若晶振CL 12pF取Cs 3pF则C1 C2 2 * (CL - Cs) 18pF取容值相近的 18pF 贴片电容。若晶振CL 20pF取Cs 3pF则C1 C2 2 * (20 - 3) 34pF取 33pF 或 33pF 与 1pF 并联。电容偏差会导致实际频率偏移偏太多会造成串口波特率误差和定时器时基漂移飞控解锁后电机会出现间歇性抽搐这是典型的原理图看着没问题但飞不起来的隐性原因。2.3 无源器件选型MOS 管、LDO、上拉电阻的容差控制电机驱动部分用 SI2302 这类 N-MOS 管是常见做法但要注意 SI2302 在 3.3V 栅极电压下的导通电阻范围。选型时重点看 VGS(th) 阈值电压确保 MCU 的 3.3V GPIO 能完全打开 MOS 管否则电机在大电流下 MOS 管发热严重会反向影响机身的散热和 IMU 温度漂移。I2C 上拉电阻选择 4.7kΩ 是保守值如果 MPU6050 通信偶尔失败可以换成 2.2kΩ 增加上升沿速率。飞控的 SDA 和 SCL 走线尽量等长且远离 PWM 输出线避免电机调速的 PWM 信号对 I2C 产生串扰。3. 无线链路协议设计nRF24L01 六通道遥控帧格式与收发程序实现3.1 通信帧结构定义数据对齐比数据内容更关键STM32 飞控接收遥控器的数据最忌讳的是定义一个大结构体直接内存拷贝。不同编译器的字节对齐规则不一致而且 STM32 和遥控器如果用了不同的优化等级结构体内部 padding 会悄悄改变数据大概率错位。我一般会定义一个固定长度的数组作为协议层载体再用 memcpy 逐字段映射// 六通道遥控帧固定 10 字节长度 #pragma pack(1) typedef struct { uint8_t head; // 帧头 0xAA uint8_t cmd; // 0x01遥控数据, 0x02解锁/锁定, 0x03紧急停机 uint8_t throttle; // 油门 0-255 uint8_t yaw; // 偏航 0-255, 中点约 127 uint8_t pitch; // 俯仰 0-255 uint8_t roll; // 横滚 0-255 uint8_t aux1; // 辅助通道 1模式切换 uint8_t aux2; // 辅助通道 2LED/蜂鸣器 uint8_t crc_low; // CRC16 低字节 uint8_t crc_high; // CRC16 高字节 } rc_frame_t; #pragma pack()代码逻辑说明#pragma pack(1)强制结构体按 1 字节对齐保证无论接收端单片机优化选项怎么变字段偏移量始终固定这是跨平台无线通信的基本功。帧头0xAA用于接收端找同步起始位摇杆通道都用 0 到 255 表示中点 127 左右通过遥控器代码里的中位校准修正。提示CRC 校验不能省。NRF24L01 虽然硬件自带 CRC但只能检出射频传输错误无法识别两帧数据之间的粘包和丢帧。在 payload 里再算一次 CRC16 是极便宜的保险。3.2 NRF24L01 收发最小代码SPI 驱动和 Enhanced ShockBurst 配置NRF24L01 收发代码可以直接复用到飞控和遥控器两端只做一个发送端和接收端的条件编译。初始化时要特别注意模块的 CE 引脚拉高 130 微秒以上才进入发送模式10 微秒内的短脉冲只会让模块进入待机状态初学者常在这里卡死英语注释里写的 CE high for at least 10us 经常被误解成 10 微秒即可。// NRF24L01 发送模式最小初始化代码 void nrf24_tx_init(void) { // 配置发送端地址和射频参数 nrf24_write_reg(REG_CONFIG, 0x0E); // CRC 使能, 2字节CRC, 上电 nrf24_write_reg(REG_EN_AA, 0x01); // 仅数据管道0开启自动应答 nrf24_write_reg(REG_SETUP_RETR, 0x1A); // 自动重发500us间隔, 最多重发10次 nrf24_write_reg(REG_RF_CH, 40); // 2.480GHz 频点 nrf24_write_reg(REG_RF_SETUP, 0x26); // 2Mbps, 0dBm 发射功率 nrf24_write_reg(REG_RX_PW_P0, 10); // 管道0接收长度10字节 // 设置发送和接收地址为相同的 5 字节地址 uint8_t addr[5] {0xE7, 0xE7, 0xE7, 0xE7, 0xE7}; nrf24_write_reg_burst(REG_TX_ADDR, addr, 5); nrf24_write_reg_burst(REG_RX_ADDR_P0, addr, 5); }发送函数内部要处理MAX_RT中断标志位否则重发次数耗尽后模块会一直报错导致后续发送全部失败uint8_t nrf24_send(uint8_t *data, uint8_t len) { CE_LOW(); // 清空发送中断标志 nrf24_write_reg(REG_STATUS, (1 5) | (1 4) | (1 3)); // 写入发送缓冲区 nrf24_cmd_write_burst(CMD_W_TX_PAYLOAD, data, len); // CE 拉高至少 15us 触发发送 CE_HIGH(); delay_us(20); CE_LOW(); // 等待发送完成或超时 uint32_t timeout 10000; while ((nrf24_read_reg(REG_STATUS) (1 5)) 0 timeout--) { delay_us(10); } if (timeout 0) return 0; // 发送超时 uint8_t status nrf24_read_reg(REG_STATUS); if (status (1 4)) { // MAX_RT 重发耗尽 nrf24_write_reg(REG_STATUS, (1 4)); // 清标志后重新发送 return 0; } return 1; }这个函数的参数说明REG_STATUS里 bit5 是 TX_DS发送完成bit4 是 MAX_RT重发次数耗尽。判断发送成功要看两个条件TX_DS 必须置位并且 MAX_RT 不能同时置位。只判断 TX_DS 会漏掉一种情况——重发 10 次后刚好某个数据包被应答了模块会同时置位 TX_DS 和 MAX_RT这帧数据虽然发出去了但延迟已经超过 50ms对飞控来说是失效数据网络热词里说的stm32和变频器通讯踩坑模型虽然是指工业场景但和这里的超时语义一致——通讯协议里不能只看最终成功标志必须看时序约束。3.3 无线频点和速率的选择2Mbps 速率下的数据噪声边界NRF24L01 支持 250kbps、1Mbps、2Mbps 三档速率飞控场景我建议用 2Mbps。理由很直接飞行器控制链路对延迟敏感对绝对误码率不敏感。四轴在 80km/h 左右飞行时多径效应和同频干扰很常见2Mbps 下单包传输时间短被 WiFi 或者蓝牙跳频信号干扰的概率窗口更小。缺点是接收灵敏度从 -94dBm 降到 -82dBm 左右但对室内 30 到 50 米遥控距离来说完全可用。频点选 402.480GHz附近是业界默认的打架区2.4G WiFi 的信道 11 到 13 也在这附近。更稳妥的做法是在遥控器上加一个频道偏移代码上电时自动扫描 RSSI避开噪声最大的频点再建立链路这个功能本项目的遥控器端源码里通常会做成自动跳频的简版。4. 飞控端源码核心MPU6050 数据采集、姿态解算与串级 PID 输出4.1 MPU6050 原始数据读取I2C 时序和量程配置飞控源代码第一步是初始化 MPU6050 的陀螺仪和加速度计量程。四轴飞行器常飞的动作范围里陀螺仪用 ±500°/s 量程、加速度计用 ±4g 量程比较合适。±250°/s 在快速翻转时会削顶导致姿态解算瞬变飞控会给出错误的电机修正量±2000°/s 虽然不会削顶但量化噪声被放大到 16.4 LSB/°/s静止状态下陀螺仪数据波动会明显增大。以下是 I2C 初始化代码void mpu6050_init(void) { // 退出睡眠模式: 设置 PWR_MGMT_1 寄存器为 0x00 uint8_t pwr_data 0x00; i2c_write_reg(MPU6050_ADDR, 0x6B, pwr_data, 1); // 陀螺仪量程 ±500°/s: 对应 GYRO_CONFIG 寄存器值为 0x08 uint8_t gyro_cfg 0x08; i2c_write_reg(MPU6050_ADDR, 0x1B, gyro_cfg, 1); // 加速度计量程 ±4g: 对应 ACCEL_CONFIG 寄存器值为 0x08, 灵敏度 8192 LSB/g uint8_t accel_cfg 0x08; i2c_write_reg(MPU6050_ADDR, 0x1C, accel_cfg, 1); // 配置数字低通滤波带宽 94Hz: 对应 CONFIG 寄存器值为 0x03 uint8_t dlpf_cfg 0x03; i2c_write_reg(MPU6050_ADDR, 0x1A, dlpf_cfg, 1); }代码里的参数逻辑DLPF数字低通滤波设为 94Hz 是要在保留飞行操控频段信号的同时滤掉电机和桨叶的高频振动。四轴飞行时姿态变化的核心频段在 5Hz 到 30Hz94Hz 低通足够保持响应又不会把 200Hz 以上的电机噪声传递进解算环路。读取原始数据时要注意帧同步MPU6050 的加速度计和陀螺仪各自输出 6 个字节I2C 多字节读取时从 0x3B 地址一次性连续读 14 个字节最稳妥分多次单字节读取会引入中间采样时刻的时间漂移。4.2 四元数互补滤波用加速度计修正陀螺仪积分漂移姿态解算选择互补滤波而不是完全用四元数梯度下降法的原因是运算量F103C8T6 在 72MHz 下跑 Mahony 互补滤波大约耗时 200 微秒而完整跑一遍 Madgwick 梯度下降法要 500 微秒以上这会挤压后续 PID 和无线通信的时间预算。互补滤波的思路是把陀螺仪积分后的高频姿态和加速度计计算出的低频姿态按权重融合飞控专用代码如下void attitude_update(float gx, float gy, float gz, float ax, float ay, float az, float dt) { // 归一化加速度计向量 float norm sqrtf(ax * ax ay * ay az * az); ax / norm; ay / norm; az / norm; // 当前四元数计算出的重力方向参考向量 float vx 2.0f * (q1 * q3 - q0 * q2); float vy 2.0f * (q0 * q1 q2 * q3); float vz q0 * q0 - q1 * q1 - q2 * q2 q3 * q3; // 叉积误差: 测量向量与参考向量之间的偏差 float ex ay * vz - az * vy; float ey az * vx - ax * vz; float ez ax * vy - ay * vx; // 误差积分项, 消除静态偏差 (Kp1.5, Ki0.3) ex_int ex * Ki * dt; ey_int ey * Ki * dt; ez_int ez * Ki * dt; // 陀螺仪角速度补偿 gx Kp * ex ex_int; gy Kp * ey ey_int; gz Kp * ez ez_int; // 一阶龙格库塔法更新四元数 float q0_dot -q1 * gx - q2 * gy - q3 * gz; float q1_dot q0 * gx q2 * gz - q3 * gy; float q2_dot q0 * gy - q1 * gz q3 * gx; float q3_dot q0 * gz q1 * gy - q2 * gx; q0 q0_dot * dt; q1 q1_dot * dt; q2 q2_dot * dt; q3 q3_dot * dt; // 四元数归一化, 防止数值累积误差 float q_norm sqrtf(q0 * q0 q1 * q1 q2 * q2 q3 * q3); q0 / q_norm; q1 / q_norm; q2 / q_norm; q3 / q_norm; // 由四元数转欧拉角, 用于 PID 控制和上位机显示 roll_angle_out atan2f(2.0f * (q0 * q1 q2 * q3), 1.0f - 2.0f * (q1 * q1 q2 * q2)); pitch_angle_out asinf(2.0f * (q0 * q2 - q1 * q3)); yaw_angle_out atan2f(2.0f * (q0 * q3 q1 * q2), 1.0f - 2.0f * (q2 * q2 q3 * q3)); }这段代码里最值得说明的是Kp和Ki的含义Kp是加速度计对陀螺仪的交叉修正强度越大则姿态跟踪越快但噪声越容易被引入Ki是积分修正项负责消除陀螺仪零偏在积分过程中造成的累积漂移。飞行调试时如果发现姿态角在悬停时缓慢偏向一个方向优先调大Ki如果机身发抖且姿态角跳变优先调小Kp。初始值Kp1.5, Ki0.3是我从大量调试记录里抽出来的适用手抛型小四轴的中位值可以从这个基础上再微调。提示atan2f和asinf在 F103 上编译后大约各耗 3 到 5 微秒完整转换一次在 20 微秒内可以完成。如果你的姿态解算频率是 1kHz欧拉角输出耗时会吃掉 2% 的 CPU 时间对整体实时性没有实质影响。4.3 串级 PID 控制内环角速度、外环角度与电机混控映射飞控常用的控制结构是串级 PID外环是角度环内环是角速度环。外环输出直接作为内环的期望值输入这样设计可以让内环以更快的频率抑制扰动。一般外环频率跑 250Hz内环频率跑 1kHz。以下是最小可用的外环加内环实现// 外环角度环 PID, 输入期望角度和目标角度, 输出期望角速度 float angle_pid_outer(float expected, float measured, float dt) { float err expected - measured; float p_out kp_angle * err; angle_integral[0] ki_angle * err * dt; // 积分限幅, 防止 windup if (angle_integral[0] ANGLE_I_LIMIT) angle_integral[0] ANGLE_I_LIMIT; if (angle_integral[0] -ANGLE_I_LIMIT) angle_integral[0] -ANGLE_I_LIMIT; return p_out angle_integral[0]; } // 内环角速度环 PID, 输出是电机 PWM 修正量 float rate_pid_inner(float expected_rate, float measured_rate, float dt) { float err expected_rate - measured_rate; float p_out kp_rate * err; rate_integral[0] ki_rate * err * dt; if (rate_integral[0] RATE_I_LIMIT) rate_integral[0] RATE_I_LIMIT; if (rate_integral[0] -RATE_I_LIMIT) rate_integral[0] -RATE_I_LIMIT; float d_out kd_rate * (err - prev_err[0]) / dt; prev_err[0] err; return p_out rate_integral[0] d_out; }参数逻辑说明内环的kd_rate是对角速度变化率的阻尼调大了飞机会变得僵硬调小了会振荡。内环输出经过一个限幅函数后再和遥控器油门值混合成四个电机的 PWM 输出。混合是 x 型布局的固定映射电机 1 和电机 4 是一对对角线电机 2 和 3 是另一对横滚和俯仰命令分别以差分形式叠加。差分叠加的符号方向必须和 MPU6050 的安装方向一致否则飞控会越修越偏飞机解锁后直接原地自旋或者翻机。5. 遥控器端源码与交互逻辑摇杆采样、按键消抖与 OLED 回传显示5.1 摇杆 ADC 采样和原点校准中点漂移对飞控的影响遥控器端的核心输入是两颗二维摇杆内部是两个电位器输出模拟电压到 STM32 的 ADC 引脚。F103 的 ADC 是 12 位分辨率摇杆量程的物理范围通常只能用到 10 位左右原因在于电位器碳膜两端的死区。我一般把 ADC 原始值映射到uint8_t的 0 到 255再进行中位校准。摇杆中位偏移是四轴飞控最隐蔽的问题来源电位器用久了碳膜磨损机械中心对应的 ADC 值不会是理论上的 2048如果拿原始值直接当作 127 发给飞控飞控外环会一直收到一个固定的非零期望角度飞机解锁后就会向某个方向缓缓漂移。处理方式是在遥控器初始化时进行 200 次 ADC 采样平均把每次采样的 ADC 值减去校准值后再映射到 -127 到 127 的输出范围// 摇杆通道校准: 上电时检测当前摇杆位置并保存为中心值 uint16_t stick_center[6]; void stick_calibrate(void) { for (uint8_t ch 0; ch 6; ch) { uint32_t sum 0; for (uint8_t i 0; i 200; i) { sum adc_read_channel(ch); delay_us(100); } stick_center[ch] sum / 200; } }参数说明这里的delay_us(100)是为了让 ADC 采样避开同一时刻 PWM 电机信号或 OLED 刷新带来的电源干扰。200 次采样取平均能消除大部分白噪声但需要注意的是校准必须在遥控器开机后 2 秒内完成期间用户不能碰摇杆否则中心值会被污染。如果后续发现摇杆回中后飞控端仍有漂移可以把校准时间延长到 3 秒并在 OLED 上显示校准中字样。5.2 按键消抖与模式切换逻辑用状态机替代延时消抖遥控器上一般有解锁、锁定和模式切换三个按键。初学者最容易在按键处理上犯的错是直接用delay_ms(20)消抖这会阻塞遥控器主循环导致 NRF24L01 的发送周期被拖长到 30ms 以上飞行器的手感会变肉。正确的做法是用定时器扫描按键配合状态机消抖// 按键状态机: 等待按下 - 确认按下 - 等待释放 typedef enum { KEY_STATE_IDLE 0, // 空闲状态 KEY_STATE_PRESSED, // 已检测到按下 KEY_STATE_CONFIRM // 消除抖动后确认 } key_state_t; void key_scan_10ms(void) { static key_state_t state[4]; static uint8_t key_dbnc_cnt[4]; for (uint8_t i 0; i 4; i) { uint8_t level gpio_read(key_pin[i]); switch (state[i]) { case KEY_STATE_IDLE: if (level 0) { // 检测到低电平按下 state[i] KEY_STATE_PRESSED; } break; case KEY_STATE_PRESSED: if (level 0) { key_dbnc_cnt[i]; if (key_dbnc_cnt[i] 3) { // 连续 3 次扫描确认按下 key_event_callback(i); // 触发对应的模式切换 state[i] KEY_STATE_CONFIRM; } } else { key_dbnc_cnt[i] 0; state[i] KEY_STATE_IDLE; } break; case KEY_STATE_CONFIRM: if (level 1) { // 等待释放 key_dbnc_cnt[i] 0; state[i] KEY_STATE_IDLE; } break; } } }这个状态机在定时器中断里每 10ms 执行一次三个连续采样周期保持一致才判定按键有效相当于 30ms 的硬件消抖时间不会阻塞主循环。按键回调里做的工作很轻只是翻转一个system_mode全局变量然后往无线发送缓冲区写一帧命令数据真正的发送动作由无线发送周期决定。5.3 OLED 回传显示用串口双工把飞控姿态和电量拉回遥控器遥控器端通过 NRF24L01 的 ACK 载荷机制可以在接收遥控命令的同时带回飞控的遥测数据。NRF24L01 的增强型 ShockBurst 模式支持在自动应答时附带 1 到 32 字节的数据这意味着飞控收到遥控器数据包后把姿态角和电池电压填写到 ACK 载荷里返回给遥控器通信链路不需要额外的回传包省时省带宽。OLED 显示的内容一般包括当前的飞行模式自稳/定高/手动、油门输出百分比、飞控回传的横滚角和俯仰角以及飞控端的电池电压。对四轴飞行器来说电池电压回传是最常用的功能因为机载电池电压如果低于 3.5V 单片电压大油门时电压会瞬间跌穿 LDO 的最低工作电压导致飞控重启。常见方案是飞控的 ADC 脚直接分压测量电池电压分压电阻用 10kΩ 和 10kΩ 各一颗ADC 结果乘以 2 再乘以 3.3V/4096 得到单节电压回传到遥控器端后OLED 的电压值低于 3.5V 时切换成大字报警显示。6. 串级 PID 的滤波陷阱与参数整定用手抛法验证飞控姿态响应串级 PID 参数整定是整套 STM32 飞控系统里最耗时的环节而最容易被忽略的是内环微分项的滤波。内环角速度环的微分项放大了高频噪声电机 PWM 变化和 IMU 采样抖动都会通过 D 项引入输出抖动飞控会出现小幅度高频颤振。常见做法是先对陀螺仪原始的角速度数据做一阶低通滤波再送入微分环节// 陀螺仪角速度低通滤波, 截止频率约 42Hz float gyro_filtered[3]; float gyro_lpf(float raw, uint8_t idx, float dt) { float alpha dt / (dt 1.0f / (2.0f * 3.14159f * 42.0f)); gyro_filtered[idx] alpha * (raw - gyro_filtered[idx]); return gyro_filtered[idx]; }这个滤波器的参数逻辑alpha是平滑系数dt是采样周期0.001 秒对应 1kHz 采样率42Hz 截止频率保留了人类操控所需的姿态响应带宽同时过滤掉了高频机械噪声。如果把截止频率提到 100Hz滤波延时变小但噪声变大降到 20Hz 后飞控的响应会明显迟钝快速打舵时飞机会有黏滞感。参数整定的实际操作我建议遵循先内环后外环先 P 后 D 再 I的原则。保持外环禁用期望角度直接置为 0先把内环的kp_rate从小到大加直到用手拿起飞机旋转机身可以感觉到明显的对抗力继续增加直到出现高频振荡然后回退 50% 作为安全初值。接着加kd_rate每次增加 20%同时观察机身是振荡收敛还是持续振荡。内环调完再开外环kp_angle从 2.0 起步用手抛法验证手拿飞机轻轻向某个方向偏转 20 度然后释放如果飞机能在 1 秒内回到水平且没有明显回弹说明外环 P 够用了否则继续加大。提示手抛法验证比绑在桌子上试车安全得多操作要领是把飞机举在胸口高度手部动作要轻微避免螺旋桨打到手。电机解锁状态下手不要贴近桨叶平面只抓机臂中心附近的开阔区域。参数整定的最终落地路径是写进飞控的常量区而不是每次重新编译烧录。正式发货或做演示前把一组稳定的参数固化到config.h里并且把这个配置区做成可以在遥控器上调整的调试模式按下遥控器的某个组合按键进入参数调节菜单用摇杆把 PID 参数实时写入 EEPROM重启后生效。这个做法在反复调参过程中省掉了大量烧录等待时间效率比每次用 ST-LINK 重新下载高一个数量级。至此从系统架构、原理图 PCB 到无线通信和飞控源码的实际参数整定已经形成一条完整闭环。本文还有配套的精品资源点击获取