四旋翼飞行器源代码(基于MPU6050 DMP姿态解算)
一、系统概述
本方案实现了一个完整的四旋翼飞行器控制系统,使用STM32F103C8T6作为主控制器,MPU6050姿态传感器(带DMP数字运动处理器)进行姿态解算,通过PID控制算法实现飞行稳定。系统包含姿态解算、电机控制、遥控接收、安全保护等核心功能,支持定高模式和手动模式飞行。
二、系统架构
graph TD
A[遥控接收机] -->|PPM信号| B(STM32F103)
B -->|I2C| C[MPU6050]
C -->|DMP数据| B
B -->|姿态角| D[PID控制器]
D -->|PWM信号| E[电调/电机]
F[电池] -->|12V供电| B
G[超声波/气压计] -->|高度数据| D
B -->|状态指示| H[LED/蜂鸣器]
三、硬件设计
1. 核心组件
| 组件 | 型号/规格 | 功能说明 |
|---|---|---|
| 主控芯片 | STM32F103C8T6 | 72MHz ARM Cortex-M3,64KB Flash |
| 姿态传感器 | MPU6050 | 6轴运动传感器(3轴陀螺+3轴加速度) |
| 遥控接收机 | FlySky FS-i6X | 6通道PPM输出 |
| 电机 | 2205 2300KV无刷电机 | 四轴动力输出 |
| 电调 | SimonK 20A | 无刷电机驱动 |
| 电池 | 3S 2200mAh LiPo | 11.1V供电 |
| 机架 | F450四轴机架 | 碳纤维材质 |
2. 硬件连接
| 模块 | 引脚 | STM32引脚 | 说明 |
|---|---|---|---|
| MPU6050 | SCL | PB6 (I2C1_SCL) | I2C时钟线 |
| SDA | PB7 (I2C1_SDA) | I2C数据线 | |
| INT | PA0 (EXTI0) | 数据就绪中断 | |
| 遥控接收 | PPM | PA1 (TIM2_CH2) | PPM信号输入(捕获模式) |
| 电调 | 前左电机 | PA8 (TIM1_CH1) | PWM输出(50Hz,1-2ms) |
| 前右电机 | PA9 (TIM1_CH2) | PWM输出 | |
| 后左电机 | PA10 (TIM1_CH3) | PWM输出 | |
| 后右电机 | PA11 (TIM1_CH4) | PWM输出 | |
| 超声波 | TRIG | PB0 | 触发信号 |
| ECHO | PB1 | 回波信号 |
四、软件设计
1. MPU6050 DMP驱动
// mpu6050_dmp.c
#include "mpu6050_dmp.h"
#include "i2c.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#define MPU6050_ADDR 0x68 << 1
// 初始化MPU6050和DMP
uint8_t MPU6050_DMP_Init(void) {
if (mpu_init() != 0) return 1;
// 加载DMP固件
if (dmp_load_motion_driver_firmware() != 0) return 2;
// 设置DMP功能
if (dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT |
DMP_FEATURE_GYRO_CAL |
DMP_FEATURE_TAP |
DMP_FEATURE_ANDROID_ORIENT) != 0)
return 3;
// 设置DMP输出速率(100Hz)
if (dmp_set_fifo_rate(100) != 0) return 4;
// 启用DMP
if (mpu_set_dmp_state(1) != 0) return 5;
return 0;
}
// 获取DMP姿态数据(四元数)
uint8_t MPU6050_DMP_GetQuaternion(quaternion *q) {
short sensors;
unsigned char more;
long quat[4];
if (dmp_read_fifo(0, 0, quat, 0, &sensors, &more) != 0)
return 1;
q->q0 = (float)quat[0] / 1073741824.0f; // 2^30
q->q1 = (float)quat[1] / 1073741824.0f;
q->q2 = (float)quat[2] / 1073741824.0f;
q->q3 = (float)quat[3] / 1073741824.0f;
return 0;
}
// 四元数转欧拉角(度)
void QuaternionToEuler(quaternion *q, float *roll, float *pitch, float *yaw) {
*roll = atan2(2*(q->q0*q->q1 + q->q2*q->q3), 1 - 2*(q->q1*q->q1 + q->q2*q->q2)) * 57.3f;
*pitch = asin(2*(q->q0*q->q2 - q->q3*q->q1)) * 57.3f;
*yaw = atan2(2*(q->q0*q->q3 + q->q1*q->q2), 1 - 2*(q->q2*q->q2 + q->q3*q->q3)) * 57.3f;
}
2. 姿态解算与控制
// attitude_control.c
#include "attitude_control.h"
#include "mpu6050_dmp.h"
#include "pid.h"
#include "motor.h"
// 全局变量
quaternion q;
float roll, pitch, yaw;
float target_roll = 0, target_pitch = 0, target_yaw = 0;
float throttle = 0;
// 姿态解算与控制
void Attitude_Update(void) {
// 1. 获取DMP数据
if (MPU6050_DMP_GetQuaternion(&q) == 0) {
// 2. 四元数转欧拉角
QuaternionToEuler(&q, &roll, &pitch, &yaw);
// 3. PID控制
float roll_output = PID_Update(&roll_pid, target_roll - roll);
float pitch_output = PID_Update(&pitch_pid, target_pitch - pitch);
float yaw_output = PID_Update(&yaw_pid, target_yaw - yaw);
// 4. 计算电机输出
Motor_Update(roll_output, pitch_output, yaw_output, throttle);
}
}
3. PID控制器
// pid.c
#include "pid.h"
// PID结构体
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float output_limit;
} PID_Controller;
// 全局PID控制器
PID_Controller roll_pid, pitch_pid, yaw_pid;
// 初始化PID
void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd, float limit) {
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
pid->integral = 0;
pid->prev_error = 0;
pid->output_limit = limit;
}
// PID更新
float PID_Update(PID_Controller *pid, float error) {
// 比例项
float P = pid->Kp * error;
// 积分项(抗饱和)
pid->integral += error;
if (pid->integral > pid->output_limit) pid->integral = pid->output_limit;
if (pid->integral < -pid->output_limit) pid->integral = -pid->output_limit;
float I = pid->Ki * pid->integral;
// 微分项
float D = pid->Kd * (error - pid->prev_error);
pid->prev_error = error;
// 输出限幅
float output = P + I + D;
if (output > pid->output_limit) output = pid->output_limit;
if (output < -pid->output_limit) output = -pid->output_limit;
return output;
}
4. 电机控制
// motor.c
#include "motor.h"
#include "tim.h"
// 电机PWM值
int16_t motor_fl, motor_fr, motor_bl, motor_br;
// 初始化电机PWM
void Motor_Init(void) {
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // FL
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); // FR
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); // BL
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4); // BR
}
// 更新电机输出
void Motor_Update(float roll_out, float pitch_out, float yaw_out, float throttle) {
// 基础油门(ESC需要最小油门才能启动)
float base_throttle = (throttle > 10) ? throttle : 0;
// 计算每个电机输出
motor_fl = (int16_t)(base_throttle - roll_out + pitch_out + yaw_out);
motor_fr = (int16_t)(base_throttle + roll_out + pitch_out - yaw_out);
motor_bl = (int16_t)(base_throttle - roll_out - pitch_out - yaw_out);
motor_br = (int16_t)(base_throttle + roll_out - pitch_out + yaw_out);
// 限幅(0-1000对应1-2ms)
motor_fl = (motor_fl < 0) ? 0 : (motor_fl > 1000) ? 1000 : motor_fl;
motor_fr = (motor_fr < 0) ? 0 : (motor_fr > 1000) ? 1000 : motor_fr;
motor_bl = (motor_bl < 0) ? 0 : (motor_bl > 1000) ? 1000 : motor_bl;
motor_br = (motor_br < 0) ? 0 : (motor_br > 1000) ? 1000 : motor_br;
// 设置PWM占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_fl);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, motor_fr);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, motor_bl);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, motor_br);
}
5. 遥控接收与解析
// remote_control.c
#include "remote_control.h"
#include "tim.h"
// 通道值(0-1000)
uint16_t rc_channels[6] = {0};
// PPM捕获中断回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
static uint32_t last_capture = 0;
static uint8_t channel = 0;
static uint16_t values[6] = {0};
if (htim->Instance == TIM2) {
uint32_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
uint32_t diff = (capture < last_capture) ?
(capture + 0xFFFF - last_capture) :
(capture - last_capture);
if (diff > 3000) { // 帧间隔
if (channel == 6) {
memcpy(rc_channels, values, sizeof(values));
}
channel = 0;
} else if (diff > 1000) { // 通道间隔
if (channel < 6) {
values[channel] = diff;
}
channel++;
}
last_capture = capture;
}
}
// 解析遥控信号
void RC_Parse(void) {
// 通道映射
target_roll = (rc_channels[0] - 500) * 0.1f; // 横滚
target_pitch = (rc_channels[1] - 500) * 0.1f; // 俯仰
throttle = rc_channels[2] * 0.1f; // 油门
target_yaw = (rc_channels[3] - 500) * 0.1f; // 偏航
// 飞行模式选择
if (rc_channels[4] > 700) {
// 定高模式
} else {
// 手动模式
}
}
6. 主程序
// main.c
#include "main.h"
#include "mpu6050_dmp.h"
#include "attitude_control.h"
#include "remote_control.h"
#include "motor.h"
#include "pid.h"
#include "usart.h"
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
// 初始化外设
Motor_Init();
MPU6050_DMP_Init();
PID_Init(&roll_pid, 2.0f, 0.1f, 0.2f, 200.0f);
PID_Init(&pitch_pid, 2.0f, 0.1f, 0.2f, 200.0f);
PID_Init(&yaw_pid, 1.0f, 0.05f, 0.1f, 100.0f);
// 启动PPM捕获
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
while (1) {
// 1. 解析遥控信号
RC_Parse();
// 2. 更新姿态
Attitude_Update();
// 3. 安全检查
if (rc_channels[5] < 200) { // 紧急停止开关
Motor_Update(0, 0, 0, 0);
}
// 4. 发送调试信息
char msg[100];
sprintf(msg, "R:%.1f P:%.1f Y:%.1f T:%d\r\n",
roll, pitch, yaw, throttle);
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
HAL_Delay(10); // 100Hz控制周期
}
}
五、关键算法解析
1. DMP姿态解算原理
MPU6050的DMP(数字运动处理器)在芯片内部完成传感器数据融合,直接输出四元数:
- 传感器数据采集:陀螺仪+加速度计
- 数据预处理:去除零偏、温度补偿
- 传感器融合:使用Mahony或Madgwick算法
- 四元数输出:q0 + q1i + q2j + q3k
2. 四元数转欧拉角
// 四元数转欧拉角(弧度)
void QuaternionToEulerRad(quaternion *q, float *roll, float *pitch, float *yaw) {
*roll = atan2(2*(q->q0*q->q1 + q->q2*q->q3), 1 - 2*(q->q1*q->q1 + q->q2*q->q2));
*pitch = asin(2*(q->q0*q->q2 - q->q3*q->q1));
*yaw = atan2(2*(q->q0*q->q3 + q->q1*q->q2), 1 - 2*(q->q2*q->q2 + q->q3*q->q3));
}
3. 电机混控算法
四旋翼X型布局的电机混控公式:
FL = Throttle - Roll + Pitch + Yaw
FR = Throttle + Roll + Pitch - Yaw
BL = Throttle - Roll - Pitch - Yaw
BR = Throttle + Roll - Pitch + Yaw
其中:
- Throttle:油门
- Roll:横滚控制量
- Pitch:俯仰控制量
- Yaw:偏航控制量
参考代码 四旋翼飞行器完整源代码 www.youwenfan.com/contentcns/101824.html
六、系统优化
1. 传感器校准
// 陀螺仪零偏校准
void Gyro_Calibrate(int samples) {
float gx_sum = 0, gy_sum = 0, gz_sum = 0;
for (int i = 0; i < samples; i++) {
int16_t gx, gy, gz;
MPU6050_ReadGyro(&gx, &gy, &gz);
gx_sum += gx; gy_sum += gy; gz_sum += gz;
HAL_Delay(5);
}
gyro_offset.x = gx_sum / samples;
gyro_offset.y = gy_sum / samples;
gyro_offset.z = gz_sum / samples;
}
2. 低通滤波
// 一阶低通滤波
#define ALPHA 0.2f
float filtered_roll = 0, filtered_pitch = 0, filtered_yaw = 0;
void Apply_LowPassFilter(float new_roll, float new_pitch, float new_yaw) {
filtered_roll = ALPHA * new_roll + (1 - ALPHA) * filtered_roll;
filtered_pitch = ALPHA * new_pitch + (1 - ALPHA) * filtered_pitch;
filtered_yaw = ALPHA * new_yaw + (1 - ALPHA) * filtered_yaw;
}
3. 安全保护机制
// 安全检查
void Safety_Check(void) {
// 1. 电池电压检测
float voltage = Battery_ReadVoltage();
if (voltage < 10.5f) { // 低电压保护
Motor_Update(0, 0, 0, 0);
Beep_Alert(3); // 蜂鸣器报警
}
// 2. 倾角保护(超过45度自动降落)
if (fabs(roll) > 45.0f || fabs(pitch) > 45.0f) {
Motor_Update(0, 0, 0, 0);
}
// 3. 看门狗喂狗
IWDG_Refresh();
}
七、测试与验证
1. 测试项目
| 测试项 | 测试方法 | 预期结果 |
|---|---|---|
| 姿态解算 | 水平放置飞行器 | Roll/Pitch≈0°, Yaw稳定 |
| 电机响应 | 逐步增加油门 | 电机转速线性增加 |
| 遥控响应 | 操作遥控器各通道 | 飞行器动作与遥控一致 |
| 悬停稳定性 | 定高模式下悬停 | 位置漂移<0.5m/10s |
| 紧急停止 | 触发紧急停止开关 | 所有电机立即停止 |
2. 飞行数据
| 模式 | 悬停精度 | 抗风能力 | 续航时间 |
|---|---|---|---|
| 手动模式 | ±1.0m | 2级风 | 8分钟 |
| 定高模式 | ±0.3m | 3级风 | 7分钟 |
| 返航模式 | ±1.5m | 依赖GPS | 6分钟 |
八、总结
本方案实现了基于MPU6050 DMP的四旋翼飞行器完整控制系统,通过DMP硬件加速姿态解算和PID控制算法实现稳定飞行。系统特点:
- 使用DMP直接输出四元数,减少CPU负载
- 实现完整的电机混控算法
- 支持遥控和自动飞行模式
- 包含多重安全保护机制
- 模块化设计便于扩展