Blog dedicado a CREAR conocimiento sobre robótica, electrónica, proporcionando las herramientas necesarias para que puedas "hacer tú mismo" tu propio robot :)
En esta versión he mejorado la estabilidad en velocidad comparando algunas opciones para determinar la velocidad a la que puede navegar el robot pioneer. Además, he usado un comparador con la velocidad anterior para suavizar los cambios bruscos.
Con esto he evitado el efecto "tembleque" que se produce cuando se modifica la velocidad del robot considerablemente en cada iteración.
Falta por mejorar el algoritmo VFF a uno que tenga en cuenta mejor la distancia a los objetos usando una ecuación trigonométrica y aunque la mejora en la decisión de giro hace que éste tome mejor las curvas sigue siendo lento debido al poco ángulo de visión y a que a poca distancia del objeto éste es plano en su parte inferior por el recorte de la cámara. Probaré a bajar la cámara un poco para evitar ese efecto a corta distancia.
Un problema que me encontraré si hago eso es que la regla de tres dejará de funcionar para estimar el ángulo al que se encuentra cada columna de la imagen y deberé hacer el cálculo por cada pixel. Para mejorar el rendimiento sólo calcularé el ángulo del último pixel de suelo en cada columna, pero aún así las cuentas se complican.
Primer acercamiento a la navegación visual sin asistencia de líneas pintadas en el suelo.
Para llegar a una solución he pensado en implementar un algoritmo VFF basado en la distancia en la que se encuentran los objetos que aparecen en la imagen de la cámara de vídeo.
He estimado que la apertura de visión de la cámara es algo inferior a 70º, de modo que se puede estimar el ángulo al que se encuentra cada columna de la imagen con una simple regla de tres.
Queda por mejorar los "saltitos" o efecto "tembleque" que tiene el robot cuando ajusta su velocidad basada en el módulo del vector de repulsión y mejorar la decisión de giro, la encuentro demasiado lenta debido al poco ángulo de visión del que disponemos.
Por fin he puesto en marcha el introrob para gazebo. Tras tener problemas compilando el newintrorob2.tar.gz por fin descubro que simplemente hay que usar el mismo introrob que viene con jderobot 4.3.
En esta primera implementación creamos tres umbrales lineales que determinarán cómo se comportará el robot ante la tarea de seguir la línea roja.
En el siguiente vídeo muestro cómo se comporta el robot en el pequeño circuito incluido en las prácticas. Podría aumentar la velocidad un poco más, pero el robot pionner se vuelve muy inestable en los giros cerrados ya que tiende a volcar cuando gira, o a desviarse cuando frena o acelera bruscamente.
En la implementación inicial he incluido un algoritmo de compensación de inercia. Con él consigo dejar de girar antes de llegar al punto de destino, de modo que no me lo pase por la inercia de giro del robot. La estimación es lineal y usa únicamente el estado anterior para calcular la posición futura.
Detecté pero no arreglé un pequeño problema derivado de calcular si ha llegado el robot o no al ángulo de destino. Ahora toca arreglar ese defecto para que la práctica sea completa.
Para comprender el problema debemos saber dos cosas del robot:
Los sensores de orientación del robot no son perfectos, por consiguiente la brújula tiene un margen de error y una granularidad que impide la búsqueda de un ángulo de destino perfecto. Por consiguiente no se busca un ángulo de destino, si no un ángulo de destino con un margen de error de varios grados.
En la imagen puede verse una representación del ángulo de destino:
En ella puede verse el ángulo de destino (356º) y el margen de error anterior (356º - 10º = 346º) y posterior (356º + 10º = 366º = 6º).
¿Qué problema existe en la búsqueda de ese sentido de orientación?
Para averiguar si ha llegado o no el robot a su orientación de destino restamos los ángulos destino y actual y obtenemos el valor absoluto.
φ = | φr - φf |
Donde φr es la orientación actual del robot y φd la orientación final a la que debe llegar.
Veamos varios ejemplos de cómo se comporta este mecanismo de cálculo con varios ejemplos:
Estamos en 340º: |340 - 356| = 16 (> 10 fuera del margen - CORRECTO).
Estamos en 349º: |349 - 356| = 7 (< 10 dentro del marge - CORRECTO).
Estamos en 358º: |340 - 356| = 2 (< 10 dentro del margen - CORRECTO).
Estamos en 4º: |4 - 356| = 352 (> 10 fuera del margen - INCORRECTO).
Estamos en 8º: |8 - 356| = 348 (> 10 fuera del margen - CORRECTO).
Podemos resumir el comportamiento de este cálculo en la siguiente ilustración:
En ella podemos ver que cuando el ángulo es cercano al destino pero éste no "cruza" el eje de abscisas todo funciona correctamente (zona verde) pero si estamos dentro de un ángulo al otro lado del eje de abscisas entonces nos encontramos con que los cálculos fallan.
¿Cómo arreglarlo?
Existen varias soluciones. La que proponen en clase es comprobar cuándo el robot puede verse involucrado en este tipo de situaciones y modificar el cálculo de la diferencia en ese caso.
¿Cómo?
Veamos la siguiente ilustración:
Podemos ver dos zonas la anaranjada y la azulada. Corresponden al arco comprendido entre 10º y 0º (zona anaranjada) y de 0º a -10º (zona azulada).
Podríamos hacer sectores y comprobar cuándo pasan por el eje de abscisas y arreglarlo. Una serie de sentencias condicionales nos ayudarían en la tarea.
Pero hay una forma más sencilla, comprobando únicamente cuándo el cálculo podría darnos problemas.
¿Cómo podríamos averiguar eso?
Podríamos hacer las siguientes comprobaciones:
Si el robot está orientado en un ángulo inferior a 10º (zona anaranjada) y el destino es superior a 350º (zona azulada) le quitamos 360 grados al ángulo de destino.
Si el robot está orientado en un ángulo superior a 350º (zona azulada) y el destino es inferior a 10º (zona anaranjada) le quitamos 360 grados al ángulo del robot.
Con eso los cálculos saldrían correctos. Podemos comprobarlo con los ejemplos anteriores.
Pensemos que la condición "el destino es superior a 350º" siempre se está cumpliendo de modo que sólo hay que restarle 360º al destino cuando el robot tiene su ángulo de giro por debajo de los 10º de margen:
Estamos en 340º: |340 - 356| = 16 (> 10 fuera del margen - CORRECTO).
Estamos en 349º: |349 - 356| = 7 (< 10 dentro del marge - CORRECTO).
Estamos en 358º: |340 - 356| = 2 (< 10 dentro del margen - CORRECTO).
Estamos en 4º: |4 - (356 - 360)| = |4 + 4)| = 8 (< 10 dentro del margen - CORRECTO).
Estamos en 8º: |8 - (356 - 360)| = |8 + 4| = 12 (> 10 fuera del margen - CORRECTO).
¿Y no existe una solución más simple?
Casi siempre hay una solución mejor a la que inicialmente pensamos de forma natural.
Basta con restarle a 360º el ángulo calculado si éste supera los 180º.
Lo comprobamos con el ejemplo anterior:
Estamos en 340º: |340 - 356| = 16 (> 10 fuera del margen - CORRECTO).
Estamos en 349º: |349 - 356| = 7 (< 10 dentro del marge - CORRECTO).
Estamos en 358º: |340 - 356| = 2 (< 10 dentro del margen - CORRECTO).
Estamos en 4º: |4 - 356| = 352 -> 360 - 352 = 8 (< 10 dentro del margen - CORRECTO).
Estamos en 8º: |8 - 356| = 348(*) -> 360 - 348 = 12 (> 10 fuera del margen - CORRECTO).
(*)Para minimizar el hitrate de dicha comprobación y ahorrarnos restas innecesarias comprobamos que únicamente esté por encima del margen superior, 350º, que será cuando nos interfiera en el cálculo en vez de hacerlo cada vez que supere los 180º.
En estos ejemplos hemos usado un error de 10º y hemos usado grados para facilitar los cálculos pero en la práctica el margen de error con el ángulo de destino es de tan sólo 5º y todos los cálculos se hacen en radianes.
En el siguiente vídeo muestro el funcionamiento del cálculo. Estamos calculando el ángulo de diferencia de manera continua, pero cuando la diferencia es mayor de 330º (360º - 30º) habilitamos el cálculo de corrección.
Hay que notar que en la práctica existen dos umbrales de decisión: <30º para girar más despacio y <5º para decidir que se ha llegado al objetivo. De modo que hay que tener en cuenta el umbral más bajo (360º - 30º) o no aplicar la optimización. De no hacerlo el robot podría pasar girando sobre los 5º de margen y pasarse en el giro, teniendo que completar una nueva vuelta y rezar que no vuelva a ocurrir lo mismo.
Hoy sólo he hecho una jornada de entrenamiento en el circuito de Cheste para comprobar si mi algoritmo de navegación local VFF es suficiente para recorrer el circuito..
Para recorrer el circuito aumenté un poco el margen desde el cual los obstáculos ejercen fuerzas de repulsión (arco verde) y aumenté la velocidad máxima cuando el destino está entre -30º y 30º. En la penúltima curva se ve claramente que la lógica de decisión necesita unos retoques para evitar comportamientos erráticos, aunque sean breves.
Dejaremos a Fernando Robonso y su fórmula R descansar un poco antes de pensar cómo mejorar la lógica de navegación.
El algoritmo de vectores de fuerzas de repulsión basa su funcionamiento en fuerzas vectoriales que se oponen al movimiento frontal del robot creados por los obstáculos a su alrededor.
Cuando el robot no tiene ningún obstáculo frente a él la única fuerza que actúa sobre él es la atracción ejercida por el punto de destino (vector dirección).
Sin embargo conforme aparecen obstáculos, éstos ejercen una fuerza con dirección al robot y fuerza inversamente proporcional a su distancia. Eso provoca que los objetos lejanos empiecen a modificar la trayectoria del robot intentando que evite acercarse a ellos, pero conforme el robot se acerca a un obstáculo por estar en medio del camino éste ejerce cada vez más fuerza de repulsión hasta que ésta supera a la fuerza de atracción del destino, provocando que el robot se detenga y busque otro camino.
En la mayoría de los casos, si el objeto se encuentra en la tangente del trayecto, las fuerzas de repulsión modificarán la trayectoria del robot levemente sin hacer que éste se detenga para evitar la colisión.
En el siguiente vídeo se observa una implementación de la navegación local usando fuerzas VFF en el que la lógica de decisión realizará las siguientes acciones dependiendo del ángulo que tenga el vector resultante (la suma del vector de repulsión más el vector de navegación al destino). Hay que tener en cuenta que mientras un obstáculo no se encuentre dentro de la zona delimitada por el arco verde éste no ejercerá fuerzas de repulsión:
Si el vector forma menos 30º respecto a la orientación del robot: El robot avanza a máxima velocidad corrigiendo levemente su trayectoria.
Si el vector forma un ángulo comprendido entre 30º y 60º respecto al avance del robot: El robot modera su velocidad corrigiendo su trayectoria poco a poco.
Si el ángulo que forma con el sentido de avance del robot es superior a 60º: el robot se detiene y gira con más velocidad para corregir el error de dirección.
Para evitar que el robot se comporte de manera errática cuando los vectores de dirección y repulsión se contrarrestan he agregado dos constantes. Una de ellas empuja constantemente al robot hacia delante sumándose al vector resultante anterior. Por otro lado si el módulo del vector resultante no supera una magnitud mínima entonces el robot avanza y gira como si el vector resultante y la trayectoria del robot formasen entre 30 y 60º (avanza con velocidad moderada mientras corrige la dirección).
En el siguiente vídeo puede observarse que la mejora funciona bien sin tener que complicar la lógica de decisión:
En la segunda mejora he achatado los umbrales de seguridad (arco rojo) y de velocidad (umbral verde) pero, como modificación más importante, el algoritmo de decisión del sentido de giro ya no es completamente aleatorio.
Para decidir hacia qué sentido girar calculo la suma de distancias a obstáculos a derecha e izquierda del robot haciendo que éste gire hacia el sentido en el que la distancia media sea mayor. De este modo evitamos movimientos inútiles hacia esquinas de la habitación, permitiendo, a su vez, que decida ir en sentido de un pasillo en vez de ir contra las paredes de forma aleatoria.
El arco que recorremos para dicha decisión es el comprendido entre los grados 30 y 90 a ambos lados del robot. El ángulo de giro aleatorio también estará comprendido entre esos ángulos.
El el vídeo podemos observar que ahora el robot ha recorrido una mayor superficie de nuestro mundo virtual gracias a evitar giros muy cortos que apenas mejoran la posición respecto del obstáculo o aquellos que lo llevan frente a un obstáculo, pared o esquina.