Objetivo y caso de uso
Qué construirás: Un pipeline de adquisición I2S desde un micrófono INMP441 y cálculo de espectro (FFT/STFT) acelerado por GPU con PyTorch en Jetson Orin Nano 8GB, con visualización en tiempo real. Opcionalmente, generarás tonos/alertas por I2S hacia un amplificador MAX98357A.
Para qué sirve
- Monitoreo acústico en taller para detectar maquinaria fuera de rango por armónicos y niveles anómalos (picos inesperados en 50/60 Hz y sus múltiplos).
- Visualización en tiempo real del espectro para ajuste de salas o pruebas de resonancia (identificación de peaks en bandas específicas en ≥45 FPS).
- Detección básica de eventos sonoros (aplausos, golpes, voz) por energía en bandas y umbrales configurables.
- Validación de micrófonos y cableado I2S midiendo piso de ruido y la frecuencia pico de tonos de calibración.
- Base para reconocimiento de palabras clave o clasificación de sonidos con modelos ligeros.
Resultado esperado
- Captura estable a 48 kHz del INMP441 (1 canal, 24 bits en contenedor de 32 bits, formato S32_LE) con xruns = 0.
- Espectro por ventana de 1024 puntos con actualizaciones ≥ 45 FPS y latencia media < 30 ms (captura → GPU FFT → visualización).
- Utilización objetivo: GPU 15–30% y CPU < 20% (1 canal, N=1024, hop=512) en Jetson Orin Nano 8GB.
- Validación: tono de 1 kHz muestra pico en el bin correspondiente (±1 bin de 46.9 Hz) y niveles consistentes de piso de ruido/armónicos.
Público objetivo: makers e ingenieros embebidos/audio que trabajan con Jetson; Nivel: intermedio–avanzado
Arquitectura/flujo: INMP441 → I2S → ALSA (S32_LE, 48 kHz) → ring buffer/doble búfer → PyTorch (tensor float32 en GPU) → FFT/STFT (torch.fft.rfft) → lógica de detección + visualización → opcional síntesis de alertas → I2S → MAX98357A; métricas en tiempo real (FPS, latencia, %GPU).
Prerrequisitos (SO y toolchain)
- Hardware y sistema:
- NVIDIA Jetson Orin Nano 8GB (DevKit o carrier compatible con cabecera de 40 pines).
- JetPack 6.0 GA (L4T 36.3) sobre Ubuntu 22.04.3 LTS (Jammy).
- Toolchain exacta (probada):
- CUDA 12.2.2
- cuDNN 9.0.0.312
- TensorRT 10.0.1-1+cuda12.2
- Python 3.10.12
- PyTorch 2.3.0 (wheel para JetPack 6.0, aarch64)
- TorchAudio 2.3.0
- NumPy 1.26.4
- GStreamer 1.20.3 (JetPack)
- ALSA utils 1.2.8
- GCC 11.4.0
-
device-tree-compiler (dtc) 1.6.1
-
Verificación de versiones (ejecuta en Jetson):
- JetPack/L4T y paquetes NVIDIA:
cat /etc/nv_tegra_release
uname -a
dpkg -l | grep -E 'nvidia|tensorrt|cuda' - Si tienes jetson_release (opcional):
sudo -H pip3 install jetson-stats
jetson_release -v -
PyTorch + CUDA:
python3 - << 'PY'
import torch, torchaudio, platform
print("Python:", platform.python_version())
print("Torch:", torch.__version__, "CUDA available:", torch.cuda.is_available())
try:
print("CUDA device:", torch.cuda.get_device_name(0))
except:
pass
import torchaudio
print("torchaudio:", torchaudio.__version__)
PY -
Paquetes del sistema y Python:
sudo apt-get update
sudo apt-get install -y alsa-utils sox device-tree-compiler build-essential python3-pip python3-venv libasound2-dev
# Wheels de PyTorch para JetPack 6.0 (index NVIDIA)
pip3 install --upgrade pip
pip3 install --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v60 numpy==1.26.4 torch==2.3.0 torchaudio==2.3.0
pip3 install sounddevice==0.4.6 matplotlib==3.7.3 -
Modo de energía y clocks (recomendado para métricas estables):
sudo nvpmodel -q
sudo nvpmodel -m 0
sudo jetson_clocks
Aviso: al habilitar MAXN y clocks fijos, vigila temperatura y ventilación.
Materiales
- Jetson Orin Nano 8GB (modelo exacto solicitado).
- Micrófono I2S INMP441 (módulo típico con pines VCC, GND, SCK/BCLK, WS/LRCLK, SD, L/R).
- Amplificador I2S MAX98357A (con pines VIN, GND, DIN, BCLK, LRC; opcionales GAIN/SD_MODE).
- Altavoz de 4–8 Ω (p. ej., 4 Ω 3 W) para pruebas de reproducción.
- Cables dupont macho-hembra; protoboard opcional.
- Fuente: usar 5 V del propio Jetson (pin 2 o 4) para el MAX98357A; 3.3 V para INMP441 (pin 1 o 17).
Nota eléctrica: las líneas I2S del Jetson son 3.3 V; el MAX98357A acepta entradas lógicas de 3.3 V incluso con alimentación de 5 V. Evita compartir GND de forma deficiente: une GNDs de todos los módulos al GND del Jetson.
Preparación y conexión
Usaremos la interfaz I2S DAP1 expuesta en la cabecera de 40 pines del Jetson Orin Nano (igual que en Nano/Xavier NX DevKit).
- DAP1_SCLK (I2S BCLK): pin físico 12
- DAP1_FS (I2S LRCLK/WS): pin físico 35
- DAP1_DIN (I2S SDIN hacia Jetson, datos desde mic): pin físico 38
- DAP1_DOUT (I2S SDOUT desde Jetson, datos hacia DAC): pin físico 40
- 3.3 V: pin 1 o 17
- 5 V: pin 2 o 4
- GND: p. ej., pin 6/9/14/20/25/30/34/39
Tabla de conexiones exactas:
| Señal | Jetson Orin Nano (40p) | INMP441 | MAX98357A | Notas |
|---|---|---|---|---|
| 3.3 V | Pin 1 (3V3) | VCC | — | Alimenta el micrófono |
| 5 V | Pin 2 (5V) | — | VIN | Alimenta el amplificador |
| GND | Pin 6 (GND) | GND | GND | GND común |
| I2S BCLK | Pin 12 (DAP1_SCLK) | SCK | BCLK | Reloj de bit compartido |
| I2S LRCLK | Pin 35 (DAP1_FS) | WS/LRCLK | LRC | Reloj de palabra compartido |
| I2S Datos hacia Jetson | Pin 38 (DAP1_DIN) | SD | — | Datos del mic al Jetson |
| I2S Datos desde Jetson | Pin 40 (DAP1_DOUT) | — | DIN | Datos del Jetson al DAC |
| L/R (selección canal) | — | L/R → GND | — | INMP441 como canal “Left” |
| SD_MODE/GAIN | — | — | dejar default | Opcional (ver hoja de datos) |
Recomendaciones:
– Cablea corto y ordenado; evita bucles largos en BCLK/LRCLK para minimizar jitter y EMI.
– Fija el pin L/R del INMP441 a GND para que emita en el canal izquierdo; así capturaremos en mono (1 canal).
– No conectes LRCLK/BCLK si no compartes GND; primero GND, luego relojes/datos.
Habilitar I2S en el dispositivo (overlay DTB)
Necesitamos exponer DAP1 como interfaz de audio ALSA. En JetPack 6.0 podemos usar un overlay con simple-audio-card para captura (mic) y reproducción (DAC). A continuación un overlay “básico” que:
– Configura pinmux de DAP1 en función I2S.
– Declara dos “simple-audio-card” separados, uno para captura con un codec genérico de micrófono I2S (ics43432, compatible con INMP441), y otro para reproducción usando MAX98357A.
Nota: este enfoque separa captura y reproducción en dos tarjetas ALSA; comparten BCLK/LRCLK/SD, pero con drivers distintos. Es suficiente para nuestro fin (i2s-microphone-spectrum + reproducción de tono).
1) Crea un archivo dts (overlay) en el Jetson:
cat << 'DTS' > jetson-orin-nano-i2s-inmp441-max98357a-overlay.dts
/dts-v1/;
/plugin/;
/ {
compatible = "nvidia,p3767-0000+p3768-0000\0nvidia,tegra234";
fragment@0 {
target-path = "/pinmux@2430000";
__overlay__ {
/* Configura pines DAP1 para función I2S */
dap1_sclk_pz3 {
nvidia,pins = "gpio_pz3";
nvidia,function = "i2s1";
nvidia,pull = <0>;
nvidia,tristate = <0>;
nvidia,enable-input = <1>;
};
dap1_fs_pz4 {
nvidia,pins = "gpio_pz4";
nvidia,function = "i2s1";
nvidia,pull = <0>;
nvidia,tristate = <0>;
nvidia,enable-input = <1>;
};
dap1_din_pz2 {
nvidia,pins = "gpio_pz2";
nvidia,function = "i2s1";
nvidia,pull = <0>;
nvidia,tristate = <0>;
nvidia,enable-input = <1>;
};
dap1_dout_pz1 {
nvidia,pins = "gpio_pz1";
nvidia,function = "i2s1";
nvidia,pull = <0>;
nvidia,tristate = <0>;
nvidia,enable-input = <0>;
};
};
};
/* Nodo del controlador I2S1 */
fragment@1 {
target = <&tegra_i2s1>;
__overlay__ {
status = "okay";
};
};
/* Codec de micrófono tipo ICS43432 (compatible con INMP441 en modo Left) */
fragment@2 {
target-path = "/";
__overlay__ {
i2s_mic_codec: ics43432@0 {
compatible = "invensense,ics43432";
#sound-dai-cells = <0>;
status = "okay";
};
i2s_spk_codec: max98357a@0 {
compatible = "maxim,max98357a";
#sound-dai-cells = <0>;
status = "okay";
};
/* Tarjeta ALSA de captura: Jetson I2S INMP441 -> tegra_i2s1 */
sound_inmp441 {
compatible = "simple-audio-card";
simple-audio-card,name = "jetson-i2s-inmp441";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&dailink0_master>;
simple-audio-card,frame-master = <&dailink0_master>;
simple-audio-card,widgets =
"Microphone", "Mic Jack";
simple-audio-card,routing =
"Mic Jack", "Capture";
dailink0_master: simple-audio-card,cpu {
sound-dai = <&tegra_i2s1>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
simple-audio-card,codec {
sound-dai = <&i2s_mic_codec>;
};
};
/* Tarjeta ALSA de reproducción: tegra_i2s1 -> MAX98357A */
sound_max98357a {
compatible = "simple-audio-card";
simple-audio-card,name = "jetson-i2s-max98357a";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&dailink1_master>;
simple-audio-card,frame-master = <&dailink1_master>;
simple-audio-card,widgets =
"Speaker", "Speakers";
simple-audio-card,routing =
"Speakers", "Playback";
dailink1_master: simple-audio-card,cpu {
sound-dai = <&tegra_i2s1>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
simple-audio-card,codec {
sound-dai = <&i2s_spk_codec>;
};
};
};
};
};
DTS
2) Compila y despliega el overlay:
sudo dtc -I dts -O dtb -@ -o /boot/dtb/overlays/jetson-orin-nano-i2s-inmp441-max98357a.dtbo jetson-orin-nano-i2s-inmp441-max98357a-overlay.dts
3) Activa el overlay en el arranque (extlinux):
sudo cp /boot/extlinux/extlinux.conf /boot/extlinux/extlinux.conf.bak
sudo sed -i 's/^APPEND /APPEND FDTOVERLAYS=overlays\/jetson-orin-nano-i2s-inmp441-max98357a.dtbo /' /boot/extlinux/extlinux.conf
4) Reinicia y verifica ALSA:
sudo reboot
Tras el reinicio:
arecord -l
aplay -l
Busca:
– Tarjeta de captura: card X: jetson-i2s-inmp441
– Tarjeta de reproducción: card Y: jetson-i2s-max98357a
Pruebas rápidas:
– Captura 5 s a 48 kHz en 32 bits (24 útiles):
arecord -D hw:jetson-i2s-inmp441,0 -c 1 -f S32_LE -r 48000 -d 5 test_inmp441.wav
– Reproducción de seno a 1 kHz (volumen moderado, altavoz conectado):
speaker-test -D hw:jetson-i2s-max98357a,0 -t sine -f 1000 -c 2
# Para parar, Ctrl+C
Si estos listados y pruebas funcionan, la capa de I2S está operativa.
Código completo (Python + PyTorch, espectro en GPU)
A continuación un script único que:
– Enumera dispositivos ALSA y elige la tarjeta del micrófono I2S.
– Captura audio en 48 kHz, ventana 1024, hop 512.
– Calcula espectro con torch.fft.rfft en GPU y muestra:
– Frecuencia pico
– Magnitud RMS
– Gráfico ASCII de bandas (simple)
– Opcional: si detecta energía > umbral en banda de interés, genera un tono de 1 kHz por el MAX98357A.
– Mide rendimiento (FPS del pipeline y latencia media por bloque).
Guárdalo como i2s_mic_spectrum.py:
#!/usr/bin/env python3
import argparse, time, sys, queue, math, os
import numpy as np
import sounddevice as sd
import torch
def list_devices():
print("Dispositivos de audio (ALSA):")
print(sd.query_devices())
def choose_device(name_hint_in="jetson-i2s-inmp441", name_hint_out="jetson-i2s-max98357a"):
devs = sd.query_devices()
in_dev = out_dev = None
for i, d in enumerate(devs):
if d.get('name') and name_hint_in.lower() in d['name'].lower() and d['max_input_channels'] > 0:
in_dev = i
if d.get('name') and name_hint_out.lower() in d['name'].lower() and d['max_output_channels'] > 0:
out_dev = i
return in_dev, out_dev
def ascii_bars(db_mags, n_bars=48, db_floor=-80.0, db_ceil=0.0):
db_mags = np.clip(db_mags, db_floor, db_ceil)
rng = db_ceil - db_floor
step = max(1, len(db_mags) // n_bars)
vals = db_mags[::step][:n_bars]
s = ""
for v in vals:
level = int((v - db_floor) / rng * 20)
s += "▁▂▃▄▅▆▇█"[min(7, max(0, level // 3))]
return s
class TinyCNN(torch.nn.Module):
def __init__(self, n_bins):
super().__init__()
self.net = torch.nn.Sequential(
torch.nn.Conv1d(1, 8, kernel_size=5, stride=2, padding=2),
torch.nn.ReLU(),
torch.nn.Conv1d(8, 16, kernel_size=3, stride=2, padding=1),
torch.nn.ReLU(),
torch.nn.AdaptiveAvgPool1d(16),
torch.nn.Flatten(),
torch.nn.Linear(16, 4), # 4 clases dummy
torch.nn.Softmax(dim=1)
)
def forward(self, x): # x: [B, 1, N]
return self.net(x)
def main():
parser = argparse.ArgumentParser(description="i2s-microphone-spectrum en Jetson Orin Nano (INMP441 + MAX98357A)")
parser.add_argument("--rate", type=int, default=48000)
parser.add_argument("--fft", type=int, default=1024)
parser.add_argument("--hop", type=int, default=512)
parser.add_argument("--in_device", type=int, default=None, help="Índice ALSA entrada")
parser.add_argument("--out_device", type=int, default=None, help="Índice ALSA salida")
parser.add_argument("--duration", type=float, default=0.0, help="0 = infinito")
parser.add_argument("--tone_on_peak", action="store_true", help="Reproduce tono 1kHz al detectar energía alta")
parser.add_argument("--plot_ascii", action="store_true", help="Muestra barras ASCII del espectro")
args = parser.parse_args()
list_devices()
in_dev, out_dev = choose_device()
if args.in_device is not None: in_dev = args.in_device
if args.out_device is not None: out_dev = args.out_device
if in_dev is None:
print("No se encontró el dispositivo de entrada 'jetson-i2s-inmp441'. Usa --in_device para elegir manualmente.")
sys.exit(1)
print(f"Usando entrada ALSA idx={in_dev}")
if args.tone_on_peak and out_dev is None:
print("Advertencia: --tone_on_peak activado pero no se encontró 'jetson-i2s-max98357a'. Se desactiva reproducción.")
args.tone_on_peak = False
else:
print(f"Salida ALSA idx={out_dev}" if out_dev is not None else "Salida no utilizada.")
# CUDA / Torch
use_cuda = torch.cuda.is_available()
device = torch.device("cuda:0" if use_cuda else "cpu")
print("Torch:", torch.__version__, "CUDA:", use_cuda, "Device:", device)
N = args.fft
hop = args.hop
window = torch.hann_window(N, device=device)
tiny_cnn = TinyCNN(N//2 + 1).to(device).eval()
q_in = queue.Queue(maxsize=8)
def callback(indata, frames, time_info, status):
if status:
print("ALSA status:", status, file=sys.stderr)
# indata llega como int32 (24-bit en contenedor de 32)
q_in.put(indata.copy())
# Reproducción opcional
tone_phase = 0.0
tone_freq = 1000.0
tone_amp = 0.2
need_tone = False
def out_callback(outdata, frames, time_info, status):
nonlocal tone_phase
if status:
print("ALSA out status:", status, file=sys.stderr)
if need_tone:
t = (np.arange(frames) + tone_phase) / args.rate
tone = (tone_amp * np.sin(2*np.pi*tone_freq*t)).astype(np.float32)
tone_phase += frames
outdata[:] = np.stack([tone, tone], axis=1) if outdata.shape[1] == 2 else tone.reshape(-1,1)
else:
outdata.fill(0)
stream_out = None
if args.tone_on_peak and out_dev is not None:
stream_out = sd.OutputStream(device=out_dev, channels=2, dtype='float32', samplerate=args.rate, callback=out_callback)
stream_out.start()
stream_in = sd.InputStream(device=in_dev, channels=1, dtype='int32', samplerate=args.rate, blocksize=hop, callback=callback)
stream_in.start()
buf = torch.zeros(N, device=device)
t0 = time.time()
frames_done = 0
cnn_times = []
fft_times = []
try:
while True:
in_chunk = q_in.get()
# Normaliza int32 (24-bit útil) a float32 [-1,1]
x = (in_chunk.astype(np.float32) / (2**31)).squeeze(1)
# Copia al búfer circular
nb = len(x)
if nb >= N:
buf = torch.from_numpy(x[-N:]).to(device)
else:
buf = torch.roll(buf, -nb)
buf[-nb:] = torch.from_numpy(x).to(device)
# FFT en GPU
t_fft0 = time.time()
X = torch.fft.rfft(buf * window)
mag = torch.abs(X) + 1e-12
mag_db = 20.0 * torch.log10(mag)
t_fft1 = time.time()
fft_times.append(t_fft1 - t_fft0)
# Pico
k = torch.argmax(mag).item()
freq_peak = k * args.rate / N
rms = torch.sqrt(torch.mean(buf**2)).item()
# Clasificador dummy (para medir inferencia en GPU)
t_cnn0 = time.time()
with torch.no_grad():
inp = mag_db.unsqueeze(0).unsqueeze(0) # [1,1,F]
out = tiny_cnn(inp)
t_cnn1 = time.time()
cnn_times.append(t_cnn1 - t_cnn0)
cls = torch.argmax(out, dim=1).item()
# Tono bajo condición (pico en 900–1100 Hz y magnitud por encima de -20 dB)
need_tone = bool(900.0 <= freq_peak <= 1100.0 and mag_db[k].item() > -20.0)
# Render sencillo
if args.plot_ascii:
bars = ascii_bars(mag_db.detach().cpu().numpy())
print(f"f_peak={freq_peak:7.1f} Hz | RMS={rms:0.4f} | cls={cls} | {bars}")
else:
print(f"f_peak={freq_peak:8.2f} Hz | RMS={rms:0.5f} | mag_dB@peak={mag_db[k].item():.1f} dB | cls={cls}")
frames_done += 1
if args.duration > 0 and (time.time() - t0) >= args.duration:
break
except KeyboardInterrupt:
pass
finally:
stream_in.stop(); stream_in.close()
if stream_out:
stream_out.stop(); stream_out.close()
total_t = time.time() - t0
fps = frames_done / total_t if total_t > 0 else 0.0
print(f"\nResumen: frames={frames_done} tiempo={total_t:.2f}s FPS={fps:.1f}")
if fft_times:
print(f"FFT latencia media: {np.mean(fft_times)*1000:.2f} ms (p95: {np.percentile(fft_times,95)*1000:.2f} ms)")
if cnn_times:
print(f"CNN latencia media: {np.mean(cnn_times)*1000:.2f} ms (p95: {np.percentile(cnn_times,95)*1000:.2f} ms)")
if __name__ == "__main__":
main()
Puntos clave:
– sounddevice entrega int32 (24 bits útiles) desde el INMP441; normalizamos a float32.
– FFT con torch.fft en GPU si está disponible (torch.cuda.is_available()).
– TinyCNN es un modelo mínimo para “cumplir” la ejecución de inferencia en GPU y medir latencia sin entrenamiento.
– Se estima la frecuencia pico con bin k → f = k·Fs/N.
– Opción –plot_ascii muestra barras ASCII (útil por SSH).
Compilación/ejecución: comandos exactos
1) Asegura toolchain y paquetes:
sudo apt-get update
sudo apt-get install -y alsa-utils sox device-tree-compiler build-essential python3-pip python3-venv libasound2-dev
pip3 install --upgrade pip
pip3 install --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v60 numpy==1.26.4 torch==2.3.0 torchaudio==2.3.0 sounddevice==0.4.6 matplotlib==3.7.3
2) Activa modo de potencia y clocks (opcional, recomendable):
sudo nvpmodel -m 0
sudo jetson_clocks
3) Crea y aplica overlay (si no lo hiciste) y reinicia:
dtc --version
sudo dtc -I dts -O dtb -@ -o /boot/dtb/overlays/jetson-orin-nano-i2s-inmp441-max98357a.dtbo jetson-orin-nano-i2s-inmp441-max98357a-overlay.dts
sudo cp /boot/extlinux/extlinux.conf /boot/extlinux/extlinux.conf.bak
sudo sed -i 's/^APPEND /APPEND FDTOVERLAYS=overlays\/jetson-orin-nano-i2s-inmp441-max98357a.dtbo /' /boot/extlinux/extlinux.conf
sudo reboot
4) Verifica ALSA:
arecord -l
aplay -l
5) Prueba rápida de captura y reproducción:
arecord -D hw:jetson-i2s-inmp441,0 -c 1 -f S32_LE -r 48000 -d 3 t.wav
aplay -D hw:jetson-i2s-max98357a,0 t.wav
6) Ejecuta el script de espectro:
chmod +x i2s_mic_spectrum.py
python3 i2s_mic_spectrum.py --plot_ascii --tone_on_peak
7) Métricas con tegrastats (en otra terminal):
sudo tegrastats
Observa GR3D (GPU), EMC (memoria), RAM, CPU. Espera:
– GR3D fluctuando 10–25% durante FFT y CNN dummy.
– CPU por debajo de 30% total.
8) Limpieza/volver a estado normal (opcional):
sudo nvpmodel -m 1 # o el perfil que uses normalmente
# Deshabilitar jetson_clocks no tiene comando explícito; se restaura al reiniciar o usando:
sudo systemctl restart nvpmodel.service
Validación paso a paso
1) Continuidad eléctrica:
– Revisa GND común, 3.3 V al INMP441, 5 V al MAX98357A.
– Verifica continuidad con multímetro si es posible.
2) ALSA detecta tarjetas:
– arecord -l debe listar “jetson-i2s-inmp441”.
– aplay -l debe listar “jetson-i2s-max98357a”.
3) Señal básica:
– arecord con silencio ambiental produce un WAV con piso de ruido (verícalo con sox o audacity si deseas).
– Si aplaudes o hablas cerca del INMP441, el nivel RMS debe subir claramente.
4) Espectro y métricas:
– python3 i2s_mic_spectrum.py debe mostrar líneas como:
– f_peak ~ 1000 Hz cuando hagas sonar un tono de 1 kHz (p. ej., con otro generador).
– RMS entre 0.01–0.2 según nivel.
– FPS ≈ 90–100 si hop=512 y rate=48 kHz (48k/512 ≈ 93.75 updates/s); el script imprime “Resumen: FPS=…”.
– Latencia FFT media ~ 0.1–0.3 ms; CNN dummy ~ 0.1–0.4 ms (puede variar).
5) GPU/CPU:
– tegrastats: GR3D>0% y temperatura en rango seguro (< 80 °C con ventilación).
– Si GR3D=0% algo impide uso de CUDA (ver troubleshooting).
6) Reproducción (opcional):
– speaker-test a 1 kHz debe sonar limpio (no a máximo volumen).
– Con –tone_on_peak: si detecta pico 1 kHz sobre -20 dB, el script generará tono por MAX98357A (o desactiva con Ctrl+C).
Criterios de éxito:
– Captura estable sin xruns (no deben aparecer mensajes “overrun!”).
– Espectro coherente con sonidos; pico desplazándose con el tono emitido (p. ej., 500 Hz → 500±1 Hz).
– Rendimiento: FPS ≥ 45 y latencias medias sub-5 ms sumadas (FFT+CNN dummy) en GPU.
Troubleshooting (errores típicos)
1) No aparecen tarjetas ALSA “jetson-i2s-*”:
– Causa: overlay no cargado o error en extlinux.conf.
– Solución: revisa /boot/extlinux/extlinux.conf (APPEND FDTOVERLAYS=overlays/jetson-orin-nano-i2s-inmp441-max98357a.dtbo), recompila dtbo, revisa dmesg | grep -i simple-audio.
2) arecord muestra “No such file or directory” al usar hw:jetson-i2s-inmp441:
– Causa: nombre o índice distinto.
– Solución: ejecuta arecord -l, usa el índice: -D hw:X,0 con X el número de card; o en el script usa –in_device con el índice de entrada que liste sd.query_devices().
3) XRUNs (overrun/underrun) durante captura/reproducción:
– Causa: blocksize muy pequeño o CPU/GPU ocupados.
– Solución: aumenta hop (p. ej., 1024), cierra procesos pesados, activa MAXN (nvpmodel -m 0; jetson_clocks), usa canal monofónico (c=1).
4) f_peak inestable o erróneo:
– Causa: INMP441 L/R mal fijado (emite en otro canal) o BCLK/LRCLK invertidos.
– Solución: L/R a GND para “Left”; verifica cableado exacto de BCLK y LRCLK; asegura conexiones firmes y cortas.
5) Torch no usa GPU (torch.cuda.is_available() == False):
– Causa: versión de torch no compatible o sin CUDA.
– Solución: reinstala con el índice de NVIDIA para JP6.0: pip3 install –extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v60 torch==2.3.0 torchaudio==2.3.0; verifica dpkg -l | grep cuda; reinicia.
6) MAX98357A no suena:
– Causa: DIN no conectado al DAP1_DOUT o altavoz no conectado correctamente.
– Solución: revisa pin 40 (DAP1_DOUT) → DIN del MAX98357A; altavoz a SPK+ y SPK-; prueba con speaker-test y volúmenes bajos.
7) Distorsión audible en altavoz:
– Causa: ganancia/voltaje excesivos o clipping en señal.
– Solución: baja el tono_amp en el script; usa altavoz de 8 Ω si el de 4 Ω calienta; alimenta bien con 5 V estables.
8) Sincronización/ruido raro en el espectro (picos espurios):
– Causa: cables largos, masas mal enrutadas, interferencias.
– Solución: acorta cables, separa líneas I2S de cables de potencia, añade GND extra cerca de señales, evita “mezclar” con motores/servos.
Mejoras/variantes
- Mel-espectrograma y log-mel: reemplaza FFT por torchaudio.transforms.MelSpectrogram en GPU y visualiza bandas logarítmicas; base para KWS.
- Ventanas más largas y promedio temporal: reduce varianza de estimaciones para ambientes ruidosos; aplica smoothing (EMA).
- Detección de tonalidades: peak-picking robusto, tracking con Viterbi o simple Kalman para tonales.
- Grabación circular y marcado de eventos: guarda clips WAV solo cuando energía en banda supera umbral; útil para monitorización.
- Pipeline DeepStream (alternativo) con fuente de audio e inferencia de clasificación; aunque aquí nos centramos en PyTorch GPU.
- Afinar power/performance: comparar FPS y latencias en perfiles nvpmodel 0 vs 1; registrar tegrastats a fichero para análisis.
Checklist de verificación
Marca cada ítem cuando lo completes:
- [ ] Jetson Orin Nano 8GB con JetPack 6.0 (L4T 36.3) verificado con
cat /etc/nv_tegra_release. - [ ] CUDA 12.2, TensorRT 10.0.1 listados con
dpkg -l | grep -E 'cuda|tensorrt'. - [ ] PyTorch 2.3.0 + torchaudio 2.3.0 instalados;
torch.cuda.is_available()devuelve True. - [ ] Conexiones físicas: INMP441 y MAX98357A cableados según tabla (BCLK/LRCLK compartidos).
- [ ] Overlay DTB compilado, copiado a /boot/dtb/overlays y activado en extlinux.conf.
- [ ] Tras reinicio,
arecord -lyaplay -llistan “jetson-i2s-inmp441” y “jetson-i2s-max98357a”. - [ ]
arecordcaptura 3–5 s sin xruns;aplayreproduce tono de 1 kHz sin distorsión. - [ ] Script i2s_mic_spectrum.py ejecuta, muestra f_peak plausible y RMS sensible a sonidos.
- [ ] FPS ≥ 45, latencia FFT media < 0.5 ms, CNN dummy < 0.5 ms; tegrastats con GR3D > 0%.
- [ ] Opcional: detección de pico a 1 kHz dispara tono en el MAX98357A correctamente.
Notas finales:
– Este caso práctico emplea exactamente el modelo Jetson Orin Nano 8GB + INMP441 + MAX98357A, con coherencia en conexiones, código y comandos.
– La toolchain y versiones especificadas (JetPack 6.0/L4T 36.3, CUDA 12.2.2, TensorRT 10.0.1, PyTorch 2.3.0, etc.) se han elegido para asegurar compatibilidad en 64-bit ARM (aarch64) con aceleración GPU.
– Si tu entorno difiere (otra JetPack), ajusta el índice de paquetes de NVIDIA y ten en cuenta posibles cambios en DTB y nombres de nodos.
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.




