模糊自適應PID算法及其運用

        模糊自適應PID算法主要是由模糊控制器和PID控制器結合而成,模糊控制器以誤差e和誤差變化率ec作爲輸入,利用模糊規則對PID控制器的參數Kp、Ki、和Kd進行自適應整定,使被控對象保持在良好的動、靜態穩定狀態。相比傳統的PID控制,模糊自適應PID更加的靈活穩定,特別是對於時變性和非線性較大的被控對象,其優點更加突出。


設計模糊自適應PID控制需要以下步驟:

1. 模糊控制規則的確定。(模糊控制規則主要可以通過專家經驗和採樣數據兩個方面獲得)

2.利用模糊規則表對e(誤差)和ec(誤差變化率)進行模糊化處理得出相應的隸屬度。

3.利用所得出的隸屬度及相應隸屬度的橫座標(eg:PB、NS)帶入公式求出 Kp、Ki、Kd。

公式如下:


其中UAi(x)、UBi(y)表示求出的隸屬度,Zi表示對應隸屬度的橫座標(eg:PB、NS)

4.由Kp = Kp + Kp得出整定後的參數Kp、Ki、Kd,並帶入PID控制器中運算。

PID公式如下:


以下爲模糊自適應PID算法:

/************************************************************************************
* @author: 鏡璍氺月
* @date : 2018/2/15
* @fuction name:FUZZY_PID_CONTROL
* @fuction description: 模糊自適應控制算法,爲了方便測試默認e、ec在[-3,3]區間,
* 如需改變e、ec範圍,需引入量化因子(Ke、Kec=N/emax)、縮放因子(Ku=umax/N)。以下代碼採
*用三角隸屬函數求隸屬度以及加權平均法解模糊,PID採用位置式PID算法,算法僅供參考,歡迎報錯。
*************************************************************************************/
#define IS_Kp 1
#define IS_Ki 2
#define IS_Kd 3

#define NL       -3
#define NM	 -2
#define NS	 -1
#define ZE	 0
#define PS	 1
#define PM	 2
#define PL	 3



static const float fuzzyRuleKp[7][7]={
	PL,	PL,	PM,	PM,	PS,	PS,	ZE,
	PL,	PL,	PM,	PM,	PS,	ZE,	ZE,
	PM,	PM,	PM,	PS,	ZE,	NS,	NM,
	PM,	PS,	PS,	ZE,	NS,	NM,	NM,
	PS,	PS,	ZE,	NS,	NS,	NM,	NM,
	ZE,	ZE,	NS,	NM,	NM,	NM,	NL,
	ZE,	NS,	NS,	NM,	NM,	NL,	NL
};

static const float fuzzyRuleKi[7][7]={
	NL,	NL,	NL,	NM,	NM,	ZE,	ZE,
	NL,	NL,	NM,	NM,	NS,	ZE,	ZE,
	NM,	NM,	NS,	NS,	ZE,	PS,	PS,
	NM,	NS,	NS,	ZE,	PS,	PS,	PM,
	NS,	NS,	ZE,	PS,	PS,	PM,	PM,
	ZE,	ZE,	PS,	PM,	PM,	PL,	PL,
	ZE,	ZE,	PS,	PM,	PL,	PL,	PL
};

static const float fuzzyRuleKd[7][7]={
	PS,	PS,	ZE,	ZE,	ZE,	PL,	PL,
	NS,	NS,	NS,	NS,	ZE,	NS,	PM,
	NL,	NL,	NM,	NS,	ZE,	PS,	PM,
	NL,	NM,	NM,	NS,	ZE,	PS,	PM,
	NL,	NM,	NS,	NS,	ZE,	PS,	PS,
	NM,	NS,	NS,	NS,	ZE,	PS,	PS,
	PS,	ZE,	ZE,	ZE,	ZE,	PL,	PL
};

typedef struct{
	float Kp;
	float Ki;
	float Kd;
}PID;






PID fuzzy(float e,float ec)
{

     float etemp,ectemp;
     float eLefttemp,ecLefttemp;
     float eRighttemp ,ecRighttemp;

     int eLeftIndex,ecLeftIndex;
     int eRightIndex,ecRightIndex;
     PID      fuzzy_PID;
     etemp = e > 3.0 ? 0.0 : (e < - 3.0 ? 0.0 : (e >= 0.0 ? (e >= 2.0 ? 2.5: (e >= 1.0 ? 1.5 : 0.5)) : (e >= -1.0 ? -0.5 : (e >= -2.0 ? -1.5 : (e >= -3.0 ? -2.5 : 0.0) ))));

     eLeftIndex = (int)e;
     eRightIndex = eLeftIndex;
     eLeftIndex = (int)((etemp-0.5) + 3);        //[-3,3] -> [0,6]
     eRightIndex = (int)((etemp+0.5) + 3);

     eLefttemp =etemp == 0.0 ? 0.0:((etemp+0.5)-e);
     eRighttemp=etemp == 0.0 ? 0.0:( e-(etemp-0.5));

     ectemp = ec > 3.0 ? 0.0 : (ec < - 3.0 ? 0.0 : (ec >= 0.0 ? (ec >= 2.0 ? 2.5: (ec >= 1.0 ? 1.5 : 0.5)) : (ec >= -1.0 ? -0.5 : (ec >= -2.0 ? -1.5 : (ec >= -3.0 ? -2.5 : 0.0) ))));

     ecLeftIndex = (int)((ectemp-0.5) + 3);        //[-3,3] -> [0,6]
     ecRightIndex = (int)((ectemp+0.5) + 3);

     ecLefttemp =ectemp == 0.0 ? 0.0:((ectemp+0.5)-ec);
     ecRighttemp=ectemp == 0.0 ? 0.0:( ec-(ectemp-0.5));

/*************************************反模糊*************************************/




	fuzzy_PID.Kp = (eLefttemp * ecLefttemp *  fuzzyRuleKp[ecLeftIndex][eLeftIndex]                    
					+ eLefttemp * ecRighttemp * fuzzyRuleKp[ecRightIndex][eLeftIndex]
					+ eRighttemp * ecLefttemp * fuzzyRuleKp[ecLeftIndex][eRightIndex]
					+ eRighttemp * ecRighttemp * fuzzyRuleKp[ecRightIndex][eRightIndex]);

	fuzzy_PID.Ki =   (eLefttemp * ecLefttemp * fuzzyRuleKi[ecLeftIndex][eLeftIndex]
					+ eLefttemp * ecRighttemp * fuzzyRuleKi[ecRightIndex][eLeftIndex]
					+ eRighttemp * ecLefttemp * fuzzyRuleKi[ecLeftIndex][eRightIndex]
					+ eRighttemp * ecRighttemp * fuzzyRuleKi[ecRightIndex][eRightIndex]);

	fuzzy_PID.Kd = (eLefttemp * ecLefttemp *    fuzzyRuleKd[ecLeftIndex][eLeftIndex]
					+ eLefttemp * ecRighttemp * fuzzyRuleKd[ecRightIndex][eLeftIndex]
					+ eRighttemp * ecLefttemp * fuzzyRuleKd[ecLeftIndex][eRightIndex]
					+ eRighttemp * ecRighttemp * fuzzyRuleKd[ecRightIndex][eRightIndex]);
return fuzzy_PID;

}


float speed_pid()
{
        float tar = 0,cur = 0;                //目標值 , 實際值
	static PID pid= {0, 0, 0};      //賦予初值kp,ki,kd
	static int sumE = 0;                   //累加偏差
	static int lastE = 0;

	PID OUT = {0, 0, 0};
	float e = -1,ec = -2.6;



	e = tar - cur;             //目標值 - 實際值
	ec = e - lastE;            //誤差變化率
	sumE += e;
	lastE = e;
	OUT = fuzzy(e, ec);      //模糊控制調整  kp,ki,kd

	return (pid.Kp+OUT.Kp)*e + (pid.Kd+OUT.Kd)*ec + (pid.Ki+OUT.Ki)*sumE;
}
可直接應用於對象,需要傳入傳感器的值於speed_pid()的cur,並更改tar值,賦值pid初始參數。
static PID pid= {0, 0, 0};    //需要自己賦值


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