这几天稍微看了一点点的PID,今天有了小小头绪,于是,就想把自己的想法写出来,欢迎拍砖。
最开始的时候,是先了解什么是PID。PID实指“比例proportional”、“积分integral”、“微分derivative”,如果我们要求被控制的对象最终趋于一个稳定的结果,一般就可以使用PID算法。假设说,有一辆速度为1m/s的小车,我们要求他的速度改变为5m/s,要完成这样的一件事,我们必须要有,1.小车驱动装置(用程序控制它输出多大的电压,电压决定驱动的马力),2.被驱动器控制的部分(即小车),3.检测当前速度的装置(当前速度与目标速度的差称为误差)。本来,我们可以给小车一个驱动力让小车加速,直到检测到小车速度达到5m/s,撤去驱动力。然而,这样做会带来几个问题。1,当小车速度达到5m/s时,从装置检测到这个速度,通知控制器,让控制器改变输出的电压,这一个过程需要耗费一定时间,在这个时间里面,小车速度可能增加了不少。2,撤去驱动力后,外界条件如摩擦会让小车速度进一步改变。PID算法可以在一定误差内解决这些问题。
使用PID算法时,大致是这样的。每一个采样周期,通过速度检测装置获得当前速度,传入程序,通过程序计算得到电压控制小车得到新速度。下一个采样周期又把新速度传入,获得新电压,再传入速度,再获得电压,如此反复。
PID算法的关键,是如何根据当前得到的速度值,输出一个“恰当”的电压,以致小车最终能够趋于稳定。
PID算法采用比例,积分,微分三种方法进行控制。三种方法都有自己对应的一个常量(pconst,iconst,dconst)。这三个变量都需要在实验中多次尝试得出。
一般地,先把与算法相关的量放入一个结构体,方便调用。
srtuct PID
{
double pconst, //比例控制常量
iconst, //积分控制常量
dconst; //微分控制常量
double ErrorSum; //误差累计
double iMax,iMin; //积分上下限
double Vnew; //当前速度
};
比例:
Vnew = GetV(); //获得当前速度
error = aim - Vnew; //误差=目标速度 - 当前速度
pTerm = error*pconst; //比例项的值就是误差乘上比例常量
Motor(pTerm); //让电机根据得出的值工作
有时候,我们增大比例常数可以让小车更快的到达目标速度,但是过大的比例常数又容易产生过冲,即速度超过目标速度5m/s,然后再减速,再加速,在减速。这样,系统产生振荡,然而,振荡的结果不一定能趋于稳定。
积分:
Vnew = GetV(); //获得当前速度
ErrorSum += (aim - Vnew); //每一次的误差累计起来
if(ErrorSum > iMax)ErrorSum = iMax;
else if(ErrorSum < iMin)ErrorSum = iMin;
iTerm = ErrorSum *iconst; //误差和乘上积分常量
Motor(iTerm); //让电机根据得出的值工作
一般我们都让比例配合积分一起使用,即PI算法,类似上面的程序,得出pTerm和iTerm,然后Motor(pTerm+iTerm)。PI控制一般会比纯比例控制花费更少时间使小车到达目标值,但是我们必须避免积分饱和,通常的做法是像上面程序一样设定iMax和iMin。它们和积分常量的大小相关。
微分:
我们用比例控制或者PI控制,都不能保证任何系统一定能趋于稳定。比例控制根据现在来控制。积分控制根据过去来控制。而微分能够根据将来来控制,非常强大,能使被控设备更好地趋于稳定。
Vnew = GetV(); //获得当前速度
dTerm = (Vnew - Vlast)*dconst; //当前速度 - 上次速度,再乘微分常数
Vlast = Vnew ; //保留本次速度,下次当成上次速度使用
Motor(dTerm); //让电机根据得出的值工作
同样,微分控制也是与比例,积分一起使用,形成PID算法,Motor(pTerm+iTerm+dTerm)。
微分控制相当强大,然而也很容易出问题。具体调控和整定我想等到实际应用时再学习。 |