PID.uEkFlag[0]=1;//E(k)-E(k-1)为负数
}
/*===================================================================*/
Temp[2]=PID.liEkVal[1]*2;//2E(k-1)
if((PID.liEkVal[0]+PID.liEkVal[2])>Temp[2])//E(k-2)+E(k)>2E(k-1)否?
{
Temp[2]=(PID.liEkVal[0]+PID.liEkVal[2])-Temp[2];
PID.uEkFlag[2]=0;//E(k-2)+E(k)-2E(k-1)为正数
}
else//E(k-2)+E(k)<2E(k-1)
{
Temp[2]=Temp[2]-(PID.liEkVal[0]+PID.liEkVal[2]);
PID.uEkFlag[2]=1;//E(k-2)+E(k)-2E(k-1)为负数
}
/*===================================================================*/
Temp[0]=(uInt32)PID.uKP_Coe*Temp[0];//KP*[E(k)-E(k-1)]
Temp[1]=(uInt32)PID.uKI_Coe*PID.liEkVal[0];//KI*E(k)
Temp[2]=(uInt32)PID.uKD_Coe*Temp[2];//KD*[E(k-2)+E(k)-2E(k-1)]
/*以下部分代码是讲所有的正数项叠加,负数项叠加*/
/*=========计算KP*[E(k)-E(k-1)]的值=========*/
if(PID.uEkFlag[0]==0)
PostSum+=Temp[0];//正数和
else
NegSum+=Temp[0];//负数和
/*=========计算KI*E(k)的值=========*/
if(PID.uEkFlag[1]==0)
PostSum+=Temp[1];//正数和
else
;/*空操作。
就是因为PID.iSetVal>PID.iCurVal(即E(K)>0)才进入if的,
那么就没可能为负,所以打个转回去就是了*/
/*=========计算KD*[E(k-2)+E(k)-2E(k-1)]的值=========*/
if(PID.uEkFlag[2]==0)
PostSum+=Temp[2];//正数和
else
NegSum+=Temp[2];//负数和
/*=========计算U(k)=========*/
PostSum+=(uInt32)PID.iPriVal;
if(PostSum>NegSum)//是否控制量为正数
{
Temp[0]=PostSum-NegSum;
if(Temp[0]<100)//小于上限幅值则为计算值输出
PID.iPriVal=(uInt16)Temp[0];
elsePID.iPriVal=100;//否则为上限幅值输出
}
else//控制量输出为负数,则输出0(下限幅值输出)
PID.iPriVal=0;
}
}
elsePID.iPriVal=0;//同上,嘿嘿
}
/*********************************************************
/*函数名称:
PID_Output()
/*函数功能:
PID输出控制
/*入口参数:
无(隐形输入,U(k))
/*出口参数:
无(控制端)
*********************************************************/
voidPID_Output(void)
{
staticuInt16iTemp;
staticuChar8uCounter;
iTemp=PID.iPriVal;
if(iTemp==0)
ConOut=1;//不加热
elseConOut=0;//加热
if(g_bPIDRunFlag)//定时中断为100ms(0.1S),加热周期10S(100份*0.1S)
{
g_bPIDRunFlag=0;
if(iTemp)iTemp--;//只有iTemp>0,才有必要减“1”
uCounter++;
if(100==uCounter)
{
PID_Operation();//每过0.1*100S调用一次PID运算。
uCounter=0;
}
}
}
/*********************************************************
/*函数名称:
PID_Output()
/*函数功能:
PID输出控制
/*入口参数:
无(隐形输入,U(k))
/*出口参数:
无(控制端)
*********************************************************/
voidTimer0Init(void)
{
TMOD|=0x01;//设置定时器0工作在模式1下
TH0=0xDC;
TL0=0x00;//赋初始值
TR0=1;//开定时器0
EA=1;//开总中断
ET0=1;//开定时器中断
}
voidmain(void)
{
Timer0Init();
while
(1)
{
PID_Output();
}
}
voidTimer0_ISR(void)interrupt1
{
staticuInt16uiCounter=0;
TH0=0xDC;
TL0=0x00;
uiCounter++;
if(100==uiCounter)
{
g_bPIDRunFlag=1;
}
}
首先帮大家解决一下什么是PID调节,为什么就要这样的疑惑。
PID是比例,积分,微分的英文单词的首字母的简称。
下面举个例子说明一下PID,让大家有个感官的认识,。
一个人闭眼走路,假设他知道自己离目的地有100米远,那么他就可以以每秒一米一步这样的速度走向目的地,100米刚刚好是100步,这是一个非常理想化的现象。
假设他不知道目的地有多远,目的地可能是1000米也有可能是10000米,他用每秒每步3米得速度向前,很不巧的是这个目的地在80米处,他走了26步时刚刚好差2米,走27步有刚刚好又多出1米,这就是所谓的稳态误差,如果这个人知道目的地在大概15米处得地方,开始这个人以每秒一米一步的速度,走完一步然后目测一下离目的地还有多远,结果发现还剩下大概14米,显然一米一步太慢,因此这个人决定每秒大于一米一步走,得出一条式子,
y=Kp*e(t)
其中y为下一次要每步要走的距离,e(t)为目测距离,也就是偏差,换句话说就是自己走了的距离跟要走的距离也就是目的地的误差,Kp就是一个常数,假设我们把Kp设置为0.5,
y=Kp*e(t)可以得出y=7;也就是说那个人下一步要以每秒7米得速度走,重复上述的过程,,7+1共走了8米,然后目测一下距离15米处还有多远,还有7米得误差,所以下一步要走3.5米,然后在重复,发现最后会出现一个稳态的误差,也就是多走一步会超出目的地,少走一步又没到目的地。
当然这个上述的例子情况非常特殊,大家可能觉得最后那些误差可以忽略,但是实际应用中,肯定没有人走路的那么特殊,按照这种线性比例下去最后得到的误差会非常大,所以就引入了一个积分的概念,积分的数学几何定义是在区间[a,b]里连续的非负曲线与直线x=a,x=b围成的图形的面积。
从积分的定义可以得到一个函数
其中Ti为积分时间,e(t)就是误差了。
Y就是输出,它是个不定积分,事实上把它融入到上述人走路的例子它是个定积分,从0到t时刻的误差的对时间的积分,也就是说误差曲线e(t)与时间轴围成的面积,积分时间Ti是一个常量,也就是说是自己规定大小,很明显,由上式得y为e(t)与t所围成的图形的面积的除以Ti的值,Ti越大y越小,Ti越小y越大,大了系统会动荡,所以要慢慢调节系数。
下面是关于积分跟比例的专业阐述:
比例(P)控制
比例控制是一种最简单的控制方式。
其控制器的输出与输入误差信号成比例关系。
当仅有比例控制时系统输出存在稳态误差(Steady-stateerror)。
积分(I)控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。
对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(SystemwithSteady-stateError)。
为了消除稳态误差,在控制器中必须引入“积分项”。
积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。
这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。
因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。
微分调节就是偏差值的变化率。
例如,如果输入偏差值线性变化,则在调节器输出侧叠加一个恒定的调节量。
大部分控制系统不需要调节微分时间。
因为只有时间滞后的系统才需要附加这个参数。
如果画蛇添足加上这个参数反而会使系统的控制受到影响。
举个例子,人去调节窝炉的温度,慢慢调节旋钮,使得温度慢慢变大,要使得温度达到某个固定值,人可以慢慢调节,边看温度边调节,如果开始离这个这目标温度远就快速旋旋钮(比例效果),到最后要使得温度误差小就微调(积分效果),然后实际上温度是有一个惯性在那里,开始你以很快速度调节旋钮的时候温度不会突变,不会一下子就达到稳定值,它慢慢增加到最后,但是不是每个人都是这么有经验,当他看到温度值离目标温度还差这么远,又加快旋转旋钮,最终结果导致实际温度跟目标温度差别非常远,微调也跟本没法调整,最后导致系统的不稳定,但是如果这个人很有经验,他事先知道这个温度是有惯性的,开始它快速旋转旋钮看温度上升率非常高,也就是温度变化非常快,他就放慢旋转速度了,最后结果是准确的把温度调整到最佳(微分效果)。
人可以是这样子,但是计算机可不会这样调节,那么就要通过一个PID得到一个输出值来调节了。
下面是一段关于微分的专业阐述:
控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。
自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。
其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。
解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。
这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。
所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。
综上所述得到一个一条公式,这个就是模拟PID
下面是关于应用,增量式PID算法。
其实PID的算法可以做很深,但没必要,一般入门级的算法已经在很多场合够用了,这里之所以选用增量式PID算法(另外还有位置式PID等等),因为增量式PID算法运算量少,非常适合单片机的应用。
显然要想给单片机运算,就必须是数字量,而上述的PID是模拟PID,我们要将他数字化,离散化。
其中积分在上面说到的,他的几何意义就是求e(t)与时间轴t围成的图形的面积,将这个面积分成T等分 ,T=0到T=1跟e(t)围成的面积加上T=1到T=2跟e(t)围成的面积一直累加……直到T-1到T跟e(t)围成的面积刚好就是整个e(t)与t时间轴围成的面积,刚刚好是e(t)对t的积分,如果T无限大,那么就可以分割成无限个小图形那么这个图形的面积就可以用T[e
(1)+e
(2)+………+e(T-1)+e(T)]来代替积分效果,而这个T等分就是AD在整个时间轴t中采样的点,显然越快的AD在相同的时间t里面采样的点越多,换句话说就是T更接近无限大。
因此积分可以用累和代替。
下面为积分的专业的解释
定义
设函数f(x)在[a,b]上有界,在[a,b]中任意插入若干个分点
a=x0 把区间[a,b]分成n个小区间
[x0,x1],...[xn-1,xn]。
在每个小区间[xi-1,xi]上任取一点ξi(xi-1≤ξi≤xi),作函数值f(ξi)与小区间长度的乘积f(ξi)△xi,并作出和
如果不论对[a,b]怎样分法,也不论在小区间上的点ξi怎样取法,只要当区间的长度趋于零时,和S总趋于确定的极限I,
这时我们称这个极限I为函数f(x)在区间[a,b]上的定积分,记作
微分用差分代替,先说明一下微分的几何意义
我们可以想象把上图中的f(x)换成e(t),x轴换成t轴,把△x换成△t,当△t非常小的时候曲线MN等价于直线MN,△y就等于dy,所以
可以用Td*[e(t)-e(t-1)]/△t,同样△t就是采样时间~越小越好。
因此模拟PID离散化得到在k-1时刻的输出
因此得到一个增量
其中的T为采样时间
,如果计算机控制系统采用恒定的采样周期T,一旦确定A、B、C(系数的选取是PID的关键这里不做讨论)
增量式PID控制算法与位置式PID算法相比,计算量小得多,因此在实际中得到广泛的应用。
位置式PID控制算法也可以通过增量式控制算法推出递推计算公式:
就是目前在计算机控制中广泛应用的数字递推PID控制算法。
下面是程序
typedefstructPID
{
intSetPoint;//设定目标DesiredValue
longSumError;//误差累计
doubleProportion;//比例常数ProportionalConst
doubleIntegral;//积分常数IntegralConst
doubleDerivative;//微分常数DerivativeConst
intLastError;//Error[-1]
intPrevError;//Error[-2]
}PID;
staticPIDsPID;
staticPID*sptr=&sPID;
/*====================================================================
InitializePIDStructure PID参数初始化
===================================================================*/
voidIncPIDInit(void)
{
sptr->SumError=0;
sptr->LastError=0;//Error[-1]
sptr->PrevError=0;//Error[-2]
sptr->Proportion=0;//比例常数ProportionalConst
sptr->Integral=0;//积分常数IntegralConst
sptr->Derivative=0;//微分常数DerivativeConst
sptr->SetPoint=0;
}
/*====================================================================增量式PID计算部分
====================================================================*/
intIncPIDCalc(intNextPoint)
{
registerintiError,iIncpid;//当前误差
iError=sptr->SetPoint-NextPoint;//增量计算
iIncpid=sptr->Proportion*iError//E[k]项
-sptr->Integral*sptr->LastError//E[k-1]项
+sptr->Derivative*sptr->PrevError;//E[k-2]项
//存储误差,用于下次计算
sptr->PrevError=sptr->LastError;
sptr->LastError=iError;
//返回增量值
return(iIncpid);
}
Kp:
比例系数-----比例带(比例度)P:
输入偏差信号变化的相对值与输出信号变化的相对值之比的百分数表示 (比例系数的倒数)
T:
采样时间
Ti:
积分时间
Td:
微分时间
温度T:
P=20~60%,Ti=180~600s,Td=3-180s
压力P:
P=30~70%,Ti=24~180s,
液位L:
P=20~80%,Ti=60~300s,
流量L:
P=40~100%,Ti=6~60s。
(1)一般来说,在整定中,观察到曲线震荡很频繁,需把比例带增大以减少震荡;当曲线最大偏差大且趋于非周期过程时,需把比例带减少
(2)当曲线波动较大时,应增大积分时间;曲线偏离给定值后,长时间回不来,则需减小积分时间,以加快消除余差。
(3)如果曲线震荡的厉害,需把微分作用减到最小,或暂时不加微分;曲线最大偏差大而衰减慢,需把微分时间加长而加大作用
(4)比例带过小,积分时间过小或微分时间过大,都会产生周期性的激烈震荡。
积分时间过小,震荡周期较长;比例带过小,震荡周期较短;微分时间过大,震荡周期最短
(5)比例带过大或积分时间过长,都会使过渡过程变化缓慢。
比例带过大,曲线如不规则的波浪较大的偏离给定值。
积分时间过长,曲线会通过非周期的不正常途径,慢慢回复到给定值。
注意:
当积分时间过长或微分时间过大,超出允许的范围时,不管如果改变比例带,都是无法补救的
1.PID调试