reducir-ruido-sensores-arduino-muestreo-multiple

Reducir ruido en sensores en Arduino con muestreo múltiple

Ruido, ruido, ruido. A estas alturas del blog debe quedar claro que en cualquier sistema electrónico uno de los principales problemas es el ruido de las señales. En este contexto entendemos por ruido a señales de alta frecuencia y carácter aleatorio que interfieren con la señal de interés.

El ruido es una realidad patente y molesta a la hora de utilizar sensores. Aunque la calidad del sensor influye notablemente en la cantidad de ruido en la medición, lo cierto es que todos los sensores presentan ruido.

Con el rango de precios de los sensores que compramos nosotros para nuestros proyectos domésticos, tristemente, el ruido en las mediciones va a ser un constante problema con el que tratar.

En algunas ocasiones podemos incorporar algún sistema de filtrado físico pero, en general, si queremos usar de forma seria sensores en nuestros proyectos tendremos que aprender a tratar y filtrar el ruido de las mediciones de sensores de forma digital.

El muestro múltiple al rescate

Asumiendo que ya hemos hecho todo lo posible por mejorar la física del sensor y su entorno, el problema se reduce a que vamos a adquirir muestras y estas no van a reflejar correctamente la magnitud medida.

Dado que las muestras tomadas con carácter individual no son fiables y ya hemos hecho todo lo posible para mejorarlas, se deduce que la única posibilidad es realizar un muestreo múltiple, es decir, adquirir varias veces la medición del sensor.

Posteriormente tendremos que aplicar algún tipo de algoritmo para combinar las muestras y obtener una medición con mayor precisión que las muestras individuales.

Imaginemos, por ejemplo, que estamos tomando la temperatura de una instancia. Al realizar mediciones en un sensor de temperatura recibiremos una secuencia que, por ejemplo, sea 24.9, 24.8, 24.7, 24.8, 27, 24.8.

Vemos que hay pequeñas variaciones entre muestras (ruido, oscilaciones) y un punto (27) que en principio parece “anómalo” (a este tipo de puntos los denominaremos espurios o outliners).

¿Cuál es la temperatura de la instancia? Seguramente, como primera intuición, diríamos que unos 24.8 grados. Pero merece la pena reflexionar un segundo sobre esta respuesta tan rápida.

Antes de responder deberíamos saber, por ejemplo, la precisión del sensor, el tiempo entre muestras, y la velocidad del sistema que estamos midiendo. ¿Podría ser que estén tomadas cada 30 minutos, y realmente en un momento hayamos alcanzado 27º?

La medición, el muestreo, y el filtrado son temas importantes, amplios y complejos. Existe muchos parámetros a tener en cuenta, y una enorme variedad de formas de algoritmos de filtrado (media, mediana, exponencial…) cada uno con sus ventajas y desventajas.

En el blog veremos muchas formas de filtrar señales y, en particular, como emplearlos para reducir el ruido en adquisición de sensores. De momento, vamos a iniciarnos con dos procedimientos sencillos para realizar un muestreo múltiple de un sensor.

Adquisición por muestras constante

Probablemente la forma más sencilla de realizar un muestreo múltiple es medir un número de veces fijo. Por ejemplo, tenemos un sensor pero en lugar de tomar una única medición tomamos 20, o 50, o 100 seguidas.

El número de muestras dependerá de la velocidad del sensor y del tiempo que queramos estar midiendo.

Posteriormente tendremos que tratar los datos. En el ejemplo vamos a emplear la media, porque es rápido de calcular y no requiere almacenar los valores medidos. En otras entradas veremos otros filtros.

float MeasureN(int samplesNumber, float (*funct)())
{
  float sum;
  for (int i = 0; i < samplesNumber; i++)
  {
    sum += funct();
  }

  return sum / samplesNumber;
}

//Funcion que simula la medicion de un sensor
float getMeasure()
{
  return 0.0;
}

void setup()
{
  float measureN = MeasureN(100, getMeasure);
}

void loop()
{

}

Adquisición en tiempo constante

Una variación es medir en un intervalo de tiempo constante. Ejemplo, tomamos un sensor y decidimos que queremos realizar todas las mediciones posibles durante 20, o 50 o 100 ms.

Nuevamente, el tiempo depende de la velocidad del sensor y del tiempo que queramos dedicar a la medición, pero en este caso tenemos un control mucho mayor del tiempo que vamos a realizar la medición. Esto hace que en muchas ocasiones sea más conveniente para integrar en nuestros programas.

float MeasureT(int msInterval, float(*funct)())
{
  float sum;
  long startTime = millis();
  int samplesNumber = 0;

  while (millis() - startTime < msInterval)
  {
    samplesNumber++;
    sum += getMeasure();
  }
  return sum / samplesNumber;
}

//Funcion que simula la medicion de un sensor
float getMeasure()
{
  return 0.0;
}

void setup()
{
  float measureT = MeasureT(50, getMeasure);
}

void loop()
{

}

Añadir puntos Max, Min y Mid

En ocasiones no sólo queremos obtener el valor filtrado (en este caso la media) del muestreo, si no también estamos interesados en el intervalo de variación de la muestra. Por ejemplo, para determinar si un proceso está dentro de un rango de estabilidad adecuado.

Con los algoritmos presentados resulta sencillo hacer unas modificaciones para obtener el mínimo, máximo, el punto medio entre ambos. No obstante, realizar estos cálculos supone un pequeño trabajo adicional, por lo que usarlos sólo si realmente si es necesario.

float min, max, mid;
float MeasureN(int samplesNumber, float(*funct)())
{
  float value;

  for (int i = 0; i < samplesNumber; i++)
  {
    value = funct();
    if (value > max) max = value;
    if (value < min) min = value;
  }

  mid = (max - min) / 2;
  return sum / samplesNumber;
}

float MeasureT(int msInterval, float(*funct)())
{
  float value;
  long startTime = millis();

  while (millis() - startTime < msInterval)
  {
    value = funct();
    if (value > max) max = value;
    if (value < min) min = value;
  }
  
  mid = (max - min) / 2;
  return sum / samplesNumber;
}

float getMeasure()
{
  return 0.0;
}

void setup()
{
  MeasureN(100, getMeasure);
  MeasureT(50, getMeasure);
}

void loop()
{

} 

En próximas entradas veremos distintas formas avanzadas y eficientes de realizar el filtrado de las mediciones de un sensor, como el filtro de media, mediana y exponencial.

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full