Skip to content
This repository has been archived by the owner on Jun 9, 2022. It is now read-only.
Horkrux8 edited this page Jun 9, 2022 · 16 revisions

P5 - "Tür der fetten Dame"

Dies ist eine Projektausarbeitung zum Bauen einer Sprachgesteuerten, automatischen Tür.

Vorwort

Dieses Dokument enthält in jeder Form Dokumentation & Reflexion Es ist in verschiedenen Versionen erhältlich:

  • Online Dies ist die empfohlene Variante, der Code ist dort zugänglich, dieses Dokument ist auf der Seite direkt sichtbar, hat ein Inhaltsverzeichnis und die Links funktionieren.

Von den folgenden Versionen wird abgeraten, aufgrund vielen mangelnden Funktionen:

  1. Papier
  • Keine Links
  • Kein Inhaltsverzeichnis
  • Fehlerhafte Formatierung Diese Version wird als Abgabe in einer Mappe vorliegen.
  1. README.pdf
  • Einige Fehlerhafte Links
  • Kein Inhaltsverzeichnis (bei falscher Software)
  • Fehlende Medien

Diese Datei liegt im abgegeben Zip-Archiv/READMEs vor.

Die Dokumentation / Reflexion ist aufgeteilt in mehrere Kategorien, welche zuerst alle Dokumentiert werden (Status zur Abgabe) und später reflektiert werden (Verlauf der Bearbeitung).

Dokumentation der Arbeitspakete

Software / Hardware

Die technische Umsetzung der Tür erfolgt mit zwei Geräten, einem Host (Laptop) und einem Client (Mikrocontroller / esp8266). Der Host startet das Programm, welches aufnimmt was gesagt wird, und wenn es das gesuchte Wort erkennt, wird der Client über das USB Kable benachrichtigt. Der Client durchsucht dann die empfangene Nachricht nach der Servo Konfiguration und stellt den Servo ein.

Vorwort - Codeblöcke

Die Codeblöcke der Dokumentation sind keine vollständige Repräsentation der fertigen Applikation, sondern dienen als der Erklärung dieser.


  • Im folgenden Text wird des öfteren in-Zeilen-Code referiert (Dieser ist in den Blöcken wiederzufinden).
Funktionen: Funktions-Namen() in Überschriften
  • Codeblöcke mit einer Generellen Beschreibung darüber:

Dieser Codeblock dient als Beispiel zur Veranschaulichung der Formatierung.

# von Datei-Namen: Funktions-Namen(), Conditional-Statement
print("Ein Code Beispiel") # Mit Kommentar
  • Und einer detaillierteren Benennung des Ablaufs darunter:

Die erste Zeile beschreibt die Position im Programm, dies hat meist ein # oder // davor, und Kennzeichnet Kommentare.

Genaueres - talking.py

Dies ist das Host Programm, welches für Spracherkennung, Tonausgabe und Kommunikation mit dem Client verantwortlich ist.

Funktion: record()

Diese Funktion ist für Tonaufnahmen und Spracherkennung zuständig.

Hier wird die Library: "SpeechRecognition" genutzt. (Im code: recognizer.xyz() & microphone.xyz())

# von talking.py: record()
with microphone as source: recognizer.adjust_for_ambient_noise(source) # Take ambient noise into account
    while True:
        print("Now Ready, listening in %s. (Press Ctrl+c to or say exit/quit/stop to quit)" % c.language_val)
        with microphone as source: audio = recognizer.listen(source)
        print("found audio sample")

Das Programm stellt sich am Anfang auf Störgeräusche ein und speichert eine Tonaufnahme in audio.

Diese Aufnahme wird an eine Google API geschickt, welche dann den erkannten Satz zurückgibt.

# von talking.py: record()
value = recognizer.recognize_google(audio, language=language_val)

Danach wird value "decoded", also in normale Schrift umgewandelt und weitergegeben.

Funktionen: send(Send_string) & receive(Search_string)

Diese Funktion ist der Sende Teil der Seriellen Kommunikation (Universal-Serial-Bus)

Hier wird die Library: "pyserial" genutzt. Im code: serial.Serial(port, Geschwindigkeit, timeout).xyz() oder auch: ser.xyz()

Anfangs konfiguriert das Programm die Schnittstelle mit den Werten aus config.py.

# von talking.py
port, baud = [value for value in list(c.serialcom.values())]
ser = serial.Serial(port, baud, timeout=1)
ser.flush()

Die Sende Funktion schickt den gegeben Wert Send_string mit einem Token zum Identifizieren auf die geteilte Serielle Schnittstelle.

# von talking.py: send(Send_string)
ser.write((c.HostKey+str(Send_string)).encode())
    print("HOST Send: %s %s" % c.HostKey, Send_string)

Die Empfangen Funktion pausiert das Programm so lange bis es auf der Schnittstelle den gegebenen Wert: Search_string in allen empfangenen Nachrichten: data findet. (Search_string ist in späterer Anwendung immer der Identifizierungs-Token des Clients)

# von talking.py: receive(Search_string)
while True:
        if ser.in_waiting > 0:
            data = ser.readline().decode('utf-8').rstrip()
            print("HOST Received: %s" % data)
            if Search_string in data:
                print("HOST found: %s" % Search_string)
                return data

Die Funktion gibt auch die Empfangenen Daten weiter1, sollten diese den Search_string enthalten.


[1]: In meiner Anwendung überflüssig, es wird nur für Debug nutzen ausgegeben.

Funktion: play(play_string)

Diese Funktion ist für das Abspielen von vorhandenen oder Computer generierten Tonaufnahmen verantwortlich.

Hier wird die Library: "os" genutzt. (Im code: c.os.xyz()) Sie wird zur Systemunterscheidung von Windows & Linux, und zum Ausführen von Befehlen genutzt.

Hier durchsucht das Programm eine vordefinierte Liste an Tonaufnahmen in ./audio (Definiert in config.py) nach dem play_string. Sollte es ein Eintrag geben, wird diese Tonaufnahme abgespielt.

# von talking.py: play(play_string)
if play_string in c.audio_dict.keys():
        # Play one of the audios defined in config
        print("Found existing audio")
        # c.os because im recycling the import from config (c)
        c.os.system("mpg123 -q " + c.audio_dict[play_string]) 

Alternativ nutzt das Programm die Library: "google-Text-To-Speech" Im code: gTTS("xyz", "de").xyz() oder: tts.xyz(). Hier schickt das Programm den Satz an Google, was gesagt werden soll (play_string), und Google schickt eine Computer-generierte Tonaufnahme zurück, diese wird dann gespeichert, abgespielt und gelöscht.

# von talking.py: play(play_string)
else:
        # Create new audio by Google
        print("Creating temporary audio")
        file = play_string+".mp3"
        tts = gTTS(play_string, c.language_val[0:2]) # Take first two char from language_val (de)_DE
        tts.save(file)
        # Play audio using command line player
        c.os.system("mpg123 -q " + file)
        c.os.system("rm " + file)
Funktion: main()

Diese Funktion verknüpft alles zusammen in einer geordneten Ablaufstruktur. Sie wird am Ende aufgerufen.

# von talking.py: main()
print("running in mode = %s" % runmode)
while True: # Keep running even on false reply
    # Decide runmode (mostly for debug or should anything not work during presentations)
    if runmode == 1:
        recstring = record() 
    elif runmode == 2:
        recstring = c.magic
    elif runmode == 3:
        configureServo()

Hier wird der Programmverlauf entschieden, abhängig von manueller Konfiguration:

  1. Normal, Aufnehmen -> Servo verstellen.
  2. Spracherkennung Fail safe, Servo verstellen.
  3. Konfiguration, Servo je nach Nutzer Eingabe verstellen.

Nun wird entschieden, ob das Wort in recstring denn das gewünschte Wort in c.magic ist.

# von talking.py: main(), while True
if recstring.lower() == c.magic.lower():
    print("Magic word recognized = %s" % c.magic)
    send("180") # actually 90 for the big Servo
    break

Wenn dies der Fall ist, wird die vorher erwähnte send(Send_string) Funktion genutzt, welche darauf den Identifizierungs-Token mit 180 am Ende an den Client schickt. Wie der Client die Nachricht versteht, wird im nächsten Kapitel (opendoor.ino) erläutert.

Zuletzt wird noch alles an die Konsole weitergegeben was auf der Seriellen Schnittstelle ankommt. Sollte der Client Identifizierungs-Token dabei sein, wird das Programm beendet. (Siehe Funktion receive())

# von talking.py: main(), while True
receive(c.ClientKey) # Read esp serial debug
ser.close()

Genaueres - opendoor.ino

Dies ist das Client Programm, welches für die Steuerung des Servos und Kommunikation mit dem Host zuständig ist.

Hier wird Servo.h genutzt, eine Library von Arduino (Im code: Servo.xyz oder auch door.xyz()).

Am Anfang werden Setup Werte konfiguriert:

// von opendoor.ino
#define StartDegree 0 // Start from on power reset
#define PIN 4 // 4 is D2
#define baudrate 9600 // Communication speed
  • Der StartDegree gibt die Servo Rotation im geschlossenen Zustand an.
  • Der PIN ist der Pulse-width-modulation Pin, er transferiert Daten zwischen dem Client und dem Servomotor.
  • Die baudrate ist die Kommunikationsgeschwindigkeit der Seriellen Schnittstelle, diese muss für Host & Client identisch sein.

In der setup() Funktion wird die Library initialisiert, die Serielle Schnittstelle und der Servo konfiguriert.

// von opendoor.ino: setup()
door.attach(PIN); // This is pin D3
Serial.begin(baudrate);
Serial.printf("\nCLIENT: Started \n");
door.write(StartDegree); // Reset servo

Nach dem Setup geht der Client in die loop() Funktion und wiederholt diese für immer.

// von opendoor.ino: loop()
if (Serial.available() > 0){
        // Read all incoming
        incomingString = Serial.readString();
        Serial.print("CLIENT: Found incoming serial: "+incomingString+"\n");

Hier wartet der Client bis auf der Seriellen Schnittstelle Nachrichten kommen, diese werden in incomingString gespeichert.

// von opendoor.ino: loop(), if (Serial.available() > 0)
if (incomingString.indexOf(HostKey) == 0){
            // If incomingString starts with HostKey, strip HostKey of incoming to find target & change rate
            int targetDegree = incomingString.substring(HostKey.length()).toInt();
            int changeDegree = 4; // Degree to continue in one turn
            int changeDelay = 10; // Delay in ms between turns
            int currDegree = door.read();
            int overflowDegree = (currDegree - targetDegree) % changeDegree;
            changeDegree = (currDegree > targetDegree) ? changeDegree*-1 : changeDegree*1; // Negate direction

Sollte die Nachricht den Host Identifizierungs-Token enthalten wird dieser abgeschnitten und der Rest in targetDegree gespeichert (Servo Zielposition). Außerdem wird festgelegt in welchen Schritten, in welchen Zeitabständen und in welche Richtung sich der Servo dreht, wobei letzteres automatisch passiert.

Da nicht jede Ziel Position in jeder Schrittgröße von jeder Start Position erreichbar ist, wird hier der Rest der Division von der Differenz von Start-Ziel Positionen durch die Schrittgröße genommen und zu der derzeitigen Position zugerechnet.

Equation.

Der Versuch sich von 30° nach 80° mit einer Schrittgröße von 4 zu rotieren, würde darin Enden, dass der Motor sich auf eine falsche oder sogar nicht vorhandene Position dreht (2° oder -2°), was Probleme verursacht.

Also passt das Programm die derzeitige Position so an, dass diese Restlos mit der Schrittgröße Teilbar ist.

// von opendoor.ino: loop() if (Serial.available() > 0) -> if (incomingString.indexOf(HostKey) == 0)
if (overflowDegree != 0){
                door.write(currDegree + overflowDegree);
                Serial.printf("Adjusting to step size: %d, starting from %d\n", changeDegree, currDegree - overflowDegree);
                currDegree = currDegree + overflowDegree;
            }

Nun ist die Ziel Position von der Start Position erreichbar und es kann gedreht werden, dies passiert in der folgenden Schleife:

// von opendoor.ino: loop() if (Serial.available() > 0) -> if (incomingString.indexOf(HostKey) == 0)
// Slow down rotation by rotating +changeDegree° every changeDelay ms
for (currDegree; currDegree!=targetDegree; currDegree += changeDegree) {
    Serial.printf("CLIENT: Turning to: %d, target: %d, step size: %d \n", currDegree, targetDegree, changeDegree);
    door.write(currDegree);
    delay(changeDelay);
    }

Die Schleife läuft so lange wie die derzeitige- von der Ziel- Position abweicht, bei jedem Durchlauf wird der Wert der derzeitigen Position (currDegree) um die Schrittgröße (changeDegree) inkrementiert und der Servomotor zu dem gesteigerten Wert gedreht.

Bei der Beendung des Programms wird sichergestellt das der Servo auch wirklich an der gewünschten Position ist und es wird eine Nachricht an den Host geschickt, dass dieser aufhören soll, Debug Nachrichten auf die Konsole ausgeben soll und sich selbst beendet.

// von opendoor.ino: loop()
door.write(targetDegree); // Close any remaining gap to target
Serial.printf("CLIENT: done (at: %d) \n", door.read());
Serial.println(ClientKey);
// Everything beyond ClientKey wont be read by Host

Reflexion der Arbeitspakete

Organisation

Rollenverteilung

Die Rollen- und Verantwortungsbereiche wurden in den Projektmanagement-Tagen verteilt, mache Bereiche wurden getauscht.

Aufgabenverteilung

Inga:

  • Materialbeschaffung
  • Zeitmanagement
  • Team-Organisierung/Kommunikation

Justus:

  • Entwicklung Öffnungsmechanismus
  • Bau des Öffnungsmechanismus
  • Bau der Tür

Lennart:

  • Software für Mikrocontroller (Servo)
  • Software für Host (Spracherkennung/Ausgabe)
  • Externe-Organisierung/Kommunikation
  • Bündeln/Überarbeitung der Dokumentation/Reflexion

Andis:

  • Sammlung von Dokumenten/Formalien
  • Dokumentation des Arbeitsprozesses
  • Bau der Tür

Die jeweilige Dokumentation/Reflexion ist auch von der Zuständigen Person verfasst worden.

Zeitmanagement

Das Zeitmanagement im Verlaufe des Projekts funktionierte gut. Die Gruppe hat in den Projektmanagement Tagen ihre Projektidee ausarbeiten und zeitlich strukturieren können. Es wurden beide Konsultationssitzungen wahrgenommen, wo der Fortschritt präsentiert und das zukünftige Verfahren abgesprochen wurde.

So war die Tür schon zur zweiten Konsultationssitzung fertig, allerdings mit einem schwachen Motor. Dort wurden die Probleme und mögliche Lösungen besprochen und in den folgenden Wochen die Aufrüstung als Problemlösung gesetzt. So wurde der fertigen Stand bereits gegen Ende April erreicht. Mit der Dokumentation wurde auch schnell begonnen sodass bereits Anfang Juni alle Teile des Projekts grundsätzlich fertig waren.

Dies lief stressfrei ab und es wurden Termine gefunden, die für alle passten. So wurde verhindert dass die Gruppenmitglieder sich untereinander behindern, was eine entspannte und souveräne Arbeitsatmosphäre schuf.

Materialbeschaffung

Eine Liste der benötigten Materialien stellte sich zusammen, nachdem die Umsetzung des Mechanismus und der Hardware fertig geplant war. Diese war grob, da die Gruppe keine Erfahrung hatte, was für einen solchen Bau benötigt wurde.

Dank der Hobbywerkstatt “Hackerspace Bremen” waren viele Werkzeuge und Materialien bereits vorhanden. Genutztes Werkzeug waren zum Beispiel eine Heißklebepistole, Schleifmaschine oder auch Kabelbinder.

Die einzigen Materialeinkäufe die im Verlaufe des Projekts waren die Scharniere und ein größerer Servomotor.

Öffnungsmechanismus

Die Planung des Öffnungsmechanismus hat mit einer Recherchephase begonnen. Es wurden verschiedene Öffnungsmechanismen für Türen in einem Dokument gesammelt und mit Ideen zur Umsetzung vervollständigt. Anschließend wurden die Mechanismen auf Funktionalität und Kompatibilität in unserem Modell geprüft. Im Wesentlichen haben sich 2 infrage kommende Öffnungsmechanismen gebildet.

Ein-Schienen Mechanismus

Bei dieser Konstruktion wird der Servomotor oberhalb der Tür befestigt, und bewegt nach unten gehend einen Metallrotor um etwa 90 grad. Der Rotor ist durch eine Metallschiene oben an der Tür befestigt.

Dies hat den Vorteil das es im Vergleich zum Zwei-Schienen Mechanismus simple zum Konstruieren ist.

Zwei-Schienen Mechanismus

Der Zwei-Schienen Mechanismus übersetzt eine Drehung von 180 grad in eine Öffnung von 90 grad.

Dies bringt den Vorteil dass der Motor mit Doppelter Kraft agieren kann, ist aber wesentlich komplexer im Bau.

Software / Hardware

Die Umsetzung ließ sich in einfachere Unterprobleme gruppieren:

  • Spracherkennung
  • Sprachausgabe
  • Tür Öffnung

Da ein Mikrocontroller zur Ansteuerung des Motors sowieso nötig war, (mehr in Tür Öffnung) stand fest das ein separates Gerät zur Sprach- Erkennung/Ausgabe genutzt wird. Schlussendlich wurde ein Programm zur Kommunikation zwischen Computer und Mikrocontroller benötigt.

Flowchart.

Das Vorwissen für eine solche Kommunikationsbrücke hatte ich bereits seit einer Wetterstation-Klausurleistung.

Spracherkennung

Anfangs wurde eine Software zur Spracherkennung rausgesucht, diese war aber eher als Home-Automation gedacht, also ein Programm zu Ausführung von simplen Befehlen bei Erkennung eines Wortes. Es wurde schnell klar, dass der Begriff "Software" nicht die Anforderungen beschreibt.

Darauf wurde die Code-Library "SpeechRecognition" gefunden, diese wurde in das Programm der Kommunikationsbrücke integriert.

Sprachausgabe

Die Sprachausgabe war eine Idee, welche im Verlauf des Projekts aufkam, die Tür sollte nicht nur bei dem Passwort aufgehen, sondern auch selber Kommentare machen können.

Nach der Erfahrung die in Spracherkennung gemacht wurde, hat die Gruppe direkt nach einer Library geschaut. Auch in gTTS wird ein Text an Google geschickt und es kommt eine Audiodatei zurück.

Allerdings werden nur vorgefertigte Audiodateien genutzt.

Tür Öffnung

Zur Öffnung der Tür wird ein Motor benötigt, es gab mehrere Arten welche zur Auswahl standen:

  • Plain-Elektromotor
  • Stepper-Motor
  • Servo-Motor

Ein einfacher Elektromotor dreht sich Kontinuierlich und benötigt ein zusätzliches Modul zum Rückwarstdrehen (Umdrehen der Spannung).

Ein Stepper-Motor dreht sich in Schrittgrößen, also z.B. 200 Zähne (Am Zahnrad) vorwärts, allerdings hat dieser eine geringer Zugkraft und drehte sich nicht Rückwärts.

Ein Servo erschien sehr schnell als nützlichste Art aufgrund der absolut-Grad-basierten Drehung und der hohen Zugkraft. Ursprünglich war die Drehung des Servo-Motors sehr abrupt.

Dies war zu schnell für die Öffnung der Tür, glücklicherweise ließ sich dies mit einer Schleife lösen.

Es wurde vermutet dass die Stärke des Servos nicht ausreichte, dieser hatte eine Kraft von $2,5kg/cm$ Der Motor funktionierte mäßig während eines Testlaufs in der Werkstatt:

Allerdings funktionierte dieser nicht während der 2. Konsultationssitzung. Aufgrund diesem Fehlschlag wurde auf einen größeren Servo umgestiegen, dieser unterstützt $10kg/cm$

Dieser hatte bei weitem mehr Kraft und erfüllte die Anforderungen:

Türbau & Qualitätsprüfung

Der Türbau wurde auf mehrere Tage aufgeteilt, es gab verschieden Probleme, welche überkommen worden mussten, um unser Ziel zu erreichen.

Der erste Schritt war es anhand des schon im Voraus in der Gruppe besprochenen Maßstabs einen generellen Bauplan zu erstellen, nachdem die Tür konstruiert werden sollte. Nachdem die Gruppe sich auf einen Bauplan geeinigt hatte, konnte die Arbeit anfangen.

Nach der Organisation der nicht vorhandenen Materialien fehlten noch Räumlichkeiten um eine Tür zu bauen, dabei bot sich das Hacker Space an, welches viele Materialien und Werkzeuge bietet.

Nachdem alle Materialien sowie Räumlichkeiten zur Verfügung standen, konnten der Bau des ersten Teils anfangen.

Türrahmen

Es wurde ein Teil eines Bilderrahmens als Türrahmen genutzt, an welchem die Tür befestigt wurde.

Dieser Bilderrahmen wurde so modifiziert, dass er auf der unteren Seite offen ist. Um die rauen Kanten an den Sägestellen zu entfernen, wurden diese abgeschliffen.

Türfront

Nachdem das Rahmen Gerüst genau ausgemessen wurde, sollte eine dünne Holzplatte, welche zurechtgeschnitten wurde, als Türfront genutzt werden.

Die Holzplatte wurde mit Linien versehen und dort geteilt. Die Schnittstellen wurden anhand der Größe des Türrahmens angepasst.

Im nächsten Schritt wurde die Holzplatte mit Hilfe von Schraubstöcken an einen Tisch befestigt, damit diese während des Zersägens nicht zu stark vibriert und die Schnittstelle grade bleiben.

Nach dem Teilen wurden die Schnittstellen glatt geschliffen, im Gegensatz zu dem Rahmen wurde hier Schleifpapier genutzt, da anderes Werkzeug das dünne Holz beschädigt hätte.

Um die Front etwas ansprechender zu gestalten, wurden dies Umrisse der "Fetten Dame" auf der Holzplatte, mithilfe eines Beamers als Vorlage, gezeichnet.

Scharniere

Nach der Fertigstellung der Teile, wurden Türscharniere genutzt, welche mit Metallstiften die Front und den Rahmen verbinden.

Um die Scharniere mit dem Rahmen zu verbinden, wurde der Rahmen mit Schraubstöcken an dem Tisch befestigt und die Scharniere per Hand und Elektroschrauber eingeschraubt.

Nachdem die Scharniere am Rahmen befestigt wurden, musste noch die Zweite Hälfte an der Tür montiert werden, dafür wurde eine Holzstütze an die Türfront gebaut, in welcher die Scharniere befestigt werden konnten, ohne das Holz zu beschädigen.

Dafür wurden zwei Holzplanken in passende Teile gesägt, damit diese am Rande der Tür angeklebt werden konnten.

Die Holzplanken wurden mithilfe von Holzleim und Schraubstöcken an der Holzplatte befestigt.

Nachdem die Stütze an der Türfront angebracht wurde, konnten auch die Scharniere befestigt werden. Dabei mussten auf die rotations-Richtung, sowie die Position geachtet werden.

Nach der Befestigung der Scharniere konnte die Tür mithilfe der Metallstifte eingehakt werden.

Türbeine

Die Türbeine dienen dazu, dass die Tür aufrecht stehen kann und nicht festgehalten oder angelehnt werden muss.

Jetzt wo nur noch Beine für die Fertigstellung der Tür fehlten, wurde der Öffnungs-/Schließmechanismus eingebaut.

Der nächste Schritt bestand daraus, zwei große passende Holzplanken zu finden, welche massive genug sind um das Gleichgewicht der Tür zu halten. Diese Holzplanken befestigten wir nun an der offenen Unterseite des Rahmens mit Hilfe von Metallwinkel.

In der Abbildung ist die verpackte Tür mit sichtbaren Holzplanken als Beinen dargestellt.

Gemälde

Der letzte Schritt zur Vervollständigung der Tür, war die Fertigstellung des Gemäldes der "Fetten Dame", dies ist die Leistung einer unserer externen Helfer.

Externe Hilfe

Unser Projekt hat auch Unterstützung von außerhalb der Gruppe bekommen.

Mikal

Als Veranstaltungstechniker am TBZ brachte er mehr handwerkliches Wissen in Form von "freundlicher" Kritik ein, als wir gebündelt erreicht hätten.

Catharina

Nach Kunst-LK in der Schule und eigenem Interesse am Malen erklärte Sie sich freundlicherweise dazu unsere Zeichnung in ein Gemälde zu verwandeln.

Hr. Scheichel

Als Binf Lehrer hat er Motoren die wir getestet, aber letztendlich zurückgegeben haben, und unseren Mikrocontroller beigesteuert.