Resumen | Este codelab fue creado para lograr comprender una posible implementación de algoritmos de transformadas de fourier y análisis espectral en una ESP32. Aunque son algoritmos computacionalmente complejos, se puede lograr un compromiso entre el muestreo y el procesamiento de señal. Se espera que usted al finalizar esté en capacidad de:
|
Fecha de Creación: | 2024/03/01 |
Última Actualización: | 2024/03/01 |
Requisitos Previos: | |
Adaptado de: | |
Referencias: | |
Escrito por: | Fredy Segura-Quijano |
Las transformadas y el análisis espectral son técnicas fundamentales en el procesamiento de señales que permiten descomponer una señal en sus componentes frecuenciales. Esto es crucial para aplicaciones donde se requiere entender el contenido en frecuencia de una señal, como en telecomunicaciones, análisis de vibraciones, procesamiento de audio, y diagnóstico de maquinaria. En sistemas embebidos, la eficiencia y la correcta implementación del análisis espectral son vitales debido a las limitaciones de recursos.
Las transformadas y el análisis espectral son herramientas matemáticas que convierten una señal del dominio del tiempo al dominio de la frecuencia. Las más comunes incluyen la Transformada de Fourier (FT) y sus variantes discretas.
Para implementar la Transformada de Fourier Discreta (DFT) en la ESP32, se puede escribir un código propio que realice la DFT manualmente. Sin embargo, debido a la complejidad computacional de la DFT, generalmente se prefiere usar la FFT (Fast Fourier Transform) que es más eficiente. Inicialmente se presenta un ejemplo básico de cómo implementar la DFT manualmente en la ESP32.
#include <Arduino.h>
#include <math.h>
#define SAMPLES 64 // Número de muestras
#define SAMPLING_FREQUENCY 1000 // Frecuencia de muestreo en Hz
// Señal de prueba
double signal[SAMPLES];
double real[SAMPLES];
double imag[SAMPLES];
void setup() {
Serial.begin(115200);
// Generar una señal de prueba (senoidal)
for (int i = 0; i < SAMPLES; i++) {
signal[i] = sin(2 * PI * i / SAMPLES) + 0.5 * sin(2 * PI * 2 * i / SAMPLES);
}
// Calcular la DFT
for (int k = 0; k < SAMPLES; k++) {
real[k] = 0;
imag[k] = 0;
for (int n = 0; n < SAMPLES; n++) {
double angle = 2 * PI * k * n / SAMPLES;
real[k] += signal[n] * cos(angle);
imag[k] -= signal[n] * sin(angle);
}
}
// Imprimir los resultados
Serial.println("k\tReal\tImag\tMagnitude");
for (int k = 0; k < SAMPLES; k++) {
double magnitude = sqrt(real[k] * real[k] + imag[k] * imag[k]);
Serial.print(k);
Serial.print("\t");
Serial.print(real[k]);
Serial.print("\t");
Serial.print(imag[k]);
Serial.print("\t");
Serial.println(magnitude);
}
}
void loop() {
// No se necesita código en el loop para este ejemplo
}
Sin embargo para la implementación de la Transformada de Fourier Rápida (FFT) en una ESP32, se puede usar la biblioteca arduinoFFT, que facilita el cálculo de la FFT en microcontroladores. Esto asumiendo que dicha librería logra una buena implementación del algoritmo.
#include <Arduino.h>
#include "arduinoFFT.h"
#define SAMPLES 128 // Debe ser una potencia de 2
#define SAMPLING_FREQUENCY 1000 // Frecuencia de muestreo en Hz
arduinoFFT FFT = arduinoFFT();
unsigned int samplingPeriodUs;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
void setup() {
Serial.begin(115200);
samplingPeriodUs = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}
void loop() {
/* Colectar las muestras */
for (int i = 0; i < SAMPLES; i++) {
microseconds = micros(); // Registra el tiempo en microsegundos
vReal[i] = analogRead(34); // Lee el valor ADC del canal 34
vImag[i] = 0; // Inicializa la parte imaginaria con 0
while (micros() < (microseconds + samplingPeriodUs)) {
// Espera el tiempo de muestreo requerido
}
}
/* Realiza la FFT */
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); // Aplica la ventana de Hamming
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); // Computa la FFT
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); // Convierte a magnitud
/* Imprimir las frecuencias */
for (int i = 0; i < (SAMPLES / 2); i++) {
Serial.print("Freq: ");
Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
Serial.print(" Hz");
Serial.print("\tMagnitude: ");
Serial.println(vReal[i], 1); // Imprime la magnitud
}
delay(1000); // Espera 1 segundo antes de la siguiente lectura
}
#include <Arduino.h>
#include "arduinoFFT.h"
#define SAMPLES 128
#define SAMPLING_FREQUENCY 1000
arduinoFFT FFT = arduinoFFT();
unsigned int samplingPeriodUs;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
void setup() {
Serial.begin(115200);
samplingPeriodUs = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}
void loop() {
for (int i = 0; i < SAMPLES; i++) {
microseconds = micros(); // Registra el tiempo en microsegundos
vReal[i] = analogRead(34); // Lee el valor ADC del canal 34
vImag[i] = 0; // Inicializa la parte imaginaria con 0
while (micros() < (microseconds + samplingPeriodUs)) {
// Espera el tiempo de muestreo requerido
}
}
/* Realiza la FFT */
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); // Aplica la ventana de Hamming
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); // Computa la FFT
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); // Convierte a magnitud
/* Imprimir las frecuencias */
for (int i = 0; i < (SAMPLES / 2); i++) {
Serial.print("Freq: ");
Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
Serial.print(" Hz");
Serial.print("\tMagnitude: ");
Serial.println(vReal[i], 1); // Imprime la magnitud
}
delay(1000); // Espera 1 segundo antes de la siguiente lectura
}
La Transformada Rápida de Fourier (FFT) es una herramienta valiosa para el análisis de señales en el dominio de la frecuencia, pero su eficacia depende de ciertas consideraciones sobre la señal de entrada. Algunos factores clave a tener en cuenta son:
La transformada y el análisis espectral no son exactamente lo mismo, aunque están estrechamente relacionados y a menudo se utilizan juntos en el procesamiento de señales. El análisis espectral es el proceso de examinar la distribución de la energía o potencia de una señal en función de la frecuencia. Implica identificar los diferentes componentes de frecuencia presentes en una señal y sus amplitudes. El objetivo del análisis espectral es comprender cómo se distribuye la energía de la señal a lo largo de diferentes frecuencias. Esto es útil para identificar patrones, frecuencias dominantes, y características ocultas en la señal.
Implementar la Transformada de Fourier de Ventana Móvil o de Corto Tiempo (STFT) en un ESP32 es posible, aunque puede ser más exigente en términos de recursos computacionales en comparación con la FFT. La STFT se utiliza para analizar señales no estacionarias, proporcionando información sobre cómo las frecuencias cambian con el tiempo.
La STFT divide una señal en segmentos cortos de igual longitud y aplica la FFT a cada segmento. Esto permite observar cómo cambian las componentes de frecuencia a lo largo del tiempo. La implementación de la STFT en el ESP32 puede seguir estos pasos:
#include <arduinoFFT.h>
#define SAMPLES 64 // Número de muestras por ventana. Debe ser una potencia de 2.
#define SAMPLING_FREQUENCY 1000 // Frecuencia de muestreo en Hz.
#define OVERLAP 0.5 // Porcentaje de solapamiento (50%).
arduinoFFT FFT = arduinoFFT();
double vReal[SAMPLES];
double vImag[SAMPLES];
double signal[256]; // Ejemplo de señal larga.
void setup() {
Serial.begin(115200);
analogReadResolution(10); // Configurar la resolución de lectura analógica (10 bits para ESP32)
// Generar una señal de prueba
for (int i = 0; i < 256; i++) {
signal[i] = sin(2 * PI * 50 * i / SAMPLING_FREQUENCY) + 0.5 * sin(2 * PI * 120 * i / SAMPLING_FREQUENCY);
}
}
void loop() {
int step = SAMPLES * (1 - OVERLAP); // Paso de solapamiento
for (int start = 0; start < 256 - SAMPLES; start += step) {
// Copiar y ventanear los datos
for (int i = 0; i < SAMPLES; i++) {
vReal[i] = signal[start + i] * 0.54 - 0.46 * cos(2 * PI * i / (SAMPLES - 1)); // Aplicar ventana Hamming
vImag[i] = 0; // Parte imaginaria inicializada a 0
}
// Realizar la FFT
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
// Imprimir los resultados
Serial.print("Window starting at sample ");
Serial.println(start);
for (int i = 0; i < (SAMPLES / 2); i++) {
double frequency = (i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES;
Serial.print(frequency, 1);
Serial.print("Hz: ");
Serial.println(vReal[i], 1); // Magnitud
}
Serial.println();
}
delay(5000); // Esperar antes de la próxima iteración
}
Debe haber un compromiso entre la resolución temporal y la resolución frecuencial. Ventanas más cortas proporcionan mejor resolución temporal, mientras que ventanas más largas proporcionan mejor resolución frecuencial. La STFT puede ser computacionalmente intensiva, por lo que optimizar el código y el manejo de la memoria es crucial para un rendimiento eficiente en el ESP32. La STFT es útil para analizar señales no estacionarias en tiempo real, como señales de audio, vibraciones mecánicas, y datos biomédicos.