TSM - Proiect IoT: Talk to God

Ovidiu Mățan - Fondator @ Today Software Magazine


Contestat de multe ori și uneori pe bună dreptate, chatGPT-ul are o caracteristică unică: primim de fiecare dată un răspuns. Acest amănunt reprezintă și premisa aplicației de față: o funcționalitate talk to God, prin intermediul căreia primim un răspuns oricărei întrebări pe care o adresăm. Articolul de față dezvoltă funcționalitatea implementată în două articole precedente: Transcrierea și citirea unui text folosind modele OpenAI și Emoții și culoare - un proiect IoT.

De multe ori atunci când dorim să comunicăm cu cineva în mod eficient, trebuie să găsim canalul de comunicare potrivit. De obicei, avem de ales între un mesaj text sau o discuție față în față. În ceea ce privește interfața chatGPT-ului, principalul dezavantaj din perspectiva accesibilității îl poate reprezenta exact această nevoie de a comunica în scris. Proiectul prezentat în articolul de față reprezintă o integrare mai naturală a vastei rețele neuronale dezvoltată de OpenAI. Practic vom putea adresa întrebări și primi răspunsurile vocal fără a mai trebui să accesăm vreo pagină web. Mai mult, vom încerca să stimulăm utilizatorii într-un mod cromatic prin schimbarea culorilor din mediul ambiental.

Logica aplicației

  1. Înregistrăm întrebarea utilizatorului și o salvăm local într-un fișier wav.

  2. Transcriem conținutul în text folosind toolul de la OpenAI: whisper.

  3. Transmitem întrebarea către API-ul chatGPT și așteptăm un răspuns.

  4. Răspunsul obținut îl vom translata în format audio folosind voice API de la OpenAI.

  5. Redăm răspunsul primit.

Pentru ca totul să fie și un pic distractiv, utilizatorii vor putea adresa o întrebare, doar după ce zâmbesc la cameră. Din perspectiva hardware-ului, acesta rămâne neschimbat de la proiectul anterior de IOT:

Mai avem, totuși, de adăugat o boxă bluetooth sau un TV care să redea sunetele.

Notă: aplicația poate rula mai rapid pe un PC, dar îi va lipsi un pic din farmecul dat de detaliile cromatice.

Înregistrarea și salvarea întrebării

Odată detectat un zâmbet din bucla algoritmului de ML, pornim metoda de înregistrare audio folosind în cazul de față microfonul de la camera video:

p = pyaudio.PyAudio()

def record_audio()->None:
    print („start recording audio”)
    frames = []        
    # Open stream
    stream = p.open(
    format=pyaudio.paInt16,
         channels=CHANNELS,
         rate=RATE,
         input=True,
         frames_per_buffer=CHUNK)
    print(„talk now”)
    # Record data for 
    # the set duration
    for i in range(0, int(RATE  
    / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)

    print(„Finished recording.”)
    stream.stop_stream()
    stream.close()

    # Save the recorded data as a 
    # WAV file
    wf = wave.open(
    WAVE_OUTPUT_FILENAME, ‚wb’)

    wf.setnchannels(CHANNELS)
    wf.setsampwidth(
    p.get_sample_size(FORMAT))

    wf.setframerate(RATE)
    wf.writeframes(b’’.join(frames))
    wf.close()

Observație: La un număr de 44100 (RATE - Sample rate) de eșantioane/secundă obținem numărul acestora prin împărțirea la dimensiunea bufferului (CHUNK) și înmulțim totul cu durata în secunde a înregistrării (RECORDS_SECONDS).

Apelăm metoda de înregistrare nu înainte de a înștiința utilizatorul că urmează să înregistrăm sunetul:

print („start audio rec”)
play_text(„Ask your question”)
record_audio()

Transcrierea conținutului

Odată înregistrat, vom folosi toolul Whisper pentru transcrierea acestuia. În contextul rulării pe un Raspberry Pi 5, vom asista la anumite limitări, dar acestea sunt de ordinul secundelor față de un PC:

model=whisper.load_model(„base”)
result=model.transcribe(„./output.wav”)
question=result[‚text’]
print(„transcribe done:”,question)

Rezultatul este 90% corect în general pentru întrebările adresate în engleză, pentru cele în română nu prea există suport, dar probabil că există alternative.

Transmiterea întrebării și obținerea răspunsului de la chatGPT API

#add your real API key 
client = OpenAI(api_key = ‚___IZWw8Vs9________’  )

def get_answer(question):

    completion = client.chat.completions.create(
      model=”gpt-4”,
      messages=[
       {„role”: „system”, „content”: „You are an 
       answering robot. Your response will be 
       transform into speech. (100 letters answer)”},
       {„role”: „user”, „content”: question}
      ]
    )
    play_text(completion.choices[0].message.content)

Pentru inițializare va trebui să vă definiți o cheie în API-ul openAI, în special dacă doriți să folosiți versiunea a patra. Așa cum se poate vedea din rolul sistem, am încercat să limităm dimensiunea răspunsului primit la 100 de caractere.

Translatarea în format audio a răspunsului primit

def play_audio(file_path):
    # Initialize pygame
    pygame.mixer.init()

    # Load the audio file
    pygame.mixer.music.load(file_path)

    # Play the audio
    pygame.mixer.music.play()

    # Wait for playback to finish
    while pygame.mixer.music.get_busy():
        time.sleep(1)
def play_text(text):
    print(„playing:”,text)
    response = client.audio.speech.create(
        model=”tts-1”,
        voice=”fable”,
        input=text
    )
    print(„playing”)
    audio_content = response.content
    play_audio(io.BytesIO(audio_content))

Metoda play_text() va folosi API-ul de speech al openAI pentru a transmite textul serviciului specializat în generarea discursului. Am ales vocea “fable”, dar dacă nu vă place mai sunt alte cinci opțiuni. Referitor la calitate, am ales varianta simplă: tts-1 și nu cea hd: tts-1-hd. Din perspectiva testelor realizate, a doua variantă este mult mai înceată și nu conferă o calitate remarcabilă conținutului audio.

Redarea conținutului audio în metoda play_audio() folosește librăria pygame.

Schimbările cromatice

Culorile prind viață ușor prin transmiterea unui indice componentei Arduino. Indicii corespund diverselor stări (neutru, fericire/zâmbet, mirare ...) și au asociate o culoare. Codul este relativ simplu:

await send_command(bytes(str(5),’utf-8’))

unde

async def send_command(data):
    protocol, writer=await serial_asyncio
    .open_serial_connection(
    url=’/dev/ttyACM0’,baudrate=9600)
    writer.write(data)
    await writer.drain()
    #writer.close()
    await asyncio.sleep(0.1)

Considerente viitoare

Aplicația de față este doar un mic demo ce mai poate fi îmbunătățit. La un stand expo ar trebui să implementăm o trecere mai lină a culorilor benzii luminoase.

Din perspectiva posibilelor dezvoltări, am putea genera muzică pe care să o redăm local sau chiar să generăm cod care să fie încorporat în aplicația noastră. Doar gândiți-vă cum ar fi să îi spunem: generează-mi cod pentru recunoașterea culorilor și spune-mi câte cuburi roșii și verzi sunt pe masă?

Concluzie

Sper că v-am deschis apetitul pentru un alt fel de abordări prin care codul scris de noi - chiar dacă mai primim uneori ajutor de la chatGPT - să poată beneficia de o perspectivă mai largă de răspunsuri prin valorificarea acestor servicii online.