Fast Fourier Transform (FFT) mit dem ESP32
Dieser Artikel beleuchtet, wie man eine FFT als anspruchsvolle mathematische Aufgabe auf einem ESP32-Entwicklungsboard ausführen kann. Hierzu wird auf die Unterstützung durch eine Bibliothek, ein Pmod-I2S2-Modul und ein spezielles Programm zurückgegriffen.
Die Fourier-Transformation ist eines der wichtigsten Themen in der Signalverarbeitung, da sie ein Signal aus dem Zeitbereich in den Frequenzbereich transformiert. Jedes Signal beliebiger Form kann durch eine unendliche Summe von Sinus- und Cosinus-Wellenformen dargestellt werden. Bei analogen Signalen wird die Fourier-Transformation (FT) verwendet, um die Komponenten dieser Reihen zu ermitteln. Das Ergebnis ist das Frequenzspektrum des analogen Eingangssignals, aus dem jeder seiner Frequenz- und Amplitudenwerte extrahiert werden kann.
Die diskrete Fourier-Transformation (DFT) ist das Gegenstück der FT für digitale Signale; die DFT ist also die diskrete Version der ursprünglichen FT. Die Fast-Fourier-Transformation (FFT) schließlich ist ein Algorithmus zur schnellen und effizienten Berechnung der DFT. Die FFT ist eine Implementierung der DFT, die nahezu identische Ergebnisse liefert, aber viel schneller und effizienter ist.
Sowohl bei der DFT als auch bei der FFT handelt es sich um komplexe Verfahren, für deren Verständnis gute mathematische Kenntnisse erforderlich sind. Obwohl es mehrere FFT-Algorithmen gibt, wurde einer der am häufigsten verwendeten von Cooley-Tukey entwickelt, ein Teile-und herrsche-Verfahren, durch das die DFT in kleinere, weniger komplexe Teile zerlegt wird.
Hinweis: Dieser Artikel (230623-02) ist ein Auszug aus dem 254-seitigen Buch Practical Audio DSP Projects with the ESP32 (Elektor, 2023). Der Auszug wurde formatiert und leicht bearbeitet, um den redaktionellen Standards und dem Seitenlayout der Zeitschrift Elektor zu genügen. Der Autor und der Redakteur haben bei der Bearbeitung ihr Bestes gegeben und stehen für Rückfragen gerne zur Verfügung.
Warum FFT?
Der FFT-Algorithmus ist so effizient und schnell, dass er selbst für die meisten Mikrocontroller implementiert werden kann. Mit der FFT kann man untersuchen, welche Frequenzen in einem beliebigen digitalen Signal vorhanden sind und welche dieser Frequenzen dominieren. Im resultierenden Spektrum sieht man die Frequenz auf der horizontalen Achse und die Amplitude auf der vertikalen Achse. Es wird eine Kurve mit einem oder mehreren Peaks angezeigt.
Eine hoher Peak indiziert einen Frequenzanteil, der im Eingangssignal dominant ist. Normalerweise entspricht der Peak mit der höchsten Amplitude der Grundfrequenz des Eingangssignals. Die anderen Peak zeigen Oberschwingungen, die bei Vielfachen der Grundfrequenz auftreten.
Handelt es sich bei dem Eingangssignal um eine Sinuswelle, so zeigt sich nur ein Peak mit Frequenz des Eingangssignals. Bei einer Rechteckwelle wird eine Reihe von Peaks mit abnehmender Amplitude an verschiedenen Frequenzen resultieren. Der FFT-Algorithmus arbeitet mit einer endlichen Anzahl von Eingangsabtastwerten (der so genannten Länge), die ein Vielfaches von 2 sein müssen (zum beispiel 32, 64, 128, 256, …) Im Allgemeinen gilt: Je mehr Abtastwerte, desto langsamer ist der Algorithmus und desto höher ist auch der Aufwand, da mehr Daten verarbeitet werden müssen. Eine größere Länge führt jedoch zu genaueren Ergebnissen.
In diesem Artikel wird ein FFT-Programm mit der Arduino Audio Tools-Bibliothek von Phil Schatzmann entwickelt, die auf der Klasse AudioRealFFT basiert. Die Ergebnisse werden im Seriellen Monitor mit der Frequenz, ihrem Betrag und der der Frequenz entsprechenden Musiknote angezeigt. Weitere Details sind auf einer speziellen Webseite zu finden.
Praktische FFT eines Eingangssignals
In diesem Projekt wird das Modul Pmod I2S2 verwendet, das billig und aus verschiedenen Quellen erhältlich ist. Ein Signal (Sinus- beziehungsweise Rechtecksignal oder eine andere Wellenform) wird in den Eingang (blaue Buchse) eingespeist. Die Grundfrequenz, ihr Betrag und die dieser Frequenz am nächsten liegende Frequenz einer Note werden im seriellen Monitor angezeigt. Das Ziel dieses Projekts ist zu zeigen, wie die Bibliothek Arduino Audio Tools für eine FFT-Anwendung auf einem ESP32 verwendet werden kann. Im genannten Buch wird hierzu das Board ESP32-WROOM-32D von Espressif (Bild 1) verwendet. Es ist kompatibel zu Steckbrettern und hat die Maße 50,5 × 25,5 mm.
Bild 2 zeigt die Schaltung des Projekts. Das Modul Pmod I2S2 empfängt Audiosignale vom Signalgenerator PCSGU250 oder einem gleichwertigen Gerät in Ihrem Labor. Obwohl nur der ADC-Teil des Moduls verwendet wird, sind die Anschlüsse für ADC und DAC im Schaltplan dargestellt. Das Programm verarbeitet die Eingangssignale und zeigt die Ergebnisse im seriellen Monitor an.
Listing 1 zeigt das FFT-Programm. Dieses Programm ist in einem Software-Archiv zur Unterstützung des Buches Practical Audio DSP Projects with the ESP32 enthalten. Das Archiv ist als kostenloser Download im Bereich „Bücher“ auf der Elektor-Store-Webseite erhältlich. Weiter unten auf der Webseite bei „Downloads“ kann man die Zip-Datei Software_Practical Audio DSP Projects with the ESP32 (62 MB) herunterladen. Anschließend entpackt man das Archiv und sucht Sie das Programm FFT.ino im Material zu Kapitel 14.
/***************************************************************
* FAST FOURIER TRANSFORM
* ======================
*
* This program uses the Arduino Audio Tools library to display
* the FFT components of an input signal.
*
* Author : Dogan Ibrahim
* Program: FFT
* Date : May, 2023
*
* (This program is a modified version of the program developed
* by Phil Pschatzmann)
***************************************************************/
#include "AudioTools.h"
#include "AudioLibs/AudioRealFFT.h"
uint16_t sample_rate=44100;
uint8_t channels = 2;
I2SStream in;
AudioRealFFT fft;
StreamCopy copier(fft, in);
//
// Display fft result on Serial Monitor
//
void fftResult(AudioFFTBase &fft)
{
float diff;
auto result = fft.result();
if (result.magnitude>100)
{
Serial.print(result.frequency);
Serial.print(" ");
Serial.print(result.magnitude);
Serial.print(" => ");
Serial.print(result.frequencyAsNote(diff));
Serial.print( " diff: ");
Serial.println(diff);
}
}
void setup(void)
{
Serial.begin(115200);
//
// Configure in stream
//
auto configin = in.defaultConfig(RX_MODE);
configin.sample_rate = sample_rate;
configin.channels = channels;
configin.bits_per_sample = 16;
configin.i2s_format = I2S_STD_FORMAT;
configin.is_master = true;
configin.port_no = 0;
configin.pin_ws = 15; // LRCK
configin.pin_bck = 14; // SCLK
configin.pin_data = 22; // SDOUT
configin.pin_mck = 0;
in.begin(configin);
//
// Configure FFT
//
auto tcfg = fft.defaultConfig();
tcfg.length = 8192;
tcfg.channels = channels;
tcfg.sample_rate = sample_rate;
tcfg.bits_per_sample = 16;
tcfg.callback = &fftResult;
fft.begin(tcfg);
}
//
// Copy input signal to fft
//
void loop()
{
copier.copy();
}
Am Anfang des Programms finden sich die Header-Dateien AudioTools.h und AudioLibs/AudioRealFFT.h. Hier werden die Streams fft und in definiert. Innerhalb der Funktion setup() wird der I2S-Stream in (im RX_MODE) konfiguriert, indem die GPIO-Ports des ESP32 angegeben werden, mit denen er verbunden ist. Außerdem wird die FFT konfiguriert, indem die Länge auf 8.192 Samples gesetzt wird. Der Callback-Funktion wird der Name fftResult übergeben, so dass die FFT-Ergebnisse in dieser Funktion verfügbar sind. Die Funktion fftResult() zeigt die Frequenz, den Betrag und die Note an, die dieser Frequenz am nächsten liegt. Hinzu kommt noch die Differenz zwischen diesen beiden Frequenzen.
Ein Beispiel für die Ausgabe ist in Bild 3 zu sehen. Verarbeitet wurde hier ein Sinussignal mit einer Frequenz von 440 Hz. Das war einfach, nicht wahr?
Sie haben Fragen oder Kommentare?
Gerne können Sie sich an den Autor unter d.ibrahim@btinternet.com oder die Elektor-Redaktion unter der E-Mail-Adresse redaktion@elektor.de wenden.