一、公式拆解
P
I
D
PID
PID公式展示:
u
(
t
)
=
K
p
(
e
(
t
)
+
1
T
t
∫
0
t
e
(
t
)
d
t
+
T
D
d
e
(
t
)
d
t
)
u(t)=K_p(e(t)+\frac{1}{T_t } ∫_0^te(t)dt+T_D \frac {de(t)}{dt})
u(t)=Kp?(e(t)+Tt?1?∫0t?e(t)dt+TD?dtde(t)?)
把
K
p
K_p
Kp?乘进去得:
u
(
t
)
=
K
p
e
(
t
)
+
K
p
T
t
∫
0
t
e
(
t
)
d
t
+
K
p
T
D
d
e
(
t
)
d
t
u(t)=K_pe(t)+\frac{K_p}{T_t } ∫_0^te(t)dt+K_pT_D \frac {de(t)}{dt}
u(t)=Kp?e(t)+Tt?Kp??∫0t?e(t)dt+Kp?TD?dtde(t)? 令
K
p
K_p
Kp? 为比例时间系数
令
K
i
=
K
p
T
t
K_i=\frac{K_p}{T_t }
Ki?=Tt?Kp?? 为积分时间系数
令
K
d
=
K
p
T
D
K_d=K_pT_D
Kd?=Kp?TD? 为微分时间系数
就变成了这个亚子:
u
(
t
)
=
K
p
e
(
t
)
+
K
i
∫
0
t
e
(
t
)
d
t
+
K
d
d
e
(
t
)
d
t
u(t)=K_p e(t)+K_i ∫_0^te(t)dt+K_d\frac{de(t)}{dt}
u(t)=Kp?e(t)+Ki?∫0t?e(t)dt+Kd?dtde(t)?
符号或表达式 | 代表意义 |
---|
u
t
u_t
ut? | PID输出(不是系统输出) |
K
p
K_p
Kp? | 比例系数 |
K
i
K_i
Ki? | 积分系数 |
K
d
K_d
Kd? | 微分系数 |
e
(
t
)
e(t)
e(t) | 目标值和当前值的误差 |
对于这个式子,其实涵盖了三种控制算法,每一种都可以单独拿出来。
比例控制算法
P
P
P:
u
(
t
)
1
=
K
p
e
(
t
)
u(t)_1=K_p e(t)
u(t)1?=Kp?e(t)
积分控制算法
I
I
I:
u
(
t
)
2
=
K
i
∫
0
t
e
(
t
)
d
t
u(t)_2=K_i ∫_0^te(t)dt
u(t)2?=Ki?∫0t?e(t)dt
微分控制算法
D
D
D:
u
(
t
)
3
=
K
d
d
e
(
t
)
d
t
u(t)_3=K_d\frac{de(t)}{dt}
u(t)3?=Kd?dtde(t)?
你没有看错!PID算法其实就是三个算法的组合,而且,在数学上面就是简单的代数和!
二、基于物理进程的解释
A.比例控制算法
在这里面,
e
(
t
)
=
目
标
值
?
当
前
值
e(t)=目标值-当前值
e(t)=目标值?当前值,自然是离散数据,也就是说
u
(
t
)
1
=
K
p
e
(
t
)
u(t)_1=K_p e(t)
u(t)1?=Kp?e(t)这个输出是根据当前值和目标值的差,乘以了一个比例系数得到的输出,
举个例子,假如我们要给一个100ml的A量筒装满水,此时A量筒里面已经有了20ml的水。而我们运水的工具是一个实际容积未知的B杯子
我们假如B杯子是一个50ml的杯子(我们并不知道是50ml),给其划上100等分的刻度,
那么根据条件可得
T
1
=
e
(
t
)
=
目
标
值
?
当
前
值
=
100
?
20
=
80
T_1=e(t)=目标值-当前值=100-20=80
T1?=e(t)=目标值?当前值=100?20=80 。因此现在我们给B杯子装入可达第80刻度线的水并“一滴不漏”倒入A量筒,由于B杯子实际容积为50ml,所以实际倒入A量筒的水体积为40ml。 可以发现,我们要给100ml的量筒装满水,通过量筒的刻度我们轻易能获知还需要倒80ml才能装满杯子,但是B杯子的容积我们并不知道。我们给未知容积的B杯子划上刻线,以获知的80这个数据来给A量筒倒水,最好的情况是我的B杯子容积为100ml,这样的话按照80的刻度,一次性就可以把水加满。由于B杯子容积只有50,所以第一次加水只加了40ml,这便是比例系数的引入。
(这之间读者务必注意你的已知和未知) 从上文举的例子,我们可以明白,我们获知量和得知该量继而调整输出之间是难以对等的,例子中都是容积,实际上做控制时,将会是跨物理单位的转换,比如你获知你的小车速度设定为3m/s,实际只有2m/s,但你不可能直接给小车送定额的速度。你只能去调整等效电压、电流的大小,或者是PWM的占空比以使得速度达到3m/s,速度和电压必然是不对等的,但这之间会存在一个未知的关系,在这里,你可以认为是比例系数。
单纯以容积的刻度作为比照,此处B杯子50ml(我们刻了100等分刻度),所以虽然我们不知道容积,但这个过程,B杯子一个刻度的水量相当于A量筒的刻度的一半,所以实际上 Kp=0.5 | (务必注意此处的0.5是根据刻度转换得来的,也就是你心中的量度转化为实际输出的关系,我看有些文章以容积为对比,这显然错误)
加了40ml水后,A量筒水位达到20+40=60ml的刻度,
T
2
=
e
(
t
)
=
目
标
值
?
当
前
值
=
100
?
60
=
40
T_2=e(t)=目标值-当前值=100-60=40
T2?=e(t)=目标值?当前值=100?60=40 依照这个40,B杯子加水到40刻度,
K
p
e
(
t
)
=
0.5
×
40
=
20
m
l
K_p e(t)=0.5\times40=20ml
Kp?e(t)=0.5×40=20ml,倒进A之后水位达到60+20=80ml,如此不断无穷循环下去,A量筒最终会被装满。(例子举的是B容积小于A量筒的情况,各位看官可以自行假想B容积大于A量筒的情形。我们使用时肯定是会逐步修正这个系数的)
B.积分控制算法
照前面比例控制算法,量筒最终会被装满,那么为何还需要别的算法呢?客官莫急,待老朽与您娓娓道来(娓娓道来:形容谈论不倦,或说话动听,没错,我是后者)
刚才比例控制算法那段内容我给“一滴不漏”特别标成了红色,实际上我们真的能一滴不漏吗?当然不能!
回到之前举的装水问题,现在已经装了
80
m
l
80ml
80ml,现在我们多加入一个条件,那就是我们每次加水,都会漏掉10ml。
现在可得
e
(
t
)
2
=
100
?
80
=
20
e(t)_2=100-80=20
e(t)2?=100?80=20,那么按照比例控制算法,我们通过B杯子加进去能加入10ml的水,但是现在又会漏掉
10
m
l
10ml
10ml,
10
m
l
?
10
m
l
=
0
m
l
10ml-10ml=0ml
10ml?10ml=0ml,哦豁,加完水还是
80
m
l
80ml
80ml,之后条件不变,意味着
80
m
l
80ml
80ml到头了,升不上去了,可是到此为止离我们的目标100ml还差20ml呢! 现实中就是这样,你不可能想给多少那边就接收多少。 于是这里我们加入积分控制算法,
u
(
t
)
2
=
K
i
∫
0
t
e
(
t
)
d
t
u(t)_2=K_i ∫_0^te(t)dt
u(t)2?=Ki?∫0t?e(t)dt
u
(
t
)
=
u
(
t
)
1
+
u
(
t
)
2
=
K
p
e
(
t
)
+
K
i
∫
0
t
e
(
t
)
d
t
u(t)=u(t)_1+u(t)_2=K_p e(t)+K_i ∫_0^te(t)dt
u(t)=u(t)1?+u(t)2?=Kp?e(t)+Ki?∫0t?e(t)dt
可以看到这个式子与误差的积分成正比,在积分系数已经定下时(时间肯定取单位时间),误差的积分越大,这个积分控制算法得到的也越大,先设它积分系数为0.2,接着上面的80ml继续计算,已知比例控制算法的输入和漏掉的相等,那么这是整个
P
I
PI
PI算法只需要看积分控制算法的输出就行了,,故积分控制输出4ml,很显然,积分控制的加入打破了之前的稳定状态。积分的来源是误差,误差的累加会增大输入,从而不让系统卡在稳定误差,事实上另一方面积分项也加快了整体控制算法的响应速度(因为
e
(
t
)
e(t)
e(t)的符号,让正的值更大,负的更小)。
最后到了目标值后,假如没有波动,回过去看看三种控制算法,貌似无论是比例还是微分控制,输出都是0,只有积分的值是固定输出(积分误差累加),很显然,就是为了消除之前那种稳定状态的静差。(静差:被控量的稳定值和给定值之差,一般用于衡量系统的准确性)静差很难被消除,但是通过积分控制可以尽量去减小,而且积分系数不可太大,太大静差反而更大,有网友给出就平衡车工程经验而言,一般
K
i
=
K
p
200
K_i=\frac{K_p}{200}
Ki?=200Kp??,稍微来点就可以。
C.微分控制算法
这里就是对误差进行微分:
d
e
(
t
)
d
t
=
E
r
r
o
r
(
n
o
w
)
?
E
r
r
o
r
(
p
a
s
t
)
\frac{de(t)}{dt}=Error(now)-Error(past)
dtde(t)?=Error(now)?Error(past)
①当 当前值<目标值 时:一般在该调节过程中,误差是越来越小的(正实数运算),这也就可以得
E
r
r
o
r
(
n
o
w
)
?
E
r
r
o
r
(
p
a
s
t
)
<
0
Error(now)-Error(past)<0
Error(now)?Error(past)<0
对于主要的比例控制而言,此时
e
(
t
)
>
0
e(t)>0
e(t)>0,二者符号相反,换言之,这里微分控制起到了削减比例控制力度的作用
②当 当前值>目标值 时: 输出过大,需要减小,对于比例控制,
e
(
t
)
<
0
e(t)<0
e(t)<0,反观微分控制:
E
r
r
o
r
(
n
o
w
)
<
0
Error(now)<0
Error(now)<0
E
r
r
o
r
(
p
a
s
t
)
<
0
Error(past)<0
Error(past)<0
∣
E
r
r
o
r
(
n
o
w
)
∣
<
∣
E
r
r
o
r
(
p
a
s
t
)
∣
|Error(now)|<|Error(past)|
∣Error(now)∣<∣Error(past)∣
?
E
r
r
o
r
(
n
o
w
)
<
?
E
r
r
o
r
(
p
a
s
t
)
-Error(now)<-Error(past)
?Error(now)<?Error(past)
E
r
r
o
r
(
n
o
w
)
>
E
r
r
o
r
(
p
a
s
t
)
Error(now)>Error(past)
Error(now)>Error(past)
E
r
r
o
r
(
n
o
w
)
?
E
r
r
o
r
(
p
a
s
t
)
>
0
Error(now)-Error(past)>0
Error(now)?Error(past)>0
哦豁,符号又和比例控制相反,太显然了,这明摆着就是和主要的比例控制唱反调啊!太嚣张了!
如果愿意的话,还可以细究一些量的关系,这里就不多说了。
最终结论就是微分起到阻尼作用,减小震荡,提高稳定,减小变化趋势,但是也会降低响应速度。
总结一下,PID三个字母,大致可以说是P主管响应,I减小静差,D抑制震荡,但使用不当还会有反作用。
三、C语言代码框架和解释(单片机可用)
1、定义一个PID结构体代码
typedef struct PID
{
double SetValue;
double Kp;
double Kd;
double Ki;
double Error1;
double Error2;
double sum_error;
}PID;
2、初始化PID结构
void PIDInit(PID*Stru)
{
memset(Stru,0,sizeof(PID));
}
double PID_OP(PID*Stru , double NewValue)
{
double dError,Error;
Error= Stru->SetValue-NewValue;
dError=Stru->Error2-Stru->Error1;
Stru->Sum_Error+=Error;
Stru->Error1=Stru->Error2;
Stru->Error2=Error;
return(Stru->Kp*Error+Stru->Ki*Stru->Sum_Error+Stru->Kd*dError);
}
3、输入函数和输出函数
void Input()
{
}
void System_Out(PID_out)
{
}
4、定时器初始化函数
void time_interrupt_Init(这里可以放装填初值啥的,随意)
{
这里进行一些启动定时器中断、设置工作方式、装填初值啥的骚操作
}
5、中断函数
void Timer0() interrupt 1
{
定时器硬件的装初值或其它操作语句,具体硬件具体分析;
PID_in=Input();
PID_out=PID_OP(&Squ,PID_in);
System_Out(PID_out);
}
6、主函数
void main()
{
PID Squ;
double PID_out,PID_in;
PIDInit(&Squ);
Squ.Kp=0.5;
Squ.Ki=0.5;
Squ.Kd=0.1;
Squ.SetValue=100.0;
void time_interrupt_Init(这里可以放装填初值啥的,随意)
while(1);
主函数里面还可以放一些存储调试数据的语句,这种语句优先等级低,在不干扰正常操作情况下存储或者发送调试数据,个人认为对调试有一定帮助(强烈推荐eeprom);
}
P.S.:我们在PID控制中不可能处理并且输出连续数据,因此这本文档中都是处理离散数据,而这些离散数据的源头是编码盘或者其它硬件,这也就意味着必须考虑采集数据的装置的采集速度、周期。采集完后又得CPU进行处理,处理又需要时间,电压或者电流的响应也需要时间。为了不让其中发生“碰撞”,这就需要我们去设置一个控制周期,调整好二者,给予二者良好配合的空间。 |
四、PID归纳
写程序不难,难的是调出一个好的参数 在实际使用中,不一定非要把P、I、D三个算法都用上 依据需要进行组合使用吧
各个系数的作用表
性能指标 | 参数 | Kp增大 | Ki减小 | Kd增大 | 偏差 | 增大 | 增大 | 减小 | 稳态误差 | 减小 | ----- | ----- | 超调量 | 增大 | 增大 | 减小 | 振荡频率 | 增大 | 增大 | 增大 |
|