【基于STM32的PID平衡车实战】手把手教你调出“稳如泰山“的自平衡小车!
目录
前言
本文详细记录了从零开始实现基于STM32的PID自平衡小车全流程。通过实际可运行的代码,深入剖析直立环、速度环、转向环的三环串级PID控制策略,并结合MPU6050传感器数据融合、编码器测速、电机驱动等关键技术。文中不仅提供完整控制逻辑源码,还分享多日调参实战经验,解决高频抖动、跑偏、转向失灵等常见问题。无论你是电子竞赛选手、机器人爱好者还是嵌入式学习者,都能从中获得可直接复用的项目经验。
一、项目概述:我们要做什么?
目标:制作一辆能够自主保持直立,并响应遥控指令(前进、后退、转向)的两轮自平衡小车STM32 F103C8T6入门PID控制算法指南(以平衡小车为例)。
核心挑战:
如何感知自身姿态? -> MPU6050(陀螺仪+加速度计)传感器,通过DMP库或滤波算法获取精确的倾角与角速度 。
如何保持直立? -> 直立环PD控制。这是小车的“本能反射”。
如何控制速度? -> 速度环PI控制。防止小车因微小偏差而越跑越快,实现定速与刹车。
如何实现转向? -> 转向环PD控制。通过差速实现灵活转向。
最终效果:小车在无外力下稳定直立,轻推可回正,通过遥控器可控制其前后移动和左右转向。
二、核心硬件选型建议
| 模块 | 型号/参数 | 备注 |
|---|---|---|
| 主控 | STM32F103C8T6 (蓝桥杯/正点原子最小系统) | Cortex-M3,72MHz,资源足够 |
| 姿态传感器 | MPU6050 (集成DMP) | 必须支持DMP,极大降低姿态解算难度 |
| 电机 | 25GA370减速电机 (带AB相编码器) | 减速比1:30,编码器11线,供电6V |
| 电机驱动 | TB6612FNG | 双路驱动,性能优于L298N |
| 电池 | 2S锂电池 (7.4V) | 容量建议1500mAh以上 |
| 车架 | 某宝平衡小车底盘套件 | 含电机、轮子、亚克力板等 |
三、核心控制算法详解:三环PID串级控制
这是本文的核心思想,也是平衡车能够稳定运行的理论基础零基础制作平衡小车【连载】13—平衡小车代码讲解(附源码)。
控制回路框架图
链接: Image source.

各环代码实现与解析
1.直立环(内环,核心)
作用:快速响应姿态变化,保持车身直立。控制周期要短(本例为5ms)。
// 直立环PD控制器
// 输入:期望角度(Med)、真实角度(Angle)、Y轴角速度(gyro_Y)
int Vertical(float Med, float Angle, float gyro_Y) {
int temp;
temp = Vertical_Kp * (Angle - Med) + Vertical_Kd * gyro_Y;
return temp; // 输出为PWM基础值
}
参数范围:Vertical_Kp(约180),Vertical_Kd(约2)。Kd是镇定关键。
2. 速度环(外环,PI控制)
作用:将编码器测量的轮速与目标速度比较,通过PI运算输出一个角度调整量,叠加到直立环的目标角度上,从而实现“想加速就前倾,想减速就后仰”。
int Velocity(int Target, int encoder_L, int encoder_R) {
static int Err_LowOut_last, Encoder_S;
static float a = 0.7; // 低通滤波系数
int Err, Err_LowOut, temp;
Velocity_Ki = Velocity_Kp / 200; // Ki与Kp关联设置,简化调参
// 1. 偏差 = (左+右编码器值) - 目标速度
Err = (encoder_L + encoder_R) - Target;
// 2. 一阶低通滤波,消除脉冲干扰
Err_LowOut = (1 - a) * Err + a * Err_LowOut_last;
Err_LowOut_last = Err_LowOut;
// 3. 积分分离与限幅,防止积分饱和
Encoder_S += Err_LowOut;
Encoder_S = Encoder_S > 20000 ? 20000 : (Encoder_S < (-20000) ? (-20000) : Encoder_S);
if(stop == 1) Encoder_S = 0, stop = 0; // 停车时清空积分
// 4. PI计算
temp = Velocity_Kp * Err_LowOut + Velocity_Ki * Encoder_S;
return temp; // 输出为角度调整量
}
- 低通滤波:滤除编码器计数中的毛刺,使速度信号更平滑。
- 积分限幅:必须做!否则积分项会无限累积(“windup”),导致系统失控。
- 参数范围:Velocity_Kp(约0.6),Velocity_Ki(约0.003)。数值很小,但影响巨大。
3. 转向环(独立环,PD控制)
作用:根据遥控转向指令和Z轴角速度,计算左右电机的差速值,实现灵活、稳定的转向。
int Turn(float gyro_Z, int Target_turn) {
int temp;
temp = Turn_Kp * Target_turn + Turn_Kd * gyro_Z;
return temp; // 输出为转向差速值
}
- 转向约束:代码中一个精妙的设计是,当没有转向指令时,Turn_Kd较大,利用Z轴陀螺仪抑制小车自身意外偏转(“转向阻尼”);当有主动转向指令时,Turn_Kd设为0,消除阻尼,让转向更灵敏。
- 参数范围:Turn_Kp(约10),Turn_Kd(0~0.6)。
4. 最终PWM合成
// 三环输出合成
Velocity_out = Velocity(Target_Speed, Encoder_Left, Encoder_Right); // 速度环输出是角度调整量
Vertical_out = Vertical(Velocity_out + Med_Angle, roll, gyrox); // 直立环核心
Turn_out = Turn(gyroz, Target_turn); // 转向环差速
PWM_out = Vertical_out; // 直立环输出作为PWM基础值
MOTO1 = PWM_out - Turn_out; // 左轮 = 基础值 - 差速
MOTO2 = PWM_out + Turn_out; // 右轮 = 基础值 + 差速
Limit(&MOTO1, &MOTO2); // PWM限幅
Load(MOTO1, MOTO2); // 加载到电机驱动
}
- Med_Angle(机械中值):小车物理平衡时的实际角度。因安装、重心偏移,通常不是0。必须通过实际测量校准。
四、PID参数整定实战:从“摇头晃脑”到“稳如老狗”
调试顺序:先内环(直立),后外环(速度),最后转向环。史上最详细的PID教程——理解PID原理及优化算法
4.1 直立环调参(先P后D)
- 准备:用支架或手稳住小车,连接上位机(如VOFA+)观察角度和PWM波形。参数先清零。
- 只加P:设Kp=10, Kd=0。上电,小车会向一边加速倒下。逐渐增大Kp(20, 50, 100…),直到小车在平衡点附近持续、稳定地来回摆动。此时Kp约为临界值。
- 加入D:在摆动的基础上,逐渐加入Kd(从0.5开始)。观察现象:摆动幅度会明显减小。继续增大Kd,直到小车能勉强站立2-3秒。
- 微调:此时轻推小车,它应有“弹性”地回正。如果回正后来回振荡(“点头”),增大Kd或减小Kp。如果反应迟钝,推了没反应,减小Kd或增大Kp。
4.2 速度环调参(先P后I)
- 前提:直立环已基本稳定,小车能用手扶着在原地站立。
- 只加P:设Ki=0,给Kp一个极小值(如0.1)。目标速度设为0。松手,观察现象:
- 小车可能缓慢向一个方向移动 -> 说明P在起作用,但太小。
- 小车突然加速冲向一边 -> 说明P太大,或极性反了(检查编码器方向)。
- 调整P:缓慢增大Kp,让小车在受到轻微速度干扰(如轻推)后,能缓慢回到原点,而不是越跑越快。P太大会导致小车前后“抽搐式”移动。
- 加入I:非常小心地增加Ki(如Kp/200)。I的作用是消除静态误差(比如地面不平导致的小范围漂移)。I过大会引起低频大幅振荡,非常危险。
4.3 转向环调参
- 静态转向:抬起小车,发送转向指令。观察左右轮是否按预期差速转动。调整Turn_Kp改变转向灵敏度。
- 动态转向:放下小车,在直立和速度环工作的基础上测试转向。如果转向时车身晃动大,适当增大Turn_Kd(但注意代码中的“转向约束”逻辑,主动转向时Kd=0)。
五、 关键代码模块与调试技巧
5.1 传感器数据获取
// 1. 编码器(定时器编码器模式)
Encoder_Left = __HAL_TIM_GET_COUNTER(&htim2);
__HAL_TIM_SET_COUNTER(&htim2, 0); // 读取后清零
// 注意:右轮编码器方向取反,因为电机对称安装
Encoder_Right = -__HAL_TIM_GET_COUNTER(&htim4);
__HAL_TIM_SET_COUNTER(&htim4, 0);
// 2. MPU6050 DMP库(强烈推荐!)
if(mpu_dmp_get_data(&pitch, &roll, &yaw) == 0){
// 姿态角已由DMP解算好,单位:度
MPU_Get_Gyroscope(&gyrox, &gyroy, &gyroz); // 读取原始角速度
}
DMP库是成功的关键!它内部完成了复杂的传感器融合和四元数解算,直接输出稳定的欧拉角,极大降低了开发难度。
5.2 控制周期与定时器
使用一个基本定时器(如TIM3)产生精确的5ms中断,在中断服务函数中调用Control()函数。定时必须准确,这是数字控制器的“心跳”。
// HAL库定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM3) {
Control(); // 核心控制函数,每5ms执行一次
}
}
5.3 蓝牙遥控与指令解析
通过串口中断接收蓝牙指令(如手机APP发送的‘F’, ‘B’, ‘L’, ‘R’字符),设置全局标志位(Fore, Back, Left, Right),在主控制循环中响应。
// 串口中断服务函数 (简化)
void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
char cmd = USART_ReceiveData(USART1);
switch(cmd) {
case 'F': Fore = 1; break;
case 'B': Back = 1; break;
// ... 其他指令
}
}
}
六 、核心硬件选型建议
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 一上电就猛转 | 1. 电机线接反,2. 角度极性反,3. PWM输出极性反 | 1. 交换电机线测试,2. 在代码中给roll角乘以-1,3. 检查电机驱动逻辑 |
| 高频剧烈抖动 | 1. Kd过大,2. 控制周期不稳定,3. 机械结构松动 | 1. 减小Kd,2. 检查定时器配置,确保中断周期准确 |
| 缓慢向一边漂移 | 1. 机械中值不准,2. 速度环I项过大或过小,3. 地面不平 | 1. 重新校准Med_Angle,2. 微调速度环Ki,或加入死区 |
| 转向不灵敏或过冲 | 1. 转向环Kp/Kd不合适,2. 编码器安装松动,3. 电池电量低 | 1. 调整转向环参数,2. 紧固编码器码盘,3. 充电!电量不足时控制性能急剧下降 |
| 用手能稳住,松手就倒 | 1. 直立环参数未调好,2. 重心过高,3. 速度环在捣乱 | 1. 回归步骤,重新调直立环PD,2. 降低重心(电池下置),3. 先将速度环参数设为0,只调直立环 |
结语
总之,PID参数的调试是一门结合理论知识与实践经验的精妙艺术。恰当的参数能让STM32平衡小车在受到扰动时迅速稳定恢复,展现出令人满意的动态性能。通过本文对三环串级PID控制原理的剖析、硬件平台的搭建、核心代码的实现以及分步调参方法的详细介绍,希望读者能够成功完成基于STM32与MPU6050的自平衡小车项目,并在此过程中,真正领悟到PID算法的精髓及其在嵌入式系统中的应用魅力PID深度解剖。
源码获取与交流
本文涉及的所有完整工程源码(包含STM32 HAL库驱动、MPU6050 DMP库、完整控制逻辑、工程文件等),已整理归档。
获取方式:关注我的CSDN博客,在本文评论区留言"PID平衡车源码",或私信我,我会将GitHub仓库链接或网盘链接发送给你。
调参过程中遇到任何问题,欢迎在评论区留言,我们一起讨论解决!
版权声明: 本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
更多推荐
所有评论(0)