Chinaunix首页 | 论坛 | 博客
  • 博客访问: 22554
  • 博文数量: 4
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 55
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-07 16:05
文章分类

全部博文(4)

文章存档

2016年(4)

我的朋友

分类: 嵌入式

2016-08-04 14:52:09

在用过这么多次PID之后,我对PID唯一的感受就是神奇,它几乎可以应付任何需要进行自动调节的地方,它可以使被控制的对象反应更加灵敏。但是我经常看见有人对这个算法有很多不理解的地方,而网上的资料又杂乱无章,去抱这一本自控理论来啃也不太现实,所以呢我就希望用我自己的经验和对PID这个算法的理解,来帮助大家掌握这个神奇的工具,好让我们在今后的比赛中,或是在工作中,都能设计出一套性能更加优越的自动控制装置。


进入正题。


PID被设计出来的时候,就是为了解决被控制系统的复杂性的问题,被控制系统的响应函数一般都不是线性关系的,就算理论上是线性,实际运行中也会由于摩擦或是其他什么因素,影响系统的线性关系。


举个例子:(图片来自于互联网)


上图是一个旋转倒立摆的模型,这个系统需要做的是,根据角度传感器返回的角度值,来控制直流电机相应的输出,从而使摆杆直立起来,一个最简单的控制策略是,当摆杆往左偏的时候,让旋臂往左转,反之右转。乍一看这个思路好像没什么问题,因为程序可以运行很快,如果摆杆稍微离开我们需要设定的位置,他就可以被迅速调节回来。但是现实是摆杆很快就被甩飞了,囧。在现实中的被控制对象,刚刚的思路忽略的一个因素,那就是物体的惯性,当我们让物体很快回复的过程中,这时物体的动能很大,到达目标位置的时候,根本无法停下来,继续到达相反的,更加偏离目标的位置,这是一个恶心循环,左摆右摆,不断积累动能,最终系统调节不过来,就是刚刚说的被甩飞。要如何应对这种系统呢?答案就是今天的主角:PID登场!


什么是PID


很简单,就是这么一条公式。关于其中复杂的控制理论,我们也不用去深究它,知道各个参数的意义就可以了。

u(t):某时刻对应的输出,比如单片机的pwm占空比

e(t):某时刻的目标值和实际值的差,比如倒立摆和竖值位置相差的角度

Kp:比例系数

Ki:积分系数

Kd:微分系数


这个公式的意义就是,根据当前的偏差(et)),算出系统需要输出的值,比如占空比,这个值等于 一定比例当前的偏差(比例P+一定比例的偏差从0时刻的积分(积分I+一定比例的当前偏差的微分(微分D)。


以下图中白色是目标值,绿色是反馈值,红色是PID控制器的输出值:


比例P


上图,的p设为了2,其他参数设为了0

比例的意义:

按照我自己的理解,比例的可以想成是弹簧的效果,比如上面的倒立摆,想成是一个弹簧在往左边拉,一个在往右边拉,同时作用可以使其迅速回到中间位置。当然从公式上也能看出来他们的相似性,胡克定理:F=k×Δx。


积分I:


上图,i=0.01,其余全为0。

可以看到图中红色图像是绿色图像的积分。

积分的意义:

当误差不断累计,这时还只是用死板的比例就永远也无法使其调节过来,必须对这个误差进行累积,也就是积分,按照一定比例混合进比例P的作用中,增强系统的效果。

微分D:


D=100,其余全为0。

微分的意义:

我用了一个比较形象的模型来理解这个参数。微分的数学意义是变量的变化快慢,而对于e(t)/dt,就是偏差的变化快慢,PI加上D的效果,就是在抑制PI的控制效果(因为偏差的方向和需要调节的方向相反,公式里写的是加号,实际应该是减号,所以是抑制,千万不要理解成了增强,我当初就犯过这错)。所以整体效果就是,偏差缩小的越快,系统的阻力越大,可以把上述的倒立摆想象成当已经加入了P和I后,又把他们侵入了水中,当摆杆接近中心位置越来越快的时候,由于水的阻力会随着速度的增大而增大,最终结果就是摆杆迅速回到中心位置,但是不会造成过度调节,因此也避免了最开始来回震荡的问题。

我第一次体会到PID的神奇之处就是源于此,当时程序也写的不长,就是用了PID算法,慢慢的调节P、I、D三个参数,最后突然摆杆非常完美的立了起来,瞬间让我对发明这个算法的人无比崇拜。一个公式,简洁明了,却可以适应这么多的控制系统。大家也可以去了解下,我们熟知的两轮直立车、四轴飞行器或是其它什么东西,都能够用PID进行解决。

代码贴出来,有错误还请指正:

pid.h

点击(此处)折叠或打开

  1. /************************************************************************
  2. 简介:位置式PID控制程序
  3. 作者:logic
  4. 日期:2016年3月1日
  5. ************************************************************************/
  6. #ifndef PID_H
  7. #define PID_H

  8. typedef struct PID_Struct
  9. {
  10.     float P;
  11.     float I;
  12.     float D;
  13.     float IThreshold;
  14.     float rate_actual_output;

  15.     float aim;
  16.     float previous;
  17.     float current;
  18.     float error;
  19.     
  20.     float I_count;/*integral from n=0 to n=t*/

  21.     float result;
  22. }PID_Struct;

  23. /*
  24. *function:pid初始化
  25. *arg:rate_actual_output:开环时,实际反馈与输出的比值,需实测。
  26. *    此为自创,比如用于电机调速,因为积分速度过慢会导致系统不够灵敏,加上这个控制效果能使速度变化更明显!
  27. *    p,i,d:比例积分微分系数
  28. *    IThreshold:积分阈值
  29. */
  30. void PID_Init(PID_Struct *PID,float rate_actual_output,float P,float I,float D,float IThreshold);
  31. /*
  32. *function:设置目标值
  33. *arg:aim:目标值(单位以反馈值为准)
  34. *details:和PID_out异步运行
  35. */
  36. void PID_SetAim(PID_Struct *PID,float aim);//used by user(asynchronous with PID_Out)
  37. /*
  38. *function:计算pid输出
  39. *arg:current:此刻的反馈值
  40. *details:需周期性调用此函数,并且把输出传给执行装置
  41. */
  42. float PID_Out(PID_Struct *PID,float current);//used by system


  43. #endif


pid.c

点击(此处)折叠或打开

  1. #include "pid.h"

  2. void PID_Init(PID_Struct *PID,float rate_actual_output,float P,float I,float D,float IThreshold)
  3. {
  4.     PID->P=P;
  5.     PID->I=I;
  6.     PID->D=D;
  7.     PID->IThreshold=IThreshold;
  8.     PID->aim=0;
  9.     PID->previous=0;
  10.     PID->current=0;
  11.     PID->error=0;
  12.     PID->I_count=0;
  13.     PID->result=0;
  14.     PID->rate_actual_output=rate_actual_output;
  15. }
  16. void PID_SetAim(PID_Struct *PID,float aim)//used by user(asynchronous with Calc)
  17. {
  18.     if(PID->aim==aim)
  19.         return;
  20.     PID->aim=aim;
  21.     PID->I_count=0;//I think the integral should be reset when aim change.

  22. }
  23. float PID_Out(PID_Struct *PID,float current)//used by system
  24. {
  25.     /*declare for variables*/
  26.     float P_result;
  27.     float I_result;
  28.     float D_result;
  29.     /*calculate for error*/
  30.     PID->previous=PID->current;
  31.     PID->current=current;
  32.     PID->error=PID->current-PID->aim;
  33.     /*calculate for P,I,D's results separately*/
  34.     //for P
  35.     P_result=PID->P*PID->error;
  36.     //for I
  37.     PID->I_count+=PID->error;
  38.     I_result=PID->I*PID->I_count;
  39.     if(I_result>PID->IThreshold)
  40.     {
  41.         PID->I_count=PID->IThreshold/PID->I;
  42.         I_result=PID->IThreshold;
  43.     }
  44.     else if(I_result<-PID->IThreshold)
  45.     {
  46.         PID->I_count=-PID->IThreshold/PID->I;
  47.         I_result=-PID->IThreshold;
  48.     }
  49.     //for D
  50.     D_result=PID->D*(PID->current-PID->previous);
  51.     /*resulte*/
  52.     PID->result=P_result+I_result+D_result;
  53.     //return -PID->result+PID->aim/19.83;
  54.     if(PID->rate_actual_output==0)
  55.         return -PID->result;
  56.     else
  57.         return -PID->result+PID->aim/PID->rate_actual_output;
  58. }



阅读(4220) | 评论(0) | 转发(0) |
0

上一篇:iic总线协议与软件实现

下一篇:没有了

给主人留下些什么吧!~~