Однако полученный график частотной характеристики неверен — на нем либо видна ровная линия, либо неожиданные провалы, либо шумные артефакты. Я подозреваю, что развертка не фиксируется должным образом, но я не уверен, в чем проблема.
К сожалению, я не могу опубликовать свой код Arduino, но вот некоторая информация:
Соответствующая настройка Arduino
- Микроконтроллер: ESP32
- Микроконтроллер: INMP441 (цифровой микрофон I²S)
- Частота дискретизации: 48 000 Гц
- Размер буфера: 6 000 выборок (≈125 мс)
- I²S конфигурация:
I2S_MODE_MASTER | I2S_MODE_RX - I2S_BITS_PER_SAMPLE_32BIT
- I2S_CHANNEL_FMT_ONLY_LEFT
- ESP32 отправляет «ГОТОВО» по последовательному соединению
- Затем захватывает 6000 сэмплов с микрофона
- Применяет удаление и фильтрацию смещения постоянного тока
- Отправляет необработанные образцы с плавающей запятой по последовательному каналу между «DATA_START» и «DATA_END»
Что, по моему мнению, может пойти не так
- Несоответствие времени: ESP32 начинает запись до воспроизведения развертки, поэтому он может пропустить развертку полностью или захватить только ее часть.
- Развертка недостаточно громкая: Возможно, развертка не достигает микрофона четко или микрофон находится слишком далеко от динамика.
- Ошибка выравнивания. Если развертка не зафиксирована правильно, взаимная корреляция в Python не обнаружит правильную задержку, что приведет к смещению или пустому массиву Align_mic.
- Оконные артефакты или артефакты БПФ. Если выравнивание отклонено хотя бы на несколько выборок, передаточная функция на основе БПФ будет искажена.
- Как я могу гарантировать, что ESP32 захватывает развертку во время ее воспроизведения?
- Есть ли лучший способ синхронизировать воспроизведение развертки и запись с микрофона без изменения кода Arduino?
- Существуют ли передовые методы выравнивания и проверки захваченного сигнала перед вычисляем передаточную функцию?
- правильно ли я вычисляю частотную характеристику?
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"
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"
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()
Подробнее здесь: https://stackoverflow.com/questions/798 ... eep-playba
Мобильная версия