风筝
发表于: 2018-9-11 20:28:53 | 显示全部楼层

你有没有想过温控器、3D打印机的加热床、汽车发动机或烤箱等设备是如何测量温度的?通过本篇文章,您可以理解其原理!


在产品中,获知温度可以得到一些非常有用的数据。温度信息有助于调节室内温度以保持舒适的环境,确保3D打印机床足够热,使得ABS等材料粘在其表面,防止发动机过热,以及防止食物被烧毁。


在本篇文章中,我们仅关注一种可以测量温度的传感器。该传感器称为热敏电阻。热敏电阻比其他类型的电阻对温度更敏感。


我们将使用Arduino开发板测量和处理热敏电阻的读数,然后将其转换为可识别的常见温度单位格式。


以下是我们将要使用的热敏电阻的图片:

Corleto_thermistor3.jpg


所需得材料

●    Arduino MEGA或Arduino Uno开发板

●    连接导线

●    焊锡和烙铁

●    Arduino IDE开发环境


相关的理论知识

在电阻的常见应用中,您不希望电阻随温度变化。实际上,这在现实生活中是不可能的,但是可以确保电阻在较大的温度变化下发生微小变化。如果随温度变化较大,则电阻会使电路中发生奇怪的事情,例如LED随着环境温度变化而变得更亮或更暗。


但是,如果你确实希望LED的亮度是随温度变化的呢?这就是热敏电阻的用武之地。正如您可能已经猜到的那样,在温度变化很小的情况下,热敏电阻的电阻变化很大。为了说明这个概念,请查看以下热敏电阻的典型曲线:

Corleto_thermistor1.jpg


显示单位不是实际值,因为热敏电阻可根据您购买的不同而变成不同的范围。如上所示,温度越来越高,电阻值越来越小。这是负温度系数电阻(简称NTC)的特性。


还有一些热敏电阻具有正温度系数(简称PTC),这意味着随着温度的升高,电阻会增加。但是,PTC热敏电阻具有一种临界点,并且在某些温度下电阻值会变化很大。这使得PTC热敏电阻很难使用。因此,对于大多数低成本温度测量产品,NTC热敏电阻是首选。

对于本文的剩下部分,您可以认为我们指的是NTC型热敏电阻。


找到曲线拟合公式的四种方法

现在我们已经了解了热敏电阻的通常特性,您可能会开始想知道我们如何使用Arduino开发板来测量温度。上图中的曲线是非线性的,因此,似乎不可能使用简单的线性方程。

那么该怎么办?


在继续阅读之前,请考虑如何在Arduino开发板或甚至没有微处理器组件的电路中执行此操作。

您可以通过以下几种方法解决此问题。这里并不是所有的技术,但它会向您展示一些流行的方法。


方法一:

一些制造商非常友好的为您提供映射某个整数范围的温度和电阻(典型值)的整个图表。 在Vishay公司的数据表中就可以找到一个这样的热敏电阻。


但是你想想将如何在Arduino中做到这一点。您需要将所有这些值硬编码到巨大的查找表或非常长的“switch ... case”或“if ... then”控制结构中的代码中。


如果制造商不够好以提供查找表,则需要自己测量每个点以生成数据。对于一个程序员来说,这是一个非常糟糕的一天。但这种方法并不全是坏事而且有其地位。如果手边的项目只检查几个点甚至是小范围,这可能是首选的方法。例如,一种情况是,如果您只想测量值是否在选定的温度范围内,并设置LED指示灯亮起以指示这种情况。

但对于我们的项目,我们希望测量接近连续的范围并将其发送到串口监视器,因此不会使用此方法。


方法二:

您可以尝试通过添加外部电路来“线性化”热敏电阻的响应。


一种流行的方法是将一个电阻与热敏电阻并联。有些IC可以为您提供此功能。


确定如何选择和线性化区域以及选择正确的值是可以使用整篇文章来介绍。如果微处理器没有浮点精度(如PICAXE),这种方法很好,因为它简化了温度范围到线性响应。它还使得设计没有微处理器的电路变得更容易。

但是我们在本文中有一个微处理器,并希望利用整个范围。


方法三:

您可以从数据表中获取表格数据,或者生成您使用独立测量所做的数据,并在Excel中重新创建绘图。然后,您可以使用曲线拟合功能为曲线创建公式。这不是一个坏主意,并且所有执行的工作都会为您的程序提供一个很好的公式 - 但它确实需要一些时间和数据的预处理。


虽然这是一种合法的方法,但我们不希望被困在分析所有这些数据。此外,每个热敏电阻都略有不同。


方法四:

事实证明,有一种通用曲线拟合公式适用于热敏电阻等器件。它被称为Steinhart-Hart方程。它的一个版本如下所示:

1/T = A + BLn(R)+ C(LN(R))3

其中R是热敏电阻在温度T(以开尔文为单位)的电阻。


这适用于所有NTC型电阻的通用曲线拟合方程。对于大多数应用来说,温度和电阻之间的近似值“足够好”。


注意,等式需要常数A、B和C。这些对于每个热敏电阻是不同的,需要给出或计算。由于存在三个未知数,因此在一定温度下需要进行三次电阻测量,然后可以使用这三种方程来求解这些常数。

即使对于我们这些代数向导的人来说,这仍然是太多的工作。


相反,有一个更简单的方程式不太准确但只有一个常数。常数用β表示,因此该方程称为β方程。

1/T = 1/T0+(1/β)* ln(R/RO)

其中Ro是参考温度To下的电阻(例如,室温下的电阻)。 β通常在数据表中给出 - 如果没有,您只需要一次测量(一个等式)来计算它。这是我将用于热敏电阻接口的公式,因为它非常简单,并且是我遇到的最简单的方法,无需线性化热敏电阻的响应。


用Arduino开发板测量电阻

现在已经完成了我们的方法,我们需要弄清楚如何用Arduino开发板实际测量电阻,然后才能将这些信息输入β方程。我们可以使用分压电路来实现:

Corleto_Thermistor2.jpg


这将是我们热敏电阻的接口电路。当热敏电阻检测到温度变化时,这将反映在输出电压中。


现在,我们通常使用分压电路,其公式如下:

VOUT =Vs⋅*(Rbalance / (Rthermistor + Rbalance))

但我们不希望Vout作为参考 - 我们想要Rthermistor。那么让我们使用代数公式来解决这个问题:

Rthermistor =Rbalance⋅*(Vs / Vout - 1)

这几乎是完美的,但我们需要现在测量我们的电压输出以及电源电压。这是我们充分利用Arduino内置ADC的地方。


我们可以将电压表示为一定范围内的数字。所以我们的等式最终如下:

Rthermistor =Rbalance *(Dmax / Dmeasured - 1)

这在数学上是有效的,因为无论我们如何表示电压(以伏特或数字为单位),这些单位都会抵消分数中的顶部和底部,留下无量纲数。然后,乘以电阻得到以欧姆表示的结果。

Dmax值为1023,这是10位ADC可以产生的最高数字。 Dmeasured是测量的ADC值,最低到零,最高到1023。

跳转到指定楼层
风筝
发表于: 2018-9-11 20:44:07 | 显示全部楼层

连接原理图

我用的是TH10K热敏电阻。在分压电路中还使用了一个10k的电阻来实现Rbalance。这里没有给出β,所以我需要自己计算。


下面是一个完整的原理图。 实际上非常简单明了!

Corleto_Thermistor_1.jpg

最终连接的实际电路类似于:

SAM_0034.jpg


Arduino代码

这里的代码经过深思熟虑,并有大量的注释来帮助您理解逻辑运行。


它可以测量分压电路的电压、计算温度值,然后在串口显示器上显示。


为了好玩,还有一些“if ... then”语句来说明如何对一系列温度和单个数据点采取行动。

  1. /*
  2. ================================================================================

  3.     File........... Thermistor_Demo_Code
  4.     Purpose........ Thermistor demonstration code
  5.     Author......... Joseph Corleto
  6.     E-mail......... corleto.joseph@gmail.com
  7.     Started........ 7/25/2016
  8.     Finished....... 7/25/2016
  9.     Updated........ --/--/----

  10. ================================================================================
  11.    Notes
  12. ================================================================================

  13. ================================================================================
  14.   Updates
  15. ================================================================================
  16. */

  17. //===============================================================================
  18. //  Header Files
  19. //===============================================================================

  20. //===============================================================================
  21. //  Constants
  22. //===============================================================================
  23. //Thermistor related:

  24. /* Here we have a few constants that make editing the code easier. I will go
  25.    through them one by one.

  26.    A reading from the ADC might give one value at one sample and then a little
  27.    different the next time around. To eliminate noisy readings, we can sample
  28.    the ADC pin a few times and then average the samples to get something more
  29.    solid. This constant is utilized in the readThermistor function.
  30.    */
  31. const int    SAMPLE_NUMBER      = 10;

  32. /* In order to use the Beta equation, we must know our other resistor
  33.    within our resistor divider. If you are using something with large tolerance,
  34.    like at 5% or even 1%, measure it and place your result here in ohms. */
  35. const double BALANCE_RESISTOR   = 9710.0;

  36. // This helps calculate the thermistor's resistance (check article for details).
  37. const double MAX_ADC            = 1023.0;

  38. /* This is thermistor dependent and it should be in the datasheet, or refer to the
  39.    article for how to calculate it using the Beta equation.
  40.    I had to do this, but I would try to get a thermistor with a known
  41.    beta if you want to avoid empirical calculations. */
  42. const double BETA               = 3974.0;

  43. /* This is also needed for the conversion equation as "typical" room temperature
  44.    is needed as an input. */
  45. const double ROOM_TEMP          = 298.15;   // room temperature in Kelvin

  46. /* Thermistors will have a typical resistance at room temperature so write this
  47.    down here. Again, needed for conversion equations. */
  48. const double RESISTOR_ROOM_TEMP = 10000.0;

  49. //===============================================================================
  50. //  Variables
  51. //===============================================================================
  52. // Here is where we will save the current temperature
  53. double currentTemperature = 0;

  54. //===============================================================================
  55. //  Pin Declarations
  56. //===============================================================================
  57. //Inputs:
  58. int thermistorPin = 0;  // Where the ADC samples the resistor divider's output

  59. //Outputs:

  60. //===============================================================================
  61. //  Initialization
  62. //===============================================================================
  63. void setup()
  64. {
  65.   // Set the port speed for serial window messages
  66.   Serial.begin(9600);
  67. }

  68. //===============================================================================
  69. //  Main
  70. //===============================================================================
  71. void loop()
  72. {
  73.   /* The main loop is pretty simple, it prints what the temperature is in the
  74.      serial window. The heart of the program is within the readThermistor
  75.      function. */
  76.   currentTemperature = readThermistor();
  77.   delay(3000);
  78.   
  79.   /* Here is how you can act upon a temperature that is too hot,
  80.   too cold or just right. */
  81.   if (currentTemperature > 21.0 && currentTemperature < 24.0)
  82.   {
  83.     Serial.print("It is ");
  84.     Serial.print(currentTemperature);
  85.     Serial.println("C. Ahhh, very nice temperature.");
  86.   }
  87.   else if (currentTemperature >= 24.0)
  88.   {
  89.     Serial.print("It is ");
  90.     Serial.print(currentTemperature);
  91.     Serial.println("C. I feel like a hot tamale!");
  92.   }
  93.   else
  94.   {
  95.     Serial.print("It is ");
  96.     Serial.print(currentTemperature);
  97.     Serial.println("C. Brrrrrr, it's COLD!");
  98.   }
  99. }

  100. //===============================================================================
  101. //  Functions
  102. //===============================================================================
  103. /////////////////////////////
  104. ////// readThermistor ///////
  105. /////////////////////////////
  106. /*
  107. This function reads the analog pin as shown below. Converts voltage signal
  108. to a digital representation with analog to digital conversion. However, this is
  109. done multiple times so that we can average it to eliminate measurement errors.
  110. This averaged number is then used to calculate the resistance of the thermistor.
  111. After this, the resistance is used to calculate the temperature of the
  112. thermistor. Finally, the temperature is converted to celsius. Please refer to
  113. the allaboutcircuits.com article for the specifics and general theory of this
  114. process.

  115. Quick Schematic in case you are too lazy to look at the site :P

  116.           (Ground) ----\/\/\/-------|-------\/\/\/---- V_supply
  117.                      R_balance      |     R_thermistor
  118.                                     |
  119.                                Analog Pin
  120. */

  121. double readThermistor()
  122. {
  123.   // variables that live in this function
  124.   double rThermistor = 0;            // Holds thermistor resistance value
  125.   double tKelvin     = 0;            // Holds calculated temperature
  126.   double tCelsius    = 0;            // Hold temperature in celsius
  127.   double adcAverage  = 0;            // Holds the average voltage measurement
  128.   int    adcSamples[SAMPLE_NUMBER];  // Array to hold each voltage measurement

  129.   /* Calculate thermistor's average resistance:
  130.      As mentioned in the top of the code, we will sample the ADC pin a few times
  131.      to get a bunch of samples. A slight delay is added to properly have the
  132.      analogRead function sample properly */
  133.   
  134.   for (int i = 0; i < SAMPLE_NUMBER; i++)
  135.   {
  136.     adcSamples = analogRead(thermistorPin);  // read from pin and store
  137.     delay(10);        // wait 10 milliseconds
  138.   }

  139.   /* Then, we will simply average all of those samples up for a "stiffer"
  140.      measurement. */
  141.   for (int i = 0; i < SAMPLE_NUMBER; i++)
  142.   {
  143.     adcAverage += adcSamples;      // add all samples up . . .
  144.   }
  145.   adcAverage /= SAMPLE_NUMBER;        // . . . average it w/ divide

  146.   /* Here we calculate the thermistor’s resistance using the equation
  147.      discussed in the article. */
  148.   rThermistor = BALANCE_RESISTOR * ( (MAX_ADC / adcAverage) - 1);

  149.   /* Here is where the Beta equation is used, but it is different
  150.      from what the article describes. Don't worry! It has been rearranged
  151.      algebraically to give a "better" looking formula. I encourage you
  152.      to try to manipulate the equation from the article yourself to get
  153.      better at algebra. And if not, just use what is shown here and take it
  154.      for granted or input the formula directly from the article, exactly
  155.      as it is shown. Either way will work! */
  156.   tKelvin = (BETA * ROOM_TEMP) /
  157.             (BETA + (ROOM_TEMP * log(rThermistor / RESISTOR_ROOM_TEMP)));

  158.   /* I will use the units of Celsius to indicate temperature. I did this
  159.      just so I can see the typical room temperature, which is 25 degrees
  160.      Celsius, when I first try the program out. I prefer Fahrenheit, but
  161.      I leave it up to you to either change this function, or create
  162.      another function which converts between the two units. */
  163.   tCelsius = tKelvin - 273.15;  // convert kelvin to celsius

  164.   return tCelsius;    // Return the temperature in Celsius
  165. }
复制代码

可能的后续步骤

本文中的所有内容都展示了一种使用廉价热敏电阻测量温度的简单方法。您可以通过以下几种方式改进设置:

●    将一个小电容与输出电压并联。这样可以稳定电压,甚至可以消除对许多样本进行平均的需要(如代码中所示) - 或者至少可以平均更少的样本。

●    使用精密电阻(优于1%)可以获得更加一致和可预测的测量。如果您需要绝对临界温度测量,请记住热敏电阻的自热会影响测量;这个项目不能补偿自热。


当然,热敏电阻只是一种用于温度测量的传感器。另一个受欢迎的选择是使用像这样的温度IC。这样你就不需要处理线性化或复杂的方程式。另外两个选项是热电偶和IR红外传感器;后者可以在没有物理接触的情况下测量温度,但它们并不便宜。


我希望这能让您更好地了解如何为下一个项目测量温度!在开发过程中遇到任何问题,请在本贴下面进行回复。


回复

使用道具 举报

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

本版积分规则

主题 705 | 回复: 1492



手机版|

GMT+8, 2024-11-22 04:56 , Processed in 0.068356 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

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

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