PTENES
MÓDULO 5.2 · PRÁCTICA

🎙️ Voz local: escuchar y hablar

El robot escuchará una solicitud en portugués, la convertirá en texto con Whisper, la pasará por el cerebro seguro del módulo 5.1 y responderá en voz alta con Piper. Todo sin conexión, en la PC o en la Raspberry Pi 5: la voz de tu casa no sale de ella.

6
Temas
Avanzado
Nivel
Práctica
Tipo
Micrófono USB
Hardware adicional
0%
0 de 0 temas leídos · Sección 1 de 6

Contenido detallado

1

🔊 Flujo de voz: escuchar, entender, hablar

Un asistente de voz no es un solo modelo: es una línea de ensamblaje de cuatro estaciones. El micrófono capta el sonido; el reconocimiento de voz convierte el sonido en texto; el cerebro del módulo 5.1 elige y valida la acción; la síntesis de voz convierte la respuesta en sonido. Cada estación es un programa independiente que puedes probar y cambiar sin modificar las demás.

🌱 ¿Eres nuevo aquí?

STT (speech-to-text) es el reconocimiento de voz: el sonido se convierte en texto. TTS (text-to-speech) es el proceso inverso. VAD (detección de actividad de voz) separa los segmentos con voz de los segmentos en silencio. La frecuencia de muestreo indica cuántas mediciones del sonido hace el micrófono por segundo; Whisper trabaja con 16 000 (16 kHz), en un solo canal (mono).

micrófonosonidoWhispersonido → textocerebro 5.1elige y validaESP32ejecuta Pipertexto → sonidoaltavoz"avanzando 30 cm"todo funciona en tu máquina, sin internet

Cómo leer: la línea de arriba (flechas color teal) es el recorrido del pedido, desde tu voz hasta el motor. La línea de abajo (flechas cian discontinuas) es el retorno: el cerebro cuenta lo que hizo y Piper lo dice. La caja brillante es la misma del módulo 5.1; la voz solo cambia el teclado por un micrófono.

✓ Hazlo

  • ✓Prueba cada estación por separado antes de unirlas.
  • ✓Muestra en pantalla el texto que entendió Whisper.
  • ✓Haz que el robot diga qué va a hacer antes de hacerlo.

✗ No lo hagas

  • ✗Saltar el validador porque «solo fue una frase».
  • ✗Usar la voz como única forma de detener el robot.
  • ✗Unir todo de una vez y depurar a ciegas.

Por qué aprender

La voz es la interfaz más natural para quien no programa: un niño, un visitante, un estudiante en la feria de ciencias. Armar el pipeline por partes te enseña a desconfiar de cada estación y a encontrar dónde está la demora o el error.

Conceptos clave

STT
sonido → texto
TTS
texto → sonido
VAD
habla o silencio
16 kHz mono
el audio de Whisper
2

👂 Whisper local (faster-whisper / whisper.cpp)

O Whisper es un modelo abierto de reconocimiento de voz que entiende portugués. Dos versiones funcionan bien sin usar la nube: faster-whisper, en Python, que usa la CPU con números de 8 bits (int8) o la GPU NVIDIA; y whisper.cpp, en C++, ligero y adecuado para la Pi. El curso usa faster-whisper porque se comunica directamente con el script del módulo 5.1. El modelo se descarga de internet la primera vez; después, funciona sin conexión.

ModeloPrecisión en portuguésDónde usarlo
tiny / baseconfunde palabras parecidasPi 5, comandos cortos y pruebas
smallbueno para frases sencillasPi 5 con paciencia, cualquier PC
mediummucho mejor con acento y ruidoPC, de preferencia con GPU
large-v3 / turboel más precisoPC con GPU NVIDIA

Objetivo: instalar faster-whisper y averiguar el número de tu micrófono. En la primera transcripción, el modelo elegido se descarga una vez.

terminal
source ~/robo-ia/bin/activate          # el mismo entorno del módulo 5.1
sudo apt install -y libportaudio2      # Linux/Pi: sounddevice lo necesita
pip install faster-whisper sounddevice numpy
python3 -m sounddevice                 # lista los micrófonos: anota el número del tuyo

Objetivo: grabar algunos segundos desde el micrófono e imprimir lo que dijiste. El archivo también sirve como módulo: el asistente del tema 4 importa las funciones grabar e transcribir desde aquí.

ouvir.py
#!/usr/bin/env python3
"""Graba desde el micrófono y transcribe en portugués con faster-whisper (sin conexión después de la primera descarga).
pip install faster-whisper sounddevice numpy"""
import time
import sounddevice as sd
from faster_whisper import WhisperModel

TAXA = 16000                # Whisper trabaja con 16 kHz, mono
MODELO_WHISPER = "small"    # tiny, base, small, medium... cuanto mayor, más acierta y más tarda
MICROFONE = None            # None = predeterminado del sistema; o el número de: python3 -m sounddevice
_modelo = None

def carregar():
    global _modelo
    if _modelo is None:       # CPU int8 funciona en Pi 5 y en cualquier PC
        _modelo = WhisperModel(MODELO_WHISPER, device="cpu", compute_type="int8")
        # con GPU NVIDIA: WhisperModel(MODELO_WHISPER, device="cuda", compute_type="float16")
    return _modelo

def gravar(segundos=4.0):
    print(f"Grabando {segundos:.0f} s... habla ahora.")
    audio = sd.rec(int(segundos * TAXA), samplerate=TAXA, channels=1, dtype="float32", device=MICROFONE)
    sd.wait()
    return audio.flatten()

def transcrever(audio):
    segmentos, _info = carregar().transcribe(audio, language="pt", vad_filter=True, beam_size=1)
    return " ".join(s.text.strip() for s in segmentos).strip()

if __name__ == "__main__":
    carregar()
    while True:
        input("Presiona Enter para hablar (Ctrl+C para salir) ")
        audio = gravar()
        t0 = time.perf_counter()
        texto = transcrever(audio)
        print(f"Dijiste: '{texto}'  (transcripción en {time.perf_counter() - t0:.1f} s)")

Cómo verificar

  • ✓"anda treinta centímetros hacia adelante" aparece escrito casi igual, junto con el tiempo de transcripción.
  • ✓Si guardas silencio, el texto queda vacío (el VAD recortó el silencio).
  • ✗¿Error de PortAudio o el texto siempre queda vacío? Revisa el número del micrófono en MICRÓFONO y el volumen de captura del sistema.

Por qué aprender

El reconocimiento de voz es la puerta de entrada: si entiende "treinta" como "tres", todo lo demás también se equivoca. Medir el tiempo y probar modelos de distintos tamaños en tu hardware te permitirá decidir si tu robot responde en un segundo o en diez.

Conceptos clave

faster-whisper
Whisper en Python, rápido
whisper.cpp
Whisper en C++, ligero
int8
números más pequeños, más rápido
language="pt"
no pierde tiempo adivinando
3

🔔 Palabra de activación y micrófono en el robot

El robot no debe transcribir todo lo que oye. Necesita un disparador para empezar a prestar atención. Lo más sencillo y confiable es presionar para hablar: una tecla en la PC o un botón en el GPIO de la Pi. La alternativa es una palabra de activación ("oye, robot"), detectada por un modelo diminuto que funciona todo el tiempo; proyectos abiertos como openWakeWord hacen esto, y puedes entrenar una palabra propia (consulta la documentación del proyecto elegido).

esperando escuchandopensandohablando yejecutandobotón o"oye, robot" textoválidoterminó, hubo un error o solo hubo silencio: vuelve a esperar

Cómo leer: el robot pasa la mayor parte del tiempo en el círculo de la izquierda, que pulsa. Solo el disparador lo lleva a "escuchando". Cualquier resultado no válido (texto vacío, acción rechazada, modelo sin respuesta) vuelve por la flecha cian a "esperando", sin activar el motor.

✓ Un micrófono que funcione

  • ✓Un micrófono USB sencillo o de solapa, apuntado hacia quien habla.
  • ✓Lejos de los motores y del ventilador de la Pi (ruido mecánico).
  • ✓No grabes mientras el propio robot habla (se escucharía a sí mismo).

✗ Causas de error

  • ✗El micrófono de la laptop a dos metros, en una sala con eco.
  • ✗Escuchar siempre, sin disparador: comandos activados por conversaciones ajenas.
  • ✗Volumen de captura al máximo: el sonido se distorsiona y Whisper se equivoca.

🌱 ¿Eres nuevo aquí?

En Raspberry Pi, un botón de "presionar para hablar" es un botón común entre un GPIO y GND. Con la biblioteca gpiozero (módulo 3.1), Button(17).wait_for_press() reemplaza input() del script: el robot espera que presiones el botón en vez de esperar a que presiones Enter.

Por qué aprender

Un robot que obedece cualquier frase que escucha en la sala es impredecible. El disparador y una buena ubicación del micrófono resuelven la mayoría de los problemas de reconocimiento antes de que necesites un modelo más grande.

Conceptos clave

Presionar para hablar
el disparador más seguro
Palabra de activación
"oye, robot"
Máquina de estados
esperar, escuchar, pensar, hablar
Ruido
motor, eco, ventilador
4

🔗 Del texto al comando (con el 5.1)

El texto que sale de Whisper entra exactamente donde, en el módulo 5.1, entraba lo que escribías. Nada cambia en el validador: la voz es solo otra vía de entrada. La novedad es la confirmación hablada: para movimientos grandes, el robot pregunta "¿confirmas?" y solo sigue si oye "sí".

Paso a paso

1

Junta los archivos en una carpeta

cerebro_llm.py (módulo 5.1), ouvir.py (tema 2) y falar.py (tema 5). El asistente importa funciones de los tres.

2

Prueba en modo simulado

Ejecuta con --simular: el robot habla, pero no se envía ninguna línea por el puerto serial. Revisa el texto que oyó y la acción elegida para cada solicitud.

3

Conecta el ESP32 con el robot suspendido

Graba en el ESP32 el ejecutor del módulo 5.1, deja las ruedas en el aire o el brazo con espacio libre y solo entonces ejecútalo sin --simular.

Objetivo: el asistente completo: Enter para hablar, transcripción, elección de la acción por el modelo local, validación, confirmación por voz cuando el movimiento es grande, envío al ESP32 y respuesta hablada, con el tiempo de cada etapa en la terminal.

assistente_voz.py
#!/usr/bin/env python3
"""Asistente de voz sin conexión: escuchar (Whisper) -> elegir (modelo local) -> validar -> ESP32 -> hablar (Piper).
En la misma carpeta: cerebro_llm.py (módulo 5.1), ouvir.py y falar.py (este módulo).
Uso: python3 assistente_voz.py [--simular]"""
import sys, time
from ouvir import carregar, gravar, transcrever
from falar import falar
from cerebro_llm import perguntar_modelo, validar, abrir_serial, enviar

FRASES = {"ANDAR": "avanzando {} centímetros", "GIRAR": "girando {} grados",
          "GARRA": "garra: {}", "PARAR": "parando"}

def em_palavras(linha):                        # "ANDAR 30" -> "avanzando 30 centímetros"
    partes = linha.split()
    molde = FRASES.get(partes[0], linha)
    return molde.format(*[p.lower() for p in partes[1:]]) if "{}" in molde else molde

def main():
    simular = "--simular" in sys.argv
    carregar()
    s = abrir_serial(simular)
    falar("Listo. Presiona Enter y habla.")
    while True:
        try:
            input("\n[Enter] para hablar, Ctrl+C para salir ")
        except (KeyboardInterrupt, EOFError):
            break
        audio = gravar(4)
        t0 = time.perf_counter(); texto = transcrever(audio); t1 = time.perf_counter()
        print(f"Oí: '{texto}'")
        if not texto:
            falar("No entendí. ¿Puedes repetir?"); continue
        try:
            resp = perguntar_modelo(texto)
        except Exception as e:                    # sin respuesta o JSON ilegible: no mueve nada
            print("Falla en el modelo:", e); falar("Mi cerebro no respondió."); continue
        t2 = time.perf_counter()
        ok, linha, confirmar = validar(resp)
        if not ok:
            print("Recusado:", linha); falar("No puedo hacer eso."); continue
        if confirmar:
            falar(f"¿Confirmas {em_palavras(linha)}? Di sí o no.")
            palavras = transcrever(gravar(2.5)).lower().replace(".", " ").replace(",", " ").split()
            if "sim" not in palavras:            # palabra completa: "así" no confirma
                falar("Cancelado."); continue
        print("ESP32:", enviar(s, linha))
        falar(em_palavras(linha))
        t3 = time.perf_counter()
        print(f"Tiempos: escuchar {t1 - t0:.1f} s · pensar {t2 - t1:.1f} s · ejecutar y hablar {t3 - t2:.1f} s")
    if s:
        s.write(b"PARAR\n"); s.close()

if __name__ == "__main__":
    main()

Cómo verificar

  • ✓"gira noventa grados a la derecha" → "Oí: ...", "ESP32: (simulado) GIRAR 90" y la voz dice "girando 90 grados".
  • ✓"avanza ochenta centímetros" → el robot pregunta "¿Confirmas...?"; responder "no" cancela.
  • ✓"baila para mí" → "No puedo hacer eso." y no se envía nada.

Por qué aprender

Combinar piezas probadas es como se construyen los sistemas reales. Como cada archivo tiene una función clara, el proyecto final del módulo 5.6 reutiliza los tres sin reescribir nada.

Conceptos clave

Importar módulos
reutilizar el 5.1
Confirmación hablada
"sí" antes del movimiento grande
perf_counter
cronómetro preciso
Falla segura
error = no mover
5

🗣️ Piper: voz sin conexión en portugués

O Piper es un sintetizador de voz abierto, lo bastante rápido para Raspberry Pi. Cada voz es un archivo .onnx (el modelo) con un .onnx.json al lado (la configuración). Hay voces en portugués de Brasil, como pt_BR-faber-medium; la lista completa está en el repositorio de voces. El proyecto dejó su dirección original (rhasspy/piper) y continúa como piper1-gpl, de Open Home Foundation; el paquete piper-tts de pip acompaña este cambio, y la función de síntesis cambió de nombre entre versiones. El siguiente script funciona con ambas.

texto"girando"fonemasʒiˈɾɐ̃du voz .onnxpt_BRonda sonoraWAVfunciona en la Pi 5 sin GPU ni internet

Cómo leer: de izquierda a derecha, la frase se divide en sonidos del idioma (fonemas), que el modelo de voz transforma en audio. Cambiar de voz es cambiar solo el archivo de la caja iluminada; el resto del proceso es igual.

Objetivo: instalar Piper y descargar una voz en portugués. Al final, los archivos pt_BR-faber-medium.onnx e .onnx.json deben estar en la carpeta.

terminal
pip install piper-tts
python3 -m piper.download_voices pt_BR-faber-medium   # descarga .onnx y .onnx.json en la carpeta actual
# ¿versión antigua del paquete? descarga los dos archivos de la voz en huggingface.co/rhasspy/piper-voices

Objetivo: una función falar(texto) que sintetiza en la memoria y reproduce el audio en el altavoz predeterminado, sin guardar ningún archivo en el disco.

falar.py
#!/usr/bin/env python3
"""Lee un texto en portugués con Piper, sin conexión. pip install piper-tts sounddevice numpy
Uso: python3 falar.py "Olá, eu sou o robô." """
import io, sys, wave
import numpy as np
import sounddevice as sd
from piper import PiperVoice

VOZ = "pt_BR-faber-medium.onnx"    # el .onnx.json debe estar al lado
_voz = None

def falar(texto):
    global _voz
    if _voz is None:
        _voz = PiperVoice.load(VOZ)
    buf = io.BytesIO()
    with wave.open(buf, "wb") as wav:
        if hasattr(_voz, "synthesize_wav"):   # versiones nuevas de piper-tts
            _voz.synthesize_wav(texto, wav)
        else:                                 # versiones antiguas
            _voz.synthesize(texto, wav)
    buf.seek(0)
    with wave.open(buf, "rb") as wav:
        taxa = wav.getframerate()
        dados = np.frombuffer(wav.readframes(wav.getnframes()), dtype=np.int16)
    sd.play(dados, taxa)
    sd.wait()                                 # espera a que termine: el robot no se oye cuando habla

if __name__ == "__main__":
    falar(" ".join(sys.argv[1:]) or "Hola, soy el robot del curso. Estoy listo.")

Cómo verificar: python3 falar.py "Girando noventa grados" reproduce la frase en portugués en menos de un segundo en una PC (un poco más en la Pi). Si aparece "No such file", el nombre en VOZ no coincide con el archivo descargado; si no se oye nada, revisa el dispositivo de salida con python3 -m sounddevice.

Por qué aprender

Un robot que dice lo que va a hacer es más seguro y más fácil de depurar: oyes "girando noventa grados" antes del movimiento y tienes tiempo de presionar el botón de parada si no era eso.

Conceptos clave

Piper
TTS abierto y ligero
.onnx + .json
la voz y la configuración
Fonema
unidad de sonido del idioma
pt_BR
voces de Brasil
6

⏱️ Latencia y privacidad

Latencia es el tiempo entre que dejas de hablar y el robot reacciona. Es la suma de las etapas, y casi siempre una de ellas predomina. Por eso el asistente imprime los tiempos por separado: optimizas la etapa más lenta, no la que parece más lenta. La figura muestra la forma típica en una PC sin GPU; los números de tu hardware serán distintos, así que mídelo.

grabarfijo: el tiempo que hablas Whisperaumenta con el modelomodelo (LLM)por lo general, la más largaPiperpequeñavalores ilustrativos: ejecuta el asistente y anota los tuyos

Cómo leer: cada barra es una etapa; su longitud representa el tiempo. La barra discontinua no depende de la computadora, sino de cuánto grabas. La barra que pulsa suele ser la del modelo de lenguaje: es donde un modelo más pequeño o una GPU hacen más diferencia.

✓ Reduce la espera

  • ✓Carga los modelos una sola vez, al inicio (el script ya lo hace).
  • ✓Graba solo mientras mantienes presionado el botón, en lugar de usar un tiempo fijo.
  • ✓Un modelo de lenguaje más pequeño y un prompt breve; usa la GPU si tienes una.
  • ✓Di un «ok» breve enseguida, mientras el modelo piensa.

✗ Aumenta la espera

  • ✗Cargar Whisper en cada frase.
  • ✗Dejar que Whisper adivine el idioma.
  • ✗Pi 5 sin cooler: la CPU se calienta y reduce la velocidad.
  • ✗Respuestas largas del modelo para comandos breves.

Privacidad es la otra mitad de la elección. En un servicio de voz en la nube, el audio de tu habitación viaja hasta un servidor de terceros. En un salón de clases, con voces de niños, esto requiere autorización y cuidado con la ley de protección de datos. En el pipeline local, el sonido se procesa y se descarta dentro del robot.

localmicrófonoPi / PCel sonido no sale de casa nubemicrófonoservidor deterceros el audio atraviesa internet

Cómo leer: a la izquierda, la flecha corta queda dentro del marco teal: el audio nunca sale del dispositivo. A la derecha, la flecha roja discontinua cruza internet. La nube puede ser más precisa o más rápida; la elección depende de quién habla cerca del robot y de lo que puedes enviar fuera.

Por qué aprender

Medir la latencia transforma «está lento» en «el modelo de lenguaje tarda tanto», algo que tiene solución. Y pensar en la privacidad desde el inicio evita rehacer el proyecto cuando llegue a una escuela, una feria o la casa de alguien.

Conceptos clave

Latencia
suma de las etapas
Cuello de botella
la etapa más lenta
Sin conexión
funciona sin internet
Los datos de voz
requieren autorización

✅ Resumen del módulo

✓
Pipeline por etapas: micrófono, Whisper, cerebro 5.1, ESP32 y Piper, probados uno por uno.
✓
faster-whisper: 16 kHz mono, language="pt", VAD activado, modelo del tamaño adecuado para tu hardware.
✓
Activador: presiona para hablar o usa una palabra de activación; micrófono cerca y lejos del motor.
✓
Mismo validador: la voz solo reemplaza el teclado; para un movimiento grande, pide un «sí» hablado.
✓
Piper, latencia y privacidad: voz pt_BR sin conexión, tiempos medidos por etapa, audio que no sale de casa.

Para profundizar

Siguiente módulo

5.3 · Brazo SO-101 y LeRobot: arma, calibra y teleopera el brazo que aprenderá contigo.