目录
简介
PID是一种常见的使被控对象“保持稳定”的控制算法。从名称上就表明该算法包含了------比例(proportional)、积分(integral)、微分(derivative)三大部分。
下面给出整个体系的流程图
由上我们可以得到一个如下图的公式
其中
Kp为比例增益,Kp值越大,被控对象响应越快
Ki为积分增益,积分时乘的系数就越大,积分效果越明显。作用就是减小静态情况下的误差,让受控物理量尽可能接近目标值。
Kd为微分增益,用于稳定被控对象的运动,Kd参数越大,向速度相反方向运动的力道就越强。
e为误差error,其计算公式为 error=目标速度-当前速度
t为当前时间
举个例子
假如我们现在想操作飞控使其稳定在期望的高度。如果没有PID算法的情况下,直接使用开环计算,由于实际环境的重力以及空气阻力等各种原因会导致飞控无法达到我们期望的高度。因为开环是我们输入数据给到被控对象,它只会执行一次。但闭环则完全不同,它可以根据我们输入的数据进行调整,不再是一个恒定的值了。
飞控的升力关系:
如下图所示,黑线为我们的期望高度,现在飞控所在位置与期望高度之间的距离就是误差error
现在我们加入一个比例系数P,飞控从而响应,缩短了与目标高度的距离
再调大P的值
此时我们发现飞控超出了原先设定的期望高度,这是为什么呢?
上面我们说过P为比例系数,P值越大,系统的响应就越快。但是想象如果我们的被控对象马上就要接近期望值的时候,此时我们希望它再精准一点,然后再把P值稍微增加一点,结果却是飞控意外地超过了我们设定的高度。系统响应快的同时飞控自身还在做着运动,由于惯性,当系统想稳定时,飞控却会超过预期高度,再逐渐回到我们设定的高度,但这样震荡很大,并不能实现精确定位。上述现象就是 超调。
那如何对震荡这一现象进行抑制呢
这时候我们就引入了 D(微分增益)算法
我们可以看到误差值error就是我们与期望高度的距离,对距离求微分就是速度v。
所以D算法会抵消一部分由P算法计算出来的值,从而减缓系统的震荡。Kd的值越恰当,系统的控制会越稳定。
但仅靠PD算法,我们的静态误差还是无法被消除,此时就需要引入 I(积分增益)算法。
通过时间对误差进行累计从而提供更大的升力,使我们的飞控能够更精确达到我们的预期高度,这就是I算法。
在我们的PID系数中的值并不是越大越好,必须要将三者精确在一个适当的位置。
以上就是PID算法的实际应用。
调参方法
我们除了知道算法的具体应用,还需要掌握对其调参的方法。确定我们是否调到合适的位置一般可以用串口上位机,通过观察波形图的实时变化来调整对应的参数。
如上图所示
1为的目标值,我们要做的就是通过改变三个参数来使蓝色曲线逼近且尽量与红色虚线重合。
在调节三个值的时候,我们先将Ki与Kd置0。
随着Kp越大,曲线到达目标的速度就越快,时间越短。当我们发现P值响应能够满足我们的要求时再逐渐增加Ki的值,随着Ki的增大,我们的静态误差将会逐渐累加(可以看到曲线从开始右半部分在红色虚线下 到逐渐增大Ki 到与红色虚线有交叉的部分)。当Ki取到了一定值后,我们可以慢慢增大Kd的值从而减小震荡。如图所示,Kd逐渐增大的时候,曲线也开始逐渐拟合红色曲线。
以上就是算法的调参,参数调得越恰当,PID的效果会越好,系统就越能达到我们期望的值。
代码
typedef struct
{
float P;
float I;
float D;
float MAX;
float MIN;
float error[3];
float Dev[2];
float Ad_value;
float PID_Out;
float SumError;//累计误差
}PID;
float PID_Positional(PID* PID_Ptr, float speed_real, float speed_ask)//位置式PID
{
float Positional = 0;
PID_Ptr->error[0] = speed_ask - speed_real;//PID_Ptr->error[0]为当前误差,PID_Ptr->error[1]为上一次误差,PID_Ptr->error[2]为误差积分
PID_Ptr->error[2] += PID_Ptr->error[0];
if(PID_Ptr->error[2] > 50) PID_Ptr->error[2] = 50;//一般这里还会有积分限幅
if(PID_Ptr->error[2] < -50) PID_Ptr->error[2] = -50;
Positional = PID_Ptr->P * PID_Ptr->error[0] +
PID_Ptr->I * PID_Ptr->error[2] +
PID_Ptr->D * (PID_Ptr->error[0] - PID_Ptr->error[1]);//计算PID
PID_Ptr->error[1] = PID_Ptr->error[0];
if(Positional >PID_Ptr-> MAX) Positional =PID_Ptr-> MAX;//PID限幅
if(Positional <PID_Ptr-> MIN) Positional =PID_Ptr-> MIN;
return Positional;
}
文章引用:一文搞懂PID控制算法 - 知乎 (zhihu.com)、通俗易懂的 PID 控制算法讲解_哔哩哔哩_bilibili