En esta nota podés ver como creé un modelo de aprendizaje automático de detección de anomalías con Edge Impulse basado en imágenes térmicas, con datos enviados por celular a la nube a través de Notecard.
Historia
Cuando digo «detección de anomalías», se puede imaginar un proceso demasiado complicado, algo exclusivo de los algoritmos de aprendizaje profundo y la codificación indescifrable. En realidad, el concepto de descubrir un comportamiento anómalo en un sistema es solo el acto de identificar un estado incierto o no clasificado.
En el proyecto de Machine Learning que publiqué en el artículo: «INFRACCION. Detector de excesos de velocidad de vehículos«, creé modelos de ML con diferentes entradas de datos y les pedí que generaran inferencias basadas en los datos conocidos proporcionados. Pero, ¿qué sucede cuando le pido al modelo que cree una inferencia sobre algo que no sabe?
Esto, en esencia, es lo que traté de lograr en este proyecto.
Creé un modelo ML de detección de anomalías con Edge Impulse que procesa imágenes térmicas para detectar estados desconocidos de lecturas térmicas y transmite los datos recopilados a la nube con Blues Wireless Notecard.
Si bien construí esto alrededor del sistema de calefacción de mi hogar, este tipo de solución podría usarse para monitorear prácticamente cualquier tipo de equipo que produzca calor.
Mediante el uso de las capacidades «edge ML» proporcionadas por Edge Impulse, la bomba de datos del dispositivo a la nube de Notecard celular de Blues Wireless, una Raspberry Pi Zero 2 y un panel de control de Ubidots, pude crear un dispositivo simple, de bajo consumo y estación de monitoreo térmico con solo un poco de codificación Python requerida.
El Problema a Resolver
En pocas palabras, cualquier maquinaria que genere una cantidad significativa de calor corre el riesgo de fallar catastróficamente. Incluso los sistemas de calefacción domésticos queman más de una cantidad insignificante de petróleo o gas natural al día. El riesgo de falla puede ser muy bajo, pero si escala a un entorno industrial, una falla puede ser devastadora.
Pero hoy vamos a reducir la escala a mi sistema de caldera de agua caliente, en el que recientemente reemplacé la caldera con esta elegante bestia:
¿Qué podría salir mal con una caldera? Como propietario de una casa de más de 100 años, le ahorraré los detalles, pero no hace falta decir que hay mucha complejidad y muchas tuberías que entran y salen de la caldera que podrían monitorearse. Un ejemplo de «problema» es una válvula de liberación de presión de agua que nunca debería abrirse realmente:
Si es así, es una señal de algún problema interno que únicamente mi compañía de HVAC debería abordar. Así que me dispuse a construir un sistema que pudiera monitorear las lecturas térmicas de la mayoría de las tuberías que ve y advertirme si aparecían «puntos calientes» donde no deberían.
NOTA: ¿Viste todas las tuberías de cobre? El cobre tiene un índice de emisividad extremadamente bajo, lo que dificulta su lectura en una cámara térmica. Afortunadamente, hay suficientes componentes de hierro mezclados para que este proyecto funcione lo suficientemente bien como para mi prueba de concepto.
Las piezas del rompecabezas
Con el problema bastante bien entendido, era hora de juntar las piezas del rompecabezas de hardware que necesitaba para que esto funcionara.
Dentro del sótano de una casa de más de 100 años, el acceso Wi-Fi es irregular en el mejor de los casos y no hay suficientes tomacorrientes estándar para alimentar cualquier dispositivo sin usar un cable de extensión largo. Por lo tanto, construir una solución de bajo consumo con LTE-M celular estaba en la parte superior de mi lista.
Empecé con Blues Wireless Notecard, que brinda acceso celular global prepago que incluye 500 MB de datos y 10 años de servicio. El modelo que elegí funciona con los protocolos LTE-M y NB-IoT, por lo que incluso con solo «una barra» de servicio celular en mi sótano, había suficiente ancho de banda para enviar los datos que necesitaba a la nube. También es un dispositivo de muy bajo consumo de energía por una suma de ~ 8uA cuando está inactivo.
A continuación, necesitaba una cámara termográfica. Elegí la cámara térmica IR MLX90640 de Adafruit . Genera una imagen térmica con una resolución de 32×24 que, aunque no parezca gran cosa, puede proporcionar una imagen térmica razonablemente precisa:
A continuación, necesitaba una cámara termográfica. Elegí la cámara térmica IR MLX90640 de Adafruit. Genera una imagen térmica con una resolución de 32×24 que, aunque no parezca gran cosa, puede proporcionar una imagen térmica razonablemente precisa:
Elegir un microcontrolador host o una computadora de placa única se convirtió en el siguiente obstáculo. Y debo confesar que fue dictado en gran medida por el ejemplo exclusivo de Linux proporcionado por Adafruit para la cámara térmica.
Elegí una Raspberry Pi Zero 2 W, ya que es probablemente la mejor opción para la informática de bajo consumo que todavía es compatible con una distribución completa de Linux (Raspbian).
El Notecarrier-Pi HAT de Blues Wireless hace que agregar datos móviles a cualquier Raspberry Pi sea muy sencillo:
Para una fuente de alimentación opté por un gran banco de energía USB-C de 30 000 mAh. Tiempo de confesión: no hice un buen trabajo al medir el consumo de energía, pero funcionó lo suficientemente bien como para mantener mi Pi funcionando durante unos días, con energía de sobra.
Entonces llegó el momento de comenzar el ensamblaje de un tipo diferente: construir un modelo ML desde cero y conectarlo con algún código de Python y servicios en la nube.
Construcción de un modelo térmico 🔥 TinyML
Lo que al principio puede parecer el aspecto más intimidante de todo este proyecto (construir un modelo de Machine Learning desde cero) posiblemente resultó ser el más fácil gracias a Edge Impulse .
Edge Impulse es una plataforma de desarrollo utilizada para construir e implementar modelos ML en dispositivos informáticos de borde.
Así es como construí un modelo ML «pequeño» en muy poco tiempo, utilizando la interfaz de usuario basada en web proporcionada por Edge Impulse Studio para crear un nuevo proyecto de clasificación de imágenes
NOTA: Si bien Edge Impulse admite la creación de verdaderos modelos de detección de anomalías con datos de audio o acelerómetro, aún no admiten imágenes. ¡Es por eso que me quedé con un modelo de clasificación de imágenes para construir más un modelo de detección de anomalías de prueba de concepto!
1) Adquisición de datos
Con mi hardware ensamblado, al menos estaba listo para comenzar a tomar fotografías con la cámara térmica y guardarlas en la RPi Zero. Al configurar una función de Python simple para que se ejecutara cada 10 minutos, generé un conjunto de ~120 imágenes que eran una buena aproximación de los diferentes estados del funcionamiento adecuado de la caldera (por ejemplo, frío, tibio y caliente).
Tenga en cuenta que el código de Python para generar imágenes se presenta un poco más abajo en este tutorial, pero también está disponible en este repositorio de GitHub .
Con el conjunto completo de imágenes cargadas en mi proyecto en el estudio Edge Impulse, tuve que etiquetar las imágenes individuales:
«Frío» – lo que significa que el sistema no está encendido en absoluto:
«Caliente»: significa que el sistema se está calentando o enfriando:
«Caliente» – lo que significa que el sistema está calentando activamente la casa:
Y también «Anomalía», lo que significa que se reconoció un estado anómalo (es decir, incierto o desconocido).
Las imágenes se cargaron en el estudio Edge Impulse, en la pestaña Adquisición de datos:
Pero espera, ¿cómo podría identificar estados anómalos sin registrar ningún comportamiento anómalo? ¡A través de la magia de Photoshop, por supuesto!
Al iniciar este proyecto, sabía que quería equiparar el comportamiento anómalo con puntos calientes que ocurrían lejos de cualquier punto caliente conocido (como el área de alivio de presión antes mencionada). Al ‘comprar en algunos puntos calientes, creé un conjunto adicional de imágenes que podrían representar anomalías, dándome un conjunto de cuatro estados:
2) Crea un Impulso
En Edge Impulse, un impulso «toma datos sin procesar, usa el procesamiento de señales para extraer características y luego usa un bloque de aprendizaje para clasificar nuevos datos». Mi impulso estaba compuesto por los siguientes bloques de procesamiento y aprendizaje:
3) Entrena al modelo
Usando las opciones predeterminadas proporcionadas, entrené mi modelo ML en función del conjunto de datos y la configuración mencionados anteriormente. El resultado inicial de este proceso me mostró que mi modelo iba a ser notablemente preciso (la prueba se basa en la preasignación de Edge Impulse de un subconjunto de imágenes a un conjunto de datos de «entrenamiento»).
Un fantástico «explorador de características» también lo ayuda a identificar cualquier imagen mal etiquetada antes de utilizar el modelo en cualquier entorno del mundo real.
¡Y eso fue todo lo que necesité para inicializar y entrenar un modelo TinyML con Edge Impulse!
A continuación, necesitaba configurar Raspberry Pi Zero, instalar algunos paquetes de software necesarios, descargar el modelo ML y ¡escribir algo de Python para conectarlo todo!
Configurando el Pi
Cada vez que comienzo un nuevo proyecto en mi Pi, me gusta asegurarme de que todos los paquetes instalados estén actualizados con:
sudo apt update
sudo apt full-upgrade
sudo apt clean
Configuración del Pi para el aprendizaje automático
Luego necesitaba instalar algunas dependencias para usar el corredor Edge Impulse en el Pi. Tenga en cuenta que las instrucciones proporcionadas son para Raspberry Pi 4, ¡pero funcionaron sin problemas para mí en Pi Zero 2!
Para conectar este dispositivo a Edge Impulse (que es necesario para descargar el archivo de modelo ML generado), necesitaba ejecutar:
edge-impulse-linux --disable-camera
La --disable-camera
bandera se usa si su Pi no tiene un módulo de cámara conectado.
Instalé el SDK de Linux Python para Edge Impulse. Por alguna razón, en Pi Zero tuve que instalar las dependencias enumeradas en la sección «Raspberry Pi» por separado, así que YMMV 🤷.
El último paso de Edge Impulse consistió en ejecutar este comando para compilar y descargar el archivo del modelo de Machine Learning ( modelfile.eim
) a la RasPi:
edge-impulse-linux-runner --download modelfile.eim
Sabía que todo estaría bien en el mundo cuando vi este resultado en la terminal:
[RUN] Downloading model...
Configuración del Pi para la cámara térmica
Un truco con el uso del MLX90640 en Raspberry Pi es la adafruit_blinka
biblioteca. Esto traduce la API de hardware de CircuitPython a cualquier biblioteca que proporcione Pi. ¡La ventaja aquí es que cualquier código que se ejecute en CircuitPython se ejecutará sin problemas en una Raspberry Pi!
Puede (y debe) revisar las instrucciones de configuración completas proporcionadas por Adafruit. Sin embargo, se pueden reducir a los siguientes comandos de terminal:
cd ~
sudo pip3 install --upgrade adafruit-python-shell
wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py
sudo python3 raspi-blinka.py
Luego adafruit-circuitpython-mlx90640
se puede instalar a través de pip:
sudo pip3 install adafruit-circuitpython-mlx90640
Configuración del Pi para celular
Las únicas otras dependencias que se instalaron fueron Python SDK para Notecard celular (y python-periphery
para que Pi se comunique con Notecard a través de I2C):
pip3 install note-python python-periphery
Todo el Python Codez 🐍
Con el hardware ensamblado, el modelo TinyML creado y las dependencias de software instaladas, era hora de escribir algo de Python.
NOTA: Todo el código utilizado para este proyecto está disponible en este repositorio de GitHub.
El primer Python que «escribí» fue en realidad, ejem, tomado de Adafruit y ligeramente editado. Ese es un script utilizado para acceder a la cámara térmica, generar una imagen codificada por colores y guardarla en el sistema de archivos. Aquí hay una versión muy abreviada del código:
térmica.py
MINTEMP = 20.0 # low range of the sensor (deg C)
MAXTEMP = 40.0 # high range of the sensor (deg C)
COLORDEPTH = 1000 # how many color values we can have
INTERPOLATE = 20 # scale factor for final image
mlx = adafruit_mlx90640.MLX90640(board.I2C())
def takePicture():
# get sensor data
frame = [0] * 768
success = False
while not success:
try:
mlx.getFrame(frame)
success = True
except ValueError:
continue
# create the image
pixels = [0] * 768
for i, pixel in enumerate(frame):
coloridx = map_value(pixel, MINTEMP, MAXTEMP, 0, COLORDEPTH - 1)
coloridx = int(constrain(coloridx, 0, COLORDEPTH - 1))
pixels[i] = colormap[coloridx]
# save to file
img = Image.new("RGB", (32, 24))
img.putdata(pixels)
img = img.transpose(Image.FLIP_LEFT_RIGHT)
img = img.resize((32 * INTERPOLATE, 24 * INTERPOLATE), Image.BICUBIC)
ts = str(int(time.time()))
filename = "ir_" + ts + ".jpg"
img.save("images/" + filename)
return filename
Tenía que asegurarme de establecer las constantes adecuadas , MINTEMP
ya MAXTEMP
que esto proporcionaba la mejor gama de colores fríos/calientes según el escenario que estaba midiendo.
Como puede ver arriba, cuando takePicture
se ejecuta el método, se genera una imagen térmica y se guarda como jpeg en un images
subdirectorio con una marca de tiempo.
El otro script de Python importante en esta aplicación es main.py
el que realiza las siguientes funciones cada 5 minutos :
1) Llame al takePicture
método thermal.py
y cargue la imagen en un cv2
objeto de imagen:
filename = thermal.takePicture()
img = cv2.imread("images/" + filename)
2) Procese la imagen con Edge Impulse Linux runner para generar una inferencia de clasificación (por ejemplo, anomalía, frío, tibio, caliente):
features = runner.get_features_from_image(img)
res = runner.classify(features)
if "classification" in res["result"].keys():
print('Result (%d ms.) ' % (
res['timing']['dsp'] + res['timing']['classification']), end='')
El print
comando en este código genera una cadena como esta:
Result (65 ms.) anomaly: 0.12 cold: 0.00 hot: 0.88 warm: 0.00
3) Almacene las inferencias generadas (clasificaciones de imágenes) en JSON y sincronícelas con la nube usando Notecard celular:
note_body = {}
for label in labels:
score = res['result']['classification'][label]
print('%s: %.2f\t' % (label, score), end='')
note_body[label] = round(score, 4)
print('', flush=True)
note.add(nCard,
file="thermal.qo",
body=note_body)
Por ejemplo, el note_body
objeto podría verse así, con una imagen que representa un estado «frío»:
{
"anomaly": 0.0,
"cold": 0.99850000000000019,
"hot": 0.0,
"warm": 0.0015
}
En el código anterior mencioné que «sincronizo [los datos JSON] con la nube». Veamos adónde van exactamente esos datos.
Creación de un panel de control en la nube ☁️
Dado que Notecard es una bomba de datos del dispositivo a la nube, no vive en la Internet pública (lo que la convierte en un dispositivo extremadamente seguro) y, por lo tanto, necesita un proxy con el que sincronizar los datos. ¡Ingrese a Blues Wireless Notehub!
Notehub es un servicio de nube delgada que acepta de forma segura datos de la Notecard celular (fuera de la Internet pública, usando túneles VPN privados) y luego enruta instantáneamente los datos al proveedor de la nube de su elección (ya sea AWS, Azure, Google Cloud o cualquier otro). servicio optimizado para IoT como Ubidots, Datacake, Losant y otros).
¿ Recuerdas el note.add
comando usado main.py
arriba?
note.add(nCard,
file="thermal.qo",
body=note_body)
El JSON creado ( note_body
), se guarda en la Notecard como una Nota. ¿ Ves el file
parámetro? Eso es un archivo de notas: un archivo de notas puede almacenar varias notas. Tan pronto como la Notecard establece una conexión con Notehub, los archivos de notas almacenados se sincronizan con la nube junto con algunos datos de la sesión:
NOTA: (Juego de palabras, ¡ja!) Las API de Notecard y Notehub están basadas en JSON. Obtenga un adelanto de la API y vea todos los recursos para desarrolladores proporcionados en dev.blues.io.
Con mis datos sincronizados con Notehub, era hora de darle sentido y crear un tablero donde pudiera ver una representación visual de los datos. En este caso, usé Ubidots, que he usado varias veces en el pasado para crear tableros realmente atractivos:
Siguiendo el completo tutorial de enrutamiento proporcionado por Blues Wireless, creé una ruta simple de Notehub a Ubidots pasando una URL de punto final, un encabezado de autorización y una expresión JSONata para transformar mis datos sobre la marcha:
{
"cold": {"value": body.cold, "timestamp": when * 1000},
"warm": {"value": body.warm, "timestamp": when * 1000},
"hot": {"value": body.hot, "timestamp": when * 1000},
"anomaly": {"value": body.anomaly, "timestamp": when * 1000}
}
La expresión anterior convirtió una parte relativamente grande de JSON…
{
"event": "5ffa7e24-c559-40a0-8228-46ff8426b387",
"session": "9afdece2-9fd2-48ff-8e7d-5e66ce118b3c",
"best_id": "dev:864475",
"device": "dev:864475",
"product": "product:com.blues.xxx:xxx",
"received": 1644340365.888508,
"routed": 1644340370,
"req": "note.add",
"when": 1644340339,
"file": "thermal.qo",
"body": {
"anomaly": 0.0,
"cold": 0.99850000000000019,
"hot": 0.0,
"warm": 0.0015
},
"best_location_type": "tower",
"best_lat": 43.073787499999995,
"best_lon": -89.44367187499999,
"best_location": "Shorewood Hills WI",
"best_country": "US",
"best_timezone": "America/Chicago",
"tower_when": 1644340365,
"tower_lat": 43.073787499999995,
"tower_lon": -89.44367187499999,
"tower_country": "US",
"tower_location": "Shorewood Hills WI",
"tower_timezone": "America/Chicago",
"tower_id": "310,410,17169,77315601"
}
…en la siguiente carga útil optimizada para Ubidots:
{
"cold": {"value": 0.99850000000000019, "timestamp": 1644340339000},
"warm": {"value": 0.0015, "timestamp": 1644340339000},
"hot": {"value": 0.0, "timestamp": 1644340339000},
"anomaly": {"value": 0.0, "timestamp": 1644340339000}
}
Obtenga más información sobre JSONata en una guía proporcionada por Blues Wireless.
En el lado de Ubidots, vi que los datos aparecían casi instantáneamente, lo que me permitió crear un gráfico de líneas simple pero efectivo de los estados registrados de las imágenes térmicas:
¡Alerta! 🚨 ¡Anomalía detectada!
¡¿Qué es un sistema de detección de anomalías sin alertas en tiempo real?!
Notehub también proporciona una integración con Twilio para permitir el envío de mensajes de correo electrónico o SMS cuando se cumple una condición específica. En mi caso, solo quería recibir una alerta si mi sistema detectaba una anomalía (un mensaje SMS funciona bien en este caso).
Para enviar un SMS con Twilio, simplemente tuve que crear una nueva ruta en Notehub siguiendo el tutorial de enrutamiento de Twilio. Sin embargo, solo quería que esta ruta se activase en determinadas circunstancias (por ejemplo, cuando se detectaba una anomalía).
Al extender el código existente provisto en main.py
, creé una declaración condicional y activé la otra ruta, así, cuando se encontró una etiqueta de anomalía con un valor> 0.7:
if anomaly > 0.7:
note.add(nCard,
file="alert.qo",
body=note_body)
Nuevamente, puede seguir la guía de enrutamiento de Twilio proporcionada para obtener más información sobre el envío de mensajes SMS a través de Notehub.
Sé el primero en comentar