PID控制器的数字实现与C语法讲解。PID控制器的数字实现同C语法讲解。

PID控制器的数字实现和C语法讲解

PID控制器的数字实现与C语法讲解

  • 概述  
  • 概述  

  为好学习及交流,根据自己的了解和经验写了立即卖教程,有误的处请求各位读者给指出,具体包含以下三有些情节:

  为便宜学习及交流,根据自己之知与经验写了当下卖教程,有错误的处在请求各位读者给指出,具体包含以下三有情节:

  (1)
 PID数字化的演绎过程(实质:微积分的近似计算);

  (1)
 PID数字化的演绎过程(实质:微积分的近似计算);

  (2)
 程序风格介绍(程序风格来TI官方案例);

  (2)
 程序风格介绍(程序风格来TI官方案例);

  (3)
 C有关语法简述(语法会结合实例进行教学)。

  (3)
 C有关语法简述(语法会结合实例进行教学)。

==========================================================================================================================================

==========================================================================================================================================

  • PID控制器的数字化
  • PID控制器的数字化

  PID控制器是工业经过控制着广运用的同样种控制器,其中,P、I、D分别吗比例(Proportion)、积分(Integral)、微分(Differential)的简写;将病的比重、积分及微分通过线性组合成控制量,用该控制量对受控对象进行支配,称为PID算法

  PID控制器是工业经过控制着常见采取的同样种植控制器,其中,P、I、D分别吗比例(Proportion)、积分(Integral)、微分(Differential)的简写;将错误的百分比、积分及微分通过线性组合成控制量,用该控制量对受控对象进行支配,称为PID算法

  为了用软件实现PID算法,需用PID控制器离散化。

  为了用软件实现PID算法,需用PID控制器离散化。

  1. 总体思路
  1. 一体化思路

        图片 1

        图片 2

  2. 方框图

  2. 方框图

  PID控制器的方框图如图所示:

  PID控制器的方框图如图所示:

          图片 3 

          图片 4 

  3. 关氏域的表达式

  3. 爱屋及乌氏域的表达式

  根据方框图,可写起PID控制器对应的传递函数:

  根据方框图,可写起PID控制器对应之传递函数:

            图片 5  

            图片 6  

         其间,Kp为比例系数,ki为积分系数,Kd为微分系数

         内,Kp为比例系数,ki为积分系数,Kd为微分系数

 

 

  4. 时域的表达式

  4. 时域的表达式

  于条分缕析时,通常借助于拉氏空间,例如判断系的安定团结和相对平静;而现在咱们关注的是时域里的题材,因此对上式进行拉普拉斯逆变换,得到时域里的表达式:

  于解析时,通常借助于拉氏空间,例如判断系的康乐与相对平静;而现咱们关注的是时域里的题目,因此对上式进行拉普拉斯逆变换,得到时域里的表达式:

             图片 7

             图片 8

       其相应的布局框图如图所示:

       其相应的布局框图如图所示:

              图片 9

              图片 10

  5. 差分方程

  5. 差分方程

  该时域里的表达式不便民编程处理,因此待对该式进行离散化处理,从而得到可编程实现的差分方程,解析过程如下:

  该时域里之表达式不便宜编程处理,因此待对该式进行离散化处理,从而取得可编程实现之差分方程,剖析过程如下:

  (说明:PID离散化的本来面目为微积分的离散化(数值化处理),由于这个推导过程很多教科书及都产生介绍,因而有些去演绎过程,只受出最终表达式,程序的算法就是依据这个表达式而写的)

  (说明:PID离散化的实质为微积分的离散化(数值化处理),由于此推导过程很多教科书及且有介绍,因而稍去演绎过程,只吃闹最终表达式,程序的算法就是因这个表达式而写的)

  数字PID控制器的增量式算法:

  数字PID控制器的增量式算法:

      图片 11

      图片 12

      里头,T为宽度,即采样周期(由于微控制器的定时器确定

      里,T为宽度,即采样周期(出于微控制器的定时器确定

  u(kT)=u(k),便得到PID控制器增量式算法的差分方程:

  u(kT)=u(k),便得到PID控制器增量式算法的差分方程:

      图片 13

      图片 14

  这样就不过编程实现了也许有人会问,为什么差分方程就不过编程实现啊?这是盖解差分方程的一般解法就是迭代法,而迭代法只需要新值与通项公式,这在计算机编程中颇易实现 

  这样就算不过编程实现了也许有人会咨询,为什么差分方程就可编程实现为?这是以解差分方程的相似解法就是迭代法,而迭代法惟有待新值与通项公式,这在电脑编程中充分易实现 

  也使编程方便,可引入中变量,定义如下:

  为使编程方便,可引入中变量,定义如下:

             图片 15

             图片 16

  则,PID控制器增量式算法的差分方程变为:

  则,PID控制器增量式算法的差分方程变为:

      图片 17

      图片 18

  说明:

  说明:

   (1)在PID增量式算法中就待对输出u(t)作限幅处理

   (1)在PID增量式算法中单单待对输出u(t)作限幅处理

   (2)当微分系数 Kd=0 时,PID控制器就改为了PI控制器(在编写**PID程序时默认使其为PI调节器**)

   (2)当微分系数 Kd=0 时,PID控制器就变成了PI控制器(在编写**PID程序时默认使其为PI调节器**)

       当积分系数 Ki=0 时,PID控制器就变成了PD控制器。

       当积分系数 Ki=0 时,PID控制器就成为了PD控制器。

=======================================================================================

=======================================================================================

  •  根据微控制器的算法实现
  •  依据微控制器的算法实现

  我写的数字PID程序如图所示(于结尾之附件部分),有半点仿照代码,一仿照是直接函数调用**(C/C++通用),另一样法是使函数指针进行函数调用惟有适用于C**),现于简单只地方针对该次召开教授:

  我形容的数字PID程序如图所示(以最终的附件部分),有些许法代码,一学是一直函数调用**(C/C++通用),另一样效是以函数指针进行函数调用单单适用于C**),现于有限独面针对该次召开教授:

(一)程序风格

(一)程序风格

  次下了模块化编程的琢磨,这样做的目的增长代码的可移植性及程序的可读性

  先后行使了模块化编程的思辨,这样做的目的加强代码的可移植性及顺序的可读性

  程序被拆分成为三个模块:

  程序被拆分变成三单模块:

  一个凡PID的峰文件’PID.h’:主要是概念算法实现有关的数据类型;

  一个是PID的峰文件’PID.h’:主要是概念算法实现有关的数据类型;

  一个凡是PID的源文件’PID.c’:主要是概念算法实现的函数;

  一个凡PID的源文件’PID.c’:主要是概念算法实现的函数;

  一个凡是主函数文件’amain.c’:PID程序的用方式,即于主程序中开相应的初始化工作,在暂停服务程序中展开PID的计算。

  一个是主函数文件’amain.c’:PID程序的利用办法,即于主程序中开相应的初始化工作,在暂停服务程序中开展PID的计算。

说明:**念这次时可能发接触困难,不过当下属于情理之中的事,毕竟刚刚接触这种风格的童鞋不极端能够明白这种风格的起(为什么如此做)及作用(这么做的利);我之建议是:在领略算法的规律后,根据自己的编程风格尝试着写一下,然后再同这套程序对比着来解,推敲一下别人怎么而这么做;当熟悉了全方位工艺流程后,你才能够体味这种程序风格的优势,再将这种编程风格慢慢转化为好的编程风格。**

说明:**念这顺序时可能发接触困难,不过当下属于情理之中的事,毕竟刚刚接触这种风格的童鞋不绝能够领悟这种风格的发(为什么如此做)及作用(这么做的好处);我的建议是:在了解算法的规律后,根据自己的编程风格尝试着写一下,然后又跟这套程序对比着来喻,推敲一下别人干什么要这么做;当熟悉了全流程后,你才会体味这种程序风格的优势,再以这种编程风格慢慢转化为温馨之编程风格。**

(二)程序中涉及的C语法讲解

(二)程序中干的C语法讲解

  这里,我光讲述何以要使用这些语法以及行使这些语法所带来的功利,至于细枝末节的问题,就请求各位童鞋自行查阅有关材料,顺带给大家推荐一按照对的C语言教材:C Primer
Plus
,毕竟上之兴味浓度以及书籍的编辑也有关。

  这里,我仅讲述为何而运用这些语法以及动用这些语法所带来的便宜,至于细枝末节的问题,就请求各位童鞋自行查阅有关材料,顺带给大家推荐一如约无可非议的C语言教材:C Primer
Plus
,毕竟上之兴趣浓度及书籍的编排也有关。

  1. 条件编译指令

  1. 原则编译指令

  第一处:#ifndef PID_H语句

  第一处:#ifndef PID_H语句

  使用该语句之目的是免造成把再定义语句(如,结构体类型定义)添加到工程中,而让编译出错

  使用该语句之目的大凡免造成把再定义语句(如,结构体类型定义)添加到工程中,而令编译出错

  说明:骨子里为可不要#ifndef语句,因为每个定义之变量都负有特定的大体意义,不会见促成更定义现象。

  说明:实质上为可是不要#ifndef语句,因为每个定义之变量都兼备特定的大体意义,不见面造成更定义现象。

  第二处:#if (PID_DEBUG) 语句

  第二处:#if (PID_DEBUG) 语句

  使用该语句的目的兑现力量切换注意了:大凡当校正PID参数后手动切换,通过改变宏定义语句#define
PID_DEBUG 1中之宏体实现),具体求看程序清单。

  使用该语句的目的心想事成力量切换注意了:凡是于校正PID参数后手动切换,通过变更宏定义语句#define
PID_DEBUG 1中之宏体实现),具体求看程序清单。

  2. 结构体及结构体指针

  2. 结构体及结构体指针

  动用结构体类型的好处:可为实现有平作用的各国变量进行“打包”处理

  动结构体类型的好处:可也促成有同功效的诸变量进行“打包”处理

  使用结构体指针的好处:通过传址调用,对方便对结构体变量本身进行操作

  使用结构体指针的好处:通过传址调用,对好对结构体变量本身进行操作

  3. typedef数据类型定义

  3. typedef数据类型定义

  动typedef数据类型定义的好处是方便过平台开展代码移植操作;但出于教材的故,造成广大童鞋都停于外表层次上的接头(typedef
数据类型 别名)
,因而这里作第一教学。

  运typedef数据类型定义的好处是方便跨越平台拓展代码移植操作;但鉴于教材的来头,造成过多童鞋都留于表层次上的明亮(typedef
数据类型 别名)
,因而这里作重点教学。

       本人之明:任何一个typedef声明中之标识符不再是一个变量,而是表示一个数据类型,其表示的数据类型为正规变量声明(去掉typedef)的充分标识符的数据类型。

       我的知道:任何一个typedef声明遭的标识符不再是一个变量,而是表示一个数据类型,其表示的数据类型为常规变量声明(去掉typedef)的老标识符的数据类型。



  理解起来或发接触困难,现做实例来上课:

  理解起来也许来接触困难,现做实例来教学:

       [例1]

       [例1]

  typedef int Myint;
  typedef int Myint;

  分析:

  分析:

  **率先步:正常变量声明(去丢typedef)**

  **首先步:正常变量声明(去丢typedef)**

    int Myint; 
    int Myint; 

  该语句表示定义一个int型变量Myint此,Myint为变量名

  该语句表示定义一个int型变量Myint此地,Myint为变量名

  其次步:整体分析

  老二步:整体分析

    typedef int Myint;
    typedef int Myint;

  该语句表示定义一个Myint类型这,Myint为数量列标识符,其切实所表示的项目:int型;

  该语句表示定义一个Myint类型这时,Myint为多少类标识符,其具体所表示的类:int型;

  应用:

  应用:

    Myint a; //声明整型变量a
    Myint a; //声明整型变量a

       [**例2]    **

       [**例2]    **

  typedef struct
  {
      //省略成员
  }PID;
  typedef struct
  {
      //省略成员
  }PID;

  分析:

  分析:

  第一步:例行变量声明(去掉typedef)**

  第一步:健康变量声明(去掉typedef)**

    struct
    {
        //省略成员
    }PID;
    struct
    {
        //省略成员
    }PID;

  该语句表示定义一个结构体变量PID此处,PID为变量名

  该语句表示定义一个结构体变量PID这边,PID为变量名

  第二步:一体化分析**

  第二步:一体化分析**

    typedef struct
    {
        //省略成员
    }PID;
    typedef struct
    {
        //省略成员
    }PID;

  该语句表示定义一个PID类型这,PID为多少类标识符,其实际所表示的档次:结构体类型,且其兼具的积极分子以及结构体变量PID这里,PID为变量名

  该语句表示定义一个PID类型这儿,PID为数据类标识符,其现实所表示的色:结构体类型,且该独具的积极分子和结构体变量PID此间,PID为变量名

  应用:

  应用:

      PID ASR; //定义结构体变量ASR
      PID ASR; //定义结构体变量ASR

       [例3]

       [例3]

  typedef void (*PFun)(int );
  typedef void (*PFun)(int );

  分析:

  分析:

  **第一步:好端端变量声明(去掉typedef)**

  **第一步:正常变量声明(去掉typedef)**

    void (*PFun)(int );
    void (*PFun)(int );

  该语句表示定义一个函数指针PFun此间,PFun为变量名

  该语句表示定义一个函数指针PFun这里,PFun为变量名

  第二步:整分析**

  第二步:完分析**

    typedef void (*PFun)(int );
    typedef void (*PFun)(int );

  该语句表示定义一个PFun类型此时,PFun为数据列标识符,其现实所代表的种类:函数指针类型,且该指向形参为int型,无返回值的同样类函数;

  该语句表示定义一个PFun类型此刻,PFun为数据列标识符,其现实所代表的色:函数指针类型,且该指向形参为int型,无返回值的平类函数;

  应用:

  应用:

    PFun  pf; //定义函数指针pf
    PFun  pf; //定义函数指针pf


  说明:typedef的用法及宏定义#define的用法类似,但还要出区别,体现在以下简单沾:

  说明:typedef的用法及宏定义#define的用法类似,但还要生出区别,体现于偏下简单碰:

  (a)
 typedef是指向数据类型的概念,而#define是对准数值的概念;

  (a)
 typedef是本着数据类型的概念,而#define是针对性数值的概念;

  (b)
 typedef由编译器解释,而#define由预处理器执行。

  (b)
 typedef由编译器解释,而#define由预处理器执行。

  4. 空形参函数和展示参带(void)函数

  4. 空形参函数和出示参带(void)函数

  这是在C/C++中相当好模糊的地方,因此这里主要介绍一下,若是这知识点没打明白,那么这个顺序你就是无法看懂为什么会如此定义函数指针及以函数指针来开展函数调用。

  这是在C/C++中相当易模糊的地方,因此此关键介绍一下,若是者知识点没做懂,那么这个次你就无法看懂为什么会如此定义函数指针及动函数指针来进展函数调用。

  void本身便是同种植多少类(空类型),把void作为形参时,表示此函数不欲参数。

  void本身就是是同等栽多少列(空类型),把void作为形参时,表示是函数不需要参数。

  在C++中,空形参表与新参为void是等价格的,这是C++中明确规定的;但在C中虽是两码事:C中的空形参表仅代表函数的形参个数和品种不确定,并非无参数,这会少挂于编译器的路检查机制,从而导致类型安全隐患,所以于C中待表示函数无形参时,最好用void,此时编译器将进行函数参数类型验证。

  以C++中,空形参表与新参为void是等价格的,这是C++中明确规定的;但在C中虽是两码事:C中的空形参表仅代表函数的形参个数与花色不确定,并非没有参数,这会小挂于编译器的门类检查体制,从而导致类型安全隐患,所以于C中需要表示函数无形参时,最好用void,此时编译器将开展函数参数类型验证。

  [例]

  [例]

void pid_calc(int); //函数声明
void (*calc_1)(int); //函数指针声明
void (*calc_2)(); //函数指针声明

void main()
{
    //将函数的入口地址赋给函数指针
    calc_1=pid_calc; //C编译通过;C++编译通过
    calc_2=pid_calc; //C编译通过;C++编译失败
}
void pid_calc(int); //函数声明
void (*calc_1)(int); //函数指针声明
void (*calc_2)(); //函数指针声明

void main()
{
    //将函数的入口地址赋给函数指针
    calc_1=pid_calc; //C编译通过;C++编译通过
    calc_2=pid_calc; //C编译通过;C++编译失败
}

  5. 函数指针及其函数调用

  5. 函数指针及其函数调用

  函数调用,除了直接调用”函数称呼(实参)”这种语法外,还而通过函数指针来实现,两者并无分,但为代码的紧凑性及美观性,建议大家利用函数指针来进行函数调用。

  函数调用,除了直接调用”函数称作(实参)”这种语法外,还只是通过函数指针来兑现,两者并凭别,但为代码的紧凑性及美观性,建议大家以函数指针来进展函数调用。

  于我放的点滴效代码中,一拟是直接函数调用C/C++通用),另一样效仿是使函数指针进行函数调用只有适用于C),大家而咀嚼这半种植用法的别。

  以自家放的少效代码中,一拟是直函数调用C/C++通用),另一样效仿是动用函数指针进行函数调用就适用于C),大家只是咀嚼这半种用法的分别。

  6. 数据类型转换

  6. 数据类型转换

  C语言中的数据类型分为自动类型转换与强制类型转换

  C语言中之数据类型分为自动类型转换与强制类型转换

  (1) 自动类型转换(**出于编译器完成**)

  (1) 自动类型转换(**由于编译器完成**)

  机动转换的适用场合及其转换规则,请读者查阅有关材料

  活动转换的适用场合及其转换规则,请读者查阅有关材料

  (2) 强制类型转换由此类型转换运算实现

  (2) 强制类型转换经过类型转换运算实现

  在本程序中,即好将自定义函数的函数名pid_calc(**函数称作代表针对诺函数的入口地址**)直接赋值给函数指针calc,也可是将自定义函数的函数名pid_calc先强制类型转换改换为函数指针继,再赋值给函数指针calc;这片种方法尽管能及相同的效应,但该所体现的考虑也大相径庭。

  在本程序中,即好用自定义函数的函数名pid_calc(**函数名为代表针对诺函数的入口地址**)直赋值给函数指针calc,也可将由定义函数的函数名pid_calc先强制类型转换易为函数指针继,再赋值给函数指针calc;这点儿种艺术尽管能达成平等的效益,但那所体现的合计也大相径庭。

  现把代码截取出来,方便大家对待:

  现把代码截取出来,方便大家对待:

void pid_calc(PID *p); //函数声明
void (*calc)(); //函数指针:指向PID计算函数

void main()
{
    //将函数的入口地址赋给指针变量
    calc=(void (*)(unsigned long))pid_calc; //编译通过(强制类型转换)
    calc=pid_calc; //编译通过
}
void pid_calc(PID *p); //函数声明
void (*calc)(); //函数指针:指向PID计算函数

void main()
{
    //将函数的入口地址赋给指针变量
    calc=(void (*)(unsigned long))pid_calc; //编译通过(强制类型转换)
    calc=pid_calc; //编译通过
}

  7. 代码换行问题

  7. 代码换行问题

  为了代码的漂亮调节好,需涉及到代码换行问题

  为了代码的优美调剂好,需涉及到代码换行问题

  于本程序的宏定义语句被使了”\”,这是宏定义中连接达下行的连接符,表示该宏定义还无结束。

  于本程序的宏定义语句被运用了”\”,这是宏定义中连接上下行的连接符,表示该宏定义还免结。

//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0, \
                      (void (*)(unsigned long))pid_calc}
//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0, \
                      (void (*)(unsigned long))pid_calc}

=======================================================================================

=======================================================================================

附件一:直接函数调用(C/C++通用)

附件一:直接函数调用(C/C++通用)

PID.h文件

PID.h文件

//===================================================
//PID.h
//===================================================
#ifndef PID_H
#define PID_H

//定义PID计算用到的结构体类型
typedef struct
{
    float Ref;         //输入:系统待调节量的给定值
    float Fdb;         //输入:系统待调节量的反馈值

    //PID控制器部分
    float Kp;          //参数:比例系数
    float Ki;          //参数:积分系数
    float Kd;          //参数:微分系数

    float T;           //参数:离散化系统的采样周期

    float a0;          //变量:a0
    float a1;          //变量: a1
    float a2;          //变量: a2

    float Err;          //变量:当前的偏差e(k)
    float Err_1;           //历史:前一步的偏差e(k-1)
    float Err_2;          //历史:前前一步的偏差e(k-2)

    float Out;           //输出:PID控制器的输出u(k)
    float Out_1;            //历史:PID控制器前一步的输出u(k-1)
    float OutMax;          //参数:PID控制器的最大输出
    float OutMin;          //参数:PID控制器的最小输出

}PID;

//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0}

//条件编译的判别条件
#define PID_DEBUG 1                     

//函数声明
void pid_calc(PID *p);

#endif

//===================================================
//End of file.
//===================================================
//===================================================
//PID.h
//===================================================
#ifndef PID_H
#define PID_H

//定义PID计算用到的结构体类型
typedef struct
{
    float Ref;         //输入:系统待调节量的给定值
    float Fdb;         //输入:系统待调节量的反馈值

    //PID控制器部分
    float Kp;          //参数:比例系数
    float Ki;          //参数:积分系数
    float Kd;          //参数:微分系数

    float T;           //参数:离散化系统的采样周期

    float a0;          //变量:a0
    float a1;          //变量: a1
    float a2;          //变量: a2

    float Err;          //变量:当前的偏差e(k)
    float Err_1;           //历史:前一步的偏差e(k-1)
    float Err_2;          //历史:前前一步的偏差e(k-2)

    float Out;           //输出:PID控制器的输出u(k)
    float Out_1;            //历史:PID控制器前一步的输出u(k-1)
    float OutMax;          //参数:PID控制器的最大输出
    float OutMin;          //参数:PID控制器的最小输出

}PID;

//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0}

//条件编译的判别条件
#define PID_DEBUG 1                     

//函数声明
void pid_calc(PID *p);

#endif

//===================================================
//End of file.
//===================================================

 

 

PID.c文件 

PID.c文件 

//===================================================
//PID.c
//===================================================
#include "PID.h"
//===================函数定义========================
/****************************************************
*说    明:
*    (1)PID控制器默认为PI调节器
*    (2)使用了条件编译进行功能切换:节省计算时间
*        在校正PID参数时,使用宏定义将PID_DEBUG设为1;
*        当参数校正完成后,使用宏定义将PID_DEBUG设为0,同时,在初始化时
*    直接为p->a0、p->a1、p->a2赋值
****************************************************/
void pid_calc(PID *p)
{
    //使用条件编译进行功能切换
    #if (PID_DEBUG)
    float a0,a1,a2;
    //计算中间变量a0、a1、a2
    a0=p->Kp+p->Ki*p->T+p->Kd/p->T;
    a1=p->Kp+2*p->Kd/p->T;
    a2=p->Kd/p->T;
    //计算PID控制器的输出
    p->Out=p->Out_1+a0*p->Err-a1*p->Err_1+a2*p->Err_2;
    #else
    //计算PID控制器的输出
    p->Out=p->Out_1+p->a0*p->Err-p->a1*p->Err_1+p->a2*p->Err_2;
    #endif

    //输出限幅
    if(p->Out>p->OutMax)
        p->Out=p->OutMax;
    if(p->Out<p->OutMin)
        p->Out=p->OutMin;

    //为下步计算做准备
    p->Out_1=p->Out;
    p->Err_2=p->Err_1;
    p->Err_1=p->Err;

}

//===================================================
//End of file.
//===================================================
//===================================================
//PID.c
//===================================================
#include "PID.h"
//===================函数定义========================
/****************************************************
*说    明:
*    (1)PID控制器默认为PI调节器
*    (2)使用了条件编译进行功能切换:节省计算时间
*        在校正PID参数时,使用宏定义将PID_DEBUG设为1;
*        当参数校正完成后,使用宏定义将PID_DEBUG设为0,同时,在初始化时
*    直接为p->a0、p->a1、p->a2赋值
****************************************************/
void pid_calc(PID *p)
{
    //使用条件编译进行功能切换
    #if (PID_DEBUG)
    float a0,a1,a2;
    //计算中间变量a0、a1、a2
    a0=p->Kp+p->Ki*p->T+p->Kd/p->T;
    a1=p->Kp+2*p->Kd/p->T;
    a2=p->Kd/p->T;
    //计算PID控制器的输出
    p->Out=p->Out_1+a0*p->Err-a1*p->Err_1+a2*p->Err_2;
    #else
    //计算PID控制器的输出
    p->Out=p->Out_1+p->a0*p->Err-p->a1*p->Err_1+p->a2*p->Err_2;
    #endif

    //输出限幅
    if(p->Out>p->OutMax)
        p->Out=p->OutMax;
    if(p->Out<p->OutMin)
        p->Out=p->OutMin;

    //为下步计算做准备
    p->Out_1=p->Out;
    p->Err_2=p->Err_1;
    p->Err_1=p->Err;

}

//===================================================
//End of file.
//===================================================

 

 

amain.c主函数文件

amain.c主函数文件

//===================================================
//amain.c
//===================================================

//将用户定义的头文件包含进来
#include "PID.h"

//=============宏定义=====================
#define T0   0.0002       //离散化采样周期,单位s

//============全局变量========================
//定义PID控制器对应的结构体变量
PID ASR=PID_DEFAULTS;       //速度PI调节器ASR

//定义PID控制器的参数及输出限幅值
float SpeedKp=2,SpeedKi=1,SpeedLimit=10;  //速度PI调节器ASR

//===============主程序=======================
void main()
{
    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;

}

//============中断服务程序====================
interrupt void T1UFINT_ISR(void)
{
    //转速调节ASR
    ASR.Ref=input1;         //速度给定
    ASR.Fdb=input2;         //速度反馈
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    pid_calc(&ASR);         //函数调用:启动PID计算
    output=ASR.Out;         //读取PID控制器的输出

}

//===================================================
//End of file.
//===================================================
//===================================================
//amain.c
//===================================================

//将用户定义的头文件包含进来
#include "PID.h"

//=============宏定义=====================
#define T0   0.0002       //离散化采样周期,单位s

//============全局变量========================
//定义PID控制器对应的结构体变量
PID ASR=PID_DEFAULTS;       //速度PI调节器ASR

//定义PID控制器的参数及输出限幅值
float SpeedKp=2,SpeedKi=1,SpeedLimit=10;  //速度PI调节器ASR

//===============主程序=======================
void main()
{
    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;

}

//============中断服务程序====================
interrupt void T1UFINT_ISR(void)
{
    //转速调节ASR
    ASR.Ref=input1;         //速度给定
    ASR.Fdb=input2;         //速度反馈
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    pid_calc(&ASR);         //函数调用:启动PID计算
    output=ASR.Out;         //读取PID控制器的输出

}

//===================================================
//End of file.
//===================================================

=======================================================================================

=======================================================================================

附件二:使用函数指针进行函数调用(仅适用于C)

附件二:使用函数指针进行函数调用(仅适用于C)

PID.h文件

PID.h文件

//===================================================
//PID.h
//===================================================
#ifndef PID_H
#define PID_H

//定义PID计算用到的结构体类型
typedef struct
{
    float Ref;       //输入:系统待调节量的给定值
    float Fdb;       //输入:系统待调节量的反馈值

    //PID控制器部分
    float Kp;        //参数:比例系数
    float Ki;        //参数:积分系数
    float Kd;        //参数:微分系数

    float T;         //参数:离散化系统的采样周期

    float a0;        //变量:a0
    float a1;        //变量: a1
    float a2;        //变量: a2

    float Err;       //变量:当前的偏差e(k)
    float Err_1;      //历史:前一步的偏差e(k-1)
    float Err_2;        //历史:前前一步的偏差e(k-2)

    float Out;       //输出:PID控制器的输出u(k)
    float Out_1;        //历史:PID控制器前一步的输出u(k-1)
    float OutMax;     //参数:PID控制器的最大输出
    float OutMin;     //参数:PID控制器的最小输出

    void (*calc)();    //函数指针:指向PID计算函数

}PID;

//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0, \
                      (void (*)(unsigned long))pid_calc} //加与不加强制类型转换都没影响

//条件编译的判别条件
#define PID_DEBUG 1                     

//函数声明
void pid_calc(PID *p);

#endif

//===================================================
//End of file.
//===================================================
//===================================================
//PID.h
//===================================================
#ifndef PID_H
#define PID_H

//定义PID计算用到的结构体类型
typedef struct
{
    float Ref;       //输入:系统待调节量的给定值
    float Fdb;       //输入:系统待调节量的反馈值

    //PID控制器部分
    float Kp;        //参数:比例系数
    float Ki;        //参数:积分系数
    float Kd;        //参数:微分系数

    float T;         //参数:离散化系统的采样周期

    float a0;        //变量:a0
    float a1;        //变量: a1
    float a2;        //变量: a2

    float Err;       //变量:当前的偏差e(k)
    float Err_1;      //历史:前一步的偏差e(k-1)
    float Err_2;        //历史:前前一步的偏差e(k-2)

    float Out;       //输出:PID控制器的输出u(k)
    float Out_1;        //历史:PID控制器前一步的输出u(k-1)
    float OutMax;     //参数:PID控制器的最大输出
    float OutMin;     //参数:PID控制器的最小输出

    void (*calc)();    //函数指针:指向PID计算函数

}PID;

//定义PID控制器的初始值
#define PID_DEFAULTS {0,0, \
                      0,0,0, \
                      0.0002, \
                      0,0,0, \
                      0,0,0, \
                      0,0,0,0, \
                      (void (*)(unsigned long))pid_calc} //加与不加强制类型转换都没影响

//条件编译的判别条件
#define PID_DEBUG 1                     

//函数声明
void pid_calc(PID *p);

#endif

//===================================================
//End of file.
//===================================================

 

 

PID.c文件

PID.c文件

//===================================================
//PID.c
//===================================================
#include "PID.h"
//===================函数定义========================
/****************************************************
*说    明:
*    (1)PID控制器默认为PI调节器
*    (2)使用了条件编译进行功能切换:节省计算时间
*        在校正PID参数时,使用宏定义将PID_DEBUG设为1;
*        当参数校正完成后,使用宏定义将PID_DEBUG设为0,同时,在初始化时
*    直接为p->a0、p->a1、p->a2赋值
****************************************************/
void pid_calc(PID *p)
{
    //使用条件编译进行功能切换
    #if (PID_DEBUG)
    float a0,a1,a2;
    //计算中间变量a0、a1、a2
    a0=p->Kp+p->Ki*p->T+p->Kd/p->T;
    a1=p->Kp+2*p->Kd/p->T;
    a2=p->Kd/p->T;
    //计算PID控制器的输出
    p->Out=p->Out_1+a0*p->Err-a1*p->Err_1+a2*p->Err_2;
    #else
    //计算PID控制器的输出
    p->Out=p->Out_1+p->a0*p->Err-p->a1*p->Err_1+p->a2*p->Err_2;
    #endif

    //输出限幅
    if(p->Out>p->OutMax)
        p->Out=p->OutMax;
    if(p->Out<p->OutMin)
        p->Out=p->OutMin;

    //为下步计算做准备
    p->Out_1=p->Out;
    p->Err_2=p->Err_1;
    p->Err_1=p->Err;

}

//===================================================
//End of file.
//===================================================
//===================================================
//PID.c
//===================================================
#include "PID.h"
//===================函数定义========================
/****************************************************
*说    明:
*    (1)PID控制器默认为PI调节器
*    (2)使用了条件编译进行功能切换:节省计算时间
*        在校正PID参数时,使用宏定义将PID_DEBUG设为1;
*        当参数校正完成后,使用宏定义将PID_DEBUG设为0,同时,在初始化时
*    直接为p->a0、p->a1、p->a2赋值
****************************************************/
void pid_calc(PID *p)
{
    //使用条件编译进行功能切换
    #if (PID_DEBUG)
    float a0,a1,a2;
    //计算中间变量a0、a1、a2
    a0=p->Kp+p->Ki*p->T+p->Kd/p->T;
    a1=p->Kp+2*p->Kd/p->T;
    a2=p->Kd/p->T;
    //计算PID控制器的输出
    p->Out=p->Out_1+a0*p->Err-a1*p->Err_1+a2*p->Err_2;
    #else
    //计算PID控制器的输出
    p->Out=p->Out_1+p->a0*p->Err-p->a1*p->Err_1+p->a2*p->Err_2;
    #endif

    //输出限幅
    if(p->Out>p->OutMax)
        p->Out=p->OutMax;
    if(p->Out<p->OutMin)
        p->Out=p->OutMin;

    //为下步计算做准备
    p->Out_1=p->Out;
    p->Err_2=p->Err_1;
    p->Err_1=p->Err;

}

//===================================================
//End of file.
//===================================================

 

 

amain.c主函数文件

amain.c主函数文件

//===================================================
//amain.c
//===================================================

//将用户定义的头文件包含进来
#include "PID.h"

//=============宏定义=====================
#define T0   0.0002     //离散化采样周期,单位s

//============全局变量========================
//定义PID控制器对应的结构体变量
PID ASR=PID_DEFAULTS;     //速度PI调节器ASR

//定义PID控制器的参数及输出限幅值
float SpeedKp=2,SpeedKi=1,SpeedLimit=10;   //速度PI调节器ASR

//===============主程序=======================
void main()
{
    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;

}

//============中断服务程序====================
interrupt void T1UFINT_ISR(void)
{
    //转速调节ASR
    ASR.Ref=input1;         //速度给定
    ASR.Fdb=input2;         //速度反馈
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    ASR.calc(&ASR);         //函数调用:启动PID计算
    output=ASR.Out;         //读取PID控制器的输出

}

//===================================================
//End of file.
//===================================================
//===================================================
//amain.c
//===================================================

//将用户定义的头文件包含进来
#include "PID.h"

//=============宏定义=====================
#define T0   0.0002     //离散化采样周期,单位s

//============全局变量========================
//定义PID控制器对应的结构体变量
PID ASR=PID_DEFAULTS;     //速度PI调节器ASR

//定义PID控制器的参数及输出限幅值
float SpeedKp=2,SpeedKi=1,SpeedLimit=10;   //速度PI调节器ASR

//===============主程序=======================
void main()
{
    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;

}

//============中断服务程序====================
interrupt void T1UFINT_ISR(void)
{
    //转速调节ASR
    ASR.Ref=input1;         //速度给定
    ASR.Fdb=input2;         //速度反馈
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    ASR.calc(&ASR);         //函数调用:启动PID计算
    output=ASR.Out;         //读取PID控制器的输出

}

//===================================================
//End of file.
//===================================================

 

 

相关文章

admin

网站地图xml地图