Ukázka práce s termokamerou AMG8833 na ESP32. Data jsou odesílána sériovou linkou do počítače a následně vizualizována prostřednictvím Pythonu.
AMG8833 je 64 pixelový snímač teploty vyvinutý společností Panasonic v rámci produktové řady Grid-EYE®. Senzor obsahuje pole 8x8 infračervených termočlánků, které aproximují teplotu měřením infračerveného záření emitovaného z emisních těles. Grid-EYE komunikuje přes sběrnici I2C, díky čemuž je také kompatibilní s Raspberry Pi a Arduino. AMG8833 obsahuje integrovanou silikonovou čočku, která omezuje pozorovací úhel snímače na 60 stupňů, což má za následek oblast snímání užitečnou pro objekty ve středním poli (na rozdíl od vzdáleného nebo blízkého pole). Pracuje také při 3,3V a 5V, při vzorkovací frekvenci 1Hz-10Hz, s přibližným teplotním rozlišením 0,25°C v rozsahu 0°C až 80°C. AMG8833 je užitečný pro aplikace v termovizi, analýzách přenosu tepla, monitorování lidské teploty, řízení vytápění a klimatizace, průmyslovém řízení a dalších aplikacích při bezkontaktním měření teploty.
Záření šedého tělesa a Stefan-Boltzmannův zákon
AMG8833 pasivně měří tepelné záření z šedého těla vyzařujícího infračervené záření. Teplota se vypočítá pomocí Stefan-Boltzmannova zákona:
kde ε se nazývá emisivita (mezi 0 a 1), A je plocha povrchu, σ je Stefanova-Boltzmannova konstanta, T je teplota tělesa a Π je zářivý výkon. Typické infračervené teploměry (termopily termočlánků) měří radiační výkon, takže je potřeba přepočet na přibližnou teplotu. Jednoduchá rovnice pro aproximaci teploty šedého tělesa je následující:
kde V představuje napětí naměřené nezpracovaným senzorem. Proměnná k je empirická konstanta, která absorbuje A, ε, σ a elektronický šum, který může existovat. Ts je teplota samotného senzoru a zbývající Tobj je teplota měřeného objektu. Teplota senzoru je odečítána, aby bylo zajištěno, že teplota senzoru neovlivňuje měření teploty objektu. Aby se dosáhlo přesné předpovědi teploty, jsou tyto senzory často kalibrovány pomocí cílového materiálu při různých teplotách, aby byla zajištěna přesná hodnota k. A jakmile je toto hotovo, lze implementovat empirickou rovnici pro teplotu objektu:
Tato rovnice je „konečnou“ aproximací pro určení teploty šedého tělesa pomocí infračerveného detektoru [1].
Zapojení
Zapojení díky využití I2C si vystačíme se dvěma piny. Na desce ESP32, jsou to GPIO21 a GPIO22. Napájení je zapojeno standardně. V uvedeném schématu se využívá 3.3V.
Programování ESP32
Pro programování desky ESP32 k získání aktuálních dat ze senzoru AMG8833, lze využít knihovnu SparkFun_GridEYE_Arduino_Library.h. Tato knihovna jako jediná z několika odzkoušených fungovala velice dobře a je dostupná i v prostřexí VSC Platformio.IO.
#include <Wire.h>
#include <SparkFun_GridEYE_Arduino_Library.h>
GridEYE grideye;
void setup() {
Wire.begin();
grideye.begin();
Serial.begin(115200);
}
void loop() {
for(unsigned char i = 0; i < 64; i++){
Serial.print(grideye.getPixelTemperature(i));
Serial.print(",");
}
Serial.println();
delay(100);
}
Programový kód je velice jednoduchý. Nedělá nic než, že čte hodnoty z jednotlivých pixelů a dává je do odesílaného řetězce. Hodnoty jsou reprezentovány samotnou teplotou a jsou odděleny čárkou.
Interpolace IR kamery v reálném čase v Pythonu
Nyní, když je AMG8833 funkční, lze provádět jednoduché analýzy na výstupu infračerveného obrazu z pole termočlánků. Nejprve lze přidat interpolační rutinu pro vyhlazení infračerveného obrazu. V tomto zjednodušeném případě se k vyhlazení obrazu používá bikubická interpolace. Kód pro rutinu interpolace v reálném čase je uveden níže:
import matplotlib.pyplot as plt
import numpy as np
import serial
import time
from mpl_toolkits.axes_grid1 import make_axes_locatable
ser = serial.Serial('/dev/cu.wchusbserial110', 115200)
ser.flushInput()
ser_bytes = ser.readline()
# preallocating variables
norm_pix = []
cal_vec = []
kk = 0
cal_size = 10 # size of calibration
cal_pix = []
time_prev = time.time() # time for analyzing time between plot updates
def handle_close(evt):
raise SystemExit('Closed figure, exit program.')
def get_data():
global data, ser
line = ser.readline().decode().strip()
line = line.rstrip(line[-1])
temps = [float(t.strip() or 0) for t in line.split(",")]
data = np.array(temps).reshape((8, 8))
return data
plt.ion()
try:
while(1):
norm_pix = get_data()
if kk == 0:
print("Sensor should have clear path to calibrate against environment")
graph = plt.imshow(np.reshape(np.repeat(0,64),(8,8)), cmap=plt.cm.viridis , interpolation='bicubic')
plt.colorbar()
plt.clim(1, 8) # can set these limits to desired range or min/max of current sensor reading
plt.show()
plt.pause(0.0001)
if kk < cal_size + 1:
kk += 1
if kk == 1:
cal_vec = norm_pix
continue
elif kk <= cal_size:
for xx in range(0, len(norm_pix)):
cal_vec[xx] += norm_pix[xx]
if kk == cal_size:
cal_vec[xx] = cal_vec[xx] / cal_size
continue
else:
cal_pix = np.subtract(np.array(norm_pix), np.array(cal_vec))
if cal_pix.min() < 0:
for y in range(0, len(cal_pix)):
cal_pix[y] += abs(cal_pix.min())
# Moving Pixel Plot #
graph.set_data(np.reshape(cal_pix,(8,8))) # updates heat map in 'real-time'
plt.show() # plots updated heat map
plt.pause(0.0001)
cal_pix = [] # off-load variable for next reading
print(time.time() - time_prev) # prints out time between plot updates
time_prev = time.time()
except KeyboardInterrupt:
print("CTRL-C: Program Stopping via Keyboard Interrupt...")
exit()
finally:
print("Exiting Loop")
V první řadě musíme nahrát potřebné knihovny. To se děje v Pythonu standardním postupem:
import matplotlib.pyplot as plt
import numpy as np
import serial
import time
from mpl_toolk
Dále se přidá kód pro vykreslení, který uspořádá 64 naměřených teplot do obrazového pole rozloženého v mřížce 8x8. Python pak data interpoluje pomocí metody 'bicubic' (lze použít i lanczos). Tato metoda je v podstatě funkcí sin, ale důležitou součástí je, že tato konkrétní interpolace demonstruje nejlepší výsledky pro tepelné mapování. Dále nastavíme barevný pruh, limity teploty a požádáme program, aby nakreslil graf.
print("Sensor should have clear path to calibrate against environment")
graph = plt.imshow(np.reshape(np.repeat(0,64),(8,8)), cmap=plt.cm.viridis , interpolation='lanczos')
plt.colorbar()
plt.clim(1, 8) # can set these limits to desired range or min/max of current sensor reading
plt.show()
plt.pause(0.0001)
Dále se provede normalizace senzoru na okolní prostředí. Poznámka: tato metoda, která je zde uvedena, nebude poskytovat údaje o teplotě, ale spíše o teplotě nad okolním prostředím, když je senzor kalibrován s okolím. Metoda je určena k měření změn v IR záření.
for xx in range(0, len(norm_pix)):
cal_vec[xx] += norm_pix[xx]
if kk == cal_size:
cal_vec[xx] = cal_vec[xx] / cal_size
Následně se zajistí, že v grafu nebudou přítomny žádné záporné hodnoty, protože to způsobí nepravidelné výsledky během vykreslování. Tomu se zabrání přidáním nejnižší záporné hodnoty ke každému pixelu - to dá absolutní referenční rámec pro indikaci intenzity tepla.
cal_pix = np.subtract(np.array(norm_pix), np.array(cal_vec))
if cal_pix.min() < 0:
for y in range(0, len(cal_pix)):
cal_pix[y] += abs(cal_pix.min())
Nakonec se aktualizuje graf pomocí nových interpolovaných pixelů pomocí metody: graph.set_data().
graph.set_data(np.reshape(cal_pix,(8,8)))
plt.show() # plots updated heat map
plt.pause(0.0001)
cal_pix = [] # off-load variable for next reading
print(time.time() - time_prev)
time_prev = time.time()
Výsledek
V tuto chvíli by měla být funkční tepelná mapu pomocí AMG8833 a Raspberry Pi. Níže jsou dvě animace uvedeného experimentování s kódem a IR detektorem. Vlevo je použitá metodu interpolace na 'bicubic'. Vpravo je implementace 'lanczos' interpolační metody.
Druhým příkladem vizualizace tepelné mapy ze senzoru je využití podobné struktury kódu, ale s přispění trochu jiných knihoven. Tento příklad disponuje navíc dynamickou změnu teplotní rozsahu, která je znázorňována na stupnici.
import matplotlib.pyplot as plt
import numpy as np
import serial
import time
from scipy import interpolate
from math import trunc
ser = serial.Serial('/dev/cu.wchusbserial110', 115200)
ser.flushInput()
ser_bytes = ser.readline()
def handle_close(evt):
raise SystemExit('Closed figure, exit program.')
def get_data():
global data, ser
line = ser.readline().decode().strip()
line = line.rstrip(line[-1])
temps = [float(t.strip() or 0) for t in line.split(",")]
data = np.array(temps).reshape((8, 8))
return data
pix_res = (8, 8) # pixel resolution
xx,yy = (np.linspace(0, pix_res[0], pix_res[0]), np.linspace(0, pix_res[1], pix_res[1]))
zz = np.zeros(pix_res) # set array with zeros first
# new resolution
pix_mult = 10 # multiplier for interpolation
interp_res = (int(pix_mult*pix_res[0]), int(pix_mult*pix_res[1]))
grid_x, grid_y = (np.linspace(0, pix_res[0], interp_res[0]), np.linspace(0, pix_res[1], interp_res[1]))
# interp function
def interp(z_var):
# cubic interpolation on the image
# at a resolution of (pix_mult*8 x pix_mult*8)
f = interpolate.interp2d(xx, yy, z_var, kind='cubic')
return f(grid_x, grid_y)
grid_z = interp(zz) # interpolated image
norm_pix = get_data()
plt.rcParams.update({'font.size':12})
fig_dims = (10, 9) # figure size
fig, ax = plt.subplots(figsize=fig_dims) # start figure
fig.canvas.manager.set_window_title('AMG8833 Image Interpolation')
im1 = ax.imshow(grid_z, vmin=norm_pix.min(), vmax=norm_pix.max(), cmap=plt.cm.RdBu_r) # plot image, with temperature bounds
cbar = fig.colorbar(im1, fraction=0.0475, pad=0.03) # colorbar
cbar.set_label('Temperature [C]', labelpad=25) # temp. label
fig.canvas.draw() # draw figure
ax_bgnd = fig.canvas.copy_from_bbox(ax.bbox) # background for speeding up runs
fig.show() # show figure
def plotUpdate():
fig.canvas.restore_region(ax_bgnd) # restore background (speeds up run)
new_z = interp(np.reshape(get_data(), pix_res)) # interpolated image
im1.set_data(new_z) # update plot with new interpolated temps
im1.set_clim(vmin=new_z.min(), vmax=new_z.max()) # set bounds
cbar_ticks = np.linspace(new_z.min(), new_z.max(), num=10, endpoint=True).round(1)
cbar.set_ticks(cbar_ticks)
cbar.draw_all()
ax.draw_artist(im1) # draw image again
fig.canvas.blit(ax.bbox) # blitting - for speeding up run
fig.canvas.flush_events() # for real-time plot
plt.pause(0.0001)
try:
while True:
plotUpdate()
except KeyboardInterrupt:
print("CTRL-C: Program Stopping via Keyboard Interrupt...")
exit()
finally:
print("Exiting Loop")
Závěr
AMG8833 infračervené pole termočlánků je 64 pixelový (8x8) detektor, který aproximuje teplotu ze sálavých těles. Modul komunikuje přes sběrnici I2C na frekvenci 400 kHz, aby odeslal teplotu ze všech 64 pixelů volitelnou rychlostí 1-10 vzorků za sekundu. Přibližná teplota je vydávána s rozlišením 0,25 °C v rozsahu 0 °C až 80 °C. Infračervená kamera v reálném čase (IR kamera) byla představena jako způsob monitorování teploty pro aplikace počítání osob, přenos tepla elektroniky, monitorování vnitřního komfortu, průmyslové bezkontaktní měření teploty a další aplikace, kde může být vícebodové monitorování teploty užitečný. Přibližná chyba senzoru v jeho provozním rozsahu je 2,5 °C, což je zvláště užitečné pro aplikace s většími teplotními výkyvy.
Ke stažení:
Kód ESP32 , Python 1, Python II
Literatura:
[1] Heat Mapping with a 64-Pixel Infrared Detector (AMG8833), Raspberry Pi, and Python. Maker Portal [online]. Maker Portal, 2018 [cit. 2022-09-28]. Dostupné z: https://makersportal.com/blog/2018/1/25/heat-mapping-with-a-64-pixel-infrared-sensor-and-raspberry-pi