PID算法在STM32上的实现

您所在的位置:网站首页 pid算法程序 PID算法在STM32上的实现

PID算法在STM32上的实现

2023-12-04 04:31| 来源: 网络整理| 查看: 265

PID调节算法在STM32上的实现

(一)PID控制算法(P:比例    I:积分 D:微分)

(二)首先先说明原理,使用的是数字PID算法,模拟PID算法在计算机这样的系统中是不能够直接使用的,数字PID算法又分为位置式PID控制算法和增量式PID控制算法,那么下面从原理上说明这两种算法

(三)原理分析如图

(四)从上面图中我们可以得到定义

定义变量

用户设定值: SV

当前值(实际值): PV

偏差: E = SV - PV

(五)如果我们在一段时间内就从传感器读取一个值,那么我们就可以得到一个实际值的数据序列,,那么我们也会得到一个偏差值的序列.

读取时间:  t(1)    t(2)    ------  t(k-1)    t(k) 读取到的值:  X(1)    X(2)    ------  X(k-1)  X(k)

偏差值:     E(1)    E(2)    ------  E(k-1)  E(k)

那么我们从偏差值中可以知道:  E(X)  > 0     说明未达标

                E(X) = 0     说明正好达标

                E(X) < 0              说明超标

 (六)比例控制(P),作用:对偏差起到及时反映的作用,一旦产生偏差,控制器立即做出反映.............

定义: 比例系数:Kp    (根据系统进行调节) 比例输出:POUT  = Kp * E(k)      

POUT   = Kp * E(k) + OUT0 

OUT0说明:OUT0是防止E(K) = 0 时候比例控制不作用,所以添加个OUT0进去,OUT0可以根据系统定义大小

Kp说明:如果我们得到一个偏差之后,将偏差进行放大或者缩小来让控制器进行控制

(七)积分控制(I),作用:消除静差............

从上面我们得到偏差序列: 偏差值: E(1) E(2)    ------  E(k-1)  E(k)

定义,历史偏差值之和:S(k) = E(1) + E(2) + .... + E(k-1) + E(K) 定义,积分输出:   IOUT = Kp * S(k) + OUT0

八)微分控制(D),作用:反映偏差信号的变化趋势.............

从上面我们得到偏差序列:

偏差值: E(1) E(2)    ------  E(k-1)  E(k) 定义,偏差之差:D(k) = E(k) - E(k-1) 定义,微分输出:DOUT = Kp * D(k) + OUT0

(九)那么我们从上面就能得出PID的控制算法

PIDOUT = POUT + IOUT + DOUT = (Kp * E(k) + OUT0) + (Kp * S(k) + OUT0) + (Kp * D(k) +OUT0)        = Kp * (E(k) + S(k) + D(k)) + OUT0

OUT0防止PIDOUT = 0 时候算法还有输出,防止失去控控制 比例(P):考虑当前 积分(I):考虑历史 微分(D):考虑未来

(十)位置式PID,上面只是简单的说明了一下原理,那么实际的数字PID控制算法中

Ti:积分常数 TD:微分常数 T:计算周期

上面两个是变化后的积分和微分的那个,那么我们把上面的两个替换到第九点中,我们就得到位置上PIDOUT的公式,两个式子是一样的

 

(十一)增量式PID,本次基础上加上多少偏差:△OUT

(十二)上面的只是PID的原理说明,那么数字PID的公式是

(十三)那么我们将上面的公式通过通过C语言写出来

1. 位置式PID

#ifndef _pid_ #define _pid_ #include "stm32f10x_conf.h" #define     MODEL_P         1 #define     MODEL_PI         2 #define     MODEL_PID     3     typedef struct {     u8 choose_model;        //使用哪个模式调节          float Sv;     //用户设定值     float Pv;        //当前值,实际值       float Kp;        //比例系数     float T;      //PID计算周期--采样周期     u16   Tdata;    //判断PID周期到没到     float Ti;        //积分时间常数     float Td;       //微分系数                    float Ek;          //本次偏差     float Ek_1;        //上次偏差     float SEk;         //历史偏差之和          float Iout;        //积分输出     float Pout;        //比例输出         float Dout;        //微分输出          float OUT0;        //一个维持的输出,防止失控       float OUT;        //计数最终得到的值               u16 pwmcycle;//pwm周期           }PID;   extern PID pid; //存放PID算法所需要的数据 void PID_Calc(void); //pid计算 void PID_Init(void);        //PID初始化       #endif #include "pid.h" #include "PWM_Config.h" #include "USART_Config.h"   //USART设置   PID pid; //存放PID算法所需要的数据 void PID_Init()             {     pid.choose_model = MODEL_PID;          pid.T=330;                //采样周期,定时器使用1ms,则最小执行PID的周期为330ms        pid.Sv=280;                //用户设定值     pid.Kp=0.5;                //比例系数   pid.Ti=180;            //积分时间     pid.Td=1;                    //微分时间     pid.OUT0=0;                //一个维持的输出          pid.pwmcycle = 330;    //PWM的周期 }      void PID_Calc()  //pid计算 {     float DelEk;            //本次和上次偏差,最近两次偏差之差     float ti,ki;     float td;     float kd;     float out;       if(pid.Tdata < (pid.T))  //最小计算周期未到      {             return ;      }     pid.Tdata = 0;     pid.Ek=pid.Sv-pid.Pv;               //得到当前的偏差值     pid.Pout=pid.Kp*pid.Ek;          //比例输出       pid.SEk+=pid.Ek;                    //历史偏差总和       DelEk=pid.Ek-pid.Ek_1;              //最近两次偏差之差       ti=pid.T/pid.Ti;     ki=ti*pid.Kp;       pid.Iout=ki*pid.SEk*pid.Kp;  //积分输出    /*注意:上面程序中多了个pid.Kp,原程序中有,请自动删除,正确的应该是pid.Iout=ki*pid.SEK */       td=pid.Td/pid.T;       kd=pid.Kp*td;     pid.Dout=kd*DelEk;                //微分输出             switch(pid.choose_model)      {          case MODEL_P:     out= pid.Pout;                                                printf("使用P运算\r\n") ;              break;                    case MODEL_PI:  out= pid.Pout+ pid.Iout;                            printf("使用PI运算\r\n") ;              break;                            case MODEL_PID: out= pid.Pout+ pid.Iout+ pid.Dout;        printf("使用PID运算\r\n") ;              break;      }     printf("PID算得的OUT:\t%d\r\n",(int)out) ;    //           /*判断算出的数是否符合控制要求*/      if(out>pid.pwmcycle)        //不能比PWM周期大,最大就是全高吗      {         pid.OUT=pid.pwmcycle;      }      else if(out         pid.OUT=out;      }      printf("实际输出使用的pid.OUT:\t%d\r\n",(int)pid.OUT) ;      pid.Ek_1=pid.Ek;  //更新偏差            Turn_Angle((int)pid.OUT);        //输出PWM       } 2.增量式PID

#ifndef _pid_ #define _pid_ #include "stm32f10x_conf.h" #define     MODEL_P         1 #define     MODEL_PI         2 #define     MODEL_PID     3   typedef struct {     u8 choose_model;    //使用哪个模式调节        float curr;              //当前值     float set;               //设定值            float En;                    //当前时刻     float En_1;                //前一时刻     float En_2;                //前二时刻              float Kp;               //比例系数     float T;                     //采样周期---控制周期,每隔T控制器输出一次PID运算结果     u16   Tdata;            //判断PID周期到没到     float Ti;               //积分时间常数     float Td;               //微分时间常数          float Dout;                //增量PID计算本次应该输出的增量值--本次计算的结果     float OUT0;                //一个维持的输出,防止失控          short currpwm;      //当前的pwm宽度     u16 pwmcycle;       //pwm周期   }PID;     extern u8 STATUS; extern PID pid; void PIDParament_Init(void);  /*增量式PID初始化*/ void pid_calc(void);                  /*pid计算 并输出*/   #endif #include "pid.h" #include "PWM_Config.h" #include "USART_Config.h"   //USART设置   PID pid;   void PIDParament_Init()  // {     pid.choose_model = MODEL_PID;     pid.T=330;                //采样周期,定时器使用1ms,则最小执行PID的周期为330ms        pid.set =280;            //用户设定值     pid.Kp=0.5;                //比例系数     pid.Ti=40;                //微分系数常数     pid.Td=10;                //积分时间常数     pid.OUT0=0;                //一个维持的输出       pid.pwmcycle = 330;    //PWM的周期 }        void pid_calc()  //pid?? {   float dk1;float dk2;   float t1,t2,t3;          if(pid.Tdata < (pid.T))  //最小计算周期未到      {             return ;      }     pid.Tdata = 0;          pid.En=pid.set-pid.curr;  //本次误差     dk1=pid.En-pid.En_1;   //本次偏差与上次偏差之差     dk2=pid.En-2*pid.En_1+pid.En_2;        t1=pid.Kp*dk1;                            //比例          t2=(pid.Kp*pid.T)/pid.Ti;      //积分     t2=t2*pid.En;          t3=(pid.Kp*pid.Td)/pid.T;        //微分     t3=t3*dk2;          switch(pid.choose_model)      {          case MODEL_P:     pid.Dout= t1;                    printf("使用P运算\r\n") ;              break;                    case MODEL_PI:  pid.Dout= t1+t2;                printf("使用PI运算\r\n") ;              break;                            case MODEL_PID: pid.Dout= t1+t2+t3;        printf("使用PID运算\r\n") ;              break;      }                 pid.currpwm+=pid.Dout;  //本次应该输出的PWM     printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ;           /*判断算出的数是否符合控制要求*/     if(pid.currpwm>pid.pwmcycle)            //算出的值取值,肯定是在0-pid.pwmcycle之间,不然的话PWM怎么输出     {       pid.currpwm=pid.pwmcycle;     }     if(pid.currpwm



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3