Tras la muerte de bracitos, para continuar con las prácticas utilizamos dos herramientas nuevas: Gazebo y JDE.
El primero es un simulador en 3D que permite cargar diferentes tipos de robots, sensores y actuadores en un mundo artificial, mientras que el segundo es una herramienta que nos permite desarrollar comportamientos para nuestro robot.
La práctica consiste en intentar que el robot consiga avanzar por el mundo siguiendo la linea roja que hay pintada sobre el suelo.
Ante los problemas encontrados en el aula de prácticas (no funcionaba el entorno, la corrección del entorno fue demasiado tarde para nosotros), nos ha sido imposible terminar la práctica. Aunque no la hemos finalizado, si que hemos intentado hacerla asique vamos a comentar el trabajo que hemos desarrollado.
Para conseguir el objetivo, debemos desarrollar un controlador PD. El algoritmo a ejecutar sería el siguiente:
Obtener una imagen con el sensor de imágenes.
Analizamos la imagen y con ello obtenemos una matriz. Esta matriz va a estar formada por píxeles (Red, Green, Blue). Para el funcionamiento óptimo, debemos estudiar 2 líneas de la matriz. Una de ellas se corresponde con la posición actual y la otra se corresponde con una posición mas avanzada de la actual.
Analizamos las dos líneas. Si en la región central de la línea de posición encontramos un rojo y en la línea de la posición avanzada también, podemos suponer que avanzamos en línea recta y el robot puede acelerar. Si por el contrario, en la línea de la posición avanzada hay un blanco, debemos frenar y buscar el rojo para que el robot pueda girar y no perder la línea roja.
Hasta aquí ha llegado nuestra aventura en el mundo de la robótica.
Así han sido las cosas, y así se las hemos contado.
hoy toca realizar la práctica 4. En esta práctica debemos conseguir que bracitos se mantenga de pie sobre dos ruedas. Para ello hemos tenido que cambiar el diseño de bracitos:
Para conseguir que bracitos mantuviese el equilibrio, hemos implementando un controlador PID, el cual consiste en un mecanismo de control por realimentación que calcula la desviación o error entre un valor medido y el valor que se quiere obtener, para aplicar una acción correctora que ajuste el proceso.
El algoritmo de cálculo del control PID se da en tres parámetros distintos: el proporcional, el integral, y el derivativo. Controlador P
El controlador P o proporcional determina la reacción del error actual. Su fórmula es:
La salida obtenida de este controlador, nos ofrece información del error actual. Con este controlador no conseguimos controlar el balanceo de bracitos. Con una Kp alta, bracitos oscila demasiado y cuando el error es muy grande no recupera la posición de equilibrio y siempre se cae.
Controlador I
El controlador I o integral tiene como propósito disminuir y eliminar el error en estado estacionario, provocado por el modo proporcional. El control integral actúa cuando hay una desviación entre la variable y el punto de consigna, integrando esta desviación en el tiempo y sumándola a la acción proporcional. Su fórmula es:
En conclusión, con este controlador, podemos comprobar si el error va aumentando o disminuyendo, por lo que la salida será mayor o menor en relación a la integral del error. Se reduce el oscilamiento del robot, por lo que Ki, debe tener un valor pequeño.
Controlador D El controlador D o derivativo determina la reacción del tiempo en el que el error se produce. Su fórmula es:
La función del componente derivativo es mantener el error al mínimo corrigiéndolo proporcionalmente con la misma velocidad que se produce; de esta manera evita que el error se incremente. Por lo que Kd, tiene que tener un valor mayor en relación al valor de Kp.
El calculo global para obtener una salida del controlador PID es:
Calculo del valor de las constantes
Las constantes Kp, Ki y Kd son constantes experimentales, es decir, para conocer sus valores, no queda otra que hacer miles de pruebas.
Sin duda, esta ha sido la parte mas costosa, por las numerosas pruebas y tiempo que hemos empleado para dar con los valores correctos para que bracitos mantuviera el equilibrio, cosa que finalmente conseguimos con los siguientes valores:
*Kp = 15 *Ki = 1 *Kd = 30
A parte de estas tres constantes, para el correcto funcionamiento del controlador, debemos acotar el error a un valor máximo, al que nosotros hemos situado en 200.
Una vez claro cómo conseguir el controlador, detallamos los pasos seguidos para conseguir el objetivo:
Obtener el valor de equilibrio. Para ello obtenemos la media de 10000 mediciones en posición vertical. Al obtener este valor, es muy importante tener en cuenta, que el robot tiene tendencia a caer hacia adelante, por lo que hay que considerar cierto error. Para compensarlo, al valor obtenido le restamos la constante ERRORPESO con valor 10.
Entramos en un bucle infinito, en el que constantemente vamos a calcular el valor del controlador PID.
Analizamos el resultado obtenido, si la salida sobrepasa el error máximo permitido, el error toma el valor de error máximo.
Si el valor final es negativo, significa que bracitos se cae para alante, por lo que deberá girar hacia atrás a la velocidad que marque la salida del controlador. Si por el contrario es mayor que 0, bracitos se cae de espaldas, por lo que deberá avanzar. El valor del error a la hora de configurar la velocidad en cada caso es en valor absoluto.
Después de tanto rollo y teoría, podemos ver como hemos cumplido el objetivo y bracitos mantiene el equilibrio durante un buen rato:
Sin mas dilatación, así son las cosas y así se las hemos contado.
Hoy es turno de la realización de la practica 3. En este caso vamos a usar programación de comportamientos (behavior). Se trata de definir un mecanismo para ver que comportamiento ha de ser ejecutado en cada momento. Destacar que no es obligatorio su uso, pero nosotros para una mayor comodidad y simplicidad nos decantamos por usarlos. Para organizar los distintos comportamientos usamos una instancia de la clase Arbitrator. Dicha clase se trata de un array de comportamientos cuyo orden es importante ya que el mas prioritario es el que tiene el indice mas elevado del array, es decir, el de la derecha.
Cada clase behavior implementa tres método:
takeControl, devuelve un boolean. True si se cumple la condición para el comportamiento entre en acción.
suppress, es lo último que ejecutará el comportamiento.
action, se desarrollará la actividad del comportamiento.
Una vez entendido el manejo de la clase behavior pasamos a montar los distintos montajes que tenia bracitos para solucionar los apartados propuestos en el guión de la práctica. Mostramos unas imagenes de los diferentes montajes.
Bracitos + 2 sensores de contactoBracitos + sensor de ultrasonidos
Bracitos + 2 sensores de luzBracitos + luz + contacto
Bracitos + 2 sensores de luz + sensor ultrasonidos
- Comportamiento de evitación de obstáculos usando sensores de contacto
Equipamos a nuestro robot de dos sensores de contacto. Cuando uno de estos sensores sea presionado, se activará el comportamiento Esquivar, por lo que el robot deberá esquivar el obstáculo (una lata de cocacola) rodeándolo. La forma de rodearlo sera retrocediendo unos centímetros y realizar un arco adecuado, terminando exactamente en la posición inicial. Para efectuar el arco usamos "steer". Tras distintas pruebas con determinados números de grados diferentes logramos conseguir el ángulo exacto para que Bracitos bordee el obstáculo de forma correcta. Adjuntamos un video demostración para comprobar el correcto funcionamiento.
- Comportamiento de evitación de obstáculos usando el sensor de ultrasonidos.
En este caso equipamos a Bracitos de un sensor de ultrasonidos orientado hacia delante y montado encima de un motor. Para montar esta parte nos las tuvimos que ingeniar con las piezas dadas y tras varias horas el resultado fué el siguiente:
El sensor girará gracias al motor donde esta situado en un intervalo comprendido entre -60º y 60º. Cuando detecte el obstáculo, a una distancia de 30 cm, se activará el comportamiento. Tomará 6 medidas distintas (una medida por cada 10 º) y tomará el camino adecuado para bordear el obstáculo, terminado orientado en el mismo sentido que la posición inicial.
Sin duda, esta es la parte que mas nos ha costado realizar de todas las que llevamos ya que para su solución se requiere el cálculo de una seria de fuerzas virtuales que han de ser resueltas mediante reglas trigonométricas.
Para su explicación adjuntamos la foto que se encuentra en el guión de la practica:
En la imagen podemos apreciar 3 fuerzas. La flecha azul se corresponde con la flecha de atracción, la flecha roja es la de repulsión y la verde la fuerza resultante de las dos anteriores, siendo esta la que se desea calcular.
Conocemos la distancia al obstáculo (a la que llamamos min) y el grado generado con el eje y, llamado gradoRepulsión, por lo que conocemos la coordenada polar del obstáculo. Del objetivo, conocemos las coordenadas cartesianas, por lo que para hallar el punto destino:
Pasamos la coordenada polar del obstáculo a cartesiana
Sumamos la coordenada cartesiana del obstáculo con la del punto destino
Pasamos la coordenada resultante a polar.
De la coordenada polar resultante, alpha será los grados que debe girar el robot para evitar el obstáculo y el modulo será la distancia que recorrerá antes de retomar la orientación original. Corroboramos el funcionamiento con el siguiente vídeo.
- Comportamiento ir hacia la luz.
Para esta actividad los profesores nos facilitaron dos sensores extras encargados de detectar la luz. Ambos sensores estarán orientados a -45º y 45º con respecto a la parte delantera del robot. Si uno de los sensores recibe una intensidad mayor que el otro,se activa el comportamiento que permite al robot girar hacia el lado donde mas intensidad de luz hay. No es necesario calibrar nada cuando ejecutamos este apartado ya que usamos el método getNormalizedLightValue().
- Comportamiento ir hacia la luz evitando obstáculos.
Este apartado es una combinación de los anteriores ya que debe ir hacia la luz evitando los obstáculos que haya en su camino. Para la evitación de obstáculos se nos da a elegir entre usar el sensor de contacto o el de ultrasonido, pero nosotros hemos realizado una prueba con cada uno de los sensores.
Como dijimos al inicio del blog, en este práctica, aunque no sea obligatorio, se nos recomendaba usar comportamientos. Y ahora, este apartado destaca por su sencillez y gran simplificación ya que es únicamente una combinación del comportamiento SeguirLuz del apartado anterior, Esquivar con el sensor de ultrasonido del segundo apartado y Esquivar con el sensor de contacto. Si no hubiéramos usado la programación de comportamientos nos hubiera llevado el doble de tiempo y esfuerzo.
Ya han pasado otras dos semanas y aquí estamos de nuevo, esta vez con la práctica 2. En esta práctica profundizaremos en los sensores que se le puede incluir a bracitos!!, en concreto el sensor de ultrasonidos, de contacto, de luz y de sonido.
Para comenzar tuvimos que poner con las piezas necesarias cada sensor para su correcto funcionamiento, os ponemos las imágenes de cada sensor.
Sensor de contactoSensor de luz
Sensor de sonidoSensor de ultrasonido
Con los sensores instalados, nos pusimos manos a la obra con la práctica. En primer lugar se nos pedía escribir un programa que mostrara la siguiente información en el display LCD del robot:
- Nombre del robot: Para este apartado usamos la función getName(), de la clase NXTCommDevice.
- Valor del sensor de ultrasonidos en mm: Para obtener el valor usamos la función getDistance().
- Valor del sensor de luz: Este apartado se divide en dos, el primero obtener el valor en crudo con la función readNormalizedValue() y el segundo calibrar los valores altos y bajos del ambiente donde nos encontremos con las funciones calibrateLow() y calibrateHigh() respectivamente.
- Tensión de la batería en Milivoltios: Para este apartado usamos la función getVoltageMilliVolt(), de la clase Battery.
- Memoria libre disponible en Bytes: Usamos la función getRuntime().freeMemory() de la clase Runtime.
Este apartado fue bastante fácil y no nos costo mucho, pero para no enrollarnos más , os mostramos una imagen donde se confirma el correcto funcionamiento de nuestro código.
Seguidamente pasamos a la instalación del sensor de sonido en nuestro robot. En esta parte realizamos un programa para que cuando se escuche una palmada el robot sea capaz de parar y reanudar su camino con otra palmada. Primero tuvimos que hacer una medida del sonido ambiente para que el sensor de sonido estuviera calibrado correctamente. Para obtener el valor medido del sensor de sonidos utilizamos la función readValue(). Hemos definido el valor de una palmada como el valor de una constante + el sonido ambiente, de este modo habrá una mayor posibilidad de que "Bracitos" se pare justo en el momento de la palmada.
Adjuntamos un vídeo que demuestre el correcto funcionamiento:
A continuación, seguimos con el siguiente apartado. El objetivo es lograr que "Bracitos" camine por su entorno y cuando choque contra un obstáculo, éste debe retroceder y girar un número aleatorio de grados y continuar avanzando. Para el cálculo de este número aleatorio usamos la función Math.random. Además este número de grados será mostrado por pantalla en cada giro.
La siguiente parte de la práctica es muy similar a la anterior, pero con la clara diferencia que debe evitar los obstáculos, es decir, no chocar contra ellos. Para dectectar los obstaculos usamos el sensor de ultrasonidos. La distancia mínima que hay que tener con los obstáculos es de 20 centímetros.
Para mayor claridad, mostramos por pantalla el número de grados que gira el robot al toparse con un impedimento junto con a distancia que nos encontramos de él.
El siguiente apartado se nos pedía seguir la pared para salir de un laberinto. Hemos definido un intervalo 'x', en el cual el robot anda en linea recta, a continuación usamos un controlador D, es decir, dependiendo de la distancia a la pared, si la distancia es < que la 'x' definida entonces el robot girará hacia un lado, y si lo supera girará al lado contrario.
Finalmente llegamos al último apartado. Es el más costoso y, por tanto, el que más puntos vale. Cuando se trabaja con sensores, existen variaciones del mismo modelo que influyen negativamente en el correcto funcionamiento de nuestra aplicación. Por ello, es necesario modelar las características particulares de los sensores que utilizamos.
Los pasos son los siguientes:
* Acercar y alejar el robot de la pared y anotar la distancia máxima y mínima que puede medir el sensor.
Mediante el uso de un metro, comprobamos que la distancia mínima que mide nuestro sensor es alrededor de 6cm, y la distancia máxima 254cm. Podemos comprobar que el sensor tiene una exactitud bastante grande.
* Situar el robot a 40 centímetros de la pared y girar el robot de manera que el ángulo con respecto a la pared aumente. Estos giros deben ser menores o iguales a 10 grados. Anotar el máximo ángulo que podemos girar para que los valores sean válidos.
Para este apartado pusimos al robot en perpendicular a la pared, y mediante giros de 10º ibamos comprobando la distancia que podia medir el sensor. Esto nos dió que a distancias entre el intervalo [-55º | +55º] el sensor de ultrasonidos deja de darnos la distancia correcta.
* Colocar el robot a una distancia de la pared de 20, 30, 40, 50, 60, 70, 80, 90 y 100 centímetros y apuntar el valor que nos indique el sensor de ultrasonidos. La finalidad de esto es conocer si el sensor tiene un error sistemático.
las medidas de toda la tabla están en cm
Observando la tabla podemos apreciar que la diferencia máxima, medida en centímetros, entre la distancia real y la distancia medida es 2 centímetros. Por tanto, podemos asegurar que el sensor funciona de manera más que aceptable.
Para el cálculo del error sistemático de nuestro sensor, basta con realizar el sumatorio de las medidas resultantes de la diferencia entre la distancia real y la marcada por el sensor y dividirla entre las nueve medidas tomadas, es decir, 14/9=1,5555556 cm.
* Calcular la incertidumbre del sensor en el eje X. Para ello, colocamos el robot a una distancia de la pared de 40, 50, 60, 70, 80, 90, 100, 110 y 120 centímetros. Por cada distancia tomamos 10 medidas distintas, desconectado y volviendo a conectar el sensor de ultrasonido para cada una de las medidas. Anotar la diferencia entre la distancia medida y la distancia real (xi − x).
las medidas de toda la tabla están en cm
Observado la tabla podemos apreciar claramente que en el eje X el error sistemático es ínfimo, siendo su error mas grande 0.3 centímetros para una distancia de 40 centímetros. Para el resto de valores los resultados son los esperados ya que la distancia marcada por nuestro sensor se corresponde prácticamente con la distancia real.
* Calcular la incertidumbre del sensor en el eje Y. Para realizar este calculo, colocamos el robot en el origen de coordenadas. Mediante el uso de un folio vamos a determinar a qué distancia del eje X comienza a detectar el obstáculo. Colocamos el folio a 10, 20, 30, 40, 50, 60 y 80 centímetros del eje X y apuntamos en qué momento es percibido por el sensor. Realizar el mismo proceso colocando el elemento a la altura del eje X que está a 40, 50, 60, 70, 80, 90, 100, 110, 120 centímetros del robot.
Este apartado destaca por su pesadez a la hora de tomar valores. Plasmamos los resultados en la siguiente tabla:
las medidas de toda la tabla están en cm
Si observamos la tabla podemos ver como más o menos se dibuja el cono de apertura descrito en el guión de la práctica. El valor medio más elevado, siendo asi la cumbre del cono, es a una distancia de 80 centímetros en el eje de la X ya que ve el obstáculo a 14.5 centímetros en el eje de la Y.
En este caso la precisión es menor que anteriormente ya que el sensor tiene mayor dificultad de reconocer los objetos que no están en su vertical.
Añadimos un par de fotos para demostrar la toma de medidas.
* Hallar la matriz de covarianza, ya usada en la práctica anterior.
A continuación mostraremos las 10 covarianzas que se corresponden con las nueve medidas dadas en el guión.
Durante estas dos últimas semanas hemos estado elaborando la práctica 1. Se trata de una toma de contacto con el API que proporciona LeJOS, en donde manejaremos el uso básico de los motores y de la pantalla LCD.
Lo primero que hicimos fué configurar el eclipse siguiendo las instrucciones que vienen en la documentación entregada, no nos costo nada puesto que estaba muy bien explicado cada paso.
Los apartados de la práctica son los siguientes:
- BasicMotor1: Debíamos crear un programa que al tocar un botón el motor se moviera hasta que volviéramos a tocar otro botón. Para conseguir este primer objetivo, hemos utilizado las funciones:
Button.waitForPress(), permite recoger la pulsación de un botón del ladrillo.
Motor.A.forward(), su funcion es mover hacia alante el motor A.
Motor.A.stop(), detiene instantáneamente el motor A.
- BasicMotor2: En este segundo apartado, debíamos conseguir que el motor rotase 45º al pulsar un botón. Fue una tarea sencilla y las funciones utilizadas fueron Button.waitForPress() y Motor.A.rotate(45).
- BasicMotor3: Se debe obtener el mismo resultado que en el apartado anterior pero usando el método rotateTo(). El funcionamiento de rotateTo() es similar a rotate(), pero la única diferencia es que hay que reiniciar la odometría del motor para que el motor gire. Esto se consigue con la función Motor.A.resetTachoCount().
- Visualización de la odometría del motor: En este apartado, se nos pedía escribir un programa que mostrara constantemente la posición del motor (grados de giro) en el LCD del ladrillo. El angulo mostrado debia estar en el rango [0.360]. En sí lo sacamos rápido, aunque nos costó un rato mostrar el ángulo correcto y no ángulos negativos o superiores a 360º.
- Cuadrado de calibración de movimiento: Consiste en poner un lápiz próximo al eje de rotación del robot de manera que dibuje su trayectoria en el suelo a medida que se desplaza. Lo lados del cuadrado dibujado deben ser de 40 cm cada uno. El proceso ha de repetirse para 10 iteraciones.
Esta parte se nos atragantó un poco, porque al principio no eramos capaces de meter un bolígrafo en un espacio tan pequeño. Pero tras varias chapuzas, llego la versión final y hemos de decir que nos quedó bastante bien centrado con respecto a los ejes de las ruedas.
Haga click en la imagen para agrandar!!
Tras ponerle las piezas necesarias, nos pusimos a hacer el programa en sí, el cual no tenía mayor complicación. En el siguiente vídeo os mostramos como funciona:
Como podemos observar en el vídeo, el cuadrado lo hace aparentemente perfecto. Aunque no se aprecia la linea que va haciendo el robot en el vídeo, hemos remarcado cada linea en cada una de las 10 iteraciones para que quedase mas vistoso y este es el resultado.Hay que decir que al ser una cartulina, una mina de lápiz no da para mas...y no pinta como nos gustaría.Por otro lado, lo hemos probado en un folio normal y pinta con normalidad.
Haga click en la imagen para agrandar!!
En la imagen anterior tenemos donde empezaba y acababa nuestro robot cada vez que iniciábamos el programa. Podemos observar una desviación clara pero esto sucede a esta velocidad, ya que si bajamos la velocidad el programa tendría una precisión de 99.9%. En las aulas de Linux probamos a hacerlo a baja velocidad y el resultado fue un cuadrado perfecto.
- Cálculo de la matriz de covarianza: Se trata de realizar un estudio estadístico para conocer el grado de incertidumbre del movimiento (error cometido) sobre el circuito anterior. A continuación se adjunta una tabla que muestra la desviación, en centímetros, durante diez iteraciones en el eje de las X y de las Y:
Por tanto, la matriz de covarianza resultante es la siguiente:
siendo el punto medio: (-0,7 . 1,6).
Cada dato de la matriz se corresponde con la incertidumbre de los cuatros ejes del cuadrado.
- Visualización de la trayectoria: Para solucionar este apartado, modificamos el código del cuadrado y lo actualizamos. Para ello mostramos en el LCD constantemente la posiciónx, y y los grados del motor sin trampa. Decimos sin trampa, ya que para hallar las nuevas posiciones no lo hicimos sumando 90º grados "a pelo" sino que lo hicimos con la función navigator.getAngle(). Así podemos observar correctamente la desviación del robot al realizar la trayectoria.
A continuación mostraremos dos videos, uno con velocidad normal y otro con velocidad baja, para que se pueda apreciar que a velocidades bajas no sale un cuadrado perfecto.
Velocidad Normal:
Velocidad Baja:
Ha sido una práctica muy interesante donde nos ha ayudado para familiarizarnos con bracitos, cada vez sabemos indicarle que haga mas cosas, en concreto con el uso de los motores, dentro de nada le tendremos en un gimnasio haciendo pesas porque se masca la tragedia para las proximas prácticas.
Hoy hemos montado el robot para el correcto funcionamiento de la práctica 1, a continuación mostraremos algunas de la imágenes hasta el resultado final.
Bracitos ya esta aquí!!!
Este diseño obviamente cambiará segun vayamos necesitando en cada práctica, añadiendole nuevos sensores, motores...etc
Hoy hemos completado las piezas que nos faltaban de nuestro robot. Además hemos tenido que configurar las variables de entorno para el correcto funcionamiento del software.
Posteriormente hemos aprendido a compilar,enlazar y crear el fichero ejecutable para luego descargarlo en el ladrillo. También hemos trasteado con el comando nxjbrowse para gestionar de manera gráfica los ficheros que hay en nuestro robot.
Por último hemos estado ejecutando varios de los ejemplos propuestos por el profesor, uno de ellos ha sido el famoso "Hola Mundo!", el cual , simplemente saca por la pantalla del ladrillo el siguiente mensaje: