1. 项目概述:从零构建STM32无人机飞控系统
作为一名嵌入式开发者,我最近完成了一个基于STM32F103和MPU6050的无人机飞控基础系统。这个项目让我深刻体会到,一个可靠的飞控系统需要精准的姿态感知和快速响应的执行控制。通过这个项目,我实现了从传感器数据采集到电机控制的全链路开发,下面将完整分享我的实现过程和经验教训。
飞控系统的核心在于实时获取飞行器姿态并做出相应调整。我选择STM32F103C8T6作为主控芯片,主要考虑其72MHz主频足够处理传感器数据,且内置丰富的外设接口。MPU6050作为六轴惯性测量单元(IMU),能同时提供三轴加速度和三轴角速度数据,成本仅十几元,是入门级飞控的理想选择。
2. 硬件设计与连接方案
2.1 关键硬件选型解析
在硬件选择上,我经过多次对比测试确定了以下配置方案:
-
主控芯片:STM32F103C8T6最小系统板
- 选择理由:72MHz Cortex-M3内核,64KB Flash,20KB RAM,完全满足基础飞控需求
- 成本优势:淘宝售价约15元,性价比极高
- 开发便利:丰富的社区资源和成熟的工具链支持
-
姿态传感器:MPU6050模块
- 集成加速度计和陀螺仪,减少硬件复杂度
- I2C接口通信,仅需2个IO口
- 模块已内置4.7K上拉电阻,简化电路设计
-
执行机构:SG90舵机(初期验证用)
- 相比无刷电机更安全且易于观察
- 标准50Hz PWM控制,参数明确
- 成本仅10元左右,适合原型开发
2.2 硬件连接细节与避坑指南
实际接线时,我遇到了几个典型问题,这里特别提醒:
重要提示:务必先断电再接线路,我曾因热插拔烧毁过一个MPU6050模块
完整接线方案:
| STM32引脚 | 连接目标 | 注意事项 |
|---|---|---|
| 5V | MPU6050-VCC | 模块内部有稳压到3.3V |
| GND | MPU6050-GND | 必须共地,否则数据异常 |
| PB6 | MPU6050-SCL | I2C时钟线 |
| PB7 | MPU6050-SDA | I2C数据线 |
| PA8 | 舵机信号线 | TIM1_CH1 PWM输出 |
| PA9 | USB-TTL的RX | 串口调试用 |
| PA10 | USB-TTL的TX | 串口调试用 |
| 5V | 舵机VCC | 需独立供电防止电流不足 |
常见接线错误:
- I2C线序接反(SCL/SDA混淆)- 会导致通信失败
- 未共地 - 传感器数据会出现随机跳变
- 舵机电源与MCU共用 - 可能因电流不足导致复位
3. 开发环境配置实战
3.1 工具链安装与配置
我推荐以下开发环境配置流程,已经过多次验证:
-
STM32CubeMX安装
- 必须安装Java运行时环境(JRE 8+)
- 安装时选择"为所有用户安装",避免权限问题
- 安装完成后务必通过Help > Manage Packages更新F1系列固件库
-
Keil MDK5配置
- 安装时不要使用中文路径
- 安装后立即通过Pack Installer添加STM32F1xx_DFP
- 注册问题:社区版有32KB代码限制,建议购买正版或使用其他IDE
3.2 CubeMX工程关键配置
在CubeMX中创建工程时,这几个配置点至关重要:
时钟树配置:
c复制HSE(8MHz) → PLLMUL x9 → SYSCLK = 72MHz
APB1 Prescaler = 2 → PCLK1 = 36MHz
APB2 Prescaler = 1 → PCLK2 = 72MHz
外设配置要点:
- I2C1配置为标准模式(100kHz)
- TIM1配置为PWM Generation CH1模式
- USART1配置为异步模式,115200波特率
一个容易忽略的设置:
在Project Manager → Code Generator中,务必勾选"Generate peripheral initialization as a pair of .c/.h files",这样外设配置会更清晰。
4. MPU6050驱动开发详解
4.1 传感器初始化与校准
MPU6050的初始化流程需要严格按照数据手册操作:
c复制void MPU6050_Init(void) {
// 1. 唤醒设备
MPU6050_Write_Reg(PWR_MGMT_1, 0x00);
HAL_Delay(100); // 重要!等待稳定
// 2. 配置采样率
MPU6050_Write_Reg(SMPLRT_DIV, 0x07); // 125Hz
// 3. 配置低通滤波器
MPU6050_Write_Reg(CONFIG, 0x06); // 5Hz
// 4. 配置陀螺仪量程
MPU6050_Write_Reg(GYRO_CONFIG, 0x08); // ±500°/s
// 5. 配置加速度计量程
MPU6050_Write_Reg(ACCEL_CONFIG, 0x00); // ±2g
}
校准过程中的经验:
- 校准时必须保持传感器绝对静止
- 我采用1000次采样取平均的方法
- 加速度计Z轴要减去1g(16384 LSB)的偏移量
4.2 数据读取与处理
原始数据读取后需要进行以下处理:
- 字节序转换(高低字节合并)
- 减去校准偏移量
- 转换为物理量(g或°/s)
c复制void MPU6050_Read_RawData(MPU6050_DataDef *data) {
uint8_t buf[14];
MPU6050_Read_Reg(ACCEL_XOUT_H, buf, 14);
data->accel_x = (int16_t)(buf[0]<<8 | buf[1]) - data->accel_x_offset;
data->accel_y = (int16_t)(buf[2]<<8 | buf[3]) - data->accel_y_offset;
data->accel_z = (int16_t)(buf[4]<<8 | buf[5]) - data->accel_z_offset;
// 陀螺仪数据同理...
}
5. 姿态解算算法实现
5.1 加速度计角度计算
通过加速度计数据可以计算俯仰(Pitch)和横滚(Roll)角:
c复制float MPU6050_Get_Accel_Angle(MPU6050_DataDef *data, uint8_t axis) {
float ax = data->accel_x / 16384.0f;
float ay = data->accel_y / 16384.0f;
float az = data->accel_z / 16384.0f;
if(axis == 0) { // Pitch
return atan2(ay, sqrt(ax*ax + az*az)) * 180.0f / PI;
} else { // Roll
return atan2(-ax, sqrt(ay*ay + az*az)) * 180.0f / PI;
}
}
5.2 互补滤波实现
互补滤波融合了加速度计和陀螺仪的优势:
c复制void MPU6050_Complementary_Filter(MPU6050_DataDef *data, float dt) {
// 陀螺仪数据转换
float gyro_x_rate = data->gyro_x / 65.5f; // ±500°/s量程
float gyro_y_rate = data->gyro_y / 65.5f;
// 加速度计角度
float accel_pitch = MPU6050_Get_Accel_Angle(data, 0);
float accel_roll = MPU6050_Get_Accel_Angle(data, 1);
// 互补滤波融合
data->pitch = 0.98f * (data->pitch + gyro_x_rate * dt) + 0.02f * accel_pitch;
data->roll = 0.98f * (data->roll + gyro_y_rate * dt) + 0.02f * accel_roll;
}
参数调整经验:
- 0.98/0.02是常用权重比
- 高频振动环境下可调整为0.95/0.05
- dt值要准确,建议通过定时器精确测量
6. PWM输出与控制实现
6.1 定时器配置要点
SG90舵机需要50Hz(20ms周期)的PWM信号:
c复制// TIM1配置:
Prescaler = 7199 // 72MHz/(7199+1) = 10kHz
Counter Period = 199 // 10kHz/(199+1) = 50Hz
Pulse = 15 // 初始1.5ms脉宽(中立位)
6.2 PWM控制代码
实现动态调整占空比的函数:
c复制void PWM_Set_DutyCycle(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t duty) {
if(duty > 199) duty = 199; // 限制最大值
switch(channel) {
case TIM_CHANNEL_1:
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, duty);
break;
// 其他通道...
}
}
舵机控制映射:
- 0° → 占空比5 (0.5ms)
- 90° → 占空比15 (1.5ms)
- 180° → 占空比25 (2.5ms)
7. 系统集成与调试
7.1 主程序架构
c复制int main(void) {
// 初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
// 传感器初始化
MPU6050_Init();
MPU6050_Calibrate(&mpu_data);
// PWM启动
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
// 主循环
while(1) {
uint32_t now = HAL_GetTick();
if(now - last_time >= 10) { // 10ms周期
float dt = (now - last_time) / 1000.0f;
last_time = now;
MPU6050_Read_RawData(&mpu_data);
MPU6050_Complementary_Filter(&mpu_data, dt);
// 控制舵机跟随Roll角
uint16_t duty = (uint16_t)(mpu_data.roll / 90.0f * 10.0f + 15);
PWM_Set_DutyCycle(&htim1, TIM_CHANNEL_1, duty);
}
}
}
7.2 调试技巧
- 串口打印优化:
c复制printf("Pitch:%.1f° Roll:%.1f° Yaw:%.1f° PWM:%d\r\n",
mpu_data.pitch, mpu_data.roll, mpu_data.yaw, duty);
- 常见问题排查:
- 数据全零:检查I2C地址(0x68<<1)
- 角度漂移:重新校准传感器
- 舵机抖动:检查电源稳定性
8. 项目优化方向
经过基础实现后,可以考虑以下优化:
-
算法升级:
- 用卡尔曼滤波替代互补滤波
- 增加PID控制器实现稳定控制
-
硬件扩展:
- 增加无刷电机驱动
- 添加遥控接收机接口
-
功能增强:
- 实现高度保持(需气压计)
- 增加GPS定位功能
这个项目让我深刻理解了飞控系统的基本原理,从传感器数据采集到执行机构控制的完整链条。在实际操作中,最关键的几点是:精确的传感器校准、合适的滤波算法选择、以及稳定的PWM输出控制。