旧乡故客
发表于: 2018-8-3 10:58:11 | 显示全部楼层

我们将在固件中实现一个基本PID(比例 - 积分 - 微分)控制器,并使用示波器和LED指示灯观察运行结果。


该PID(比例 - 积分 - 微分)温度控制系统的主要组件包含EFM8微控制器、DAC和MAX31855热电偶数字转换器。该系列总共有6部分:

●    Part 1:电路原理设计

●    Part 2:板级集成

●    Part 3:实现与可视化


PID过程

在第一篇文章中,我们介绍了用于驱动通过加热元件电阻的相对较大的电流的电路,在第二篇文章中,我们讨论了与SPI接口相关的时序细节和固件,这使我们能够集成PID系统的三个主要组件。现在我们准备实现一个基本的PID控制程序。以下是我们将转换为EFM8代码的一般过程:

1.  选择比例,积分和微分增益的值。

2.  清除应从零开始的变量,即积分增益使用的累积误差和导数增益使用的先前误差值。

3.  从MAX31855收集数据并将其转换为温度值(以摄氏度为单位)。

4.  通过从设定点温度中减去测量温度来计算当前误差。

5.  将当前误差添加到积分增益使用的累积误差。

6.  通过从当前误差中减去先前的误差来计算误差的变化率(与微分增益一起使用)。

7.  通过将先前的误差设置为等于当前误差来更新先前的误差(用于下一次迭代)。

8.  通过将P增益乘以当前误差,I增益乘以累积误差,并将D增益乘以误差的变化率来计算PID控制输出,然后将这三次乘法的结果相加。

9.  根据可接受的范围限制PID控制输出。我们的电流驱动电路只能提供大约1 A的电流,我们不能低于零电流。 (请记住,在这个系统中,我们只能产生热量;我们无法移除热量。因此,低于零的PID输出值没有物理意义。例如,如果我们使用环境控制系统,情况就不是这样了。有一个加热器和一个空调器。在这种情况下,负PID输出将告诉系统完全停用加热器,然后启动空调。)

10.  应用PID控制输出(在我们的例子中,这意味着将新值写入控制加热器驱动电压的DAC通道)。

11.  等到当前PID间隔到期后再开始下一次迭代。


时间发生了什么?

您在上述过程中可能注意到的一件事是,当我们整合和区分误差时,我们没有合并时间量。在连续时间表示中,误差在时间上是不同的并且与时间相关。但是,在我们的离散时间实现中,我们不需要明确考虑增量时间dt,因为我们的PID间隔是常数,我们在每次迭代期间计算积分和微分误差。例如,导数误差的目的是告诉我们测量温度的变化速度(以及在什么方向,正面或负面)。通过简单地从当前误差中减去先前的误差,我们可以确定在前一个PID间隔期间温度变化了多少。下一个导数误差将告诉我们在后续PID间隔期间温度变化了多少,等等。因此,数量dt有效地包括在计算中 - 一个导数误差可以直接与所有其他导数误差进行比较,因为它们是参考相同的时间间隔计算的。


可视化

除非你是那种能够看到某些东西并确定其确切温度的人,否则如果没有某种可视化系统变化的方法,你将无法知道你的PID控制器在做什么。对于项目的这个阶段,我们将以两种方式评估我们的PID功能:1)通过使用oscope显示施加到加热元件电阻器的电压,以及2)通过在测量温度接近时增加LED的亮度设定点温度。当测量温度等于(或超过)设定点温度时,我们还会通过打开第二个LED来使LED技术更具信息性。


我们用于该项目的PCB包括一个RGB LED模块以及允许我们通过DAC输出精确控制每个LED亮度的电路。我们将使用红色LED表示温度变化,使用绿色LED指示测量温度已达到设定值。


您可以通过不同的方式通过LED亮度传达温度信息。我们的方法如下:当我们首次激活PID控制器时,我们将当前测量温度和设定值之间的差值存储为Initial_Error。在每次迭代期间,我们计算1减去当前误差与初始误差的比率,然后我们根据预定的8位DAC值缩放该减法的结果:

DAC值=(1-电流误差/初始误差)×DAC值,以获得最佳亮度


因此,当PID控制器首次启动时,(1 - (电流错误)/(初始错误))将接近零,LED将熄灭。随着测量温度的增加,电流误差将减小,因此(1 - (电流误差)/(初始误差))将朝1增加,因此DAC值将朝“DAC值增加,以获得最佳亮度。”我选择100作为最佳亮度值,因为我不喜欢盯着这些LED,当它们一直加速到完全20 mA时(DAC值255对应20 mA,因此100约为8 mA)。


这是完成LED功能的代码。

  1. /*-----This section executes before the main while loop.-----*/
  2. /*-----------------------------------------------------------*/
  3. GatherMAX31855Data();
  4. while(TEMP_DATA_READY == FALSE);        //wait until the SPI transaction is complete

  5. Measured_Temp = ConvertMAX31855Data_to_TempC();

  6. //the initial error is needed only for the LED functionality
  7. Initial_Error = Setpoint_Temp - Measured_Temp;
  8. /*-----------------------------------------------------------*/


  9. /*-----This section is included in the while loop.-----*/
  10. /*-----------------------------------------------------*/
  11. /*The red LED stays off if for some reason the current
  12. * error exceeds the initial error.*/
  13. if(Error > Initial_Error)
  14.         LED_Temp_Indicator = 0;
  15. /*This calculation is explained in the article.*/
  16. else
  17.         LED_Temp_Indicator = (1 - (Error/Initial_Error)) * 100;

  18. /*Here we turn on the green LED if we have
  19. * reached the setpoint. Notice that there
  20. * is no code that sets the green LED's DAC
  21. * value back to zero. This means that once
  22. * the green LED is on, it stays on.*/
  23. if(Measured_Temp >= Setpoint_Temp)
  24.         LED_Setpoint_Reached = 100;

  25. UpdateDAC(DAC_RGB_R, LED_Temp_Indicator);
  26. UpdateDAC(DAC_RGB_G, LED_Setpoint_Reached);
  27. /*-----------------------------------------------------*/
复制代码

固件

以下是PID代码(包括用于控制LED的代码)。 注释和描述性标识符应该使一切都清楚。 请注意,PID控制输出限制为200。我们可以高达255,但仅仅演示功能不需要完整的加热器电流。

  1. Setpoint_Temp = 50;

  2. K_proportional = 40;
  3. K_integral = 10;
  4. K_derivative = 0;

  5. Error_Integral = 0;
  6. Previous_Error = 0;

  7. /*Apparently the MAX31855 generates better
  8. * temperature data if it has a little extra time
  9. * after power-up. This is why we have a 1-second
  10. * delay here.*/
  11. Delay_10ms(100);

  12. GatherMAX31855Data();
  13. while(TEMP_DATA_READY == FALSE);        //wait until the SPI transaction is complete

  14. Measured_Temp = ConvertMAX31855Data_to_TempC();

  15. //the initial error is needed only for the LED functionality
  16. Initial_Error = Setpoint_Temp - Measured_Temp;

  17. while (1)
  18. {
  19.         GatherMAX31855Data();
  20.         while(TEMP_DATA_READY == FALSE);        //wait until the SPI transaction is complete

  21.         Measured_Temp = ConvertMAX31855Data_to_TempC();

  22.         Error = Setpoint_Temp - Measured_Temp;

  23.         /*We don't want the integral error to get
  24.          * way too large. This is a standard problem
  25.          * referred to as integral windup. One solution
  26.          * is to simply restrict the integral error to
  27.          * reasonable values.*/
  28.         Error_Integral = Error_Integral + Error;
  29.         if(Error_Integral > 50)
  30.                 Error_Integral = 50;
  31.         else if(Error_Integral < -50)
  32.                 Error_Integral = -50;

  33.         Error_Derivative = Error - Previous_Error;
  34.         Previous_Error = Error;

  35.         PID_Output = (K_proportional*Error) + (K_integral*Error_Integral) + (K_derivative*Error_Derivative);

  36.         /*We need to restrict the PID output to
  37.          * acceptable values. Here we have limited it
  38.          * to a maximum of 200, which corresponds
  39.          * to about 780 mA of heater-drive current, and
  40.          * a minimum of 0, because we cannot drive
  41.          * less than 0 A through the heating
  42.          * element.*/
  43.         if(PID_Output > 200)
  44.                 PID_Output = 200;
  45.         else if(PID_Output < 0)
  46.                 PID_Output = 0;

  47.         //here we convert the PID output from a float to an unsigned char
  48.         Heater_Drive = PID_Output;

  49.         UpdateDAC(DAC_HEATER, Heater_Drive);

  50.         /*The red LED stays off if for some reason the current
  51.          * error exceeds the initial error.*/
  52.         if(Error > Initial_Error)
  53.                 LED_Temp_Indicator = 0;
  54.         /*This calculation is explained in the article.*/
  55.         else
  56.                 LED_Temp_Indicator = (1 - (Error/Initial_Error)) * 100;

  57.         /*Here we turn on the green LED if we have
  58.          * reached the setpoint. Notice that there
  59.          * is no code that sets the green LED's DAC
  60.          * value back to zero. This means that once
  61.          * the green LED is on, it stays on.*/
  62.         if(Measured_Temp >= Setpoint_Temp)
  63.                 LED_Setpoint_Reached = 100;

  64.         UpdateDAC(DAC_RGB_R, LED_Temp_Indicator);
  65.         UpdateDAC(DAC_RGB_G, LED_Setpoint_Reached);

  66.         /*Here we wait until the PID interval has expired,
  67.          * then we begin a new iteration. The interval is
  68.          * currently set to 1 second.*/
  69.         PID_WAIT = TRUE;
  70.         while(PID_WAIT == TRUE);
  71. }
复制代码

结果

以下两个视频可让您了解系统的工作原理。我们将在未来的文章中更多地讨论调整 - 即正确选择比例,积分和微分增益。现在,我们将简单地看一下仅比例系统和比例积分(即无导数)系统的结果。比例增益设置为40,积分增益设置为10(这些值主要基于系统特性的基本考虑,尽管我根据经验观察调整了比例增益)。


第一个视频显示了P-only系统将温度从大约30°C提升到50°C的设定值。首先,示波器轨迹增加到最大驱动电压,然后您会注意到红色LED随着测量温度的增加而逐渐变亮。随着测量温度接近设定点,驱动电压最终降低。但是,绿色LED从不打开,然后驱动电压再次开始增加。该系统往往比设定点低几度;这种稳态误差是P系统的共同缺点。


绿色LED指示达到设定值,但对于仅P系统,测量温度往往比设定值低几度。因此,绿色LED永远不会打开。


现在我们引入一些积分增益来减少稳态误差。该PI系统执行相同的控制任务,即将温度从30℃升至50℃的设定点。驱动电压最初增加到最大值,红色LED逐渐变亮,如第一个视频中那样,但随后绿色LED亮起,表示我们达到了设定值。积分误差不会立即降至零,因此驱动电压暂时保持最大值;在此期间,测量温度高于设定值。最终,积分误差“展开”足以导致驱动电压降低。


我们可以引入积分增益来减少稳态误差。使用PI系统,绿色LED亮起,表示我们达到了设定值。

跳转到指定楼层
回复

使用道具 举报

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

本版积分规则

主题 29 | 回复: 32



手机版|

GMT+8, 2024-12-21 21:23 , Processed in 0.037674 second(s), 5 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

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