Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

EasyFFT – Zuverlässige FFT-Bibliothek für Arduino-Projekte

Komponenten und Zubehör

Arduino Nano R3 × 1

Apps und Online-Dienste

Über dieses Projekt

Die Frequenzmessung aus einem erfassten Signal ist anspruchsvoll, besonders auf Arduino mit begrenzter Rechenleistung. Nullstellen-Detektion (Zero-Crossing) funktioniert nicht bei Signalüberlagerungen mehrerer Frequenzen.

Dieses Projekt bietet einen einfach zu implementierenden FFT-Code für Arduino, ohne tiefes mathematisches Verständnis zu erfordern. Ideal für Musik-, Audio- und Signalanalyse-Projekte. Die Funktionsweise der FFT wird nicht erklärt – nur die Anwendung der Funktion.

Für 3× schnellere FFT mit ~5% Genauigkeitsverlust siehe ApproxFFT.

Schritt 1: Fast Fourier Transform (FFT)

Der FFT-Algorithmus (Cooley/Tukey, 1965) gilt als einer der wichtigsten Algorithmen des 20. Jahrhunderts. Er zerlegt Signale in gerad/ungerade Anteile und reduziert komplexe Multiplikationen von N² (DFT) auf N·logN – ein massiver Vorteil bei großen Abtastwerten.

Referenzen für mathematischen Hintergrund:

  1. Flylib – FFT Herleitung
  2. Jake VanderPlas – Understanding FFT
  3. CNX – FFT Details
  4. Wikipedia – Fast Fourier Transform

Schritt 2: Code-Erklärung – Schnelle Sinus/Kosinus

FFT benötigt häufig Sinus/Kosinus-Werte. Die Arduino-Standardfunktionen sind zu langsam. Lösung: Sinus-Werte für 0–90° als Vielfache von 255 in einem Byte-Array sine_data[91] speichern (4× weniger Speicher als float). Wichtig: sine_data[] und f_peaks[] müssen als globale Variablen am Programmstart deklariert werden.

byte sine_data[91] = {
  0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70, 75, 79, 83,
  87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146,
  150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 189, 192, 195,
  198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, 231,
  233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 251,
  252, 253, 253, 254, 254, 254, 255, 255, 255, 255
};
float f_peaks[5]; // Top 5 Frequenzen (absteigend)

Die Hilfsfunktionen sine() und cosine() nutzen diese Tabelle mit einer einzigen Float-Division – schneller als Standardbibliothek, minimaler Genauigkeitsverlust. Vorteil: ~8 ms bei 64 Samples, ~20 ms bei 128 Samples.

Schritt 3: FFT-Funktion

FFT funktioniert nur für Potenzen von 2 (2, 4, 8, 16, 32, 64, 128…). Andere Werte werden auf die nächstkleinere Potenz gerundet (z. B. 70 → 64).

Empfohlene Sample-Größen: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048…

Auf Arduino Nano: Arrays out_r und out_im (float) limitieren nutzbare Samples auf ≤128 (RAM).

Ablauf der FFT-Funktion:

  1. Bit-reverse Order für Sample-Größe generieren
  2. Eingabedaten entsprechend umsortieren
  3. FFT-Berechnung (Cooley-Tukey)
  4. Amplituden aus Real/Imaginär Teil berechnen
  5. Peaks detektieren und absteigend sortieren
  6. Ergebnisse in globalem Array f_peaks[] ablegen

Für Zugriff auf Rohdaten (außer Peaks) müssen lokale Variablen in globale kopiert werden.

Schritt 4: Code testen

Test mit Dreieckssignal: Sampling 10 Hz, Signal 1,25 Hz. Ergebnisse stimmen mit Scilab-FFT überein (geringe Abweichung durch schnelle Sinus-Approximation).

Ausgabe-Frequenzbins: 1,25 Hz und 3,75 Hz (Oberschwingung). Exakte Werte nicht garantiert – Werte liegen in Frequenz-Bins.

Laufzeiten (Arduino Nano):

Schritt 5: Fazit

Der Code eignet sich für Echtzeitanwendungen (~30 ms bei 64 Samples). Auflösung limitiert durch Sample-Anzahl und RAM. Mit Arduino Mega oder leistungsstärkeren Boards (Teensy, ESP32) lässt sich die Genauigkeit steigern.

Fragen, Vorschläge, Korrekturen gerne in den Kommentaren.

Vollständiger Code (EasyFFT)

/* 
// Beispiel-Daten:
int data[64] = {14, 30, 35, 34, 34, 40, 46, 45, 30, 4, -26, -48, -55, -49, -37, -28, -24, -22, -13, 6, 32, 55, 65, 57, 38, 17, 1, -6, -11, -19, -34, -51, -61, -56, -35, -7, 18, 32, 35, 34, 35, 41, 46, 43, 26, -2, -31, -50, -55, -47, -35, -27, -24, -21, -10, 11, 37, 58, 64, 55, 34, 13, -1, -7};
*/
//---------------------------------------------------------------------------
// byte sine_data[91] = { ... }; // HIER EINFÜGEN (global)
// float f_peaks[5];             // HIER EINFÜGEN (global)
//---------------------------------------------------------------------------

void setup() {
  Serial.begin(250000);
}

void loop() {
  /* 
  // Beispiel-Nutzung:
  FFT(data, 64, 100); // 64 Samples, 100 Hz Sampling
  Serial.println(f_peaks[0]);
  Serial.println(f_peaks[1]);
  delay(99999);
  */
}

//----------------------------- FFT Funktion ----------------------------------------------
float FFT(int in[], int N, float Frequency) {
  unsigned int data[13] = {1,2,4,8,16,32,64,128,256,512,1024,2048};
  int a, c1, f, o, x;
  a = N;
  for (int i = 0; i < 12; i++) {
    if (data[i] <= a) { o = i; }
  }
  int in_ps[data[o]] = {};
  float out_r[data[o]] = {};
  float out_im[data[o]] = {};

  x = 0;
  for (int b = 0; b < o; b++) {
    c1 = data[b];
    f = data[o] / (c1 + c1);
    for (int j = 0; j < c1; j++) {
      x = x + 1;
      in_ps[x] = in_ps[j] + f;
    }
  }
  for (int i = 0; i < data[o]; i++) {
    if (in_ps[i] < a) out_r[i] = in[in_ps[i]];
    if (in_ps[i] > a) out_r[i] = in[in_ps[i] - a];
  }

  int i10, i11, n1;
  float e, c, s, tr, ti;
  for (int i = 0; i < o; i++) {
    i10 = data[i];
    i11 = data[o] / data[i+1];
    e = 360.0 / data[i+1];
    e = 0 - e;
    n1 = 0;
    for (int j = 0; j < i10; j++) {
      c = cosine(e * j);
      s = sine(e * j);
      n1 = j;
      for (int k = 0; k < i11; k++) {
        tr = c * out_r[i10 + n1] - s * out_im[i10 + n1];
        ti = s * out_r[i10 + n1] + c * out_im[i10 + n1];
        out_r[n1 + i10] = out_r[n1] - tr;
        out_r[n1] = out_r[n1] + tr;
        out_im[n1 + i10] = out_im[n1] - ti;
        out_im[n1] = out_im[n1] + ti;
        n1 = n1 + i10 + i10;
      }
    }
  }

  // Amplitude aus komplexer Zahl
  for (int i = 0; i < data[o-1]; i++) {
    out_r[i] = sqrt(out_r[i]*out_r[i] + out_im[i]*out_im[i]); // sqrt weglassen für mehr Speed
    out_im[i] = i * Frequency / N;
  }

  // Peak-Detektion
  x = 0;
  for (int i = 1; i < data[o-1] - 1; i++) {
    if (out_r[i] > out_r[i-1] && out_r[i] > out_r[i+1]) {
      in_ps[x] = i;
      x++;
    }
  }

  // Nach Magnitude sortieren (absteigend)
  float s = 0, c = 0;
  for (int i = 0; i < x; i++) {
    for (int j = c; j < x; j++) {
      if (out_r[in_ps[i]] < out_r[in_ps[j]]) {
        s = in_ps[i];
        in_ps[i] = in_ps[j];
        in_ps[j] = s;
      }
    }
    c++;
  }

  // Top 5 in globales Array
  for (int i = 0; i < 5; i++) {
    f_peaks[i] = out_im[in_ps[i]];
  }
}

float sine(int i) {
  int j = i;
  while (j < 0) j += 360;
  while (j > 360) j -= 360;
  if (j >= 0 && j < 91) return sine_data[j] / 255.0;
  else if (j < 181) return sine_data[180 - j] / 255.0;
  else if (j < 271) return -sine_data[j - 180] / 255.0;
  else return -sine_data[360 - j] / 255.0;
}

float cosine(int i) {
  int j = i;
  while (j < 0) j += 360;
  while (j > 360) j -= 360;
  if (j >= 0 && j < 91) return sine_data[90 - j] / 255.0;
  else if (j < 181) return -sine_data[j - 90] / 255.0;
  else if (j < 271) return -sine_data[270 - j] / 255.0;
  else return sine_data[j - 270] / 255.0;
}
//------------------------------------------------------------------------------------

Schaltplan

EasyFFT – Zuverlässige FFT-Bibliothek für Arduino-Projekte
Referenzschaltung – nur Arduino Nano nötig

Herstellungsprozess

  1. Die Fourier-Transformation verstehen: Grundlagen, Anwendungen und Signalzerlegung
  2. DTMF-Decoder mit Arduino bauen – ganz ohne zusätzlichen IC
  3. SERENA: Eigenes Arduino Mega 2560 Alarmsystem mit TFT-LCD-Touchscreen
  4. Python 3 zu Arduino UNO: Einfache Steuerung & LED-Demo
  5. Smartes Temperatur-Monitoring für Schulen – Arduino, Raspberry Pi, Sigfox & ThingSpeak
  6. Effiziente 8-Bit-I/O-Port-Bibliothek für Arduino – Digitale Steuerung erweitern
  7. Arduino 64-Tasten-Prototyp-Keyboard-Matrix – Eigenes Keyboard mit Schieberegistern bauen
  8. Isolierter Analogeingang für Arduino: Spannung/Strom galvanisch trennen
  9. Smartes Indoor-Navigations-Roboter-Kit für Arduino-Projekte
  10. HS-Code für Hydraulikpumpen – Klassifizierung und Verwendung