jueves, 11 de noviembre de 2010

LeJOS en Mac OSX

Este viene a conformarse como un breve tutorial para hacer funcionar LeJOS en Mac OSX.

Los pasos a seguir son los siguientes:

1.- Bajar e instalar Java.
2.- Bajar e instalar el driver USB de LEGO para Mac OSX.
3.- Bajar, instalar, y configurar LeJOS.
4.- Conectar el brick NXT, y verificar si podemos conectarnos con éste.

1.- Bajar e instalar Java.

En Mac OSX, java viene instalado por default, así que lo único que tenemos que hacer es asegurarnos de que tenemos la última version instalada (para efectos de este tutorial, la versión corresponde a la 1.6 (Update 3)).

Para lo anterior, seleccionamos , y posteriormente la opción de “Software Update”.

2.- Bajar e instalar el driver USB de LEGO para Mac OSX.

El driver que necesitaremos bajar, es el conocido como “Fantom Driver”; este puede ser ubicado en la página de soporte de LEGO Mindstorms.

El driver, puede ser bajado de la siguiente dirección:




3.- Bajar, instalar, y configurar LeJOS
Desde la página oficial de LeJOS, descargamos la versión para Linux / Mac OSX:

La carpeta puede ser extraída donde sea; en mi caso, lo he hecho en el directorio Home.

El siguiente paso consiste en configurar las variables de entorno, tanto para Java como para LeJOS; para esto, abrimos la Terminal, e introducimos el siguiente comando:


sudo pico .profile


La línea anterior, nos permitirá crear un archivo denominado .profile, o editarlo en caso de que ya exista. Introducimos nuestro password de administrador, y añadimos las siguientes líneas:


export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
export NXJ_HOME=/Users/rafaelochoa/lejos_nxj
export DYLD_LIBRARY_PATH=$NXJ_HOME/bin
export PATH=$PATH:$JAVA_HOME/bin:$NXJ_HOME/bin


En mi caso, mi directorio Home, corresponde a “rafaelochoa”; habrá que modificarlo, según el nombre del directorio que el usuario haya asignado.

La primer línea corresponde a la ruta donde se encuentra instalado JAVA.
La segunda, corresponde a la ruta donde se encuentra localizada la carpeta lejos_nxj
La tercera sólo es necesaria, para hacer uso de una configuración de eclipse que nos hará más fácil tanto la compilación, como la subida de archivos al brick NXT.
La cuarta línea, añade las carpetas correspondientes a los binarios tanto de LeJOS como de Java.

Para salir, tecleamos CTRL + X , Y (Yes) para salvar los cambios,  y enter, confirmando antes, que el nombre del archivo es .profile.

Suponiendo que hemos extraído la carpeta “lejos_nxj” , en el directorio Home, accedemos al directorio “lejos_nxj/bin”:


cd lejos_nxj/bin


Una vez en el directorio, ejecutamos el siguiente comando:


chmod +x *


Para que los cambios que hemos realizado tengan efecto, debemos reiniciar nuestra computadora.
Nota:
Si se tiene instalado Snow Leopard, y LeJOS no funciona, luego de terminar la instalación/configuración, sustituye los archivos de la carpeta lejos_nxt/bin, por los siguientes:


Luego de extraer la carpeta, basta con sustuir la carpeta bin original, por la contenida en el archivo .zip.

Si fue necesario hacer la sustición anteriormente mencionada, es necesario volver a cambiar los permisos de la carpeta por medio del comando:


chmod +x *


Los archivos contenidos en el .zip forzan a Java a ejecutarse en un modo de 32 bits, con el fin de ser completamente compatible con LeJOS.

Si se posee una versión anterior a Snow Leopard, este paso no es necesario.

4.- Conectar el brick NXT, y verificar si podemos conectarnos con éste.

Una vez instalado, y configurado el LeJOS, vamos a verificar si nos podemos conectar con éste. Para ello, conectamos el brick con el cable USB, prendemos el brick, y tecleamos el sigueinte comando:


nxjbrowse


Deberá aparecer una ventana como la siguiente:



Le damos click en connect, una vez establecida la conección, podemos decir que ha sido configurado todo lo necesario para poder comenzar a escribir programas, y hacerlos funcionar en nuestro brick NXT.


Finalmente, adjunto una liga para descargar esta misma guía en formato pdf:


http://www.multiupload.com/ESOJ2TYQV6

martes, 9 de noviembre de 2010

Mindstorms NXT - LeJOS (Salir de un laberinto)

El objetivo de este ejercicio consiste en programar al robot, para que utilizando el sensor ultrasónico, pueda localizar obstáculos y girar para evitarlos. El robot también se debe programar para que utilizando estas habilidades (localizar obstáculos y girar) pueda salir de un laberinto de ángulos rectos.

Se muestra a continuación un par de fotografías que ilustran el modelo de robot utilizado, para la resolución del problema:





Solución

Como se comentó hace tres entradas, se utilizó para programar al robot, una arquitectura conocida como “subsumption architecture”; para más información acerca de en qué consiste, consultar la entrada del blog mencionada.

En el caso preciso de este ejercicio, se cuenta con una única capa, que representa el “seguir el contorno de una pared/objeto”.

El código para lograr lo anterior, se encuentra divido en dos clases: WallFollower.java, y FollowWall.java,. La primera de ellas actúa básicamente como un activador del comportamiento descrito en el párrafo anterior.

Se muestra a continuación el código de cada una de las dos clases:

WallFollower.java

import lejos.nxt.*;
import lejos.robotics.navigation.*;
import lejos.robotics.subsumption.*;

public class WallFollower { 
 public static void main(String [] args){
  TachoPilot pilot = new TachoPilot(5.6f, 11.25f, Motor.A, Motor.C);
  UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S3);

  Behavior b1 = new FollowWall(pilot, sonic);
  
  Behavior [] behaviorArray = {b1};
  Arbitrator arbitrator = new Arbitrator(behaviorArray);
  
  LCD.drawString("WallFollower", 2, 2);
  Button.waitForPress();
  arbitrator.start();
 }

}

FollowWall.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;

public class FollowWall implements Behavior{
 private TachoPilot pilot;
 private UltrasonicSensor sonic;

 private int distance;
 private int lastDistance;
 private int distanceDifference;
 
 private int veryCloseLeft;
 private int slightlyCloseLeft;
 private int notCloseLeft;
 private int slightlyFarRight;
 private int veryFarRight;
 
 private int basePowerLeft;
 private int basePowerRight;
 
 private Motor rightMotor;
 private Motor leftMotor;

 public FollowWall(TachoPilot pilot, UltrasonicSensor sonic){
  this.pilot = pilot;
  this.sonic = sonic;

  distance = 0;
  lastDistance = 0;
  
  veryCloseLeft = 20;
  slightlyCloseLeft = 25;
  notCloseLeft = 30;
  
  slightlyFarRight = 35;
  veryFarRight = 40;
  
  basePowerLeft = 100;
  basePowerRight = 99;
  
  rightMotor = (Motor)pilot.getRight();
  leftMotor = (Motor)pilot.getLeft();
 }

 public boolean takeControl(){
   LCD.clear();
   LCD.drawString("WallFollow", 2, 2);
   return true;
 }

 public void suppress(){
  pilot.stop();
 }

 public void action(){
  pilot.forward();

  while(true){
   lastDistance = distance;
   distance = sonic.getDistance();
   distanceDifference = lastDistance - distance;
   
   if(distance <= notCloseLeft){
    if(distance <= veryCloseLeft){
     rightMotor.setPower(basePowerRight);
     rightMotor.backward();
     leftMotor.setPower(basePowerRight);
     leftMotor.forward();
    }
    else if(distance <= slightlyCloseLeft){
     rightMotor.setPower(0);
     rightMotor.stop();
     leftMotor.setPower(basePowerLeft);
     leftMotor.forward();
    }
    else if(distance <= notCloseLeft){
     leftMotor.setPower(basePowerLeft);
     leftMotor.forward();
     rightMotor.setPower(0);
     rightMotor.flt();
    }  
   }
   else{
    if(distance >= slightlyFarRight){
     if(distanceDifference < 2){
      leftMotor.setPower(0);
      leftMotor.stop();
      rightMotor.setPower(basePowerRight);
      rightMotor.forward();
     }
    }
    else if(distance >= veryFarRight){
     leftMotor.setPower(basePowerLeft/2);
     leftMotor.forward();
     rightMotor.setPower(basePowerLeft);
     rightMotor.forward();
    }
   }
   try {
    Thread.sleep(30);
   } catch (InterruptedException e) {}
  }
 }
}


Finalmente, se adjunta un par de videos, donde se puede apreciar al robot ejecutando la tarea encomendada:




Mindstorms NXT - LeJOS (Rodear un objeto)

El objetivo de este ejercicio consiste en programar al robot, para que utilizando el sensor de tacto, pueda encontrar, y rodear un objeto siguiendo su contorno. El objeto puede tener cualquier forma (rectangular, como una caja o cilíndrica como un bote), asimismo, el objeto será lo suficientemente pesado como para que el robot no lo mueva, dentro de límites razonables.

Se muestra a continuación un par de fotografías que ilustran el modelo de robot utilizado, para la resolución del problema:


Solución




Como se comentó hace dos entradas, se utilizó para programar al robot, una arquitectura conocida como “subsumption architecture”; para más información acerca de en qué consiste, consultar la entrada del blog mencionada.

En el caso preciso de este ejercicio la capa más baja representa el “moverse hacia delante”; por su parte, la capa más elevada representa el comportamiento de “reconocer que se ha chocado, retirarse del objeto que se tiene enfrente una distancia determinada, rotar de tal manera que el sensor ultrasónico quede mirando en dirección al objeto, y rodearlo”; ésta capa toma control cuando se ha detectado una colisión por medio del sensor de tacto.

El código para lograr lo anterior, se encuentra divido en tres clases: BWallFollower.java, DriveForward.java, y HitWall.java,. La primera de ellas actúa básicamente como un regulador, para decidir qué comportamientos deben ser activados, mientras que las otras dos, aluden a los comportamientos descritos en el párrafo anterior, de manera correspondiente.

Se muestra a continuación el código de cada una de las tres clases:

BWallFollower.java

import lejos.nxt.*;
import lejos.nxt.comm.RConsole;
import lejos.robotics.navigation.*;
import lejos.robotics.subsumption.*;

public class BWallFollower { 
 public static void main(String [] args){
  TachoPilot pilot = new TachoPilot(5.6f, 11.25f, Motor.A, Motor.C);
  TouchSensor touch = new TouchSensor(SensorPort.S1);
  UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S3);
  
  Behavior b1 = new DriveForward(pilot);
  Behavior b2 = new HitWall(pilot, touch, sonic);
  
  Behavior [] behaviorArray = {b1, b2};
  Arbitrator arbitrator = new Arbitrator(behaviorArray);
  
  LCD.drawString("BWallFollower", 2, 2);
  Button.waitForPress();
  arbitrator.start();
 }

}

DriveForward.java

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

public class DriveForward implements Behavior{
 
 private Pilot pilot;
 
 public DriveForward(Pilot pilot){
  this.pilot = pilot;
 }
  
 public boolean takeControl(){
  LCD.clear();
  LCD.drawString("DriveForward", 2, 2);
  return true;
 }
 
 public void suppress(){
  pilot.stop();
 }
 
 public void action(){
  pilot.forward();
 }

}

HitWall.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;


public class HitWall implements Behavior, SensorPortListener{
 private TachoPilot pilot;
 private TouchSensor touch;
 private UltrasonicSensor sonic;
 private boolean hasCollided;

 private int backoff;
 private int rotationAngle;
 private int distance;
 private int lastDistance;
 private int distanceDifference;
 
 private int veryCloseLeft;
 private int slightlyCloseLeft;
 private int notCloseLeft;
 private int slightlyFarRight;
 private int veryFarRight;
 
 private int basePowerLeft;
 private int basePowerRight;
 
 private Motor rightMotor;
 private Motor leftMotor;

 public HitWall(TachoPilot pilot, TouchSensor touch, UltrasonicSensor sonic){
  this.pilot = pilot;
  this.touch = touch;
  this.sonic = sonic;

  hasCollided = false;
  SensorPort.S1.addSensorPortListener(this);

  backoff = 5;
  rotationAngle = -90;
  distance = 0;
  lastDistance = 0;
  
  veryCloseLeft = 20;
  slightlyCloseLeft = 25;
  notCloseLeft = 30;
  
  slightlyFarRight = 35;
  veryFarRight = 40;
  
  basePowerLeft = 100;
  basePowerRight = 99;
  
  rightMotor = (Motor)pilot.getRight();
  leftMotor = (Motor)pilot.getLeft();
 }

 public void stateChanged(SensorPort port, int oldValue, int newValue){
  if(touch.isPressed())
   hasCollided = true;
 }

 public boolean takeControl(){
  if(hasCollided){
   LCD.clear();
   LCD.drawString("HitObject", 2, 2);
   hasCollided = false;
   return true;
  } else
   return false;
 }

 public void suppress(){
  pilot.stop();
 }

 public void action(){
  pilot.stop();
  pilot.travel(-backoff);
  pilot.rotate(rotationAngle);
  
  while(true){
   lastDistance = distance;
   distance = sonic.getDistance();
   distanceDifference = lastDistance - distance;
   
   if(distance <= notCloseLeft){
    if(distance <= veryCloseLeft){
     rightMotor.setPower(basePowerRight);
     rightMotor.backward();
     leftMotor.setPower(basePowerRight);
     leftMotor.forward();
    }
    else if(distance <= slightlyCloseLeft){
     rightMotor.setPower(0);
     rightMotor.stop();
     leftMotor.setPower(basePowerLeft);
     leftMotor.forward();
    }
    else if(distance <= notCloseLeft){
     leftMotor.setPower(basePowerLeft);
     leftMotor.forward();
     rightMotor.setPower(0);
     rightMotor.flt();
    }  
   }
   else{
    if(distance >= slightlyFarRight){
     if(distanceDifference < 2){
      leftMotor.setPower(0);
      leftMotor.stop();
      rightMotor.setPower(basePowerRight);
      rightMotor.forward();
     }
    }
    else if(distance >= veryFarRight){
     leftMotor.setPower(basePowerLeft/2);
     leftMotor.forward();
     rightMotor.setPower(basePowerLeft);
     rightMotor.forward();
    }
   }
   try {
    Thread.sleep(30);
   } catch (InterruptedException e) {}
  }
 }
}

Finalmente, se adjunta un par de videos, donde se puede apreciar al robot ejecutando la tarea encomendada:



Mindstorms NXT - LeJOS (Ubicar y encontrar una fuente de sonido)

El objetivo de este ejercicio consiste en programar el robot, para que utilizando el sensor de sonido (micrófono), pueda ubicar e ir en dirección de una fuente de sonido.

Se muestra a continuación un par de fotografías que ilustran el modelo de robot utilizado, para la resolución del problema:








Solución

Como se comentó en la entrada anterior, se utilizó para programar al robot, una arquitectura conocida como “subsumption architecture”; para más información acerca de en qué consiste, consultar la entrada anterior.

En el caso preciso de este ejercicio la capa más baja representa el “no escuchar a la fuente de sonido”; el robot al encontrarse en esta situación, comienza a hacer giros incrementales, de izquierda a derecha, hasta que vuelve a encontrar la fuente; es en este momento, que toma control la capa más elevada, y que básicamente se encarga de mover al robot hacia delante, mientras lo que el sensor de sonido “escuche”, sea un determinado nivel de decibeles.

El código para lograr lo anterior, se encuentra divido en tres clases: SoundFollower.java, OutOfSound.java, y DriveForward.java. La primera de ellas actúa básicamente como un regulador, para decidir qué comportamientos deben ser activados, mientras que las otras dos, aluden a los comportamientos descritos en el párrafo anterior, de manera correspondiente.

Se muestra a continuación el código de cada una de las tres clases:



SoundFollower.java



import lejos.nxt.*;
import lejos.robotics.navigation.*;
import lejos.robotics.subsumption.*;

public class SoundFollower {
 
 public static void main(String[] args){
  Pilot pilot = new TachoPilot(5.6f, 11.5f, Motor.A, Motor.C);
  SoundSensor sound = new SoundSensor(SensorPort.S3);
  pilot.setTurnSpeed(180);
  
  Behavior b1 = new OutOfSound(pilot, sound);
  Behavior b2 = new DriveForward(pilot, sound);
  Behavior [] behaviorArray = {b1, b2};
  Arbitrator arbitrator = new Arbitrator(behaviorArray);
  
  LCD.drawString("SoundFollower", 2, 2);
  Button.waitForPress();
  arbitrator.start();
 }

}

OutOfSound.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;

public class OutOfSound implements Behavior, SensorPortListener{

 private Pilot pilot;
 private SoundSensor sound;
 private int sweep;
 private boolean suppress;
 private boolean outOfSound;

 public OutOfSound(Pilot pilot, SoundSensor sound){
  this.pilot = pilot;
  this.sound = sound;
  suppress = false;
  outOfSound = false;
  SensorPort.S3.addSensorPortListener(this);
 }

 public void stateChanged(SensorPort port, int oldValue, int newValue){
  if(sound.readValue() < 40)
   outOfSound = true;
 }

 public boolean takeControl(){
  if(outOfSound){
   LCD.clear();
   LCD.drawString("OutOfSound", 2, 2);
   outOfSound = false;
   return true;
  } else
   return false;
 }

 public void suppress(){
  suppress = true;
 }

 public void action(){
  sweep = 10;
  while(!suppress){
   pilot.rotate(sweep, true);
   while(!suppress && pilot.isMoving())
    Thread.yield();
   sweep*=-2;
  }
  pilot.stop();
  suppress = false;
 }
}

DriveForward.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;

public class DriveForward implements Behavior, SensorPortListener{
 
 private Pilot pilot;
 private SoundSensor sound;
 private boolean heardSound;
 
 public DriveForward(Pilot pilot, SoundSensor sound){
  this.pilot = pilot;
  this.sound = sound;
  heardSound = false;
  SensorPort.S3.addSensorPortListener(this);
 }
 
 public void stateChanged(SensorPort port, int oldValue, int newValue){
  if(sound.readValue() >= 40)
   heardSound = true;  
 }
 
 public boolean takeControl(){
  if(heardSound){
   LCD.clear();
   LCD.drawString("DriveForward", 2, 2);
   heardSound = false;
   return true;
  } else
   return false;
 }
 
 public void suppress(){
  pilot.stop();
 }
 
 public void action(){
  pilot.forward();
  while(sound.readValue() >= 40)
   Thread.yield();
 }

}

Finalmente, se adjunta un video, donde se puede apreciar al robot ejecutando la tarea encomendada:



Mindstorms NXT - LeJOS (Seguir una línea negra)

El objetivo de este ejercicio consiste en programar al robot, para que utilizando el sensor de luz, pueda encontrar, y seguir una trayectoria marcada con una línea negra, pintada sobre una superficie plana.


Se muestra a continuación un par de fotografías que ilustran el modelo de robot utilizado, para la resolución del problema:



La siguiente fotografía corresponde a la trayectoria (marcada por una línea negra) a seguir por el robot:


Solución

En general, para los cuatro ejercicios presentados, como parte de la práctica, se utilizó para programar al robot, una arquitectura conocida como: “subsumption architecture”, la cual consiste básicamente en una arquitectura para robots reactivos, altamente asociada con robótica basada en comportamientos.

En la arquitectura mencionada, se descompone comportamiento inteligente en varios módulos “simples” de comportamiento, los cuales son organizados en capas. Cada capa implementa una meta en particular del agente, y capas más altas, son incrementalmente abstractas. La meta de cada capa subsume la de capas que se encuentran por debajo de ésta. En el caso preciso de este ejercicio la capa más baja representa el estar “fuera de la línea”, donde el robot al encontrarse en esta situación, comienza a hacer giros incrementales, de izquierda a derecha, hasta que se encuentra con una línea negra; es en este momento, que toma control la capa más elevada, y que básicamente se encarga de mover al robot hacia delante, mientras lo que el sensor de luz “vea”, sea una línea negra.

El código para lograr lo anterior, se encuentra divido en tres clases: LineFollower.java, OutOfLine.java, y DriveForward.java. La primera de ellas actúa básicamente como un regulador, para decidir qué comportamientos deben ser activados, mientras que las otras dos, aluden a los comportamientos descritos en el párrafo anterior, de manera correspondiente.

Se muestra a continuación el código de cada una de las tres clases:

LineFollower.java
import lejos.nxt.*;
import lejos.robotics.navigation.*;
import lejos.robotics.subsumption.*;

public class LineFollower {
 
 public static void main(String[] args){
  Pilot pilot = new TachoPilot(5.6f, 11.25f, Motor.A, Motor.C);
  LightSensor light = new LightSensor(SensorPort.S3);
  pilot.setTurnSpeed(180);
  
  Behavior b1 = new OutOfLine(pilot, light);
  Behavior b2 = new DriveForward(pilot, light);
  Behavior [] behaviorArray = {b1, b2};
  Arbitrator arbitrator = new Arbitrator(behaviorArray);
  
  LCD.drawString("Line Follower", 2, 2);
  Button.waitForPress();
  arbitrator.start();
 }

}

OutOfLine.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;

public class OutOfLine implements Behavior, SensorPortListener{

 private Pilot pilot;
 private LightSensor light;
 private int sweep;
 private boolean suppress;
 private boolean outOfBlackLine;

 public OutOfLine(Pilot pilot, LightSensor light){
  this.pilot = pilot;
  this.light = light;
  suppress = false;
  outOfBlackLine = false;
  SensorPort.S3.addSensorPortListener(this);
 }

 public void stateChanged(SensorPort port, int oldValue, int newValue){
  if(light.readValue() > 35)
   outOfBlackLine = true;
 }

 public boolean takeControl(){
  if(outOfBlackLine){
   LCD.clear();
   LCD.drawString("OutOfLine", 2, 2);
   outOfBlackLine = false;
   return true;
  } else
   return false;
 }

 public void suppress(){
  suppress = true;
 }

 public void action(){
  sweep = 10;
  while(!suppress){
   pilot.rotate(sweep, true);
   while(!suppress && pilot.isMoving())
    Thread.yield();
   sweep*=-2;
  }
  pilot.stop();
  suppress = false;
 }
}

DriveForward.java

import lejos.nxt.*;
import lejos.robotics.subsumption.*;
import lejos.robotics.navigation.*;

public class DriveForward implements Behavior, SensorPortListener{
 
 private Pilot pilot;
 private LightSensor light;
 private boolean seenBlackLine;
 
 public DriveForward(Pilot pilot, LightSensor light){
  this.pilot = pilot;
  this.light = light;
  seenBlackLine = false;
  SensorPort.S3.addSensorPortListener(this);
 }
 
 public void stateChanged(SensorPort port, int oldValue, int newValue){
  if(light.readValue() <= 35)
   seenBlackLine = true;  
 }
 
 public boolean takeControl(){
  if(seenBlackLine){
   LCD.clear();
   LCD.drawString("DriveForward", 2, 2);
   seenBlackLine = false;
   return true;
  } else
   return false;
 }
 
 public void suppress(){
  pilot.stop();
 }
 
 public void action(){
  pilot.forward();
  while(light.readValue() <= 35)
   Thread.yield();
 }

}

Finalmente, se adjunta un video, donde se puede apreciar al robot ejecutando la tarea encomendada, en la pista anteriormente mostrada: