miércoles, 24 de marzo de 2010

Práctica 4 - Modelo de movimiento probabilístico mediante filtro de partículas

Con esta nueva práctica comenzamos a indagar en el campo de la auto-localización, comenzando por el método de filtro de partículas (explicado de forma bastante amplia en el enunciado de la práctica).

En este caso no se trataba de implementar dicho método (lejos ya contiene una serie de clases que lo hacen*) , sino de entenderlo para poder usarlo correctamente.


*CLASES DE LEJOS: LineMap, MCLParticleSet, Line, Point, Movement.




MODELO DE MOVIMIENTO MEDIANTE LA CLASE MCLParticleSet

Una vez entendido cómo funciona de forma teórica el filtro de partículas habia que llevarlo a la práctica. Para ello creamos un pequeño mapa (new LineMap...) en el que creamos 100 partículas (new MCLParticleSet (map,numPartículas)), a las que aplicamos un cierto error en distancia y ángulo con setDistanceNoiseFactor y setAngleNoiseFactor, error que habíamos calculado previamente realizando diversas mediciones y hallando la desviación típica de dicho error.


Una vez hecho esto las situamos en el punto de partida con setLocation, y nos creamos una pequeña función con la el robot realizaba una trayectoria en forma de cuadrado. Mientras el robot avanza las partículas van actualizando su posición, se van moviendo con applyMove, mostrándose los resultados por pantalla.

Si juegas con el error en la distancia y el ángulo puedes comprobar como varían los resultados. Mientras que con errores pequeños la trayectoria en cuadrado es claramente visible, si introduces un error un poco más grande, las partículas se dispersan por toda la pantalla obteniendo como resultado un manchurrón negro que poco tiene que ver con el movimiento del robot.







CÁLCULO DE TRAYECTORIAS

En este caso se trata de conseguir que el robot llegue a unas coordenadas introducidas por pantalla y acabe con una cierta orientación.

Para la primera implementacion (pathfollow1) el robot tiene que orientarse hacia el punto, avanzar en linea recta hacia él, y por útimo corregir su orientación si fuese necesario.

Para este problema hemos usado la clase vector, que ya habíamos implementado para prácticas anteriores. Un vector de este tipo se construye con dos coordenadas, y tiene funciones muy útiles como getAngulo o getMódulo. Partiendo de la base de que nuestro robot está siempre en el eje de coordenadas (0,0), es fácil obtener la trayectoria gracias al uso de dichas funciones, y llevarla a cabo por medio de travel y rotate.







Para la segunda versión de pathfollow hay realizar trayectorias en forma de arco, y finalmente corregir la orientación (de nuevo solo si es necesario).

Este apartado es bastante más complejo que el anterior, y nos llevo mucho más tiempo obtener la solución. Finalmente creemos que, en el fondo, es como aquellos problemas infernales del bachillerato de encuentra la circunferencia que pase por dos puntos y sea tangente a la recta dada (en este caso el eje de abscisas), qué se le va a hacer. Así obtenemos el centro del arco y el ángulo, que es lo que nos queda, se calcula hallando el ángulo que forman el destino con el centro del arco a partir del eje de ordenadas.

Con todo esto sacamos una fórmula, muy bien pero ¿funciona de verdad o es otro fracaso más? Cigarro no es un buen sitio para probarla porque la mitad de las veces hace lo que quiere así que tuvimos que hacer esto de abajo para probarla cómodamente.



El código de la animación (está en as3):



Funciona bien y todo. Abajo, cómo se comporta en el mundo real™:







Por último hemos intentado dar repuesta a la pregunta: ¿Sería posible realizar una trayectoria compuesta por dos arcos, de maneraque se alcanzase el punto de destino y la orientación correctos sin necesidad de rotar adicionalmente una vez llegados al punto de destino?

En una primera aproximación se nos ha ocurrido que sería buena idea hacer un waitpoint a mitad de camino. Con el primer arco te desplazarías hacia dicho punto, por lo que tu orientación ya comenzaría corregirse. Y con el segundo arco llegarías al punto de destino. Gracias a este segundo arco podrías realizar trayectorias en zig-zag o en forma de S, logrando acabar con la orientación correcta.

Con una segunda pensada tenemos lo siguiente:


Podría calcularse una trayectoria más eficiente usando además una recta aunque esto se saldría de lo que dice el guión de la práctica.

miércoles, 10 de marzo de 2010

Práctica 3 - Navegación local evitando obstáculos

En estas dos semanas nos hemos dedicado a desarrollar ciertos programas de navegación, ayudándonos de los sensores de contacto, ultrasonido y luz.

En lineas generales esta práctica ha consistido en dejar al robot navegando en busca de un cierto punto de destino evitando los obtáculos que encontrase a su paso.

Para trabajar comodamente con los sensores hemos tenido que cambiar el aspecto de nuestro robot (Cigarro, la encuesta salió positiva!!). Como puede verse en la imagen, hemos situado el sensor de ultrasonido en la parte superior montado sobre un motor con el fin de poder realizar los barridos. A los dos lados del robot, en la parte delantera están los dos sensores de luz, formando un ángulo de unos 45 y -45º, y finalmente, el sensor de contacto con su parachoques.


También cabe mencionar, que para esta práctica ha sido necesario implementar comportamientos (para ello es necesario usar la clase Behavior).




COMPORTAMIENTO DE EVITACIÓN DE OBSTÁCULOS USANDO SENSORES DE CONTACTO

Como describe el nombre del apartado, se trataba de esquivar los obstáculos usando sensores de contacto. En este caso los obstáculos eran latas con un cierto peso. No hay que olvidar que el objetivo final es llegar a la meta. Por tanto, es fácil distinguir los comportamiento que el robot tiene que llevar a cabo: Avanzar hacia delante y esquivar los obstáculos.

El robot tiene activado el comportamiento de ir para alante. Cuando el sensor de contacto advierte la presencia de un obstáculo salta el comportamiento esquivar, y una vez que el objeto ha sido sobrepasado se vuelve a activar el avanzar.

Avanzar:
action: navegador.forward().
supress: navegador.stop().
takeControl: siempre true (es el comportamiento principal).

Esquivar:
action: realizar el giro (travel, rotate,...)
supress: vacio.
takeControl: verdad si touch.isPressed().







COMPORTAMIENTO DE EVITACIÓN DE OBSTÁCULOS USANDO EL SENSOR DE ULTRASONIDOS.

Este aparto es sin duda el mayor reto al que nos hemos enfrentado hasta el momento, y al que le hemos dedicado mayor tiempo durante estas semanas. A pesar de ello los resultados hasta el momento no son tan buenos como a nosotros nos hubiera gustado.

La temática de este ejercicio es muy similar a la del anterior, solo que esta vez no será necesario chocar con el objeto para saber que está ahí, sino que usaremos métodos más sofisticados.

El robot deberá avanzar hacia delante realizando barridos con el sensor de ultrasonido. Si en ese barrido se detecta algún obstáculo hay que "guardar" su posición para no impactar con él. Es decir, de nuevo volvemos a tener dos comportamientos claramente diferenciados: Avanzar hacia delante (esta vez sensado con el ultrasonido) y esquivar.

Para llevar a cabo el comportamiento esquivar es necesario usar el método de navegación local conocido como VFF (Virtual Forces Field). Para este método hay que calcular la fuerza atractiva de tu punto de destino así como la fuerza repulsiva del obstáculo. Una vez que tienes los dos vectores puedes sumarlos, obteniendo así el vector resultante que determine el próximo movimiento del robot (evitando que impacte contra el obstáculo). Estos vectores deben actualizarse contínuamente, hasta que el objeto haya sido superado.

¿Problemas? Muchos. Para empezar la incertidumbre en el eje y del sensor nos juega malas pasadas por lo que tuvimos que tomar medidas como realizar un segundo barrido, a parte del que va haciendo el robot cuando viaja hacia delante, que en cada ángulo halle tres distancias y devuelva la menor de ellas. También da muchísimos problemas la actualización de las coordenadas a medida que nos vamos moviendo. Esto lo solucionamos separando las rotaciones de los desplazamientos en línea recta. En los desplazamientos hacia delante utilizamos prácticamente las mismas fórmulas que en la práctica 1 y en las rotaciones simplemente aumentamos el ángulo del vector en sus coordenadas polares.

Aquí podemos ver un esquemático del funcionamiento:



Y un vídeo en el que se ve la movida:






COMPORTAMIENTO IR HACIA LA LUZ


En este caso el robot tiene que avanzar hacia la luz, representada por un foco. Ahora nuestros comportamientos son avanzar y buscarluz. Pensando un poco en la solución reconocimos 4 posibles situaciones a las que deberíamos enfrentarnos:

1: El robot no encuentra la luz: en ese caso debe girar hasta que la detecte, y comenzar a avanzar hacia ella.

2: El sensor derecho detecta más luz que el izquierdo: La lámpara está a la derecha, hay que girar hacia ese mismo lado.

3:El sensor izquierdo detecta más luz que el derecho: Igual que en el caso anterior, pero girando en el sentido contrario.

4:Ambos sensores detectan la misma luz: El foco está en frente, hay que andar recto hacia el.



Avanzar:
action: navegador.forward().
supress: navegador.stop().
takeControl: siempre true (es el comportamiento principal).

BuscarLuz:
action: resolución de los casos anteriores.
supress: vacio.
takeControl: devolverá verdadero o falso según la luz captada por los sensores.

Tanto para este como para el siguiente apartado merece la pena dar una vuelta por la API de lejos, ya que estamos usando sensores de luz del modelo anterior, el RCX, y hay ciertos métodos de la clase RCXLightSensor que están obsoletos y conviene usar lo menos posible.


Además es necesario calibrar el robot cada vez que vaya a usarse, ya que la luz del ambiente puede variar, por lo que si no nos adaptamos al entorno, nuestro programa dejará de funcionar.






COMPORTAMIENTO IR HACIA LA LUZ EVITANDO OBSTÁCULOS


Una vez implementados los ejercicios anteriores, obtener realizar este apartado resulta relativamente sencillo, ya que solo es necesario unir el comportamiento de buscar luz, al de avanzar hacia delante y esquivar obstáculo. Para este apartado podíamos elegir entre usar el sensor de contacto o el de ultrasonido. Nosotros nos hemos decantado por el de contacto, ya que su implementación es más simple y controlamos mejor su funcionamiento.

Avanzar:
action: navegador.forward().
supress: navegador.stop().
takeControl: siempre true (es el comportamiento principal).

BuscarLuz:
action: resolución de los casos anteriores.
supress: vacio.
takeControl: devolverá verdadero o falso según la luz captada por los sensores.


Esquivar:
action: realizar el giro (travel, rotate,...)
supress: vacio.
takeControl: verdad si touch.isPressed().