在stm32的pid控制中一般分为两种方式,一种是位置式pid,一种是增量式pid。

位置式pid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
float Target,Actual,Out; //目标值;实际值;输出值(调节力)
float Kp=值,Ki=值,Kd=值; //比例项;积分项;微分项的权重
float Error0,Error1,ErrorIntegral; //本次误差,上次误差,误差积分
int main(void)
{
Timer_Init();
while(1){
/*用户根据需求写入pid控制器的目标值*/
Target=用户指定的一个值;
}
}

void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
/*每隔一段时间,程序来这里执行一次,相当于pid的调控周期*/
/*执行pid*/
Actual=读取传感器(); //获得实际值

Error1=Error0; //获得上次误差
Error0=Target-Actual; //获得本次误差
ErrorIntegral+=Error0; //误差积分(累加)

/*pid计算*/
Out=Kp*Error0+Ki*ErrorIntegral+Kp*(Error0-Error1);

/*输出限幅*/
if(Out>上限){Out=上限;}
if(Out<下限){Out=下限;}

/*执行控制*/
输出至被控对象(Out);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}

增量式pid(最好不要把Pi设置为0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
float Target,Actual,Out; //目标值;实际值;输出值(调节力)
float Kp=值,Ki=值,Kd=值; //比例项;积分项;微分项的权重
float Error0,Error1,Error2; //本次误差,上次误差,上上次误差
int main(void)
{
Timer_Init();
while(1){
/*用户根据需求写入pid控制器的目标值*/
Target=用户指定的一个值;
}
}

void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
/*每隔一段时间,程序来这里执行一次,相当于pid的调控周期*/
/*执行pid*/
Actual=读取传感器(); //获得实际值

Error2=Error1; //获得上上次误差
Error1=Error0; //获得上次误差
Error0=Target-Actual; //获得本次误差

/*pid计算*/
Out+=Kp*(Error0-Error1)+Ki*Error0+Kd*(Error0-2*Error1+Error2);

/*输出限幅*/
if(Out>上限){Out=上限;}
if(Out<下限){Out=下限;}

/*执行控制*/
输出至被控对象(Out);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}

pid的6种改进措施(3种针对积分项,3种针对微分项)

  • 积分限幅

  • 积分分离

  • 变速积分

  • 微分先行

  • 不完全微分

微分先行

针对目标值经常大幅度跳变的情况,如果pid的效果不好,此时可以考虑积分先行策略。

不完全微分

实际应用pid受噪声干扰严重,就可以使用此方法。