从零打造二自由度姿态稳定系统:STM32F103与MPU6050实战指南
1. 项目背景与核心需求
在无人机航拍、机器人关节控制等领域,姿态稳定系统扮演着关键角色。想象一下,当你用自制无人机拍摄视频时,画面总是晃动不稳;或者机器人手臂在移动时总是无法精准定位——这些问题都可以通过二自由度姿态稳定系统来解决。
这个项目适合三类人群:
- 创客爱好者:想为自己的项目添加专业级姿态控制功能
- 嵌入式初学者:通过完整项目掌握STM32开发全流程
- 机器人开发者:需要低成本解决方案实现关节稳定控制
核心硬件非常简单:
- 主控:STM32F103C8T6(蓝色pill开发板)
- 传感器:MPU6050(六轴陀螺仪加速度计)
- 执行机构:SG90舵机(9g微型舵机)
- 其他:杜邦线、电源模块、洞洞板
提示:整套硬件成本可以控制在100元以内,非常适合个人开发者和小型项目。
2. 硬件系统搭建
2.1 元器件选型与采购建议
| 元器件 | 型号 | 参考价格 | 购买渠道 |
|---|---|---|---|
| 主控板 | STM32F103C8T6 | 15-25元 | 淘宝/得捷电子 |
| 传感器 | MPU6050 | 8-15元 | 立创商城/淘宝 |
| 舵机 | SG90 | 10-15元/个 | 本地电子市场 |
| 电源 | LM2596模块 | 5-8元 | 天猫电子元件店 |
关键选购建议:
- MPU6050选择带I2C电平转换的版本(5V兼容)
- 舵机注意区分模拟和数字型号(本项目两者均可)
- 电源模块建议选择3A以上输出能力
2.2 电路连接详解
系统接线图如下:
STM32F103C8T6 <--> MPU6050 PB6(SCL) <--> SCL PB7(SDA) <--> SDA 3.3V <--> VCC GND <--> GND STM32F103C8T6 <--> SG90舵机x2 PA8 <--> 舵机1信号线 PA9 <--> 舵机2信号线 5V <--> 舵机电源+ GND <--> 舵机电源-注意:舵机电源建议单独供电,避免STM32板载稳压器过载。
3. 软件开发环境配置
3.1 工具链安装
开发需要以下软件:
- Keil MDK:STM32官方推荐IDE
- STM32CubeMX:图形化引脚配置工具
- 串口调试助手:用于数据监控
- PID调试工具:可选,如匿名科创地面站
安装步骤:
- 下载并安装Keil MDK(注意安装STM32F1支持包)
- 安装STM32CubeMX并更新器件库
- 配置USB转串口驱动(CH340/CP2102等)
3.2 工程创建流程
// 使用CubeMX生成基础工程 1. 新建工程 -> 选择STM32F103C8 2. 配置时钟树:外部晶振8MHz,系统时钟72MHz 3. 使能I2C1(PB6/PB7) 4. 配置TIM1_CH1/CH2(PA8/PA9)为PWM输出 5. 生成MDK-ARM工程4. MPU6050数据采集与处理
4.1 传感器初始化
MPU6050需要以下初始化步骤:
void MPU6050_Init(void) { // 1. 解除睡眠模式 MPU6050_Write_Byte(MPU6050_RA_PWR_MGMT_1, 0x00); // 2. 设置陀螺仪量程±2000°/s MPU6050_Write_Byte(MPU6050_RA_GYRO_CONFIG, 0x18); // 3. 设置加速度计量程±8g MPU6050_Write_Byte(MPU6050_RA_ACCEL_CONFIG, 0x10); // 4. 设置DLPF带宽42Hz MPU6050_Write_Byte(MPU6050_RA_CONFIG, 0x03); }4.2 姿态解算算法
常用的姿态解算方法有三种:
- 互补滤波:简单易实现,适合初学者
- 卡尔曼滤波:精度高但计算复杂
- DMP库:MPU6050内置处理单元
这里展示互补滤波实现:
float ComplementaryFilter(float accelAngle, float gyroRate, float dt) { static float angle = 0; float alpha = 0.98; // 滤波系数 // 陀螺仪积分 angle += gyroRate * dt; // 加速度计补偿 angle = alpha * angle + (1-alpha) * accelAngle; return angle; }5. PID控制算法实现
5.1 离散PID公式
位置式PID算法:
u(k) = Kp*e(k) + Ki*∑e(j) + Kd*(e(k)-e(k-1))增量式PID算法(更适合舵机控制):
Δu(k) = Kp*(e(k)-e(k-1)) + Ki*e(k) + Kd*(e(k)-2e(k-1)+e(k-2))5.2 代码实现
typedef struct { float Kp, Ki, Kd; float error[3]; float output; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float feedback) { // 计算当前误差 pid->error[2] = setpoint - feedback; // 增量计算 float delta = pid->Kp * (pid->error[2] - pid->error[1]) + pid->Ki * pid->error[2] + pid->Kd * (pid->error[2] - 2*pid->error[1] + pid->error[0]); // 更新误差历史 pid->error[0] = pid->error[1]; pid->error[1] = pid->error[2]; // 限制输出范围 pid->output += delta; if(pid->output > 1000) pid->output = 1000; if(pid->output < 0) pid->output = 0; return pid->output; }5.3 PID参数整定技巧
经验调参法:
- 先将Ki和Kd设为0,逐步增大Kp直到系统开始振荡
- 取振荡时Kp值的50%作为基准
- 逐步增加Ki消除静差
- 最后加入Kd抑制超调
典型参数范围:
- 俯仰轴:Kp=3.0, Ki=0.05, Kd=0.5
- 横滚轴:Kp=2.5, Ki=0.03, Kd=0.3
6. 系统集成与调试
6.1 主程序框架
int main(void) { // 硬件初始化 HAL_Init(); SystemClock_Config(); PWM_Init(); MPU6050_Init(); // PID控制器初始化 PID_Controller pitch_pid = {3.0, 0.05, 0.5}; PID_Controller roll_pid = {2.5, 0.03, 0.3}; while(1) { // 1. 读取传感器数据 MPU6050_Read_Data(); // 2. 姿态解算 float pitch = ComplementaryFilter(accel_pitch, gyro_y, 0.01); float roll = ComplementaryFilter(accel_roll, gyro_x, 0.01); // 3. PID计算 float pitch_out = PID_Update(&pitch_pid, 0, pitch); float roll_out = PID_Update(&roll_pid, 0, roll); // 4. 输出PWM PWM_SetDuty(TIM1, CH1, 1500 + pitch_out); PWM_SetDuty(TIM1, CH2, 1500 + roll_out); // 5. 延时10ms HAL_Delay(10); } }6.2 常见问题排查
问题1:舵机抖动不工作
- 检查电源电压是否足够(建议5V 2A以上)
- 确认PWM信号频率为50Hz(周期20ms)
- 测量信号线连接是否可靠
问题2:MPU6050无数据
- 用逻辑分析仪检查I2C波形
- 确认上拉电阻已接(通常模块已内置)
- 尝试降低I2C时钟速度(如100kHz)
问题3:系统响应迟钝
- 检查主循环执行周期是否稳定
- 尝试减小互补滤波系数alpha
- 适当增大PID的Kp参数
7. 进阶优化方向
- 动态参数调整:根据运动状态自动调节PID参数
- 上位机监控:通过串口实时绘制姿态曲线
- 机械结构优化:使用3D打印件减少舵机负载
- 低功耗模式:在空闲时进入睡眠状态
实际测试中发现,使用碳纤维杆作为云台支架可以显著减少振动干扰。另外,在代码中加入死区处理可以有效避免舵机在平衡点附近的小幅抖动:
// 在PID输出前加入死区处理 if(fabs(error) < 0.5f) { output = 0; }对于需要更高精度的场景,可以考虑升级到STM32F4系列芯片,利用其硬件FPU加速浮点运算。不过对于大多数业余项目,STM32F103已经能够提供足够性能。