模糊自适应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}; //需要自己赋值