Energías AlternasEA·Lab
← ProyectosProyecto

GCU Sentinel

por Kalev Pineda ·

Energía EólicaIngeniería InversaSistemas IoT e Inteligentes
GCU Sentinel

Monitoreo térmico autónomo de la unidad central de control (GCU) en aerogeneradores

GCU Sentinel es una plataforma de inspección termográfica continua para la GCU (unidad central de control del generador): los paneles y gabinetes de control ubicados en el pie de la torre del aerogenerador. Una cámara térmica montada sobre un servo recorre ambos paneles, detecta puntos calientes en tiempo real, se centra sobre la anomalía, captura evidencia radiométrica y la envía a la nube, donde un dashboard permite analizarla, visualizarla en 3D y apoyarse en IA para el diagnóstico.

El sistema se construye sobre HeatStream: el servicio en C++ para Raspberry Pi que lee el buffer crudo de la cámara térmica y publica la matriz de temperaturas por ZeroMQ PUB/SUB. GCU Sentinel es uno de los suscriptores de ese flujo: lo consume para detectar y alertar sobre puntos calientes.


Problemática

La GCU concentra, en gabinetes eléctricos en el pie de la torre, los elementos que gobiernan y protegen la turbina: controlador (PLC), fuentes de alimentación, interruptores, contactores, relevadores, borneras, cableado de potencia y de control, y el sistema de ventilación del gabinete. Es un punto crítico: si la GCU falla, la turbina se detiene, aunque el rotor, el generador y la red estén en perfecto estado.

Por qué es un punto débil:

  • Carga cíclica y variable. La potencia de un aerogenerador cambia con el viento minuto a minuto. Conductores, contactores y borneras pasan por ciclos térmicos constantes que dilatan y contraen las conexiones y, con el tiempo, las aflojan.
  • Una conexión floja es un punto caliente. Un borne mal apretado, un contacto desgastado o un terminal oxidado aumentan la resistencia de contacto. Más resistencia produce más calor, y más calor acelera la oxidación: un ciclo que termina en arco eléctrico, conductores quemados o incendio del gabinete.
  • Enfriamiento deficiente. Ventiladores dañados o filtros saturados de polvo elevan la temperatura interna de todo el gabinete y reducen la vida útil de fuentes, electrónica y aislamientos.
  • Las fallas avisan antes de ocurrir. Casi todas empiezan como zonas con temperatura anómala, detectables por termografía mucho antes de un disparo o una falla catastrófica. Por eso la termografía es una práctica estándar de mantenimiento predictivo de tableros eléctricos (NFPA 70B).
  • Ubicación de difícil acceso. Los parques están en zonas remotas; la inspección termográfica manual es periódica, cara y solo captura un instante. Un punto caliente que aparece entre dos inspecciones pasa desapercibido.
  • Tiempo muerto costoso. Cada falla de la GCU implica turbina detenida, traslado de personal técnico y pérdida de generación.

Objetivo del proyecto: sustituir la inspección puntual por vigilancia térmica continua, autónoma y de bajo costo de los paneles de la GCU, que detecte el punto caliente cuando aparece, lo documente con datos radiométricos reales (no solo una foto) y lo notifique de inmediato.


Repositorios que componen el sistema

RepositorioRol en el sistemaTecnología
HeatStream (thermal_daemon)Adquisición: lee la cámara térmica y publica la matriz °C por ZeroMQC++17, libuvc, ZeroMQ
gsu-sentinel-coreOrquestador en el borde: consume el stream térmico, detecta puntos calientes, dirige la cámara, captura evidencia y la sube a la nubeRust, Tokio, ZeroMQ, MQTT
gnu_camera_controlControl de movimiento: firmware de la ESP32 que estabiliza el servo SG90 con un PID propio y ejecuta el paneo entre GCUC, ESP-IDF, FreeRTOS, MQTT, Julia (simulación)
gnu_sentinel_cloudBackend en la nube: recibe telemetría y evidencias, guarda capturas .npz, expone la API y distribuye la configuración remotaRust, Axum
gnu_dashboardCentro de mando web: estado en vivo, análisis de capturas (2D, 3D, IA), descargas y configuraciónReact, TypeScript, Vite, Three.js, Recharts, Gemini

Algunos repositorios conservan en su nombre los prefijos gsu/gnu de versiones anteriores del proyecto; todos forman parte de GCU Sentinel.


Arquitectura general

Principio de diseño: cada capa está desacoplada por un protocolo estándar.

  • ZeroMQ PUB/SUB separa la adquisición del análisis. ThermaCast no sabe quién consume los datos, y el Core podría reemplazarse o duplicarse sin tocar la captura.
  • MQTT separa la decisión (Core) de la actuación (ESP32).
  • HTTP separa el borde de la nube. Si se pierde internet, el borde sigue inspeccionando.

Flujo de funcionamiento

Máquina de estados del orquestador (gsu-sentinel-core)

  1. Patrullaje (Panning). El Core mueve la cámara de un panel al otro en pasos de pan_step_degrees, publicando el setpoint por MQTT cada ≥50 ms. En cada extremo espera scan_wait_time_sec (IdleAtEndpoint).

  2. Detección. Con cada frame recibido por ZeroMQ (~25 FPS) calcula la temperatura máxima de la matriz. Si supera max_temp_trigger, pasa a Tracking.

  3. Seguimiento visual (Tracking). Localiza la columna del píxel más caliente y calcula cuánto debe girar la cámara para centrarlo:

    rust
    // 4.5 px por grado: calibración del campo de visión de la cámara
    correction = -(hotspot_x - width/2) / PIXELS_PER_DEGREE;
    angle += correction * 0.4;   // ganancia < 1 para no oscilar

    Es un lazo de visual servoing: la cámara térmica funciona como sensor de posición del objetivo.

  4. Captura de evidencia (CollectingData). Con el punto caliente centrado, acumula 3 s de frames, guarda el frame central como matriz radiométrica .npz (float32 en °C, no una imagen) y la sube a la nube con el ángulo y el ID de turbina.

  5. Telemetría y configuración remota. Cada segundo envía un heartbeat con modo, ángulo y temperatura máxima. La nube responde con la configuración vigente (umbral, tiempos, paso, ON/OFF), así que un cambio hecho en el dashboard llega al borde en ≤1 s sin abrir puertos entrantes en la turbina.

Secuencia de una alerta


Ingeniería inversa

El SG90 es un micro-servo de juguete: engranes de nylon, un potenciómetro de carbón y un chip de control analógico. Tiene una sola entrada (el ancho de pulso PWM) y ninguna salida: el microcontrolador ordena una posición y supone que el servo llegó. Para apuntar una cámara térmica a un transformador a varios metros, ese supuesto no alcanza.

1. Diagnóstico: por qué falla la implementación común

La forma habitual de usar un SG90 (la primera versión del firmware, en el README del repo) es mandar el PWM del ángulo deseado y esperar:

c
set_servo_pwm(ANGLE_LEFT_GSU);            // "ve a 125°"
vTaskDelay(pdMS_TO_TICKS(SCAN_INTERVAL_MS)); // ...y confía

Con la cámara montada, este lazo abierto presenta cuatro problemas:

ProblemaOrigen físicoEfecto en la inspección
Banda muertaEl chip interno ignora errores pequeños (~1–2°) para no vibrarLa cámara se detiene cerca del objetivo, no en él
Error estacionario bajo cargaEl lazo interno es solo proporcional; el peso y el cable USB de la cámara ejercen un par constanteDesviación sistemática que cambia según hacia dónde gire
SobreimpulsoGanancia interna alta y poca amortiguaciónLa cámara se pasa del objetivo y regresa, y las primeras capturas salen movidas
Holgura de engranes (backlash)Juego mecánico entre engranes de nylonLa posición final depende de la dirección de llegada

El sistema no tiene forma de enterarse de nada de esto, porque no mide la posición real.

2. El hack de hardware: dar al servo un sentido del tacto

Dentro del SG90, el potenciómetro acoplado al eje de salida es el sensor de posición que usa el chip interno. La modificación consistió en:

  1. Abrir el servo y localizar las tres terminales del potenciómetro.
  2. Soldar un cable al cursor (wiper), la terminal central, cuyo voltaje es proporcional al ángulo real del eje.
  3. Llevar esa señal a la ESP32, al GPIO 34 (ADC1_CH6), configurado a 12 bits (0–4095) con atenuación de 11 dB para cubrir el rango de voltaje.
c
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);

float read_angle_sensor() {
    int raw = adc1_get_raw(ADC1_CHANNEL_6);
    return ((float)raw / 4095.0f) * 180.0f;   // calibración lineal ADC → grados
}

Con esto, un servo de lazo abierto se convierte en un actuador con retroalimentación de posición, como un servo industrial, sin cambiar de hardware.

3. Modelado antes de programar

Antes de escribir el controlador se modeló la planta en Julia (DifferentialEquations.jl), en tres niveles de detalle (carpeta sim/):

ModeloArchivoEcuación
Servo comercial como sistema de 2.º ordenservo2.jlθ̈ = ωₙ²(θ_ref − θ) − 2ζωₙ θ̇ con ωₙ = 12 rad/s, ζ = 0.7
Motor DC mecánico sin control internoservo3.jlJ θ̈ + b θ̇ = Kₘ·u
Motor DC electromecánico completoservo_dynamic.jlJ ω̇ = K·i − B·ω ; L i̇ = u − R·i − K·ω

Las simulaciones sirvieron para elegir el periodo de muestreo (20 ms), comprobar que el PID digital con saturación y anti-windup converge en trayectorias multi-escalón (45° → 90° → 20° → 120°), y ver en el espacio de fases cómo cambia la trayectoria del estado.

4. El controlador: PID externo + feed-forward

El firmware final (main/main.c) cierra un segundo lazo por fuera del lazo interno del servo:

  • Feed-forward: traduce directamente el ángulo deseado al pulso nominal (0° = 500 µs, 180° = 2400 µs → 10.55 µs/°). Hace el ~95 % del trabajo de inmediato.
  • PID: solo corrige lo que el feed-forward no puede prever (banda muerta, carga, holgura). El término integral acumula el error residual hasta "empujar" el comando más allá de la banda muerta del chip interno, y así elimina el error estacionario que el lazo interno (solo proporcional) no puede eliminar.
  • Anti-windup: la integral se limita a ±100 para que un recorrido largo (55° → 125°) no la cargue y provoque sobreimpulso al llegar.
  • Tiempo real: el PID corre en una tarea de FreeRTOS con vTaskDelayUntil a exactamente 50 Hz (el mismo periodo del PWM del servo) y prioridad 5. La lógica de negocio (auto/manual) corre en otra tarea con prioridad 4, y la comunicación MQTT nunca retrasa el lazo de control.
c
float feed_forward = 500.0f + (target_setpoint * 10.55f);
float final_signal = feed_forward + output;           // output = P + I + D
uint32_t duty = (uint32_t)((final_signal / 20000.0f) * 8191.0f);  // µs → duty 13 bits

5. Resultado: diagrama de fases

El diagrama de fases (posición θ contra velocidad ω) muestra la trayectoria completa del estado del servo. Un buen controlador describe una curva limpia que termina exactamente en el punto objetivo (ω = 0, θ = θ*). Uno deficiente se pasa, da vueltas alrededor del objetivo o se queda a un lado.

Diagrama de fases comparativo

Simulación del ciclo de inspección 55° ↔ 125° con un modelo del SG90 que incluye banda muerta, fricción de Coulomb, par de carga de la cámara, holgura de engranes, ruido del potenciómetro y cuantización del ADC.

Métrica (llegada al panel GCU 1, 125°)Implementación comúnControlador propio
Error estacionario medio1.07°0.30°
Sobreimpulso2.32°0.31°

Cómo leer la figura:

  • Panel A (común): la trayectoria se pasa del objetivo (el lazo se abre más allá de 125°) y vuelve para quedarse a un lado del punto objetivo, dentro de la banda muerta. Ahí se queda: nada lo corrige.
  • Panel B (controlador propio): la llegada tiene un sobreimpulso mínimo, y el término integral hace pequeñas correcciones finales que llevan la trayectoria hasta el punto objetivo.
  • Panel C: en el tiempo, la curva común se asienta visiblemente por debajo del setpoint; la del controlador queda encima de él.

¿Por qué importa 1° en este proyecto? Con la calibración de la cámara (4.5 px/°), 1° de error equivale a ~4–5 píxeles térmicos: suficiente para que un borne caliente quede fuera del centro, que el lazo de seguimiento visual del Core oscile, o que dos capturas del mismo GCU no sean comparables entre sí.

Simulaciones originales del repositorio (Julia):

Servo sin lazo externo (servo2.jl)PID multi-escalón (servo3.jl)
Fase servoFase PID

Protocolos e interfaces

ZeroMQ — ThermaCast → Core (tcp://127.0.0.1:5555)

Mensaje multiparte en little-endian:

ParteContenido
0 · Header (≥ 20 B)u64 timestamp · i32 filas · i32 columnas
1 · Payloadfloat32[filas × columnas] en °C (192 × 256 × 4 = 196 608 B)

MQTT — Core ↔ ESP32 (Mosquitto :1883)

TópicoDirecciónPayloadUso
gsu/control/setpointCore → ESP32"97.4"Ángulo objetivo (paneo y seguimiento)
gsu/control/mode→ ESP32auto / manualRutina automática o control manual
gsu/control/manual→ ESP32left / rightPaso de ±5° en modo manual
gsu/data/statusESP32 →1 / 2Panel GCU que se está observando

HTTP — Borde ↔ Nube (gnu_sentinel_cloud, :8080)

EndpointMétodoQuién lo usaDescripción
/ingest/heartbeatPOSTCoreEstado en vivo; responde con RemoteConfig
/ingest/uploadPOST multipartCoreSube la evidencia .npz y crea la alerta
/api/liveGETDashboardEstado en vivo (offline si no hay heartbeat en 5 s)
/api/configGET / POSTDashboardLeer / cambiar umbral, tiempos, paso y ON/OFF
/api/alertsGETDashboardÚltimas 50 alertas
/api/filesGETDashboardCapturas almacenadas
/api/matrix/:file/:frameGETDashboardMatriz cruda + min/max para renderizar
/api/evolution/:fileGETDashboardMáx / promedio de la captura
/api/download/:fileGETDashboardDescarga forzada del .npz

Configuración dinámica (editable desde el dashboard)

ParámetroSignificado
max_temp_triggerUmbral de alerta (°C)
scan_wait_time_secTiempo de observación en cada panel
pan_step_degreesVelocidad de paneo (grados por frame)
system_enabledPausa / reanuda la inspección de forma remota

Dashboard

SecciónQué ofrece
Centro de MandoTemperatura máxima en vivo, ángulo de la cámara, ID de turbina, última sincronización, modo actual
Análisis y AlertasLista de capturas con cinco vistas: resumen temporal, matriz 2D a color, escala de grises, superficie 3D (Three.js) y análisis con IA (Gemini 2.5 Flash)
Archivos y ExportaciónDescarga de las capturas radiométricas .npz para análisis externo (NumPy, MATLAB, Julia)
ConfiguraciónUmbral, tiempo de espera, velocidad angular, correo de alertas y API key

En producción se sirve detrás de Nginx, que redirige /api al puerto 8080 y evita problemas de CORS y de mixed content.


Especificaciones de los dispositivos

Sitio de instalación

ParámetroValor
Equipo monitoreado2 paneles de la GCU en el pie de la torre
Área de gabinetes (referencia)2 m profundidad × 2.5 m ancho × 3 m alto
Ubicación de la cámaraEntrada de la torre
Altura del soporte1.70 m (centro visual de los paneles)
Inclinación (pitch)−5° a −8°
Paneo (yaw)Panel GCU 1 (izq.) ≈ 125° · Panel GCU 2 (der.) ≈ 55°

Hardware

ComponenteEspecificaciónFunción
Cámara térmicaInfiRay Tiny1-C, 256 × 192 px, USB UVC, ~25 FPSSensor radiométrico
Computadora de bordeRaspberry Pi [modelo / RAM]ThermaCast + Mosquitto + gsu-sentinel-core
MicrocontroladorESP32 (ESP-IDF v5, FreeRTOS, Wi-Fi)PID del servo + cliente MQTT
ActuadorTowerPro SG90 modificado (cable al cursor del potenciómetro)Paneo de la cámara
Señal PWMGPIO 18 · 50 Hz · LEDC 13 bitsComando al servo
FeedbackGPIO 34 · ADC1_CH6 · 12 bits · 11 dBPosición real del eje
Alimentación servo5 V (VIN), tierra común con la ESP32—
Servidor en la nubeVPS Linux + NginxSentinel Cloud + Dashboard

Software

CapaStack
AdquisiciónC++17 · libuvc · ZeroMQ
Orquestación en el bordeRust (edition 2024) · Tokio · zeromq · rumqttc · reqwest · ndarray / ndarray-npy
FirmwareC · ESP-IDF v5 · FreeRTOS · esp-mqtt · LEDC · ADC1
SimulaciónJulia · DifferentialEquations.jl · Plots.jl
NubeRust · Axum 0.7 · tower-http · ndarray-npy
FrontendReact 18 · TypeScript · Vite · Three.js · Recharts · @google/genai

Relación con HeatStream

HeatStream resolvió cómo obtener temperaturas reales de una cámara térmica sin SDK, y cómo compartirlas con cualquier software mediante PUB/SUB. GCU Sentinel es la primera aplicación completa construida sobre ese flujo:

HeatStream ──(ZeroMQ PUB/SUB)──► Sentinel Core ──► alerta de punto caliente "¿cuánto mide cada píxel?" "¿esto es un problema?, ¿dónde?, ¿qué hago?"

Gracias al desacoplamiento, el mismo stream puede alimentar al mismo tiempo el Core, la API FastAPI de HeatStream, un visor local o un modelo de visión artificial, sin cambiar una línea del código de adquisición.


Resultado

Con componentes de bajo costo (una cámara térmica USB, una Raspberry Pi, una ESP32 y un servo de juguete estabilizado con ingeniería inversa y teoría de control), GCU Sentinel convierte la inspección termográfica de los paneles de control (GCU) de un aerogenerador en un proceso continuo, autónomo y trazable:

  • detecta el punto caliente cuando aparece, no en la siguiente visita programada;
  • centra la cámara sobre la anomalía por sí solo;
  • guarda evidencia radiométrica (°C por píxel) en lugar de una simple fotografía;
  • permite ajustar el comportamiento del sistema de forma remota desde un dashboard web con análisis 3D e IA.

gcu-sentinel-sim

Interactivo · nivel 2

Participantes

GitHub ↗CameraControl ↗
GCU Sentinel · Energías Alternas