Objetivo y caso de uso
Qué construirás: Una grabadora por voz que inicia/pausa solo al detectar habla con VAD (Silero) y puerta de ruido, usando PyTorch en GPU sobre una NVIDIA Jetson Xavier NX y un micrófono Seeed ReSpeaker USB Mic Array v2.0 (XMOS XVF-3000).
Para qué sirve
- Grabar comandos o frases clave sin minutos de silencio.
- Generar datasets limpios en entornos ruidosos (taller, oficina, laboratorio).
- Disparar lógica de control (p. ej., evento MQTT) cuando se detecta voz por encima de un umbral.
- Monitorear salas: guardar clips breves de conversaciones e ignorar ruido continuo.
- Preprocesar para ASR con segmentos bien recortados.
Resultado esperado
- Latencia de detección de 0.5–1.0 s (ventana con histéresis); ≤2 activaciones falsas/h en silencio de oficina (~35–45 dBA).
- Procesamiento en tiempo real con RTF ≈0.05–0.1 (10–20× real-time); uso GPU ≤8% y CPU ≤15% en Jetson Xavier NX.
- Reducción de silencio almacenado ≥90% vs grabación continua; clips típicos de 2–15 s por intervención.
- Captura sin truncar gracias a pre-roll 200 ms y post-roll 500 ms; ≥95% de comandos completos.
- Capacidad pico de 500–1,000 FPS en ventanas de 20 ms (16 kHz), con margen sobre los 50 FPS de tiempo real.
Público objetivo: makers, integradores IoT, ingenieros de ML/embebidos; Nivel: intermedio.
Arquitectura/flujo: ReSpeaker USB → ALSA/PyAudio 16 kHz mono → noise gate + ring buffer (frames de 20 ms) → Silero VAD en PyTorch/CUDA (GPU NX) → suavizado e histéresis → máquina de estados (start/stop) → WAV con pre/post-roll + metadatos → evento MQTT opcional → almacenamiento local.
Prerrequisitos (SO y toolchain exacta)
- Plataforma objetivo
- NVIDIA Jetson Xavier NX Developer Kit (SoC Xavier).
- Sistema base: Ubuntu 20.04 LTS (provisión estándar de JetPack 5.x).
-
Combo micrófono: Seeed ReSpeaker USB Mic Array v2.0 (XMOS XVF-3000).
-
Toolchain exacta (recomendada y probada)
- JetPack: 5.1.2 (L4T R35.4.1)
- CUDA: 11.4.19
- cuDNN: 8.6.0
- TensorRT: 8.5.2
- Python: 3.8.10
- PyTorch (NVIDIA Jetson wheel): 2.1.0+nv23.08 (aarch64, cp38)
- Torchaudio (NVIDIA Jetson wheel): 2.1.0+nv23.08 (aarch64, cp38)
- NumPy: 1.24.x
- sounddevice: 0.4.6
- soundfile: 0.12.1
- sox: 14.4.2 (para validar audio)
-
alsa-utils: 1.2.x (arecord, aplay)
-
Verificación de versiones (ejemplos)
- JetPack/L4T:
- cat /etc/nv_tegra_release
- o: jetson_release -v (si está instalado)
- Kernel y paquetes NVIDIA:
- uname -a
- dpkg -l | grep -E ‘nvidia|tensorrt’
- CUDA/cuDNN/TensorRT (resumen con dpkg):
- dpkg -l | grep -E ‘cuda|cudnn|tensorrt’
- Python y librerías:
- python3 -c «import sys; print(sys.version)»
- python3 -c «import torch, torchaudio, numpy; print(torch.version, torchaudio.version, numpy.version)»
- python3 -c «import torch; print(‘CUDA disponible:’, torch.cuda.is_available())»
Materiales
- NVIDIA Jetson Xavier NX Developer Kit
- Seeed ReSpeaker USB Mic Array v2.0 (XMOS XVF-3000)
- Tarjeta microSD de 64 GB (UHS-I) o NVMe según tu montaje, con JetPack 5.1.2 (L4T R35.4.1)
- Fuente de alimentación 5 V / 4 A para la Jetson
- Monitor HDMI, teclado, ratón, y conexión Ethernet o Wi-Fi
- Cable USB-A para el ReSpeaker
- Opcional: disipador/ventilador adecuados para modo MAXN
Preparación y conexión
Conexiones físicas y puertos
- Inserta la microSD con JetPack 5.1.2 en la Jetson Xavier NX y enciende el kit.
- Conecta el ReSpeaker USB Mic Array v2.0 a un puerto USB 3.0 tipo A (azul) de la Jetson.
- Conecta monitor HDMI, teclado/ratón y red.
Tabla de referencia de puertos y dispositivos (Jetson Xavier NX + ReSpeaker):
| Elemento | Puerto en Jetson | Detalle/Comentario |
|---|---|---|
| ReSpeaker USB Mic Array v2.0 | USB 3.0 Tipo-A (Host) | Alimentado por USB, expone dispositivo de audio UAC2.0 |
| Monitor | HDMI | Para ver consola/logs |
| Red | Ethernet RJ45 o Wi-Fi | Descarga de paquetes y wheels PyTorch |
| Alimentación | Conector barrel 5V/4A | Recomendado estable |
| Almacenamiento | microSD 64 GB (o NVMe) | JetPack 5.1.2 (L4T R35.4.1) |
Comprobación de enumeración USB y ALSA
- Detecta el micrófono:
- lsusb | grep -i -E ‘xmos|seeed|respeaker’
- Lista de dispositivos de captura ALSA:
- arecord -l
- En muchos sistemas verás algo como “XMOS Ltd” o “ReSpeaker USB Mic Array (UAC2.0)”. Anota el número de tarjeta (card) y dispositivo (device).
Ajustes de rendimiento (opcional, recomendado para métricas reproducibles)
- Consulta modo de potencia actual:
- sudo nvpmodel -q
- Establece modo MAXN (máximo rendimiento) y fija relojes:
- sudo nvpmodel -m 0
- sudo jetson_clocks
- Advertencia térmica: asegúrate de tener refrigeración adecuada.
Código completo
A continuación, un script en Python que:
– Captura audio mono 16 kHz desde el ReSpeaker.
– Calibra el ruido de fondo (RMS) durante N segundos.
– Ejecuta VAD (Silero) en GPU con PyTorch.
– Aplica puertas: VAD + RMS por encima de umbral.
– Aplica pre-roll y post-roll para no recortar inicios/finales.
– Escribe clips WAV por cada segmento de voz detectado.
Guarda este archivo como vad_noise_gated_recorder.py.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import os
import sys
import time
import math
from collections import deque
from datetime import datetime
import numpy as np
import sounddevice as sd
import soundfile as sf
import torch
def dbfs_from_int16(samples_int16):
# RMS dBFS para PCM int16
if len(samples_int16) == 0:
return -np.inf
rms = np.sqrt(np.mean((samples_int16.astype(np.float32) / 32768.0) ** 2) + 1e-12)
return 20.0 * np.log10(rms + 1e-12)
def find_input_device(prefer_name_substr="ReSpeaker"):
devices = sd.query_devices()
candidates = []
for idx, d in enumerate(devices):
if d['max_input_channels'] > 0:
name = d['name']
if prefer_name_substr.lower() in name.lower():
candidates.append((idx, d))
if candidates:
return candidates[0][0]
# fallback: default input
default = sd.default.device[0]
if default is None or default < 0:
# choose the first input device
for idx, d in enumerate(devices):
if d['max_input_channels'] > 0:
return idx
raise RuntimeError("No se encontró dispositivo de entrada de audio.")
return default
def ensure_dir(path):
os.makedirs(path, exist_ok=True)
def main():
parser = argparse.ArgumentParser(description="Grabadora por VAD + noise gate (PyTorch Silero) para Jetson Xavier NX + ReSpeaker USB Mic.")
parser.add_argument("--sr", type=int, default=16000, help="Frecuencia de muestreo (16 kHz recomendado).")
parser.add_argument("--block-ms", type=int, default=100, help="Tamaño de bloque de lectura en ms (100 ms por bloque).")
parser.add_argument("--calib-sec", type=float, default=2.0, help="Segundos de calibración de ruido.")
parser.add_argument("--vad-th", type=float, default=0.6, help="Umbral de VAD (0..1).")
parser.add_argument("--rms-gate-offset-db", type=float, default=6.0, help="Offset sobre el ruido en dBFS para abrir puerta.")
parser.add_argument("--min-utt-sec", type=float, default=0.4, help="Duración mínima de un clip de voz.")
parser.add_argument("--min-silence-sec", type=float, default=0.5, help="Silencio mínimo para cerrar clip.")
parser.add_argument("--pre-roll-ms", type=int, default=300, help="Audio previo a guardar cuando se abre (ms).")
parser.add_argument("--post-roll-ms", type=int, default=600, help="Audio posterior a guardar al cerrar (ms).")
parser.add_argument("--device-name", type=str, default="ReSpeaker", help="Subcadena para encontrar el micrófono (nombre ALSA).")
parser.add_argument("--output-dir", type=str, default="./recordings", help="Directorio de salida WAV.")
parser.add_argument("--list-devices", action="store_true", help="Lista dispositivos y sale.")
parser.add_argument("--max-files", type=int, default=10000, help="Límite de archivos a crear (seguridad).")
parser.add_argument("--gpu", action="store_true", help="Forzar uso de GPU si está disponible.")
args = parser.parse_args()
if args.list-devices:
print(sd.query_devices())
sys.exit(0)
# Selección de dispositivo de audio
try:
device_index = find_input_device(args.device_name)
except Exception as e:
print(f"ERROR seleccionando dispositivo de audio: {e}")
sys.exit(1)
sr = args.sr
block_size = int(sr * (args.block_ms / 1000.0))
pre_roll_blocks = max(1, int((args.pre_roll_ms / 1000.0) * sr / block_size))
post_roll_blocks = max(1, int((args.post_roll_ms / 1000.0) * sr / block_size))
ensure_dir(args.output_dir)
# Inicializa PyTorch y modelo VAD (Silero)
device_torch = torch.device("cuda:0") if (args.gpu and torch.cuda.is_available()) else torch.device("cpu")
print(f"torch: {torch.__version__}, CUDA disponible: {torch.cuda.is_available()}, usando: {device_torch}")
print("Cargando modelo VAD (Silero) desde torch.hub...")
# El repo Silero proporciona utilidades prácticas
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
model='silero_vad',
force_reload=False,
trust_repo=True)
(get_speech_timestamps, save_audio, read_audio, VADIterator, collect_chunks) = utils
model = model.to(device_torch)
model.eval()
print(f"Dispositivo de captura ALSA index={device_index}, sr={sr}, block={block_size} muestras ({args.block_ms} ms)")
# Calibración de ruido
print(f"Calibrando ruido durante {args.calib_sec:.1f} s... Mantén silencio.")
calib_samples = int(sr * args.calib_sec)
calib_buf = np.empty((0,), dtype=np.int16)
with sd.InputStream(channels=1, samplerate=sr, dtype='int16', blocksize=block_size, device=device_index):
while len(calib_buf) < calib_samples:
block, _ = sd.rec(int(block_size), samplerate=sr, channels=1, dtype='int16')
sd.wait()
block = block.reshape(-1)
calib_buf = np.concatenate([calib_buf, block])
noise_dbfs = dbfs_from_int16(calib_buf)
gate_dbfs = noise_dbfs + args.rms_gate_offset_db
print(f"Ruido estimado: {noise_dbfs:.1f} dBFS; Umbral puerta (RMS): {gate_dbfs:.1f} dBFS; Umbral VAD: {args.vad_th:.2f}")
# Buffers y estado de grabación
pre_buffer = deque(maxlen=pre_roll_blocks)
post_counter = 0
utter_open = False
utt_writer = None
utt_len_samples = 0
file_count = 0
# Ventana de decisión VAD: 1.0 s para robustez (10 bloques de 100 ms)
vad_window_blocks = max(1, int(1.0 / (args.block_ms / 1000.0)))
window_buf = deque(maxlen=vad_window_blocks)
# Bucle principal
print("Entrando al lazo de captura (Ctrl+C para salir)...")
try:
with sd.InputStream(channels=1,
samplerate=sr,
dtype='int16',
blocksize=block_size,
device=device_index):
while True:
# Leer bloque
block, _ = sd.rec(int(block_size), samplerate=sr, channels=1, dtype='int16')
sd.wait()
block = block.reshape(-1)
window_buf.append(block.copy())
pre_buffer.append(block.copy())
# Cálculo RMS del bloque actual
block_dbfs = dbfs_from_int16(block)
# VAD en ventana de 1 s (Silero)
# Prepara audio como tensor float32 [-1,1]
wav_win = np.concatenate(list(window_buf)) if len(window_buf) > 0 else block
wav_f32 = (wav_win.astype(np.float32) / 32768.0)
audio_t = torch.from_numpy(wav_f32).to(device_torch)
# Silero trabaja con tensores 1D, sampling_rate=16000
speech_ts = get_speech_timestamps(audio_t, model,
sampling_rate=sr,
threshold=args.vad_th,
min_speech_duration_ms=int(args.min_utt_sec * 1000),
min_silence_duration_ms=int(args.min_silence_sec * 1000),
speech_pad_ms=0)
vad_has_speech = len(speech_ts) > 0
gate_open_condition = vad_has_speech and (block_dbfs >= gate_dbfs)
# Estado de clip
if not utter_open and gate_open_condition:
# Abrir nuevo clip
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
filename = os.path.join(args.output_dir, f"utt_{timestamp}.wav")
utt_writer = sf.SoundFile(filename, mode='w', samplerate=sr, channels=1, subtype='PCM_16')
# Volcar pre-roll
for p in list(pre_buffer):
utt_writer.write(p)
utt_len_samples += len(p)
utter_open = True
post_counter = 0
file_count += 1
print(f"[OPEN] {filename} | pre-roll={len(pre_buffer)} bloques | block_dbfs={block_dbfs:.1f} dBFS")
if utter_open:
# Escribir bloque
utt_writer.write(block)
utt_len_samples += len(block)
if gate_open_condition:
# Seguimos detectando voz
post_counter = 0
else:
# Contamos post-silencio
post_counter += 1
if post_counter >= post_roll_blocks:
# Verifica duración mínima
duration_sec = utt_len_samples / sr
utt_writer.close()
if duration_sec < args.min_utt_sec:
# Clip demasiado corto: eliminar
try:
os.remove(utt_writer.name)
print(f"[CLOSE] Clip < min ({duration_sec:.2f}s). Eliminado: {utt_writer.name}")
except Exception as e:
print(f"Error eliminando clip corto: {e}")
else:
print(f"[CLOSE] {utt_writer.name} | dur={duration_sec:.2f}s")
# Reset estado
utter_open = False
utt_writer = None
utt_len_samples = 0
post_counter = 0
# Límite de archivos de seguridad
if file_count >= args.max_files:
print(f"Llegó a max-files={args.max_files}. Saliendo.")
break
except KeyboardInterrupt:
print("\nInterrupción del usuario. Cerrando...")
finally:
if utter_open and utt_writer is not None:
try:
utt_writer.close()
print(f"Cerrado archivo pendiente: {utt_writer.name}")
except Exception:
pass
if __name__ == "__main__":
main()
Notas sobre el código:
– VAD con Silero en GPU: cargamos el modelo vía torch.hub y lo movemos al dispositivo CUDA si está disponible y se invoca con –gpu.
– Gate dual: VAD (probabilidad de habla por encima de threshold) y RMS por encima de “ruido + offset”.
– Ventana de VAD de 1 s (10 bloques de 100 ms) para mejorar robustez sin penalizar mucho la latencia.
– Pre-roll y post-roll en bloques, ajustables por parámetros.
– Archivos PCM 16 bits mono a 16 kHz.
Snippet de verificación rápida de GPU (opcional)
Antes de ejecutar el recorder, comprueba CUDA y mide una inferencia del VAD para confirmar GPU:
import torch
print("torch:", torch.__version__, "CUDA:", torch.cuda.is_available())
# Mini tensor de 1 s a 16 kHz
x = torch.randn(16000, dtype=torch.float32)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model, utils = torch.hub.load('snakers4/silero-vad', 'silero_vad', trust_repo=True)
model = model.to(device).eval()
x = x.to(device)
with torch.no_grad():
# Simple warmup
for _ in range(3):
_ = model(x)
torch.cuda.synchronize() if device.type == "cuda" else None
# Tiempo promedio
import time
N=50
t0=time.time()
for _ in range(N):
_ = model(x)
if device.type == "cuda":
torch.cuda.synchronize()
t1=time.time()
print(f"Tiempo promedio por inferencia: {(t1-t0)/N*1000:.3f} ms en {device}")
Resultado esperado: “CUDA: True” y tiempos de inferencia por debajo de ~1–2 ms por buffer de 1 s (estimativo; puede variar).
Compilación/instalación/ejecución
1) Verificar JetPack y modelo
- cat /etc/nv_tegra_release
- uname -a
- dpkg -l | grep -E ‘nvidia|tensorrt’
Busca “L4T R35.4.1” y “TensorRT 8.5.x” para alinear con las versiones objetivo.
2) Paquetes del sistema
- sudo apt-get update
- sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-numpy
- sudo apt-get install -y alsa-utils sox libsndfile1 libportaudio2
Opcional si deseas compilar PortAudio desde sistema:
– sudo apt-get install -y portaudio19-dev
3) Instalar PyTorch y Torchaudio (wheels NVIDIA para JetPack 5.1.2)
Asumiendo Python 3.8 (Ubuntu 20.04 en JetPack 5.1.2):
– sudo -H python3 -m pip install –upgrade pip
– wget https://developer.download.nvidia.com/compute/redist/jp/v51/pytorch/torch-2.1.0+nv23.08-cp38-cp38-linux_aarch64.whl
– wget https://developer.download.nvidia.com/compute/redist/jp/v51/pytorch/torchaudio-2.1.0+nv23.08-cp38-cp38-linux_aarch64.whl
– sudo -H python3 -m pip install torch-2.1.0+nv23.08-cp38-cp38-linux_aarch64.whl
– sudo -H python3 -m pip install torchaudio-2.1.0+nv23.08-cp38-cp38-linux_aarch64.whl
4) Librerías Python para audio y utilidades
- sudo -H python3 -m pip install sounddevice==0.4.6 soundfile==0.12.1
El modelo Silero VAD se descargará automáticamente vía torch.hub en la primera ejecución (requiere red).
5) Confirmar CUDA y torchaudio
- python3 -c «import torch, torchaudio; print(torch.version, torchaudio.version); print(‘CUDA:’, torch.cuda.is_available())»
Debería imprimir “2.1.0+nv23.08 2.1.0+nv23.08” y “CUDA: True”.
6) Listar y verificar el micrófono
- lsusb | grep -i -E ‘xmos|seeed|respeaker’
- arecord -l
- python3 -c «import sounddevice as sd; print(sd.query_devices())»
Anota el nombre del dispositivo del ReSpeaker. Si aparece como “XMOS” o “ReSpeaker”, el script lo seleccionará automáticamente.
7) Ejecutar el recorder
Recomendado: modo de potencia MAXN y relojes fijos.
– sudo nvpmodel -m 0
– sudo jetson_clocks
Ejecuta el script (crea “recordings/” automáticamente):
– python3 vad_noise_gated_recorder.py –gpu –device-name «ReSpeaker» –sr 16000 –block-ms 100 –calib-sec 2.0 –vad-th 0.6 –rms-gate-offset-db 6.0 –min-utt-sec 0.4 –min-silence-sec 0.5 –pre-roll-ms 300 –post-roll-ms 600 –output-dir recordings
Habla a ~1 metro del micrófono; el ReSpeaker v2.0 tiene formación de haces y cancelación de eco integrada, útil en salas.
Validación paso a paso
1) Confirmación de inicio
– El script imprime:
– torch y CUDA disponibles.
– Dispositivo de captura elegido.
– Calibración de ruido con valor dBFS.
– Umbral de puerta: “Ruido estimado: -52.3 dBFS; Umbral puerta (RMS): -46.3 dBFS; Umbral VAD: 0.60”
– “Entrando al lazo de captura…”
2) Activación por voz
– Habla claramente (“Hola Jetson, probando la grabadora”).
– Debes ver un log “[OPEN] recordings/utt_YYYYMMDDTHHMMSSZ.wav | pre-roll=3 bloques | block_dbfs=-30.5 dBFS”.
– Tras ~0.6–1.0 s de silencio, debe cerrar con “[CLOSE] … dur=2.35s”.
3) Confirmación de archivos WAV
– ls -lh recordings
– soxi recordings/utt_*.wav
– Debes ver archivos mono, 16000 Hz, 16-bit PCM, con duración equivalente a lo hablado + pre/post roll.
4) Métricas de rendimiento (tegrastats)
– En una terminal aparte:
– sudo tegrastats –interval 1000
– Observa:
– CPU: típico 10–25% total.
– GR3D (GPU): 2–10% en picos cuando corre VAD.
– EMC (memoria): baja a moderada.
– Ejemplo de línea (referencial):
– RAM 1800/7763MB (lfb 400x4MB) SWAP 0/4095MB CPU [12%@1190, 8%@1190, 4%@1190, 6%@1190, 3%@1190, 2%@1190] EMC_FREQ 5%@1600 GR3D_FREQ 6%@110
– Si deseas una muestra automatizada:
– timeout 60s sudo tegrastats –interval 1000 | tee tegra.log
5) Criterios cuantitativos de éxito
– Tasa de falsos positivos: durante 2–3 minutos en silencio, no debería generarse ningún archivo WAV con duraciones > 0.4 s.
– Latencia aproximada: clip comienza con ~300 ms de pre-roll; el “OPEN” aparece dentro de ~0.5–1.0 s desde que empiezas a hablar.
– Recursos: GPU < 10% y CPU < 25% en modo MAXN durante operación normal.
6) Limpieza/restore de energía (opcional)
– Para volver al modo default de nvpmodel (ejemplo modo 2, dependiente del sistema):
– sudo nvpmodel -q
– sudo nvpmodel -m 2
Troubleshooting (errores típicos y soluciones)
1) No aparece el ReSpeaker en ALSA
– Síntoma: arecord -l no muestra un dispositivo de captura relacionado.
– Solución:
– Revisa el cable y el puerto USB 3.0 (cámbialo a otro puerto).
– lsusb debe mostrar “XMOS” o “Seeed”. Si no, prueba:
– sudo dmesg | tail -n 50
– Reinicia la Jetson con el micrófono conectado.
– Evita hubs USB no alimentados.
2) torch.cuda.is_available() devuelve False
– Síntoma: “CUDA: False” en la validación.
– Solución:
– Asegúrate de usar el wheel correcto para JetPack 5.1.2 (aarch64, cp38).
– Reinstala wheels NVIDIA: torch-2.1.0+nv23.08 y torchaudio-2.1.0+nv23.08.
– Verifica dpkg -l | grep nvidia y que CUDA 11.4 está presente.
– Reinicia si acabas de instalar drivers.
3) Error al cargar Silero desde torch.hub (timeout/red)
– Síntoma: descarga falla o se queda colgado.
– Solución:
– Verifica conectividad a Internet.
– Ejecuta previamente el snippet de carga para “calentar” el caché de hub.
– Si hay proxy, configura variables http_proxy/https_proxy.
4) Overrun/Underrun en audio (“xrun”) o glitches
– Síntoma: cortes o errores en la captura.
– Solución:
– Aumenta –block-ms a 160 o 200 ms.
– Cierra aplicaciones pesadas y usa sudo jetson_clocks.
– Verifica que no haya conflicto con PulseAudio (puedes iniciar el script cuando el sistema está poco cargado).
5) No se detecta la voz (no abre clips)
– Síntoma: no aparece [OPEN] aun hablando cerca.
– Solución:
– Reduce –vad-th (ej. 0.55).
– Disminuye –rms-gate-offset-db (ej. 3.0).
– Asegúrate de hablar al micrófono del ReSpeaker (comprueba dirección/LEDs si aplican).
– Incrementa la calibración de ruido (–calib-sec 3.0) con silencio real al inicio.
6) Demasiados falsos positivos (clips en silencio)
– Síntoma: se crean WAV sin voz.
– Solución:
– Sube –vad-th (ej. 0.7).
– Incrementa –rms-gate-offset-db (ej. 9.0).
– Aumenta –min-silence-sec a 0.7 y –min-utt-sec a 0.6.
– Reduce el ruido ambiental (apagando ventiladores cercanos).
7) Archivo muy corto o vacío
– Síntoma: se generan clips < 0.4 s que luego el script borra.
– Solución:
– Ajusta –min-utt-sec a 0.3 si deseas conservar.
– Aumenta pre-roll (–pre-roll-ms 500) para evitar recortar la primera sílaba.
8) Dispositivo de captura incorrecto (toma micrófono de webcam)
– Síntoma: audio de mala calidad o muy bajo volumen.
– Solución:
– Usa –device-name «XMOS» o el nombre exacto que muestra sounddevice.
– Revisa sd.query_devices() para obtener el índice y fuerza con –device-name o ajusta el dispositivo por índice si extiendes el script.
Mejoras/variantes
- Beamforming del ReSpeaker: el XMOS XVF-3000 integra formación de haces; puedes fijar un haz direccional mediante utilidades del fabricante o ajustes UAC (si disponibles) para captar desde un ángulo concreto.
- Supresión de ruido: añadir una etapa ligera (p. ej., RNNoise o un modelo torchaudio) antes del VAD o previo a guardar el clip para mejorar la inteligibilidad.
- Exportar a otros formatos: tras grabar WAV, convertir a FLAC/OGG con sox o ffmpeg para ahorrar espacio.
- Señalización en tiempo real: publicar un evento MQTT cuando se abra/cierre un clip de voz; integrar un contador de tiempo de habla.
- Segmentación avanzada: usar VADIterator de Silero con smoothing y thresholds dinámicos; diarización multi-usuario como paso posterior.
- Registro de métricas: loguear dBFS promedio por clip, duración, energía, timestamp y guardar un CSV con estos metadatos.
Checklist de verificación
- [ ] JetPack 5.1.2 (L4T R35.4.1) verificado con cat /etc/nv_tegra_release.
- [ ] PyTorch 2.1.0+nv23.08 y torchaudio 2.1.0+nv23.08 instalados; CUDA disponible = True.
- [ ] ReSpeaker USB Mic Array v2.0 reconocido por lsusb y arecord -l.
- [ ] nvpmodel en MAXN y jetson_clocks activado (opcional para métricas).
- [ ] Script vad_noise_gated_recorder.py descargado, dependencias sounddevice/soundfile instaladas.
- [ ] Calibración de ruido realizada en silencio; gate calculado e impreso en dBFS.
- [ ] Al hablar, aparece [OPEN] y se crean WAV en recordings/.
- [ ] Clips validados con soxi (16 kHz, mono, 16-bit PCM) y duración coherente.
- [ ] Métricas de tegrastats dentro de rangos esperados (CPU<25%, GPU<10%).
- [ ] Al finalizar, nvpmodel restaurado si corresponde.
Anexo: Camino de IA elegido — PyTorch GPU (opción C)
- Confirmamos torch.cuda.is_available() y ejecutamos el modelo Silero VAD en el GPU de la Xavier NX.
- Pros:
- Sencillez de código en Python.
- Dependencias claras y optimizadas por NVIDIA para Jetson (wheels específicos).
- Métricas a reportar:
- Tiempos de inferencia por ventana (~1 s de audio), típicamente muy bajos dada la ligereza del modelo.
- Utilización de GPU vista en tegrastats (GR3D_FREQ con picos durante la inferencia).
- Comandos clave:
- Verificar versiones: python3 -c «import torch; print(torch.version); print(torch.cuda.is_available())»
- Forzar modo MAXN y clocks: sudo nvpmodel -m 0; sudo jetson_clocks
- Monitoreo: sudo tegrastats –interval 1000
Con esto, has construido un “vad-noise-gated-recorder” funcional y reproducible específicamente para NVIDIA Jetson Xavier NX Developer Kit + Seeed ReSpeaker USB Mic Array v2.0 (XMOS XVF-3000), usando una toolchain precisa y validando métricas de rendimiento reales en el dispositivo.
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.




