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<0)             //值不能为负数
     {
        pid.OUT=pid.OUT0; 
     }
     else 
     {
        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<0)
    {
     pid.currpwm=0;
    }
    
    printf("实际输出使用的OUT:\t%d\r\n",(int)pid.currpwm) ;
    pid.En_2=pid.En_1;
    pid.En_1=pid.En;
    
    
    Turn_Angle(pid.currpwm);                //输出PWM
    
    
 
}
(十五)上面我们贴出来位置式PID算法和增量式PID算法的核心部分了,但是上面的理论上可以直接移植,添加一些还没有定义的变量就好了,下面是具体的演示工程

PID算法整定
(一)首先在使用PID算法之前先进行基础的设置

1. PWM正脉冲控制输出开

2. 传感器曲线随着PWM占空比越大而越大

3. 传感器在环境下情况下最低数据    ,和最高数据。 设定值不能超过这两个范围

(二)凑试法的基本方法是:对参数实行先比例,后积分,再微分的整定步踩具体步骤如下:

1)首先先只整定比例部分。将比例系数Kp由小变大,观察系统响应的变化情况,直到得到反应快、超调小的响应曲线。如果系统没有静差或静差小到允许的范围内,那么只需要比例控制器即可满足要求。

2)整定积分时间常数。在比例控制下系统的静差不满足设计要求时,则需要采用积分环节来消除静差。整定时,首先置积分时间Ti为一较大值,并将经第一步整定很到的比例系数略为缩小(如缩小为原值的4/5),观察系统响应的情况,然后根据观察的情况来减小积分时间常数,同时比例系数也可能缩小,使系统消除静差的同时能够获得良好的动态性值在此过程中,在此过程可根据响应曲线的好坏反复改变比例系数与积分时间常数,以期得到满意的控制过程与整定参数。

3)整定微分时间常数。若使用比例积分控制器消除了静差,但动态过程经反复调胜仍不能满意(主要是响应速度达不到要求),则可加人微分环节,构成PID控制器。整定时先置.较小的微分时间常数,同时比例系数略微减小、 积分时间常数略微增大,观察系统师应的情况。然后加大微分时间常数,比例系数、积分时间常数相应调整,反复调整,直到得满意的控制过程和整定参数。

(三)总结试凑法

1. 比例系数有小到大,然后找出超调小的Kp

2. 积分时间常数Ti由大变小,适当调整Kp

3. 微分时间常数Td由小变大,适当调整Ti和Kp

(四)实验总结

1. KP设定,最初使用1,假如控制之后实际值比设定值小不够,那就增大。反之就减少。

2. TI设定,数据先很大。看效果

  1). 如果TI加进去之后数据很久才变化到目标值就逐渐减小。如果TI减少到执行几次都是比设定值大的时候那就逐渐增大

  2). 如果刚加进去变化很快,并且超调很高,就增大来调节

3. 积分就看情况调节

(五)增量式PID控制与位置式PID控制比较,有如下优点:

  1)由式增量式PID控制与位置式PID可见,增量式PID控制算式只需要现时刻以及前两个时刻的偏差采样值,计算量和存储量都小,且计算的是增量,当存在计算误差或精度不足时,对控制量计算的影响较小。而位置式PID控制算式每次计算均与整个过去的状态有关,需要过去所有的偏差采样值参与累积计算,容易产生较大的积累误差,存储量和计算量较大。

  2)增量式PID控制容易实现无扰动切换。增量式PID控制算法是对前一一时刻控制量的增量式变化,对执行机构的冲击小。位置式PID控制算法则是对前一稳定运行点控制量的绝对大小的变化,变化大,对执行机构容易产生冲击。

  3)增量式PID控制本质上具有更好的抗干扰能力。若某个时刻采样值受到干扰,对于位置式PID控制,这个干扰会一直影响系统的整个运行过程;而增量式PID控制算式只需要现时刻以及前两个时刻的偏差采样值,干扰最多影响3个采样时间,其他时间不受该干扰的影响。

  4)位置式PID控制比增量式PID控制更容易产生积分饱和。

PID算法在STM32上的实现相关推荐

  1. 基于PID算法的STM32爬坡循迹智能小车的设计与实现

    目录 摘要 0 引言 1 需求分析 2 系统结构 3 硬件电路设计 3.1 整体硬件框架 3.2 主控制电路 3.3 电源模块 3.4 红外循迹 3.5 电机驱动 3.6 无线蓝牙控制 4 软件设计 ...

  2. PID调节算法在STM32上的实现

    (一)PID控制算法(P:比例    I:积分 D:微分) (二)首先先说明原理,使用的是数字PID算法,模拟PID算法在计算机这样的系统中是不能够直接使用的,数字PID算法又分为位置式PID控制算法 ...

  3. PID控制器开发笔记之一:PID算法原理及基本实现

           在自动控制中,PID及其衍生出来的算法是应用最广的算法之一.各个做自动控制的厂家基本都有会实现这一经典算法.我们在做项目的过程中,也时常会遇到类似的需求,所以就想实现这一算法以适用于 ...

  4. PID算法原理及模板讲解

    很早都想写一栏关于PID算法的专栏,整个大学期间把谈恋爱的时间都拿来搞PID算法了(这样你们还不信我是真的搞PID的嘛..).为了学习PID算法买过平衡小车之家的平衡车(最后拆成玩具了),买过正点原子 ...

  5. PID算法(三)串级PID

    串级PID 如果说前一次的衍生PID,是在基础PID算法的横向上变形的话,那本片的串级PID就是在基础PID的算法上,做纵向的嵌套. 与单级PID的不同 与单级PID不同的是,串级PID,需要两个实际 ...

  6. stm32直流电机控制—PID算法篇

    stm32直流电机控制-PID算法篇 一.常用的控制算法 1.控制系统的基本结构: 2.常用控制算法:位式控制 ①二位式控制算法 ②.具有回差的二位式控制算法 ③三位式控制算法 ④小结 二.PID控制 ...

  7. 【DIY】使用STM32及PID算法实现一个磁悬浮玩具

    今天教大家来做一个磁悬浮玩具,先上两张成品的效果图: 1)磁悬浮的基本原理 磁悬浮有下推式和上拉式两种基本形式. 所谓下推式,就是控制部分在底座上,悬浮的磁铁在上面,依靠底座从下往上的排斥磁力推动磁铁 ...

  8. 在 FPGA 上快速构建 PID 算法

    在 FPGA 上快速构建 PID 算法 副标题:优秀的IC/FPGA开源项目(四)-使用HLS构建PID算法 <优秀的IC/FPGA开源项目>是新开的系列,旨在介绍单一项目,会比<优 ...

  9. STM32上可用的的SM 2 3 4国密算法

    可在STM32上使用的国密算法 SM 2 3 4 SM2 SM3 SM4 下面直接给出代码,有问题可评论,自己改动的,测试不到的地方可能有bug,欢迎指正. SM2 由于SM2算法牵扯到一些较为复杂的 ...

最新文章

  1. c++ map嵌套队列(队列嵌套结构体指针)ok
  2. Py之argparse:Python库之argparse(命令行解析)简介、安装、使用方法之详细攻略
  3. docker环境搭建
  4. CodeForces - 1543D2 RPD and Rap Sheet (Hard Version)(交互+模拟)
  5. 70 include指令
  6. opencv图像分割2-GMM
  7. js深入研究之神奇的匿名函数类生成方式
  8. cin.get(),cin.getline(),getline(),gets(),getchar()
  9. 2016级算法第五次上机-E.AlvinZH的学霸养成记IV
  10. POJ 3125 Printer Queue(队列,水题)
  11. 本地怎么传文件到服务器,本地传输文件到服务器
  12. python xlrd 过滤excel里的隐藏行
  13. 求解字谜游戏java_有趣的字谜游戏,一起来解锁答案吧!
  14. 含蓄的告别,google今日LOG
  15. android 怎么绘制圆形,Android编程绘制圆形图片的方法
  16. Stimulsoft Reports.Java 2022.2.3 Crack
  17. java使用poi读取跨行跨列excel
  18. 详细解读【虚拟内存】
  19. jvm 内存溢出的多种原因及优化方法
  20. win10-Administrator账户停用怎么重新启用(亲测有效)

热门文章

  1. java日志切割工具_JavaSwing版本的日志文件分割器
  2. 来个newsmth笑话四月刊转载
  3. UVa Online Judge的重建
  4. 【Java】2022年团体程序设计天梯赛 L1 和 L2-042 题解
  5. MVC使用poi根据excel模板导出文件,并通过浏览器下载。
  6. RSD的面向对象分类设计
  7. linux驱动时间差引发的64位除法问题
  8. ccproxy的缺省代理端口列表
  9. chocolatey的安装与使用与chocolatey安装失败的解决方法
  10. 基于Vue+SpringBoot智慧校园疫情防控系统(PC端、手机端)--附源码