Fusão de sensores é a técnica de combinar leituras de vários sensores para que o resultado seja mais preciso, mais estável ou mais completo do que qualquer sensor isolado conseguiria oferecer. Para desenvolvedores IoT, ela costuma ser a diferença entre um sinal trêmulo e com deriva e um sinal em que você pode confiar em um dashboard ou em um modelo. Este artigo explica as principais abordagens de fusão em termos simples, mostra quando fazer a fusão na borda ou na nuvem e traz código funcional para ESP32 e Python que publica valores fundidos via MQTT.
Por que combinar sensores
Todo sensor tem pontos fracos. Um acelerômetro mede bem a inclinação quando o dispositivo está parado, mas é perturbado por qualquer vibração. Um giroscópio mede a rotação de forma suave, mas o ângulo integrado deriva com o tempo. O GPS fornece posição absoluta, mas atualiza devagar e salta quando está sob árvores; a odometria das rodas é suave, mas acumula erro. Duas sondas de temperatura baratas no mesmo tanque, digamos na câmara fria de um frigorífico, discordam ligeiramente, e cada uma tem picos ocasionais.
A fusão aproveita o fato de esses pontos fracos serem diferentes. Se um sensor é ruidoso em altas frequências e outro deriva em baixas frequências, combiná-los gera um sinal bom em toda a faixa. Se dois sensores medem a mesma grandeza com ruídos independentes, uma combinação ponderada é mais precisa do que qualquer um deles sozinho. E, quando um sensor falha, uma estimativa fundida consegue detectar a inconsistência em vez de reportar silenciosamente um valor errado.
Três níveis de fusão de sensores
| Nível | O que é combinado | Exemplo | Método típico |
|---|---|---|---|
| Baixo nível (dados) | Medições brutas da mesma grandeza | Duas sondas de temperatura, acelerômetro mais giroscópio | Média ponderada, filtro complementar, filtro de Kalman |
| Nível de características | Características derivadas de sensores diferentes | RMS de vibração mais corrente do motor mais temperatura | Vetores de características para um modelo de ML |
| Nível de decisão | Saídas de detectores separados | A câmera diz "pessoa", o PIR diz "movimento" | Votação, regras, combinação bayesiana |
A fusão de baixo nível é o foco deste artigo, porque é nela que vivem filtros como o complementar e o de Kalman. A fusão no nível de características é a entrada cotidiana dos modelos de machine learning, como em machine learning para manutenção preditiva, onde vibração, temperatura e corrente juntas revelam uma degradação que nenhuma delas mostra claramente sozinha.
O filtro complementar
O filtro complementar é o método de fusão útil mais simples e um bom ponto de partida para sensores inerciais. Para estimar um ângulo de inclinação, ele integra a taxa do giroscópio para as mudanças de curto prazo e puxa suavemente o resultado em direção ao ângulo do acelerômetro para remover a deriva de longo prazo:
angle = alpha * (angle + gyro_rate * dt) + (1 - alpha) * accel_angle
Com alpha em torno de 0,98 e um período de amostragem de 10 ms, o giroscópio domina em frações de segundo e o acelerômetro domina em alguns segundos. O filtro não precisa de matrizes, roda em microssegundos em um microcontrolador e é fácil de ajustar: aumente alpha se a saída estiver ruidosa e diminua se ela derivar.
O filtro de Kalman
O filtro de Kalman generaliza a mesma ideia. Ele mantém uma estimativa do estado e da sua própria incerteza, prevê como o estado vai mudar e depois corrige a previsão a cada medição, ponderando a correção pelo quanto confia na medição em relação à previsão. Quando a medição é ruidosa, o filtro se apoia na previsão; quando a previsão é incerta, ele se apoia na medição.
Para uma única grandeza com dois sensores, a atualização de Kalman é curta o bastante para ser lida por inteiro:
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 # estimate, its variance, process noise
def predict(self):
self.p += self.q
def update(self, z, r):
k = self.p / (self.p + r) # Kalman gain
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 # measurement variances from datasheets or tests
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) # occasional spike
kf.predict()
if abs(ntc - kf.x) < 3 * (R_NTC + kf.p) ** 0.5: # gate out implausible readings
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()
Instale com pip install paho-mqtt e assine com mosquitto_sub -h broker.connectmq.com -p 1883 -i fusion-watch-$RANDOM -t "connectmq/demo/+/tank1/#" -v para ver o valor fundido ao lado das leituras brutas. Repare no filtro de validação: leituras que discordam da estimativa atual por mais de três desvios-padrão são descartadas, e é assim que o filtro rejeita os picos do NTC. Publicar a variância junto com o valor permite que os consumidores saibam o quanto a estimativa é confiável.
Em problemas multidimensionais, como posição e velocidade a partir de GPS e de uma IMU, a mesma estrutura usa matrizes, e sistemas não lineares usam o filtro de Kalman estendido ou o unscented. Bibliotecas como a FilterPy em Python ou os filtros de Madgwick e Mahony para orientação poupam você de implementá-los à mão.
Fusão na borda: exemplo com ESP32
A fusão inercial precisa rodar perto do sensor, porque exige amostras rápidas e regularmente espaçadas, muitas vezes a 100 Hz ou mais. Publicar cada amostra bruta via MQTT desperdiçaria banda e bateria e acrescentaria jitter. O melhor padrão é fazer a fusão no dispositivo e publicar o resultado a uma taxa moderada.
Este sketch lê um MPU6050 via I2C em um ESP32, executa um filtro complementar a 100 Hz e publica o ângulo de pitch fundido duas vezes por segundo no servidor MQTT público em broker.connectmq.com, que serve para testes e protótipos, não para dados de produção. Ele usa as bibliotecas Adafruit MPU6050 e 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 to deg/s
pitch = ALPHA * (pitch + gyroRate * dt) + (1.0f - ALPHA) * accelPitch;
}
if (millis() - lastPublish >= 500) { // 2 messages per second
lastPublish = millis();
char payload[64];
snprintf(payload, sizeof(payload), "{\"pitch\":%.2f,\"unit\":\"deg\"}", pitch);
mqtt.publish(TOPIC, payload);
}
}
Troque o segmento de topic imu-7f3a por algo único. Para um dispositivo de produção, mude para WiFiClientSecure na porta 8883 com o username, a senha e o client ID do ConnectMQ; o tutorial de ESP32 com MQTT cobre a configuração de TLS em detalhes.
Fusão na borda ou na nuvem: como decidir
- Faça a fusão na borda quando os sensores estiverem no mesmo dispositivo, as taxas de amostragem forem altas, a latência importar (balanceamento, estabilização, segurança) ou a banda e a bateria forem limitadas.
- Faça a fusão na nuvem ou em um servidor quando os sensores estiverem em dispositivos diferentes, as taxas forem baixas, você precisar de contexto histórico ou quiser mudar o algoritmo sem atualizar o firmware.
- Publique os dois por um tempo durante o desenvolvimento: valores brutos a uma taxa reduzida e valores fundidos, para verificar o filtro contra a realidade.
- Sempre registre o timestamp na origem ao fundir dados de vários dispositivos, porque a ordem de chegada no broker não é a ordem das medições.
É o mesmo trade-off discutido em o que é edge AI: o processamento vai para onde estão os dados e os requisitos de latência.
Erros comuns na fusão de sensores
- Parâmetros de ruído errados. Um filtro de Kalman é tão bom quanto o ruído de medição e de processo que ele assume. Meça o ruído a partir de uma gravação com o sensor parado, em vez de chutar.
- Eixos e unidades incompatíveis. Giroscópios costumam reportar em rad/s; os eixos do acelerômetro podem estar montados de forma diferente da que você imagina. Confira os sinais inclinando a placa com a mão.
- Amostragem irregular. Os filtros assumem um
dtconhecido. Calcule-o a partir dos timestamps em vez de assumir uma constante. - Fontes não sincronizadas. Fundir dois dispositivos cujos relógios diferem em segundos gera artefatos. Sincronize com NTP e transporte os timestamps de origem.
- Mascarar falhas de sensores. A fusão pode esconder um sensor com defeito. Publique indicadores brutos de saúde ou valores de inovação para que as falhas continuem visíveis, o que também alimenta a detecção de anomalias em IoT.
Para onde vão os dados fundidos
Valores fundidos são entradas melhores para dashboards, alertas, modelos e para um gêmeo digital do ativo. O ConnectMQ atua como o broker MQTT gerenciado que os leva dos dispositivos aos consumidores: o explorador de topics ao vivo mostra topics fundidos e brutos lado a lado, e regras de alerta por limite podem disparar sobre um valor fundido, como um ângulo de inclinação além de um limite seguro. Para entender de forma mais ampla como modelos e dispositivos conectados trabalham juntos, veja o guia de IA e IoT.
Comece a usar o ConnectMQ
Teste primeiro os exemplos no servidor MQTT público (gratuito) e depois crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, para obter credenciais privadas e TLS para os seus dispositivos.
Perguntas frequentes
O que é fusão de sensores em IoT?
A fusão de sensores combina medições de vários sensores para produzir uma estimativa mais precisa, estável ou completa do que a de qualquer sensor isolado, por exemplo um ângulo de inclinação sem deriva obtido de um acelerômetro e de um giroscópio.
Devo usar um filtro complementar ou um filtro de Kalman?
Comece com um filtro complementar para problemas simples de orientação em microcontroladores. Use um filtro de Kalman quando precisar combinar sensores com características de ruído conhecidas, estimar vários estados relacionados ou reportar a incerteza.
A fusão de sensores deve rodar no dispositivo ou na nuvem?
Rode no dispositivo quando os sensores forem locais e rápidos, porque enviar cada amostra bruta é caro e acrescenta jitter. Rode de forma centralizada quando os sensores estiverem em dispositivos diferentes ou a taxa de atualização for baixa.
Ainda consigo acessar os dados brutos se fizer a fusão na borda?
Sim. Um padrão comum é publicar os valores fundidos continuamente e os valores brutos a uma taxa menor ou sob demanda em topics separados, o que mantém o uso de banda baixo e ainda permite a verificação.



