/Integrar Hablando con Sara con robot butia
Contenido
Integrantes
- Montserrat López
- Pablo García
- Francisco Polti
Introducción
Desde la actividad "Hablando con Sara" permitir el envío de mensajes al robot Butiá. Por ejemplo: enviar comandos para mover los motores, o consultar sensores. La idea es que Sara se comunique con el usuario utilizando el motor de habla implementado en dicha actividad.
Objetivo
El objetivo de este proyecto consiste en integrar la actividad “Hablando con Sara” con el robot Butiá [1], de modo de poder enviarle comandos al robot desde la funcionalidad bot de la actividad, de la misma manera que poder recibir información del robot, como por ejemplo valores sensados e informarle los mismos al usuario a través de la actividad, de esta manera se pueden ingresar comandos a Sara y esta indica al robot que debe hacer, luego Sara responde al usuario de una manera adecuada y detallando información necesaria en caso de ser requerida.
Ambiente de Desarrollo
Trabajamos en este proyecto sobre Ubuntu 11.04, utilizando para desarrollar la versión 2.7 de python y para probar la actividad utilizamos “sugar in a window” versión 0.90. Para configurar el servidor bobot seguimos los pasos indicados en la wiki del proyecto Butiá [3]. Nos vimos obligados a utilizar la versión 11.04 de Ubuntu ya que con versiones más nuevas la actividad no funcionaba correctamente en “sugar in a window”.
Investigación
Como mencionamos anteriormente nuestro objetivo era integrar el robot Butiá con la actividad “Hablando con Sara” y dicha integración la debíamos realizar mediante la funcionalidad bot de la actividad. La funcionalidad bot permite interactuar con el usuario de manera “inteligente”, para ello el usuario debe escribir una pregunta y Sara responderá a esa pregunta con una respuesta que obtendrá desde las “bases de conocimiento” de la actividad. Las bases de conocimientos están formadas por diferentes archivos escritos en lenguaje AIML (Artificial Intelligence Markup Language) [2], que contienen patrones y posibles respuestas a esos patrones. Se pueden mapear diferentes patrones y dar una respuesta específica o una respuesta aleatoria en función de cómo sea definido. A partir de los distintos archivos con extensión .aiml se genera un único archivo con extensión .brn (sara.brn) el cual será la base utilizada por la funcionalidad para dar respuesta a las preguntas que el usuario realice. Al modificar un archivo .aiml o crear un archivo nuevo para formar parte de la base de conocimientos, hay que generar nuevamente el archivo sara.brn ejecutando un script, ya que la base de conocimientos no se actualiza automáticamente. Para ejecutar el script que genera la base hay que instalar previamente la librería PyAIML ya que de lo contario el script no ejecuta y da error, pero instalar ésta librería generó inconvenientes cuando trabajamos en la integración. Para realizar la integración del robot Butiá con la actividad, identificamos primero cual era la clase y función encargada de analizar el texto ingresado por el usuario para luego dar una respuesta al mismo.
Implementación
En esta sección vamos a desarrollar y especificar en detalle todo lo mencionado en la sección de Investigación. La primera tarea que realizamos luego de haber configurado el ambiente de desarrollo fue descargarnos la última versión de la actividad “Hablar” [1] (versión 42) e interactuar con ella para conocer sus funcionalidades y en particular la funcionalidad bot mediante la cual debíamos integrar la actividad con el robot. Sara utiliza una base de conocimientos para dar una respuesta, la cual se compone de uno o más archivos con el formato AIML [2]. En estos archivos se especifican los diferentes patrones y respuestas a los mismos y finalmente se debe generar la base (archivo con extensión brn) que va a ser usada por la actividad para dar la respuesta adecuada. A continuación mostramos un ejemplo de este tipo de archivos:
<?xml version="1.0" encoding="ISO-8859-1"?> <aiml version="1.0"> <category> <pattern>SARA</pattern> <template> <random> < li>Si?</ li> < li>Dime.</ li> < li>Qué deseas?</ li> </random> </template> </category> <category> <pattern>SARA *</pattern> <template> <sr/> </template> </category> <category> <pattern>* SARA</pattern> <template> <sr/> </template> </category> <category> <pattern>_ SARA *</pattern> <template> <sr/> </template> </category> </aiml>
En el ejemplo anterior se pueden observar distintos patrones que corresponden con la palabra “sara” y en función de ello se da una respuesta aleatoria. Estos archivos se encuentran en Speak.activity/bot/sara y para generar la base de conocimientos (Speak.activity/bot/sara.brn) hay que ejecutar el script Speak.activity/bot/gen_brains.py donde previamente se debe haber instalado la librería PyAIML ya que de lo contrario da un error. Para este proyecto nosotros creamos nuestro propio archivo Speak.activity/bot/sara/butia.aiml y luego para poder usarlo generamos nuevamente la base ejecutando el script mencionado anteriormente. Más adelante detallaremos nuestro archivo butia.aiml. En paralelo a que íbamos trabajando con los archivos .aiml necesitábamos identificar dónde y cómo se procesaba el texto ingresado para luego ser macheado con los patrones existentes en la base y de esa manera obtener una respuesta. El procesamiento del texto se realiza en la función respond perteneciente a la clase Speak.activity/aiml/Kernel.py. La función respond parsea el texto ingresado por el usuario en “sentencias” las son separadas por ‘.’, ‘!’ y ‘?’. Por ejemplo, en el texto “Hola Sara, cómo estás? Necesito tu ayuda.” existen dos sentencias: “Hola Sara, cómo estás?” y “Necesito tu ayuda.” Luego para cada una de las distintas sentencias encontradas realiza un macheo con la base de conocimientos y va concatenando las distintas respuestas según el mapeo realizado, para finalmente devolver al usuario en forma oral la respuesta final. El siguiente paso fue comenzar a implementar la integración, y para esto la idea era hacer un diseño los mas desacoplado posible. Las primeras pruebas donde agregábamos nuestro código tuvieron resultados negativos, la actividad no tomaba nuestros cambios y no sabíamos el motivo, este problema nos llevo a comunicarnos vía email con los desarrolladores de la actividad para pedirles ayuda y además para contarles la integración que íbamos a realizar en su actividad y dejar la puerta abierta para que en el futuro desarrollen una especie de plug-in para realizar la integración (análogo a como sucede con la paleta Butiá en la actividad TurtleArt). Tuvimos una comunicación positiva en donde nos indicaron que el problema estaba en la librería PyAIML que habíamos instalado para generar la base de conocimientos. La actividad tiene incorporada ésta librería (Speak.activity/aiml) pero en caso de que además se instale en el sistema, la actividad por defecto toma las clases de la librería instalada y no de la incluida en la actividad. Una vez resuelto este problema logramos avanzar hacia la solución final, la cual consistió en realizar una función butiaParser en el módulo Speak.activity/aiml/butiaParser.py la cual es invocada desde la función respond en el Speak.activity/aiml/Kernel.py. La función butiaParser se encarga de analizar si la sentencia que se le pasa por parámetro contiene un “comando” para el robot Butiá. Nosotros decidimos que para poder enviarle comandos al robot, el mismo debía ser definido en una sentencia y conteniendo la palabra “BUTIA” para identificar que se quiere enviar un comando al robot Butiá. A continuación se listan los distintos comandos que se le pueden enviar al robot Butiá a través de la actividad:
- BUTIA ESPERA VALOR
- BUTIA CARGA BATERIA
- BUTIA VELOCIDAD VALOR
- BUTIA ADELANTE
- BUTIA ADELANTE VALOR
- BUTIA ATRAS
- BUTIA ATRAS VALOR
- BUTIA IZQUIERDA
- BUTIA DERECHA
- BUTIA GIRAR VALOR
- BUTIA DETENER
- BUTIA PANTALLA
- BUTIA LED
- BUTIA BOTON
- BUTIA ESCALA GRIS
- BUTIA LUZ
- BUTIA TEMPERATURA
- BUTIA DISTANCIA
- BUTIA INCLINACION
- BUTIA CAMPO
- BUTIA VIBRACION
En la siguiente imagen se muestra la actividad Sara en ejecución, para poder interactuar con el robot se debe estar en el modo "inteligente" marcado con rojo.
Para enviarle un comando de los listados anteriormente al robot es necesario ingresar una sentencia que contenga las palabras indicadas en el comando. Por ejemplo, para mover el robot hacia adelante una sentencia valida seria “Hola Sara, quiero mover el robot butiá hacia adelante.”, si quisiéramos saber la carga de la batería se debería crear una sentencia como “Cuál es la carga de la batería del robot butiá?”, si quisiéramos cambiar la velocidad de los motores del robot deberíamos crear una sentencia como “quiero modificar la velocidad del robot butiá a 800”, y si quisiéramos saber si el botón se está presionando ingresamos "Butia boton" como en el ejemplo de la imagen anterior. No es necesario que las palabras que conforman el comando se encuentren en el orden en el cual se listaron, pero si es necesario que dichas palabras estén contenidas en la sentencia. De la misma manera los comandos que requieren un parámetro numérico es necesario que se indique un valor numérico y solo uno, en caso de ingresar más de un valor numérico en la sentencia se toma el primero ingresado.
Nota importante: Si se está enviando un comando asociado con un sensor, y hay más de un mismo sensor conectado al robot, se debe ingresar el número de puerto del que se quiere obtener el resultado. Por ejemplo, en el caso de que haya dos botones conectados al robot, uno en el puerto 3 y otro en el puerto 4, Sara devuelve un mensaje diciendo que se debe indicar el número de puerto que se quiere consultar. En caso de que el sensor no este conectado al robot, el mensaje indica que no se puede consultar al sensor porque no está conectado.
Detalles Técnicos
Al iniciar la actividad debemos levantar el bobot-server para poder comunicarnos con el robot butiá a través de la butiá API. Para esto, agregamos dentro de la actividad un paquete que contiene los archivos necesarios para que el bobot-server se inicie correctamente y desde la función init() de Speak.activity/activity.py se inicia el bobot-server invocando a la función bobot_launch(), esta función se agregó al archivo Speak.activity/activity.py. Al cerrar la actividad, se invoca a la función save_instance() de Speak.activity/activity.py que guarda la instancia de la actividad "Sara", para detener el bobot-server, modificamos esta función invocando a la función kill_bobot() definida en Speak.activity/activity.py que se encarga de detener el proceso.
La lógica del modo inteligente de la actividad, se realiza en el archivo Speak.activity/aiml/Kernel.py. Para poder invocar al bobot nosotros modificamos la funcion respond() de dicho archivo, agregándole las llamadas correspondientes a las clases implementadas por nosotros. La idea es la siguiente, al invocar a esta función, con el texto ingresado por el usuario, llamamos a la funcion butiaParser() de Speak.activity/aiml/butiaParser.py que procesa este texto, y si contiene la palabra "BUTIA", se procesa el comando ingresado, se verifica que sea un comando válido, y se invoca a la función correspondiente en la clase Speak.activity/aiml/butiaSara.py, esta clase fue implementada siguiendo el patrón de diseño Singleton para mantener el estado del robot y es la encargada de interactuar con la clase Speak.activity/aiml/butiaAPI.py y se devuelve el resultado correspondiente, en caso de que la palabra "BUTIA" no se encuentre en el texto ingresado no se invoca al bobot-server y se realiza el procesamiento normal que se hacía antes.
En la siguiente imagen se muestra el diagrama de flujo al ingresar un texto:
¿Cómo agregar una nueva funcionalidad?
Si se quisiera agregar una nueva funcionalidad, como un sensor que hoy en día no esté soportado, pero que en un futuro sea parte del robot Butiá se deberían seguir los siguientes pasos:
- Paso 1
Hay que definir cuales serian las palabras claves que determinarían el comando para el nuevo sensor, recordando que la sentencia debe contener la palabra Butiá. Por ejemplo: BUTIA SENSOR X, serian las palabras claves.
- Paso 2
En el módulo Speak.activity/aiml/butiaParser.py se encuentran las funciones command_count y butiaParser, command_count determina la cantidad de comandos existentes en la sentencia (debe haber un solo comando por sentencia) por tanto en dicha función se debería agregar el siguiente código en función de las palabras claves que determinan el comando. A continuación se muestra el código que debería agregarse a dicha función:
if ("sensor" in sentence and "x" in sentence): count += 1
En la función butiaParser, hay que agregar una nueva invocación para este nuevo sensor que llame a una función (también a implementar) que se encuentra en la clase Speak.activity/aiml/ButiaSara.py.
elif ("sensor" in sentence and "X" in sentence): resultado = estaPresente(sentence,"X") esta = resultado[0] puerto = resultado[1] if esta == "true": return ["BUTIA SENSOR X",str(butiaSara.sensorX(puerto))] elif esta == "false": return ["SENSOR NO ENCONTRADO","-1"] else: return ["HAY MAS DE UN SENSOR","-1"]
- Paso 3
El tercer paso consiste de implementar en la clase Speak.activity/aiml/ButiaSara.py la función que es invocada desde el método butiaParser (mencionada en el paso anterior). Esta sería la función de nombre sensorX que se muestra a continuación y que se encarga de realizar la invocación correspondiente a una función de la butiaAPI que debe haber sido implementada para interactuar con el nuevo sensor.
def sensorX(self, puerto): return self.bobot.getSensorX(puerto)
Donde getSensorX(puerto) es la función correspondiente al nuevo sensor, y es implementada en la Speak.activity/aiml/butiaAPI.py.
Cuarto: El paso cuatro consiste de agregar los patrones y respuestas a esos patrones en la base de conocimiento, en nuestro caso en el archivo Speak.activity/bot/sara/butia.aiml que hemos creado para este proyecto, aunque también se podría crear un nuevo archivo aiml. En el archivo se debe agregar un nuevo tag category como se muestra a continuación.
<category> <pattern>BUTIA SENSOR X</pattern> <template> <random> < li>El valor sensado es< /li> < li>El valor del sensor es< /li> </random> </template> </category>
Se puede agregar tantos mensajes como se quiera dentro del tag random. Luego de hecho lo anterior debemos generar nuevamente la base de conocimientos ejecutando el script Speak.activity/bot/gen_brains.py como mencionamos anteriormente.
Referencias
- [1] http://activities.sugarlabs.org/es-ES/sugar/ última visita 28/08/2012.
- [2] http://www.alicebot.org/aiml.html última visita 28/08/2012 última visita 28/08/2012.
- [3] http://www.fing.edu.uy/inco/proyectos/butia/mediawiki/index.php/Git_Butia última visita 28/08/2012.