Teorie, kalibrace a experiment termistoru v Arduino

25.09.2021 Arduino #arduino #teplota

Teoretický základ pro používání termistorů s platformou Arduino.


Termistor, jehož název je odvozen z kombinace therm a resistor, je určen pro snímání teploty zařízení, který registruje změny vnitřního odporu jako funkce teploty. Termistorům se dává často přednost před termočlánky, protože jsou přesnější, mají kratší dobu odezvy a jsou obecně levnější. Pro většinu aplikací jsou termistory chytrou a snadnou volbou pro snímání teploty pod 300 stupňů Celsia. V našem případě použijeme termistor s negativním teplotním koeficientem (NTC), kde odpor klesá se zvyšující se teplotou. Termistory NTC jsou nejběžnější v komerčních produktech, které pracují v řádu desítek stupňů, jako jsou termostaty, toustovače a dokonce i 3-D tiskárny. Bude použit termistor NTC 3950 100k, který je navržen pro odpor 100 kOhm při 25 stupních Celsia.

Seznam dílů pro experiment

Použijeme termistor NTC 3950 100k a bude sloužit jako primární součást použitá v tomto tutoriálu.

  1. Termistor NTC 3950 100k

  2. Arduino Uno

  3. Snímač teploty DHT22

  4. Rezistory 220k a 10k

  5. Breadboard

  6. 10uF kondenzátor

  7. Propojovací vodiče

Steinhart – Hartova aproximace pro termistory

Termistory lze aproximovat za předpokladu funkce třetího řádu, která se nazývá Steinhart-Hartova aproximace [ zdroj kalibrace termistoru ]:

rovnice-01 

kde T je teplota, vypočtená ze změny termistoru odporu, R. Koeficienty 0, C1 C2 je třeba najít pomocí nelineární regresní metody. Steinhart-Hartova rovnice se často simuluje a přepisuje jako exponenciál prvního řádu:

rovnice-02 

Nyní vidíme aproximační metodu týkající T k odporu, R . Koeficienty a, b, c lze nalézt pomocí nejmenších čtverců přizpůsobených továrním kalibračním datům získaným od výrobce. Pro termistor jsou k dispozici tovární tabulky, které umožňují přizpůsobit data pomocí výše uvedené rovnice [ příklad datového listu s tabulkou ].

Pomocí Pythonu a tabulek pro termistor a pomocí výše uvedené funkce a sady nástrojů scipy 'curve_fit' přizpůsobit data exponenciální křivce. Výsledný vztah a koeficienty jsou uvedeny níže:

Obrázek 1: Kalibrace z výroby na vztah teploty a odporu pro odečty termistorů.

Obrázek 1: Kalibrace z výroby na vztah teploty a odporu pro odečty termistorů.

 

Nyní, když máme vztah mezi odporem drátu termistoru a měřenou teplotou, musíte pochopit, jak můžete převést odpor na smysluplnou veličinu, kterou můžet měřit pomocí analogově-digitálního převodníku, konkrétně potřebujete převést odolnost vůči napětí. A to je vysvětleno v další části.

Měření odporu pomocí děliče napětí

Arduino má 10 bitový převodník analogového signálu na digitální (ADC), který měří hodnoty napětí. Protože termistor vydává odpor, musí se vytvořit vztah mezi odporem a napětím, aby se mohlo spojit změnu odporu s napětím. Lze to udělat pomocí jednoduchého děliče napětí:

Obrázek 2: Obvod děliče napětí pro měření napětí místo odporu z termistoru.

Obrázek 2: Obvod děliče napětí pro měření napětí místo odporu z termistoru.

 

Pro Arduino použijete 3,3 V jako V0, aby se udržel nízký šum na měření termistoru. Použití Kirchhoffových zákonů k odvození vztahu mezi napěťovým vstupem a dvěma odpory pomocí následujícího vztahu:

které lze přepsat z hlediska odporů a proudu společné smyčky:

Nakonec lze přepsat proud jako funkci dvou odporů smyčky:

Zkombinováním posledních dvou rovnic lze získat reprezentaci napětí na druhém odporu (termistoru):

což lze vyřešit pro V2, což je napětí, které bude číst Arduino:
a pokud jen trochu:

A nakonec máme klasickou rovnici děliče napětí:

rovnice-09  

Rovnice děliče napětí je důležitá pro vztah odporu termistoru, R2, k napětí načtenému Arduinem. Než však přečteme R2 , musíme se nejprve rozhodnout, kterou R1 chceme vybrat pro obvod děliče napětí. A protože již známe V0 = 3,3 V, dělicí odpor je poslední neznámou.

Výběr odporu pro optimální rozsah termistoru

Možná to není zřejmé, ale výběr správného R1 je zásadně důležitý. Vyžaduje znalost vztahu mezi odporem termistoru a měřenou teplotou. Lze začít přepsáním rovnice děliče napětí:

a pokud řešíme pro R1:
Samozřejmě můžeme přepsat R2, pokud jde o vhodné parametry a teplotu:
rovnice-13 

A pokud předpokládáme teplotní rozsah, můžeme vzít střed teplotního rozsahu a umístit V2 doprostřed, čímž zajistíme, že naše hodnota R1 dokonale umístí střed teplotního rozsahu do středu napěťového rozsahu Arduina. Jakmile je odpor vypočítán z požadovaného teplotního rozsahu, můžeme použít následující rovnici pro předpověď napětí Arduina jako funkce teploty:

rovnice-14 

nyní můžeme použít vhodné parametry z továrně odvozené termistorové rovnice a vytvořit graf, který demonstruje odezvu napětí v důsledku změn teploty a odporu děliče napětí, R1. Při použití V0 = 3,3V bude graf vypadat následovně: Obrázek 3: Křivky reakce odporu děliče napětí.  Odezva napětí termistoru se mění na základě zvoleného odporu děliče napětí.  Ujistěte se, že jste vybrali rezistor v blízkosti rezistoru výše pro váš konkrétní požadovaný teplotní rozsah.

Obrázek 3: Křivky reakce odporu děliče napětí. Odezva napětí termistoru se mění na základě zvoleného odporu děliče napětí. Ujistěte se, že jste vybrali rezistor v blízkosti rezistoru výše pro váš konkrétní požadovaný teplotní rozsah.

 

Úplná implementace algoritmů a obrázků 1 a 3 je implementována níže v Pythonu 3.6.

#!/usr/bin/env python3
#
# script for determining resistor pairing with thermistor NTC 3950 100k
#
#
import csv
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy import stats

plt.style.use('ggplot')

def exp_func(x,a,b,c):
    return a*np.exp(-b*x)+c

temp_cal,resist_cal = [],[]
with open('ntc_3950_thermistor_cal_data.csv',newline='') as csvfile:
    csvreader = csv.reader(csvfile)
    for row in csvreader:
        temp_cal.append(float(row[1]))
        resist_cal.append(float(row[2]))

fit_params,_ = curve_fit(exp_func,temp_cal,resist_cal,maxfev=10000)

test_fit = [exp_func(ii,*fit_params) for ii in temp_cal]

RMSE = np.sqrt(np.mean(np.power(np.subtract(test_fit,resist_cal),2)))
mape = np.mean(np.abs(np.divide(np.subtract(resist_cal,test_fit),resist_cal)))*100
err_percent = 100.0*(RMSE/np.mean(np.abs(resist_cal)))
print('RMSE = {0:2.2f} ({1:2.1f}%)'.format(RMSE,err_percent))

fit_txt_eqn = '$R(T) = ae^{-bT}+c$'
fit_txt_params = 'n $a = {0:2.1f}$ n $b = {1:2.5f}$ n $c = {2:2.1f}$'.format(*fit_params)
fit_txt = fit_txt_eqn+fit_txt_params

fig1 = plt.figure(figsize=(15,9))
ax = fig1.gca()
plt.plot(temp_cal,resist_cal,marker='o',markersize=10,label='Data')
plt.plot(temp_cal,test_fit,marker='o',markersize=10,alpha=0.7,label='Fit (Error = {0:2.1f}%)'.format(mape))
plt.text(np.mean((temp_cal))
         ,np.mean((resist_cal)),fit_txt,size=20)
plt.title('NTC 3950 100k Thermistor Factory Calibration Plot and Fit')
plt.xlabel(r'Temperature [$^circ$C]',fontsize=16)
plt.ylabel(r'Resistance [$Omega$]',fontsize=16)
plt.legend()
#plt.savefig('thermistor_factory_fit.png',dpi=300,facecolor=[252/255,252/255,252/255])
plt.show()

## voltage divider selection for temperature ranges
#
#
fig2 = plt.figure(figsize=(15,9))
ax3 = fig2.add_subplot(1,1,1)

for T_2 in np.linspace(20.0,100.0,7):
    V_0 = 3.3
    T_1 = -40.0
    test_temps = np.linspace(T_1,T_2,10)
    R_2_1 = exp_func((T_1+T_2)/2.0,*fit_params)

    R_1 = R_2_1*((V_0/(V_0/2.0))-1)
    print(R_1)

    ## Thermistor test expectations with various voltage divider resistor values
    #
    #
    R_2 = exp_func(test_temps,*fit_params)
    V_2 = V_0*(1/(np.divide(R_1,R_2)+1))
    ax3.plot(test_temps,V_2,linewidth=4,label='R_1 = {0:2.0f}'.format(R_1))

ax3.set_ylabel('Thermistor Voltage Output [V]',fontsize=18)
ax3.set_xlabel('Temperature [$^circ$C]',fontsize=18)
plt.legend()
plt.title('Voltage Divider Resistor Selection Response Curves')
#plt.savefig('thermistor_resistor_selection.png',dpi=300,facecolor=[252/255,252/255,252/255])
plt.show()

Experiment s termistorem a Arduinem

Nyní, když máme vztah mezi napětím odečteným Arduinem a teplotou měřenou termistorem, a vybrali jsme náš odpor děliče napětí - nyní můžeme vyzkoušet, zda systém funguje a zda je náš algoritmus správný! Správná předpověď teploty ze známých výše uvedených parametrů je následující:

Pro tabulku dat termistoru používáme a, b, c z naší shody výše. Také jsme vybrali R1 na základě požadovaného teplotního rozsahu a V0 se nastavuje pomocí Arduina. Nakonec můžeme do rovnice zadat V2 jako proměnnou, která se čte z jednoho z analogových pinů. thermistor_wiring_no_dht22_fire_source

K výše uvedenému schématu zapojení lze učinit několik pozorování. První je, že mezi piny 3,3 V a GND je umístěn kondenzátor 10 uF. Je také důležité si uvědomit, že budeme používat externí referenci napětí pomocí 3,3V kolíku. A důvod je dvojí: očekávané napětí z termistoru bude v rozsahu 1,5 V a za druhé, 3,3 V pin má menší šum, takže naše hodnoty napětí budou stabilnější, což povede ke stabilnějším odečtům teploty. Arduino kód pro měření teploty pomocí našich derivací výše a zapojení na obrázku 4 je níže:

// Arduino code for use with NTC thermistor

#include <math.h>

#define therm_pin A0
float T_approx;

float V_0 = 3.3; // voltage reference

// first resistance value for voltage divider
float R_1 = 220000.0;
// fit coefficients
float a = 283786.2;
float b = 0.06593;
float c = 49886.0;

int avg_size = 10; // averaging size

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode(therm_pin,INPUT);

  // set analog reference to read AREF pin
  analogReference(EXTERNAL);
}

void loop() {

  // loop over several values to lower noise
  float T_sum = 0.0;
  for (int ii;ii<avg_size;ii++){
    // read the input on analog pin 0:
    int sensorValue = analogRead(therm_pin);
    // Convert the analog reading (which goes from 0 - 1023) to voltage reference (3.3V or 5V or other):
    float voltage = (sensorValue/1023.0)*V_0;

    // this is where the thermistor conversion happens based on parameters from fit
    T_sum+=(-1.0/b)*(log(((R_1*voltage)/(a*(V_0-voltage)))-(c/a)));
  }

  // averaging values from loop
  T_approx = T_sum/float(avg_size);

  // readout for Celsius and Fahrenheit
  Serial.print("Temperature: ");
  Serial.print(T_approx);
  Serial.print(" (");
  Serial.print((T_approx*(9.0/5.0))+32.0);
  Serial.println(" F)");

  delay(500);
}

Výše uvedený kód průměruje 10 teplotních odečtů pro stabilnější výstup a poskytuje odečet zhruba každých 500 ms v stupních Celsia i Fahrenheita. Parametry by měly být aktualizovány pro uživatelsky specifický termistor a průměrné množství lze také upravit na základě požadované stability uživatele.

KONDENZÁTOR MÁ ZA NÁSLEDEK VYHLAZENOU TEPLOTNÍ ODEZVU

Obrázek 5: Účinek vyhlazování kondenzátoru na ADC pro čtení termistorů.

Obrázek 5: Účinek vyhlazování kondenzátoru na ADC pro čtení termistorů.

 

V další části se porovná termistor se snímačem teploty a vlhkosti DHT22.

Jako jednoduchý test jsem se rozhodl zapojit snímač teploty a vlhkosti DHT22, abych zjistil, jak dobře termistorová rovnice přibližuje teplotu na základě jejího odporu. DHT22 je klasický snímač Arduino. Také jsem chtěl vidět jejich příslušné reakce, když se zvýší jejich okolní teploty, a sledovat reakci s časem, abych získal představu o tom, jak senzory fungují při aktivně se měnících teplotních scénářích.

Níže je uvedeno zapojení pro kombinaci termistoru a senzoru DHT22.

Obrázek 6: Zapojení pro srovnání mezi snímačem DHT22 a termistorem.

Obrázek 6: Zapojení pro srovnání mezi snímačem DHT22 a termistorem.

 

Níže je uveden také kód Arduino, který doprovází srovnání DHT22 a termistoru. Používá knihovnu „SimpleDHT“, kterou lze nainstalovat pomocí Správce knihoven.

#include <math.h>
#include <SimpleDHT.h>

#define therm_pin A0
#define pinDHT22 2

float T_approx;

float V_0 = 3.3;
float R_1 = 220000.0;
float a = 283786.2;
float b = 0.06593;
float c = 49886.0;

int avg_size = 50;

SimpleDHT22 dht22;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode(therm_pin,INPUT);
  analogReference(EXTERNAL);
}

// the loop routine runs over and over again forever:
void loop() {
  float T_sum = 0.0;
  for (int ii;ii<avg_size;ii++){
    // read the input on analog pin 0:
    int sensorValue = analogRead(therm_pin);
    // Convert the analog reading (which goes from 0 - 1023) to voltage reference (3.3V or 5V or other):
    float voltage = (sensorValue/1023.0)*V_0;

    T_sum+=(-1.0/b)*(log(((R_1*voltage)/(a*(V_0-voltage)))-(c/a)));
  }

  T_approx = T_sum/float(avg_size);

  Serial.print("Thermistor: ");
  Serial.print(T_approx);
  Serial.print(" (");
  Serial.print((T_approx*(9.0/5.0))+32.0);
  Serial.println(" F)");

  float temperature = 0;
  dht22.read2(pinDHT22, &temperature, NULL, NULL);
  Serial.print("DHT22: ");
  Serial.print((float)temperature); Serial.println(" *C, ");

  Serial.print("Difference: ");
  Serial.print(temperature-T_approx);
  Serial.println(" C");
  delay(500);
}

Výše uvedený kód vypočítá obě teploty a vytiskne je na sériový monitor každých 0,5 sekundy. Průměruje také každých 10 odečtů z termistoru. Kód také vytiskne rozdíl mezi oběma metodami teplotního senzoru. Níže jsem vykreslil teplotní rozdíl, abych ukázal průměrnou odchylku mezi termistorem a DHT22.
 

dht22_thermistor_screenshot_difference.png

Rozdíl mezi údaji teploty termistoru DHT22 a NTC

 

V průměru a v závislosti na skutečné teplotě může rozdíl přesáhnout 0,05 C - 1,5 C. A toto rozpětí je pravděpodobně způsobeno několika věcmi: ADC je na Arduinu poněkud hlučné, dokonce i s kondenzátorem a referenčním externím napětím 3,3 V - nemluvě o tom, že je to jen 10bitové; rovnice termistoru s sebou také nese určitou chybu-takže pro vysoce přesná měření by byla interpolace teplota podle teploty nejpřesnějším způsobem, jak zajistit kvalitní výsledky; a konečně, DHT22 navíc nese chybu 0,5 C, takže můžeme očekávat, že chyby mezi nimi budou až 2 C. Takže skutečnost, že mezi nimi vidíme pouze 0,5 C rozdíl, není špatná!

Abychom porovnali schopnosti těchto dvou senzorů, níže uvedený diagram ukazuje sílu termistoru a slabost DHT22:

 

DHT22 a Thermistor Dfiference Plot pro horký poryv

Rozdíl mezi DHT22 a termistorem během horkého poryvu

 

Na obrázku výše je snadné vidět sílu termistoru a jeho schopnost zvládnout rychle se měnící scénáře. DHT22 je vybaven tak, aby zvládl rychlost aktualizace 0,5 s, a ve skutečnosti dokáže vyřešit pouze okolní teploty, nikoli velké výboje horka nebo chladu. Níže uvedený graf skutečně ilustruje nedostatky ve schopnosti DHT22 zvládnout výbuchy teplotních změn. Termistory mají teplotní odezvy, které jsou poměrně rychlé, zatímco DHT22 trvá několik měření. DHT22 také potřebuje nějaký čas na zotavení z topného období, a to především kvůli jeho pouzdru a pomalé odezvě komponent.

Porovnání tepelné odezvy DHT22 a termistoru

Termistor a tepelné reakce DHT22

 

Termistor je jasným vítězem, když mají teplotní výkyvy pro měření velký význam. Proto se často používají v experimentech, kde teploty rychle kolísají a jsou zapotřebí přesná měření.

Závěr

V tomto článku jsou diskutovány termistory a jak je implementovat v Arduinu přizpůsobením továrně kalibrovaných dat, abychom získali přesné koeficienty pro zjištění teploty z odporu. Dále, jak použít dělič napětí k měření napětí jako funkce odporu vystupujícího z termistoru. A nakonec byl použit teplotní senzor DHT22 k porovnání přesnosti a výhod použití termistoru.

Termistory se používají v široké škále aplikací kvůli jejich přesnosti, vysoké odezvě v rychle se měnících prostředích a jejich levnému a snadno použitelnému hardwaru. Jednou z potíží s používáním termistorů je jejich nelineární repsonse, nicméně s kalibrací kvality a křivkami odezvy lze nelineární efekty zvládnout. Existuje mnoho dalších experimentů, které lze provést s termistory k analýze jejich časových reakcí, snížení nelineárních překážek a zkoumání účinků samovolného zahřívání. Tento projekt měl představit termistory a jejich teorii a zároveň zlepšit porozumění tomu, proč jsou skvělou volbou oproti jiným metodám snímání teploty.