西门子 PID之 FB41

西门子 PID之 FB41,第1张

剑指工控微信技术群,很多人都在问PID的复杂的微积分算法如何形成程序的?我在这里把STEP7 里FB41源代码和注释给大家贴出来,让大家学习一下,FB41是积分PID。另外如果大家习惯了STEP7的PID也可以通过这个源代码移植到别的控制器上。

FUNCTION_BLOCK "CONT_C"

TITLE ='continuous PID controller'

AUTHOR : Jiansiting

FAMILY : JZGK

NAME : CONT_C

VERSION : '2.0'

KNOW_HOW_PROTECT

VAR_INPUT

COM_RST : BOOL := FALSE//完全重启动

MAN_ON : BOOL := TRUE//手动值打开

PVPER_ON : BOOL := FALSE//外设过程变量打开

P_SEL : BOOL := TRUE//比例作用打开

I_SEL : BOOL := TRUE//积分作用打开

INT_HOLD : BOOL := FALSE//积分作用保持

I_ITL_ON : BOOL := FALSE//积分作用初始化

D_SEL : BOOL := FALSE//微分作用打开

CYCLE : TIME := T#1S//采样时间

SP_INT : REAL := 0.0//内部设定值

PV_IN : REAL := 0.0//过程变量输入

PV_PER : WORD := W#16#0//外设过程变量

MAN : REAL := 0.0//手动值

GAIN : REAL := 2.0//比例增益

TI : TIME := T#20S//积分复位时间

TD : TIME := T#10S//微分时间

TM_LAG : TIME := T#2S//微分作用时间延时

DEADB_W : REAL := 0.0//死区带宽

LMN_HLM : REAL := 100.0//积分值上限

LMN_LLM : REAL := 0.0//积分值下限

PV_FAC : REAL := 1.0//过程变量因子

PV_OFF : REAL := 0.0//过程变量偏移量

LMN_FAC : REAL := 1.0//调节值因子

LMN_OFF : REAL := 0.0//调节值偏移量

I_ITLVAL : REAL := 0.0//积分作用的初始化值

DISV : REAL := 0.0//干扰变量

END_VAR

VAR_OUTPUT

LMN : REAL := 0.0//调节值

LMN_PER : WORD := W#16#0//外设调节值

QLMN_HLM : BOOL := FALSE//达到调节值上限

QLMN_LLM : BOOL := FALSE//达到调节值下限

LMN_P : REAL := 0.0//比例分量

LMN_I : REAL := 0.0//积分分量

LMN_D : REAL := 0.0//微分分量

PV : REAL := 0.0//

ER : REAL := 0.0//误差信号

END_VAR

VAR

sInvAlt : REAL := 0.0//上周期比例偏差值

sIanteilAlt : REAL := 0.0//上周期积分值

sRestInt : REAL := 0.0//上周期积分偏差量(浮点数计算偏差)

sRestDif : REAL := 0.0//上周期微分偏差量(浮点数计算偏差)

sRueck : REAL := 0.0//

sLmn : REAL := 0.0//上周期调节值

sbArwHLmOn : BOOL := FALSE//上周期达到调节值上限

sbArwLLmOn : BOOL := FALSE//上周期达到调节值下限

sbILimOn : BOOL := TRUE//备用-本程序没有使用该变量

END_VAR

VAR_TEMP

rCycle : REAL //采样时间浮点值

Iant : REAL //积分增量

Diff : REAL //积分量

Istwert : REAL //过程变量浮点值

ErKp : REAL //偏差比例值

rTi : REAL //积分时间浮点值

rTd : REAL //微分时间浮点值

rTmLag : REAL //微分作用时间延时浮点值

Panteil : REAL //比例值

Ianteil : REAL //积分值

Danteil : REAL //微分值

Verstaerk : REAL //

RueckDiff : REAL //

RueckAlt : REAL //上周期积分量

dLmn : REAL //调节量

gf : REAL //Hilfwert

rVal : REAL //Real Hilfsvariable

END_VAR

IF COM_RST THEN //PID初始化

sIanteilAlt := I_ITLVAL

LMN := 0.0

QLMN_HLM := FALSE

QLMN_LLM := FALSE

LMN_P := 0.0

LMN_I := 0.0

LMN_D := 0.0

LMN_PER := W#16#0

PV := 0.0

ER := 0.0

sInvAlt := 0.0

sRestInt := 0.0

SRestDif := 0.0

sRueck := 0.0

sLmn := 0.0

sbArwHLmOn := FALSE

sbArwLLmOn := FALSE

ELSE

rCycle := DINT_TO_REAL( TIME_TO_DINT( CYCLE ) ) / 1000.0 //采样时间转换为浮点数值

Istwert := DINT_TO_REAL( INT_TO_DINT( WORD_TO_INT ( PV_PER ) ) ) * 0.003616898

Istwert := Istwert * PV_FAC + PV_OFF //外设输入转换为浮点数值

IF NOT PVPER_ON THEN //过程变量选择

Istwert := PV_IN

END_IF

PV := Istwert

ErKp := SP_INT - PV //计算偏差

IF ErKp <-DEADB_W THEN

ER := ErKp + DEADB_W

ELSIF ErKp >DEADB_W THEN

ER := ErKp - DEADB_W

ELSE

ER := 0.0

END_IF

ErKp := ER * GAIN //偏差比例增益

rTi := DINT_TO_REAL( TIME_TO_DINT( TI ) ) / 1000.0

rTd := DINT_TO_REAL( TIME_TO_DINT( TD ) ) / 1000.0

rTmLag := DINT_TO_REAL( TIME_TO_DINT( TM_LAG ) ) / 1000.0

IF rTi rCycle * 0.5 THEN //积分时间必须 >= 采样时间的0.5倍

rTi := rCycle * 0.5

END_IF

IF rTd rCycle THEN //微分时间必须 >= 采样时间

rTd := rCycle

END_IF

IF rTmLag rCycle * 0.5 THEN //微分作用延时时间必须 >= 采样时间的0.5倍

rTmLag := rCycle * 0.5

END_IF

IF P_SEL THEN //比例作用投入

Panteil := ErKp

ELSE

Panteil := 0.0

END_IF

IF I_SEL THEN //积分作用投入

IF I_ITL_ON THEN //积分初始化

Ianteil := I_ITLVAL

sRestInt := 0.0

ELSIF MAN_ON THEN //手动值输入时的积分量计算,用于用于手动切换自动无扰切换

Ianteil := sLmn - Panteil - DISV

sRestInt := 0.0

ELSE //积分计算

Iant := ( rCycle / rTi ) * ( ErKp + sInvAlt ) * 0.5 + sRestInt

IF ( ( Iant >0.0 AND sbArwHLmOn ) OR INT_HOLD ) OR ( Iant <0.0 AND sbArwLLmOn )THEN //抗积分饱和

Iant := 0.0

END_IF

Ianteil := sIanteilAlt + Iant //当前积分值 := 上时刻积分值 + 本次积分量

sRestInt := sIanteilAlt - Ianteil + Iant

END_IF

ELSE

Ianteil := 0.0

sRestInt := 0.0

END_IF

Diff := ErKp

IF NOT MAN_ON AND D_SEL THEN //微分作用投入

Verstaerk := rTd / ( rCycle * 0.5 + rTmLag )

Danteil := ( Diff - sRueck ) * Verstaerk

RueckAlt := sRueck

RueckDiff := rCycle / rTd * Danteil + sRestDif

sRueck := RueckDiff + RueckAlt

sRestDif := RueckAlt - sRueck + RueckDiff //同积分一样计算微分误差量

ELSE //

Danteil := 0.0

sRestDif := 0.0

sRueck := Diff

END_IF

dLmn := Panteil + Ianteil + Danteil + DISV //PID输出

IF MAN_ON THEN //PID手动之打开

dLmn := MAN

ELSE

IF NOT I_ITL_ON AND I_SEL THEN //干扰量处理

IF Ianteil >LMN_HLM - DISV AND dLmn >LMN_HLM AND dLmn - LMN_D >LMN_HLM THEN

rVal := LMN_HLM - DISV

gf := dLmn - LMN_HLM

rVal := Ianteil - rVal

IF rVal >gf THEN

rVal := gf

END_IF

Ianteil := Ianteil - rVal

ELSIF Ianteil <LMN_LLM - DISV AND dLmn <LMN_LLM AND dLmn - LMN_D <LMN_LLM THEN

rVal := LMN_LLM - DISV

gf := dLmn - LMN_LLM

rVal := Ianteil - rVal

IF rVal <gf THEN

rVal := gf

END_IF

Ianteil := Ianteil - rVal

END_IF

END_IF

END_IF

LMN_P := Panteil

LMN_I := Ianteil

LMN_D := Danteil

sInvAlt := Erkp

sIanteilAlt := Ianteil

sbArwHLmOn := FALSE

sbArwLLmOn := FALSE

IF dlmn >= LMN_HLM THEN //调节辆限幅(上限)

QLMN_HLM := TRUE

QLMN_LLM := FALSE

dlmn := LMN_HLM

sbArwHLmOn := TRUE

ELSE

QLMN_HLM := FALSE

IF dLmn = 32512.0 THEN

dLmn := 32512.0

ELSIF dLmn

增量式PID:

typedef struct{  

    float scope        //输出限幅量  

    float aim       //目标输出量  

    float real_out     //实际输出量   

    float Kp     

    float Ki     

    float Kd     

    float e0          //当前误差  

    float e1          //上一次误差  

    float e2          //上上次误差  

}PID_Type  

#define min(a, b)           (a<b? a:b)  

#define max(a, b)           (a>b? a:b)  

#define limiter(x, a, b)      (min(max(x, a), b))  

#define exchange(a, b, tmp) (tmp=a, a=b, b=tmp)  

#define myabs(x)            ((x<0)? -x:x)  

  

float pid_acc(PID_Type *pid)  

{  

    float out  

    float ep, ei, ed  

      

    pid->e0 = pid->aim - pid->real_out  

    ep = pid->e0  - pid->e1  

    ei = pid->e0  

    ed = pid->e0 - 2*pid->e1 + pid->e2  

    out = pid->Kp*ep + pid->Ki*ei + pid->Kd*ed  

    out = limiter(out, -pid->scope, pid->scope)  

    pid->e2 = pid->e1  

    pid->e1 = pid->e0  

    return out  

}

位置式PID:

typedef struct{  

    float scope    //输出限幅量  

    float aim   //目标输出量  

    float real_out //反馈输出量  

    float Kp         

    float Ki  

    float Kd  

    float Sum  

    float e0       //当前误差  

    float e1       //上一次误差  

}PID_Type  

  

#define max(a, b)           (a>b? a:b)  

#define min(a, b)           (a<b? a:b)  

#define limiter(x, a, b)      (min(max(x, a), b))  

  

float pid_pos(PID_Type *p)  

{  

    float pe, ie, de  

    float out = 0  

  

    p->e0 = p->aim - p->real_out      //计算当前误差    

  

    p->Sum += p->e0       //误差积分  

  

    de = p->e0 - p->e1     //误差微分  

  

    pe = p->e0  

    ie = p->Sum  

  

    p->e1 = p->e0  

  

    out = pe*(p->Kp) + ie*(p->Ki) + de*(p->Kd)  

  

    out = limiter(out, -p->scope, p->scope)       //输出限幅

    return out  

}

亲手移植到我的stm32小车上 调试3个参数后正常使用。

没意义。PID是有很强的针对性的,我以前看到的几个网上的PID程序都还是可以用的。但是需要你自己定的,可不只是PID三个数。采样周期是多少?PID计算后打算用多少位的数据,都是要自己定的,等等。

而所谓温度控制的程序,直接套用的(参数不套用,自己调),没有几个能好用的,要么计算量太大,要么调节不够好。

还是看看公式自己写的好。就算我写了一个在我手上好用的,给你,你那也未必好用,因为还要根据实际去修改一些输出、输入量,或加以限制,不同的系统,这些都是不同的。就像是汽车底盘都是4个轮子,你要的是越野车,我给你个轿车的底盘,上面再怎么改也不好用阿!


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/7987143.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-12
下一篇 2023-04-12

发表评论

登录后才能评论

评论列表(0条)

保存