023_STM32之PID算法原理及应用

发布者:DreamySunset最新更新时间:2024-08-16 来源: cnblogs关键字:STM32  PID算法  增量式 手机看文章 扫描二维码
随时随地手机看文章

(一)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运算rn') ;

             break;

         

         case MODEL_PI:  out= pid.Pout+ pid.Iout;                            printf('使用PI运算rn') ;

             break;

                 

         case MODEL_PID: out= pid.Pout+ pid.Iout+ pid.Dout;        printf('使用PID运算rn') ;

             break;

     }

    printf('PID算得的OUT:t%drn',(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%drn',(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的周期

[1] [2]
关键字:STM32  PID算法  增量式 引用地址:023_STM32之PID算法原理及应用

上一篇:025_STM32之MDK5软件仿真之查看io口输出
下一篇:022_STM32中断优先级分组解析

推荐阅读最新更新时间:2024-11-12 09:04

hex文件大小与STM32中芯片flash大小的关系
之前一直以为STM32flash空间大小和hex文件相关,以为hex文件大小超过flas大小后程序就会出问题但是我发现hex文件大于flash也可以正常下载,有的程序也可以正常运行,有的不可以,所以经过总结如下: 1、flhex文件其实是个格式规范的文本文件。程序代码大小与hex文件大小没有绝对的关联性,因为我们在用串口下载程序时一般都是用的hex文件下载,,所以大家会以为hex文件大小和flash大小息息相关,hex文件大小超过了flash大小就会出问题,我也以为是这样,直到最近我发现有hex文件大于flash的大小但是依然可以写进去,因为真正烧写进去的是二进制文件,在hex文件中包含了bin文件的信息 2、hex文件大
[单片机]
stm32-esp8266驱动程序
esp8266模块是串口通讯,通讯的协议是AT指令,要正确的配置对应的AT指令模块才可以工作,这里就需要知道AT指令有没有配置成功,这个比较容易实现,一般发送AT指令配置成功的话,芯片都会返回至少一个OK,当然还有其它数据,我们只需要去解析有没有接收到OK就知道指令发送是否成功了。我这里是作为TCP客服端,所以只需要以下一些指令即可: 1 发送 ATrn 返回 OK 以上操作确保芯片连接正常,工作正常 2 发送 ATE0rn 返回 OK 关闭回显 3 发送 AT+CWMODE=1rn 返回 OK 设置成客服端模式 4 发送 AT+CIPSTATUS 返回 2 3 4 5 2
[单片机]
stm32-esp8266驱动程序
MDK V5.12 + ST-Link V2 + Win10 STM32开发环境搭建1
养成每天写笔记的习惯,很多知识,时间一长就忘记了。以前都是用Jlink来仿真调试STM32,现在找不到Jlink去哪里了,可能留在了前公司。然后了解到ST-Link很便宜,就果断在淘宝上了化了不到20买了一个回来。 环境: 操作系统:Win10 64位 IDE:keil MDK uVision V5.12 仿真下载器:ST-Link V2 注:开始的时候使用keil MDK V4.0 的版本,好像对ST-Link V2的支持不好,然后就用了MDK V5.12 。 自己有个Jlink-V7是盗版的,MDK5.12 会识别到JLink-V7,直接闪退,MDK V4.0 倒是没有问题。 1:ST-Link V2 介
[单片机]
MDK V5.12 + ST-Link V2 + Win10 <font color='red'>STM32</font>开发环境搭建1
STM32时钟库函数RCC_DeInit介绍
void RCC_DeInit(void) { RCC- CR |= (uint32_t)0x00000001; //开启内部8MHz时钟 #ifndef STM32F10X_CL //STM32F10X_CL指的是STM32互联系列微处理器 RCC- CFGR &= (uint32_t)0xF8FF0000; //其它类型处理器的CFGR寄存器中27-31位是保留位,24-26为MCO位 #else //而互联型处理器的CFGR寄存器中,28-31位是保留位,24-27位属MCO RCC- CFGR &= (uint32_t)0xF0FF0000; //初始化CFGR寄存器,详见注释第1条 #endif RCC-
[单片机]
stm32实用篇4: stm32数据类型长度
由于经常会忘记stm32的数据类型长度,测试一下: DEBUG_INFO( stm32数据类型长度 ); DEBUG_INFO( char = %d byte. , sizeof(char)); DEBUG_INFO( short = %d byte. , sizeof(short)); DEBUG_INFO( int = %d byte. , sizeof(int)); DEBUG_INFO( long = %d byte. , sizeof(long)); DEBUG_INFO( long long = %d byte. , sizeof(long long)); DEBUG_INF
[单片机]
<font color='red'>stm32</font>实用篇4: <font color='red'>stm32</font>数据类型长度
STM32_RTC晶振不起振的原因及解决方法
STM32的RTC晶振经常出现不起振的问题,这已经是“业界共识”了。。。很多人在各种电子论坛上求助类似于“求高手指点!RTC晶振不起振怎么办”的问题,而其答案基本可以概括为“这次高手帮不了你了” 更有阴谋论者提出让人啼笑皆非的解释——STM32的RTC晶振不起振是ST与晶振厂商串通后故意搞出来的,目的是提高某晶振厂商高端晶振的销量。。。 最近做的几块板子也用到了STM32的RTC,前后两版一共做了大概6片,幸运的是并未遇到晶振不起振的现象。而我采用的是3毛钱一个的普通晶振,并未选用传说中低负载高精度晶振。。。后来在另外一片实验性质的板子上首次遇到了晶振不起振的问题,而且做了2片都不起振,这才让我意识到这个问题的严重性。 从上述现象
[单片机]
STM32单片机学习笔记(3):虚拟串口
项目简介 利用CubMX生成基于32单片机的HAl库工程,然后编写程序在proteus上仿真验证。本项目最适合没有开发板的同学学习,零成本利用仿真软件率先入门STM32单片机。这是第三部分针对串口通信的一个实例,虚拟串口其实只是计算机以软件的方式模拟串口通信的功能,可以基本等同于实际的串口。本文主要用于对STM32串口通信的理解。 硬件模块 STM32F103R4 串口模块 软件工具 CubMX Proteus KEIL 电路连接图 STM32F103R4 串口模块 工作流程 首先是下载相应的虚拟串口,这里下载的是Virtual Serial Port Driver软件。 安装好后,其使用期限是14天,后续超过这个
[单片机]
<font color='red'>STM32</font>单片机学习笔记(3):虚拟串口
STM32固件库
很久没有碰单片机了,两年了吧,因为项目需要,最近入手一块红牛的开发板,核心为STM32F103ZE。虽然以前做过大概半年的stm32的开发,现在天天在.net平台下写代码,已经忘记的差不多,恰逢周末,补补课,以后用的时候也方便点。 ST推出的FW大大提高了单片机SW的开发效率,所以在新建工程前先来了解下这个SDK。我使用的是最新的v3.5固件库,这个固件库可以分为三个部分: 1、内核支持(..STM32F10x_StdPeriph_Lib_V3.5.0LibrariesCMSISCM3CoreSupport) 2、硬件系统支持(..STM32F10x_StdPeriph_Lib_V3.5.0LibrariesCMSI
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved