PID算法的实现

 很久没做电机控制类的项目,突然接触还是有点手生。以前做了许多PID控制类项目,但是都没有正经八百的记录下来。还是自己没有良好的记录习惯,最近公司接到一个小项目,算是有机会重拾PID算法,还是有点小兴奋。早些年参加很多小车竞赛,多用PID对电机的控速,也算是对PID算法有较多的感触。对于常规使用场合,PID算法是最实用的控制算法。结构简单、易于操作实现。具体算法原理不做赘述,就基于STM32的算法实现,笔者在此做一个稍微的总结。

PID控制系统原理框图如下:

           

基本控制器PID算法公式为:

式中:

u(t)-控制器输出控制量;

e(t)-系统偏差,当前设定值与反馈值之差;

KP-比例系数,放大增益(P);

Ti-积分时间常数,(对应参数是I);

Td-微分时间常数,(对应参数D);

数字PID控制算法通常分为位置式PID和增量式PID控制算法。

位置式PID算法

设U(K)为第K次采样时刻控制器的输出值,于是离散的PID算式如下:

e(k):用户设定值(目标值)- 控制对象的当前的状态值

比例P:

积分I:∑e(i)  误差的累加

微分(D):e(k) -e(k-1) 当前误差 - 上次误差

注意事项:由于积分项的不断累加,也就是说前控制输出与过去的所有状态均有关系,容易产生较大的累加误差,如果控制输出的某一个状态出错就会导致当前系统的大幅度变化,同时在积分项达到饱和时,误差在积分的作用下继续累加,一旦误差开始反向变化,系统需要较长时间退饱和,因此为了防止这种情况的发生,通常对积分和输出限幅。在一定的情况下停止积分的作用。

/*************************************
Uout=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
*************************************/
typedef struct
{
	float Set;             //目标值
	float fdb;             //反馈值
	
	float kp;              //比例系数
	float ki;              //积分系数
	float kd;              //微分系数
	
	float err_Integral_Lim;//积分限幅值
	
	float errNow;           //当前偏差e(k)
	float err_old_Last;     //上一次偏差e(k-1)
	float err_old_LLast;    //上上次偏差e(k-2)
	
	float dCtrOut;          //控制增量输出
	float ctrOut;           //控制输出
	
	float KPout;            //比例输出
	float KIout;            //积分输出
	float KDout;            //微分输出
	
	float KP_OUTMAX;        //比例限幅
	float KP_OUTMIN;
	
	float KI_OUTMAX;        //积分限幅
	float KI_OUTMIN;
	
	float KD_OUTMAX;        //微分限幅
	float KD_OUTMIN;

        float OutMax;           //输出最大值
        float OutMin; 	        //输出最小值
	
}PID;

PID P_PID;                      //定义一个位置式的PID结构体变量

void PID_PositionalMode()
{
	float P_errP = 0.0,P_errI = 0.0,P_errD = 0.0;
	
	P_PID.fdb  = 0.0;       //反馈值
	P_PID.Set  = 0.0;       //目标值
	
	P_PID.errNow = P_PID.Set - P_PID.fdb;         //当前偏差

	P_errP = P_PID.errNow;    
        P_errI += P_PID.errNow;                       
        P_errD = P_PID.errNow-P_PID.err_old_Last;	
	P_PID.err_old_Last = P_PID.errNow;

	if(P_PID.err_Integral_Lim!=0)                                   //积分限幅
	{
	    if(P_errI> P_PID.err_Integral_Lim)   P_errI = P_PID.err_Integral_Lim;
	    else if(P_errI<-P_PID.err_Integral_Lim)P_errI = -P_PID.err_Integral_Lim;         
	}
	 
	  P_PID.KPout = P_PID.kp * P_errP;                              //
	  if(P_PID.KPout>P_PID.KP_OUTMAX) P_PID.KPout = P_PID.KP_OUTMAX;
	  if(P_PID.KPout<-P_PID.KP_OUTMIN)P_PID.KPout = P_PID.KP_OUTMIN;
	
	  P_PID.KIout = P_PID.ki * P_errI;
	  if(P_PID.KIout>P_PID.KI_OUTMAX) P_PID.KIout =P_PID.KI_OUTMAX; //
	  if(P_PID.KIout<-P_PID.KI_OUTMIN)P_PID.KIout =-P_PID.KI_OUTMIN;
	  
	  P_PID.KDout = P_PID.kd * P_errD;
	  if(P_PID.KDout>P_PID.KD_OUTMAX)P_PID.KDout = P_PID.KD_OUTMAX; //
	  if(P_PID.KDout<-P_PID.KD_OUTMIN)P_PID.KDout = P_PID.KD_OUTMIN;
          //位置式PID计算
	  P_PID.ctrOut = P_PID.KPout +P_PID.KIout+P_PID.KDout;          
	  if(P_PID.ctrOut>P_PID.OutMax) P_PID.ctrOut = P_PID.OutMax;
	  if(P_PID.ctrOut<-P_PID.OutMin)P_PID.ctrOut = P_PID.OutMin;

}	

增量式PID算法

设U(K)为第K次采样时刻控制器的输出值,于是离散的PID算式如下:

比例P:e(k)-e(k-1) 当前误差-上次误差

积分I:e(i) 当前误差

微分D:e(k)-2e(k-1)-e(k-2) 当前误差-2*上次误差+上上次误差

增量式PID与位置式相比,最终求出的是控制输出的增量,而且只使用了前后三次的测量值的偏差。

注意事项:

       由于位置式 PID的控制输出与整个过去的状态都有关,增量式PID只与当前三次状态有关,没有积分的累加误差,因此相对来说,增量式的累计误差相对较小。但是增量式PID的缺点也是很明显的,积分截断效应大,有稳态误差,溢出的影响大,需要对输出进行限幅。

/********************************************************
//根据增量式离散PID公式 
//Uout+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
//e(k)代表本次偏差 
//e(k-1)代表上一次的偏差  
//e(k-2)代表上上次的偏差
********************************************************/

typedef struct
{
	float Set;             //目标值
	float fdb;             //反馈值
	
	float kp;              //比例系数
	float ki;              //积分系数
	float kd;              //微分系数
	
	float err_Integral_Lim;//积分限幅值
	
	float errNow;           //当前偏差e(k)
	float err_old_Last;     //上一次偏差e(k-1)
	float err_old_LLast;    //上上次偏差e(k-2)
	
	float dCtrOut;          //控制增量输出
	float ctrOut;           //控制输出
	
	float KPout;            //比例输出
	float KIout;            //积分输出
	float KDout;            //微分输出
	
	float KP_OUTMAX;        //比例限幅
	float KP_OUTMIN;
	
	float KI_OUTMAX;        //积分限幅
	float KI_OUTMIN;
	
	float KD_OUTMAX;        //微分限幅
	float KD_OUTMIN;

        float OutMax;           //输出最大值
        float OutMin; 	        //输出最小值
	
}PID;

PID P_PID;                      //定义一个增量式的PID结构体变量

void PID_IncrementalMode(PID* PID)
{
	float dErrP  = 0,dErrI = 0.0,dErrD = 0.0;
	  
	  P_PID.fdb  = 0.0;    //反馈值
	  P_PID.Set  = 0.0;    //目标值
	
	  P_PID.errNow = P_PID.Set - P_PID.fdb;                           //当前偏差
	  dErrP  = P_PID.errNow - P_PID.err_old_Last;  
	  dErrI  = P_PID.errNow;
	  dErrD  = P_PID.errNow-P_PID.err_old_Last+P_PID.err_old_LLast - P_PID.err_old_Last;
		
		P_PID.err_old_LLast = P_PID.err_old_Last;
		P_PID.err_old_Last = P_PID.errNow;  
		
	
	  if(P_PID.err_Integral_Lim!=0)
		{
			if(dErrI>P_PID.err_Integral_Lim)
				dErrI = P_PID.err_Integral_Lim;
			else if(dErrI < -P_PID.err_Integral_Lim)
				dErrI = -P_PID.err_Integral_Lim;
		}
		P_PID.dCtrOut = dErrP*P_PID.kp +dErrI*P_PID.ki +dErrD*P_PID.kd;   //输出增量
		P_PID.ctrOut += P_PID.dCtrOut;                                    //控制输出
      if(P_PID.ctrOut>P_PID.OutMax)		P_PID.ctrOut=P_PID.OutMax;	
}

猜你喜欢

转载自blog.csdn.net/enjoybocai/article/details/106353329