本文主要介绍作者自己制作STM32平衡车的过程,因为作者本人主要为了学习PID算法,故没有加很多功能。
硬件部分:
不想自己画时间去设计主控板,故使用网上开源的底板,很好用,如果也有相同需求的小伙伴,也可以照抄。
主控使用STM32F103C8T6,降压模块是某宝上的成品模块,大家如果自己制作,也很简单,例如7805等LDO芯片都可以使用,外围电路少。电机驱动是一个集成的半桥模块,原理就是开关MOS管,如果大家有兴趣,也可以去制作,这个过程还是很有意思的。陀螺仪使用的MPU6050,虽然零飘很严重,但我们对精度并没有很大的要求,直接使用就行了。(后面的四轴飞行器制作,使用的另一款六轴传感器)
硬件部分就是这样。
软件部分:
平衡车的核心部分首先是这个MPU6050,可以说它是平衡车的心脏,我使用的是软件I2C和DMP库的姿态解算,比较简单,还可以使用四元数解算配合卡尔曼滤波等滤波算法一起使用(大多用于自制飞行器),我这里并没有很高的精度需求,故不需要使用。所以使用的是DMP库。(文末会附上源码)
得到解算的欧拉角后,我们就可以调直立环了,这就不得不提到大名鼎鼎的PID。
PID:即为对偏差进行比例、积分和微分控制。由三个元素构成,分别是比例(P),积分(I),微分(D)。工程中P必然存在,在P的基础上又有如PI控制(比例积分),PD控制(比例微分),PID控制(比例积分微分)。
比例项:提高响应速度,减小静差。
积分项:消除稳态误差。
微分项:减小震荡以及超调。
而PID又分为位置式PID和增量式PID。
位置式PID: PWM_out=Kp×e(k)+Ki× Σe(k) +Kd×[e(k)-e(k-1)]
e(k):本次偏差
e(k-1):上一次偏差
Σe(k):e(k)以及以前的偏差的累积和,其中k为1,2,…
PWM_out:输出
int Position_PID(int Encoder, int Target)
{
static float Bias, PWM_out, Integral_bias, Last_bias;
Bias = Encoder-Target; // 计算偏差
Integral_bias += Bias; // 计算偏差的积分
// 位置式PID控制器公式
PWM_out = Position_Kp*Bias+Position_Ki* Integral_bias+ Position_KD*(Bias- Last_bias)
Last_bias = Bias; // 保存上一次偏差
return PWM_out; // 输出
}
增量式PID: PWM_out=Kp×e(k)+Ki× e(k-1) +Kd×e(k-2)
e(k):本次偏差
e(k-1):上一次偏差
e(k-2):上上次偏差
PWM_out:输出
float IncPIDCal(PID *pid, int32_t NowValue, int32_t AimValue)
{
int32_t iError; //当前误差值
float Output; //控制输出增量值
iError = AimValue - NowValue; //目标值与当前值之差
Output = (pid->P * iError) //E[k]项
-(pid->I * pid->LastError) //E[k-1]项
+(pid->D * pid->PrevError); //E[k-2]项
pid->PrevError = pid->LastError; //存储误差,用于下次计算
pid->LastError = iError;
return(Output); //返回增量值
}
大家可根据合适的选择。
位置式PID:
1、对单片机算力要求更高,因为它的积分项是过去所有时刻的总和,工作量大。(这点作者本人并没有很好的体会,因为现在的单片机算力都比较强,并且工程量不大)
2、因为最新的单片机主频普遍比较高,积分项的累计会很快,如果不对积分项进行及时有效地限幅,肯能会发生重大事故。
3、并且积分项由于累加缘故,会表现出一定的滞后性,所以一般位置式PID只使用PD。
增量式PID:
1、只使用前后三次测量值的偏差,因此没有误差累加,且计算量小。
2、积分截断效应大,有稳态误差;溢出的影响大。有的被控对象用增量式则不太好。
但不论是哪一种PID,在使用时,要对输出和积分项进行适当的限幅,以防发生重失误,可在后来慢慢加大限幅。
直立环:
这里使用的是PD,需要各位慢慢调,只有直立环并不能很好的站住,后续需要加入速度环才能站稳。输入的真实角度和真实角速度是由MPU6050的安装方向所决定,并不是一尘不变,各位读者需要注意一下。
Kp,Kd的方向:这也是一个易错点,平衡车向哪边倒,轮就向哪边滚,不知道大家能否理解,如下图所示,所以大家调节之前一定要确认好极性,不然做很多无用功。
调节方法:将Kd=0,慢慢加Kp,一点点加,当小车在稳态附近时产生较高频率大幅度晃动时,立马断电,以防烧毁电机,然后再慢慢的降低Kp,降低到不晃动或小幅度晃动即可,切记不能着急,慢慢来。确定了Kp后,将Kp乘以0.6~0.8,(我也不知道原因,工程经验)然后确定好Kp方向慢慢加大Kp值,当小车出现高频小幅度震荡时,慢慢降低Kp,前后尝试,这是小车能在稳态停住几秒,但并不能抵抗外力,很容易倒,需加入速度环。
/*********************
直立环PD控制器:Kp*Ek+Kd*Ek_D
入口:期望角度、真实角度、真实角速度
出口:直立环输出
*********************/
int Vertical(float Med,float Angle,float gyro_Y)
{
int PWM_out;
PWM_out=Vertical_Kp*(Angle-Med)+Vertical_Kd*(gyro_Y-0);
return PWM_out;
}
速度环:
速度环需要先对编码器进行采样,切记不可以放在主函数循环中,放在中断里进行采样,将采样的值传入速度环函数,进行低通滤波,(这里的低通滤波非必要,可不进行)低通滤波中的权重a需视情况而定,(推荐大家一个上位机软件Vofa+,可以将滤波前后的波形打出来进行对比),这里使用的时PI,并进行有效限幅,调节方法和直立环一样。
/*********************
速度环PI:Kp*Ek+Ki*Ek_S
*********************/
int Velocity(int Target,int encoder_left,int encoder_right)
{
static int Encoder_S,EnC_Err_Lowout_last,PWM_out,Encoder_Err,EnC_Err_Lowout;
float a=0.7;
//1.计算速度偏差
Encoder_Err=((encoder_left+encoder_right)-Target);//舍去误差--我的理解:能够让速度为"0"的角度,就是机械中值。
//2.对速度偏差进行低通滤波
//low_out=(1-a)*Ek+a*low_out_last;
EnC_Err_Lowout=(1-a)*Encoder_Err+a*EnC_Err_Lowout_last;//使得波形更加平滑,滤除高频干扰,防止速度突变。
EnC_Err_Lowout_last=EnC_Err_Lowout;//防止速度过大的影响直立环的正常工作。
//3.对速度偏差积分,积分出位移
Encoder_S+=EnC_Err_Lowout;
//4.积分限幅
Encoder_S=Encoder_S>20000?20000:(Encoder_S<(-20000)?(-20000):Encoder_S);
if(stop==1)Encoder_S=0,stop=0;//清零积分量
//5.速度环控制输出计算
PWM_out=Velocity_Kp*EnC_Err_Lowout+Velocity_Ki*Encoder_S;
return PWM_out;
}
转向环:
/*********************
转向环:系数*Z轴角速度+系数*遥控数据
*********************/
int Turn(int gyro_Z,int RC)
{
int PWM_out;
//这不是一个严格的PD控制器,Kd针对的是转向的约束,但Kp针对的是遥控的转向。
PWM_out=Turn_Kd*gyro_Z + Turn_Kp*RC;
return PWM_out;
}
三环代码大概如此,在本工程中,我将三环进行并联,直接相加就可以,但切记速度不可过快,以防影响直立环。(在后面的文章中,会在四轴四轴飞行器专题详细介绍串联PID,更加稳定的调节。)
作者这篇文章主要给大家介绍PID算法,水平有限,讲得不好和不对的地方,希望大家指出,如有疑问也欢迎大家评论区讨论,作者尽量给出答复。
工程源码:链接:https://pan.baidu.com/s/1miTIfhVCHh2fi99lE24NiA
提取码:s9qj
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/paulgeorge__13/article/details/145180592
|