ESP32 + INMP441: Die Frequenzgangmessung schlägt trotz Chirp-Sweep-Wiedergabe von Python fehlPython

Python-Programme
Anonymous
 ESP32 + INMP441: Die Frequenzgangmessung schlägt trotz Chirp-Sweep-Wiedergabe von Python fehl

Post by Anonymous »

Ich versuche, den Frequenzgang eines INMP441-Mikrofons mit einem ESP32 und einem Python-Skript zu messen. Der ESP32 erfasst 6000 Samples über I²S und sendet sie seriell. Ich spiele mit Python (

Code: Select all

sounddevice.play()
) und richten Sie dann das aufgezeichnete Mikrofonsignal am Sweep aus, um die Übertragungsfunktion zu berechnen.
Das resultierende Frequenzgangdiagramm ist jedoch falsch. Es zeigt entweder eine flache Linie, unerwartete Einbrüche oder verrauschte Artefakte. Ich vermute, dass der Sweep nicht richtig erfasst wird, bin mir aber nicht sicher, wo das Problem liegt.
Ich kann meinen Arduino-Code leider nicht posten, aber hier sind einige Hinweise:
Relevantes Arduino-Setup
  • Mikrocontroller: ESP32
  • Mikrofon: INMP441 (I²S-Digitalmikrofon)
  • Abtastrate: 48.000 Hz
  • Puffergröße: 6000 Samples (≈125 ms)
  • I²S-Konfiguration:

    Code: Select all

    I2S_MODE_MASTER | I2S_MODE_RX
  • Code: Select all

    I2S_BITS_PER_SAMPLE_32BIT
  • Code: Select all

    I2S_CHANNEL_FMT_ONLY_LEFT
[*]Datenfluss:
  • ESP32 sendet „READY“ über die serielle Schnittstelle
  • Erfasst dann 6000 Samples vom Mikrofon
  • Wendet DC-Offset-Entfernung und -Filterung an
  • Sendet die rohen Float-Samples seriell zwischen „DATA_START“ und „DATA_END“

Was meiner Meinung nach schief gehen könnte
  • Timing-Diskrepanz: Der ESP32 beginnt mit der Aufnahme, bevor der Sweep abgespielt wird, sodass er den Sweep möglicherweise ganz verpasst oder nur einen Teil davon erfasst.
  • Sweep nicht laut genug: Der Sweep erreicht das Mikrofon möglicherweise nicht deutlich oder das Mikrofon ist zu weit vom Lautsprecher entfernt.
  • Ausrichtungsfehler: Wenn der Sweep nicht sauber erfasst wird, findet die Kreuzkorrelation in Python nicht die richtige Verzögerung, was zu einem falsch ausgerichteten oder leeren „aligned_mic“-Array führt.
  • Fenster- oder FFT-Artefakte: Wenn die Ausrichtung auch nur um ein paar Samples abweicht, wird die FFT-basierte Übertragungsfunktion verzerrt.
  • Wie kann ich sicherstellen, dass der ESP32 den Sweep erfasst, während er abgespielt wird?
  • Gibt es eine bessere Möglichkeit, die Sweep-Wiedergabe und die Mikrofonaufnahme zu synchronisieren, ohne den Arduino-Code zu ändern?
  • Gibt es Best Practices für die vorherige Ausrichtung und Validierung des erfassten Signals? Berechnung der Übertragungsfunktion?
  • Berechne ich den Frequenzgang richtig?
Das ist mein Python-Code:

Code: Select all

import numpy as np
import matplotlib.pyplot as plt
import serial
import time
import sounddevice as sd
from scipy.signal import chirp, correlate, windows

# === Configuration ===
FS = 48000
DURATION = 0.0625  # 62.5 ms
N_SAMPLES = int(FS * DURATION)  # 3000 samples
NFFT = 8192
CALIBRATION_OFFSET = 120.0
SERIAL_PORT = 'COM8'
BAUD_RATE = 115200
SERIAL_TIMEOUT = 10

# === Generate Chirp Sweep ===
def generate_sweep():
t = np.linspace(0, DURATION, N_SAMPLES, endpoint=False)
f0 = 300
f1 = 15000
beta = np.log(f1 / f0) / DURATION
phase = 2 * np.pi * f0 * (np.exp(beta * t) - 1) / beta
sweep = np.sin(phase) * windows.hann(N_SAMPLES)
return sweep

sweep = generate_sweep()

# === Wait for ESP32 ===
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=SERIAL_TIMEOUT)
print("Waiting for READY from ESP32...")
while True:
line = ser.readline().decode('utf-8', errors='ignore').strip()
if line == "READY":
print("ESP32 is ready.  Playing sweep...")
break

# === Play Sweep ===
sd.play(sweep, samplerate=FS)
sd.wait()

# === Receive Mic Signal ===
print("Waiting for DATA_START...")
raw_data = []
while True:
line = ser.readline().decode('utf-8', errors='ignore').strip()
if line == "DATA_START":
raw_data = []
continue
elif line == "DATA_END":
break
else:
try:
raw_data.append(float(line))
except ValueError:
continue
ser.close()

mic_signal = np.array(raw_data)
if len(mic_signal) < 6000:
print(f"⚠️ Only {len(mic_signal)} samples received — expected 6000.")
exit()

# === Align and Extract Sweep Segment ===
corr = correlate(mic_signal, sweep, mode='valid')
lag = np.argmax(corr)
aligned_mic = mic_signal[lag:lag + N_SAMPLES]

if len(aligned_mic) != N_SAMPLES:
print(f"⚠️ Alignment failed: got {len(aligned_mic)} samples instead of {N_SAMPLES}")
exit()

aligned_mic *= windows.hann(N_SAMPLES)

# === Compute Transfer Function ===
S = np.fft.rfft(sweep, NFFT)
M = np.fft.rfft(aligned_mic, NFFT)
H = M / (S + 1e-12)
freqs = np.fft.rfftfreq(NFFT, 1 / FS)

mag_db = 20 * np.log10(np.abs(H) + 1e-12) + CALIBRATION_OFFSET
phase_deg = np.angle(H, deg=True)

# === Plot Bode-style Frequency Response ===
plt.figure(figsize=(12, 6))

plt.subplot(2, 1, 1)
plt.semilogx(freqs, mag_db, label="Mic Frequency Response", color='blue')
plt.axhline(84.0, color='gray', linestyle='--', label="Reference SPL (84 dB)")
plt.ylabel("Magnitude [dB SPL]")
plt.title("Mic Frequency Response (External Sweep Playback)")
plt.grid(True, which='both', linestyle='--', alpha=0.5)
plt.legend()

plt.subplot(2, 1, 2)
plt.semilogx(freqs, phase_deg, label="Phase Response", color='green')
plt.xlabel("Frequency [Hz]")
plt.ylabel("Phase [°]")
plt.grid(True, which='both', linestyle='--', alpha=0.5)
plt.legend()

plt.tight_layout()
plt.show()

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post