找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STM32 pid自整定+pid控温+pwm输出 源程序

  [复制链接]
跳转到指定楼层
楼主
  主要是利用继电器反馈法进行pid参数自动整定。若测出了系统的一阶模型,或得出了系统的临界比例增益,则可很容易地设计出PID调节器。继电型自整定的基本想法是,在控制系统中设置两种模态:测试模态和调节模态。在测试模态下,调节器自动转换成位式调节,即当测量值小于设定值时,调节器输出为满量程,反之为零,使系统产生振荡,振荡过程中调节器自动提取被控对象的特征参数;而在调节模态下由系统的特征参数首先得出PID控制器,然后,由此控制器对系统进行调节。当需要PID参数整定时,开关置于调整处,系统按继电反馈建立起稳定的极限环振荡后,就可以根据系系统响应特征确定PID参数。自整定计算完成后开关置于调节处,系统进入正常控制。内含STM32程序,包含DS18B20读取温度

STM32单片机源程序如下:
  1. #include "./pid/pid.h"   
  2. #include "./led/bsp_led.h"   
  3. #include "./GeneralTim/bsp_GeneralTim.h"

  4. PID pid; //存放PID算法所需要的数据

  5. uint16_t pw;
  6. uint16_t save_buff[9000] = {0}; //保存数据缓存
  7. extern volatile        uint16_t Step_Auto;       
  8. extern volatile        uint32_t PID_cool_cnt;
  9. extern volatile        uint32_t PID_heat_cnt;
  10. extern volatile        uint16_t pid_self_first_status_flag;        //标志位
  11. extern volatile        uint16_t PID_Deal,PID_auto_Deal;
  12. extern volatile        uint16_t zero_across_counter;
  13. extern volatile        uint16_t k_pid_self_counter;
  14. extern volatile        uint16_t CCR4_Val;
  15. extern volatile        uint16_t kkk;
  16. void PID_Calc()  //pid计算
  17. {
  18.         float DelEk;       
  19.         float out;
  20.         if (PID_Deal == 1)
  21.         {
  22.                 pid.Ek=pid.Sv-pid.Pv;   //得到当前的偏差值
  23.                 pid.SEk+=pid.Ek;        //历史偏差总和
  24.                 DelEk=pid.Ek-pid.Ek_1;  //最近两次偏差之差

  25.                 pid.Pout=pid.Kp*pid.Ek;     //比例输出
  26.                 pid.Iout=pid.Ki*pid.SEk;                          //积分输出
  27.                 pid.Dout=pid.Kd*DelEk;                            //微分输出
  28.                 out= pid.Pout+ pid.Iout+ pid.Dout;
  29.                 kkk++;
  30.                 if(out>255)
  31.                 {
  32.                         kkk=0;
  33.                         pid.SEk=0;
  34.                 }
  35.                 if(out>255)
  36.                 {
  37.                         pid.OUT=255;
  38.                 }
  39.                 else if(out<0)
  40.                 {
  41.                         pid.OUT=0;
  42.                 }
  43.                 else
  44.                 {
  45.                         pid.OUT=out;
  46.                 }
  47.                 pid.Ek_1=pid.Ek;  //更新偏差
  48. //                printf ( "\r\n%.1f\r\n",out);       
  49.                 PID_out();
  50.                
  51.         }
  52. }
  53. void PID_out()  //输出PID运算结果到负载---每1ms被调用1次
  54. {
  55.         uint16_t kk;
  56.         if (pid.Pv>pid.Sv)//当前温度大于用户设定温度
  57.         {
  58.                 CCR4_Val=100;
  59.                 GENERAL_TIM_Init();
  60.         }
  61.                 else
  62.                 {
  63.                 kk=100-pid.OUT*100/255;
  64.                 CCR4_Val=kk;
  65.                 GENERAL_TIM_Init();
  66. //        printf ( "\r\n%3d\r\n",kk);
  67.                 }
  68. }
  69. void PID_auto()//继电器反馈法自整定pid参数
  70. {

  71.         uint8_t i = 0;
  72.         float KC = 0;
  73.         float TC = 0;
  74.         float V_temp = 0,min_temp = 0,max_temp = 0;
  75.         float TIME_Hight=0,TIME_LOW=0;
  76.        
  77.        
  78.         //第一步进入比较初始温度 确定此时温度处于哪种情况

  79. if (PID_auto_Deal== 0)
  80. {
  81. /******************************************************************************************************/
  82.                
  83.                 PID_Deal = 0;//退出pid
  84.                
  85.                 //程序第一次进入 查看对比当前温度和设定温度

  86.                 if(pid.Pv < pid.Sv1)//当前温度低于设定温度
  87.                 {
  88.                         PID_heat_cnt++;                //热
  89.                         PID_cool_cnt = 0;
  90.                         if(PID_heat_cnt >= 3)//连续3次结果
  91.                         {
  92.                                 CCR4_Val=0;//加热
  93.                                 GENERAL_TIM_Init();

  94.                                 if(Step_Auto == 0)               
  95.                                 {
  96.                                         Step_Auto = 1;
  97.                                         zero_across_counter++;
  98. //                                        printf ( "\r\n1");       
  99.                                 }
  100.                         }
  101.                 }
  102.                 else//当前温度 大于 设定温度 停止加热
  103.                 {
  104.                         PID_cool_cnt++;
  105.                         PID_heat_cnt = 0;
  106.                         if(PID_cool_cnt > 3)
  107.                         {
  108.                                 CCR4_Val=100;                //不加热
  109.                                 GENERAL_TIM_Init();
  110.                                 if(Step_Auto == 1)                //设定温度高于当前温度
  111.                                 {
  112.                                         Step_Auto = 0;
  113.                                         zero_across_counter++;
  114.                                 }
  115.                         }
  116.                 }
  117.                 if(PID_heat_cnt >= 65535)//连续3次结果
  118.                 {
  119.                         PID_heat_cnt=65535;
  120.                 }
  121.                 if(PID_cool_cnt >= 65534)//连续3次结果
  122.                 {
  123.                         PID_cool_cnt=65534;
  124.                 }
  125.                
  126.                
  127.                
  128.                 /*****************开始计算强行振荡的周期和幅值****************************/                       

  129.                 if((zero_across_counter == 3 ) || (zero_across_counter == 4 ))
  130.                 {
  131.                         save_buff[k_pid_self_counter] = pid.Pv;
  132.                        
  133.                         k_pid_self_counter++;
  134.                        
  135.                         if(k_pid_self_counter >=9000)
  136.                         {
  137.                                 k_pid_self_counter = 0;                                       
  138.                         }
  139.                 }
  140.                 else if(zero_across_counter == 5 )//5次过0 则说明出现了振荡 整定成功
  141.                 {
  142.                                 PID_Deal = 1;
  143.                                 PID_auto_Deal = 1;
  144.                                 zero_across_counter = 0;

  145.                                         max_temp=save_buff[0];
  146.                                         min_temp=save_buff[0];
  147.                                         for(i = 0;i < k_pid_self_counter;i++)
  148.                                         {
  149.                                                 if(save_buff[i] >= max_temp)
  150.                                                 {
  151.                                                         max_temp = save_buff[i];
  152.                                                         TIME_LOW=i;
  153.                                                 }
  154.                                                 if(save_buff[i] <= min_temp)
  155.                                                 {
  156.                                                         min_temp = save_buff[i];
  157.                                                         TIME_Hight=i;
  158.                                                 }
  159.                                         }
  160.                                         V_temp =  max_temp - min_temp;                                        //最大减最小就是幅值
  161.                                        
  162.                                        
  163.                                 KC = 127/V_temp;
  164. //如果记录了 最低温度 与 最高温度对应的时间 那么沿用这个公式:TC = 2 * (TIME_Hight - TIME_LOW);       
  165.                                 TC = k_pid_self_counter;        //TC =2 * (TIME_Hight - TIME_LOW);
  166.                        
  167.                                 pid.Kp = 0.6*KC;               
  168.                                 pid.Ki = (0.6*KC)/(0.5*TC)/10;
  169.                                 pid.Kd = (0.6*KC)*(0.125*TC)/60;
  170.                                 printf ( "\r\n整定成功");               
  171.                         }
  172.         }       
  173. }
复制代码



  1. #include "stm32f10x.h"
  2. #include "core_cm3.h"
  3. #include "./systick/bsp_SysTick.h"
  4. #include "./led/bsp_led.h"
  5. #include "./usart/bsp_usart.h"
  6. #include "./ds18b20/bsp_ds18b20.h"
  7. #include "./pid/pid.h"
  8. #include "./timer/timer.h"
  9. #include "./AdvanceTim/bsp_AdvanceTim.h"
  10. #include "./GeneralTim/bsp_GeneralTim.h"

  11. volatile uint32_t time = 0; // ms 计时变量
  12. volatile uint32_t time2 = 0; // ms 计时变量
  13. float TM1,TM2;
  14. volatile uint8_t ucDs18b20Id[8];

  15. volatile        uint16_t Step_Auto;        
  16. volatile        uint32_t PID_cool_cnt;
  17. volatile        uint32_t PID_heat_cnt;
  18. volatile        uint16_t pid_self_first_status_flag;        //标志位
  19. volatile        uint16_t PID_Deal,PID_auto_Deal;
  20. volatile        uint16_t zero_across_counter;
  21. volatile        uint16_t k_pid_self_counter;
  22. volatile        uint16_t CCR4_Val;
  23. volatile        uint16_t kkk;
  24. //volatile         uint16_t save_buff[9000] = {0};
  25. void delay();

  26. void PID_Init()
  27. {
  28.                 pid.Sv=30;                //用户设定温度
  29.                 pid.OUT0=1;

  30.                 zero_across_counter=0;
  31.                 PID_cool_cnt=0;
  32.                 PID_heat_cnt=0;
  33.                 PID_Deal=0;
  34.                 PID_auto_Deal=0;
  35.                 pid_self_first_status_flag = 0;
  36.                 k_pid_self_counter=0;
  37.                 if( pid.Pv <= pid.Sv)        //设定温度高于当前温度
  38.                 {
  39.                         pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;        
  40.                         pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;        
  41.                         pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;        
  42.                         pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;        
  43.                         pid.Sv1=(pid.Sv-pid.Pv)/2+pid.Pv;        
  44.                         
  45.                 }               
  46. //                CCR4_Val=100;
  47. //                PID_auto_Deal=1;
  48. //                PID_Deal=1;
  49. //                pid.Sv=30;
  50. //                pid.Kp=38.0999985;
  51. //                pid.Ki=0.21666673;
  52. //                pid.Kd=2.8575;
  53.                
  54.         
  55. }



  56. /**
  57.   * @brief  主函数
  58.   * @param  无  
  59.   * @retval 无
  60.   */
  61. int main(void)
  62. {        
  63.                 delay();
  64.         CCR4_Val=50;
  65.         ADVANCE_TIM_Init();
  66.         GENERAL_TIM_Init();
  67.         
  68.         /* 配置SysTick 为1us中断一次 */
  69.         SysTick_Init();
  70.         
  71.         
  72. //        /* 端口初始化 */
  73. //        LED_GPIO_Config();//初始化了PB1口
  74.         
  75.         

  76.         
  77.         USART_Config();        //初始化串口1
  78.         printf("\r\n this is a ds18b20 test demo \r\n");
  79.         while( DS18B20_Init() )        
  80.         printf("\r\n no ds18b20 exit \r\n");//初始化DS18B20,不初始化就等待这里?
  81.         printf("\r\n ds18b20 exit \r\n");
  82.         DS18B20_ReadId(ucDs18b20Id);
  83.         TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
  84.         pid.Pv=TM1;
  85.         PID_Init();  //参数初始化        
  86.         pid.Sv1=28;        
  87.         while(1)        
  88.         {        
  89.                 printf ( "\r\n%.1f",TM1);                // 打印通过 DS18B20 序列号获取的温度值        
  90.                 Delay_ms(10);
  91.                 TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
  92.                 pid.Pv=TM1;//当前温度         
  93.                 pid.Pv=TM1;//当前温度        
  94.                 PID_auto();
  95.                 PID_Calc(); //pid计算                                                               
  96. //                printf ( "\r\n%.1f",TM1);                // 打印通过 DS18B20 序列号获取的温度值        
  97. //                Delay_ms(1000);
  98. //                if ( time >= 10 ) /* 10 * 1 ms = 10ms 时间到 */
  99. //    {
  100. //      time = 0;  
  101. //                        TM1=DS18B20_GetTemp_MatchRom ( ucDs18b20Id );
  102. //                        pid.Pv=TM1;//当前温度        
  103. //    }
  104. //                if ( time2 >= 1000 ) /* 1000 * 1 ms = 1s 时间到 */
  105. //    {
  106. //      time2 = 0;  
  107. //                        pid.Pv=TM1;//当前温度        
  108. //                        PID_auto();
  109. //                        PID_Calc(); //pid计算                                                               
  110. //                }               
  111.         }         
  112. }                                
  113. void delay(void)
  114. {
  115.         int i;
  116.         for(i=0; i<10000000; i++)
  117.                 ;
  118. }
复制代码

代码工程文件51hei附件下载:
代码.7z (197.13 KB, 下载次数: 451)

评分

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

查看全部评分

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

使用道具 举报

沙发
ID:275041 发表于 2021-8-14 09:17 | 只看该作者
最近正在学这PID呢,谢谢了
回复

使用道具 举报

板凳
ID:748535 发表于 2021-8-22 10:15 | 只看该作者

最近正在学这PID呢,谢谢了
回复

使用道具 举报

地板
ID:966468 发表于 2021-9-16 11:40 | 只看该作者
最近正在学这PID呢,谢谢了
回复

使用道具 举报

5#
ID:856642 发表于 2021-9-22 19:51 | 只看该作者
最近正在学这PID呢,谢谢了
回复

使用道具 举报

6#
ID:967413 发表于 2021-9-23 08:57 | 只看该作者
正在做一个PID温控器的项目,学习了!谢谢!
回复

使用道具 举报

7#
ID:936528 发表于 2021-9-24 10:38 | 只看该作者
楼主你好,请问下这里为什么当前温度大于设定温度,占空比却还要调到最大

捕获.PNG (61.39 KB, 下载次数: 175)

捕获.PNG
回复

使用道具 举报

8#
ID:462629 发表于 2021-12-28 10:18 | 只看该作者
有原理图吗
回复

使用道具 举报

9#
ID:334781 发表于 2021-12-31 09:29 | 只看该作者
楼主,有原理图吗,PID控制是不是要用可控硅呢,用继电器的话,会不会影响使用寿命呢?
回复

使用道具 举报

10#
ID:41543 发表于 2023-2-1 10:53 | 只看该作者
学习中 温控,pid ,感谢
回复

使用道具 举报

11#
ID:385620 发表于 2023-8-24 09:12 | 只看该作者
您好,您代码中自整定这块好像没有运行,KI,KD,PD这3个参数一直为初值0
回复

使用道具 举报

12#
ID:385620 发表于 2023-8-24 09:13 | 只看该作者
您好,你代码里的自整定部分没有运行,其中KI,KD,KP3个参数一直为0
回复

使用道具 举报

13#
ID:958310 发表于 2024-1-4 10:32 | 只看该作者
请问“                                KC = 127/V_temp; //如果记录了 最低温度 与 最高温度对应的时间 那么沿用这个公式:TC = 2 * (TIME_Hight - TIME_LOW);                                        TC = k_pid_self_counter;        //TC =2 * (TIME_Hight - TIME_LOW);                                                         pid.Kp = 0.6*KC;                                                pid.Ki = (0.6*KC)/(0.5*TC)/10;                                 pid.Kd = (0.6*KC)*(0.125*TC)/60;”这一段计算是根据什么得到的?
回复

使用道具 举报

14#
ID:52749 发表于 2024-2-1 16:07 | 只看该作者
CCR4_VAL=100  out=0%            CCR4_VAL=0  out=100%   

请问TC    得出后,PID运算周期 ,输出周期没有更改,这个应该很重要,关系到系统的滞后
回复

使用道具 举报

15#
ID:52749 发表于 2024-2-1 16:08 | 只看该作者

请问“                                KC = 127/V_temp; //如果记录了 最低温度 与 最高温度对应的时间 那么沿用这个公式:TC = 2 * (TIME_Hight - TIME_LOW);                                        TC = k_pid_self_counter;        //TC =2 * (TIME_Hight - TIME_LOW);                                                         pid.Kp = 0.6*KC;                                                pid.Ki = (0.6*KC)/(0.5*TC)/10;                                 pid.Kd = (0.6*KC)*(0.125*TC)/60;”这一段计算是根据什么得到的?


这个应该是继电整定法,网上能找到公式

回复

使用道具 举报

16#
ID:164385 发表于 2024-2-4 09:28 | 只看该作者
很好的代码,学习了
回复

使用道具 举报

17#
ID:592857 发表于 2024-2-28 14:39 | 只看该作者
  最近正在学这PID呢,谢谢了
回复

使用道具 举报

18#
ID:346648 发表于 2024-3-7 22:21 | 只看该作者
最近正在学这PID呢,谢谢了
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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