Montag, 19. August 2024

Eine sprachgesteuerte Balleinwurfmaschine für den Kickerkasten

Ein Tischfußball begeisterter Freund hat mich auf die Idee gebracht eine Einwurfmaschine für den Kickerkasten zu bauen. Der Ball soll auf einen Sprachbefehl hin eingeworfen werden, und die Maschine soll an verschiedenen Stellen im Spielfeld platziert werden können.

Im Bild oben ist meine Realisierung zu sehen. Eine gedruckte Scheibe, mit einer Tasche für den Ball, dreht sich in einem Kunststoff Topf. Der Ball wird während einer Umdrehung der Scheibe aufgenommen und fällt dann dann durch ein Loch im Boden des Topfes. Eine röhrenförmige Barriere verhindert, dass mehr als ein Ball durch das Loch fallen kann. In der Ansicht von unten erkennt man, dass der Ball durch ein gewinkeltes Stück Abflussrohr mit dem Durchmesser von 50 mm nach vorne ausgeworfen wird.

Als Antrieb habe ich einen 12V Schneckengetriebemotor mit 30rpm verbaut.

Laut Herstellerangaben verfügt dieser über ein maximales Drehmoment von 10 kg x cm, und dreht sich daher auch in einem mit Bällen voll gefüllten Topf gut.

Die gedruckte Scheibe mit der Tasche besteht aus zwei Teilen. Der untere Teil

enthält die Aufnahme für die Motorachse und Abstandshalter. Der obere Teil besteht aus einem planen Deckel, der sich auf die Abstandshalter schrauben lässt. 

Der Kunststofftopf mit den Bällen ist auf laminierte Sperrholzplatten der Stärke 18mm geschraubt, die sich auf die Stangen des Kickertisches stellen lassen. 

Im Bild oben ist rechts unten bereits die Steuerungseinheit der Maschine und ein Konferenz Mikrofon zu erkennen. Das gedruckte Gehäuse der Steuerung besteht aus Deckel und Boden,

und enthält neben einem Raspberry Pi 3 Modell B+ auch ein Relais, das mit 3V ansteuerbar ist, und Ströme bis zu 10A schalten kann. 

Das im Bild oben erkennbare USB Dongle Mikrofon mit einer Empfindlichkeit von -58 dB laut Herstellerangabe, habe ich später durch ein Konferenz-Mikrofon mit einer Empfindlichkeit von -45 dB ersetzt, um die Spracheingabequalität zu verbessern.

Für die Sprachsteuerung habe ich das Open-Source Software Toolkit VOSK von Aphacephei genutzt. Es unterstützt mehr als 20 Sprachen und Dialekte, darunter Englisch und Deutsch. Die kleinen Sprachmodelle, die sich auf dem Github repository von Alphacephei finden, laufen offline auch auf Geräten wie dem Raspberry Pi.  

Eine Einführung in des  Toolkit findet sich auf der Alphacephei website. Nach der Installation mit

pip3 install vosk

habe ich das Sprachmodell 'vosk-model-small-de-0.15.zip' durch

git clone https://github.com/alphacep/vosk-api

cd vosk-api/python/example

wget https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip

unzip vosk-model-small-de-0.15.zip

mv vosk-model-small-de-0.15/ model

aus dem repository geladen und entpackt.

Die Ablaufsteuerung erfolgt in Python. Der folgende Code wurde mit dem Syntax Highlighter highlight.hohli von Anton Shevchuk für den Blog aufbereitet, und mit 

<div style="background: rgb(255, 255, 255); overflow: auto; width: auto;"></div> 

ergänzt, um horizontales scrolling des Abschnitts zu gewährleisten.

import queue
import json
import sounddevice as sd
import vosk
import time
import gpiozero
import sys

q = queue.Queue()
STARTCODEs = ["einwurf", "ein wurf", "ein uhr", "ein ver", "nun eingabe", "eingabe", "ein einwurf", "ein", "ein waffen", "nun ein nur", "nun einwurf", "eingabe", "mehr", "auch ein mensch", "einwirken", "nun ein wolf", "eingriff"]

def callback(indata, frames, time, status):
    #"""This is called (from a separate thread) for each audio block."""
    if status:
        print(status, file=sys.stderr)
        pass
    q.put(bytes(indata))

if __name__ == '__main__':

    #Relais initialisieren
    red_led = gpiozero.LED(4)
    green_led = gpiozero.LED(17)

    # Speech Objekt erstellen
    model = vosk.Model('/home/pi/vosk-api/python/example/model')
    
    #Stream vom Mikrofon öffnen
    with sd.RawInputStream(samplerate=44100, blocksize=16000, device=None,dtype='int16',
                            channels=1, callback=callback):
        #Sternchen zeichnen
        #print('*' * 80)
        #Programmstart mit roter LED anzeigen
        red_led.on()
        
        # Aktivierung der vosk Spracherkennung mit Übergabe des geladenen Models. Übersetze das Gesprochene in Text.
        rec = vosk.KaldiRecognizer(model, 44100)
        while True:
            # Daten aus der Queue ziehen
            data = q.get()
            #print("Bitte sprechen!")
            if rec.AcceptWaveform(data):
                # wandle das gesprochene Wort in jason (JavaScript Object Notation) 
                res = json.loads(rec.Result())
                # und gib es aus
                print(res)
                # wenn der Aktivierungscode herausgehört wurde, wird das Relais geschaltet
                for wort in STARTCODEs:
                    if wort == res['text']:
                        green_led.on()
                        red_led.off()
                        # Relais 1 Sekunden anziehen
                        time.sleep(1)
                        green_led.off()
                        red_led.on()

            else:
                pass

Nach Laden der vosk Bibliothek wird im Hauptteil des Programms beim Start die rote Status LED gesetzt. Mit der 'sounddevice' Funktion 'RawInputStream' werden Aufnahmestückchen einer bestimmten Länge in eine 'queue' geschrieben.  Werden die Stückchen vom Modell als 'waveform' akzeptiert, werden sie transkribiert und mit den in 'STARTCODES' definierten Begriffen verglichen. Wenn der richtige Aktivierungscode herausgehört wurde, zieht das Relais für 1.3 Sekunden an, und die grüne LED wird gesetzt. Die Drehscheibe vollführt etwas mehr als eine Umdrehung, und wirft einen Ball aus. Danach erlischt die grüne LED, und die rote LED wird wieder gesetzt. Das System ist bereit für  das nächste Aktivierungswort. 

Damit das Programm nach dem Booten direkt startet, muss mit 'systemd' ein Service gestartet werden. Eine detaillierte Anleitung wie ein solcher 'Service' erzeugt werden kann findet sich z.B. hier.

Mein '/etc/systemd/system/AutoRunEinwurf_Service' file sieht so aus.

Zum bequemen Programmieren via Remote Desktop vom Laptop aus, habe ich diesesmal VNC statt X11 benutzt. Der VNC Server wird von Raspberry Pi OS standardmäßig unterstützt, muss aber aktiviert werden. Um des zu tun kann man raspi-config auf der Befehlszeile mit

sudo raspi-config

öffnen. Anschließend muss man zu 'Schnittstellenoptionen' navigieren, VNC auswählen und die bestätigen.

Als Client auf dem Laptop habe ich tigervnc heruntergeladen. Nach der Installation kann man sich wie gewohnt durch Eingabe der IP Adresse des Raspberry

auf den Desktop des kleinen Rechners einloggen.

Zu guter Letzt möchte ich die Maschine noch in Aktion zeigen.


Viel Spaß beim selber Basteln...

Keine Kommentare:

Kommentar veröffentlichen