PTENES
MÓDULO 5.2 · PRÁTICA

🎙️ Voz local: ouvir e falar

O robô vai ouvir um pedido em português, transformá-lo em texto com o Whisper, passar pelo cérebro seguro do módulo 5.1 e responder em voz alta com o Piper. Tudo offline, no PC ou no Raspberry Pi 5: a voz da sua casa não sai dela.

6
Tópicos
Avançado
Nível
Prática
Tipo
Microfone USB
Hardware extra
0%
0 de 0 tópicos lidos · Seção 1 de 6

Conteúdo detalhado

1

🔊 Pipeline de voz: ouvir, entender, falar

Um assistente de voz não é um modelo só: é uma linha de montagem de quatro estações. O microfone capta o som; o reconhecimento de fala transforma som em texto; o cérebro do módulo 5.1 escolhe e valida a ação; a síntese de voz transforma a resposta em som. Cada estação é um programa separado, que você testa e troca sem mexer nas outras.

🌱 Novo aqui?

STT (speech-to-text) é o reconhecimento de fala: som vira texto. TTS (text-to-speech) é o caminho inverso. VAD (detecção de atividade de voz) separa trechos com fala de trechos em silêncio. A taxa de amostragem diz quantas medidas do som o microfone faz por segundo; o Whisper trabalha com 16 000 (16 kHz), em um canal só (mono).

microfonesomWhispersom → textocérebro 5.1escolhe e validaESP32executa Pipertexto → somalto-falante"andando 30 cm"tudo roda na sua máquina, sem internet

Como ler: a linha de cima (setas teal) é o caminho do pedido, da sua boca até o motor. A linha de baixo (setas ciano tracejadas) é o retorno: o cérebro conta o que fez e o Piper fala. A caixa com brilho é a mesma do módulo 5.1; a voz só troca o teclado por um microfone.

✓ Faça

  • ✓Teste cada estação sozinha antes de juntar.
  • ✓Mostre na tela o texto que o Whisper entendeu.
  • ✓Faça o robô dizer o que vai fazer antes de fazer.

✗ Não faça

  • ✗Pular o validador porque "foi só uma frase".
  • ✗Usar a voz como única forma de parar o robô.
  • ✗Juntar tudo de uma vez e depurar no escuro.

Por que aprender

Voz é a interface mais natural para quem não programa: uma criança, um visitante, um aluno na feira de ciências. Montar o pipeline por partes ensina a desconfiar de cada estação e a achar onde está a demora ou o erro.

Conceitos-chave

STT
som → texto
TTS
texto → som
VAD
fala ou silêncio
16 kHz mono
o áudio do Whisper
2

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

O Whisper é um modelo aberto de reconhecimento de fala que entende português. Duas versões rodam bem fora da nuvem: o faster-whisper, em Python, que usa a CPU com números de 8 bits (int8) ou a GPU NVIDIA; e o whisper.cpp, em C++, leve e bom para o Pi. O curso usa o faster-whisper porque conversa direto com o script do módulo 5.1. O modelo é baixado da internet na primeira vez; depois disso, funciona offline.

ModeloAcerto em portuguêsOnde usar
tiny / baseerra palavras parecidasPi 5, comandos curtos e testes
smallbom para frases simplesPi 5 com paciência, qualquer PC
mediumbem melhor com sotaque e ruídoPC, de preferência com GPU
large-v3 / turboo mais precisoPC com GPU NVIDIA

Objetivo: instalar o faster-whisper e descobrir o número do seu microfone. Na primeira transcrição, o modelo escolhido é baixado uma vez.

terminal
source ~/robo-ia/bin/activate          # o mesmo ambiente do módulo 5.1
sudo apt install -y libportaudio2      # Linux/Pi: o sounddevice precisa dele
pip install faster-whisper sounddevice numpy
python3 -m sounddevice                 # lista microfones: anote o número do seu

Objetivo: gravar alguns segundos do microfone e imprimir o que você falou. O arquivo também serve de módulo: o assistente do tópico 4 importa as funções gravar e transcrever daqui.

ouvir.py
#!/usr/bin/env python3
"""Grava do microfone e transcreve em português com faster-whisper (offline após o 1º download).
pip install faster-whisper sounddevice numpy"""
import time
import sounddevice as sd
from faster_whisper import WhisperModel

TAXA = 16000                # o Whisper trabalha com 16 kHz, mono
MODELO_WHISPER = "small"    # tiny, base, small, medium... maior = acerta mais e demora mais
MICROFONE = None            # None = padrão do sistema; ou o número de: python3 -m sounddevice
_modelo = None

def carregar():
    global _modelo
    if _modelo is None:       # CPU int8 roda no Pi 5 e em qualquer PC
        _modelo = WhisperModel(MODELO_WHISPER, device="cpu", compute_type="int8")
        # com GPU NVIDIA: WhisperModel(MODELO_WHISPER, device="cuda", compute_type="float16")
    return _modelo

def gravar(segundos=4.0):
    print(f"Gravando {segundos:.0f} s... fale agora.")
    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("Enter para falar (Ctrl+C sai) ")
        audio = gravar()
        t0 = time.perf_counter()
        texto = transcrever(audio)
        print(f"Você disse: '{texto}'  (transcrição em {time.perf_counter() - t0:.1f} s)")

Como verificar

  • ✓"anda trinta centímetros para a frente" aparece escrito quase igual, com o tempo da transcrição.
  • ✓Ficando em silêncio, o texto sai vazio (o VAD cortou o silêncio).
  • ✗Erro de PortAudio ou texto sempre vazio? Confira o número do microfone em MICROFONE e o volume de captura do sistema.

Por que aprender

O reconhecimento de fala é a porta de entrada: se ele entende "trinta" como "três", todo o resto erra junto. Medir o tempo e testar modelos de tamanhos diferentes no seu hardware é o que vai decidir se o seu robô responde em um segundo ou em dez.

Conceitos-chave

faster-whisper
Whisper em Python, rápido
whisper.cpp
Whisper em C++, leve
int8
números menores, mais rápido
language="pt"
não perde tempo adivinhando
3

🔔 Palavra de ativação e microfone no robô

O robô não deve transcrever tudo o que ouve. Ele precisa de um gatilho para começar a prestar atenção. O mais simples e confiável é apertar para falar: uma tecla no PC ou um botão no GPIO do Pi. A alternativa é uma palavra de ativação ("ei, robô"), detectada por um modelo minúsculo que roda o tempo todo; projetos abertos como o openWakeWord fazem isso, e você pode treinar uma palavra própria (confira a documentação do projeto escolhido).

esperando ouvindopensandofalando eexecutandobotão ou"ei, robô" textoválidoterminou, deu erro ou só silêncio: volta a esperar

Como ler: o robô passa a maior parte do tempo no círculo da esquerda, que pulsa. Só o gatilho leva a "ouvindo". Qualquer saída ruim (texto vazio, ação recusada, modelo sem resposta) volta pela seta ciano para "esperando", sem mexer em motor.

✓ Microfone que funciona

  • ✓Microfone USB simples ou de lapela, apontado para quem fala.
  • ✓Longe dos motores e do ventilador do Pi (ruído mecânico).
  • ✓Não gravar enquanto o próprio robô fala (ele se ouviria).

✗ Causas de erro

  • ✗Microfone do notebook a dois metros, numa sala com eco.
  • ✗Ouvir sempre, sem gatilho: comandos disparados por conversa alheia.
  • ✗Volume de captura no máximo: o som distorce e o Whisper erra.

🌱 Novo aqui?

No Raspberry Pi, um botão de "apertar para falar" é um botão comum entre um GPIO e o GND. Com a biblioteca gpiozero (módulo 3.1), Button(17).wait_for_press() substitui o input() do script: o robô espera o botão em vez do Enter.

Por que aprender

Um robô que obedece a qualquer frase da sala é um robô imprevisível. O gatilho e o bom posicionamento do microfone resolvem a maioria dos problemas de reconhecimento antes de você precisar de um modelo maior.

Conceitos-chave

Apertar para falar
o gatilho mais seguro
Palavra de ativação
"ei, robô"
Máquina de estados
esperar, ouvir, pensar, falar
Ruído
motor, eco, ventilador
4

🔗 Do texto ao comando (com o 5.1)

O texto que sai do Whisper entra exatamente onde, no módulo 5.1, entrava o que você digitava. Nada muda no validador: a voz é só mais uma porta de entrada. A novidade é a confirmação falada: para movimentos grandes, o robô pergunta "confirma?" e só segue se ouvir "sim".

Passo a passo

1

Junte os arquivos numa pasta

cerebro_llm.py (módulo 5.1), ouvir.py (tópico 2) e falar.py (tópico 5). O assistente importa funções dos três.

2

Teste em modo simulado

Rode com --simular: o robô fala, mas nenhuma linha vai à serial. Confira o texto ouvido e a ação escolhida em cada pedido.

3

Ligue o ESP32 com o robô suspenso

Grave o executor do módulo 5.1 no ESP32, deixe as rodas no ar ou o braço com espaço livre e só então rode sem --simular.

Objetivo: o assistente completo: Enter para falar, transcrição, escolha da ação pelo modelo local, validação, confirmação por voz quando o movimento é grande, envio ao ESP32 e resposta falada, com o tempo de cada etapa no terminal.

assistente_voz.py
#!/usr/bin/env python3
"""Assistente de voz offline: ouvir (Whisper) -> escolher (modelo local) -> validar -> ESP32 -> falar (Piper).
Na mesma pasta: cerebro_llm.py (módulo 5.1), ouvir.py e 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": "andando {} centímetros", "GIRAR": "girando {} graus",
          "GARRA": "garra: {}", "PARAR": "parando"}

def em_palavras(linha):                        # "ANDAR 30" -> "andando 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("Pronto. Aperte Enter e fale.")
    while True:
        try:
            input("\n[Enter] para falar, Ctrl+C para sair ")
        except (KeyboardInterrupt, EOFError):
            break
        audio = gravar(4)
        t0 = time.perf_counter(); texto = transcrever(audio); t1 = time.perf_counter()
        print(f"Ouvi: '{texto}'")
        if not texto:
            falar("Não entendi. Pode repetir?"); continue
        try:
            resp = perguntar_modelo(texto)
        except Exception as e:                    # sem resposta ou JSON ilegível: não move nada
            print("Falha no modelo:", e); falar("Meu cérebro não respondeu."); continue
        t2 = time.perf_counter()
        ok, linha, confirmar = validar(resp)
        if not ok:
            print("Recusado:", linha); falar("Não posso fazer isso."); continue
        if confirmar:
            falar(f"Confirma {em_palavras(linha)}? Diga sim ou não.")
            palavras = transcrever(gravar(2.5)).lower().replace(".", " ").replace(",", " ").split()
            if "sim" not in palavras:            # palavra inteira: "assim" não confirma
                falar("Cancelado."); continue
        print("ESP32:", enviar(s, linha))
        falar(em_palavras(linha))
        t3 = time.perf_counter()
        print(f"Tempos: ouvir {t1 - t0:.1f} s · pensar {t2 - t1:.1f} s · executar e falar {t3 - t2:.1f} s")
    if s:
        s.write(b"PARAR\n"); s.close()

if __name__ == "__main__":
    main()

Como verificar

  • ✓"gira noventa graus para a direita" → "Ouvi: ...", "ESP32: (simulado) GIRAR 90" e a voz diz "girando 90 graus".
  • ✓"anda oitenta centímetros" → o robô pergunta "Confirma...?"; responder "não" cancela.
  • ✓"dança para mim" → "Não posso fazer isso." e nada é enviado.

Por que aprender

Juntar peças testadas é como sistemas de verdade são construídos. Como cada arquivo tem uma função clara, o projeto final do módulo 5.6 reaproveita os três sem reescrever nada.

Conceitos-chave

Importar módulos
reaproveitar o 5.1
Confirmação falada
"sim" antes do grande
perf_counter
cronômetro preciso
Falha segura
erro = não mover
5

🗣️ Piper: voz offline em português

O Piper é um sintetizador de voz aberto, rápido o bastante para o Raspberry Pi. Cada voz é um arquivo .onnx (o modelo) com um .onnx.json ao lado (a configuração). Há vozes em português do Brasil, como pt_BR-faber-medium; a lista completa está no repositório de vozes. O projeto saiu do endereço original (rhasspy/piper) e continua como piper1-gpl, da Open Home Foundation; o pacote piper-tts do pip acompanha essa mudança, e a função de síntese mudou de nome entre versões. O script abaixo funciona com as duas.

texto"girando"fonemasʒiˈɾɐ̃du voz .onnxpt_BRonda sonoraWAVroda no Pi 5 sem GPU e sem internet

Como ler: da esquerda para a direita, a frase é quebrada em sons da língua (fonemas), que o modelo da voz transforma em áudio. Trocar de voz é trocar só o arquivo da caixa com brilho; o resto do caminho é igual.

Objetivo: instalar o Piper e baixar uma voz em português. Ao final, os arquivos pt_BR-faber-medium.onnx e .onnx.json devem estar na pasta.

terminal
pip install piper-tts
python3 -m piper.download_voices pt_BR-faber-medium   # baixa .onnx e .onnx.json na pasta atual
# versão antiga do pacote? baixe os dois arquivos da voz em huggingface.co/rhasspy/piper-voices

Objetivo: uma função falar(texto) que sintetiza na memória e toca no alto-falante padrão, sem gravar arquivo no disco.

falar.py
#!/usr/bin/env python3
"""Fala um texto em português com Piper, offline. 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"    # o .onnx.json precisa estar ao 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"):   # versões novas do piper-tts
            _voz.synthesize_wav(texto, wav)
        else:                                 # versões antigas
            _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 terminar: o robô não se ouve falando

if __name__ == "__main__":
    falar(" ".join(sys.argv[1:]) or "Olá, eu sou o robô do curso. Estou pronto.")

Como verificar: python3 falar.py "Girando noventa graus" toca a frase em português em menos de um segundo num PC (um pouco mais no Pi). Se der "No such file", o nome em VOZ não bate com o arquivo baixado; se não sair som, confira o dispositivo de saída com python3 -m sounddevice.

Por que aprender

Um robô que diz o que vai fazer é mais seguro e mais fácil de depurar: você ouve "girando noventa graus" antes do movimento e tem tempo de apertar a parada se não era isso.

Conceitos-chave

Piper
TTS aberto e leve
.onnx + .json
a voz e a configuração
Fonema
unidade de som da língua
pt_BR
vozes do Brasil
6

⏱️ Latência e privacidade

Latência é o tempo entre você parar de falar e o robô reagir. Ela é a soma das estações, e quase sempre uma delas domina. Por isso o assistente imprime os tempos separados: você otimiza a etapa mais lenta, não a que parece mais lenta. A figura mostra a forma típica num PC sem GPU; os números do seu hardware vão ser outros, então meça.

gravarfixo: o tempo que você fala Whispercresce com o modelomodelo (LLM)em geral a maiorPiperpequenavalores ilustrativos: rode o assistente e anote os seus

Como ler: cada barra é uma etapa; o comprimento é o tempo. A barra tracejada não depende do computador, depende de quanto você grava. A barra que pulsa costuma ser a do modelo de linguagem: é nela que um modelo menor ou uma GPU fazem mais diferença.

✓ Reduz a espera

  • ✓Carregar os modelos uma vez, no início (o script já faz).
  • ✓Gravar só enquanto o botão está apertado, em vez de tempo fixo.
  • ✓Modelo de linguagem menor e prompt curto; GPU se tiver.
  • ✓Falar um "ok" curto logo, enquanto o modelo pensa.

✗ Aumenta a espera

  • ✗Carregar o Whisper a cada frase.
  • ✗Deixar o Whisper adivinhar o idioma.
  • ✗Pi 5 sem cooler: a CPU esquenta e reduz a velocidade.
  • ✗Respostas longas do modelo para comandos curtos.

Privacidade é a outra metade da escolha. Num serviço de voz na nuvem, o áudio da sua sala viaja até um servidor de terceiros. Em sala de aula, com vozes de crianças, isso exige autorização e cuidado com a lei de proteção de dados. No pipeline local, o som é processado e descartado dentro do robô.

localmicrofonePi / PCo som não sai de casa nuvemmicrofoneservidor deterceiros áudio atravessa a internet

Como ler: à esquerda, a seta curta fica dentro da moldura teal: o áudio nunca deixa o aparelho. À direita, a seta tracejada vermelha cruza a internet. A nuvem pode ser mais precisa ou mais rápida; a escolha depende de quem fala perto do robô e do que você pode mandar para fora.

Por que aprender

Medir latência transforma "está lento" em "o modelo de linguagem leva tanto tempo", que tem solução. E pensar em privacidade desde o início evita refazer o projeto quando ele for para uma escola, uma feira ou a casa de alguém.

Conceitos-chave

Latência
soma das etapas
Gargalo
a etapa mais lenta
Offline
funciona sem internet
Dados de voz
pedem autorização

✅ Resumo do módulo

✓
Pipeline em estações: microfone, Whisper, cérebro 5.1, ESP32 e Piper, testados um a um.
✓
faster-whisper: 16 kHz mono, language="pt", VAD ligado, modelo do tamanho do seu hardware.
✓
Gatilho: apertar para falar ou palavra de ativação; microfone perto e longe do motor.
✓
Mesmo validador: a voz só troca o teclado; movimento grande pede "sim" falado.
✓
Piper, latência e privacidade: voz pt_BR offline, tempos medidos por etapa, áudio que não sai de casa.

Para ir além

Próximo módulo

5.3 · Braço SO-101 e LeRobot: montar, calibrar e teleoperar o braço que vai aprender com você.