Jukeboxroboter
Aus Hansa-Wiki
Inhaltsverzeichnis |
Aufgabenstellung
Aufgabe unseres S4-Kurses in Informatik war es, einen Roboter mit Dialog-System zu entwickeln. Dieser soll in der Lage sein, einen möglichst sinnvollen und komplexen Dialog mit einem Menschen zu führen. Dies sollte zum einen mit den Lego Mindstorms-Robotern und zum anderen mit der Software DialogOS der Firma CLT Sprachtechnologie umgesetzt werden.
Über Lego Mindstorms
Die "Lego Mindstorms" des Spielzeugherstellers Lego sind weit mehr als ein Spielzeug: Eine zentrale Steuerungseinheit mit LC-Anzeige und Lautsprechern ,Elektromotoren, sowie diverse Lego-Technik-Teile ermöglichen die Konstruktion und Programmierung von interaktiven Systemen. Erweitert werden die Funktionen des Roboters durch eine Reihe von Sensoren. In der neusten Baureihe "Lego Mindstorms NXT" stehen folgende Sensoren zur Verfügung:
- Ein Berührungssensor
- Ein Ultraschallsensor(Für Abstandsmessungen)
- Ein Lichtsensor
- Ein Geräuschsensor
- Ein Rotationssensor, der die Drehung der Elektromotoren in °(Grad) ausgibt
Der Roboter lässt sich zum einen über den USB-Anschluss und zum anderen auch völlig Kabellos über Bluetooth ansteuern. Beiliegend ist auch die Software "Lego Mindstorms Education NXT" mit graphischer Oberfläche zur Programmierung des Roboters, welche von uns aber nicht genutzt wurde.
Über DialogOS
Die Software DialogOS stellt eine grafische Entwicklungsumgebung für Dialogsysteme zur Verfügung, die eine Kommunikation von Mensch und Maschine ermöglicht. Hergestellt und vertrieben wird dieses von der Firma CLT Sprachtechnologie GmbHin enger Zusammenarbeit mit der Universität des Saarlandes. Das Programm ermöglicht eine Ansteuerung von externen Geräten. In der Basis-Version ist so der Zugriff auf die Roboter der "Lego Mindstorms NXT"-Serie samt deren Sensoren möglich. Erweiterungen, die auf Anfrage von der Hersteller-Firma zur Verfügung gestellt werden, ermöglichen auch die Kommunikation mit diversen anderen externen Programmen(z.B. Datenbanken, Media Player), wozu allerdings teilweise umfangreichere Java-Kenntnisse notwendig sind. Das Programm ist somit eine ideale Möglichkeit um Grundsätze der Kommunikation von Mensch und Maschine, sowie des Dialog-Aufbaus anschaulich zu vermitteln.
Idee
Unsere Idee war es, einen Roboter so zu programieren, dass er Musikwünsche abspielen kann. Dazu soll er zuerst einen Benutzer, der vor dem Roboter steht, erkennen und ihn ansprechen. Der Benutzer soll dem Roboter nun einen Musikwunsch mitteilen können: Er kann einen Titel, einen Interpreten oder auch ein Musikgenre nennen. So soll der Benutzer dann mit dem Roboter in ein Gespräch kommen, das letztlich zur Folge hat, dass das gewünschte Lied, soweit in der "Jukebox" vorhanden, abgespielt wird.
Die Umsetzung
Startprobleme
Die Erkennung des Benutzers soll mithilfe des Ultraschall-Sensors des Roboters umgesetzt werden. Überraschenderweise trat hier bereits das erste Problem auf: Über den USB-Anschluss ließen sich alle Sensoren mit Außnahme des Ultraschall-Sensors ansprechen, der nur via Bluetooth zu erreichen war. Dieses Problem ließ sich aber durch ein Update des sehr hilfsbereiten Supports der Herstellerfirma CLT Sprachtechnologie lösen und somit konnten wir unsere Arbeit fortsetzen.
Erste Kontaktaufnahme
Der Roboter ist nun also durch den Ultraschall-Sensor in der Lage festzustellen, ob sich in dem Bereich 50cm vor ihm eine Person aufhält. Dazu wird die von DialogOS zur Verfügung gestellte Funktion "NXT Sensor abfragen" verwendet. Der vom Sensor zurückgelieferte Wert wird in der Variable "Abstand" gespeichert und im nächsten Schritt ausgewertet. Der Roboter liefert hier den Abstand von einem Objekt zum Sensor in Cm zurück. Ist die Bedingung "abstand>50" nicht erfüllt,so kehrt das Programm zum Start zurück und fragt den Sensor so immer wieder ab, bis die Bedingung erfüllt ist.
Ist die Bedingung erfüllt, also eine Person vor dem Sensor des Roboters erkannt worden, spricht der Roboter diese an und erklärt, was er für Funktionen bereitstellt. Dies wurde durch einen Sprachausgabe-Knoten realisiert. Dem Benutzer wird mitgeteilt, dass er nun einen Interpreten, einen Titel oder ein Genre nennen kann. Außerdem kann er sich, um sich einen Überblick zu verschaffen, Titel oder Interpreten von dem Roboter aufsagen lassen. Ein Spracheingabeknoten wartet an dieser Stelle auf eine Aussage des Users. Der Spracheingabeknoten erhält die benötigten Informationen aus einer Grammatik, die aus Datensätzen einer Sql-Datenbank erstellt wird. Hierzu muss jedoch erst eine Datenbank-Anbindung realisiert werden.
Probleme mit der Datenbank
Bei der Anbindung einer Datenbank an DialogOS standen wir zuerst vor einer Reihe von Problemen. Zum einen war es mit den uns bisher bekannten Möglichkeiten nicht möglich, innerhalb einer Grammatik externe Variablen zu verwenden, zum anderen war die Anbindung einer Datenbank mit dem von der Herstellerfirma CLT Sprachtechnologie mitgelieferten Programm zur Kommunikation mit externen Programmen nicht möglich.
Ein weiteres Update, sowie umfangreiche Beispiele dazu, konnte zumindest einen Teil der Probleme lösen.
Der Support von CLT konnnte uns aber auch hier weiterhelfen. Sie sendeten uns Java-Programme, mit denen die Datenbankanbindung möglich sein sollte.
Die Kommunikation mit einer Datenbank konnte jedoch trotz der Hilfestellung nicht sofort realisiert werden. Der mitgeliefere JDBC Client (JDBCClient.jar) ermöglichte es zwar Anfragen an eine Datenbank zu stellen, jedoch verwendet dieser Client eine HSQL-Datenbank. Um eine Verbindung mit einer anderen Datenbank zu ermöglichen, - in unserem Falle eine MySQL-Datenbank - stellte CLT jedoch auch die Sourcen des JDBC-Clients zur Verfügung. Da uns aber leider die hierzu nötigen Kenntnisse im Umgang mit Java fehlen, waren wir hier auf die Arbeit der Quiz-Roboter-Gruppe angewiesen. Lucas konnte hier nach einiger Arbeit ein Java-Programm auf Grundlage des mitgelieferten JDBC-Clients entwickeln, dass uns den Zugriff auf die MySQL-Datenbank ermöglicht.
Datenbankabfrage
Dank des Java-Pogramms von Lucas war es uns nun endlich möglich mit einer Datenbank zu arbeiten. In der Datenbank sind Interpreten, Titel, Genres sowie die Spieldauer unsere Jukebox-Lieder gespeichert.
Um eine Datenbankabfrage zu machen muss zunächst das Programm von Lucas DialogOS MySQL Connector" gestartet werden. Daraufhin muss unter Dialog -> Geräte ein Externes Programm (ExtProg) erstellt werden. Hier muss dan unter Connector CLT Connector (Rendezvous) ausgewählt werden, unter Server Name MySQL und unter Server localhost eingetragen werden. Weiterhin muss eine unter Graph -> Variablen eine Variable vom Typ list erstellt werden (hier: interprets).
Nun, da die Voraussetzungen geschaffen sind, erstellen wir in DialogOS einen Eingabeknoten. In diesem Tragen wir unter Ausgabe nach einem Klick auf Neu den Abfragebefehl in Anführunszeichen ein. In unserem Fall ist dies:
"SELECT interpret, title FROM jukebox"
Unter Eingabe geben wir nach einem Klick auf Neu den Namen der zuvor erstellten Variable (interprets) ein.
Der DialogOS MySQL Connector schreibt, wenn das Programm ausgeführt wird, das Ergebnis der Abfrage in diese Variable. Die sieht beispielsweise folgendermaßen aus:
[["Arctic Monkeys", "The View From The Afternoon"], ["Billy Talent ", "Devil In A Midnight Mass""], ["Bob Marley", "Jammin"], ["Metallica", "Whiskey In The Jar"], ["Murray Head", "One Night In Bangkok"]]
Auf die einzelnen Elemente der Liste greift man nun mittells der Funktionen str() und get() zu. Möchte man beispielsweise auf den Interpreten Arctic Monkeys zugreifen so geschiet dies mit dem Befehl:
str(get(get(interprets, 0), 0))
Das erste get() greift auf die erste verschachtelte Liste zu (0 = erste Stelle, 1 = zweite Stellen usw.). Das zweite get() wählt dann aus dieser Liste Arctic Monkeys aus.
str(get(get(interprets, 0), 0))
...würde beispielsweise auf den Titel The View From The Afternoon zugreifen.
Mit dieser Methode konnten wir nun endlich Grammtiken erstellen, die externe Variablen verwenden - sog. dynamische Grammatiken.
Dynamische Grammatiken
Eines der Beispiele lieferte die benötigten Informationen zur Verwendung von externen Variablen in der Grammatik. Ein in einem Beispiel mitgeliefertes Script erstellte eine dynmaische Grammatik, die je nach Datenbankabfrage variable Interpreten, Titel bzw. Genres enthalten konnte.
Das folgende Script, das wir bearbeitet haben, schreibt die aus der Datenbank zuvor mit dem Befehl "SELECT interpret from jukebox" abgefragten Interpreten in eine dynamischen Grammatik:
1 grammatic = "root $main; public $main = [Ich möchte] (";
2 if(length(interprets)>0){
3 string str=get(get(interprets, 0),0);
4 grammatic=grammatic + "\"" + str + "\" {$=\"" + str+ "\"}" ;
5 for (int i=1;i < length(interprets);i++){
6 str=get(get(interprets, i),0);
7 grammatic = grammatic + "| \"" + str + "\" {$=\"" + str+ "\"}" ;
8 }
9 }
10 grammatic = grammatic +") [hören];";
- Zuerst muss eine Grammatik definiert werden. Die Variable grammatic wurde zuvor unter Graph -> Variablen mit einem leeren String ("") definiert.
- Die If-Abfrage prüft, ob die die Liste in der Variable interprets Elemente enthält. Ist dies der Fall, wird der erste Interpret in die Variable str geschrieben.
- Enthält die Liste noch mehr Interpreten, werden die restlichen Interpreten auch in die Variable grammatic geschrieben. Dies wird sooft mittels einer Schleife wiederholt, wie die Liste Elemente hat.
- Am ende sieht dann die Variable grammatic folgendermaßen aus:
grammatic = "Interpret1" | "Interpret2" | "Interpret3" | "Interpret4";
...und die dynamische Grammatik ist erstellt.
Der weitere Dialog
In der Grammatik befinden sich nun also alle Interpreten. Auch soll es für den Benutzer möglich sein sofort einen Titel oder auch ein Genre zu nennen. Der Weg führt hier wieder genauso über die dynamischen Grammatiken.
Der Benutzer wir jetzt aufgefordert, mit dem Roboter zu sprechen. Er soll einen Musikwunsch, d.h. Interpreten, einen Titel oder ein Musikgenre äußern. Sagt der Benutzer nun eine der aus der Datenbank abgefragten und somit in der dynamischen Grammatik vorhandenen Informationen, so wird dies mittels der Spracherkennung erkannt und in die Variable output geschrieben (die natürlich zuvor unter Graph -> Variablen erstellt wurde).
Wird seine Auswahl erkannt, wird diese vom Roboter zur Sicherheit durch Verwendung eines Sprachausgabeknotens noch einmal wiederholt. Hier wird der Nutzer auch schon darüber informiert, dass er den Abspielvorgang jederzeit mit dem Kommando "STOP" abbrechen kann.
Probleme bei der Spracherkennung
Ein wenig umständlich ist an dieser Stelle, das innerhalb einer Sprachausgabe nur eine Sprache verwendet werden kann.
Da alle der von uns verwendeten Interpreten und Titel englische Namen haben, musste hier ein relativ unübersichtliches Konstrukt aus mehreren Sprachausgaben verwendet werden, was z.B. so aussehen könnte:
Um die Übersichtlichkeit des Graphen zu gewährleisten, haben wir Teile wie diesen in einen Untergraphen ausgelagert.
Nennt der Benutzer keinen Titel, sondern Interpret, Genre oder Jahr, so folgt an dieser Stelle noch ein weiterer Schritt, in dem alle Titel die zur gesagten Information passen, ausgegeben werden.
Natürlich verlief aber auch dieser Teilschritt nicht ganz problemlos:
In der Software DialogOS ist für die Spracheingabeknoten kein ELSE-Fall vorgesehen. Wird also vom Benutzer etwas gesagt, was in der angeschlossenen Datenbank nicht vorhanden ist, steckt der Roboter in einer Sackgasse: Die Spracherkennung kehrt dann in den Ausgangszustand zurück.
Dies hat natürlich auch den Vorteil, dass bei einer undeutlichen Ansage des Benutzers kein Programmabbruch stattfindet, sondern er aufgefordert werden kann, sobald die Zeitbegrenzung überschritten wurde, seine Aussage zu wiederholen oder einen anderen Wunsch zu äußern. Andererseits kann sich der Benutzer auch nicht sicher sein, ob sein Wunsch wirklich nicht vorhanden ist oder ob der Roboter ihn einfach nicht verstanden hat.
Lösen lässt sich das Problem durch die Verwendung der Zeitbegrenzungs-Funktion im Spracheingabeknoten natürlich nur Recht unzureichend. Hier muss die Praxis noch zeigen, was eine sinnvolle Warte-Zeit ist. Zur Zeit liegt diese bei 5 Sekunden.
Um vorzubeugen, dass der Benutzer nicht weiß, ob sein Wunsch-Lied in der "Jukebox" vorhanden ist, kann sich der Benutzer die vorhanden Musikstücke wie bereits zu Beginn der Dokumentation erwähnt ganz am Anfang des Dialogs aufsagen lassen.
Bei diesem Spracheingabeknoten zeigte sich ein Fehler des Programms DialogOS: Nach Anbindung an die Datenbank musste dieser überarbeitet werden und sollte deswegen vorerst gelöscht werden. Beim Löschen der Sprachknoten traten bei uns jedoch regelmäßig Fehlermeldungen auf. Diese erhielten weder sinnvolle Informationen zum Problem, noch ließen sie sich beheben. Nur ein Neustart des Programms machte ein Löschen der Knoten wieder möglich.
Weiter im Dialog
Nach der ersten Wunschäußerung des Benutzers werden alle Titel aus der Datenbank abgefragt, die den Interpret / den Titel / das Genre enthalten, das von der Spracherkennung erkannt wurde.
Bei einem Titel wird es mit ziemlicher Sicherheit nur ein Ergebnis der Abfrage geben. Also wird nun der Benutzer an dieser Stelle noch einmal zur Vergewisserung gefragt, ob er diesen Titel hören möchte. Antwortet dieser mit "Ja", wird der Titel abgespielt.
Bei einem Interpreten oder einem Genre gibt es hier noch einen Zwischenschritt, da es hier meistens mehrere Ergebnisse der Datenbankabfrage gibt. An dieser Stelle werden dann alle Titel aus der Datenbank abgefragt, die dem gewünschten Interpreten bzw. Genre entsprechen. Aus diesen Titeln soll dann der Benutzer einen Titel wählen, der natürlich zur Vergewisserung noch einmal nachfragend wiederholt und schließlich abgespielt wird.
Durch ein Script, das dem der dynamischen Grammatiken ähnelt, aber etwas einfacher ist, werden die Datenbankabfragen in einer Variable gespeichert und ausgeben. Bei den Interpreten sieht das Script folgendermaßen aus:
if(length(titles)>0){
string str=get(get(titles, 0),0);
output2=output2 + str;
for (int i=1;i < length(titles);i++){
str=get(get(titles, i),0);
output2 = output2 + ", " + str;
}
}
Das Prinzip bei diesem Script ist das Gleiche wie bei den dynamischen Grammatiken.
Das Abspielen
Abgespielt wird der Titel Mit Hilfe eines Java-Programms, welches von der Herstellerfirma für die Kommunikation von DialogOS mit externen Programmen bereitgestellt wurde: "ExternalProgrammClient.jar". Dieses steuert den auf dem PC installierten VLC Media Player an, mit dem letztendlich das Musikstück abgespielt wird. Hierzu muss zuerst unter dem Menüpunkt Dialog->Geräte ein Gerät erstellt werden:
Dieses externe Programm kann nun über einen Ausgabeknoten angesprochen werden. Hierbei muss der Pfad des Programmes, der Pfad der abzuspielenden Datei und der Befehl "start" angegeben werden:
{command="C://Programme/VideoLAN/VLC/vlc.exe",args=["C://Musik/"+title+".mp3]}
{action="start"}
Zu Beachten ist an dieser Stelle, dass diese Befehle ausschließlich mit "normalen" Slashes funktionieren. Wird ein Back-Slash verwendet, so gibt DialogOS recht unverständliche Fehlermeldungen aus, die nicht sofort auf diesen Fehler schließen lassen. Die Variable "titel" wird natürlich wieder aus der Datenbank gewonnen. Wie an diesem Beispiel gut zu sehen ist, kann man an dieser Stelle im Gegensatz zu den Grammatiken sehr einfach auf Variablen zugreifen. Dies geschieht durch die Verwendung des in vielen Programmiersprachen üblichen Syntax
"String" + variable + "String"
Während nun über das Programm VLC die gewünschte Musik ausgegeben wird, ist in unserem Dialog ein Spracheingabeknoten aktiv. Dieser wurde mit zwei Ausgängen ausgestattet. Die Spracheingabe wartet zu dieser Zeit auf das Kommando "Stop", falls der Benutzer das Abspielen abbrechen möchte. Der zweite Ausgang ist durch die Funktion "Zeitbegrenzung" im Spracheingabeknoten erstellt worden. Hier wurde von uns jedoch keine feste Zeit eingetragen, sondern die Variable "time", was hier wiederum auch problemlos möglich ist. Diese wird, wie oben schon bei den Variablen "title" und "interpret" beschrieben, aus der Datenbank bezogen. Das Abspielen lässt sich hier also entweder vom Benutzer beenden oder beendet sich nach Ende des Titels von selber.
Beendet wird VLC durch den auf beide Ausgänge der Sprachausgabe folgenden Ausgabeknoten mit dem Befehl:
{action="kill"}
Ein typischer Dialog
Ein typischer Dialog unseres Roboters könnte also so aussehen:
Roboter: Hallo! Ich bin der Jukebox-Roboter. Bitte nennen Sie mir einen Titel,Interpreten
oder Genre, damit ich ihren Musikwunsch Abspielen kann.
User : The Kooks
Roboter: Sie haben sich "The Kooks" gewünscht. Ist das richtig?
User : Ja
Roboter: Wir haben folgende Titel für Sie zur Auswahl:
See The Sun
Always Where I Need To Be
Mr. Maker
Do You Wanna
User : See The Sun
Roboter: Sie haben den Titel "See The Sun" gewählt. Soll dieser abgespielt werden?
User : Ja
[Spiele "The Kooks - See The Sun.mp3" ab bis der User "STOP" sagt oder das Lied zu Ende ist.]
User : STOP
Roboter: Sie haben das Abspielen abgebrochen. Möchte Sie einen anderen Titel hören?
User : Nein
Roboter: Vielen Dank für Ihren Besuch! Auf Wiedersehen!
