ESP32 a termokamera AMG8833

19.10.2022 Arduino #esp32 #python #termokamera

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:

Π = εAσT4

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í:

V ≈ k(Tobj4-Ts4)

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:

Tobj ≈ (V/k+Ts4)1/4

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.

bicubic    lanczos 

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")

seocn-cubic 

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