[牛人杂谈] 如何写一个标准的PID算法

[复制链接]
4871|25
 楼主| 598330983 发表于 2017-5-24 14:33 | 显示全部楼层 |阅读模式
这是我收藏的一个典型的PID处理程序,包含了最常用的PID算法的基本架构,没有包含输入输出处理部分。适合新手了解PID结构,入门学习用。
注意:
使用不同的MCU的时候,需要进行相应的简化和改写。而且由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算。而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算。这样可大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余数补偿就好了。


 楼主| 598330983 发表于 2017-5-24 14:34 | 显示全部楼层
  1. #include<reg51.h>
  2. #include<intrins.h>
  3. #include<math.h>
  4. #include<string.h>
  5. struct PID {
  6.         unsigned int SetPoint; // 设定目标 Desired Value
  7.         unsigned int Proportion; // 比例常数 Proportional Const
  8.         unsigned int Integral; // 积分常数 Integral Const
  9.         unsigned int Derivative; // 微分常数 Derivative Const
  10.         unsigned int LastError; // Error[-1]
  11.         unsigned int PrevError; // Error[-2]
  12.         unsigned int SumError; // Sums of Errors
  13.         };
  14. struct PID spid; // PID Control Structure
  15. unsigned int rout; // PID Response (Output)
  16. unsigned int rin; // PID Feedback (Input)
  17. sbit data1=P1^0;
  18. sbit clk=P1^1;
  19. sbit plus=P2^0;
  20. sbit subs=P2^1;
  21. sbit stop=P2^2;
  22. sbit output=P3^4;
  23. sbit DQ=P3^3;
  24. unsigned char flag,flag_1=0;
  25. unsigned char high_time,low_time,count=0;//占空比调节参数
  26. unsigned char set_temper=35;
  27. unsigned char temper;
  28. unsigned char i;
  29. unsigned char j=0;
  30. unsigned int s;
  31.         /***********************************************************
  32.         延时子程序,延时时间以12M晶振为准,延时时间为30us×time
  33.         ***********************************************************/
  34. void delay(unsigned char time)
  35.         {
  36.             unsigned char m,n;
  37.             for(n=0;n<time;n++)
  38.             for(m=0;m<2;m++){}
  39.         }
  40.         /***********************************************************
  41.         写一位数据子程序
  42.         ***********************************************************/
  43. void write_bit(unsigned char bitval)
  44. {
  45.           EA=0;
  46.           DQ=0; /*拉低DQ以开始一个写时序*/
  47.         if(bitval==1)
  48.         {
  49.           _nop_();
  50.           DQ=1; /*如要写1,则将总线置高*/
  51.         }
  52.          delay(5); /*延时90us供DA18B20采样*/
  53.          DQ=1; /*释放DQ总线*/
  54.         _nop_();
  55.         _nop_();
  56.         EA=1;
  57. }
  58.         /***********************************************************
  59.         写一字节数据子程序
  60.         ***********************************************************/
  61. void write_byte(unsigned char val)
  62. {
  63.             unsigned char i;
  64.             unsigned char temp;
  65.             EA=0;
  66.             TR0=0;
  67.         for(i=0;i<8;i++) /*写一字节数据,一次写一位*/
  68.         {
  69.           temp=val>>i; /*移位操作,将本次要写的位移到最低位*/
  70.           temp=temp&1;
  71.           write_bit(temp); /*向总线写该位*/
  72.         }
  73.           delay(7); /*延时120us后*/
  74.         // TR0=1;
  75.           EA=1;
  76. }
  77.         /***********************************************************
  78.         读一位数据子程序
  79.         ***********************************************************/
  80. unsigned char read_bit()
  81. {
  82.         unsigned char i,value_bit;
  83.         EA=0;
  84.         DQ=0; /*拉低DQ,开始读时序*/
  85.         _nop_();
  86.         _nop_();
  87.         DQ=1; /*释放总线*/
  88.         for(i=0;i<2;i++){}
  89.         value_bit=DQ;
  90.         EA=1;
  91.         return(value_bit);
  92. }
  93.         /***********************************************************
  94.         读一字节数据子程序
  95.         ***********************************************************/
  96. unsigned char read_byte()
  97. {
  98.         unsigned char i,value=0;
  99.         EA=0;
  100.         for(i=0;i<8;i++)
  101.         {
  102.         if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/
  103.         value|=0x01<<i;
  104.         delay(4); /*延时80us以完成此次都时序,之后再读下一数据*/
  105.         }
  106.         EA=1;
  107.         return(value);
  108. }
  109.         /***********************************************************
  110.         复位子程序
  111.         ***********************************************************/
  112. unsigned char reset()
  113. {
  114.         unsigned char presence;
  115.         EA=0;
  116.         DQ=0; /*拉低DQ总线开始复位*/
  117.         delay(30); /*保持低电平480us*/
  118.         DQ=1; /*释放总线*/
  119.         delay(3);
  120.         presence=DQ; /*获取应答信号*/
  121.         delay(28); /*延时以完成整个时序*/
  122.         EA=1;
  123.         return(presence); /*返回应答信号,有芯片应答返回0,无芯片则返回1*/
  124. }
  125.         /***********************************************************
  126.         获取温度子程序
  127.         ***********************************************************/
  128. void get_temper()
  129. {
  130.         unsigned char i,j;
  131.         do
  132.         {
  133.            i=reset(); /*复位*/
  134.         }  while(i!=0); /*1为无反馈信号*/
  135.             i=0xcc; /*发送设备定位命令*/
  136.            write_byte(i);
  137.            i=0x44; /*发送开始转换命令*/
  138.            write_byte(i);
  139.            delay(180); /*延时*/
  140.         do
  141.         {
  142.            i=reset(); /*复位*/
  143.         }  while(i!=0);
  144.            i=0xcc; /*设备定位*/
  145.            write_byte(i);
  146.            i=0xbe; /*读出缓冲区内容*/
  147.            write_byte(i);
  148.            j=read_byte();   
  149.            i=read_byte();
  150.            i=(i<<4)&0x7f;
  151.            s=(unsigned int)(j&0x0f);            //得到小数部分
  152.            s=(s*100)/16;
  153.            j=j>>4;
  154.            temper=i|j; /*获取的温度放在temper中*/
  155.         }
  156.         /*====================================================================================================
  157.         Initialize PID Structure
  158.         =====================================================================================================*/
  159. void PIDInit (struct PID *pp)
  160. {
  161.         memset ( pp,0,sizeof(struct PID));           //全部初始化为0
  162. }
  163.         /*====================================================================================================
  164.         PID计算部分
  165.         =====================================================================================================*/
  166. unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )
  167. {
  168.         unsigned int dError,Error;
  169.         Error = pp->SetPoint - NextPoint;          // 偏差           
  170.         pp->SumError += Error;                     // 积分                                   
  171.         dError = pp->LastError - pp->PrevError;    // 当前微分  
  172.         pp->PrevError = pp->LastError;                           
  173.         pp->LastError = Error;                                       
  174.         return (pp->Proportion * Error             // 比例项           
  175.         + pp->Integral * pp->SumError              // 积分项
  176.         + pp->Derivative * dError);                // 微分项
  177. }
  178.         /***********************************************************
  179.         温度比较处理子程序
  180.         ***********************************************************/
  181. void compare_temper()
  182. {
  183.         unsigned char i;
  184.         if(set_temper>temper)      //是否设置的温度大于实际温度
  185.         {
  186.            if(set_temper-temper>1)         //设置的温度比实际的温度是否是大于1度
  187.           {
  188.            high_time=100;                     //如果是,则全速加热
  189.            low_time=0;
  190.           }
  191.        else                                         //如果是在1度范围内,则运行PID计算
  192.           {
  193.             for(i=0;i<10;i++)
  194.           {
  195.             get_temper();                          //获取温度
  196.             rin = s; // Read Input
  197.             rout = PIDCalc ( &spid,rin ); // Perform PID Interation
  198.           }
  199.             if (high_time<=100)
  200.               high_time=(unsigned char)(rout/800);
  201.             else
  202.           high_time=100;
  203.               low_time= (100-high_time);
  204.           }
  205.         }
  206.         else if(set_temper<=temper)
  207.         {
  208.            if(temper-set_temper>0)
  209.           {
  210.             high_time=0;
  211.             low_time=100;
  212.           }
  213.            else
  214.           {
  215.              for(i=0;i<10;i++)
  216.            {
  217.          get_temper();
  218.          rin = s; // Read Input
  219.              rout = PIDCalc ( &spid,rin ); // Perform PID Interation
  220.            }
  221.              if (high_time<100)
  222.               high_time=(unsigned char)(rout/10000);
  223.              else
  224.               high_time=0;
  225.               low_time= (100-high_time);
  226.           }
  227.         }
  228.         // else
  229.         // {}
  230. }
  231.         /*****************************************************
  232.         T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
  233.         ******************************************************/
  234. void serve_T0() interrupt 1 using 1
  235. {
  236.         if(++count<=(high_time))
  237.         output=1;
  238.         else if(count<=100)
  239.         {
  240.         output=0;
  241.         }
  242.         else
  243.         count=0;
  244.         TH0=0x2f;
  245.         TL0=0xe0;
  246. }
  247.         /*****************************************************
  248.         串行口中断服务程序,用于上位机通讯
  249.         ******************************************************/
  250. void serve_sio() interrupt 4 using 2
  251. {
  252.         /* EA=0;
  253.         RI=0;
  254.         i=SBUF;
  255.         if(i==2)
  256.         {
  257.         while(RI==0){}
  258.         RI=0;
  259.         set_temper=SBUF;
  260.         SBUF=0x02;
  261.         while(TI==0){}
  262.         TI=0;
  263.         }
  264.         else if(i==3)
  265.         {
  266.         TI=0;
  267.         SBUF=temper;
  268.         while(TI==0){}
  269.         TI=0;
  270.         }
  271.         EA=1; */
  272. }
  273. void disp_1(unsigned char disp_num1[6])
  274. {
  275.         unsigned char n,a,m;
  276.         for(n=0;n<6;n++)
  277.         {
  278.         // k=disp_num1[n];
  279.          for(a=0;a<8;a++)
  280.          {
  281.             clk=0;
  282.           m=(disp_num1[n]&1);
  283.           disp_num1[n]=disp_num1[n]>>1;
  284.           if(m==1)
  285.            data1=1;
  286.           else
  287.            data1=0;
  288.            _nop_();
  289.            clk=1;
  290.            _nop_();
  291.          }
  292.         }
  293. }
  294.         /*****************************************************
  295.         显示子程序
  296.         功能:将占空比温度转化为单个字符,显示占空比和测得到的温度
  297.         ******************************************************/
  298. void display()
  299. {
  300.         unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};
  301.         unsigned char disp_num[6];
  302.         unsigned int k,k1;
  303.         k=high_time;
  304.         k=k%1000;
  305.         k1=k/100;
  306.         if(k1==0)
  307.         disp_num[0]=0;
  308.         else
  309.         disp_num[0]=0x60;
  310.         k=k%100;
  311.         disp_num[1]=number[k/10];
  312.         disp_num[2]=number[k%10];
  313.         k=temper;
  314.         k=k%100;
  315.         disp_num[3]=number[k/10];
  316.         disp_num[4]=number[k%10]+1;
  317.         disp_num[5]=number[s/10];
  318.         disp_1(disp_num);
  319. }
  320.         /***********************************************************
  321.         主程序
  322.         ***********************************************************/
  323. void main()
  324. {
  325.         unsigned char z;
  326.         unsigned char a,b,flag_2=1,count1=0;
  327.         unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2};
  328.         TMOD=0x21;
  329.         TH0=0x2f;
  330.         TL0=0x40;
  331.         SCON=0x50;
  332.         PCON=0x00;
  333.         TH1=0xfd;
  334.         TL1=0xfd;
  335.         PS=1;
  336.         EA=1;
  337.         EX1=0;
  338.         ET0=1;
  339.         ES=1;
  340.         TR0=1;
  341.         TR1=1;
  342.         high_time=50;
  343.         low_time=50;
  344.         PIDInit ( &spid );    // Initialize Structure
  345.         spid.Proportion = 10; // Set PID Coefficients  比例常数 Proportional Const
  346.         spid.Integral = 8;    //积分常数 Integral Const
  347.         spid.Derivative =6;   //微分常数 Derivative Const
  348.         spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value
  349.         while(1)
  350. {
  351.         if(plus==0)
  352. {
  353.         EA=0;
  354.         for(a=0;a<5;a++)
  355.         for(b=0;b<102;b++){}
  356.         if(plus==0)
  357.   {
  358.         set_temper++;
  359.         flag=0;
  360.   }
  361. }
  362.         else if(subs==0)
  363.   {
  364.         for(a=0;a<5;a++)
  365.         for(b=0;a<102;b++){}
  366.         if(subs==0)
  367.         {
  368.          set_temper--;
  369.          flag=0;
  370.         }
  371.   }
  372.         else if(stop==0)
  373.         {
  374.             for(a=0;a<5;a++)
  375.             for(b=0;b<102;b++){}
  376.             if(stop==0)
  377.         {
  378.            flag=0;
  379.            break;
  380.         }
  381.            EA=1;
  382.         }
  383.        get_temper();
  384.            b=temper;
  385.         if(flag_2==1)
  386.           a=b;
  387.         if((abs(a-b))>5)
  388.           temper=a;
  389.         else
  390.           temper=b;
  391.           a=temper;
  392.           flag_2=0;
  393.         if(++count1>30)
  394.         {
  395.           display();
  396.           count1=0;
  397.         }
  398.           compare_temper();
  399.         }
  400.            TR0=0;
  401.            z=1;
  402.         while(1)
  403.         {
  404.             EA=0;
  405.         if(stop==0)
  406.         {
  407.             for(a=0;a<5;a++)
  408.             for(b=0;b<102;b++){}
  409.             if(stop==0)
  410.             disp_1(phil);
  411.         // break;
  412.         }
  413.         EA=1;
  414. }
  415. }
 楼主| 598330983 发表于 2017-5-24 14:34 | 显示全部楼层
以上转来的,不知道好不好用,哈哈。大家可以过过目。
 楼主| 598330983 发表于 2017-5-24 14:35 | 显示全部楼层
一会儿再找个来分享。
 楼主| 598330983 发表于 2017-5-24 14:36 | 显示全部楼层
经典控制算法PID程序
PID代码

  1. //定义变量
  2. float Kp; //PI调节的比例常数
  3. float Ti; //PI调节的积分常数
  4. float T; //采样周期
  5. float Ki;
  6. float ek; //偏差e[k]
  7. float ek1; //偏差e[k-1]
  8. float ek2; //偏差e[k-2]
  9. float uk; //u[k]
  10. signed int uk1; //对u[k]四舍五入取整
  11. signed int adjust; //调节器输出调整量

  12. //变量初始化
  13. Kp=4;
  14. Ti=0。005;
  15. T=0.001;
  16. // Ki=KpT/Ti=0.8,微分系数Kd=KpTd/T=0.8,Td=0.0002,根据实验调得的结果确定这些参数
  17. ek=0;
  18. ek1=0;
  19. ek2=0;
  20. uk=0;
  21. uk1=0;
  22. adjust=0;

  23. int piadjust(float ek) //PI调节算法
  24. {
  25. if( gabs(ek)<0.1 )
  26. {
  27. adjust=0;
  28. }
  29. else
  30. {
  31. uk=Kp*(ek-ek1)+Ki*ek; //计算控制增量
  32. ek1=ek;

  33. uk1=(signed int)uk;
  34. if(uk>0)
  35. {
  36. if(uk-uk1>=0.5)
  37. {
  38. uk1=uk1+1;
  39. }
  40. }
  41. if(uk<0)
  42. {
  43. if(uk1-uk>=0.5)
  44. {
  45. uk1=uk1-1;
  46. }
  47. }
  48. adjust=uk1;
  49. }


  50. return adjust;
  51. }

  52. 下面是在AD中断程序中调用的代码。

  53. 。。。。。。。。。。。
  54. else //退出软启动后,PID调节,20ms调节一次
  55. {
  56. EvaRegs.CMPR3=EvaRegs.CMPR3+piadjust(ek);//误差较小PID调节稳住
  57. if(EvaRegs.CMPR3>=890)
  58. {
  59. EvaRegs.CMPR3=890; //限制PWM占空比
  60. }
  61. }
  62. 。。。。。。。。。。。。。。。。
 楼主| 598330983 发表于 2017-5-24 14:37 | 显示全部楼层
PID调节经验总结

PID控制器参数选择的方法很多,例如试凑法、临界比例度法、扩充临界比例度法等。但是,对于PID控制而言,参数的选择始终是一件非常烦杂的工作,需要经过不断的调整才能得到较为满意的控制效果。依据经验,一般PID参数确定的步骤如下[42]:
(1) 确定比例系数Kp
确定比例系数Kp时,首先去掉PID的积分项和微分项,可以令Ti=0、Td=0,使之成为
纯比例调节。输入设定为系统允许输出最大值的60%~70%,比例系数Kp由0开始逐渐增大,直至系统出现振荡;再反过来,从此时的比例系数Kp逐渐减小,直至系统振荡消失。记录此时的比例系数Kp,设定PID的比例系数Kp为当前值的60%~70%。
(2) 确定积分时间常数Ti
比例系数Kp确定之后,设定一个较大的积分时间常数Ti,然后逐渐减小Ti,直至系统出现振荡,然后再反过来,逐渐增大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。
(3) 确定微分时间常数Td
微分时间常数Td一般不用设定,为0即可,此时PID调节转换为PI调节。如果需要设定,则与确定Kp的方法相同,取不振荡时其值的30%。
(4) 系统空载、带载联调
对PID参数进行微调,直到满足性能要求。
 楼主| 598330983 发表于 2017-5-24 14:39 | 显示全部楼层
上面的实际是PI调节的代码,现附上PID的。
1. //声明变量
2.
3. //定义变量
4. float Kp; //PID调节的比例常数
5. float Ti; //PID调节的积分常数
6. float T; //采样周期
7. float Td; //PID调节的微分时间常数
8. float a0;
9. float a1;
10. float a2;
11.
12. float ek; //偏差e[k]
13. float ek1; //偏差e[k-1]
14. float ek2; //偏差e[k-2]
15. float uk; //u[k]
16. int uk1; //对uk四舍五入求整
17. int adjust; //最终输出的调整量
18.
19. //变量初始化,根据实际情况初始化
20. Kp=;
21. Ti=;
22. T=;
23. Td=;
24.
25. a0=Kp*(1+T/Ti+Td/T);
26. a1=-Kp*(1+2*Td/T);
27. a2=Kp*Td/T;
28. // Ki=KpT/Ti=0.8,微分系数Kd=KpTd/T=0.8,Td=0.0002,根据实验调得的结果确定这些参数
29. ek=0;
30. ek1=0;
31. ek2=0;
32. uk=0;
33. uk1=0;
34. adjust=0;
35.
36.
37. int pid(float ek)
38. {
39. if(gabs(ek)<ee) //ee 为误差的阀值,小于这个数值的时候,不做PID调整,避免误差较小时频繁调节引起震荡。ee的值可自己设
40. {
41. adjust=0;
42. }
43. else
44. {
45. uk=a0*ek+a1*ek1+a2*ek2;
46. ek2=ek1;
47. ek1=ek;
48. uk1=(int)uk;
49.
50. if(uk>0)
51. {
52. if(uk-uk1>=0.5)
53. {
54. uk1=uk1+1;
55. }
56. }
57. if(uk<0)
58. {
59. if(uk1-uk>=0.5)
60. {
61. uk1=uk1-1;
62. }
63. }
64.
65. adjust=uk1;
66. }
67. return adjust;
68.
69. }
70.
71. float gabs(float ek)
72. {
73. if(ek<0)
74. {
75. ek=0-ek;
76. }
77. return ek;
78. }
 楼主| 598330983 发表于 2017-5-24 14:40 | 显示全部楼层
这是一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID
参数必须由具体对象通过实验确定。由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算,
而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算,可
大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余
数补偿。这个程序只是一般常用pid算法的基本架构,没有包含输入输出处理部分。
#include <string.h>
#include <stdio.h>
    PID Function
   
    The PID (比例、积分、微分) function is used in mainly
    control applications. PIDCalc performs one iteration of the PID
    algorithm.
    While the PID function works, main is just a dummy program showing
    a typical usage.

typedef struct PID {
        double  SetPoint;           //  设定目标 Desired Value
        double  Proportion;         //  比例常数 Proportional Const
        double  Integral;           //  积分常数 Integral Const
        double  Derivative;         //  微分常数 Derivative Const
        double  LastError;          //  Error[-1](上一次误差)
        double  PrevError;          //  Error[-2](上上次误差)
        double  SumError;           //  Sums of Errors
} PID;
PID计算部分
double PIDCalc( PID *pp, double NextPoint )
{
    double  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             // 微分项
        );
}
Initialize PID Structure
void PIDInit (PID *pp)
{
    memset ( pp,0,sizeof(PID));
}
  Main Program
double sensor (void)                    //  Dummy Sensor Function
{
    return 100.0;
}
void actuator(double rDelta)            //  Dummy Actuator Function
{}
void main(void)
{
    PID         sPID;                   //  PID Control Structure
    double      rOut;                   //  PID Response (Output)
    double      rIn;                    //  PID Feedback (Input)
    PIDInit ( &sPID );                  //  Initialize Structure
    sPID.Proportion = 0.5;             //  Set PID Coefficients(KP,KI,KD系数设定)
    sPID.Integral   = 0.5;
    sPID.Derivative = 0.0;
    sPID.SetPoint   = 100.0;            //  Set PID Setpoint
    for (;;) {                          //  Mock Up of PID Processing(PID处理示例)
        rIn = sensor ();                //  Read Input
        rOut = PIDCalc ( &sPID,rIn );   //  Perform PID Integration
        actuator ( rOut );              //  Effect Needed Changes
    }
}

 楼主| 598330983 发表于 2017-5-24 14:41 | 显示全部楼层
PIDIncCalculator calculate pid coff
    增量式PID 控制


void PIDIncCalculator(void)
{
当前误差
stPid.mIError = 0; stPid.mIncPid= 0; stPid.mCoff = 0;

stPid.mIncPid =   stPid.mSetPoint - stPid.mNextPoint;  增量计算值
stPid.mCoff =    stPid.mProportion * stPid.mIError \   E[K]
           - stPid.mIntegral * stPid.mLastError \   E[K-1]
           + stPid.mDerivate * stPid.mPrevError;   E[K-2]
存储误差便于下次计算
stPid.mPrevError = stPid.mLastError;
stPid.mLastError = stPid.mIError;

return;
}

PIDLocCalculator calculate pid coff
    位置式PID 控制

void PIDLocCalculator(void)
{
当前误差
stPid.mIError = 0; stPid.mIncPid= 0; stPid.mCoff = 0;
stPid.mIError =   stPid.mSetPoint - stPid.mNextPoint;   偏差
stPid.mSumError += stPid.mIError;       积分
stPid.mDError += stPid.mIError - stPid.mLastError;   微分
stPid.mLastError = stPid.mIError;

stPid.mCoff =    stPid.mProportion * stPid.mIError \   比例项
           + stPid.mIntegral * stPid.mSumError \       积分项
           + stPid.mDerivate * stPid.mDError;          微分项
return;
}
 楼主| 598330983 发表于 2017-5-24 14:42 | 显示全部楼层
当执行机构需要的不是控制量的绝对值,而是控制量的增量(例如去驱动步进电动机)时,需要用PID的“增量算法”。

51单片机增量式PID算法程序

     增量式PID控制算法可以通过(2-4)式推导出。由(2-4)可以得到控制器的第k-1个采样时刻的输出值为:
51单片机增量式PID算法程序(2-5)
将(2-4)与(2-5)相减并整理,就可以得到增量式PID控制算法公式为:

51单片机增量式PID算法程序(2-6)
其中
51单片机增量式PID算法程序

        由(2-6)可以看出,如果计算机控制系统采用恒定的采样周期T,一旦确定A、B、C,只要使用前后三次测量的偏差值,就可以由(2-6)求出控制量。
增量式PID控制算法与位置式PID算法(2-4)相比,计算量小得多,因此在实际中得到广泛的应用。
位置式PID控制算法也可以通过增量式控制算法推出递推计算公式:

51单片机增量式PID算法程序(2-7)
(2-7)就是目前在计算机控制中广泛应用的数字递推PID控制算法。

增量式PID控制算法C51程序

PID Function
The PID (比例、积分、微分) function is used in mainly
control applications. PIDCalc performs one iteration of the PID
algorithm.
While the PID function works, main is just a dummy program showing
a typical usage.
typedef struct PID
{
int SetPoint; //设定目标 Desired Value
long SumError; //误差累计
double Proportion; //比例常数 Proportional Const
double Integral; //积分常数 Integral Const
double Derivative; //微分常数 Derivative Const
int LastError; //Error[-1]
int PrevError; //Error[-2]
} PID;


static PID sPID;
static PID *sptr = &sPID;

Initialize PID Structure PID参数初始化
void IncPIDInit(void)
{
sptr->SumError = 0;
sptr->LastError = 0; //Error[-1]
sptr->PrevError = 0; //Error[-2]
sptr->Proportion = 0; //比例常数 Proportional Const
sptr->Integral = 0; //积分常数Integral Const
sptr->Derivative = 0; //微分常数 Derivative Const
sptr->SetPoint = 0;
}


增量式PID计算部分
int IncPIDCalc(int NextPoint)
{
register int iError, 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);
}
捉虫天师 发表于 2017-5-24 15:28 | 显示全部楼层
标准的实际上不好用。
zhuotuzi 发表于 2017-5-24 18:48 | 显示全部楼层
如果有一个完全封装好的就Ok了,一个函数配置参数,一个函数配置输入输出等接口,就Ok。还有一个就是启动它
 楼主| 598330983 发表于 2017-5-26 23:23 | 显示全部楼层
漂亮,不过我还是参考新唐提供的。
Harvard 发表于 2017-5-27 00:31 | 显示全部楼层
有时候资料太多也是麻烦的事情 .良莠不齐 , 需要去过滤垃圾信息 .无形中增加了 寻求真相的难度.
tianjiu 发表于 2017-5-27 09:25 | 显示全部楼层
谢谢分享。
LUOYANGL 发表于 2017-7-11 09:04 | 显示全部楼层
学习了,谢谢!
643757107 发表于 2017-7-11 09:15 | 显示全部楼层
用带DSP内核的单片机,直接调用库函数。
huhaibin2 发表于 2017-9-12 20:05 | 显示全部楼层
598330983 发表于 2017-5-24 14:40
这是一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID
参数必须由具体对象通 ...

734774645 发表于 2017-9-12 22:08 | 显示全部楼层
参考参考,正需要呢。
 楼主| 598330983 发表于 2017-9-26 16:52 | 显示全部楼层
其实算法无所谓标准不标准。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

267

主题

5575

帖子

22

粉丝
快速回复 在线客服 返回列表 返回顶部