【免费开源】基于 STM32F4 的四轴飞行器设计与实现——从零开始到成功起飞(项目源码打包分享)
基于 STM32F4 的四轴飞行器设计与实现 —— 从零开始到成功起飞
源码下载
完整项目已打包,开源免费:
https://code.devzoo.top/embedded/63.html
前言
从一块 STM32F4 开发板,到一台能稳定飞行的四轴飞行器,这中间跨越了传感器数据采集、姿态解算、控制算法、无线通信、电机驱动等多个领域。
这篇文章记录了我和团队在校园创新大赛中完成四轴飞行器的全过程,希望能为后来者提供思路和借鉴。
四轴飞行器作为一种低空、低成本的遥感平台,已经在多个领域展现出广泛的应用潜力。相比其他类型的飞行器,它在硬件上结构紧凑、安装方便,但在软件层面却充满挑战——从传感器数据融合到姿态解算,再到快速且稳定的控制算法,每一环节都需要精心设计,也正因此让四轴飞行器更具技术魅力。
本项目以 ST 公司推出的 STM32 作为核心处理器,选用 STM32F4 Discovery 开发板作为遥控接收端,结合 MPU6050 姿态传感器,配备软塑料机架、空心杯电机、正反螺旋桨、锂电池以及四轴遥控器。经过系统化的调试与优化,最终实现了能够稳定飞行、响应迅速、并具备一定抗干扰能力的小型四轴飞行器。
1. 项目背景
无人机技术在近几年飞速发展,而四轴飞行器凭借结构简单、控制灵活、成本低廉,成为入门和研究的热门方向。
我们希望通过这个项目,深入理解嵌入式系统在多旋翼飞控中的应用,并在 STM32F4 平台上自主实现从传感器采集到飞行控制的完整闭环系统。
2. 硬件设计
2.1 控制核心
- 主控芯片:STM32F407VGT6(168MHz 主频,1MB Flash,192KB SRAM)
- 优点:运算能力强,外设丰富,足以胜任姿态解算与控制算法的实时运行。
2.2 传感器
- 姿态传感器:MPU6050(3 轴加速度计 + 3 轴陀螺仪)
- 气压计:BMP180(辅助高度控制)
- 采样频率 1kHz,数据通过 I²C 接口传输。
2.3 无线通信
- nRF24L01:2.4GHz 无线模块,实现与地面遥控器的数据交互。
2.4 动力系统
- 无刷电机 + 电子调速器(ESC)
- 螺旋桨选用 1045 型(直径 10 英寸,螺距 4.5 英寸)
⚙ 硬件连接建议
- 传感器接口线尽量短,减少干扰
- 电源部分加去耦电容,防止电机启动时的电压波动影响 MCU
3. 软件设计
3.1 系统框架
- 传感器采集:读取 MPU6050 原始加速度、角速度数据
- 姿态解算:使用互补滤波融合陀螺仪与加速度计数据,得到俯仰角、横滚角、偏航角
- 控制算法:三轴独立 PID 控制器
- 电机输出:根据 PID 输出调整 PWM 占空比,驱动 ESC
3.2 姿态解算 —— 互补滤波
互补滤波的原理很简单:
- 陀螺仪:短时间内精度高,但存在零漂
- 加速度计:不受零漂影响,但短时噪声大
- 互补滤波:利用低通滤波器保留加速度计的低频信息,高通滤波器保留陀螺仪的高频信息,再加权融合。
公式:
angle = α * (angle + gyro * dt) + (1 - α) * acc_angle;
其中 α 通常取 0.96 ~ 0.98。
void IMUupdate(float gx, float gy, float gz, float ax, float ay, float az){ float norm; float vx, vy, vz; float ex, ey, ez; // 先把这些用得到的值算好 float q0q0 = q0*q0; float q0q1 = q0*q1; float q0q2 = q0*q2;// float q0q3 = q0*q3; float q1q1 = q1*q1;// float q1q2 = q1*q2; float q1q3 = q1*q3; float q2q2 = q2*q2; float q2q3 = q2*q3; float q3q3 = q3*q3;if(ax*ay*az==0) return; norm = sqrt(ax*ax + ay*ay + az*az); //acc数据归一化 ax = ax /norm; ay = ay / norm; az = az / norm; // estimated direction of gravity and flux (v and w) 估计重力方向和流量/变迁 vx = 2*(q1q3 - q0q2);//四元素中xyz的表示 vy = 2*(q0q1 + q2q3); vz = q0q0 - q1q1 - q2q2 + q3q3 ; // error is sum of cross product between reference direction of fields and direction measured by sensors ex = (ay*vz - az*vy) ; //向量外积在相减得到差分就是误差 ey = (az*vx - ax*vz) ; ez = (ax*vy - ay*vx) ; exInt = exInt + ex * Ki; //对误差进行积分 eyInt = eyInt + ey * Ki; ezInt = ezInt + ez * Ki; // adjusted gyroscope measurements gx = gx + Kp*ex + exInt; //将误差PI后补偿到陀螺仪,即补偿零点漂移 gy = gy + Kp*ey + eyInt; gz = gz + Kp*ez + ezInt; //这里的gz由于没有观测者进行矫正会产生漂移,表现出来的就是积分自增或自减 // integrate quaternion rate and normalise//四元素的微分方程 q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT; q1 = q1 + ( q0*gx + q2*gz - q3*gy)*halfT; q2 = q2 + ( q0*gy - q1*gz + q3*gx)*halfT; q3 = q3 + ( q0*gz + q1*gy - q2*gx)*halfT; // normalise quaternion norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3); q0 = q0 / norm; q1 = q1 / norm; q2 = q2 / norm; q3 = q3 / norm; Q_ANGLE.Z = GYRO_I.Z;//atan2(2 * q1 * q2 + 2 * q0 * q3, -2 * q2*q2 - 2 * q3* q3 + 1)* 57.3; // yaw Q_ANGLE.Y = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch Q_ANGLE.X = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll}b)PID控制部分源码:void CONTROL(float rol_now, float pit_now, float yaw_now, float rol_tar, float pit_tar, float yaw_tar){ u16 moto1 = 0, moto2 = 0, moto3 = 0, moto4 = 0; vs16 throttle; float rol = rol_tar + rol_now; float pit = pit_tar + pit_now; float yaw = yaw_tar + yaw_now; throttle = Rc_Get.THROTTLE - 1000;//油门 节流阀 if (throttle < 0) throttle = 0; PID_ROL.IMAX = throttle / 2; Get_MxMi(PID_ROL.IMAX, 1000, 0); PID_PIT.IMAX = PID_ROL.IMAX; PID_ROL.pout = PID_ROL.P * rol; PID_PIT.pout = PID_PIT.P * pit; if (rol_tar * rol_tar < 0.1 && pit_tar * pit_tar < 0.1 && rol_now * rol_now < 30 && pit_now * pit_now < 30 && throttle > 300) { PID_ROL.iout += PID_ROL.I * rol; PID_PIT.iout += PID_PIT.I * pit; PID_ROL.iout = Get_MxMi(PID_ROL.iout, PID_ROL.IMAX, -PID_ROL.IMAX); PID_PIT.iout = Get_MxMi(PID_PIT.iout, PID_PIT.IMAX, -PID_PIT.IMAX); } else if (throttle < 200) { PID_ROL.iout = 0; PID_PIT.iout = 0; } PID_ROL.dout = PID_ROL.D * MPU6050_GYRO_LAST.X; PID_PIT.dout = PID_PIT.D * MPU6050_GYRO_LAST.Y; PID_YAW.pout = PID_YAW.P * yaw; vs16 yaw_d; if (1480 > Rc_Get.YAW || Rc_Get.YAW > 1520) { yaw_d = MPU6050_GYRO_LAST.Z + (Rc_Get.YAW - 1500) * 10; GYRO_I.Z = 0; } else yaw_d = MPU6050_GYRO_LAST.Z; PID_YAW.dout = PID_YAW.D * yaw_d; PID_ROL.OUT = PID_ROL.pout + PID_ROL.iout + PID_ROL.dout; PID_PIT.OUT = PID_PIT.pout + PID_PIT.iout + PID_PIT.dout; PID_YAW.OUT = PID_YAW.pout + PID_YAW.iout + PID_YAW.dout;/*rol 翻滚pat 俯仰角yaw //偏航*/ if (throttle > 200) {#define YAW_ADD (-30) moto1 = throttle + PID_ROL.OUT - PID_PIT.OUT +YAW_ADD+ PID_YAW.OUT/**/; moto2 = throttle - PID_ROL.OUT - PID_PIT.OUT -YAW_ADD- PID_YAW.OUT/**/; moto3 = throttle - PID_ROL.OUT + PID_PIT.OUT +YAW_ADD+ PID_YAW.OUT/**/; moto4 = throttle + PID_ROL.OUT + PID_PIT.OUT -YAW_ADD- PID_YAW.OUT/**/; } else { moto1 = 0; moto2 = 0; moto3 = 0; moto4 = 0; } if (ARMED) Moto_PwmRflash(moto1, moto2, moto3, moto4); else Moto_PwmRflash(0, 0, 0, 0);}
3.3 PID 控制
- 外环:姿态角度 PID,保证整体姿态稳定
- 内环:角速度 PID,提升动态响应速度
调参经验:
- 先调内环,再调外环
- 先调 P(比例),再加 D(微分)抑制振荡,最后加 I(积分)消除稳态误差
4. 无线遥控
遥控器端通过摇杆产生期望角度数据,nRF24L01 发送到飞控板,由飞控板解码并更新目标姿态。
数据结构示例:
typedef struct { float roll_target; float pitch_target; float yaw_target; float throttle;} RC_Command;
5. 调试与测试
- 在室内用绳子固定机架,进行姿态控制测试
- 用示波器观测 PWM 信号变化,验证 PID 输出正确性
- 室外空旷地进行低空试飞,逐步调整 PID 参数直到飞行平稳
6. 创新与优化
- 自主实现姿态解算代码,无需依赖库
- 双环 PID 结构提升控制稳定性
- 遥控器端增加 姿态锁定模式,便于新手操作
- 软件中增加 掉线保护,信号丢失时自动降落
7. 项目总结
这个四轴飞行器项目不仅让我们掌握了 STM32 在飞控中的应用,更让我们体会到跨领域协作的重要性——硬件、电路、嵌入式编程、控制理论,缺一不可。
飞行器的第一次平稳悬停,是最有成就感的时刻。