Skip to Content

Assistente de Voz no UNIHIKER M10 com OpenAI

Assistente de Voz no UNIHIKER M10 com OpenAI

O UNIHIKER M10 é uma placa compacta que integra um sistema baseado em Linux, sensores e interfaces de hardware numa única placa. Contém um processador quad-core Arm Cortex-A35 com 512 MB de RAM e 16 GB de armazenamento eMMC.

A placa também inclui um ecrã tátil de 2,8 polegadas com resolução de 240 × 320 pixels, conectividade Wi-Fi e Bluetooth. Os sensores integrados incluem um sensor de luz, acelerómetro, giroscópio e microfone.

Como a placa corre Linux e suporta programação em Python, pode ser usada para muitas aplicações. Neste tutorial, vai aprender a implementar um Assistente de Voz na placa UNIHIKER M10. O seguinte vídeo curto demonstra o Assistente de Voz:

Pode ser necessário aumentar o volume do seu computador para ouvir a minha voz. Note também que este assistente não tem saída de voz, embora seja fácil de adicionar.

Peças Necessárias

Vai precisar de uma placa UNIHIKER M10. Pode adquiri-la na DFRobot ou na Amazon, por exemplo:

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Hardware do Unihiker M10

O UNIHIKER M10 é construído em torno do chip Rockchip RK3308. Este chip usa quatro núcleos Arm Cortex-A35 e opera a uma frequência de até 1,2 GHz. O processador corre um sistema operativo Debian Linux completo com Python 3.7 pré-instalado. Também estão pré-instaladas várias bibliotecas científicas Python como Numpy, Matplotlib, Jupyter, Pandas, Seaborn, Tensorflow e outras.

A placa inclui 512 MB de memória DDR3 para o sistema. Esta memória é usada pelo sistema operativo Linux e pelas aplicações do utilizador. O armazenamento dos programas é fornecido por 16 GB de memória flash eMMC integrada.

Além do processador principal, a placa integra um microcontrolador GD32VF103C8T6 como co-processador. Este contém 64 KB de memória flash e 32 KB de SRAM. O microcontrolador gere muitas tarefas a nível de hardware, como gestão de sensores, controlo de GPIO e operação de atuadores.

Ecrã e Interface do Utilizador

O UNIHIKER M10 vem com um ecrã tátil a cores de 2,8 polegadas. O ecrã tem uma resolução de 240 × 320 pixels. A placa inclui um botão Home e dois botões programáveis de utilizador rotulados A e B, como mostrado abaixo:

Buttons on UNIHIKER M10
Botões no UNIHIKER M10

Sensores Integrados

Os sensores integrados incluem um sensor de luz, acelerómetro, giroscópio e microfone. A expansão de hardware está disponível através de portas USB, conectores Gravity para sensores e um conector edge compatível com micro:bit que expõe interfaces GPIO, I2C, SPI e UART.

Front and back of UNIHIKER M10
Frente e verso do UNIHIKER M10 (source)

Além disso, um buzzer passivo e um LED de estado fornecem saída áudio e visual simples para notificações e depuração. Um altifalante externo pode ser ligado via porta USB.

Conectividade e Rede

A comunicação sem fios está disponível através de um módulo combinado Wi-Fi e Bluetooth. A placa suporta Wi-Fi a 2,4 GHz e conectividade Bluetooth 4.0. O Wi-Fi pode ser configurado via uma interface Web em 10.1.2.3.

Interfaces e Opções de Expansão

A placa oferece várias interfaces físicas para ligar hardware externo. Um conector USB Type-C é usado para alimentação e comunicação com um computador. A placa pode ser alimentada com 5 V através desta porta.

Uma porta USB Type-A host permite a ligação de periféricos USB como dispositivos de armazenamento, teclados ou adaptadores. A placa também inclui um slot para cartão microSD para expandir a capacidade de armazenamento ou transferir ficheiros.

Além disso, estão disponíveis múltiplos conectores de expansão para sensores e atuadores. Conectores Gravity de 3 pinos fornecem acesso a entradas PWM e analógicas. Conectores dedicados de 4 pinos fornecem comunicação I2C para sensores e módulos digitais.

IO Connectors of UNIHIKER M10
Conectores IO do UNIHIKER M10 (source)

Conector Edge

A placa também possui um conector edge compatível com micro:bit. Este conector expõe até 19 pinos GPIO e suporta interfaces como I2C, UART e SPI. Também fornece várias entradas ADC e saídas PWM para controlo de hardware externo. A imagem abaixo mostra o pinout do conector edge:

Pinout of Edge Connector
Pinout do Conector Edge (source)

Esquemas

Para informações técnicas mais detalhadas, consulte os Esquemas do UNIHIKER M10 ligados abaixo:

Especificações Técnicas

A tabela seguinte resume as Especificações Técnicas da placa UNIHIKER M10:

Característica Especificação
Processador Principal Rockchip RK3308
Arquitetura CPU Quad-core Arm Cortex-A35
Frequência CPU Até 1,2 GHz
Memória do Sistema 512 MB DDR3
Armazenamento Interno 16 GB eMMC
Co-Processador Microcontrolador GD32VF103C8T6 RISC-V
Velocidade do MCU Até 108 MHz
Memória do MCU 64 KB Flash, 32 KB SRAM
Sistema Operativo Debian Linux
Ecrã Ecrã tátil de 2,8 polegadas
Resolução do Ecrã 240 × 320 pixels
Conectividade Sem Fios Wi-Fi 802.11 b/g/n (2.4 GHz), Bluetooth 4.0
Sensores Sensor de luz, IMU de 6 eixos (acelerómetro + giroscópio), microfone
Entradas do Utilizador Botão Home, botões de utilizador A/B, ecrã tátil
Saída de Áudio Buzzer passivo
Portas USB 1 × USB-C (alimentação e dados), 1 × USB-A host
Expansão de Armazenamento Slot para cartão microSD
Interfaces de Expansão Conectores Gravity de 3 pinos (analógico/PWM), conectores I2C
Conector Edge Conector edge compatível com micro:bit
Interfaces Suportadas GPIO, ADC, PWM, I2C, SPI, UART
Tensão de Funcionamento Entrada de 5 V via USB-C
Nível Lógico 3,3 V
Corrente Típica Até ~2 A
Dimensões da Placa Aproximadamente 51,6 mm × 83 mm × 13 mm

Programar o UNIHIKER M10

Existem várias aplicações que pode usar para programar o UNIHIKER M10, como Jupyter Notebook, Mind+, VSCode, Python IDLE ou Thonny. Para instruções detalhadas sobre como configurar estas aplicações, veja a Getting Started secção da documentação do UNIHIKER.

Instalar o Thonny

Pessoalmente, achei Thonny o mais fácil de usar para um projeto pequeno como este tutorial. Para instruções de instalação, veja a Download and Install secção Thonny da documentação do UNIHIKER.

Ligar o Thonny ao UNIHIKER M10

Após instalar o Thonny, ligue o seu UNIHIKER M10 ao computador com o cabo USB. Depois abra o Thonny e clique em “Run -> Select interpreter …”:

Isto abrirá uma caixa de diálogo onde deve escolher “Remote Python 3 (SSH)” como interpretador. Em Host insira “10.1.2.3”, no Username escreva “root” e o método de autenticação é “password”:

Depois de clicar em “Run -> Stop/Restart backend”, o Thonny estará ligado ao seu UNIHIKER M10 e poderá editar e executar programas Python nele.

Instalar Putty e a biblioteca OpenAI

De seguida, vamos precisar de uma ferramenta que nos permita instalar bibliotecas Python, como a OpenAI biblioteca no UNIHIKER M10. Tentei instalar bibliotecas via Thonny (“Manage Packages”, “Open System Shell…”) mas não consegui.

Em vez disso, usei Putty. Para instruções de instalação veja a SSH Tools da documentação do UNIHIKER. Depois de instalado, abra o Putty e crie uma Sessão com 10.1.2.3 como HOST:

Depois clique no botão “Open” e na shell do Putty insira “root” como nome de utilizador e “dfrobot” como palavra-passe:

Agora pode instalar a biblioteca OpenAI com o comando “pip install openai”:

Se faltar alguma outra biblioteca Python, pode instalá-la da mesma forma. Note que o UNIHIKER M10 deve estar ligado via cabo USB.

Configurar o Wi-Fi

Finalmente, também precisamos de configurar a ligação Wi-Fi, para que o nosso Assistente de Voz tenha acesso à internet para chamar as ferramentas AI da OpenAI.

Para isso, abra um navegador e insira “10.1.2.3” na barra de endereços. Vai ver uma página com um item “Network Settings” na barra lateral. Clique nele e insira as credenciais do seu Wi-Fi no diálogo à direita:

Obter a Chave API da OpenAI

O nosso Assistente de Voz vai usar modelos AI fornecidos pela OpenAI. Por isso, vai precisar de uma conta OpenAI. Vá a https://platform.openai.com e registe-se com um endereço de email ou uma conta Google ou Microsoft existente.

Depois de verificar o seu email e completar a configuração inicial, faça login no painel da OpenAI, platform.openai.com/api-keys e encontre ou crie a sua Chave API (=SECRET KEY) como mostrado abaixo:

OpenAI API keys
Chaves API da OpenAI

A Chave API é uma cadeia única e longa, começando com “sk-proj-“, necessária para autenticar os seus pedidos API (veja abaixo). Mais tarde, terá de copiar esta cadeia inteira para o código do Assistente de Voz.

sk-proj-xcA.......................OtDu0U

Isto é tudo o que realmente precisa, mas recomendo definir um limite de uso para a sua conta. Isto garante que não acaba com uma conta cara devido a um bug no seu código (ex. enviar centenas de pedidos).

Pode definir Limites de Uso e também consultar os Preços (Custos) dos diferentes modelos AI na aba Billing (platform.openai.com/settings/organization/billing).

Agora estamos prontos para escrever o código do Assistente de Voz.

Código para o Assistente de Voz

No Thonny crie um ficheiro novo, eu chamei o meu “assistant_tk.py” e copie e cole o código abaixo.

Este código implementa um assistente de voz. Grava áudio enquanto o botão A está pressionado, transcreve a fala para texto usando o modelo Whisper da OpenAI, envia o texto para um modelo de linguagem GPT para obter uma resposta, e mostra a pergunta e a resposta na interface gráfica do dispositivo.

# www.makerguides.com
# Python 3.7
# openai 1.39.0
# PyAudio 0.2.11
# pinpong 0.6.1
# numpy 1.21.6

import time
import threading
import numpy as np
import pyaudio
import io
import wave
from openai import OpenAI
from pinpong.extension.unihiker import button_a
import tkinter as tk
from tkinter import ttk
from typing import List

API_KEY = "sk-proj-my-api-key"
DEVICE_INDEX = 2
SAMPLE_RATE = 16000
CHANNELS = 1
CHUNK = 1024

client = OpenAI(api_key=API_KEY)

# GUI -----------------------------------------------------

root = tk.Tk()
root.title("Voice Chatbot")
root.geometry("240x320")

status_var = tk.StringVar()
status_var.set("Hold Button A to speak")

status_label = ttk.Label(root, textvariable=status_var,
    font=("Arial", 11), anchor="center")
status_label.pack(fill="x", padx=6, pady=4)

frame = tk.Frame(root)
frame.pack(fill="both", expand=True)

scrollbar = tk.Scrollbar(frame, width=18)
scrollbar.pack(side="right", fill="y")

text_box = tk.Text(frame, wrap="word",
    yscrollcommand=scrollbar.set, font=("Arial", 10))
text_box.bind("<Motion>", lambda e: "break")
text_box.bind("<B1-Motion>", lambda e: "break")
text_box.bind("<Button-1>", lambda e: "break")
text_box.pack(side="left", fill="both", expand=True)

scrollbar.config(command=text_box.yview)


def set_status(text):
    status_var.set(text)
    root.update_idletasks()


def show_answer(text):
    text_box.delete("1.0", tk.END)
    text_box.insert(tk.END, text)
    text_box.see(tk.END)


# Audio helpers -----------------------------------------------

def normalize(pcm_bytes: bytes) -> bytes:
    samples = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32)
    peak = np.max(np.abs(samples))
    if peak == 0:
        return pcm_bytes
    gain = (0.9 * 32767) / peak
    normalized = np.clip(samples * gain, -32768, 32767).astype(np.int16)
    return normalized.tobytes()


def record_while_held() -> bytes:
    frames: List[bytes] = []

    pa = pyaudio.PyAudio()
    stream = pa.open(
        format=pyaudio.paInt16,
        channels=CHANNELS,
        rate=SAMPLE_RATE,
        input=True,
        input_device_index=DEVICE_INDEX,
        frames_per_buffer=CHUNK
    )

    set_status("Hold Button A to speak")

    while not button_a.is_pressed():
        time.sleep(0.01)

    set_status("recording...")

    while button_a.is_pressed():
        data = stream.read(CHUNK, exception_on_overflow=False)
        frames.append(data)

    stream.stop_stream()
    stream.close()
    pa.terminate()

    return b"".join(frames)


def pcm_to_wav(pcm_bytes: bytes) -> bytes:
    buf = io.BytesIO()
    with wave.open(buf, "wb") as wf:
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(2)
        wf.setframerate(SAMPLE_RATE)
        wf.writeframes(pcm_bytes)
    return buf.getvalue()


# OpenAI -----------------------------------------------

def transcribe(wav_bytes: bytes) -> str:
    audio_file = io.BytesIO(wav_bytes)
    audio_file.name = "recording.wav"

    set_status("transcribing...")
    response = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file,
        language="en",
        temperature=0
    )
    return response.text


def ask_gpt(question: str) -> str:
    set_status("thinking...")
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "You are a helpful assistant. Answer clearly and concisely."
            },
            {"role": "user", "content": question}
        ],
    )
    return response.choices[0].message.content


# Main assistant loop -----------------------------------------------

def assistant_loop():
    while True:
        raw_pcm = record_while_held()
        if not raw_pcm:
            continue

        normalized_pcm = normalize(raw_pcm)
        wav_bytes = pcm_to_wav(normalized_pcm)
        question = transcribe(wav_bytes)
        answer = ask_gpt(question)

        set_status("ready")
        show_answer(question + "\n\n" + answer)


threading.Thread(target=assistant_loop, daemon=True).start()
root.mainloop()

Importações

O código começa por importar vários módulos necessários para a sua funcionalidade. Inclui bibliotecas padrão como time, threading, e io para temporização, execução concorrente e manipulação de fluxos de bytes. Também importa numpy para operações numéricas em dados de áudio, pyaudio para gravação de áudio, e wave para manipulação do formato WAV.

O cliente Python da OpenAI é importado para interagir com a API da OpenAI. O módulo específico do UNIHIKER button_a é importado para detetar pressões dos botões. Finalmente, tkinter e ttk são importados para criar a GUI, e List do typing é usado para tipagem.

import time
import threading
import numpy as np
import pyaudio
import io
import wave
from openai import OpenAI
from pinpong.extension.unihiker import button_a
import tkinter as tk
from tkinter import ttk
from typing import List

Constantes e Inicialização do Cliente

São definidas várias constantes para configurar os parâmetros de gravação de áudio, como a chave API para OpenAI, o índice do dispositivo de entrada de áudio, taxa de amostragem, número de canais e tamanho do buffer de áudio. Um objeto cliente OpenAI é criado com a chave API fornecida, permitindo comunicação com os serviços OpenAI.

API_KEY = "sk-proj-my-api-key"
DEVICE_INDEX = 2
SAMPLE_RATE = 16000
CHANNELS = 1
CHUNK = 1024

client = OpenAI(api_key=API_KEY)

Note que deve substituir o valor fictício “sk-proj-my-api-key” da API_KEY pela sua chave API da OpenAI!

Interface Gráfica do Utilizador (GUI)

A GUI é construída usando tkinter. É criada uma janela principal com título e tamanho fixo. Um rótulo de estado é adicionado para informar o utilizador sobre o estado atual, como pedir para manter o botão A pressionado para falar ou mostrar mensagens de progresso.

Abaixo do rótulo de estado, há uma caixa de texto com barra de rolagem para mostrar a pergunta transcrita e a resposta do assistente. A caixa de texto está configurada para ser só de leitura, desativando interações do rato que modifiquem o seu conteúdo.

root = tk.Tk()
root.title("Voice Chatbot")
root.geometry("240x320")

status_var = tk.StringVar()
status_var.set("Hold Button A to speak")

status_label = ttk.Label(root, textvariable=status_var,
    font=("Arial", 11), anchor="center")
status_label.pack(fill="x", padx=6, pady=4)

frame = tk.Frame(root)
frame.pack(fill="both", expand=True)

scrollbar = tk.Scrollbar(frame, width=18)
scrollbar.pack(side="right", fill="y")

text_box = tk.Text(frame, wrap="word",
    yscrollcommand=scrollbar.set, font=("Arial", 10))
text_box.bind("<Motion>", lambda e: "break")
text_box.bind("<B1-Motion>", lambda e: "break")
text_box.bind("<Button-1>", lambda e: "break")
text_box.pack(side="left", fill="both", expand=True)

scrollbar.config(command=text_box.yview)

Funções de Estado e Exibição

Duas funções auxiliares gerem as atualizações da GUI. A função set_status() atualiza o texto do rótulo de estado e força a GUI a atualizar imediatamente. A função show_answer() limpa a caixa de texto, insere novo texto e faz scroll até ao fim para garantir que o conteúdo mais recente está visível.

def set_status(text):
    status_var.set(text)
    root.update_idletasks()


def show_answer(text):
    text_box.delete("1.0", tk.END)
    text_box.insert(tk.END, text)
    text_box.see(tk.END)

Auxiliares de Áudio

O código inclui várias funções para processar áudio. A função normalize() recebe bytes PCM brutos, converte-os em amostras em ponto flutuante e escala para que a amplitude máxima atinja 90% do máximo de um inteiro de 16 bits. Esta normalização assegura volume consistente para a transcrição.

def normalize(pcm_bytes: bytes) -> bytes:
    samples = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32)
    peak = np.max(np.abs(samples))
    if peak == 0:
        return pcm_bytes
    gain = (0.9 * 32767) / peak
    normalized = np.clip(samples * gain, -32768, 32767).astype(np.int16)
    return normalized.tobytes()

A função record_while_held() grava áudio do dispositivo de entrada especificado enquanto o botão A está pressionado.

Inicializa um fluxo PyAudio com os parâmetros configurados e espera até o botão A ser pressionado. Depois, lê continuamente blocos de áudio e adiciona-os a uma lista até o botão ser libertado. Os frames de áudio gravados são concatenados e retornados como bytes brutos PCM.

def record_while_held() -> bytes:
    frames: List[bytes] = []

    pa = pyaudio.PyAudio()
    stream = pa.open(
        format=pyaudio.paInt16,
        channels=CHANNELS,
        rate=SAMPLE_RATE,
        input=True,
        input_device_index=DEVICE_INDEX,
        frames_per_buffer=CHUNK
    )

    set_status("Hold Button A to speak")
    while not button_a.is_pressed():
        time.sleep(0.01)

    set_status("recording...")
    while button_a.is_pressed():
        data = stream.read(CHUNK, exception_on_overflow=False)
        frames.append(data)

    stream.stop_stream()
    stream.close()
    pa.terminate()

    return b"".join(frames)

A função pcm_to_wav() converte os bytes PCM brutos num fluxo de bytes em formato WAV usando o módulo wave. Isto é necessário porque a API de transcrição da OpenAI espera ficheiros áudio em formatos padrão como WAV.

def pcm_to_wav(pcm_bytes: bytes) -> bytes:
    buf = io.BytesIO()
    with wave.open(buf, "wb") as wf:
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(2)
        wf.setframerate(SAMPLE_RATE)
        wf.writeframes(pcm_bytes)
    return buf.getvalue()

Interação com a API OpenAI

Duas funções gerem a comunicação com a API da OpenAI. A função transcribe() envia os bytes de áudio WAV para o modelo Whisper para obter a transcrição em texto. Atualiza o rótulo de estado para indicar que a transcrição está em progresso e retorna o texto reconhecido.

def transcribe(wav_bytes: bytes) -> str:
    audio_file = io.BytesIO(wav_bytes)
    audio_file.name = "recording.wav"

    set_status("transcribing...")

    response = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio_file,
        language="en",
        temperature=0
    )

    return response.text

A função ask_gpt() envia a pergunta transcrita para um modelo de chat baseado em GPT. Define o estado para “a pensar…” enquanto espera pela resposta. Depois, constrói a mensagem do sistema que instrui o modelo a ser um assistente útil. Finalmente, retorna o texto da resposta do assistente.

def ask_gpt(question: str) -> str:
    set_status("thinking...")
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "You are a helpful assistant. Answer clearly and concisely."
            },
            {"role": "user", "content": question}
        ],
    )

    return response.choices[0].message.content

Loop Principal do Assistente

A funcionalidade principal corre dentro da função assistant_loop(), que é executada numa thread separada para manter a GUI responsiva.

Espera continuamente que o utilizador mantenha o botão A pressionado e grava áudio enquanto o botão está pressionado. Se não for capturado áudio, reinicia o loop. Caso contrário, normaliza o áudio, converte para formato WAV, transcreve a fala e consulta o modelo GPT para obter uma resposta.

Após o processamento, atualiza o estado para “pronto” e mostra a pergunta e a resposta na caixa de texto.

def assistant_loop():
    while True:
        raw_pcm = record_while_held()
        if not raw_pcm:
            continue

        normalized_pcm = normalize(raw_pcm)
        wav_bytes = pcm_to_wav(normalized_pcm)

        question = transcribe(wav_bytes)
        answer = ask_gpt(question)

        set_status("ready")
        show_answer(question + "\n\n" + answer)

Iniciar o Assistente e o Mainloop da GUI

Finalmente, o loop do assistente é iniciado numa thread daemon, permitindo que corra em segundo plano. O loop principal de eventos da GUI é então iniciado com root.mainloop(), que mantém a janela aberta e responsiva às interações do utilizador.

threading.Thread(target=assistant_loop, daemon=True).start()
root.mainloop()

Mensagens de Erro

Se executar o código, pode ver os seguintes avisos no console do Thonny:

ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pcm_a52.c:823:(_snd_pcm_a52_open) a52 is only for playback
ALSA lib conf.c:5014:(snd_config_expand) Unknown parameters {AES0 0x6 AES1 0x82 AES2 0x0 AES3 0x2  CARD 0}
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM iec958:{AES0 0x6 AES1 0x82 AES2 0x0 AES3 0x2  CARD 0}
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card

Isto acontece porque ao inicializar pyaudio.PyAudio(), a biblioteca ALSA (Advanced Linux Sound Architecture) subjacente verifica todos os dispositivos e configurações de áudio possíveis no sistema (como som surround 5.1, HDMI ou S/PDIF digital).

Como o UNIHIKER M10 é um computador de placa única compacto, não tem “altifalantes traseiros” nem “portas de modem”, por isso o ALSA queixa-se de não os encontrar. Se o seu código realmente corre e processa áudio após estes avisos, a sua configuração está a funcionar bem.

Conclusões

Neste tutorial aprendeu a implementar um Assistente de Voz no UNIHIKER M10 usando os serviços da OpenAI. Para outros exemplos de código mais simples, veja a Python Coding Examples da documentação do UNIHIKER.

Note que pode facilmente expandir e melhorar o Assistente de Voz adicionando histórico de chat e funcionalidade de chamada a ferramentas. Também usei Tkinter para criar uma interface simples, mas para uma GUI melhor pode usar QtPy, que também está pré-instalado no UNIHIKER M10.

Além disso, pode adicionar um altifalante, via USB, Bluetooth ou saída de linha, para que o seu Assistente de Voz responda com áudio. Veja o tutorial AI Language Tutor with UNIHIKER M10 para um exemplo.

Para um Chatbot de Visão que pode ver e responder a perguntas sobre imagens, veja o nosso tutorial Vision Chatbot with DFRobot ESP32-S3 AI Camera and OpenAI. E para mais exemplos de AI, veja a AI Projects secção da documentação do UNIHIKER.

Se tiver alguma dúvida, sinta-se à vontade para deixar nos comentários.

Boas experiências de construção 😉