PID控制算法的C语言实现-完整版-笔记

PID控制算法的C语言实现一 PID算法原理

    

   在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法,如果能够熟练掌握PID算法的设计与实现过程,对于一般的研发人员来讲,应该是足够应对一般研发问题了,而难能可贵的是,在我所接触的控制算法当中,PID控制算法又是最简单,最能体现反馈思想的控制算法,可谓经典中的经典。经典的未必是复杂的,经典的东西常常是简单的,而且是最简单的,想想牛顿的力学三大定律吧,想想爱因斯坦的质能方程吧,何等的简单!简单的不是原始的,简单的也不是落后的,简单到了美的程度。先看看PID算法的一般形式: 

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307170454891-1867217803.png

 通过误差信号控制被控量

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307170514891-168209474.png

   2.输入量rin(t)为电机转速预定值;    

   3.输出量rout(t)为电机转速实际值;    

    4.执行器为直流电机; 

    5.传感器为光电码盘,假设码盘为10线; 

    6.直流电机采用PWM调速 转速用单位 转/min 表示;   

不难看出以下结论: 

   

  1.输入量rin(t)为电机转速预定值(转/min);    

  2 输出量rout(t)为电机转速实际值(转/min);    

    3.偏差量为预定值和实际值之差(转/min);

那么以下几个问题需要弄清楚: 

    1.通过PID环节之后的U(t)是什么值呢? 

    2.控制执行器(直流电机)转动转速应该为电压值(也就是PWM占空比)。    

   3.那么U(t)与PWM之间存在怎样的联系呢?

 http://blog.21ic.com/user1/3407/archives/2006/33541.html(见附录1)这篇文章上给出了一种方法,即,每个电压对应一个转速,电压和转速之间呈现线性关系。但是我考虑这种方法的前提是把直流电机的特性理解为线性了,而实际情况下,直流电机的特性绝对不是线性的,或者说在局部上是趋于线性的,这就是为什么说PID调速有个范围的问题。具体看一下

  

http://articles.e-works.net.cn/component/article90249.htm(见附录2)这篇文章就可以了解了。所以在正式进行调速设计之前,需要现有开环系统,测试电机和转速之间的特性曲线(或者查阅电机的资料说明),然后再进行闭环参数整定。这篇先写到这,下一篇说明连续系统的离散化问题。并根据离散化后的特点讲述位置型PID和增量型PID的用法和C语言实现过程。

PID控制算法的C语言实现二 PID算法的离散化

上一节中,我论述了PID算法的基本形式,并对其控制过程的实现有了一个简要的说明,通过上一节的总结,基本已经可以明白PID控制的过程。这一节中先继续上一节内容补充说明一下。 

   1.说明一下反馈控制的原理,通过上一节的框图不难看出,PID控制其实是对偏差的控制过程; 

  

 2.如果偏差为0,则比例环节不起作用,只有存在偏差时,比例环节才起作用。   

 3.积分环节主要是用来消除静差,所谓静差,就是系统稳定后输出值和设定值之间的差值,积分环节实际上就是偏差累计的过程,把累计的误差加到原有系统上以抵消系统造成的静差。 

  

 4.而微分信号则反应了偏差信号的变化规律,或者说是变化趋势,根据偏差信号的变化趋势来进行超前调节,从而增加了系统的快速性。 

   好了,关于PID的基本说明就补充到这里,下面将对PID连续系统离散化,从而方便在处理器上实现。下面把连续状态的公式再贴一下:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307170711031-10579245.png

假设采样间隔为T,则在第K T时刻: 

偏差err(K)=rin(K)-rout(K); 

积分环节用加和的形式表示,即err(K)+err(K+1)+„„; 

微分环节用斜率的形式表示,即[err(K)-err(K-1)]/T; 

从而形成如下PID离散表示形式:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307170723813-352289744.png

至于说Kp、Ki、Kd三个参数的具体表达式,我想可以轻松的推出了,这里节省时间,不再详细表示了。

其实到这里为止,PID的基本离散表示形式已经出来了。目前的这种表述形式属于位置型PID,另外一种表述方式为增量式PID,由U上述表达式可以轻易得到:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307170737031-2143641585.png

这就是离散化PID的增量式表示方式,由公式可以看出,增量式的表达结果和最近三次的偏差有关,这样就大大提高了系统的稳定性。需要注意的是最终的输出结果应该为 

       

    u(K)+增量调节值; 

PID的离散化过程基本思路就是这样,下面是将离散化的公式转换成为C语言,从而实现微控制器的控制作用。

PID控制算法的C语言实现三 位置型PID的C语言实现

  上一节中已经抽象出了位置性PID和增量型PID的数学表达式,这一节,重点讲解C语言代码的实现过程,算法的C语言实现过程具有一般性,通过PID算法的C语言实现,可以以此类推,设计其它算法的C语言实现。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

/*-------------------------------------------

       位置型PID C实现(控制电机转速)

--------------------------------------------*/

#include <iostream>

using namespace std;

void pid_value_init(void);

float PID_realize(float speed);

//(1)定义PID 结构体变量

struct pid

{

    float SetSpeed;    //设定速度

    float ActualSpeed;      //实际值

    float err;      //定义偏差值

    float err_last;       //上一个偏差值

    float Kp, Ki, Kd;     //p,i,d系数

    float voltage;    //电压值

    float integral;     //积分值,即积分部分的累计值

}pid;

int main()

{

    int count = 0;

    cout << "Please begin \n";

    pid_value_init();

    while (count < 1000)

    {

        float speed = PID_realize(200.0);

        cout <<"value is " <<speed<<endl ;

        count++;

    }

    system("pause");

}

//(3) 控制算法注意:这里用了最基本的算法实现形式,没有考虑死区问题,

//没有设定上下限,只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。

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) 初始化变量

void pid_value_init(void)

{

    cout << "pid_value_init begin \n" << endl;

    system("pause");

    pid.SetSpeed = 0;

    pid.ActualSpeed = 0;

    pid.err = 0;

    pid.err_last = 0;

    pid.integral = 0;

    pid.voltage = 0;

    pid.Kp = 0.1;

    pid.Ki = 0.1;

    pid.Kd = 0.1;

    cout << "pid_value_init end \n" << endl;

    system("pause");

}

PID控制算法的C语言实现四 增量型PID的C语言实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

/*-------------------------------------------

    2 位置型PID C实现(控制电机转速)

--------------------------------------------*/

//(1)定义PID 结构体变量

struct pid

{

    float SetSpeed;    //设定速度

    float ActualSpeed;      //实际值

    float err;      //k,定义偏差值

    float err_last;       //k-1,上一个偏差值

    float err_last_next;  //k-2

    float Kp, Ki, Kd;     //p,i,d系数

     

}pid;

int main()

{

    int count = 0;

    cout << "Please begin \n";

    pid_value_init();

    while (count < 100)

    {

        float speed = PID_realize(200.0);

        cout << "value is " << speed << endl;

        count++;

    }

    system("pause");

}

//(3) 控制算法注意:这里用了最基本的算法实现形式,没有考虑死区问题,

//没有设定上下限,只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。

float PID_realize(float speed)

{

    float incrementSpeed;

    pid.SetSpeed = speed;

    pid.err = pid.SetSpeed - pid.ActualSpeed;

     

    incrementSpeed = pid.Kp * (pid.err -pid.err_last ) + pid.Ki*pid.err + pid.Kd*(pid.err -2* pid.err_last + pid.err_last_next);

    pid.ActualSpeed += incrementSpeed;

    pid.err_last = pid.err;

    pid.err_last_next = pid.err_last;

    return pid.ActualSpeed;

}

//(2) 初始化变量

void pid_value_init(void)

{

    cout << "pid_value_init begin \n" << endl;

    system("pause");

    pid.SetSpeed = 0;

    pid.ActualSpeed = 0;

    pid.err = 0;

    pid.err_last = 0;

    pid.err_last_next = 0;

    pid.Kp = 0.1;

    pid.Ki = 0.15;

    pid.Kd = 0.1;

    cout << "pid_value_init end \n" << endl;

    system("pause");

}

  

PID控制算法的C语言实现五 积分分离的PID控制算法C语言实现

   在普通PID控制中,引入积分环节的目的主要是为了消除静差,提高控制精度。但在过程的启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成PID运算的积分积累,致使控制量超过执行机构可能允许的最大动作范围对应的极限控制量,引起系统较大的振荡,这在生产中是绝对不允许的。 

积分分离控制基本思路是,当被控量与设定值偏差较大时,取消积分作用,以免由于积分作用使系统稳定性降低,超调量增大;当被控量接近给定量时,引入积分控制,以便消除静差,提高控制精度

具体实现的步骤是: 

1、根据实际情况,人为设定阈值ε>0; 

2、当∣e (k)∣>ε时,采用PD控制,可避免产生过大的超调,又使系统有较快的响应; 

3、当∣e (k)∣≤ε时,采用PID控制,以保证系统的控制精度。 

<体现的思想就是分段控制> 

积分分离控制算法可表示为:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171003750-934556170.png

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171009547-136082615.png

为了克服这一问题,引入了积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。其具体实现代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

if (abs(pid.err) > 200)

    {

        index = 0;

    }

    else

    {

        index = 1;

        pid.integral += pid.err;

    }

     

     

    pid.voltage = pid.Kp * pid.err + pid.Ki*pid.integral + pid.Kd*(pid.err - pid.err_last);

  

系统到199所有的时间是原来时间的1/2(这里我没有算时间,不过运算次数确实是减少了),系统的快速性得到了提高。

PID控制算法的C语言实现六 抗积分饱和的PID控制算法C语言实现

    所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。 

     防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围:如果u(k-1)>umax,则只累加负偏差; 如果u(k-1)<umin,则只累加正偏差。从而避免控制量长时间停留在饱和区。直接贴出代码,不懂的看看前面几节的介绍。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

//(1)定义PID 结构体变量

struct pid

{

    float SetSpeed;    //设定速度

    float ActualSpeed;      //实际值

    float err;      //定义偏差值

    float err_last;       //上一个偏差值

    float Kp, Ki, Kd;     //p,i,d系数

    float voltage;    //电压值

    float integral;     //积分值,即积分部分的累计值

    //抗积分饱和

    float umax;

    float umin;

}pid;

int main()

{

    int count = 0;

    cout << "Please begin \n";

    pid_value_init();

    while (count < 100)

    {

        float speed = PID_realize(200.0);

        cout <<"value is " <<speed<<endl ;

        cout << "   " << endl;

        count++;

        system("pause");

    }

    system("pause");

}

//(3) 控制算法注意:这里用了最基本的算法实现形式,没有考虑死区问题,

//没有设定上下限,只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。

float PID_realize(float speed)

{

    int index;

    pid.SetSpeed = speed;

    pid.err = pid.SetSpeed - pid.ActualSpeed;

     

    //抗积分饱和过程

    if (pid.ActualSpeed > pid.umax)

    {

        if (abs(pid.err) > 200)

        {

            index = 0;

        }

        else

        {

            index = 1;

            if (pid.err < 0)    //向反向积分

            {

                pid.integral += pid.err;

            }

        }

    }

    else if (pid.ActualSpeed < pid.umin)

    {

        if (abs(pid.err) > 200)

        {

            index = 0;

        }

        else

        {

            index = 1;

            if (pid.err < 0)

            {

                pid.integral += pid.err;

            }

        }

    }

    else

    {

        if (abs(pid.err) > 200)

        {

            index = 0;

        }

        else

        {

            index = 1;

            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) 初始化变量

void pid_value_init(void)

{

    cout << "pid_value_init begin \n" << endl;

    system("pause");

    pid.SetSpeed = 0;

    pid.ActualSpeed = 0;

    pid.err = 0;

    pid.err_last = 0;

    pid.integral = 0;

    pid.voltage = 0;

    pid.Kp = 0.1;

    pid.Ki = 0.15;

    pid.Kd = 0.1;

    pid.umax = 400;

    pid.umin = -200;

    cout << "pid_value_init end \n" << endl;

    system("pause");

}

  

PID控制算法的C语言实现八 变积分的PID控制算法C语言实现

     变积分PID可以看成是积分分离的PID算法的更一般的形式。在普通的PID控制算法中,由于积分系数ki是常数,所以在整个控制过程中,积分增量是不变的。但是,系统对于积分项的要求是,系统偏差大时,积分作用应该减弱甚至是全无,而在偏差小时,则应该加强。积分系数取大了会产生超调,甚至积分饱和,取小了又不能短时间内消除静差。因此,根据系统的偏差大小改变积分速度是有必要的。

    变速积分的基本思想是,设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢;反之则越快,有利于提高系统品质。 

     设置系数f(e(k)),它是e(k)的函数。当∣e(k)∣增大时,f减小,反之增大。变速积分的PID积分项表达式为:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171245563-79321878.png

 系数f与偏差当前值∣e(k)∣的关系可以是线性的或是非线性的,例如,可设为:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171255453-186867975.png

这里给积分系数前加上一个比例值index:   

 当abs(err)<180时,index=1; 

      当180<abs(err)<200时,index=(200-abs(err))/20;    

    当abs(err)>200时,index=0; 

   最终的比例环节的比例系数值为ki*index; 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

//变积分过程

    if (abs(pid.err) > 200){

        index = 0;

    }

    else if (abs(pid.err) < 180){

        index = 1;

        pid.integral += pid.err;

    }

    else{

        index = (200 - abs(pid.err) / 20);

        pid.integral += pid.err;

    }

     

    pid.voltage = pid.Kp * pid.err + index*pid.Ki*pid.integral + pid.Kd*(pid.err - pid.err_last);

  

稳定速度貌似确实快。。。。。。。。。

运算值有负值,这是怎么回事????(一脸闷比)

PID控制算法的C语言实现九

(1)微分先行PID控制算法 

微分先行PID控制的特点是只对输出量yout(k)进行微分,而对给定值rin(k)不进行微分。这样,在改变给定值时,输出不会改变,而被控量的变化通常是比较缓和的。这种输出量先行微分控制适用于给定值rin(k)频繁升降的场合,可以避免给定值升降时引起系统振荡,从而明显地改善了系统的动态特性 

  

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171412906-493323820.png

(2)不完全微分PID控制算法

    在PID控制中,微分信号的引入可改善系统的动态特性,但也易引进高频

干扰,在误差扰动突变时尤其显出微分项的不足。若在控制算法中加入低通滤波器,则可使系统性能得到改善 

     不完全微分PID的结构如下图。左图将低通滤波器直接加在微分环节上,右图是将低通滤波器加在整个PID控制器之后 

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171435047-1429999730.png

(3)带死区的PID控制算法

   

在计算机控制系统中,某些系统为了避免控制作用过于频繁,消除由于频繁动作所引起的振荡,可采用带死区的PID控制算法,控制算式为:

 

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171454797-1333838430.png

式中,e(k)为位置跟踪偏差,e0是一个可调参数,其具体数值可根据实际控制对象由实验确定。若e0值太小,会使控制动作过于频繁,达不到稳定被控对象的目的;若e0太大,则系统将产生较大的滞后  控制算法流程:

https://images2015.cnblogs.com/blog/960571/201703/960571-20170307171510828-1036083494.png

注:<我们电子设计竞赛里,在简易倒立摆控制装置中就采用了带死区的PID控制算法,当时并不知道这个名称,这也就是在现场测试的时候为什么老师会问我们摆能够保持倒立静止不动,而不是靠左右抖动来控制平衡,就是因为我在里面设置了死区:好像是5度的角度>

PID控制算法的C语言实现十 专家PID与模糊PID的C语言实现

本节是PID控制算法的C语言实现系列的最后一节,前面8节中,已经分别从PID的实现到深入的过程进行了一个简要的讲解,从前面的讲解中不难看出,PID的控制思想非常简单,其主要问题点和难点在于比例、积分、微分环节上的参数整定过程,对于执行器控制模型确定或者控制模型简单的系统而言,参数的整定可以通过计算获得,对于一般精度要求不是很高的执行器系统,可以采用拼凑的方法进行实验型的整定。

然而,在实际的控制系统中,线性系统毕竟是少数,大部分的系统属于非线性系统,或者说是系统模型不确定的系统,如果控制精度要求较高的话,那么对于参数的整定过程是有难度的。专家PID和模糊PID就是为满足这方面的需求而设计的。专家算法和模糊算法都归属于智能算法的范畴,智能算法最大的优点就是在控制模型未知的情况下,可以对模型进行控制。这里需要注意的是,专家PID也好,模糊PID也罢,绝对不是专家系统或模糊算法与PID控制算法的简单加和,他是专家系统或者模糊算法在PID控制器参数整定上的应用。也就是说,智能算法是辅助PID进行参数整定的手段。

其实在前面几节的讲述中,已经用到了专家PID的一些特例行为了,从第五节到第八节都是专家系统一些特列化的算法,对某些条件进行了局部的判定,比如如果偏差太大的话,就去除积分项,这本身就是含有经验的专家系统。

专家系统、模糊算法,需要参数整定就一定要有整定的依据,也就是说什么情况下整定什么值是要有依据的,这个依据是一些逻辑的组合,只要找出其中的逻辑组合关系来,这些依据就再明显不过了。下面先说一下专家PID的C语言实现。正如前面所说,需要找到一些依据,还得从PID系数本身说起。 

1.比例系数Kp的作用是加快系统的响应速度,提高系统的调节精度。Kp越大,系统的响应速度越快,系统的调节精度越高,但是容易产生超调,甚至会使系统不稳定。Kp取值过小,则会降低调节精度,使响应速度缓慢,从而延长调节时间,是系统静态、动态特性变差;

   

   2.积分作用系数Ki的作用是消除系统的稳态误差。Ki越大,系统的静态误差消除的越快,但是Ki过大,在响应过程的初期会产生积分饱和的现象,从而引起响应过程的较大超调。若Ki过小,将使系统静态误差难以消除,影响系统的调节精度;

   

3.微分系数Kd的作用是改善系统的动态特性,其作用主要是在响应过程中抑制偏差向任何方向的变化,对偏差变化进行提前预报。但是kd过大,会使响应过程提前制动,从而延长调节时间,而且会降低系统的抗干扰性。

首先我们规定一个误差的极限值,假设为Mmax;规定一个误差的比较大的值,假设为Mmid;规定一个误差的较小值,假设为Mmin; 

当abs(e)>Mmax时,说明误差的绝对值已经很大了,不论误差变化趋势如何,都应该考虑控制器的输入应按最大(或最小)输出,以达到迅速调整误差的效果,使误差绝对值以最大的速度减小。此时,相当于实施开环控制。    

当e*ec>0时,说明误差在朝向误差绝对值增大的方向变化,此时,如果abs(e)>Mmid,说明误差也较大,可考虑由控制器实施较强的控制作用,以达到扭转误差绝对值向减小的方向变化,并迅速减小误差的绝对值。此时如果abs(e)<Mmid,说明尽管误差是向绝对值增大的方向变化,但是误差绝对值本身并不是很大,可以考虑控制器实施一般的控制作用,只需要扭转误差的变化趋势,使其向误差绝对值减小的方向变化即可。 

   

当e*err<0且e*err(k-1)>0或者e=0时,说明误差的绝对值向减小的方向变化,或者已经达到平衡状态,此时保持控制器输出不变即可。 

   当e*err<0且e*err(k-1)<0时,说明误差处于极限状态。如果此时误差的绝对值较大,大于Mmin,可以考虑实施较强控制作用。如果此时误差绝对值较小,可以考虑实施较弱控制作用。 

   当abs(e)<Mmin时,说明误差绝对值很小,此时加入积分,减小静态误差。 上面的逻辑判断过程,实际上就是对于控制系统的一个专家判断过程。

PID控制算法的c语言实现十二 模糊PID的参数整定

这几天一直在考虑如何能够把这一节的内容说清楚,对于PID而言应用并没有多大难度,按照基本的算法设计思路和成熟的参数整定方法,就算是没有经过特殊训练和培训的人,也能够在较短的时间内容学会使用PID算法。可问题是,如何能够透彻的理解PID算法,从而能够根据实际的情况设计出优秀的算法呢。

通过讲述公式和基本原理肯定是最能说明问题的,可是这样的话怕是犯了“专家”的错误了。对于门槛比较低的技术人员来讲,依然不能透彻理解。可是说的入耳了,能不能透彻说明也是一个问题,所以斟酌了几天,整理了一下思路才开始完成PID系列文章的最后一篇。

我所说的最后一篇不代表PID的功能和发展就止步与此,仅仅是说明,透过这一些列的文章,基本上已经可以涵盖PID设计的要点,至于更深入的研究,就交给有需要的读者去做。

上一节中大致讲述了一下模糊算法。实际上模糊算法的很多概念在上一节中并没有深入的解释。举的例子也只是为了说明模糊算法的基本含义,真正的模糊算法是不能这么设计的,当然也不会这么简单。模糊算法的核心是模糊规则,如果模糊规则制定的出色,那么模糊算法的控制效率就高。其实这是智能算法的一般特性,规则是系统判断和处理的前提。那么就说说PID的规则该怎么制定。

我们知道,模糊算法的本质是对PID的三个参数进行智能调节。那么首先要提出的问题是如何对PID的参数进行调节?这个问题其实是参数整定的问题,现实当中有很多整定方法。可是我们需要从根本上了解为什么这么整定,才能知道该如何建立数学模型进行分析。那么要回答如何整定参数的问题,就需要先明白PID参数的作用都是什么?对系统有什么影响?

我们从作用和副作用两个方面说明参数对系统的影响。

 1.比例环节Kp,作用是加快系统的响应速度,提高系统的调节精度,副作用是会导致超调; 

 2.积分环节Ki,作用是消除稳态误差,副作用是导致积分饱和现象;   

 3.微分环节Kd,作用是改善系统的动态性能,副作用是延长系统的调节时间。 

理解了上述问题,那么就可以“辩证施治,对症下药”了。比如说,如果系统响应速度慢,我们就加大Kp的取值,如果超调量过大我们就减小Kp的取值等等。可是问题这些语言的描述该如何用数学形式表达出来呢。我们所知道的,反馈系统的实质就是系统的输出量作为反馈量与系统的输入量进行作差,从而得到系统的误差e,那么这个误差e就能够反应目前系统所处的状态。误差e可以表明目前系统的输出状态到底偏离要求多少。而误差e的变化律ec,表示误差变化的速度。这样,我们可以根据这两个量的状态来分析三个参数此时应该如何取值,假如e为负方向比较大,ec也为负方向增大状态,此时比例环节要大一些,从而加快调节速度,而积分环节要小一些,甚至不加积分环节,从而防止负方向上出现饱和积分的现象。微分环节可以稍加一些,在不影响调节时间的情况下,起到改善系统动态性能的作用。

  • 7
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的模糊PID温度控制的C语言程序,其中使用了模糊控制器和PID控制器结合的方法: ```c #include <stdio.h> // 模糊控制器 double fuzzy_control(double error) { double output = 0.0; // 根据误差值进行模糊判断 if (error < -20.0) { output = -1.0; } else if (error < 0.0) { output = error / -20.0; } else if (error < 20.0) { output = error / 20.0; } else { output = 1.0; } return output; } // PID控制器 double pid_control(double error, double last_error, double integral) { double kp = 1.0; // 比例系数 double ki = 0.5; // 积分系数 double kd = 0.2; // 微分系数 double derivative = error - last_error; // 计算微分项 integral += error; // 计算积分项 double output = kp * error + ki * integral + kd * derivative; // 计算PID输出 return output; } int main() { double setpoint = 70.0; // 设定温度 double temperature = 50.0; // 当前温度 double last_error = 0.0; // 上一次误差 double integral = 0.0; // 积分项 int i; for (i = 0; i < 10; i++) { // 模拟10个时间步长 double error = setpoint - temperature; // 计算误差 double fuzzy_output = fuzzy_control(error); // 模糊控制器输出 double pid_output = pid_control(error, last_error, integral); // PID控制器输出 double output = fuzzy_output * pid_output; // 模糊控制器输出和PID控制器输出的加权平均值,作为最终输出 printf("时间步长:%d,温度:%f,输出:%f\n", i, temperature, output); temperature += output; // 更新温度 last_error = error; // 更新误差 } return 0; } ``` 在此程序中,模糊控制器根据误差值进行模糊判断,输出一个[-1,1]之间的模糊值;PID控制器根据误差、上一次误差以及积分项计算出一个PID输出。最终的输出是模糊控制器输出和PID控制器输出的加权平均值。程序中的温度变量可以替换成其他需要控制的变量,例如湿度、速度等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值