M

ANUAL BÁSICO LEGO MINDSTORMS EDUCATION






PROYECTO DE ELABORACIÓN DE MATERIALES CURRICULARES Y RECURSOS DIDÁCTICOS


ROBÓTICA EDUCATIVA


LA RINCONADA ( SEVILLA )


C

CONSEJERÍA DE EDUCACIÓN DE LA JUNTA DE ANDALUCÍA

URSO 2009-2010

INDICE:

  1. Introducción.

    1. Robótica educativa con LEGO.

    2. Metodología de trabajo con MINDSTORM EDUCATION.

    3. Metodología en el aula de Tecnología.

  1. Entorno.

  2. Montaje.

  3. Programación.

    1. Introducción.

    2. Tu primer programa.

    3. Construyendo un Robot.

    4. Ejecutando el bricx command center.

    5. Ejecutando el programa.

    6. Un programa más interesante.

    7. Usando variables.

    8. Estructuras de control.

    9. Sensores.

    10. Funciones y subrutinas.

  4. Conclusiones Finales.



  1. Introducción.

    1. Robótica educativa con LEGO.


Desde hace unos años la Tecnología en la ESO se ha convertido en una materia que hace que el alumnado se motive y desarrolle una serie de competencias que hasta hace unos años eran casi impensables.

Con la Robótica educativa con LEGO el alumnado descubre la ciencia y la tecnología de una forma divertida y motivadora para plantearse su futuro unido a estas nuevas tecnologías.

El alumnado pueden diseñar, construir, programar y poner a prueba a los robots. El trabajo en equipo hacen que la comunicación entre ellos se desarrolle y las estrategias de resolución de problemas sean herramientas de uso cotidiano.

Para ello contamos con el denominado Ladrillo NXT, una serie de sensores, servomotores, y diferentes piezas de construcción de lego que permiten al alumnado desarrollar totalmente su imaginación.

Además se usa un entorno de trabajo basado en el software LabView que es un estándar en la Industria para aplicaciones de ingeniería e investigación.


    1. Metodología de trabajo con MINDSTORM EDUCATION.


La metodología de trabajo se basará principalmente en el método de proyecto-construcción aunque se podrá usar la metodología de análisis en algún caso.


Al comienzo se procederá con la siguiente secuencia:


  1. Analizar y construir. En las prácticas se proporcionan instrucciones específicas para construir un robot.

  2. Programar. A continuación se deberá programar el robot usando el software de Lego o el lenguaje de programación NXC.

  3. Ensayo y ajuste. Se realizarán pruebas de funcionamiento hasta que se consiga el efecto deseado.



    1. Metodología en el aula de Tecnología.


La forma de trabajar en el aula de tecnología será:


  1. Se formarán grupos de alumnos/as que tendrán asignado un robot junto con todos sus componentes.

  2. El alumnado será responsable del robot asignado.

  3. Si la actitud del alumnado no es la adecuada se desarrollarán estos contenidos de manera teórica.

  4. Cada grupo elaborará una ficha, en cada práctica, donde se detallan todos los pasos seguidos hasta alcanzar el objetivo planteado.

  5. Cada grupo recogerá el robot y todas las piezas, 5 minutos antes de que finalice la clase, que se guardará en los armarios habilitados para ello.

  6. El profesor revisará que no falte nada de cada grupo.

  7. Se elegirá un alumno/a responsable que será quien guardará el material y revisará que no falte nada por recoger.

  8. La salida del aula se realizará cuando el profesor/a lo ordene.


  1. Entorno.


Para utilizar el entorno de trabajo de LEGO Mindstorm necesitamos instalar el software que acompaña el Robot. Para ello usaremos un equipo con window´s o MacOS. Los requerimientos de la versión actual es la 2.0 introduciendo además del entorno de programación un entorno para hacer medidas de las diferentes magnitudes que se pueden medir con los sensores del robot.

Necesitamos un equipo con los siguientes requisitos:

Mínimo de 512 MB de RAM

Hasta 700 MB de espacio disponible en el disco duro

Proyector XGA (1024 x 768)

1 puerto USB disponible

En los ultraportátiles no se puede usar ya que su configuración de pantalla es 800x600 y no sale una parte importante de la ventana de trabajo.

Usamos el CD de instalación para instalar el software y el aspecto de la pantalla es el siguiente:



Donde tenemos las siguientes opciones:


  1. Hacer clic en la Introducción. En este caso aparece un videotutorial del uso del Robot.

  2. Hacemos clic en Presentación del software. En este caso aparece un videotutorial de los controles del programa.


  1. Hacemos clic en el Robot Educador para comenzar a realizar las distintas prácticas que nos plantea por defecto el programa.

  2. Hacemos clic en Nuevo programa o “Archivo + Nuevo” y realizamos una programación por nuestra cuenta.




  1. Montaje.


Para la realización de los montajes se puede utilizar un pequeño manual que trae el kit NXT de Lego o el Robot Educador que tiene los montajes del manual y algunos que no aparecen en el manual.

Así el listado de montajes es el siguiente:




Además tenemos disponible una serie de montajes extras que podemos descargar de la página robo-tica.


Además esta versión dispone de una aplicación para la medición de magnitudes con las siguientes aplicaciones:



  1. Programación.


Para comenzar la programación del robot de lego usaremos el manual que trae el kit y realizaremos una programación manual con el ladrillo NXT de lego. Esto nos da poco margen de maniobra ya que solo podremos hacer 4 acciones distintas.


Lego Mindstorms incluye un entorno de programación llamado NXT-G, similar al Labview, que es bastante útil para comenzar a desarrollar programas sencillos, pero que resulta poco manejable cuando intentamos hacer que el robot realice tareas un poco más complejas. Además necesitas un ordenador con Window´s.


Para realizar una programación avanzada usaremos el lenguaje NXC que a continuación desarrollaremos.



    1. Introducción ( NXC ).


NXC es un lenguaje de programación diseñado por John Hansen específicamente para los robots de Lego, basado en el lenguaje C, que es muy conocido y utilizado por los programadores. Las ventajas de usar NXC son que por un lado se trata de un lenguaje muy sencillo que puedes comenzar a utilizar aunque nunca hayas programado antes, y que además aprendiendo NXC estás familiarizándote con la estructura del lenguaje C, lo cual te será muy útil si en el futuro quieres aprender algo más sobre programación en general.


Para programar en NXC, existe el Brix Command Center (BricxCC), un aplicación que te permitirá escribir tus programas y descargarlos al robot, arrancarlos, detenerlos, moverte por la memoria flash del robot, convertir ficheros de sonido a un formato propio del robot y mucho más. El aspecto del BricxCC es el de un procesador de textos, pero con algunos extras. En este manual vamos a hacer una introducción al uso de BrixCC, en verdión 3.3.7.16 o superior. La aplicación la puedes descargar en:


http://bricxcc.sourceforge.net/


Existen otras plataformas para usar NXC que puedes ver en:


http://bricxcc.sourceforge.net/nxc/


Y esta es la página web del autor del manual en inglés:


http://daniele.benedettelli.com


    1. Tu primer programa.


En lugar de explicar la teoría sobre la programación y luego hacer algún ejemplo, vamos a ir viendo ejemplos sencillos e introduciendo la teoría necesaria para cada ejemplo, de este modo te resultará más sencillo comenzar desde el principio, sobre todo si no sabes nada sobre programación.


El primer ejemplo que vamos a ver consistirá en que el robot avance 4 segundos hacia adelante, 4 segundos hacia atrás y se pare. Es muy sencillo, pero de eso se trata, de que comiences a programar con algo muy fácil.


    1. Construyendo un Robot.


El robot que vamos a utilizar para empezar es el tribot, que es el primero que aparece en el manual que viene con el NXT. Pero en esta ocasión conectaremos el motor derecho al puerto A, el izquierdo al puerto C. Si no tienes acceso a las instrucciones para montar el tripod, vale cualquier robot que se traslade mediante dos ruedas movidas por dos motores.



Asegúrate de que tienes bien instalados los drivers del NXT.



    1. Ejecutando el bricx command center.


Ejecuta el Brixc Command Center haciendo doble click en el icono BricxCC que aparece cuando los has instalado después de haberlo descargado de la dirección que aparece en la introducción.


El programa te preguntará donde localizar el robot. Conecta el robot y pulsa OK. Entonces el programa encontrará automáticamente el robot. Ahora el interface aparecerá como se muestra en la figura 1. (Sin el texto)


Figura 1: Interface de Bricc Command Center


El interface presenta el aspecto de un procesador de texto, pero contiene un menú especial para compilar (traducir el programa al lenguaje que entiende el robot), descargar programas al robot y obtener información del robot.


Vamos a escribir un nuevo programa. Para ello pulsa New file en el menú file y teclea o copia el siguiente programa:


task main()

{

OnFwd(OUT_A, 75);

OnFwd(OUT_C, 75);

Wait(4000);

OnRev(OUT_AC, 75);

Wait(4000);

Off(OUT_AC);

}


Aunque al principio pueda parecer algo complejo, vamos a analizarlo poco a poco para que lo entiendas bien.


Un programa en NXC se construye a base de funciones. Una función (task) es un conjunto de instrucciones que se ejecutan juntas y dan un resultado. Por ejemplo, podemos crear una función para dividir dos números y llamarla divide. Creamos esa función y escribimos por ejemplo a = divide (8,2). 8 y 2 serán los parámetros de la función, y ésta dará como resultado cuatro, si es que hemos construido la función de modo que el resultado de la misma sea la división entre el primer parámetro y el segundo.


Todo programa en NXC debe tener al menos una función llamada main, que es la primera que se ejecuta. Luego la función main podrá llamar a otras funciones o no. Las instrucciones de la función estarán siempre limitadas por llaves. Así vemos claramente dónde empieza y donde acaba la función. Cada instrucción terminará siempre con un punto y coma. De modo que nuestro programa será siempre de esta manera:


task main()

{

instrucción1;

instrucción2;

}


Nuestro programa tiene seis instrucciones. Vamos a analizarlas una a una:


OnFwd(OUT_A, 75);


Esta instrucción le dice al robot que ponga en marcha la salida A, es decir, la salida que tiene escrita una letra A en el ladrillo del NXT, y le dice que se mueva hacia adelante. Si quisiéramos que se moviera hacia atrás, usaríamos la instrucción OnRev. Hay que tener en cuenta que NXC distingue entre mayúsculas y minúsculas, así que hay que tener la precaución de escribir las instrucciones con la palabra exacta. El número 75 indica que el motor se moverá con el 75% de su velocidad máxima.


OnFwd(OUT_C, 75);


Es la misma instrucción, pero esta vez moverá el motor conectado a la salida C. En este momento el robot se moverá hacia adelante dado que se están moviendo los dos motores.


Wait(4000);


Ahora le estamos diciendo al robot que no haga nada durante 4000 milisegundos. Las salidas que fueron activadas siguen estándolo durante este tiempo, es decir, el robot se moverá hacia adelante durante cuatro segundos.


OnRev(OUT_AC, 75);


Ahora le estamos diciendo al robot que ande hacia atrás al 75% de la velocidad máxima. Estamos usando una instrucción un poco diferente que la primera: estamos activando las salidas A y C al mismo tiempo. Del mismo modo podríamos sustituir las dos primeras instrucciones por OnFwd(OUT_AC, 75);


Wait(4000);


Nuevamente esperamos cuatro segundos


Off(OUT_AC);


Y finalmente detenemos ambos motores.


En resumen, el programa mueve el motor 4 segundos hacia adelante con el 75% de su velocidad máxima, cuatro segundos hacia atrás y se detiene.


Los colores con los que están escritas las instrucciones los crea automáticamente NXC y varían en función de que el texto sea una instrucción, un parámetro, etc. Son útiles porque nos ayudan a detectar errores en nuestro código. Por ejemplo, si escribimos mal OnRev, no aparecerá en azul porque lo que escribamos no corresponderá a una instrucción válida en NXC. Se puede configurar NXC para modificar estos colores.


    1. Ejecutando el programa.


una vez has escrito el programa, éste debe ser compilado, es decir traducido a código binario que el robot pueda entender y ejecutar, y enviado al robot usando el cable USB o BT.



En la imagen anterior puedes ver los botones que, de izquierda a derecha te permiten compilar, descargar, ejecutar y detener el programa. Presiona el segundo botón y, si no has cometido errores al escribir el programa, éste será compilado y descargado al robot . Si has cometido errores, éstos te serán notificados.


Ahora puedes ejecutar el programa. Para ello ve a MyFiles en el menú del NXT , Software files y run. Recuerda que el programa que aparecerá en el NXT tendrá el mismo nombre que le hayas dado en NXC. También puedes ejecutar directamente el fichero que acabas de descargar pulsando CTRL+F5 o bien pulsando el botón verde de run.


Si el robot no hace lo esperado, comprueba las conexiones.


Errores en el programa


Cuando escribas programas hay una posibilidad razonable de que cometas algunos errores. El compilador nota los errores y te informa en la parte inferior de la ventana como en la figura siguiente:



Automáticamente se selecciona el primer error (tecleamos mal el nombre del motor). Cuando hay errores, puedes hacer clic en el mensaje de error y te dirigirá hacia ellos. A veces los errores en una parte del programa provocan errores en otras, por eso es bueno corregir los primeros errores y volver a compilar el programa.


También hay que tener en cuenta que los colores en que se divide el programa ayudan a evitar errores ya que si tecleamos mal una palabra, ésta no aparecerá en el color que debe.


Hay errores que no son detectados por el compilador. Por ejemplo, si nos equivocamos al teclear y seleccionamos un motor en vez del otro, el programa hará funcionar el motor erróneo, pero el compilador no mostrará ningún mensaje de error.


Cambiando la velocidad


Como habrás notado, el robot se mueve más bien rápido. Para cambiar la velocidad simplemente cambias el segundo parámetro entre paréntesis. La potencia es un número entre cero y cien, siendo cero parado. Aquí hay una nueva versión del programa en la cual el motor se mueve más lento:


task main()

{

OnFwd(OUT_AC, 30);

Wait(4000);

OnRev(OUT_AC, 30);

Wait(4000);

Off(OUT_AC);

}


Conclusiones:


En este capítulo has escrito tu primer programa en NXC, usando BricxCC. Ahora deberías saber cómo escribir un programa, cómo descargarlo al robot y como hacer que éste lo ejecuto. BricxCC puede hacer muchas más cosas. Para enterarte, lee el manual que viene con él. Éste tutorial te enseñará nada más que lo que necesitas para manejar NXC.


En primer lugar has aprendido que todo programa tiene una instrucción llamada main que siempre es ejecutada por el robot. Has aprendido también los cuatro comandos básicos para manejar el motor: OnFwd(), OnRev() and Off(). Por último has aprendido sobre la instrucción Wait().



    1. Un programa más interesante.


Nuestro primero programa no era muy fascinante, pero vamos a intentar hacer uno un poco más interesante. Vamos a hacer una introducción a algunos aspectos de la programación en NXC.

Girando:

Podemos hacer girar al robot deteniendo o invirtiendo el giro de uno de los dos motores. Aquí hay un ejemplo. Escríbelo, descárgalo al robot y ejecútalo. Debería andar un poco y después dar un giro de 90 grados a la derecha.


task main()

{

OnFwd(OUT_AC, 75);

Wait(800);

OnRev(OUT_C, 75);

Wait(360);

Off(OUT_AC);

}


A lo mejor deber intentar con algunos números diferentes de 360 en el segundo wait para conseguir un giro de 90. Dependerá de la superficie por la cual corre el robot. Mejor que utilizar un número es mejor usar una palabra para ese parámetro, En NXC se pueden definir constantes como se muestra en el siguiente programa:


#define MOVE_TIME 1000

#define TURN_TIME 360

task main()

{

OnFwd(OUT_AC, 75);

Wait(MOVE_TIME);

OnRev(OUT_C, 75);

Wait(TURN_TIME);

Off(OUT_AC);

}


Las dos primeras líneas definen dos constantes que ahora pueden ser usadas a lo largo del programa. Definir constantes es bueno por dos motivos: hacen el programa más legible y es más fácil modificar los parámetros.


Repitiendo comandos:


Intentemos escribir un programa que haga que el robot describa un cuadrado. Esto supone moverse hacia delante, girar 90 grados, volver a girar, etc. Podemos repetir la serie de comandos anteriores cuatro veces pero e mejor usar la instrucción repeat


#define MOVE_TIME 500

#define TURN_TIME 500

task main()

{

repeat(4)

{

OnFwd(OUT_AC, 75);

Wait(MOVE_TIME);

OnRev(OUT_C, 75);

Wait(TURN_TIME);

}

Off(OUT_AC);

}


El número que aparece dentro de la instrucción repeat indica cuantas veces se ejecutará el código que aparece entre llaves. Date cuenta que se han tabulado las instrucciones. Esto no es necesario, pero hace los programas más legibles. Estos consejos para hacer más legibles los programas no tienen mucha importancia para programas muy sencillos, pero son muy importantes en programas de mayor complejidad, sobre todo cuando intentamos buscar un error o en general darnos cuenta de por qué nuestro programa no funciona como quisiéramos, lo cual es muy habitual.


Como ejemplo final veamos un programa que hace que el robot describa diez veces un cuadrado. Este es el programa:


#define MOVE_TIME 1000

#define TURN_TIME 500

task main()

{

repeat(10)

{

repeat(4)

{

OnFwd(OUT_AC, 75);

Wait(MOVE_TIME);

OnRev(OUT_C, 75);

Wait(TURN_TIME);

}

}

Off(OUT_AC);

}


Ahora hay una instrucción repeat dentro de otra. Decimos que son instrucciones repeat anidadas. Puedes hacer esto tanto como quieras, pero debes tener cuidado con las llaves. El primer repeat comienza en la segunda llave y acaba en la penúltima mientras que el segundo empieza en la tercera y acaba en la cuarta. Las llaves siempre van en parejas.


Para aclararte con las llaves es muy importante tabular correctamente y situar cada instrucción en la profundidad correspondiente.


Añadiendo comentarios

Para hacer tu programa aún más legible, es bueno añadir algunos comentarios. Hacer los programas legibles es bueno, no sólo para localizar mejor aquellos errores que puedan ir surgiendo y en general facilitar la labor de escribir un programa, sino que es posible que quieras hacer un programa modificando un programa anterior, y es posible también que ese programa anterior lo hayas hecho hace tiempo y cuando lo quieras modificar no lo entiendas. También es posible que un programa que hagas tú, tenga que modificarlo otra persona, y por lo tanto es bueno que lo entienda.


Cada vez que colocas // en una línea, el resto de esa línea es ignorada y puede ser utilizada como comentario. Un comentario largo de varias líneas puede colocarse entre /* y */ Los comentarios se destacan en BricxCC. El programa completo sería el siguiente:


/* 10 SQUARES

This program make the robot run 10 squares

*/

#define MOVE_TIME 500 // Time for a straight move

#define TURN_TIME 360 // Time for turning 90 degrees

task main()

{

repeat(10) // Make 10 squares

{

repeat(4)

{

OnFwd(OUT_AC, 75);

Wait(MOVE_TIME);

OnRev(OUT_C, 75);

Wait(TURN_TIME);

}

}

Off(OUT_AC); // Now turn the motors off

}

Conclusiones:


En este capítulo has aprendido el uso de la instrucción repeat y de los comentarios. También la función de las llaves anidadas y la utilidad de los tabuladores.


Con esto ya puedes hacer que el robot se mueva por toda serie de caminos. Es un buen ejercicio hacer variaciones sobre este programa antes de seguir con el siguiente capítulo.


    1. Usando variables.


Las variables son un aspecto muy importante de cualquier lenguaje de programación. Son localizaciones de memoria en las cuales almacenamos un valor. Podemos usar ese valor en cualquier parte del programa y cambiarlo. Vamos a ver el uso de las variables mediante un ejemplo.


Movimiento en espiral


Supongamos que queremos modificar el programa anterior de modo que el robot se mueva en espiral. Podemos conseguir esto aumentando cada vez el tiempo de pausa, el parámetro MOVE_TIME.


En el programa anterior MOVE_TIME es una constante y no puede ser modificada, por tanto debemos en este caso usar una variable. Este sería el programa espiral:


#define TURN_TIME 360

int move_time; // definelaa variable

task main()

{

move_time = 200; // valor inicial

repeat(50)

{

OnFwd(OUT_AC, 75);

Wait(move_time); // uso de la variable

OnRev(OUT_C, 75);

Wait(TURN_TIME);

move_time += 200; // incremento de la variable

}

Off(OUT_AC);

}


las líneas interesantes están comentadas. Definimos la variable por medio de la palabra int seguido del nombre elegido para ésta (Normalmente minúsculas para variables y mayúsculas para constantes, pero no es necesario. Nuevamente se trata de un criterio para hacer el programa más legible). El nombre debe empezar por una letra, pero puede contener números y el guión bajo. No se permiten otros símbolos. Tampoco se permite la letra eñe. El mismo criterio se sigue para constantes, nombres de funciones, etc.


La palabra int sirve para variables que contengan números enteros. En este caso tomará los valores 200, 400, 600, etc.


Además de incrementar el valor de una variable, podemos multiplicarla por un número u otra variable con *=, restar con -= y dividir con /=. En este caso de la división, el resultado será el entero más cercano


El siguiente ejemplo no provoca ninguna acción en el robot porque no sabemos hacer que éste muestre los valores de una variable.


int aaa;

int bbb,ccc;

int values[];

task main()

{

aaa = 10;

bbb = 20 * 5;

ccc = bbb;

ccc /= aaa;

ccc -= 5;

aaa = 10 * (ccc + 3); // aaa is now equal to 80

ArrayInit(values, 0, 10); // allocate 10 elements = 0

values[0] = aaa;

values[1] = bbb;

values[2] = aaa*bbb;

values[3] = ccc;

}


Fíjate que en las primeras dos líneas podemos definir múltiples variables en una línea. Incluso podríamos haber combinado las tres líneas en una sola. Los conjuntos de variables que se identifican mediante un número se denominan arrays. Ese número se indica mediante corchetes. Los arrays se declaran como en este ejemplo:


Int values[];


Value será el nombre del array. Entonces la siguiente línea asigna diez elementos al array y los inicializa, es decir, les da valor inicial, a cero


ArrayInit(values, 0, 10);


Números aleatorios


En los anteriores programas hemos definido exactamente lo que queremos que el robot haga. Algunas veces resulta interesante no saber del todo lo que éste va a hacer. Queremos cierta aleatoriedad en el movimiento.


En NXC se pueden crear números aleatorios. El siguiente programa hace que el robot se mueva de forma aleatoria. Constantemente se mueve hacia adelante un número aleatorio de segundos y luego hace un giro de un número aleatorio de grados:


int move_time, turn_time;

task main()

{

while(true)

{

move_time = Random(600);

turn_time = Random(400);

OnFwd(OUT_AC, 75);

Wait(move_time);

OnRev(OUT_A, 75);

Wait(turn_time);

}

}


El programa define dos variables, y entonces les asigna números aleatorios. Random(600) significa un número aleatorio entre cero y 600 (El valor máximo, 600 en este caso, no se tomará en ningún caso). Cada vez que se ejecute este programa, la secuencia de números generadas por la función random será diferente, de manera que el movimiento del robot será imprevisible.


Podemos eludir el uso de variables escribiendo directamente Wait(Random(600)).


También hemos visto un tipo diferente de bucle: en lugar de usar la instrucción repeat, hemos estrito while(true). La instrucción while repite las instrucciones entre llaves que le siguen mientras la condición escrita entre paréntesis sea cierta. La palabra especial true es siempre cierta, digamos que equivale a una condición cierta, por lo tanto las instrucciones entre llaves se repetirán indefinidamente, o al menos hasta que se pulse el botón gris del NXT.



Conclusiones:



En este capítulo has aprendido el uso de variables y arrays. Puedes declarar otros tipos de datos diferentes de int: short, long, byte, bool y string.


También has aprendido a crear números aleatorios, de manera que puedas producir en el robot un comportamiento aleatorio. Por último hemos visto el uso de la función while para generar un bucle que continúe ejecutándose indefinidamente.



    1. Estructuras de control.


En los capítulos anteriores vimos las sentencias repeat y while. Esas sentencias controlan la manera en que se ejecutan otras sentencias. Se llaman estructuras de control. En este capítulo vamos a ver algunas de ellas.


La sentencia if:


A veces queremos que una parte de nuestro programa se ejecute solamente en ciertas situaciones. En esos casos se usa la sentencia if. Vamos a ver un ejemplo. Vamos a modificar el programa con el que hemos estado trabajando, pero queremos que gire bien a la derecha o a la izquierda, y que haga esa elección de modo aleatorio. Elegiremos al azar un número que puede ser positivo o negativo, y si es positivo el robot girará a la derecha y si no, girará hacia la izquierda.


Éste sería el programa:


#define MOVE_TIME 500

#define TURN_TIME 360

task main()

{

while(true)

{

OnFwd(OUT_AC, 75);

Wait(MOVE_TIME);

if (Random() >= 0)

{

OnRev(OUT_C, 75);

}

else

{

OnRev(OUT_A, 75);

}

Wait(TURN_TIME);

}

}


La sentencia if se parece a la sentencia while. Si la condición entre paréntesis es cierta, se ejecuta la parte entre paréntesis, si no lo es, se ejecutará la parte detrás de la sentencia else.


Prestemos atención a la condición que hemos usado: Random() >= 0, esto significa que Random() debe ser igual o mayor que cero para que la condición sea cierta. Se pueden comparar los valores de otras maneras, aquí vemos las más importantes:

== igual a

< menor que

<= menor o igual a

>= mayor o igual a

¡= no igual a


Se pueden combinar condiciones usando &&, que significa “y” o “||” que significa “o”. Veamos algunos ejemplos:


true siempre cierto

false nunca cierto

ttt!=3 cierto si ttt distinto de 3

(ttt<=5)&&(ttt<=10) cierto si ttt está entre 5 y 10

(aaa==10) || (bbb == 10) cierto si aaa o bbb (o los dos) son iguales a 10


Fíjate que la sentencia if tiene dos partes. La parte inmediatamente después de la condición, que se ejecuta cuando la condición es cierta, y la parte después del else, que se ejecuta si la condición es falsa. La palabra clave else y la parte que le sigue son opcionales, de manera que puedes omitirlas si no hay nada que hacer en caso de que la condición sea falsa.


La sentencia do

Hay otra estructura de control, la sentencia do. Tiene la siguiente forma:


do

{

instrucciones;

}

while (condition);


Las instrucciones entre llaves que hay después del do se ejecutan mientras la condición sea cierta. La condición tiene la misma forma que el if. Éste es un ejemplo de un programa. El robot da vueltas de modo aleatorio durante 20 segundos y después se para.


int move_time, turn_time, total_time;

task main()

{

total_time = 0;

do

{

move_time = Random(1000);

turn_time = Random(1000);

OnFwd(OUT_AC, 75);

Wait(move_time);

OnRev(OUT_C, 75);

Wait(turn_time);

total_time += move_time;

total_time += turn_time;

}

while (total_time < 20000);

Off(OUT_AC);

}


Fíjate que la sentencia do es casi igual que la sentencia while. Pero en el while, la condición se comprueba antes de entrar en las instrucciones, mientras que en el do, se comprueba al final. En el caso del while puede ocurrir que las sentencias no se ejecuten nunca, pero en el do, se ejecutarán al menos una vez.


Sumario


En este capítulo hemos visto dos estructuras de control: if y do. Junto con la sentencia repeat y while, son las sentencias que controlan cómo se ejecuta el programa. Es muy importante que comprendas su funcionamiento. Por eso es mejor que intentes hacer algunos ejemplos por tu cuenta antes de seguir adelante.


    1. Sensores.


Por supuesto, se pueden conectar sensores al NXT ara hacer que el robot reacciones a eventos externos. Antes de ver cómo hacer esto, debemos cambiar un poco el robot añadiéndole un sensor de contacto. Como antes, sigue las instrucciones del Tribot para construir un tope frontal.



Conecta el sensor de contacto a la entrada 1 del NXT


Esperando al sensor

Vamos a empezar con un programa muy simple en el que el robot se mueve hacia adelante hasta que golpea algo. Aquí está:


task main()

{

SetSensor(IN_1,SENSOR_TOUCH);

OnFwd(OUT_AC, 75);

until (SENSOR_1 == 1);

Off(OUT_AC);

}


Aquí hay dos líneas importantes. La primera línea del programa le dice al robot qué tipo de sensor estamos usando

IN_1 es el número de la entrada a la que conectamos el sensor. La otra entrada de sensor se llama IN_2, IN_3 e IN_4. SENSOR_TOUCH indica que se trata de un sensor de contacto. Para el sensor de luz usamos SENSOR_LIGHT.

Después de especificar el tipo de sensor, el programa activa los dos motores y el robot se comienza a mover hacia delante. La siguiente sentencia es muy útil. Espera a hasta que la condición entre paréntesis sea cierta. En este caso la condición es que el valor del sensor SENSOR_1 sea 1, lo cual indica que el sensor está pulsado. Mientras no esté pulsado, su valor es cero. De este modo, la instrucción espera a que se pulse el sensor. Cuando esto ocurre, desactivamos los motores y el programa termina.


Actuando sobre el sensor de contacto


Vamos a intentar que nuestro robot sortee obstáculos. Cada vez que el robot golpee un obstáculo, haremos que vuelva un poco hacia atrás, gire y después continúe. Éste es el programa:


task main()

{

SetSensorTouch(IN_1);

OnFwd(OUT_AC, 75);

while (true)

{

if (SENSOR_1 == 1)

{

OnRev(OUT_AC, 75); Wait(300);

OnFwd(OUT_A, 75); Wait(300);

OnFwd(OUT_AC, 75);

}

}

}


Como en el ejemplo anterior, primero indicamos el tipo de sensor. Después el robot se comienza a mover hacia delante. En el bucle while infinito, constantemente se está comprobando si se toca el sensor, y si es así, se mueve hacia atrás 300 ms, gira hacia la izquierda y sigue adelante otra vez.


Sensor de luz


Además del sensor de contacto, el NXT contiene un sensor de luz, un sensor de sonido y un sensor de ultrasonidos digital. El sensor de luz puede configurarse para emitir luz o no. De este modo se puede medir la cantidad de luz ambiente o reflejada desde una determinada dirección. Medir la luz reflejada es útil por ejemplo cuando se quiere hacer que el robot siga una línea de un determinado color dibujada en el suelo. Esto es lo que vamos a hacer en el siguiente ejemplo. Para continuar con el ejemplo, en primer lugar deberás construir el Tribot. Conecta el sensor de luz en la entrada 3, sensor de sonido en el 2 y el de ultrasonidos en el 4, tal como se indica en las instrucciones.




También necesitaremos la alfombrilla de pruebas con el trazo negro que viene con el NXT. El principio básico de los programas seguidores de línea es que el robot intenta mantenerse en el borde de la misma, alejándose de la línea si la lectura del sensor de luz indica un nivel demasiado bajo (sensor dentro de la línea) y acercándose a la línea si la lectura del sensor es demasiado alta, indicando que el sensor está fuera de la línea.


El siguiente es un programa seguidor de línea muy simple con un único valor de umbral.


#define THRESHOLD 40

task main()

{

SetSensorLight(IN_3);

OnFwd(OUT_AC, 75);

while (true)

{

if (Sensor(IN_3) > THRESHOLD)

{

OnRev(OUT_C, 75);

Wait(100);

until(Sensor(IN_3) <= THRESHOLD);

OnFwd(OUT_AC, 75);

}

}

}


En primer lugar el programa configura el puerto 3 como sensor de luz. Luego mueve el robot hacia adelante y entra en un bucle infinito. El programa hace que el robot se mueva hacia adelante siempre que el valor de luz sea suficientemente bajo, es decir, mientras estemos situados sobre la línea. Cuando que el valor de luz sea mayor que 40 (usamos una constante de manera que pueda ser adaptada fácilmente, porque depende mucho de los valores de la luz ambiente) giramos un motor y esperamos hasta que estamos otra vez en la línea.


Como podrás ver cuando ejecutes el programa, el movimiento no es muy suave. Intenta añadir un Wait(100) antes del until para hacer que el robot se mueva mejor. Date cuenta que el robot no funciona para movimientos en el sentido contrario a las agujas del reloj. Para eso es necesario un programa bastante más complicado.


Para leer la luz ambiente con el led apagado, configura el sensor de la siguiente manera:


SetSensorType(IN_3,IN_TYPE_LIGHT_INACTIVE);

SetSensorMode(IN_3,IN_MODE_PCTFULLSCALE);

ResetSensor(IN_3);


Sensor de sonido


Usando un sensor de sonido puedes hacer que el NXT detecte palmadas. Vamos a escribir un programa que espera que se produzca un sonido fuerte, y mueve el robot hasta que se detecta otro sonido. Conecta el sensor de sonido al puerto 2, como se describe en las instrucciones de montaje del Tribot


#define THRESHOLD 40

#define MIC SENSOR_2

task main()

{

SetSensorSound(IN_2);

while(true){

until(MIC > THRESHOLD);

OnFwd(OUT_AC, 75);

Wait(300);

until(MIC > THRESHOLD);

Off(OUT_AC);

Wait(300);

}

}


En primer lugar definimos una constante de umbral (threshold) y un alias para SENSOR_2; en la función main configuramos el puerto 2 para leer datos desde el sensor de sonido y iniciamos un bucle sin fin.


Usando la sentencia until, el programa espera hasta que el nivel de sonido es mayor que el definido en el threshold que hemos elegido: date cuenta que SENSOR_2 no es sólo un nombre, sino una macro que devuelve el valor de sonido desde el sensor.

Si un sonido fuerte tiene lugar, el robot comienza a moverse en línea recta hasta que otro sonido lo detiene.


La sentencia wait se ha introducido porque de otro modo el robot comenzaría y pararía instantáneamente: de hecho, el NXT es tan rápido que no le toma ningún tiempo ejecutar las dos líneas entre las dos sentencias until. Si pruebas a comentar los dos wait, lo entenderás mejor cuando vuelvas sobre este programa más tarde. Una alternativa a usar el until para esperar eventos es el while, es suficiente con poner entre los paréntesis la condición. Por ejemplo:


while(MIC <= THRESHOLD).


No hay mucho más que saber sobre los sensores analógicos del NXT; sólo recuerda que tanto el sensor de luz como el de sonido dan un valor entre 0 y 100.

SENSOR DE ULTRASONIDOS


El sensor de ultrasonidos funciona más o menos como un sonar, manda una ráfaga de ondas ultrasónicas y mide el tiempo que necesitan las ondas para volver al reflejarse en el objeto de delante.

Es un sensor digital, lo que quiere decir que lleva integrado un circuito para analizar y enviar los datos. Con ese sensor se puede hacer que el robot vea y evite un obstáculo antes de chocar con él como lo hace el sensor de contacto.


#define NEAR 15 //cm

task main(){

SetSensorLowspeed(IN_4);

while(true){

OnFwd(OUT_AC,50);

while(SensorUS(IN_4)>NEAR);

Off(OUT_AC);

OnRev(OUT_C,100);

Wait(800);

}

}


El programa inicializa el puerto 4 para leer datos del sensor de ultrasonidos; entonces comienza un bucle eterno donde el robot va hacia delante hasta que algo está delante a NEAR cm (15 cm en nuestro ejemplo), entonces va hacia atrás un poco y otra vez hacia delante.



Conclusiones:


En este capítulo se ha visto como trabajar con todos los sensores incluidos en el NXT. Hemos visto también como until y while son útiles para trabajar con sensores.


Te recomiendo escribir algunos programas tú mismo a este nivel. Ahora tienes bastantes conocimientos para dar a tu robot un comportamiento bastante complicado.


Intenta pasar a NXC los programas sencillos que vienen en el manual de Robocenter.


    1. Funciones y subrutinas.


Como ya se ha visto, Una función (task) es un conjunto de instrucciones que se ejecutan juntas y dan un resultado. Por ejemplo, podemos crear una función para dividir dos números y llamarla divide. Creamos esa función y escribimos por ejemplo a = divide (8,2)


Hasta ahora todos nuestros programas han tenido una sola función. Pero los programas NXC pueden tener varias funciones. También es posible crear trozos de código, llamados subrutinas, que se pueden ejecutar desde diferentes partes del programa.


Usar funciones y subrutinas hace que los programas sean más fáciles de entender y más compactos. En este capítulo veremos varias posibilidades.


Funciones:


Un programa NXC puede tener un máximo de 255 funciones; cada una de ellas con un nombre único. La función denominada main debe existir siempre, ya que es la primera que se ejecutará. El resto se ejecutará sólo en el caso de que una función que ya se esté ejecutando le diga que se ejecute


Vamos a ver un ejemplo de uso de funciones. Se trata de un programa que hace que el robot siga una trayectoria cuadrada, como antes, pero en este caso, cuando golpea un obstáculo, reacciona. Es difícil hacer esto con una sola función, porque el robot debe hacer dos cosas al mismo tiempo: dar vueltas (lo que supone encender y apagar motores a tiempo) y vigilar los sensores. Para ello es bueno usar dos funciones, una para moverse en cuadrados y la otra para reaccionar a los sensores. Este es el programa:


mutex moveMutex;

task move_square()

{

while (true)

{

Acquire(moveMutex);

OnFwd(OUT_AC, 75); Wait(1000);

OnRev(OUT_C, 75); Wait(500);

Release(moveMutex);

}

}

task check_sensors()

{

while (true)

{

if (SENSOR_1 == 1)

{

Acquire(moveMutex);

OnRev(OUT_AC, 75); Wait(500);

OnFwd(OUT_A, 75); Wait(500);

Release(moveMutex);

}

}

}

task main()

{

Precedes(move_square, check_sensors);

SetSensorTouch(IN_1);

}


La función main únicamente configura el tipo de sensor y luego llama a las otras dos funciones, añadiéndolas a una cola. Después de esto termina. La función move_square mueve el robot indefinidamente en cuadros. La función check_sensors comprueba si se ha pulsado el sensor de contacto, y en ese caso, dirige al robot fuera del obstáculo.


Es muy importante recordar que las funciones que han comenzado, están ejecutándose al mismo tiempo y esto puede producir resultados inesperados, si las dos funciones intentan mover el motor como se les ordena hacer.


Para evitar estos problemas definimos un tipo de variable extraño, mutex, (que produce una mutua exclusión): Sólo podemos actuar sobre este tipo de variables con las funciones Acquire y Release (adquiere y libera), escribiendo trozos de código críticos entre esas funciones, asegurándonos de que sólo una función cada vez puede tener el control sobre los motores.


Esas variables tipo mutex son llamadas semáforos y su técnica de programación se llama programación concurrente.


Subrutinas


Algunas veces necesitamos el mimo trozo de código en varias partes del programa. En este caso se puede poner esa parte del código en una subrutina y darle un nombre. Ahora se puede ejecutar ese trozo de código simplemente invocándolo por ese nombre desde dentro de una función. Veamos un ejemplo:



sub turn_around(int pwr)

{

OnRev(OUT_C, pwr); Wait(900);

OnFwd(OUT_AC, pwr);

}

task main()

{

OnFwd(OUT_AC, 75);

Wait(1000);

turn_around(75);

Wait(2000);

turn_around(75);

Wait(1000);

turn_around(75);

Off(OUT_AC);

}


In this program we have defined a subroutine that makes the robot rotate around its center. The main task calls

the subroutine three times. Note that we call the subroutine by writing its name and passing a numerical

argument writing it inside following parentheses. If a subroutine accepts no arguments, simply add parentheses

with nothing inside them.

So it looks the same as many of the commands we have seen.

The main benefit of subroutines is that they are stored only once in the NXT and this saves memory. But when

subroutines are short, it may be better to use inline functions instead. These are not stored separately but copied

at each place they are used. This uses more memory but there is no limit on the number of inline functions. They

can be declared as follows:


En este programa hemos definido una subrutina que hace que el robot gire sobre su centro. La función main llama a la subrutina tres veces. Date cuenta de que llamamos a la subrutina escribiendo su nombre y pasándole un valor numérico entre paréntesis. Este valor se llama argumento, la subrutina puede tenerlos o no. Si la subrutina no acepta argumentos, simplemente se añaden paréntesis sin nada dentro.


De este modo, su aspecto es el mismo que el de algunos comandos que ya hemos visto.


El principal beneficio de las subrutinas es que se almacenan sólo una vez en el NXT y ahorran memoria. Pero si la subrutina es corta, puede se mejor usar la función inline. Estas no se almacenan separadamente sino que se copian en cada parte que deben ser usada. Esto consume más memoria, pero no hay límite en el número de funciones inline. Éstas se declaran como sigue:


inline int Name( Args ) {

//body;

return x*y;

}



La definición y el uso de funciones inline va exactamente de la misma manera que las subrutinas. Por tanto, el ejemplo anterior usando funciones inline se verá así:


inline void turn_around()

{

OnRev(OUT_C, 75); Wait(900);

OnFwd(OUT_AC, 75);

}

task main()

{

OnFwd(OUT_AC, 75);

Wait(1000);

turn_around();

Wait(2000);

turn_around();

Wait(1000);

turn_around();

Off(OUT_AC);

}




En el ejemplo anterior, podemos hacer que el momento de girar sea un argumento de la función, como en el siguiente ejemplo:


inline void turn_around(int pwr, int turntime)

{

OnRev(OUT_C, pwr);

Wait(turntime);

OnFwd(OUT_AC, pwr);

}

task main()

{

OnFwd(OUT_AC, 75);

Wait(1000);

turn_around(75, 2000);

Wait(2000);

turn_around(75, 500);

Wait(1000);

turn_around(75, 3000);

Off(OUT_AC);

}




Date cuenta de que en el paréntesis de después del nombre de la función inline especificamos los argumentos de la función: En este caso indicamos que el argumento es un entero, (hay otras opciones) y que su nombre es turntime.


Cánido hay más argumentos, se separan por comas. En NXC, sub es lo mismo que void; también, las funciones pueden tener otros tipos de resultados distintos de void, pueden también devolver enteros o cadenas. Por ejemplo, si definimos la función factorial, la llamaremos de este modo a=factorial (b); Y al definir la función factorial, debemos indicar que el resultado será un entero.


Definiendo macros:


Aún hay otra manera de darle nombre a pequeños trozos de código. Se pueden definir macros en NXC (No confundir con macros en BrixCC). Hemos visto antes que podemos definir constantes, usando #define y dándoles un nombre. Pero también podemos definir trozos de código. Éste es el mismo programa otra vez, pero ahora usando una macro para girar.


#define turn_around \

OnRev(OUT_B, 75); Wait(3400);OnFwd(OUT_AB, 75);

task main()

{

OnFwd(OUT_AB, 75);

Wait(1000);

turn_around;

Wait(2000);

turn_around;

Wait(1000);

turn_around;

Off(OUT_AB);

}


Después de la instrucción #define la palabra turn_around se asimila al texto que hay detrás. Ahora cada vez que se escriba turn_around , esto se sustituye por ese texto. Date cuenta de que el texto debe estar en una línea. (En realidad hay maneras de crear un #define de más de una línea, pero no es recomendable)


La instrucción define es en realidad bastante más potente. Puede también tener argumentos. Por ejemplo, podemos poner el tiempo de giro como un argumento en esa instrucción. Aquí hay un ejemplo en el que definimos cuatro macros: una para moverse hacia adelante, otra hacia atrás, otra para girar hacia la izquierda y otra para la derecha. Cada una tiene dos argumentos: la velocidad y el tiempo.


#define turn_right(s,t) \

OnFwd(OUT_A, s);OnRev(OUT_B, s);Wait(t);

#define turn_left(s,t) \

OnRev(OUT_A, s);OnFwd(OUT_B, s);Wait(t);

#define forwards(s,t) OnFwd(OUT_AB, s);Wait(t);

#define backwards(s,t) OnRev(OUT_AB, s);Wait(t);

task main()

{

backwards(50,10000);

forwards(50,10000);

turn_left(75,750);

forwards(75,1000);

backwards(75,2000);

forwards(75,1000);

turn_right(75,750);

forwards(30,2000);

Off(OUT_AB);

}


Es muy útil definir este tipo de macros. Hace el código más compacto y legible. También permite cambiar el código fácilmente cuando por ejemplo cambiamos las conexiones de los motores.


Conclusiones:


En este capítulo hemos visto el uso de funciones, subrutinas, funciones inline y macros. Tienen diferentes usos. Las funciones normalmente se ejecutan al mismo tiempo y prestan atención a diferentes cosas que deben ser hechas al mismo momento. Las subrutinas son útiles cuando se debe usar el mismo trozo de código en diferentes sitios dentro de la misma función. Las funciones inline son útiles cuando se debe usar el mismo trozo de código en diferentes sitios en distintas funciones, pero usan más memoria. Finalmente las macros son muy útiles para pequeños trozos de código que se deben usar en diferentes sitios. Pueden tener también parámetros, lo que los hace aún más útiles.



  1. Conclusiones Finales.


El uso de los robots de lego permite que el alumnado consiga aproximarse al mundo de la programación y la automatización de procesos de una manera más práctica y cercana. Pudiendo comprobar el funcionamiento de un proyecto directamente sobre el kit de lego.

Así que, le abrimos una puerta a un mundo por descubrir y potenciamos su motivación hacia todo lo tecnológico que pensamos que es el futuro de nuestra comunidad.