PID控制器改進筆記之一:改進PID控制器之參數動態調整

前面我們發佈了一系列PID控制器相關的文章,包括經典PID控制器以及參數自適應的PID控制器。這一系列PID控制器雖說實現了主要功能,也在實際使用中取得了良好效果,但還有很多的細節部分可以改進以提高性能和靈活性。所以在這篇中我們來討論改進PID控制器以實現動態調整參數的目的。

1、提出問題

在我們一開始開發PID控制器時,我們主要是關注於其算法的實現而沒有過多的關心其使用過程。但在我們的使用過程中發現有些不夠靈活的地方。

在原有的PID控制器中,設定值是通過在外部給PID對象的參數賦值實現的,雖然說並不影響使用,但我們若想對PID控制器中的參數設定值進行某些處理就不是很方便了。而在原有的PID控制器中,輸出值在外部是不可見的,只能通過PID對象查看且不可更改。這些使得對這些參數的操作顯得不夠靈活。

而且在原有的PID控制器中3個調節參數也不能在外部隨時調整,這顯然不符合很多應用的需要,因爲PID參數的調整是很常見的工作。所以在這篇中我們來考慮實現這些參數的動態調整。

2、分析設計

爲了使得PID控制器使用起來更爲靈活,我們需要將PID對象作必要的改動。關於PID對象我們考慮將測量值、設定值、輸出值作爲對象的屬性。但我們不是直接將這幾個變量作爲對象屬性,因爲這樣達不到我們從外部靈活操作的目的,我們將幾個指向浮點變量的指針作爲對象的屬性,而初始化後這幾個指針將指向我們的測量值、設定值、輸出值變量。

同樣的三個PID參數我們想要在外部修改它,我們也將其在外部定義爲變量,而在PID對象中定義爲指向這三個變量的浮點數指針。在對對象進行初始化時,我們將變量地址賦值給這幾個指針。據此我們定義PID對象類型爲:

/*定義結構體和公用體*/
typedef struct CLASSIC
{
  float *pPV;                   //測量值指針
  float *pSV;                   //設定值指針
  float *pMV;                   //輸出值指針
  float *pKp;                   //比例係數指針
  float *pKi;                   //積分系數指針
  float *pKd;                   //微分系數指針
 
  float setpoint;               //設定值
  float lasterror;              //前一拍偏差
  float preerror;               //前兩拍偏差
  float deadband;               //死區
  float result;                 //PID控制器計算結果
  float output;                 //輸出值0-100%
  float maximum;                //輸出值上限
  float minimum;                //輸出值下限
  float errorabsmax;            //偏差絕對值最大值
  float errorabsmin;            //偏差絕對值最小值
  float alpha;                  //不完全微分系數
  float deltadiff;              //微分增量
  float integralValue;          //積分累計量
  float gama;                   //微分先行濾波係數
  float lastPv;                 //上一拍的過程測量值
  float lastDeltaPv;            //上一拍的過程測量值增量
}CLASSICPID;

3、軟件實現

我們計劃將PID參數和過程變量改成指向浮點型變量的指針,那麼代碼上需要做哪些修改呢?需要修改的主要是兩個函數:PID調節函數和PID對象初始化函數。

首先,我們來看一看PID對象的初始化函數。我們知道將這些變量修改爲指向浮點變量法的指針後,我們就必須在初始化時指定具體的變量地址,否則指向的將是不可預知的位置。所以我們修改初始化函數如下:

/* PID初始化操作,需在對vPID對象的值進行修改前完成 */
void PIDParaInitialization(CLASSICPID *vPID,    //PID控制器對象
                           float *pPV,          //測量值指針
                           float *pSV,          //設定值指針
                           float *pMV,          //輸出值指針
                           float *pKp,          //比例係數指針
                           float *pKi,          //積分系數指針
                           float *pKd,          //微分系數指針
                           float vMax,          //控制變量量程
                           float vMin,          //控制變量的零點
                             )
{
  if((vPID==NULL)||(pPV==NULL)||(pSV==NULL)||(pMV==NULL)||(pKp==NULL)||(pKi==NULL)||(pKd==NULL))
  {
    return;
  }
  vPID->pPV=pPV;
  vPID->pSV=pSV;
  vPID->pMV=pMV;
  vPID->pKp=pKp;
  vPID->pKi=pKi;
  vPID->pKd=pKd;
 
  vPID->maximum=vMax;                /*輸出值上限*/
  vPID->minimum=vMin;                /*輸出值下限*/
 
  vPID->setpoint=*pPV;               /*設定值*/
 
  vPID->lasterror=0.0;              /*前一拍偏差*/
  vPID->preerror=0.0;               /*前兩拍偏差*/
  vPID->result=vMin;                /*PID控制器結果*/
  vPID->output=0.0;                 /*輸出值,百分比*/

  vPID->errorabsmax=(vMax-vMin)*0.8;
  vPID->errorabsmin=(vMax-vMin)*0.2;
 
  vPID->deadband=(vMax-vMin)*0.0005;               /*死區*/
  vPID->alpha=0.2;                  /*不完全微分系數*/
  vPID->deltadiff=0.0;                /*微分增量*/
 
  vPID->integralValue=0.0;
 
  vPID->mode=mode;
}

其次,我們還需要修改PID調節函數。在原來的PID調節器中過程值是作爲函數的參數輸入的,而且PID參數是作爲變量存在於對象內部的,所以要針對這兩個方面做相應的修改:

/* 通用PID控制器,採用增量型算法,具有變積分,梯形積分和抗積分飽和功能,微分項採用不完全微分,一階濾波,alpha值越大濾波作用越強                    */
void PIDRegulator(CLASSICPID *vPID)
{
  float thisError;
  float result;
  float factor;
  float increment;
  float pError,dError,iError;

  vPID->setpoint=*vPID->pSV;
  thisError=vPID->setpoint-(*vPID->pPV); //得到偏差值
  result=vPID->result;
  if (fabs(thisError)>vPID->deadband)
  {
    pError=thisError-vPID->lasterror;
    iError=(thisError+vPID->lasterror)/2.0;
    dError=thisError-2*(vPID->lasterror)+vPID->preerror;
   
    //變積分系數獲取
     factor=VariableIntegralCoefficient(thisError,vPID->errorabsmax,vPID->errorabsmin);
   
    //計算微分項增量帶不完全微分
      vPID->deltadiff=(*vPID->pKd)*(1-vPID->alpha)*dError+vPID->alpha*vPID->deltadiff;
       
      increment=(*vPID->pKp)*pError+(*vPID->pKi)*factor*iError+vPID->deltadiff;   //增量計算
    }
    else
    {
      if((fabs(vPID->setpoint-vPID->minimum)<vPID->deadband)&&(fabs((*vPID->pPV)-vPID->minimum)<vPID->deadband))
      {
        result=vPID->minimum;
      }
      increment=0.0;
    }

      result=result+increment;
  
    /*對輸出限值,避免超調和積分飽和問題*/
    if(result>=vPID->maximum)
    {
      result=vPID->maximum;
    }
    if(result<=vPID->minimum)
    {
      result=vPID->minimum;
    } 
 
    vPID->preerror=vPID->lasterror;  //存放偏差用於下次運算
    vPID->lasterror=thisError;
    vPID->result=result;

    vPID->output=(vPID->result-vPID->minimum)/(vPID->maximum-vPID->minimum)*100.0;
 
      *vPID->pMV=vPID->output;
  }
}

4、總結

我們將PID參數和過程變量都改爲了對象所包含的指針,這樣當我們從上位機或者其他進程修改變量的值時,也同步修改了PID對象中的值。測試的結果比原來的方式操作更爲方便。

歡迎關注:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章