Conteúdo detalhado
🔊 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).
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
👂 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.
| Modelo | Acerto em português | Onde usar |
|---|---|---|
| tiny / base | erra palavras parecidas | Pi 5, comandos curtos e testes |
| small | bom para frases simples | Pi 5 com paciência, qualquer PC |
| medium | bem melhor com sotaque e ruído | PC, de preferência com GPU |
| large-v3 / turbo | o mais preciso | PC 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.
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.
#!/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
🔔 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).
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
🔗 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
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.
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.
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.
#!/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
🗣️ 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.
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.
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.
#!/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
⏱️ 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.
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ô.
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
✅ Resumo do módulo
Para ir além
- faster-whisper — instalação, modelos e opções de transcribe
- whisper.cpp — Whisper em C++ para o Pi
- Piper (repositório original) — aponta para o sucessor piper1-gpl
- Vozes do Piper — procure por pt_BR
- openWakeWord — palavra de ativação aberta
- Ollama — o cérebro local do módulo 5.1
Próximo módulo
5.3 · Braço SO-101 e LeRobot: montar, calibrar e teleoperar o braço que vai aprender com você.