jueves, 24 de febrero de 2011

Práctica 2 { parte 6: Calibración del sensor de ultrasonidos}

En este punto, vamos a desarrollar una aplicación que lea los valores del sensor del ultrasonido y los muestre por el LCD, para ver que distancias son críticas para el correcto funcionamiento del robot.

Construiremos el robot con el sensor de ultrasonidos en su parte superior, paralelo al suelo y mirando hacia delante.

Compararemos las medidas reales con las mostradas en el LCD. Lo haremos en cuatro puntos:

    1º Medida: Distancia real máxima y mínima.

Colocaremos el robot con el sensor perpendicular a la pared. Acercaremos y alejaremos el robot de la pared para ver cuales son las distancias máxima y mínima que puede medir.

Los resultados son:

    Distancia máxima: 219 cm 
    Distancia mínima:  6 cm


    2ª Medida: Máximo ángulo respecto a la pared.

Situaremos el robot a 40cm de la pared, e iremos girando el robot con giros <10º hasta obtener desde -90º - 90º Los resultados son:
    Rango de ángulos: -51º .. +51º


    3ª Medida: ¿Tiene el sensor error sistemático?

Colocaremos el robot perpendicularmente a la pared a distancias de 20,30,40,50,60,70,80,90,100 cm.

Una vez tomadas las medidas, calcularemos la media de la diferencia entre la medida y la distancia real.

      Medida Real (cm)                                  Medida LCD (cm)
               20                                                              22
               30                                                              31
               40                                                              41
               50                                                              51
               60                                                              60
               70                                                              70
               80                                                              81
               90                                                              91
               100                                                            101
                             110                                                            111
                          
                             120                                                            122


La media del error entre la medida tomada y la real es 1.2 cm

    4ª Medida: Incertidumbre del valor del sensor
Vamos a calcular por separado la incertidumbre en el eje X y en el eje Y.

- Cálculo incertidumbre del eje X:

Para el cálculo del eje X, colocamos el robot a distintas distancias de la pared, y tomaremos 10 medidas por cada distancia. Una vez tomadas todas las medidas calcularemos la media para cada una de las distancias.
Aquí vemos el resultado:

 
Vemos que el error es más alto cuanto más lejos está de la pared.

- Cálculo incertidumbre del eje Y:

Para el cálculo del eje Y colocaremos el robot en el origen de coordenadas, y determinaremos a qué distancia del eje X comienza a detectar el obstáculo.
Aquí vemos el resultado:


Como podemos comprobar, si forma un cono, pero es en las distancias medias, cuando más alejado es capaz de ver, por lo que se asemeja más a un rombo.


Ahora calcularemos la matriz de covarianza empleando el código que utilizamos en la primera práctica ( lo podéis ver en entradas anteriores del blog.

La matriz de covarianza queda:

P = 725.4             -11.1
-11.1               2.7


Práctica 2 { parte 5: Comportamiento sigue-pared para salir de un laberinto}

El algoritmo más simple para salir de un laberinto es seguir una misma pared sin soltarla hasta la salida. Por eso, en esta parte programaremos el robot para que siga una pared a una cierta distancia.

Para ello conectaremos al robot el sensor de ultrasonidos al puerto 1, y orientado 45º a la izquierda para que enfoque a la pared.

Aqui podemos ver una foto del montaje:



El codígo consistirá en mientras no presionemos el botón ESCAPE, que haga dos bucles separados. El primero comprobará si está más cerca de la pared de la distancia que le ponemos como límite. De ser así se parará, rotará hacia el lado contrario de la pared y volverá a medir para ver si puede salir del bucle. El otro bucle será si se aleja de la pared, acercarlo, rotando al lado contrario.
Entre ambos bucles se desplazará hacia delante.

Para desplazarlo, pararlo y girarlo usaremos lo métodos de la clase TachoPilot, stop,travel y rotate.

En resumen, hasta que queramos salir, irá moviendose mientras va tomando medidas, si mide que está cerca de la pared se aleja, y si ve que está lejos se acerca.

Como dato curioso, comenar que para este objetivo es muy importante la orientación del sensor, porque nos ha ocurrido que si estaba en una posición incorrecta (60º o 30º por ejemplo) no seguía correctamente las esquinas ya que tomaba otra medida diferente y giraba sobre sí mismo.

Aquí vemos el vídeo del funcionamiento correcto siguiendo varias esquinas, una puerta, el marco de una puerta...:




Aquí podemos ver el código:


import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.UltrasonicSensor;
import lejos.robotics.navigation.TachoPilot;


public class Laberinto {

    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.C;
      Motor Rdcha = Motor.B;

      UltrasonicSensor sonar = new UltrasonicSensor(SensorPort.S1);
      TachoPilot viajar = new TachoPilot(5.6f,10.85f,Rizda,Rdcha);

      sonar.continuous();
      viajar.forward();

      while (!Button.ESCAPE.isPressed()){

        int dist = sonar.getDistance();

        while (dist< 25){
          viajar.stop();
          viajar.rotate(-10);
          dist = sonar.getDistance();
        }
        viajar.travel(5);
        while (dist>35){
          viajar.stop();
          viajar.rotate(10);
          dist = sonar.getDistance();
        }
        viajar.stop();
        viajar.travel(5);
      }
    }
}

lunes, 21 de febrero de 2011

Práctica 2 { parte 4: Bump & Go! usando sensores de ultrasonido}

El objetivo de esta parte será que el robot deambule por el entorno y cuando choque con un obstáculo retroceda y cambie de dirección.


El montaje será el utilizado hasta ahora con los dos motores a las ruedas, pero en la parte frontal se le añadirá un sensor de ultrasonidos al puerto 1.

Aquí vemos una foto del montaje:



Para programar este comportamiento lo primero que debemos hacer es definir rueda izquierda y rueda derecha, y crear un objeto del tipo UltrasonicSensor. Las instrucciones serán forward a ambas ruedas para que empieze a moverse hacia delante, y luego en un for la condición de que si el sensor detecta un obstáculo a 20cm vuelva para atrás durante 2 segundos, gire la rueda izquierda un ángulo aleatorio, y vuelva hacia delante. Para calcular si hay un objeto a 20 cm se hace con el if (sonar.getDistance()<=25. El 25 es porque el sensor está a 5 cm por detrás de la rueda. Volver atrás lo haremos con backward, los 2 segundos lo pondremos con Thread.sleep(2000), y el ángulo aleatorio con Rizda.rotate((int) Math.random());
Aquí vemos un vídeo de ejemplo:



Aquí podemos ver el código:

import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.TouchSensor;


public class BumpAndGo {

    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.B;
      Motor Rdcha = Motor.C;

      UltrasonicSensor sonar = new UltrasonicSensor(SensorPort.S1);

      Rizda.forward();
      Rdcha.forward();
      for(;;){
        if (sonar.getDistance()<=25){
          Rizda.stop();
          Rdcha.stop();
          Rizda.backward();
          Rdcha.backward();
          Thread.sleep(2000);
          Rizda.rotate((int) Math.random());
          Rizda.forward();
          Rdcha.forward();
        }
      }
    }
}

Práctica 2 { parte 3: Bump & Go! usando sensores de contacto}

El objetivo de esta parte será que el robot deambule por el entorno y cuando choque con un obstáculo retroceda y cambie de dirección.


El montaje será el utilizado hasta ahora con los dos motores a las ruedas, pero en la parte frontal se le añadirá un sensor de contacto al puerto 4. A este sensor de contacto, ha habido que ponerle unas piezas que detecten mejor cuando choca porque sino con sólo el sensor, a veces falla.

Aquí vemos una foto del montaje:




Para programar este comportamiento lo primero que debemos hacer es definir rueda izquierda y rueda derecha, y crear un objeto del tipo TouchSensor. Las instrucciones serán forward a ambas ruedas para que empieze a moverse hacia delante, y luego en un for la condición de que si toca algo el sensor (método touch.isPressed()) vuelva para atrás durante 3 segundos, gire la rueda izquierda un ángulo aleatorio, y vuelva hacia delante. Volver atrás lo haremos con backward, los 3 segundos lo pondremos con Thread.sleep(3000), y el ángulo aleatorio con Rizda.rotate((int) Math.random());
Aquí vemos un vídeo de ejemplo:



Aquí podemos ver el código:

import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.TouchSensor;


public class BumpAndGo {

    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.B;
      Motor Rdcha = Motor.C;

      TouchSensor touch = new TouchSensor(SensorPort.S4);

      Rizda.forward();
      Rdcha.forward();
      for(;;){
        if (touch.isPressed()){
          Rizda.stop();
          Rdcha.stop();
          Rizda.backward();
          Rdcha.backward();
          Thread.sleep(3000);
          Rizda.rotate((int) Math.random());
          Rizda.forward();
          Rdcha.forward();
        }
      }
    }
}

domingo, 20 de febrero de 2011

Práctica 2 { parte 2: Control del robot por sonido}

El objetivo de esta parte será controlar a "Linguo" mediante su sensor de sonido (micrófono).
El comportamiento será sencillo, hacer que el robot se mueva en línea recta, y cuando detecte una palmada se para, y con la siguiente palmada vuelva a andar.

El montaje será parecido al de la primera práctica exceptuando que se añade un sensor de sonido en el puerto 2.

Aquí vemos una foto del montaje:



Para programar este comportamiento lo primero que debemos hacer es calibrar el sensor. Para ello haremos un for de 1 a 100 que vaya tomando valores, y luego haga una media de ellos. Esta media será el ruido en dB. Los valores los tomaremos con el método readValue que haremos sobre la variable sonido de tipo SoundSensor que nos habremos definido antes.
Después de calcular la media haremos que se mueva hacia delante. Para ello aplicaremos el método forward() a la rueda izquierda y rueda derecha definidas previamente.
Posteriormente, y dentro de un for, haremos que la primera palmada, lo pare aplicando el método stop a las ruedas,y con la siguiente palmada lo vuelva a mover.
Para saber si hay una palmada o no, será hacer un if, recogiendo el valor con readValue, y viendo si es un valor superior a la media + el 15%.

Aquí vemos un vídeo de ejemplo:



Aquí podemos ver el código:


import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.SoundSensor;
public class ClapControl {

    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.B;
      Motor Rdcha = Motor.C;
      int i;
      int media=0;

      SoundSensor sonido = new SoundSensor(SensorPort.S2);

      for(i=1;i<=100;i++){
        media=media+sonido.readValue();
      }
      media=(int) ((media/100)*1,15);
      System.out.println("Sonido limite: "+media);
      Rizda.forward();
      Rdcha.forward();
      for(;;){
        if (sonido.readValue()>media){
          Rizda.stop();
          Rdcha.stop();
        }
        if (sonido.readValue()>media){
        Rizda.forward();
        Rdcha.forward();
        }
      }
    }
}

Práctica 2 { parte 1: Obteniendo información}

En esta segunda práctica nos encargaremos de obtener información útil mediante los sensores del robot.
Comenzaremos en esta primera parte haciendo un programa sencillo que muestre por pantalla cierta información. Esta información será :
    - nombre del robot.
    - valor en mm del sensor de ultrasonidos.
    - valor del sensor de la luz en crudo.
    - valor del sensor de luz en modo pasivo (mínimo y máximo).
    - tensión de la batería en Milivoltios.
    - memoria libre en Bytes.

- El nombre lo meteremos a pelo con un LCD.drawString.

- Para calcular el valor del sensor de ultrasonidos, conectaremos el sensor al robot en el puerto 1.
El valor lo obtendremos mediante la función getDistance. Para ello definimos una variable sonar del tipo UltrasonicSensor, y hacemos en ella una llamada a getDistance.

- Para calcular la luz, conectaremos el sensor de luz del robot al puerto 3. Devolvemos el valor en crudo con el readNormalizedValue, y con el readValue devolvermos el valor del sensor en la escala 0-100% (en esta ocasión el sensor aún no está calibrado). Para ello, lo de siempre, nos creamos una variable luz del tipo LightSensor, y sobre ella aplicaciones las funciones nombradas anteriormente.

- La información de la batería la mostraremos con el método getVoltageMiliVolt de la clase Battery, y por último, la memoria libre utilizaremos el método getRuntime().freeMemory() de la clase Runtime.

Todo esto lo haremos en un bucle for infinito para que se actualicen cada 10segundos ( estos tiempo hasta que se actualice lo haremos con Thread.sleep(10000).

Aquí vemos un vídeo de cómo se muestra la información:



Aquí podemos ver el código:



import lejos.nxt.Battery;
import lejos.nxt.LCD;
import lejos.nxt.LightSensor;
import lejos.nxt.SensorPort;
import lejos.nxt.UltrasonicSensor;

public class Informacion {

    public static void main(String[] args) throws InterruptedException {
      UltrasonicSensor sonar = new UltrasonicSensor(SensorPort.S1);
      LightSensor luz = new LightSensor(SensorPort.S3);
      for(;;){

        LCD.clear();
        LCD.drawString("Nombre:Linguo",1,2);

        LCD.drawString("Sonar:",1,3);
        LCD.drawInt(sonar.getDistance(),7,3);


        LCD.drawString("Luz:",1,4);
        LCD.drawInt(luz.readNormalizedValue(),5,4);
        LCD.drawInt(luz.readValue(),10,4);

        LCD.drawString("Bateria:", 1,5);
        LCD.drawInt(Battery.getVoltageMilliVolt(),9,5);


        LCD.drawString("Memoria:",1,6);
        LCD.drawInt((int)(Runtime.getRuntime().freeMemory()),9,6);
        Thread.sleep(10000);
      }
    }
}

jueves, 10 de febrero de 2011

Práctica 1 { parte 6: Visualización de la trayectoria}

Haremos un programa con el cual mostremos en el LCD la trayectoria en tiempo real que va siguiendo el robot.
Para ello haremos uso de la clase TachoPilot y LCD.

En el LCD iremos mostrando posición X, posición Y, y ángulo. Comenzaremos en (0,0,0) e iremos actualizando la posición y grado según esta fórmula:



Aquí vemos un vídeo de como nos lo muestra en el LCD del robot:




Como se observa en el vídeo, en el LCD se muestra que llega hasta 80cm, cuando se debería hacer hasta 40. Hemos hecho una traza y vemos que realmente se mueve 40cm, pero en la posición muestra 80cm.

Aquí podemos ver el código:



import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.robotics.navigation.*;

public class Trayectoria {
    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.B;
      Motor Rdcha = Motor.C;
      int i;
      int x=0;
      int y=0;
      int dist=0;
      int grados=0;

      TachoPilot viajar = new TachoPilot(5.6f,10.85f,Rizda,Rdcha);

      for(i=1;i<=4;i++){
        viajar.reset();
        viajar.travel(40,true);
        while (viajar.getTravelDistance() < 39.9){
          dist=(int) viajar.getTravelDistance();
          x=(int) (x+dist*Math.cos(Math.toRadians(grados)));
          y=(int) (y+dist*Math.sin(Math.toRadians(grados)));
          LCD.clear();
          LCD.drawInt(x,7,3);
          LCD.drawInt(y,7,4);
          LCD.drawInt(grados,7,5);
          Thread.sleep(500);
        }
        viajar.rotate(90);
        grados=(int) (grados + viajar.getAngle());
        LCD.clear();
        LCD.drawInt(x,7,3);
        LCD.drawInt(y,7,4);
        LCD.drawInt(grados,7,5);
        Thread.sleep(250);
      }
    }
}

miércoles, 9 de febrero de 2011

Práctica 1 { parte 5: cálculo de la matriz de covarianza }

Realizaremos un estudio estadístico para conocer el grado de incertidumbre del movimiento del robot.
Este movimiento será un simple travel(90), repetido 10 veces. Trazaremos una linea recta para ver la desviación y la distancia recorrida en cada iteración.

Veamos una tabla con los datos:


Con estos datos resolveremos la siguiente matriz de covarianza que expresa algebraicamente el error cometido:


Haremos un programa en java en el que insertemos los datos obtenidos (multiplicados por 100)  y nos devolverá la matriz resuelta. Esta matriz nos servirá para obtener datos del error que tiene, la distancia que recorre, hacia donde se desvia etc...

Aquí vemos la matriz que nos devuelve el programa:


Los números salen tan grandes por las unidades, todo simplificado sería:
| 2,24125          0,04715 |
                                     
| 0,04715            0,0851 |

A continuación os mostraremos el código del programa que nos saca la matriz:



Aquí os dejamos el código:



public class Covarianza {

    public static void main(String[] args) {

      java.util.Scanner s = new java.util.Scanner(System.in)
      .useDelimiter(System.getProperty("line.separator"));

      float uno=0;
      float dos=0;
      float tres=0;
      int i;
      int mediax=0;
      int mediay=0;
      int[] x=new int[10];
      int[] y=new int[10];
      float aux,aux2;

      for (i=0;i<=9;i++){
        System.out.println("Inserte el resultado de x"+i);
        x[i] = s.nextInt();
        mediax=mediax+x[i];
      }
      mediax=mediax/10;
      for (i=0;i<=9;i++){
        System.out.println("Inserte el resultado de y"+i);
        y[i] = s.nextInt();
        mediay=mediay+y[i];
      }
      mediay=mediay/10;
      for (i=0;i<=9;i++){
        aux=x[i]-mediax;
        aux=(float) Math.pow(aux,2);
        uno=uno+aux;
      }
      uno=uno/10;
      for (i=0;i<=9;i++){
        aux=x[i]-mediax;
        aux2=y[i]-mediay;
        aux=aux*aux2;
        dos=dos+aux;
      }
      dos=dos/10;
      for (i=0;i<=9;i++){
        aux=y[i]-mediay;
        aux=(float) Math.pow(aux,2);
        tres=tres+aux;
      }
      tres=tres/10;
      System.out.print(" | "+uno);
      System.out.print(" "+dos);
      System.out.println(" |");
      System.out.println("P = | |");
      System.out.print(" | "+dos);
      System.out.print(" "+tres);
      System.out.println(" |");
    }
}

martes, 8 de febrero de 2011

Práctica 1 { parte 4: cuadrado de calibración de movimiento }

En esta parte haremos que el robot recorra un cuadrado de 40cm de lado y dibujemos su trayectoria durante 10 repeticiones para ver como está de calibrado. Para ello haremos uso de la clase TachoPilot.
Haremos un código sencillo basado en travel(40) y rotate (90) así cuatro veces. Con esto estamos indicando al robot que recorra 40 cm y gire 90º. Al hacerlo cuatro veces, conseguiremos que dibuje la trayectoria de un cuadrado.

Aquí vemos un vídeo:



Como observamos en el vídeo el robot no hace la trayectoria correctamente porque va muy deprisa, y patina.
Solucionamos este error reduciendo la velocidad con la función setSpeed(100).

Aquí vemos un vídeo:



Aquí observamos una foto de las trayectorias pintadas por el robot:



Podemos observar varios trazos diferentes, pero todos próximos. Esto nos demuestra que tiene un error mínimo, pero siempre es el mismo.
Como comentamos, no nos pinta un cuadrado perfecto, ya que no hace esquinas de 90º. Esto es debido a que el bolígrafo no esta puesto justo en centro del eje, por falta de espacio.
Las líneas no coinciden, porque al recolocarlo siempre varía la posición inicial, aunque sea unos milímetros


Conclusiones:

Con este pequeño ejemplo práctico nos damos cuenta la dificultad que tiene el creador a la hora de plasmar en el robot la realidad, ya que siempre va a haber un porcentaje de error de fallo, es decir lo que en teoría vemos posible y perfecto nunca se va a conseguir al 100% en la realidad, por diversos factores que lo alteren, en este caso, el rozamiento, inercia...
Además apreciamos que el error es sintomático, es decir, siempre se repite el mismo error de 1-2 centímetros y en los mismos puntos


Aquí os dejamos el código:


import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.robotics.navigation.*;

public class CuadradoRotate {
    public static void main(String[] args) throws InterruptedException {
      Motor Rizda = Motor.B;
      Motor Rdcha = Motor.C;
      int i;

      TachoPilot viajar = new TachoPilot(5.6f,10.85f,Rizda,Rdcha);

      viajar.setSpeed(100);

      for(i=1;i<=10;i++){
        viajar.travel(40);
        viajar.rotate(90);
        viajar.travel(40);
        viajar.rotate(90);
        viajar.travel(40);
        viajar.rotate(90);
        viajar.travel(40);
        viajar.rotate(90);
        Thread.sleep(5000);
      }
    }
}

lunes, 7 de febrero de 2011

Práctica 1 { parte 3: manejo del lcd }

Hemos realizado un código para comprobar la odometría del motor del robot. Para ello hemos conectado un motor al puerto B del "ladrillo", y al motor le pondremos una pieza que haga de brazo. Al mover este brazo, en el lcd saldrán los grados a los que está el motor.
Controlamos que los grados no salgan negativos ni sobrepasen 360.
Aquí vemos una foto del sencillo montaje:







Aquí teneis el código:

import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.LCD;
public class Odometria {

    public static void main(String[] args) throws InterruptedException {
      Motor b = Motor.B;
      int grado;
      int x = 7;
      int y = 4;

      while (!Button.ESCAPE.isPressed()){
        grado=b.getTachoCount();
        while (grado >360) {
          grado = grado -360;
        }
        while (grado < 0){
          grado = grado +360;
        }
        LCD.clear();
        LCD.drawInt(grado,x,y);
        Thread.sleep(500);
      }
    }
}

sábado, 5 de febrero de 2011

Práctica 1 { parte 2: funciones básicas del motor }

Realizamos tres códigos para ver una prueba sencilla del motor. El montaje para esto es básico. Momentáneamente extraemos el "ladridillo" del montaje anterior, y a través del puerto A, le conectamos otro motor, al que le añadiremos unas piezas para ver el movimiento. Unas fotos de los pequeños montajes:




















 

La foto de la izquierda nos valdrá para el primer código. Con este código conseguiremos que el motor gire cuando pulsemos el botón, y se pare de girar cuando lo volvamos a pulsar, y así sucesivamente. Aquí un vídeo explicativo:









Con la foto de la derecha haremos el segundo y tercer código. Con ambos conseguiremos que cuando pulsemos el botón, el motor se gire 45º. La diferencia es que con el segundo código utilizaremos el método rotate, y para el otro el método rotateTo.
Aquí un vídeo explicativo:








Aquí teneis los códigos de los 3 ejemplos:

import lejos.nxt.Button;
import lejos.nxt.Motor;
public class BasicMotor1 {
    public static void main (String[] args)
    {
      Motor a = Motor.A;

      while(!Button.ESCAPE.isPressed()){
        Button.waitForPress();
        a.forward();
        Button.waitForPress();
        a.stop();
      }
    }
}

import lejos.nxt.Button;
import lejos.nxt.Motor;
public class BasicMotor2 {
    public static void main (String[] args)
    {
      Motor a = Motor.A;

      while(!Button.ESCAPE.isPressed()){
        Button.waitForPress();
        a.rotate(45);
      }
    }
}

import lejos.nxt.*;
public class BasicMotor3 {
    public static void main (String[] args)
    {
      Motor a = Motor.A;
      int i = 45;
      while(!Button.ESCAPE.isPressed()){
        Button.waitForPress();
        a.rotateTo(i);
        i=i+45;
      }
    }
}

Práctica 1 { parte 1: primeros pasos }

En esta práctica, nos encargaremos del uso de los motores. Para ello realizaremos un montaje adecuado con ellos, y las ruedas para probar movimientos básicos. Para programar estos movimientos utilizaremos eclipse, por lo que antes haremos un primer código de prueba para ver la compilación y el enlace mediante este entorno de programación. Una vez aprendido estas nociones básicas de eclipse haremos un programa de prueba con movimientos básicos de backward,forward,rotate… A continuación mostraremos una foto del montaje, y un video explicativo de estos movimientos básicos.