【算法】系列博客链接http://blog.csdn.net/u012819339/article/category/5849177
以前在学校时做过一个电赛题目,大概是做一个恒温箱,里面用到了PID算法。本篇博客arvik以全新的角度抽取以前电赛题目实战讲解一下PID算法。
先显摆显摆这个公式
PID算法典型公式:
好了,显摆结束,没什么卵用!自然,公式里一堆符号的含义也懒得解释。本篇博客根本不是让童鞋们从公式上理解PID算法。
进入正题!
有一个恒温箱,箱子内部与外部绝热。里面有电热丝和制冷片,我们可以控制电热丝和制冷片电流的通断来控制系统加热或制冷。
我要使恒温箱里的温度从a降到 设定值a0 。这时我給制冷片通电,让其制冷,当看到显示屏温度到a0时我立刻给制冷片 断电。
又过了一小会儿,却发现温度已经低于a0了,且基本稳定在a1处。
后来我发现,我的高精度的温度传感器将温度转化为数字信号的过程中时间耗时将近1s,我的显示器显示耗时将近0.3s左右,也就是说,当我看到显示屏温度为a0的时候,其实是1.3s以前的温度!!!(如下图A 斜线)(怎样让实际温度到达设定值时就停下来呢?)
上述过程其实对应了上图A中的斜线过程。现在将整个模型转化为对温度曲线走势的控制上来! 那么,有没有一种方法让曲线先开始快速靠近平行线a0,快到a0的时候再“小心翼翼”的接近平行线a0呢? 这就涉及到了PID算法了。
PID:比例、积分、微分。
我上面给制冷片通断电的过程就好像是进行比例调控,恒温箱的温度快速均匀的降下来。
积分的意义是为了抵消累计误差。温度曲线到达平行线a0时就会出现不停的加热、制冷过程,以使温度平衡。积分的几何3意义是求面积,图B中,水平线a0上下由曲线围成的面积积分后彼此抵消。这样曲线就更接近水平线a0了。
微分的意义是为了超前控制,微分 几何上是求曲线的斜率,知道了曲线的斜率就知道了曲线的下一步走势,这我们应该能理解。我们在曲线靠近水品线a0的过程中,提前控制曲线下降的速速(或说提前放缓曲线下降的速度),也就不至于让曲线下降“过头了”。
温度线经过PID算法调节后就变成理论上的图A中的曲线了,大家仔细看看,该曲线最先快速靠近水品线a0,靠近的过程中渐渐放缓速度,最终极其缓慢的无限接近水品线a0。当然,这是理想的情况下。
实际情况却如图B所示:
黑色线是一般情况下系统来回不停的在水平线a0上下加热、制冷的过程,不多说。
红色曲线是PID调节后的实际曲线。细心的同学发现,该曲线也在a0上下“抖动”,“抖动”幅度越来越小,最终形成稳定的较小“抖动”。(PID调节是一种动态平衡的过程)
arvik在此点评一下该曲线:
由于比例常数过大,导致曲线下降过快,曲线“冲过了头”,同时由于微分常数过小,导致超前调控曲线下降趋势的力度不够,所以曲线很早就“冲过了头”! 至于积分常数嘛,看起来合理,曲线最后“抖动”的幅度越来越小,最终曲线达到动态平衡状态。
到这里似乎到了PID参数整定的时候了,这就要根据大家的经验以及实际情况而定了。要知道,PID参数只要合理就行,参数值并不唯一! 只要最终调控结果令人“满意”就行了。
arvik不教大家整定参数了,据说网上有首儿歌,会背了也就知道怎么整定了,我就不再造轮子了。(arvik其实是不管什么儿歌口诀什么的,完全凭经验加上一些小计算就能快速的整定出令人满意的PID参数!(^_^))
啰嗦一句,博客开头给出的公式是位置式公式,程序中一般采用PID增量式算法,如下:
式中 Δek=ek−ek−1
好了,此处应该有代码,没代码说个JB啊!
先定义相关结构体:
struct PID
{
unsigned int SetPoint; // 设定目标 Desired Value
unsigned int Proportion; // 比例常数 Proportional Const
unsigned int Integral; // 积分常数 Integral Const
unsigned int Derivative; // 微分常数 Derivative Const
unsigned int LastError; // Error[-1]
unsigned int PrevError; // Error[-2]
unsigned int SumError; // Sums of Errors
};
算法函数:
unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )
{
unsigned int dError,Error;
Error = pp->SetPoint - NextPoint; // 偏差
pp->SumError += Error; // 积分
dError = pp->LastError - pp->PrevError; // 当前微分
pp->PrevError = pp->LastError;
pp->LastError = Error;
return (pp->Proportion * Error // 比例项
+ pp->Integral * pp->SumError // 积分项
+ pp->Derivative * dError); // 微分项
}
一套易懂的理论,一个简单的公式,就可以让鞋童们在自己的项目或设计中使用PID算法了。看不懂的鞋童还是别学了!
好了,本篇博客到此结束!【算法】系列博客链接http://blog.csdn.net/u012819339/article/category/5849177
更多精彩实战型博客文章,请 关注 arvik CSDN博客!