国产清洁工av剧情精品_欧洲日韩av网站_国产视频手机在线播放_亚洲精品国产va在线观看蜜芽_AV网页在线好看_久久精品国产亚洲精品2023_一级日韩一级欧美_亚洲欧美日韩中文天堂_最近中文字幕mv在线视频_男女啪啪高潮激烈免费版网站

您好,歡迎訪問泰安宏盛自動化科技有限公司!
20年專注工控自動化研發(fā)制造 系統(tǒng)設(shè)計\制作\安裝一條龍服務(wù)
全國咨詢熱線:18605386049
當(dāng)前位置: 首頁 > 技術(shù)資料 > 程序案例

PID控制器的數(shù)字實現(xiàn)及C語法講解

作者:小編 時間:2024-05-02 17:31:01 點擊:

  • 概述  

  為方便學(xué)習(xí)與交流,根據(jù)自己的理解與經(jīng)驗寫了這份教程,有錯誤之處請各位讀者予以指出,具體包含以下三部分內(nèi)容:

  (1)  PID數(shù)字化的推導(dǎo)過程(實質(zhì):微積分的近似計算);

  (2)  程序風(fēng)格介紹(程序風(fēng)格來源于TI官方案例);

  (3)  C有關(guān)語法簡述(語法會結(jié)合實例進(jìn)行講解)。

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

  • PID控制器的數(shù)字化

  PID控制器是工業(yè)過程控制中廣泛采用的一種控制器,其中,P、I、D分別為比例(Proportion)、積分(Integral)、微分(Differential)的簡寫;將偏差的比例、積分和微分通過線性組合構(gòu)成控制量,用該控制量對受控對象進(jìn)行控制,稱為PID算法。

  為了用軟件實現(xiàn)PID算法,需將PID控制器離散化。

  1. 整體思路

        

undefined


  2. 方框圖

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

          image.png 

  3. 拉氏域的表達(dá)式

  根據(jù)方框圖,可寫出PID控制器對應(yīng)的傳遞函數(shù):

            image.png  

         其中,Kp為比例系數(shù),ki為積分系數(shù),Kd為微分系數(shù)

 

  4. 時域的表達(dá)式

  在分析時,通常借助于拉氏空間,例如判斷系統(tǒng)的穩(wěn)定性與相對穩(wěn)定性;而現(xiàn)在我們關(guān)心的是時域里的問題,因此對上式進(jìn)行拉普拉斯逆變換,得到時域里的表達(dá)式:

             image.png

       其對應(yīng)的結(jié)構(gòu)框圖如圖所示:

              image.png

  5. 差分方程

  該時域里的表達(dá)式不便于編程處理,因此需對該式進(jìn)行離散化處理,從而得到可編程實現(xiàn)的差分方程,分析過程如下:

  (說明:PID離散化的實質(zhì)為微積分的離散化(數(shù)值化處理),由于這個推導(dǎo)過程很多教材上都有介紹,因而略去推導(dǎo)過程,只給出最終表達(dá)式,程序的算法就是基于此表達(dá)式而寫的

  數(shù)字PID控制器的增量式算法:

      image.png

      其中,T為步長,即采樣周期(由微控制器的定時器確定

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

      image.png

  這樣就可編程實現(xiàn)了或許有人會問,為什么差分方程就可編程實現(xiàn)呢?這是因為解差分方程的一般解法就是迭代法,而迭代法只需初值跟通項公式,這在計算機編程中很容易實現(xiàn) 

  為使編程方便,可引入中間變量,定義如下:

             image.png

  則,PID控制器增量式算法的差分方程變?yōu)椋?/strong>

      image.png

  說明:

   (1)在PID增量式算法中只需對輸出u(t)作限幅處理;

   (2)當(dāng)微分系數(shù) Kd=0 時,PID控制器就成了PI控制器在編寫PID程序時默認(rèn)使其為PI調(diào)節(jié)器;

       當(dāng)積分系數(shù) Ki=0 時,PID控制器就成了PD控制器。

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

  •  基于微控制器的算法實現(xiàn)

  我寫的數(shù)字PID程序如圖所示(在最后的附件部分),有兩套代碼,一套是直接函數(shù)調(diào)用C/C++通用,另一套是使用函數(shù)指針進(jìn)行函數(shù)調(diào)用僅適用于C,現(xiàn)從兩個方面對該程序做講解:

(一)程序風(fēng)格

  程序采用了模塊化編程的思想,這樣做的目的增強代碼的可移植性及程序的可讀性

  程序被拆分成三個模塊:

  一個是PID的頭文件’PID.h’:主要是定義算法實現(xiàn)有關(guān)的數(shù)據(jù)類型;

  一個是PID的源文件’PID.c’:主要是定義算法實現(xiàn)的函數(shù);

  一個是主函數(shù)文件’amain.c’:PID程序的使用方法,即在主程序中做相應(yīng)的初始化工作,在中斷服務(wù)程序中進(jìn)行PID的計算。

說明:讀這個程序時可能有點困難,不過這屬情理之中的事,畢竟剛接觸這種風(fēng)格的童鞋不太能理解這種風(fēng)格的產(chǎn)生(為什么這么做)及用意(這么做的好處);我的建議是:在理解算法的原理后,根據(jù)自己的編程風(fēng)格嘗試著寫一下,然后再跟這套程序?qū)Ρ戎鴣砝斫?,推敲一下別人為什么要這么做;當(dāng)熟悉了整個流程后,你才能體會這種程序風(fēng)格的優(yōu)勢,再將這種編程風(fēng)格慢慢轉(zhuǎn)化為自己的編程風(fēng)格。

(二)程序中涉及的C語法講解

  這里,我只講述為什么要采用這些語法以及采用這些語法所帶來的好處,至于細(xì)枝末節(jié)的問題,就請各位童鞋自行查閱有關(guān)資料,順帶給大家推薦一本不錯的C語言教材:C Primer Plus,畢竟學(xué)習(xí)的興趣濃度跟書籍的編排也有關(guān)。

  1. 條件編譯指令

  第一處:#ifndef PID_H語句

  使用該語句的目的是避免造成把重復(fù)定義語句(如,結(jié)構(gòu)體類型定義)添加到工程中,而使得編譯出錯

  說明:其實也可不用#ifndef語句,因為每個定義的變量都具有特定的物理含義,不會造成重復(fù)定義現(xiàn)象。

  第二處:#if (PID_DEBUG) 語句

  使用該語句的目的實現(xiàn)功能切換注意了:是在校正PID參數(shù)后手動切換,通過改變宏定義語句#define PID_DEBUG 1中的宏體實現(xiàn)),具體請看程序清單。

  2. 結(jié)構(gòu)體及結(jié)構(gòu)體指針

  使用結(jié)構(gòu)體類型的好處:可為實現(xiàn)某一功能的各變量進(jìn)行“打包”處理

  使用結(jié)構(gòu)體指針的好處:通過傳址調(diào)用,對方便對結(jié)構(gòu)體變量本身進(jìn)行操作

  3. typedef數(shù)據(jù)類型定義

  使用typedef數(shù)據(jù)類型定義的好處是方便跨平臺進(jìn)行代碼移植操作;但由于教材的緣故,造成很多童鞋都停留在表面層次上的理解(typedef 數(shù)據(jù)類型 別名),因而此處作重點講解。

       我的理解:任何一個typedef聲明中的標(biāo)識符不再是一個變量,而是代表一個數(shù)據(jù)類型,其表示的數(shù)據(jù)類型為正常變量聲明(去掉typedef)的那個標(biāo)識符的數(shù)據(jù)類型。


  理解起來可能有點困難,現(xiàn)結(jié)合實例來講解:

       [例1]

  typedef int Myint;

  分析:

  第一步:正常變量聲明(去掉typedef

    int Myint; 

  該語句表示定義一個int型變量Myint這里,Myint為變量名;

  第二步:整體分析

    typedef int Myint;

  該語句表示定義一個Myint類型此時,Myint為數(shù)據(jù)類型標(biāo)識符,其具體所表示的類型:int型;

  應(yīng)用:

    Myint a; //聲明整型變量a

       [例2]    

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

  分析:

  第一步:正常變量聲明(去掉typedef)

    struct    {
        //省略成員    }PID;

  該語句表示定義一個結(jié)構(gòu)體變量PID這里,PID為變量名

  第二步:整體分析

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

  該語句表示定義一個PID類型此時,PID為數(shù)據(jù)類型標(biāo)識符,其具體所表示的類型:結(jié)構(gòu)體類型,且其具有的成員同結(jié)構(gòu)體變量PID這里,PID為變量名

  應(yīng)用:

      PID ASR; //定義結(jié)構(gòu)體變量ASR

       [例3]

  typedef void (*PFun)(int );

  分析:

  第一步:正常變量聲明(去掉typedef)

    void (*PFun)(int );

  該語句表示定義一個函數(shù)指針PFun這里,PFun為變量名;

  第二步:整體分析

    typedef void (*PFun)(int );

  該語句表示定義一個PFun類型此時,PFun為數(shù)據(jù)類型標(biāo)識符,其具體所表示的類型:函數(shù)指針類型,且其指向形參為int型,無返回值的一類函數(shù);

  應(yīng)用:

    PFun  pf; //定義函數(shù)指針pf

  說明:typedef的用法與宏定義#define的用法類似,但又有區(qū)別,體現(xiàn)在以下兩點:

  (a)  typedef是對數(shù)據(jù)類型的定義,而#define是對數(shù)值的定義;

  (b)  typedef由編譯器解釋,而#define由預(yù)處理器執(zhí)行。

  4. 空形參函數(shù)和形參帶(void)函數(shù)

  這是在C/C++中相當(dāng)容易混淆的地方,因此這里重點介紹一下,若是這個知識點沒搞懂,那么這個程序你就無法看懂為什么會如此定義函數(shù)指針及利用函數(shù)指針來進(jìn)行函數(shù)調(diào)用。

  void本身就是一種數(shù)據(jù)類型(空類型),把void作為形參時,表示這個函數(shù)不需要參數(shù)。

  在C++中,空形參表與新參為void是等價的,這是C++中明確規(guī)定的;但在C中則是兩回事:C中的空形參表僅表示函數(shù)的形參個數(shù)和類型不確定,并非沒有參數(shù),這會暫時掛起編譯器的類型檢查機制,從而造成類型安全隱患,所以在C中欲表示函數(shù)無形參時,最好用void,此時編譯器將進(jìn)行函數(shù)參數(shù)類型驗證。

  [例]

復(fù)制代碼
void pid_calc(int); //函數(shù)聲明void (*calc_1)(int); //函數(shù)指針聲明void (*calc_2)(); //函數(shù)指針聲明void main()
{    //將函數(shù)的入口地址賦給函數(shù)指針
    calc_1=pid_calc; //C編譯通過;C++編譯通過
    calc_2=pid_calc; //C編譯通過;C++編譯失敗}
復(fù)制代碼

  5. 函數(shù)指針及其函數(shù)調(diào)用

  函數(shù)調(diào)用,除了直接調(diào)用”函數(shù)名(實參)”這種語法外,還可通過函數(shù)指針來實現(xiàn),兩者并無區(qū)別,但為了代碼的緊湊性及美觀性,建議大家使用函數(shù)指針來進(jìn)行函數(shù)調(diào)用。

  在我放出的兩套代碼中,一套是直接函數(shù)調(diào)用C/C++通用),另一套是使用函數(shù)指針進(jìn)行函數(shù)調(diào)用僅適用于C),大家可體會這兩種用法的區(qū)別。

  6. 數(shù)據(jù)類型轉(zhuǎn)換

  C語言中的數(shù)據(jù)類型分為自動類型轉(zhuǎn)換與強制類型轉(zhuǎn)換

  (1) 自動類型轉(zhuǎn)換(由編譯器完成)

  自動轉(zhuǎn)換的適用場合及其轉(zhuǎn)換規(guī)則,請讀者查閱有關(guān)資料

  (2) 強制類型轉(zhuǎn)換通過類型轉(zhuǎn)換運算實現(xiàn)

  在本程序中,即可以將自定義函數(shù)的函數(shù)名pid_calc函數(shù)名代表對應(yīng)函數(shù)的入口地址直接賦值給函數(shù)指針calc,也可將自定義函數(shù)的函數(shù)名pid_calc先強制類型轉(zhuǎn)換轉(zhuǎn)換為函數(shù)指針后,再賦值給函數(shù)指針calc;這兩種方式雖說能達(dá)到同樣的效果,但其所反映的思想?yún)s有所不同。

  現(xiàn)把代碼截取出來,方便大家對比:

復(fù)制代碼
void pid_calc(PID *p); //函數(shù)聲明void (*calc)(); //函數(shù)指針:指向PID計算函數(shù)void main()
{    //將函數(shù)的入口地址賦給指針變量
    calc=(void (*)(unsigned long))pid_calc; //編譯通過(強制類型轉(zhuǎn)換)
    calc=pid_calc; //編譯通過}
復(fù)制代碼

  7. 代碼換行問題

  為了代碼的美觀調(diào)試方便,需涉及到代碼換行問題

  在本程序的宏定義語句中使用了”\”,這是宏定義中連接上下行的連接符,表示該宏定義還未結(jié)束。

復(fù)制代碼
//定義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}
復(fù)制代碼

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

附件一:直接函數(shù)調(diào)用C/C++通用

PID.h文件

復(fù)制代碼
//===================================================//PID.h//===================================================#ifndef PID_H#define PID_H//定義PID計算用到的結(jié)構(gòu)體類型typedef struct{    float Ref;         //輸入:系統(tǒng)待調(diào)節(jié)量的給定值
    float Fdb;         //輸入:系統(tǒng)待調(diào)節(jié)量的反饋值    
    //PID控制器部分
    float Kp;          //參數(shù):比例系數(shù)
    float Ki;          //參數(shù):積分系數(shù)
    float Kd;          //參數(shù):微分系數(shù)
    
    float T;           //參數(shù):離散化系統(tǒng)的采樣周期
    
    float a0;          //變量:a0
    float a1;          //變量: a1
    float a2;          //變量: a2
    
    float Err;          //變量:當(dāng)前的偏差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;          //參數(shù):PID控制器的最大輸出
    float OutMin;          //參數(shù):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                     
    
//函數(shù)聲明void pid_calc(PID *p);#endif//===================================================//End of file.//===================================================
復(fù)制代碼

 

PID.c文件 

復(fù)制代碼
#include  pid_calc(PID *
     (PID_DEBUG)    
    a0=p->Kp+p->Ki*p->T+p->Kd/p->=p->Kp+*p->Kd/p->=p->Kd/p->
    p->Out=p->Out_1+a0*p->Err-a1*p->Err_1+a2*p->
    
    p->Out=p->Out_1+p->a0*p->Err-p->a1*p->Err_1+p->a2*p->
    
    
    (p->Out>p->->Out=p->(p->Out<p->->Out=p->
    p->Out_1=p->->Err_2=p->->Err_1=p->
復(fù)制代碼

 

amain.c主函數(shù)文件

復(fù)制代碼
//===================================================//amain.c//===================================================//將用戶定義的頭文件包含進(jìn)來#include "PID.h"//=============宏定義=====================#define T0   0.0002       //離散化采樣周期,單位s//============全局變量========================//定義PID控制器對應(yīng)的結(jié)構(gòu)體變量PID ASR=PID_DEFAULTS;       //速度PI調(diào)節(jié)器ASR//定義PID控制器的參數(shù)及輸出限幅值float SpeedKp=2,SpeedKi=1,SpeedLimit=10;  //速度PI調(diào)節(jié)器ASR//===============主程序=======================void main()
{    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;
    
}//============中斷服務(wù)程序====================interrupt void T1UFINT_ISR(void)
{    //轉(zhuǎn)速調(diào)節(jié)ASR
    ASR.Ref=input1;         //速度給定
    ASR.Fdb=input2;         //速度反饋
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    pid_calc(&ASR);         //函數(shù)調(diào)用:啟動PID計算
    output=ASR.Out;         //讀取PID控制器的輸出    }//===================================================//End of file.//===================================================
復(fù)制代碼

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

附件二:使用函數(shù)指針進(jìn)行函數(shù)調(diào)用僅適用于C

PID.h文件

復(fù)制代碼
//===================================================//PID.h//===================================================#ifndef PID_H#define PID_H//定義PID計算用到的結(jié)構(gòu)體類型typedef struct{    float Ref;       //輸入:系統(tǒng)待調(diào)節(jié)量的給定值
    float Fdb;       //輸入:系統(tǒng)待調(diào)節(jié)量的反饋值    
    //PID控制器部分
    float Kp;        //參數(shù):比例系數(shù)
    float Ki;        //參數(shù):積分系數(shù)
    float Kd;        //參數(shù):微分系數(shù)
    
    float T;         //參數(shù):離散化系統(tǒng)的采樣周期
    
    float a0;        //變量:a0
    float a1;        //變量: a1
    float a2;        //變量: a2
    
    float Err;       //變量:當(dāng)前的偏差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;     //參數(shù):PID控制器的最大輸出
    float OutMin;     //參數(shù):PID控制器的最小輸出
    
    void (*calc)();    //函數(shù)指針:指向PID計算函數(shù)    }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} //加與不加強制類型轉(zhuǎn)換都沒影響//條件編譯的判別條件#define PID_DEBUG 1                     
    
//函數(shù)聲明void pid_calc(PID *p);#endif//===================================================//End of file.//===================================================
復(fù)制代碼

 

PID.c文件

復(fù)制代碼
//===================================================//PID.c//===================================================#include "PID.h"//===================函數(shù)定義========================/****************************************************
*說    明:
*    (1)PID控制器默認(rèn)為PI調(diào)節(jié)器
*    (2)使用了條件編譯進(jìn)行功能切換:節(jié)省計算時間
*        在校正PID參數(shù)時,使用宏定義將PID_DEBUG設(shè)為1;
*        當(dāng)參數(shù)校正完成后,使用宏定義將PID_DEBUG設(shè)為0,同時,在初始化時
*    直接為p->a0、p->a1、p->a2賦值
****************************************************/void pid_calc(PID *p)
{    //使用條件編譯進(jìn)行功能切換
    #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;    
    //為下步計算做準(zhǔn)備
    p->Out_1=p->Out;
    p->Err_2=p->Err_1;
    p->Err_1=p->Err;
    
}//===================================================//End of file.//===================================================
復(fù)制代碼

 

amain.c主函數(shù)文件

復(fù)制代碼
//===================================================//amain.c//===================================================//將用戶定義的頭文件包含進(jìn)來#include "PID.h"//=============宏定義=====================#define T0   0.0002     //離散化采樣周期,單位s//============全局變量========================//定義PID控制器對應(yīng)的結(jié)構(gòu)體變量PID ASR=PID_DEFAULTS;     //速度PI調(diào)節(jié)器ASR//定義PID控制器的參數(shù)及輸出限幅值float SpeedKp=2,SpeedKi=1,SpeedLimit=10;   //速度PI調(diào)節(jié)器ASR//===============主程序=======================void main()
{    //初始化PID控制器
    ASR.Kp=SpeedKp;
    ASR.Ki=SpeedKi;
    ASR.T=T0;
    ASR.OutMax=SpeedLimit;
    ASR.OutMin=-SpeedLimit;
    
}//============中斷服務(wù)程序====================interrupt void T1UFINT_ISR(void)
{    //轉(zhuǎn)速調(diào)節(jié)ASR
    ASR.Ref=input1;         //速度給定
    ASR.Fdb=input2;         //速度反饋
    ASR.Err=ASR.Ref-ASR.Fdb;    //偏差
    ASR.calc(&ASR);         //函數(shù)調(diào)用:啟動PID計算
    output=ASR.Out;         //讀取PID控制器的輸出    }//===================================================//End of file.//===================================================
復(fù)制代碼

 


QQ在線咨詢
聯(lián)系電話
17658151177
售后服務(wù)
17658151177