找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1727|回复: 1
打印 上一主题 下一主题
收起左侧

好用的模糊PID温度控制算法 C语言源代码

  [复制链接]
跳转到指定楼层
楼主
ID:230216 发表于 2023-4-7 10:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <math.h>
  4. #include "pid_fuzzy.h"

  5. //注1:自适应模糊pid最重要的就是论域的选择,要和你应该控制的对象相切合
  6. //注2:以下各阀值、限幅值、输出值均需要根据具体的使用情况进行更改
  7. //注3:因为我的控制对象惯性比较大,所以以下各部分取值较小
  8. //论域e:[-5,5]  ec:[-0.5,0.5]

  9. //误差的阀值,小于这个数值的时候,不做PID调整,避免误差较小时频繁调节引起震荡
  10. #define Emin 0.0
  11. #define Emid 0.08
  12. #define Emax 0.6
  13. //调整值限幅,防止积分饱和
  14. #define Umax 5
  15. #define Umin -5

  16. //输出值限幅
  17. #define Pmax 7200
  18. #define Pmin 0

  19. #define NB 0
  20. #define NM 1
  21. #define NS 2
  22. #define ZO 3
  23. #define PS 4
  24. #define PM 5
  25. #define PB 6

  26. int kp[7][7]= {  {PB,PB,PM,PM,PS,ZO,ZO},
  27.     {PB,PB,PM,PS,PS,ZO,ZO},
  28.     {PM,PM,PM,PS,ZO,NS,NS},
  29.     {PM,PM,PS,ZO,NS,NM,NM},
  30.     {PS,PS,ZO,NS,NS,NM,NM},
  31.     {PS,ZO,NS,NM,NM,NM,NB},
  32.     {ZO,ZO,NM,NM,NM,NB,NB}
  33. };

  34. int kd[7][7]= {  {PS,NS,NB,NB,NB,NM,PS},
  35.     {PS,NS,NB,NM,NM,NS,ZO},
  36.     {ZO,NS,NM,NM,NS,NS,ZO},
  37.     {ZO,NS,NS,NS,NS,NS,ZO},
  38.     {ZO,ZO,ZO,ZO,ZO,ZO,ZO},
  39.     {PB,NS,PS,PS,PS,PS,PB},
  40.     {PB,PM,PM,PM,PS,PS,PB}
  41. };

  42. int ki[7][7]= {  {NB,NB,NM,NM,NS,ZO,ZO},
  43.     {NB,NB,NM,NS,NS,ZO,ZO},
  44.     {NB,NM,NS,NS,ZO,PS,PS},
  45.     {NM,NM,NS,ZO,PS,PM,PM},
  46.     {NM,NS,ZO,PS,PS,PM,PB},
  47.     {ZO,ZO,PS,PS,PM,PB,PB},
  48.     {ZO,ZO,PS,PM,PM,PB,PB}
  49. };

  50. /**************求隶属度(三角形)***************/
  51. float FTri(float x,float a,float b,float c)//FuzzyTriangle
  52. {
  53.     if(x<=a)
  54.         return 0;
  55.     else if((a<x)&&(x<=b))
  56.         return (x-a)/(b-a);
  57.     else if((b<x)&&(x<=c))
  58.         return (c-x)/(c-b);
  59.     else if(x>c)
  60.         return 0;
  61.     else
  62.         return 0;
  63. }
  64. /*****************求隶属度(梯形左)*******************/
  65. float FTraL(float x,float a,float b)//FuzzyTrapezoidLeft
  66. {
  67.     if(x<=a)
  68.         return 1;
  69.     else if((a<x)&&(x<=b))
  70.         return (b-x)/(b-a);
  71.     else if(x>b)
  72.         return 0;
  73.     else
  74.         return 0;
  75. }
  76. /*****************求隶属度(梯形右)*******************/
  77. float FTraR(float x,float a,float b)//FuzzyTrapezoidRight
  78. {
  79.     if(x<=a)
  80.         return 0;
  81.     if((a<x)&&(x<b))
  82.         return (x-a)/(b-a);
  83.     if(x>=b)
  84.         return 1;
  85.     else
  86.         return 1;
  87. }
  88. /****************三角形反模糊化处理**********************/
  89. float uFTri(float x,float a,float b,float c)
  90. {
  91.     float y,z;
  92.     z=(b-a)*x+a;
  93.     y=c-(c-b)*x;
  94.     return (y+z)/2;
  95. }
  96. /*******************梯形(左)反模糊化***********************/
  97. float uFTraL(float x,float a,float b)
  98. {
  99.     return b-(b-a)*x;
  100. }
  101. /*******************梯形(右)反模糊化***********************/
  102. float uFTraR(float x,float a,float b)
  103. {
  104.     return (b-a)*x +a;
  105. }
  106. /**************************求交集****************************/
  107. float fand(float a,float b)
  108. {
  109.     return (a<b)?a:b;
  110. }
  111. /**************************求并集****************************/
  112. float forr(float a,float b)
  113. {
  114.     return (a<b)?b:a;
  115. }
  116. float ec;
  117. /*==========   PID计算部分   ======================*/
  118. int PID_realize(PID *structpid,uint16_t s,uint16_t in)
  119. {
  120.     float pwm_var;//pwm调整量
  121.     float iError;//当前误差
  122.     float set,input;

  123.     //计算隶属度表
  124.     float es[7],ecs[7],e;
  125.     float form[7][7];
  126.     int i=0,j=0;
  127.     int MaxX=0,MaxY=0;

  128.     //记录隶属度最大项及相应推理表的p、i、d值
  129.     float lsd;
  130.     int temp_p,temp_d,temp_i;
  131.     float detkp,detkd,detki;//推理后的结果

  132.     //输入格式的转化及偏差计算
  133.     set=(float)s/100.0;
  134.     input=(float)in/100.0;
  135.     iError = set - input; // 偏差

  136.     e=iError;
  137.     ec=iError-structpid->LastError;

  138.     //当温度差的绝对值小于Emax时,对pid的参数进行调整
  139.     if(fabs(iError)<=Emax)
  140.     {
  141.         //计算iError在es与ecs中各项的隶属度
  142.         es[NB]=FTraL(e*5,-3,-1);  //e
  143.         es[NM]=FTri(e*5,-3,-2,0);
  144.         es[NS]=FTri(e*5,-3,-1,1);
  145.         es[ZO]=FTri(e*5,-2,0,2);
  146.         es[PS]=FTri(e*5,-1,1,3);
  147.         es[PM]=FTri(e*5,0,2,3);
  148.         es[PB]=FTraR(e*5,1,3);

  149.         ecs[NB]=FTraL(ec*30,-3,-1);//ec
  150.         ecs[NM]=FTri(ec*30,-3,-2,0);
  151.         ecs[NS]=FTri(ec*30,-3,-1,1);
  152.         ecs[ZO]=FTri(ec*30,-2,0,2);
  153.         ecs[PS]=FTri(ec*30,-1,1,3);
  154.         ecs[PM]=FTri(ec*30,0,2,3);
  155.         ecs[PB]=FTraR(ec*30,1,3);

  156.         //计算隶属度表,确定e和ec相关联后表格各项隶属度的值
  157.         for(i=0; i<7; i++)
  158.         {
  159.             for(j=0; j<7; j++)
  160.             {
  161.                 form[i][j]=fand(es[i],ecs[j]);
  162.             }
  163.         }

  164.         //取出具有最大隶属度的那一项
  165.         for(i=0; i<7; i++)
  166.         {
  167.             for(j=0; j<7; j++)
  168.             {
  169.                 if(form[MaxX][MaxY]<form[i][j])
  170.                 {
  171.                     MaxX=i;
  172.                     MaxY=j;
  173.                 }
  174.             }
  175.         }
  176.         //进行模糊推理,并去模糊
  177.         lsd=form[MaxX][MaxY];
  178.         temp_p=kp[MaxX][MaxY];
  179.         temp_d=kd[MaxX][MaxY];
  180.         temp_i=ki[MaxX][MaxY];

  181.         if(temp_p==NB)
  182.             detkp=uFTraL(lsd,-0.3,-0.1);
  183.         else if(temp_p==NM)
  184.             detkp=uFTri(lsd,-0.3,-0.2,0);
  185.         else if(temp_p==NS)
  186.             detkp=uFTri(lsd,-0.3,-0.1,0.1);
  187.         else if(temp_p==ZO)
  188.             detkp=uFTri(lsd,-0.2,0,0.2);
  189.         else if(temp_p==PS)
  190.             detkp=uFTri(lsd,-0.1,0.1,0.3);
  191.         else if(temp_p==PM)
  192.             detkp=uFTri(lsd,0,0.2,0.3);
  193.         else if(temp_p==PB)
  194.             detkp=uFTraR(lsd,0.1,0.3);

  195.         if(temp_d==NB)
  196.             detkd=uFTraL(lsd,-3,-1);
  197.         else if(temp_d==NM)
  198.             detkd=uFTri(lsd,-3,-2,0);
  199.         else if(temp_d==NS)
  200.             detkd=uFTri(lsd,-3,1,1);
  201.         else if(temp_d==ZO)
  202.             detkd=uFTri(lsd,-2,0,2);
  203.         else if(temp_d==PS)
  204.             detkd=uFTri(lsd,-1,1,3);
  205.         else if(temp_d==PM)
  206.             detkd=uFTri(lsd,0,2,3);
  207.         else if(temp_d==PB)
  208.             detkd=uFTraR(lsd,1,3);

  209.         if(temp_i==NB)
  210.             detki=uFTraL(lsd,-0.06,-0.02);
  211.         else if(temp_i==NM)
  212.             detki=uFTri(lsd,-0.06,-0.04,0);
  213.         else if(temp_i==NS)
  214.             detki=uFTri(lsd,-0.06,-0.02,0.02);
  215.         else if(temp_i==ZO)
  216.             detki=uFTri(lsd,-0.04,0,0.04);
  217.         else if(temp_i==PS)
  218.             detki=uFTri(lsd,-0.02,0.02,0.06);
  219.         else if(temp_i==PM)
  220.             detki=uFTri(lsd,0,0.04,0.06);
  221.         else if (temp_i==PB)
  222.             detki=uFTraR(lsd,0.02,0.06);

  223.         //pid三项系数的修改
  224.         structpid->Kp+=detkp;
  225.         structpid->Ki+=detki;
  226.         //structpid->Kd+=detkd;
  227.         structpid->Kd=0;//取消微分作用

  228.         //对Kp,Ki进行限幅
  229.         if(structpid->Kp<0)
  230.         {
  231.             structpid->Kp=0;
  232.         }
  233.         if(structpid->Ki<0)
  234.         {
  235.             structpid->Ki=0;
  236.         }

  237.         //计算新的K1,K2,K3
  238.         structpid->K1=structpid->Kp+structpid->Ki+structpid->Kd;
  239.         structpid->K2=-(structpid->Kp+2*structpid->Kd);
  240.         structpid->K3=structpid->Kd;

  241.     }

  242.     if(iError>Emax)
  243.     {
  244.         structpid->pwm_out=7200;
  245.         pwm_var = 0;
  246.         structpid->flag=1;//设定标志位,如果误差超过了门限值,则认为当控制量第一次到达给定值时,应该采取下面的 抑制超调 的措施
  247.     }
  248.     else if(iError<-Emax)
  249.     {
  250.         structpid->pwm_out=0;
  251.         pwm_var = 0;
  252.     }
  253.     else if( fabs(iError) < Emin ) //误差的阀值(死区控制??)
  254.     {
  255.         pwm_var = 0;
  256.     }
  257.     else
  258.     {
  259.         if( iError<Emid && structpid->flag==1 )//第一次超过(设定值-Emid(-0.08)摄氏度),是输出为零,防止超调,也可以输出其他值,不至于太小而引起震荡
  260.         {
  261.             structpid->pwm_out=0;
  262.             structpid->flag=0;
  263.         }
  264.         else if( -iError>Emid)//超过(设定+Emid(+0.08)摄氏度)
  265.         {
  266.             pwm_var=-1;
  267.         }
  268.         else
  269.         {
  270.             //增量计算
  271.             pwm_var=(structpid->K1 * iError  //e[k]
  272.                      + structpid->K2 * structpid->LastError  //e[k-1]
  273.                      + structpid->K3 * structpid->PrevError);    //e[k-2]
  274.         }
  275.         if(pwm_var >= Umax)pwm_var = Umax;      //调整值限幅,防止积分饱和
  276.         if(pwm_var <= Umin)pwm_var = Umin;      //调整值限幅,防止积分饱和

  277.     }
  278.     structpid->PrevError=structpid->LastError;
  279.     structpid->LastError=iError;

  280.     structpid->pwm_out += 360*pwm_var;        //调整PWM输出

  281.     if(structpid->pwm_out > Pmax)structpid->pwm_out = Pmax;    //输出值限幅
  282.     if(structpid->pwm_out < Pmin)structpid->pwm_out = Pmin;    //输出值限幅

  283.     return (int)(structpid->pwm_out); // 微分项
  284. }

  285. void PID_Set(PID *structpid,float Kp,float Ki,float Kd,float T)
  286. {
  287.     (*structpid).Kp=Kp;//Kp*(1+(Td/T));
  288.     (*structpid).Ki=Ki;
  289.     (*structpid).Kd=Kd;
  290.     (*structpid).T=T;

  291.     structpid->K1=structpid->Kp*(1+structpid->Ki+structpid->Kd);
  292.     structpid->K2=-(structpid->Kp+2*structpid->Kp*structpid->Kd);
  293.     structpid->K3=structpid->Kp*structpid->Kd;
  294. }

  295. void PID_Init(PID *structpid)
  296. {
  297.     PID_Set(structpid,8.3,1.2,0,1);
  298.     structpid->flag=0;
  299.     structpid->pwm_out=0;
  300. }
复制代码

pid_fuzzy.h
  1. #ifndef PID_H_
  2. #define PID_H_
  3. #include "stm32f10x.h"

  4. typedef struct PID
  5. {
  6.         float Kp; // 增量式积分系数
  7.         float Ki;
  8.         float Kd;
  9.         float T;
  10.        
  11.         float K1; // 增量式积分系数
  12.         float K2;
  13.         float K3;
  14.         float LastError; //Error[-1]
  15.         float PrevError; // Error[-2]
  16.         float pwm_out;
  17.        
  18.         uint16_t flag;//温度状态标志位
  19. }PID;

  20. //void PID_init(PID *structpid);
  21. void PID_Set(PID *structpid,float Kp,float Ki,float Kd,float T);
  22. int PID_realize(PID *structpid,uint16_t s,uint16_t in);
  23. void PID_Init(PID *structpid);
  24. #endif /* PID_H_ */
复制代码


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏8 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:115126 发表于 2023-4-11 09:56 | 只看该作者
代码简洁,学习一下看看
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表