IoT e IA

Fusión de sensores explicada para desarrolladores IoT

Qué es la fusión de sensores, cómo los filtros complementario y de Kalman combinan sensores ruidosos, cuándo fusionar en el borde o en la nube, y ejemplos funcionales en ESP32 y Python que publican valores fusionados por MQTT.

Diagrama de fusión de sensores que combina lecturas de acelerómetro y giroscopio mediante un filtro complementario en un ángulo de inclinación estable publicado por MQTT
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

La fusión de sensores es la técnica de combinar lecturas de varios sensores para que el resultado sea más preciso, más estable o más completo de lo que podría ofrecer cualquier sensor por separado. Para los desarrolladores IoT suele marcar la diferencia entre una señal temblorosa y con deriva y otra en la que puedes confiar en un dashboard o en un modelo. Este artículo explica con sencillez los principales enfoques de fusión, muestra cuándo fusionar en el borde y cuándo en la nube, y te da código funcional en ESP32 y Python que publica valores fusionados por MQTT.

Por qué combinar sensores

Todos los sensores tienen puntos débiles. Un acelerómetro mide bien la inclinación cuando el dispositivo está quieto, pero cualquier vibración lo desajusta. Un giroscopio mide la rotación de forma suave, pero el ángulo integrado deriva con el tiempo. El GPS da una posición absoluta, pero se actualiza lentamente y salta bajo los árboles; la odometría de las ruedas es suave, pero acumula error. Dos sondas de temperatura baratas en el mismo depósito discrepan ligeramente y cada una tiene picos ocasionales.

La fusión aprovecha que esas debilidades son distintas. Si un sensor es ruidoso a altas frecuencias y otro deriva a bajas frecuencias, combinarlos da una señal buena en todo el rango. Si dos sensores miden lo mismo con ruido independiente, una combinación ponderada es más precisa que cualquiera de ellos por separado. Y cuando un sensor falla, una estimación fusionada puede detectar la incoherencia en lugar de informar en silencio de un valor erróneo.

Tres niveles de fusión de sensores

Nivel Qué se combina Ejemplo Método típico
Bajo nivel (datos) Mediciones en bruto de la misma magnitud Dos sondas de temperatura, acelerómetro más giroscopio Media ponderada, filtro complementario, filtro de Kalman
Nivel de características Características derivadas de distintos sensores RMS de vibración más corriente del motor más temperatura Vectores de características para un modelo de ML
Nivel de decisión Salidas de detectores independientes La cámara dice "persona", el PIR dice "movimiento" Votación, reglas, combinación bayesiana

Este artículo se centra en la fusión de bajo nivel porque es donde viven filtros como el complementario y el de Kalman. La fusión a nivel de características es la entrada cotidiana de los modelos de machine learning, como en el machine learning para mantenimiento predictivo, donde la vibración, la temperatura y la corriente juntas revelan una degradación que ninguna muestra con claridad por sí sola.

El filtro complementario

El filtro complementario es el método de fusión útil más sencillo y un buen punto de partida para sensores inerciales. Para estimar un ángulo de inclinación, integra la velocidad del giroscopio para los cambios a corto plazo y acerca suavemente el resultado al ángulo del acelerómetro para eliminar la deriva a largo plazo:

angle = alpha * (angle + gyro_rate * dt) + (1 - alpha) * accel_angle

Con alpha en torno a 0,98 y un periodo de muestreo de 10 ms, el giroscopio domina durante fracciones de segundo y el acelerómetro domina a lo largo de unos segundos. El filtro no necesita matrices, se ejecuta en microsegundos en un microcontrolador y es fácil de ajustar: aumenta alpha si la salida es ruidosa y redúcelo si la salida deriva.

El filtro de Kalman

El filtro de Kalman generaliza la misma idea. Mantiene una estimación del estado y de su propia incertidumbre, predice cómo cambiará el estado y luego corrige la predicción con cada medición, ponderando la corrección según cuánto confía en la medición frente a la predicción. Cuando la medición es ruidosa, el filtro se apoya en la predicción; cuando la predicción es incierta, se apoya en la medición.

Para una única magnitud con dos sensores, la actualización de Kalman es lo bastante corta como para leerla completa:

import json
import random
import time
import uuid

import paho.mqtt.client as mqtt


class Kalman1D:
    def __init__(self, x0, p0=1.0, q=0.001):
        self.x, self.p, self.q = x0, p0, q   # estimación, su varianza, ruido de proceso

    def predict(self):
        self.p += self.q

    def update(self, z, r):
        k = self.p / (self.p + r)            # ganancia de Kalman
        self.x += k * (z - self.x)
        self.p *= (1 - k)
        return self.x


BASE = f"connectmq/demo/{uuid.uuid4().hex[:8]}/tank1"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="fusion-" + uuid.uuid4().hex[:8])
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()

kf = Kalman1D(x0=20.0)
true_temp = 20.0
R_PT100, R_NTC = 0.05 ** 2, 0.4 ** 2       # varianzas de medición de hojas de datos o pruebas

for _ in range(300):
    true_temp += random.gauss(0, 0.02)
    pt100 = true_temp + random.gauss(0, 0.05)
    ntc = true_temp + random.gauss(0, 0.4) + (5 if random.random() < 0.02 else 0)  # pico ocasional
    kf.predict()
    if abs(ntc - kf.x) < 3 * (R_NTC + kf.p) ** 0.5:  # descarta lecturas inverosímiles
        kf.update(ntc, R_NTC)
    fused = kf.update(pt100, R_PT100)
    payload = {"ts": int(time.time() * 1000), "value": round(fused, 3), "variance": round(kf.p, 6),
               "raw": {"pt100": round(pt100, 3), "ntc": round(ntc, 3)}}
    client.publish(f"{BASE}/temperature/fused", json.dumps(payload))
    time.sleep(1)

client.loop_stop()
client.disconnect()

Instálalo con pip install paho-mqtt y suscríbete con mosquitto_sub -h broker.connectmq.com -p 1883 -i fusion-watch-$RANDOM -t "connectmq/demo/+/tank1/#" -v para ver el valor fusionado junto a las lecturas en bruto. Fíjate en la compuerta: las lecturas que discrepan de la estimación actual en más de tres desviaciones típicas se omiten, y así es como el filtro rechaza los picos del NTC. Publicar la varianza junto al valor permite a los consumidores saber cuánta confianza merece la estimación.

En problemas multidimensionales, como la posición y la velocidad a partir de GPS y una IMU, la misma estructura usa matrices, y los sistemas no lineales usan el filtro de Kalman extendido o unscented. Bibliotecas como FilterPy en Python o los filtros de Madgwick y Mahony para la orientación te ahorran implementarlos a mano.

Fusión en el borde: ejemplo con ESP32

La fusión inercial debe ejecutarse cerca del sensor porque necesita muestras rápidas y regularmente espaciadas, a menudo de 100 Hz o más. Publicar cada muestra en bruto por MQTT desperdiciaría ancho de banda y batería y añadiría jitter. El patrón más adecuado es fusionar en el dispositivo y publicar el resultado a una frecuencia moderada.

Este sketch lee un MPU6050 por I2C en un ESP32, ejecuta un filtro complementario a 100 Hz y publica el ángulo de cabeceo fusionado dos veces por segundo en el servidor MQTT público gratuito broker.connectmq.com. Usa las bibliotecas Adafruit MPU6050 y PubSubClient.

#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* BROKER = "broker.connectmq.com";
const char* TOPIC = "connectmq/demo/imu-7f3a/arm1/pitch";

WiFiClient net;
PubSubClient mqtt(net);
Adafruit_MPU6050 mpu;

float pitch = 0.0f;
const float ALPHA = 0.98f;
unsigned long lastSample = 0, lastPublish = 0;

void connectMqtt() {
  while (!mqtt.connected()) {
    String id = "esp32-imu-" + String((uint32_t)ESP.getEfuseMac(), HEX);
    if (!mqtt.connect(id.c_str())) delay(2000);
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  mqtt.setServer(BROKER, 1883);
  if (!mpu.begin()) {
    Serial.println("MPU6050 not found");
    while (true) delay(1000);
  }
  mpu.setGyroRange(MPU6050_RANGE_250_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_44_HZ);
  lastSample = micros();
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  if (micros() - lastSample >= 10000) {           // 100 Hz
    float dt = (micros() - lastSample) / 1e6f;
    lastSample = micros();
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);
    float accelPitch = atan2f(-a.acceleration.x,
                              sqrtf(a.acceleration.y * a.acceleration.y + a.acceleration.z * a.acceleration.z)) * 180.0f / PI;
    float gyroRate = g.gyro.y * 180.0f / PI;     // rad/s a grados/s
    pitch = ALPHA * (pitch + gyroRate * dt) + (1.0f - ALPHA) * accelPitch;
  }

  if (millis() - lastPublish >= 500) {            // 2 mensajes por segundo
    lastPublish = millis();
    char payload[64];
    snprintf(payload, sizeof(payload), "{\"pitch\":%.2f,\"unit\":\"deg\"}", pitch);
    mqtt.publish(TOPIC, payload);
  }
}

Cambia el segmento de topic imu-7f3a por algo único. El servidor público es solo para pruebas y prototipos, no para datos de producción. Para un dispositivo en producción, pasa a WiFiClientSecure en el puerto 8883 con tu usuario, contraseña y client ID de ConnectMQ; el tutorial de ESP32 MQTT explica en detalle la configuración TLS.

Fusión en el borde o en la nube: cómo decidir

  1. Fusiona en el borde cuando los sensores están en el mismo dispositivo, las frecuencias de muestreo son altas, la latencia importa (equilibrado, estabilización, seguridad) o el ancho de banda y la batería son limitados.
  2. Fusiona en la nube o en un servidor cuando los sensores están en dispositivos distintos, las frecuencias son bajas, necesitas contexto histórico o quieres cambiar el algoritmo sin actualizar el firmware.
  3. Publica ambos durante un tiempo en la fase de desarrollo: valores en bruto a una frecuencia reducida y valores fusionados, para poder verificar el filtro frente a la realidad.
  4. Pon siempre la marca de tiempo en el origen cuando fusiones entre dispositivos, porque el orden de llegada al broker no es el orden de medición.

Es el mismo equilibrio que se analiza en qué es la IA en el borde: el cálculo se desplaza a donde están los datos y los requisitos de latencia.

Errores comunes en la fusión de sensores

  • Parámetros de ruido incorrectos. Un filtro de Kalman solo es tan bueno como el ruido de medición y de proceso que supone. Mide el ruido a partir de una grabación en reposo en lugar de adivinarlo.
  • Ejes y unidades que no coinciden. Los giroscopios suelen informar en rad/s; los ejes del acelerómetro pueden estar montados de forma distinta a la que supones. Comprueba los signos inclinando la placa a mano.
  • Muestreo irregular. Los filtros suponen un dt conocido. Calcúlalo a partir de las marcas de tiempo en lugar de suponer un valor constante.
  • Fuentes no sincronizadas. Fusionar dos dispositivos cuyos relojes difieren en segundos produce artefactos. Sincroniza con NTP y transmite las marcas de tiempo de origen.
  • Ocultar fallos de sensores. La fusión puede enmascarar un sensor que falla. Publica indicadores de salud en bruto o valores de innovación para que los fallos sigan siendo visibles, lo que además alimenta la detección de anomalías IoT.

Adónde van después los datos fusionados

Los valores fusionados son mejores entradas para dashboards, alertas y modelos. ConnectMQ actúa como el broker MQTT gestionado que los transporta desde los dispositivos hasta los consumidores: el explorador de topics en directo muestra los topics fusionados y en bruto uno al lado del otro, y las reglas de alerta por umbral pueden dispararse sobre un valor fusionado, como un ángulo de inclinación que supere un límite seguro. Para ver de forma más amplia cómo trabajan juntos los modelos y los dispositivos conectados, consulta la guía de IA e IoT.

Empieza con ConnectMQ

Prueba primero los ejemplos con el servidor MQTT público gratuito y después crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, para obtener credenciales privadas y TLS para tus dispositivos.

Preguntas frecuentes

¿Qué es la fusión de sensores en IoT?

La fusión de sensores combina mediciones de varios sensores para obtener una estimación más precisa, estable o completa que la de cualquier sensor por separado, por ejemplo un ángulo de inclinación sin deriva a partir de un acelerómetro y un giroscopio.

¿Debo usar un filtro complementario o un filtro de Kalman?

Empieza con un filtro complementario para problemas sencillos de orientación en microcontroladores. Usa un filtro de Kalman cuando necesites combinar sensores con características de ruido conocidas, estimar varios estados relacionados o informar de la incertidumbre.

¿La fusión de sensores debe ejecutarse en el dispositivo o en la nube?

Ejecútala en el dispositivo cuando los sensores son locales y rápidos, porque enviar cada muestra en bruto es costoso y añade jitter. Ejecútala de forma centralizada cuando los sensores están en dispositivos distintos o la frecuencia de actualización es baja.

¿Puedo seguir accediendo a los datos en bruto si fusiono en el borde?

Sí. Un patrón habitual es publicar los valores fusionados de forma continua y los valores en bruto a menor frecuencia o bajo demanda en topics separados, lo que mantiene bajo el ancho de banda y permite la verificación.

Artículos relacionados

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.