Programacion Orientada a Comportamientos
No os dejéis engañar por el título, aunque me he permitido un pequeño juego de palabras (POO,AOP), este post no es de ningún nuevo paradigma de la programación, todo lo contrario, es una arquitectura de los años 80 empleada para el control de robots que en su momento supuso un avance sobre el control reactivo y con el que yo estoy practicando con mi Lego Mindstorms. Lo primero que hice fue recuperar mis apuntes de "Robótica Perceptual" (¿quién dijo que no se puede aprender jugando?) y bajarme el paper en el que Rodney Brooks expone su teoría. Además el tutorial de leJOS incluye una sección dedicada al API Behavior, que es la implementación de la "subsumption architecture" (como es conocido el modelo de Brooks) que este Sistema Operativo incluye para el control de robots Lego.
¿Qué son comportamientos? un comportamiento se entiende como una tarea o conjunto de tareas simples que se encargan de tratar un aspecto específico de nuestro robot (por ejemplo evitar colisiones). Así, la subsumption architecture pretende dividir nuestro programa en estas tareas o comportamientos. Estos comportamientos competirán por hacerse con los recursos del robot (como los motores) por lo que deberán estar priorizados, de tal manera que un comportamiento con alta prioridad puede "robarle" el recurso compartido a otro comportamiento con menor prioridad. Esta es una de las características del modelo de Brooks, frente a la división vertical del control que se hace en el modelo deliberativo (Percibe -> Planifica -> Actua), Brooks propone una división horizontal, en capas ordenadas según su prioridad, de manera que las capas superiores pueden cancelar la salida de las inferiores. Para hacer mis pruebas he implementado un ejemplo muy sencillo para un robot que sigue la linea negra: solo tiene dos comportamientos, uno que le hace andar hacia adelante, y otro, más prioritario, que cuando detecta (mediante el sensor de luz) que se ha salido de la línea negra se pone a girar para recuperar la posicion. Este sería el esquema:
¿Cómo se hace esto en Java? Aqui viene lo mejor, leJOS permite utilizar esta arquitectura en un robot Lego mediante el API Behavior, que consta de un interface que todas las clases que pretendan ser un Comportamiento deben implementar y de una clase que actuará como árbitro, determinando qué comportamiento tomará el control de los recursos. El mecanismo es muy sencillo y a la vez elegante: tus comportamientos implementan el interfaz Behavior por lo que tienen que definir tres métodos, takeControl() que devuelve un valor booleano cuando el comportamiento tenga que tomar el control, suppress() que se ejecuta cuando el comportamiento pierde el control, y action() en que se incluye el código del comportamiento propiamente dicho, es decir, lo que se va a ejecutar cuando este comportamiento tenga el control. Una vez que estén definidos los comportamientos, solo hay que crearlos, pasarlos a la clase arbitro como un array (teniendo en cuenta que los primeros elementos del array son lo que menos prioridad tienen) y poner en marcha al árbitro:
Behavior b1 = new Adelante();Behavior b2 = new Girar(gestSens);
Behavior[] bArray = {b1,b2};
Arbitrator arbitro = new Arbitrator(bArray);
arbitro.start();
¿Para qué sirve esto? En lugar de programar el robot mediante los típicos if ... then que acaban degenerando en un código spagheti imposible de mantener, los comportamientos encapsulan un determinado aspecto del robot, con lo que hacen el código mas claro (y mantenible), además facilitan mucho la evolución del robot, ya que es muy sencillo añadir o quitar comportamientos. Por ejemplo si queremos dotar a nuestro robot con un sensor de tacto para que además de seguir la línea negra esquive obstáculos, solo tendríamos que programar el comportamiento y añadirlo al array (bArray) según la prioridad que le queramos dar.
Si antes comentaba la elegancia de Java con el uso de los interfaces, no lo es menos la posibilidad de gestionar los eventos mediante "event listener", lo que me permite por ejemplo, en lugar de preocuparme de tener que leer periódicamente el valor de los sensores mediante el API Sensor, que un listener esté encargado de comprobar el estado del sensor y avisar cuando éste haya cambiado. En mi caso he definido una clase que implementa el interfaz SensorListener, y que mediante el método stateChanged mantiene actualizado en todo momento el valor de lo que está leyendo el sensor de luz en una variable accesible mediante un método get. Así, el método takeControl() del comportamiento Girar sólo tiene que comprobar si ese valor no corresponde al color negro (de la pista) para tomar el control. Tan sencillo y tan eficaz !!
(2005-01-17 21:23:44.0) Permalink Comentarios [1]

