关于PID各种整理例程基于stm32,位置式,增量式等
1、增量式
1.1 增量式 方式1
struct t_pid2
{
float SetSpeed2;
float ActualSpeed2;
float err2;
float err_next2;
float err_last2;
float Kp2,Ki2,Kd2;
}pid2;
void PID2_init()
{
pid2.SetSpeed2=0.0;
pid2.ActualSpeed2=0.0;
pid2.err2=0.0;//当前值
pid2.err_next2=0.0;//上一次值
pid2.err_last2=0.0;//上上次值
pid2.Kp2=0.2;
pid2.Ki2=0.015;
pid2.Kd2=0.2;
}
float PID2_realize(float speed)
{
float incrementSpeed=0;
pid2.SetSpeed2=speed;
pid2.err2=pid2.SetSpeed2-pid2.ActualSpeed2;
incrementSpeed=pid2.Kp2*(pid2.err2-pid2.err_next2)+pid2.Ki2*pid2.err2+pid2.Kd2*(pid2.err2-2*pid2.err_next2+pid2.err_last2);
pid2.ActualSpeed2+=incrementSpeed;
pid2.err_last2=pid2.err_next2;
pid2.err_next2=pid2.err2;
return pid2.ActualSpeed2;
}
1.1增量式 方式1 积分饱和
所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID 控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出 U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。
防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算 u(k)时,首先判断上一时刻的控制量 u(k-1)是否已经超出了极限范围: 如果u(k-1)>umax,则只累加负偏差; 如果 u(k-1)<umin,则只累加正偏差。从而避免控制量长时间停留在饱和区。
如上图,很明显有三个过冲:
过程①:因为这个过程存在较大幅度变化的误差,因此积分器累积了较大的值,从图中可以看到,积分器的面积比较大(阴影部分)ek为正;
过程②:此时积分已经饱和,产生了较大的过冲,并且在较长的一段时间内,一直处于过冲的状态,ek为负;
过程③:积分脱离饱和状态,产生了积极的调节作用,消除静差,系统输出达到设定值 ek为正;
处理过程:
/*定义结构体和公用体*/
typedef struct
{
float setpoint; //设定值
float proportiongain; //比例系数
float integralgain; //积分系数
float derivativegain; //微分系数
float lasterror; //前一拍偏差
float preerror; //前两拍偏差
float deadband; //死区
float result; //输出值
float maximum; //最大值
float minimum; //最小值
}PID;
void PIDRegulation(PID *vPID, float processValue)
{
float thisError;
float increment;
float pError,dError,iError;
thisError=vPID->setpoint-processValue; //本次误差
pError=thisError-vPID->lasterror; //本次偏差与上次偏差之差
iError=0;
dError=thisError-2*(vPID->lasterror)+vPID->preerror;
if(vPID->result>vPID->maximum)
{
if(thisError<=0)
{
iError=thisError;
}
}
elseif(vPID->result<vPID->minimum)
{
if(thisError>=0)
{
iError=thisError;
}
}
else
{
iError=thisError;
}
increment=vPID->proportiongain*pError+vPID->integralgain*iError+vPID->derivativegain*dError; //增量计算
vPID->preerror=vPID->lasterror; //存放偏差用于下次运算
vPID->lasterror=thisError;
vPID->result+=increment;
}
1.2 增量式 方式2
#define DEFAULT_SPEED -5 // mm/s 默认目标值
#define DEFAULT_PROPORTION 0.11f // Kp系数
#define DEFAULT_INTEGRAL 0.12f // Ki系数
#define DEFAULT_DERIVATIVE 0.03f // Kd系数
/**
* 函数功能: PID结构体初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 初始化PID参数
*/
void Init_PIDStruct()
{
vPID.SetPoint = DEFAULT_SPEED; // 目标值 单位:mm/s
vPID.Proportion = DEFAULT_PROPORTION; // Kp系数
vPID.Integral = DEFAULT_INTEGRAL; // Ki系数
vPID.Derivative = DEFAULT_DERIVATIVE; // Kd系数
vPID.LastError = 0;
vPID.PrevError = 0;
vPID.SumError = 0;
}
/**
* 函数功能:增量式PID速度环计算
* 输入参数:NextPoint 由编码器得到的速度值
* TargetVal 目标值
* 返 回 值:经过PID运算得到的增量值
* 说 明:增量式 PID 速度环控制设计,计算得到的结果仍然是速度值
*/
float IncPIDCalc(__IO float NextPoint,__IO float TargetVal) //临时变量,期望值
{
__IO float iError = 0,iIncpid = 0; //当前误差
iError = TargetVal - NextPoint; // 增量计算
// if((iError<0.5f)&&(iError>-0.5f))
// iError = 0; // |e| < 0.5,不做调整
iIncpid=(vPID.Proportion * iError) // E[k]项
-(vPID.Integral * vPID.LastError) // E[k-1]项
+(vPID.Derivative * vPID.PrevError); // E[k-2]项
vPID.PrevError=vPID.LastError; // 存储误差,用于下次计算
vPID.LastError = iError;
return(iIncpid); // 返回增量值
}
2、位置式
2.1 位置式 方式1
struct t_pid
{
float SetSpeed;
float ActualSpeed;
float err;
float err_last;
float Kp,Ki,Kd;
float voltage;
float integral;
}pid;
void PID_init()
{
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
}
float PID_realize(float speed)
{
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
pid.integral+=pid.err;
pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
2.2 位置式 方式2–积分分离
积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。
struct t_pid3
{
float SetSpeed3;
float ActualSpeed3;
float err3;
float err_last3;
float Kp3,Ki3,Kd3;
float voltage3;
float integral3;
}pid3;
void PID3_init()
{
pid3.SetSpeed3=0.0;
pid3.ActualSpeed3=0.0;
pid3.err3=0.0;
pid3.err_last3=0.0;
pid3.voltage3=0.0;
pid3.integral3=0.0;
pid3.Kp3=0.2;
pid3.Ki3=0.04;
pid3.Kd3=0.2;
}
float PID3_realize(float speed)
{
int index=0;
pid3.SetSpeed3=speed;
pid3.err3=pid3.SetSpeed3-pid3.ActualSpeed3;
pid3.integral3+=pid3.err3;
if(abs((int)pid3.err3)>200)//积分分离
{
index=0;
}
else
{
index=1;
pid3.integral3+=pid3.err3;
}
pid3.voltage3=pid3.Kp3*pid3.err3+index*pid3.Ki3*pid3.integral3+pid3.Kd3*(pid3.err3-pid3.err_last3);
pid3.err_last3=pid3.err3;
pid3.ActualSpeed3=pid3.voltage3*1.0;
return pid3.ActualSpeed3;
}
2.3 位置式 方式3–积分饱和
struct t_pid4
{
float SetSpeed4;
float ActualSpeed4;
float err4;
float err_last4;
float Kp4,Ki4,Kd4;
float voltage4;
float integral4;
float umax4;
float umin4;
}pid4;
void PID4_init()
{
pid4.SetSpeed4=0.0;
pid4.ActualSpeed4=0.0;
pid4.err4=0.0;
pid4.err_last4=0.0;
pid4.voltage4=0.0;
pid4.integral4=0.0;
pid4.Kp4=0.2;
pid4.Ki4=0.1;
pid4.Kd4=0.2;
pid4.umax4=400;
pid4.umin4=-200;
}
float PID4_realize(float speed)
{
int index;
pid4.SetSpeed4=speed;
pid4.err4=pid4.SetSpeed4-pid4.ActualSpeed4;
if(pid4.ActualSpeed4>pid4.umax4) //示抗积分饱和
{
if(abs(pid4.err4)>200) //积分分离
{
index=0;
}
else
{
index=1;
if(pid4.err4<0)
{
pid4.integral4+=pid4.err4;
}
}
}
else if(pid4.ActualSpeed4<pid4.umin4)
{
if(abs(pid4.err4)>200)//积分分离
{
index=0;
}
else
{
index=1;
if(pid4.err4>0)
{
pid4.integral4+=pid4.err4;
}
}
}
else
{
if(abs(pid4.err4)>200)
{
index=0;
}
else
{
index=1;
pid4.integral4+=pid4.err4;
}
}
pid4.voltage4=pid4.Kp4*pid4.err4+index*pid4.Ki4*pid4.integral4+pid4.Kd4*(pid4.err4-pid4.err_last4);
// pid4.voltage4=pid4.Kp4*pid4.err4+index*pid4.Ki4*pid4.integral4/2+pid4.Kd4*(pid4.err4-pid4.err_last4);
pid4.err_last4=pid4.err4;
pid4.ActualSpeed4=pid4.voltage4*1.0;
return pid4.ActualSpeed4;
}
2.4 位置式 方式4
struct t_pid5
{
float SetSpeed5;
float ActualSpeed5;
float err5;
float err_last5;
float Kp5,Ki5,Kd5;
float voltage5;
float integral5;
}pid5;
void PID5_init()
{
pid5.SetSpeed5=0.0;
pid5.ActualSpeed5=0.0;
pid5.err5=0.0;
pid5.err_last5=0.0;
pid5.voltage5=0.0;
pid5.integral5=0.0;
pid5.Kp5=0.4;
pid5.Ki5=0.2;
pid5.Kd5=0.2;
}
float PID5_realize(float speed)
{
float index;
pid5.SetSpeed5=speed;
pid5.err5=pid5.SetSpeed5-pid5.ActualSpeed5;
if(abs(pid5.err5)>200)
{
index=0.0;
}
else if(abs(pid5.err5)<180)
{
index=1.0;
pid5.integral5+=pid5.err5;
}
else
{
index=(200-abs(pid5.err5))/20;
pid5.integral5+=pid5.err5;
}
pid5.voltage5=pid5.Kp5*pid5.err5+index*pid5.Ki5*pid5.integral5+pid5.Kd5*(pid5.err5-pid5.err_last5);
pid5.err_last5=pid5.err5;
pid5.ActualSpeed5=pid5.voltage5*1.0;
return pid5.ActualSpeed5;
}
总结:从上面公式可以发现,对照PID控制算法(一),其实就分位置式与增量式至于实现过冲,其实是一样的,只不过合并同类项不同而已。