前面我們發佈了一系列PID控制器相關的文章,包括經典PID控制器以及參數自適應的PID控制器。這一系列PID控制器雖說實現了主要功能,也在實際使用中取得了良好效果,但還有很多的細節部分可以改進以提高性能和靈活性。所以在這篇中我們來討論改進PID控制器以實現手自動的方便切換。
1、提出問題
PID控制器的效果是衆所周知的,但有些時候我們希望強制輸出某個值以查看執行機構的響應,或者有些時候我們希望直接指定執行機構的行爲而不需要它隨時調整,在這些情況下我們該怎麼做呢?
這個時候我們需要說明兩個定義。當使用PID控制器自動調節時,我們稱之爲自動操作;而如上述情況下我們直接指定控制器的輸出時,或者說直接指定執行機構的行爲時,我們稱之爲手動。很顯然爲了實現前述的相應操作,我們需要爲PID控制器添加上手自動轉換功能。在這一篇中我們就來討論這個問題。
2、分析設計
現在我們需要考慮怎麼來實現手自動轉換功能。首先我們需要在PID對象中添加一個屬性,這個屬性用於標識PID控制器究竟是處於手動模式還是自動模式。爲了方便在外部修改PID對象的手自動模式,我們設計一個指向uint16_t的指針類型。所以我們設計PID對象如下:
/*定義PID對象類型*/
typedef struct CLASSIC
{
float *pPV; //測量值指針
float *pSV; //設定值指針
float *pMV; //輸出值指針
float *pKp; //比例係數指針
float *pKi; //積分系數指針
float *pKd; //微分系數指針
uint16_t *pMA; //手自動操作指針
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;
對於手自動操作,我們需要通過外部變量來賦值外,在初始化時我們將其默認初始化爲自動模式,畢竟我們使用PID控制器的目的不是爲了手動操作它。
3、軟件實現
我們爲PID對象添加了手自動操作屬性,那麼根據這個屬性我們需要對PID控制器進行哪些修改呢?我們考慮一下在自動狀態下和手動狀態下都需要做什麼工作。
在自動狀態下沒有什麼需要做的,就是讓PID控制器正常輸出就可以了。而在手動狀態下,我們要讓PID控制器的輸出爲我們人爲給定的輸出。僅僅如此,自然是不行的。我們考慮一下,PID控制器由自動轉爲手動時,可能會給系統有什麼影響。事實上,我們由自動轉爲手動時,只要不修改輸出,整個系統的狀態不會發生變化。但由手動切換到自動時,由於我們修改了輸出值,測量值也會跟着發生變化,在原有設定值的情況下,由手動轉爲自動時必然會出現階躍干擾,爲了避免這種情況我們在手動狀態下讓設定值跟隨到測量值以達到無擾動切換。據此,我們修改PID控制器代碼爲:
/* 通用PID控制器,採用增量型算法,具有變積分,梯形積分和抗積分飽和功能,微分項採用不完全微分,一階濾波,alpha值越大濾波作用越強 */
void PIDRegulator(CLASSICPID *vPID)
{
float thisError;
float result;
float factor;
float increment;
float pError,dError,iError;
if(*vPID->pMA<1) //手動模式
{
vPID->output=*vPID->pMV;
//設置無擾動切換
vPID->result=(vPID->maximum-vPID->minimum)*vPID->output/100.0+-vPID->minimum;
*vPID->pSV=*vPID->pPV;
vPID->setpoint=*vPID->pSV;
}
else //自動模式
{
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控制器置爲自動狀態時,我們通過修改設定值(SV)來實現對系統的影響。而當我們將PID控制器置爲手動狀態時,我們通過修改輸出值(MV)來實現對系統的影響。