Robotica en Java.pdf

150

description

Robotica en Java.pdf

Transcript of Robotica en Java.pdf

UNIVERSIDAD VERACRUZANA

FACULTAD DE INSTRUMENTACIÓN ELECTRÓNICA Y CIENCIAS ATMOSFÉRICAS

“Manual de prácticas de robótica móvil programadas en Java

utilizando el Kit Lego Mindstorms NXT”

TESINA

Que para evaluar la experiencia educativa Experiencia Recepcional (MEIF), del P.E. de Ingeniería en Instrumentación Electrónica

Presenta:

Esteban Antonio Castro Estrada

Director de tesina: M. C. Jesús Sánchez Orea

Co-Director de tesina:

M.I.A. Leticia Cuéllar Hernández

Xalapa Enríquez, Veracruz. Junio 2011

i

Índice Introducción........................................................................................1 Capítulo I. Kit Lego Mindstorms NXT ...............................................2

1.1 Antecedentes ............................................................................2 1.2 Características técnicas del Ladrillo NXT..................................4 1.3 Actuadores ................................................................................5 1.4 Sensores ...................................................................................6

1.4.1 Sensor de contacto .........................................................7 1.4.2 Sensor de luz..................................................................8 1.4.3 Sensor ultrasónico ..........................................................8 1.4.4 Sensor de sonido ............................................................9 1.4.5 Encoder...........................................................................9 1.4.6 Dispositivo Bluetooth ......................................................10 1.4.7 Protocolo I2C...................................................................11

1.5 Piezas de ensamblaje y estructura básica ................................12 Capítulo II. Programación ..................................................................15

2.1 Lenguajes de programación para el kit Lego Mindstorms NXT.16 2.2 Lenguaje de programación NXT-G............................................18

2.2.1 Interfaz gráfica de NXT-G ...............................................18 2.2.2 Conceptos fundamentales de NXT-G .............................22 2.2.3 Práctica: Seguidor de línea en lenguaje NXT-G .............23

2.3 Lenguaje de programación leJOS NXJ .....................................27 2.3.1 Características de leJOS NXJ.........................................27 2.3.2 Estructura de un programa en leJOS NXJ......................28 2.3.3 Compilación y descarga de programas al ladrillo NXT ...30 2.3.4 Uso de dispositivos de interfaz de usuario......................31

2.3.4.1 Funciones del display...........................................32 2.3.4.2 Funciones de botones..........................................34

2.3.5 Funciones de movimiento básico de motores .................35 2.3.6 Funciones de movimiento mediante la clase Pilot ..........37 2.3.7 Funciones básicas de sensores......................................40

2.3.7.1 Sensor de contacto ..............................................41 2.3.7.2 Sensor de luz .......................................................41 2.3.7.3 Sensor ultrasónico................................................42 2.3.7.4 Sensor de sonido .................................................42

2.3.8 Funciones de temporizado..............................................43 2.3.9 Manejo de dispositivos de comunicación........................44

2.3.9.1 Uso de Bluetooth..................................................44 2.3.9.2 Uso de I2C............................................................48

Capítulo III. Prácticas Básicas...........................................................51

3.1 Display LCD: Discretización del segmento de una recta...........52 3.1.1 Planteamiento del problema ...........................................52 3.1.2 Objetivo...........................................................................52

ii

3.1.3 Estructura física a emplear .............................................52 3.1.4 Fundamentos..................................................................52 3.1.5 Programación..................................................................53 3.1.6 Pruebas y conclusiones ..................................................54 3.1.7 Clases y métodos empleados.........................................56

3.2 Prácticas de uso de Motores.....................................................56 3.2.1 Movimiento básico de motores .......................................56

3.2.1.1 Planteamiento del problema.................................56 3.2.1.2 Objetivo ................................................................56 3.2.1.3 Estructura física a emplear...................................56 3.2.1.4 Fundamentos .......................................................57 3.2.1.5 Programación.......................................................57 3.2.1.6 Pruebas y conclusiones .......................................59 3.2.1.7 Clases y métodos empleados ..............................60

3.2.2 Movimiento de motores mediante la clase Pilot ..............61 3.2.2.1 Planteamiento del problema.................................61 3.2.2.2 Objetivo ................................................................61 3.2.2.3 Estructura física a emplear...................................61 3.2.2.4 Fundamentos .......................................................61 3.2.2.5 Programación.......................................................62 3.2.2.6 Pruebas y conclusiones .......................................63 3.2.2.7 Clases y métodos empleados ..............................64

3.3 Sensor de contacto: Robot de evasión de obstáculos...............64 3.3.1 Planteamiento del problema ...........................................64 3.3.2 Objetivo...........................................................................64 3.3.3 Estructura física a emplear .............................................65 3.3.4 Fundamentos..................................................................65 3.3.5 Programación..................................................................65 3.3.6 Pruebas y conclusiones ..................................................66 3.3.7 Clases y métodos empleados.........................................66

3.4 Sensor de sonido: Control de velocidad mediante intensidad de sonido ........................................................................67

3.4.1 Planteamiento del problema ...........................................67 3.4.2 Objetivo...........................................................................67 3.4.3 Estructura física a emplear .............................................67 3.4.4 Fundamentos..................................................................67 3.4.5 Programación..................................................................68 3.4.6 Pruebas y conclusiones ..................................................68 3.4.7 Clases y métodos empleados.........................................69

3.5 Sensor ultrasónico: Buscador de objetos cercanos 69 3.5.1 Planteamiento del problema ...........................................69 3.5.2 Objetivo...........................................................................69 3.5.3 Estructura física a emplear .............................................69 3.5.4 Fundamentos..................................................................69 3.5.5 Programación..................................................................70 3.5.6 Pruebas y conclusiones ..................................................72 3.5.7 Clases y métodos empleados.........................................72

3.6 Sensor de luz: Seguidor de línea mediante control on – off 72 3.6.1 Planteamiento del problema ...........................................72 3.6.2 Objetivo...........................................................................72

iii

3.6.3 Estructura física a emplear .............................................73 3.6.4 Fundamentos..................................................................73 3.6.5 Programación..................................................................74 3.6.6 Pruebas y conclusiones ..................................................76 3.6.7 Clases y métodos empleados.........................................76

Capítulo IV. Prácticas Avanzadas .....................................................78

4.1 Simulación de ciego y lazarillo...................................................79 4.1.1 Planteamiento del problema ...........................................79 4.1.2 Objetivo...........................................................................79 4.1.3 Fundamentos..................................................................79 4.1.4 Estructura a emplear.......................................................80 4.1.5 Programación..................................................................80 4.1.6 Pruebas y conclusiones ..................................................84

4.2 Seguidor de línea con control PI................................................84 4.2.1 Planteamiento del problema ...........................................84 4.2.2 Objetivo...........................................................................85 4.2.3 Fundamentos..................................................................85 4.2.4 Estructura a emplear.......................................................87 4.2.5 Programación..................................................................87 4.2.6 Pruebas y conclusiones ..................................................93

4.3 Interfaz entre ladrillo NXT y cámara CMUcam3.........................94 4.3.1 Planteamiento del problema ...........................................94 4.3.2 Objetivo...........................................................................94 4.3.3 Fundamentos..................................................................94 4.3.4 Estructura a emplear e implementación electrónica .......98 4.3.5 Programación..................................................................100 4.3.6 Pruebas y conclusiones ..................................................105

Conclusiones ........................................................................................106 Apéndice A: Instalación de leJOS NXJ y reemplazo de firmware ..............................................................................................108 Apéndice B: Menú del sistema operativo de leJOS NXJ ................117 Apéndice C: Restablecer firmware original del ladrillo NXT ..........119 Apéndice D: Conectores del puerto de entrada del ladrillo NXT ...121 Apéndice E: Preparación de la cámara CMUcam3 y software CMUcam2 GUI.....................................................................................122 Apéndice F: Programación del PIC18F4550.....................................123 Apéndice G: Ejemplos de funciones del lenguaje leJOS NXJ........126 Bibliografía..........................................................................................144

1

Introducción En la actualidad, el kit de desarrollo Lego Mindstorms NXT se ha considerado como una herramienta de aprendizaje, más que un juguete para niños. Diversas universidades del mundo han adaptado este kit para la enseñanza en sus clases, así como para proyectos de investigación sobre aplicaciones concretas. Tal es el caso del Instituto Tecnológico de Massachusetts (MIT), la Universidad Carnegie Mellon (CMU), la Universidad de British Columbia (UBC), y el Instituto Tecnológico Federal de Zúrich (ETH); por mencionar algunas universidades de prestigio. En muchos países, así como en México, periódicamente se llevan a cabo torneos de robótica en donde se ponen a prueba los conocimientos y habilidades de estudiantes y aficionados para resolver retos con el desarrollo de sistemas robóticos. Algunos estudiantes de la Universidad Veracruzana, han participado e incluso obtenido primeros lugares, en años previos, en el Torneo Mexicano de Robótica. Sin embargo el número de participantes provenientes de esta universidad ha sido bajo en comparación a otras universidades de México, tales como la UNAM, el IPN o el ITESM. Con el fin de promover el interés en el aprendizaje de la robótica a estudiantes de nivel licenciatura, se ha elaborado el presente manual de prácticas. En éste, se presentan fundamentos teóricos para la programación en lenguaje leJOS NXJ, versión de Java para el Kit Lego Mindstorms NXT, ofreciendo un material de apoyo al estudiante que desee iniciar alguna aplicación de robótica, así como para aquél que se encuentre en proceso de elaboración de ella. El presente manual se divide en cuatro capítulos, en el primero de ellos, se expone una introducción al Kit Lego Mindstorms NXT, donde se describen las características de sus componentes principales, así como de los dispositivos de comunicación con los que éste cuenta. En el segundo capítulo, se exponen algunos lenguajes de programación que pueden ser empleados con el Kit Lego Mindstorms NXT, haciendo énfasis en el lenguaje leJOS NXJ, ofreciendo una descripción profunda de las diferentes herramientas con las que cuenta este lenguaje, resaltando las ventajas de usarlo para programar el kit. En el tercer capítulo, se expone el empleo de la programación en lenguaje leJOS NXJ, para el desarrollo de prácticas de nivel básico. Con ellas, se espera que el estudiante utilice los fundamentos teóricos comprendidos en el segundo capítulo. En el cuarto y último capítulo, se expone el planteamiento y elaboración de prácticas de mayor dificultad que las expuestas en el tercer capítulo, utilizando programación en lenguaje leJOS NXJ. En estas últimas prácticas, además de la robótica, se aplican fundamentos teóricos de otras disciplinas.

2

Kit Lego

Mindstorms NXT Lego Mindstorms NXT es una serie de productos con fines de aprendizaje para el desarrollo de robots y automatización de aplicaciones en general. En el año 2006 fueron lanzados al mercado dos versionas de este producto, la versión comercial con 577 piezas y la versión educacional con 431 piezas. La versión utilizada en el presente manual es la comercial, sin embargo, es posible utilizar la versión educacional. Ambas versiones incluyen piezas de construcción de la serie LEGO TECHNIC (tales como ruedas, engranajes, vigas y rieles), así como sensores, motores y un ladrillo programable (ladrillo NXT). Todas las piezas anteriormente mencionadas son reutilizables y el ladrillo NXT es reprogramable.

1.1 Antecedentes

El kit Lego Mindstorms NXT, es el resultado de dos investigaciones y procesos de innovación separados. El primer proceso, es representado por la compañía Lego

I

3

con su continuo desarrollo de productos desde la primera aparición de un ladrillo reutilizable en 1949 (el “Ladrillo de enlace automático” mostrado en figura 1.1), esto permitió la apertura para niños y adultos para crear modelos de trabajo de complejidad incremental. El segundo proceso se deriva por la investigación del Grupo de Epistemología y Aprendizaje, del Instituto Tecnológico de Massachusetts, llevado por Fred Martin, Brian Silverman y Randy Sargent, bajo la orientación de de los profesores Seymour Papert y Mitchel Resnick y financiado por la compañía Lego. Este trabajo, iniciado en 1986, condujo al desarrollo del “Ladrillo Programable”, una pequeña unidad capaz de conectarse al mundo exterior a través de una variedad de sensores, diseñado para la creación de robots y otras aplicaciones en donde una computadora podría interactuar con objetos cotidianos [1].

Figura 1.1 “Ladrillo de enlace automático” fabricado por LEGO

La suma de estos dos esfuerzos produjo la primera versión de la serie Mindstorms, Robotics Invention System (RIS), el cual utilizaba un ladrillo programable llamado Robotic Control Explorer (RCX) constituido por un microcontrolador de 8 bits y acompañado de sensores y partes especiales, piezas existentes tomadas de otras series de productos Lego. Éste fue lanzado al mercado en 1998, desde ese año fue acogido tanto por el público infantil, a quienes iba dirigido inicialmente, así como a entusiastas aficionados a la robótica, público adulto. El nombre Mindstorms proviene del título del libro Mindstorms: Children, Computers, and Powerful Ideas, escrito por Seymour Papert, en donde se describen las ideas de Papert, respecto al uso de las computadoras como impulsoras del aprendizaje [2]. En los años siguientes al lanzamiento del producto, se crearon comunidades de investigadores que ampliaron las posibilidades del producto original creando entornos de programación alternativos e incluso sistemas operativos, dentro de los que se encuentra leJOS (basado en Java) y NQC (basado en C). La segunda versión de Mindstorms salió en el año 2006 con el kit Lego Mindstorms NXT, utilizando un microcontrolador de 32 bits. Con el que Lego dispuso de varios kits para desarrolladores que incluían documentación y esquemas de los sensores así como de sus protocolos de comunicación. Dicha versión, es el elemento central utilizado en el presente manual [3].

4

1.2 Características técnicas del Ladrillo NXT El componente central del kit es el ladrillo NXT, el cual es el cerebro del sistema. Este incluye un microcontrolador Atmel ARM7 de 32 bits corriendo a 48 MHz con 256 Kb de memoria flash y 64 Kb de memoria RAM, y un microcontrolador secundario Atmel AVR de 8 bits corriendo a 4 MHz con 4 Kb de memoria flash y 512 bytes de memoria RAM. En la figura 1.2 se muestra el diagrama de bloques de la estructura interna del ladrillo NXT donde se observa cómo las diferentes funciones son conectadas con los microcontroladores [4].

Figura 1.2 Diagrama de bloques del hardware del ladrillo NXT

El ladrillo NXT, incluye cuatro puertos de entrada con conectores tipo RJ-12, con soporte para señales digitales y análogas, utilizados para los sensores; y tres puertos de salida con el mismo tipo de conector, utilizados para los motores. Al frente del ladrillo se encuentra un display LCD monocromático de 100X64 pixeles junto con cuatro botones para controlar el sistema operativo. Asimismo, cuenta con un altavoz interno con una resolución de audio de 8 bits y con soporte para frecuencias de muestro de 2 a 16 KHz. Cabe mencionar que el ladrillo NXT es alimentado con 6 baterías AA. En la figura 1.3 se muestra el ladrillo NXT y los elementos anteriormente mencionados [5].

5

Figura 1.3 Ladrillo NXT

La conexión a la PC puede ser establecida mediante un cable USB conectado al ladrillo NXT por su propio puerto USB 2.0 o de manera inalámbrica utilizando la comunicación Bluetooth, también incluido en el ladrillo NXT.

1.3 Actuadores Uno de los componentes básicos de un robot, son los actuadores. Con ellos, los robots son capaces de realizar tareas en un ambiente físico. Se componen principalmente de motores y mecanismos de transmisión como engranes y poleas. Un motor es un dispositivo capaz de convertir potencia eléctrica en potencia mecánica. La entrada está dada por voltaje y corriente, mientras que la salida es el par de torsión y velocidad mecánica. La forma en que se efectúa este proceso de conversión es diferente para cada tipo de motor. En los primeros años de la robótica, era muy frecuente utilizar actuadores hidráulicos, pero las recientes mejoras del diseño de motores, han permitido que la mayoría de los nuevos robots sean completamente eléctricos. Entre los motores más utilizados en la robótica se encuentran los motores de corriente directa, motores a pasos, y servomotores [6]. Motores de Corriente Directa: El principio del funcionamiento de un motor de corriente directa se basa en que un conductor sufrirá una fuerza si una corriente eléctrica, aplicada en la armadura del motor (estator), fluye con ángulos rectos hacia un campo magnético. De esta forma, la corriente que pasa a través del campo, produce un par torsor en el rotor, dispositivo que gira en el centro del motor. Para cambiar la dirección de un motor de corriente directa, tan sélo se debe invertir la polaridad de su alimentación eléctrica.

6

Es importante señalar que regularmente este tipo de motores se acompañan de un sistema de engranajes para reducir su velocidad y aumentar su tracción [7]. Motor a pasos: La construcción de estos motores es tal que se mueve en pasos mecánicos discretos. Un cambio en la corriente de fase de un estado a otro crea un cambio de un solo paso en la posición del rotor. Sin cambios en el estado de la corriente de fase, la posición del rotor permanece en posición estable. Normalmente para hacer girar la flecha del motor en forma incremental en pasos iguales se le suministra un tren de pulsos de entrada programada. El número de pulsos por unidad de tiempo determina su velocidad. Para un estado dado de la corriente del estator, el rotor se mueve para alinear unos dientes ubicados sobre este, con otros que se encuentran en el estator [7]. Servomotores: Están conformados por un motor de corriente directa, un amplificador, un sistema reductor formado por engranes y un circuito de realimentación. Se caracterizan por tener la capacidad de desplazarse a ángulos específicos dentro de su intervalo de operación [7]. Para ello, el servomotor espera un tren de pulsos, conocido como modulador de ancho de pulso (PWM por sus siglas en inglés), que corresponde con el movimiento a realizar. Mientras exista dicha señal en la línea de entrada, el servomotor mantendrá la misma posición angular. Cuando la señal de entrada cambia, su posición angular cambia. El recorrido del desplazamiento es de 180° en la mayoría de los servomotores, sin embargo, se pueden modificar para tener un recorrido libre de 360° y, entonces, actuar como un motor común. El motor del kit Lego Mindstorms NXT (figura 1.4), es considerado un servomotor debido a que cuenta la mayoría de las características de los servomotores, con la diferencia de que éste es capaz de rotar libremente en 360° y el tren de pulsos de control no determina la posición, sino, la potencia.

Figura 1.4 Motor del kit Lego Mindstorms del NXT

1.4 Sensores

Los robots deben contar con dispositivos que le permitan percibir el ambiente que los rodea para poder modificar su comportamiento según las necesidades para las

7

que son diseñados. Los sensores pueden definirse como dispositivos que permiten medir variables del ambiente, como la intensidad luminosa, el sonido, distancias, colores, rotación, entre otros. Se puede dividir a los sensores en 3 tipos de clasificación, de acuerdo al tipo de señal que entregan, al tipo de información que proporcionan, y al tipo de interacción que tienen con el ambiente. De acuerdo al tipo de señal que entregan, se encuentran los analógicos y digitales. Los analógicos entregan un valor dentro de un determinado rango continuo, generalmente un voltaje. Los sensores digitales entregan una señal discreta dentro de un conjunto de valores posibles, éstos son dados en bits. Dentro de la clasificación de acuerdo al tipo de información que proporcionan se encuentran los internos y externos. Los internos son aquellos que nos brindan información del propio robot, como la rotación de los motores, velocidad, posición etc. Mientras que los externos, proporcionan información del entorno que los rodea, tal como la luz, sonido, etc. En la clasificación de acuerdo al tipo de interacción que tiene con el ambiente se encuentran los sensores pasivos y activos. Los activos son aquellos que necesitan enviar una señal hacia el ambiente para medir la respuesta que tiene éste ante dicha señal. Por otro lado, los sensores pasivos sólo toman lectura de la variable que les corresponde sin hacer uso de otro tipo de señal [8]. A continuación se describe el funcionamiento de los sensores que se incluyen en el kit Lego Mindstorms NXT, los cuales serán usados dentro de las prácticas del manual. 1.4.1 Sensor de contacto También llamado interruptor de límite, se considera un sensor digital debido a que éste entrega un valor lógico de 1 bit (encendido o apagado), sin embargo su funcionamiento es completamente analógico. Es un dispositivo mecánico constituido por un botón sensible a la presión. Cuando un objeto aplica presión sobre el botón se activa el interruptor cerrando el circuito. Generalmente este sensor se ocupa para la detección de obstáculos o posiciones extremas de los movimientos, donde al ser activado, se apaga el actuador correspondiente, impidiendo posibles daños al robot [6]. En la figura 1.5 se muestra el sensor de contacto con el que cuenta el kit Lego Mindstorms NXT.

8

Figura 1.5 Sensor de contacto del kit Lego Mindstorms NXT

1.4.2 Sensor de luz Está constituido por un fototransistor capaz de detectar un rango de luz con longitudes de onda desde 400nm hasta 1200nm [9], un circuito electrónico que amplifica la señal obtenida del fototransistor y un diodo emisor de luz (LED) de color rojo, el cuál permite trabajar al sensor en modo activo. El fototransistor genera un voltaje proporcional a la intensidad luminosa, éste tiene una mayor sensibilidad relativa a la luz infrarroja, dicha propiedad se aprovecha para usarlo de forma activa usando la luz del LED reflejándola en una superficie para medir distancias o detectar colores [4]. De manera pasiva, el sensor puede obtener una buena respuesta lineal ante la luz visible al ojo humano [6][9]. En la figura 1.6 se muestra el sensor de luz con el que cuenta el kit Lego Mindstorms NXT.

Figura 1.6 Sensor de luz del kit Lego Mindstorms NXT

1.4.3 Sensor ultrasónico Sirve para medir la distancia a una superficie con un máximo de 1.5m y mínimo de 5cm. Cuenta con un emisor sonoro, un receptor y un circuito digital embebido que procesa la información obtenida. Su principio se basa en enviar pulsos sonoros de alta frecuencia (200kHz) hacia una superficie, y medir el periodo de tiempo entre los pulsos enviados y los pulsos de eco. El sensor procesa la información obtenida mediante el circuito digital para obtener la distancia en centímetros y posteriormente envía dicha información al ladrillo por medio del protocolo I2C [6][7]. En la figura 1.7 se muestra el sensor ultrasónico con el que cuenta el kit Lego Mindstorms NXT.

9

Figura 1.7 Sensor ultrasónico del kit Lego Mindstorms NXT

1.4.4 Sensor de sonido Está constituido por un micrófono y un circuito analógico de amplificación y filtrado. La señal que obtiene es el nivel de presión sonora (SPL por sus siglas en inglés de Sound Pressure Level), utilizando dB como unidad de medida, éste es capaz de captar desde 50dB hasta 90dB [4][10]. En la figura 1.8 se muestra el sensor de sonido con el que cuenta el kit Lego Mindstorms NXT.

Figura 1.8 Sensor de sonido del kit Lego Mindstorms NXT

1.4.5 Encoder Son sensores internos localizados dentro de cada uno de los motores (figura 1.9), los cuales permiten determinar la dirección, posición y velocidad de rotación. Se componen de un disco con escalas de cuadrículas espaciadas uniformemente, estos giran junto con la flecha de su respectivo motor. De un lado de la escala se equipa con una fuente de luz, del otro hay celdas sensibles a la luz. Las celdas generan un pulso cada vez que un rayo de luz atraviesa una cuadrícula, de este modo por medio de software se lleva un contador para tomar el registro de rotación del motor. Para determinar el sentido, se encuentra un sistema idéntico al mencionado en desfase del tal modo que al sumar los dos trenes de pulsos se obtenga una señal diferente para cada sentido [6][8].

Figura 1.9 Encoder ubicado en el interior de cada motor del kit Lego Mindstorms NXT

10

1.4.6 Dispositivo Bluetooth El ladrillo NXT soporta una comunicación inalámbrica a través de un puerto Bluetooth, esto debido a que incluye un chip independiente CSR BlueCore4 con una memoria flash externa de 8 Mbits. Este chip es un dispositivo fabricado por la compañía CSR y contiene todo el hardware necesario para correr como un nodo Bluetooth autónomo. El firmware dentro del chip BlueCore integra una máquina virtual de tareas programables por el usuario que permiten controlar y correr pequeñas cantidades de código de aplicación. Un intérprete de comandos integrado dentro de la máquina virtual es capaz de decodificar y responder a comandos recibidos a través de la interfaz UART del microprocesador principal del NXT. La máquina virtual incluida en el chip BlueCore tiene una implementación completa de los perfiles de puerto serie A y B (SPP-A y SPP-B), con los que se emula una conexión RS-232. El SPP-A es usado cuando el BlueCore local es el que inicia la conexión mientras que el SPP-B es usado cuando otro dispositivo Bluetooth inicia la conexión. El ladrillo NXT puede ser conectado inalámbricamente a otros tres dispositivos al mismo tiempo, pero sólo puede comunicarse con uno a la vez. De este modo, es posible enviar programas y archivos de sonido entre ladrillos NXT, así como enviar y recibir datos durante ejecución de programas. La funcionalidad de la comunicación Bluetooth dentro del ladrillo NXT es establecida como un canal maestro – esclavo. Esto significa que un ladrillo NXT dentro de una red necesita funcionar como maestro y que otro ladrillo NXT se comunique a través de este si lo necesita. La figura 1.10 muestra qué dispositivos NXT pueden comunicarse con otro directamente dentro de una red.

Figura 1.10 Ladrillos NXT dentro de una red Bluetooth

11

Como puede verse, el NXT maestro puede conectarse a otros tres dispositivos Bluetooth a la vez. Sin embargo, éste sólo puede comunicarse con uno de los dispositivos esclavo durante un momento dado, esto significa que mientras el NXT maestro se encuentre comunicándose con el NXT esclavo 1 y el NXT esclavo 3 comience a enviar datos al NXT maestro, éste último no evaluará los datos recibidos hasta que conmute al NXT esclavo 3. Un ladrillo NXT no es capaz de funcionar como dispositivo maestro y esclavo al mismo tiempo porque esto podría causar pérdida de datos entre dispositivos. La conexión a otros dispositivos Bluetooth ocurre a través de canales. El ladrillo NXT tiene cuatro canales de conexión usando la comunicación Bluetooth. El canal 0 siempre es usado por el dispositivo NXT esclavo en una comunicación con el NXT maestro, mientras que los canales 1, 2 y 3 son usados para la comunicación saliente desde el dispositivo maestro a los dispositivos esclavo. En la figura 1.10, el NXT maestro usará los canales de comunicación 1, 2 y 3 cuando se comunique respectivamente con el NXT esclavo 1, NXT esclavo 2 y NXT esclavo 3. Cuando uno de los NXT esclavos se comuniquen con el NXT maestro, estos usarán el canal 0 [8]. 1.4.7 Protocolo I2C Dentro del ladrillo NXT, se encuentra implementada una interfaz digital usando el protocolo I2C. Dicho protocolo es un estándar de comunicación industrial de dos líneas que fue desarrollado por Philips Semiconductors en los años 80. Este ha sido usado en diferentes componentes industriales en los que se requiere una comunicación digital simple. El I2C funciona como la interfaz digital para dispositivos externos que necesiten comunicarse con el NXT. Tener una interfaz digital les permite desempeñar funcionalidad individual y entonces solo enviar el resultado final al ladrillo NXT o recibir nueva información desde el NXT. En un bus I2C hay un dispositivo maestro y hasta 127 dispositivos esclavos. Sin embargo, el ladrillo NXT tiene limitaciones de hardware que le hacen tener un máximo de ocho dispositivos esclavos. El ladrillo NXT tiene cuatro canales de comunicación I2C, uno por cada puerto de entrada y únicamente puede funcionar como dispositivo maestro, esto significa que el ladrillo NXT es el encargado de controlar el flujo de datos en cada uno de los canales de comunicación [8]. Dentro de un bus I2C se utilizan dos líneas, SDA y SCL (Serial DAta y Serial CLock respectivamente). La primera de ellas es la línea de señal de datos y la segunda es la línea de señal de reloj. La sincronía entre ellas define la diferencia entre la validación de un bit y las condiciones de cambio de estado en la

12

comunicación de protocolo I2C, dichas condiciones son la de inicio y paro de transmisión [11]. Cada transmisión comienza con una condición de inicio, posteriormente la palabra transmitida es la dirección del dispositivo esclavo al que va dirigido el mensaje. Esta dirección debe tener una longitud de 7 bits. El octavo bit de la primera palabra indica el sentido de la transferencia de datos en el bus, 0 indica una operación de escritura desde el maestro al esclavo y 1 indica una operación de lectura. Tras la primera palabra completada, el dispositivo esclavo responde enviando un bit de reconocimiento (conocido con el acrónimo ACK de la palabra en inglés “acknowledge”). Si el dispositivo maestro realiza una operación de escritura, éste enviara el dato inmediatamente después del bit de reconocimiento. De lo contrario, el dispositivo maestro esperará a que el dispositivo esclavo comience el envío de datos. Cuando la transmisión de datos ha sido completada, el dispositivo maestro produce una condición de paro de transmisión. En la figura 1.11 se muestra gráficamente el principio básico de transferencia de datos usando el protocolo I2C [10].

Figura 1.11 Transferencia de datos en el Protocolo I2C

En el apéndice D se describen los detalles de los conectores de los puertos de entrada del ladrillo NXT donde se muestran las líneas usadas para la comunicación I2C.

1.5 Piezas de ensamblaje y estructura básica. Además del ladrillo NXT, sensores y motores, el Kit Lego Mindstorms cuenta con piezas de la serie LEGO TECHNIC (figura 1.12), tales como ruedas, engranajes, vigas y rieles, con las cuales, es posible crear una gran variedad de estructuras. Al construir una estructura para un robot, el usuario puede hacer uso de algún instructivo, o de su creatividad, para adaptarlo a sus necesidades.

13

Figura 1.12 Piezas de ensamblaje del Kit Lego Mindstorms NXT

En el presente manual, se propone utilizar una estructura base para todas las prácticas que se realicen, de tal forma, que al realizar pequeñas modificaciones, pueda adaptarse a las necesidades de cada proyecto. De este modo, se podrá invertir un mayor tiempo en el diseño de programas y algoritmos, y menos en la construcción física de los robots.

La estructura base, que se utilizará en el presente manual, es un vehículo con dos ruedas delanteras controladas con motores independientes cada una y una rueda trasera de libre rotación (comúnmente llamada rueda tipo castor como se muestra en la figura 1.13), esta última funciona de pivote, manteniendo en equilibrio al vehículo.

Figura 1.13 rueda loca del robot móvil

Este tipo de estructuras, ofrecen al robot, una libertad de movimiento de 360° sobre su eje central, mientras sus ruedas giren con la misma potencia en sentido contrario. Asimismo, es capaz de usar la tracción de sus dos motores para avanzar en cualquiera de sus direcciones (adelante y atrás). Al asignar mayor potencia en uno de sus motores que en el otro, le permite virar con diferentes grados de curvatura. A este vehículo, se adaptará el ladrillo NXT de una manera que permita tener un acceso fácil para los conectores y uso de los botones. Por último, el vehículo deberá llevar un soporte al frente, en el cual se pueda adaptar cualquier sensor. Esta estructura permitirá realizar muchas funciones distintas, pero en ningún caso pretende ser la mejor estructura posible para ser empleada en las prácticas de este manual, debido a que pueden diseñarse una variedad de estructuras para cumplir una misma tarea.

14

Las instrucciones de construcción de la estructura mostrada en la figura 1.14, se podrán encontrar en el manual que incluye el Kit Lego Mindstorms NXT.

Figura 1.14 Estructura básica de un robot móvil

15

Programación

La habilidad de un robot para ejecutar cualquier tarea deberá ser provista por instrucciones específicas que le indiquen qué hacer, en otras palabras, se deberá programar. Esto involucra escribir un programa en una computadora, utilizando un lenguaje de programación, compilarlo y transferirlo, en este caso, al ladrillo NXT, donde se encuentra el cerebro del robot, el cual interpretará las instrucciones dadas. En este capítulo, se dará una breve introducción a algunos de los lenguajes de programación que pueden ser empleados con el kit Lego Mindstorms NXT. También se describirá la interfaz gráfica del “Software Lego Mindstorms NXT”, donde se utilizará el lenguaje de programación “NXT-G” (Lenguaje de programación oficial para el kit), y así, permitir tener un primer contacto con la programación de un robot en un entorno sencillo de comprender. La última sección de este capítulo se concentrará en el lenguaje leJOS NXJ, ofreciendo una descripción más profunda de las diferentes herramientas con las que cuenta este lenguaje de programación, resaltando las ventajas de usarlo para programar el kit Lego Mindstorms NXT.

II

16

2.1 Lenguajes de programación para el kit Lego Mindstorms NXT Los desarrolladores del Kit Lego Mindstorms NXT, junto con la empresa National Instrument, diseñaron un lenguaje oficial para el kit, el cual utiliza un entorno de programación gráfico, dirigido principalmente a usuarios con conocimientos de programación bajos o nulos. Sin embargo existen otros lenguajes de programación, no oficiales, basados en código en forma de texto, que ofrecen diferentes ventajas sobre el lenguaje oficial. A continuación se describen algunos de los lenguajes de programación comúnmente utilizados para el Kit Lego Mindstorms NXT. NeXT Byte Codes (NBC): Fue el primer lenguaje de programación basado en texto para el Kit de Lego, desarrollado por John Hansen. Se basa en una sintaxis de lenguaje ensamblador. El ladrillo NXT contiene un intérprete de código de bytes (provisto por sus mismos fabricantes), a partir del cual, el desarrollador de este lenguaje aprovechó para permitir compilar un programa fuente a un código de bytes del ladrillo NXT. Aunque el procesamiento y formato de NBC son similares al ensamblador, éste no es un lenguaje ensamblador de propósito general. Cabe mencionar que existen algunas restricciones derivadas de las limitaciones del intérprete de código de bytes diseñado por Lego [12]. Se muestra un código de ejemplo de este lenguaje en la figura 2.1.

Figura 2.1 Ejemplo de código del lenguaje de programación NBC

Not eXactly C (NXC): Es un lenguaje de alto nivel, similar al lenguaje C, construido sobre el compilador de NBC. Fue desarrollado por John Hansen casi inmediatamente después de NBC. Dado que ambos usan el mismo compilador, éste se encarga de convertir el código de NXC a NBC y posteriormente lo compila de la misma forma que si lo hiciera con un programa en NBC, es por ello que tienen las mismas restricciones derivadas del intérprete de código de bytes del NXT. Ambos lenguajes pueden ser utilizados con el Entorno de Desarrollo

17

Integrado “Bricx Command Center” [12]. Se muestra un código de ejemplo de este lenguaje en la figura 2.2.

Figura 2.2 Ejemplo de código del lenguaje de programación NXC

RobotC: El lenguaje de programación RobotC es un producto comercial dirigido al mercado de la educación y soporte. Éste no sólo es usado para el Kit Lego Mindstorms NXT, sino también para otras microcomputadoras de robótica educativa. Fue desarrollado por la academia de robótica de la Universidad Carnegie Mellon. RobotC usa una implementación del lenguaje C superior a la de NXC. Éste requiere remplazar el firmware del ladrillo NXT proporcionando mejoras sobre el firmware estándar, tales como ejecución de código más rápida, un mejor administrador de memoria, entre otras. Dado que éste es un producto comercial, se requiere una licencia para su uso [12]. Se muestra un código de ejemplo de este lenguaje en la figura 2.3.

Figura 2.3 Ejemplo de código del lenguaje de programación RobotC

18

leJOS NXJ: Fue creado como una máquina virtual de Java desarrollada por José Solórzano. leJOS NXJ posee muchas de las características que hacen destacar al lenguaje Java sobre otros lenguajes de programación. Entre éstas se puede resaltar que es un lenguaje de programación orientado a objetos, diferente a los lenguajes anteriormente mencionados basados en programación estructurada. También posee un sistema recolector de basura como el que cuenta el mismo lenguaje Java, con el que permite gestionar de una mejor manera la memoria RAM, entre otras ventajas de las que se hablará en otros apartados del manual. Al igual que RobotC, leJOS NXJ requiere del reemplazo de firmware del ladrillo NXT [1][3]. Se describirán más detalles de este lenguaje de programación en la sección 2.3 del presente manual. NXT-G: Es el lenguaje oficial del Kit Lego Mindstorms NXT. Como ya se había mencionado anteriormente, es un lenguaje de programación gráfico basado en LabVIEW, herramienta de desarrollo de software e instrumentación diseñado por National Instrument. En este lenguaje, las instrucciones o comandos, representados por bloques, pueden ser manipulados mediante un enfoque drag and drop. Si se quisiera comparar cada bloque con código en base a texto, se puede notar que la mayoría de ellos podrían equivaler a un promedio de 10 líneas de código de texto cada uno. De este modo se puede suponer que proporciona un beneficio para aquellos que desean ahorrarse tiempo en la programación, sin embargo, también ocupa un mayor espacio en la memoria y se requiere más tiempo de ejecución, debido a que muchas veces contienen instrucciones, ocultas al programador, que son innecesarias. Este hecho, entre otros, fueron los que impulsaron a otros desarrolladores y entusiastas en el área de la robótica para crear lenguajes de programación, no oficiales, que permitieran ampliar las posibilidades de manipulación del Kit Lego Mindstorms NXT [12]. Se describirán más detalles de programación en la sección 2.2.

2.2 Lenguaje de programación NXT-G A pesar de que el presente manual se concentra en el lenguaje leJOS NXJ, fue necesario incluir una introducción al entorno de programación del Kit Lego Mindstorms NXT, utilizando su lenguaje oficial NXT-G, para poner en marcha algunas de las herramientas que brinda el kit, sin necesidad de conocer algún lenguaje de programación en base a texto. De este modo, quien desee adentrarse en el mundo de la programación de robots, tendrá la facilidad de conocerlo y familiarizarse con el Kit Lego Mindstorms NXT, utilizando un entorno gráfico y de fácil comprensión. 2.2.1 Interfaz gráfica de NXT-G El entorno gráfico para el lenguaje NXT-G, también llamado Software Lego Mindstorms NXT, contiene elementos que brindan información, en base a gráficos,

19

que permiten al programador intuir qué función cumplen, sin haberlo utilizado con anterioridad. Debido a esto solo se describirán, a fondo, los componentes principales. Para una explicación más profunda de todas las herramientas que brinda el Software Lego Mindstorms NXT, se recomienda acudir a la documentación de ayuda del mismo software o la bibliografía del presente manual, en donde se presentan algunos libros dedicados únicamente al lenguaje NXT-G [12][13], de los cuales se extrajo información para generar esta sección. A continuación se describen los principales componentes de la interfaz gráfica del Software Lego Mindstorms NXT.

El área de trabajo: Consiste en una red cuadriculada sin límites, donde se

arrastrarán los bloques para generar el programa. Se encuentra en centro del Software Lego Mindstorms NXT (figura 2.4).

Figura 2.4 Área de trabajo del Software Lego Mindstorms NXT

La paleta de programación: Ubicada al lado izquierdo de la interfaz,

contiene tres divisiones: paleta común, paleta completa y paleta personalizada, éstas contienen todos los bloques de programación. La paleta común contiene los bloques de programación de uso más frecuente, la paleta completa contiene todos los bloques de programación estándar, y la paleta personalizada contiene los bloques creados por el usuario o descargados del Internet. La forma de navegar entre ellas es por medio de las pestañas que se encuentra debajo de éstas. Para agregar un bloque de programación a un programa se da click sobre el bloque deseado y se arrastra hasta el área de trabajo. En la figura 2.5 se ilustran los bloques principales de cada paleta.

20

Figura 2.5 Paletas de programación del Software Lego Mindstorms NXT

El pánel de configuración: Ubicado en la parte inferior izquierda de la

pantalla. Permite visualizar las opciones de configuración para el bloque de programación seleccionado. En la figura 2.6 se muestra el pánel de configuración para el bloque de movimiento. Cada vez que se selecciona algún bloque de programación diferente, el pánel de configuración cambia, reflejando las opciones de configuración del último bloque seleccionado. Además, el pánel de configuración se pone en blanco cuando se encuentra seleccionado más de un bloque de programación o cuando no se encuentra seleccionado ni uno solo.

Figura 2.6 Pánel de configuración del bloque de movimiento del Software Lego Mindstorms NXT

El controlador: Ubicado en la parte inferior derecha del área de trabajo,

mostrado en la figura 2.7, permite al usuario tener acceso al ladrillo NXT por medio de este software. Consiste en cinco botones descritos en la tabla 2.1.

21

Figura 2.7 Controlador del Software Lego Mindstorms NXT

Botón Nombre Función

1 Botón de ventana del

NXT

Despliega una ventana para administrar la comunicación y memoria del NXT

2 Botón Descargar

Descarga el programa activo al NXT

3 Botón Detener Detiene cualquier programa que se esté ejecutando en el NXT

4 Botón Descargar y

Ejecutar

Descarga el programa activo al NXT e inmediatamente lo ejecuta.

5 Botón Descargar y

Ejecutar seleccionado

Descarga solo los bloques de programación seleccionado actualmente al NXT e inmediatamente los ejecuta.

Tabla 2.1 Descripción de los botones del controlador del Software Lego Mindstorms NXT

La barra de menú: Ubicada en la parte superior izquierda (figura 2.8),

permite el acceso a grupos de comandos a través de menús, tales como: File, Edit, Tools, y Help. El menú File despliega los comandos para crear programas, guardarlos, cerrarlos, imprimirlos y salir del programa; el menú Edit despliega los comandos para administrar los programas y sus bloques de programa; el menú Tools despliega comandos para la calibración de sensores y actualización del firmware del NXT (herramienta que se utilizará para restablecer el firmware original mencionado en el apéndice C); y el menú Help brinda acceso a la documentación del software y algunos otros recursos de ayuda útiles.

Figura 2.8 Barra de menú del Software Lego Mindstorms NXT

22

La barra de herramientas: Ubicada justo debajo de la barra de menú. Se compone esencialmente de doce iconos que representan los comandos de uso más frecuente para la gestión de programas y acciones, así como herramientas del puntero. Los comandos que realiza cada icono se describen en la figura 2.9.

Figura 2.9 Barra de herramientas del Software Lego Mindstorms NXT

2.2.2 Conceptos fundamentales de NXT-G Todo programa, sin importar el lenguaje de programación que se utilice, requiere tener un punto inicial para que el intérprete identifique en dónde se encuentra la primera instrucción a ejecutar. Del mismo modo, es necesario que todas las instrucciones lleven una secuencia lógica. Es por ello, que en el presente apartado, se describen los conceptos que atañen al punto inicial de un programa y a la secuencia de ejecución, para efectos del lenguaje NXT-G, este último se conoce como Viga de secuencia.

Punto de inicio: Un programa en NXT-G comienza en el bloque adherido al punto de inicio, identificado con el icono del Software Lego Mindstorm NXT, ubicado al lado izquierdo del área de trabajo (figura 2.10). Puede observarse que junto al punto de inicio, a su derecha, se encuentra una marca con la palabra Start. Al arrastrar un bloque de programación sobre esta marca, automáticamente se adhiere al punto de inicio, designando este bloque como la instrucción inicial del programa.

Figura 2.10 Punto de inicio del Software Lego Mindstorms NXT

23

Viga de secuencia: Es la encargada de llevar a cabo el control de flujo del

programa. Sobre ésta se administra el orden de ejecución de los bloques de programación. Comienza desde el punto de inicio, extendiéndose hasta el último bloque del programa. Cada vez que se añade un bloque, la viga de secuencia se expande permitiendo colocar más bloques sobre ella. Los bloques de programación se ejecutarán uno tras otro, en el orden en que estos aparecen sobre la viga de secuencia, comenzando desde el punto de inicio y continuando hacia la derecha. En la figura 2.11 se muestra un ejemplo del orden de ejecución, enumerando la ejecución de cada bloque de programación.

Figura 2.11 Viga de secuencia del Software Lego Mindstorms NXT

2.2.3 Práctica: Seguidor de línea en lenguaje NXT-G Antes de comenzar a programar, se deberá definir la idea del funcionamiento del programa que se desea elaborar y la estructura física del robot que ejecutará dicho programa así como los sensores que se utilizarán. Para la presente práctica se planea diseñar un robot capaz de seguir una línea negra sobre una superficie blanca. Se diseñará un robot que detecte cuando sale y entra de la línea, utilizando el sensor de luz en modo activo, de tal modo que la luz emitida por el sensor sea reflejada con mayor intensidad sobre la superficie blanca, y así, distinga el momento en el que deba regresar a la línea moviendo una de sus ruedas, mientras que la otra se mantiene estática. De igual forma, cuando la intensidad de luz reflejada sea menor (cuando se encuentre sobre la línea negra), el robot deberá detener la rueda que giraba mientras estaba en la superficie blanca y moverá la contraria. Con ello, el robot realizará un movimiento en zigzag sobre uno de los bordes de la línea, repitiendo el proceso en un ciclo infinito. Lo anteriormente descrito, puede resumirse con un diagrama de flujo como el que se muestra en la figura 2.12.

24

Figura 2.12 Diagrama de flujo de de la práctica “Seguidor de línea en lenguaje NXT-G“

Se utilizará la estructura básica mencionada en la sección 1.5 del presente manual, añadiendo a esta, en su parte delantera, el sensor de luz apuntando hacia abajo con una distancia aproximada de 5 milímetros con respecto a la superficie sobre la que se moverá el robot. Una vez que se han establecido los parámetros de funcionamiento del robot y su estructura física, se procederá a crear un programa nuevo mediante el menú file, o bien, con el botón nuevo programa de la barra de herramientas del Software Lego Mindstorms NXT. El robot que se construirá, realizará la tarea de desplazarse, sobre el borde de la línea negra, una cantidad indeterminada de veces. Por tal motivo, el programa se desarrollará dentro de un ciclo infinito. El bloque que identifica esta función tiene el nombre de loop (ciclo en inglés), el cual se colocará junto al punto de inicio, en la marca Start. Éste puede obtenerse de la paleta de programación común, al igual que todos los bloques que se ocuparán para esta práctica. El ciclo infinito deberá verse en el área de trabajo como en la figura 2.13.

Figura 2.13 Bloque de ciclo infinito en área de trabajo del Software Lego Mindstorms NXT

25

Retomando el funcionamiento del robot, como primera acción, deberá sensar la intensidad de luz reflejada de la superficie en la que se encuentra y decidir cuál rueda girar. Para ello, se utilizará el bloque switch que incluye ambas funciones en el mismo bloque de programación. En la figura 2.14 se observa que la línea de secuencia se dividió en dos debido a que el bloque switch genera dos posibles secuencias de ejecución, de las cuales, el robot deberá elegir una de ellas, dependiendo del valor sensado y el umbral definido para la toma de decisión.

Figura 2.14 Bloque switch dentro de un ciclo infinito en el área

de trabajo del Software Lego Mindstorms NXT

Cabe mencionar que en el pánel del bloque switch (figura 2.15), se encuentra seleccionado, por defecto, el sensor de contacto. Por tal motivo, es necesario cambiarlo al sensor de luz, en la sección Sensor, dentro del mismo pánel. Como el sensor debe funcionar en modo activo, se marcará la opción Generate Light.

El umbral se define desde el pánel de configuración del bloque switch, dentro de la sección Compare, en donde se asigna un valor entero, entre el rango de 0 a 100. El valor que se asignará, deberá obtenerse mediante experimentación, utilizando la opción “Try me” que incluye el firmware original. De tal modo, podrán registrarse los valores sensados por el robot, mientras se encuentre sobre el negro o el blanco.

Figura 2.15 Pánel de configuración del bloque switch

Ahora que el robot es capaz de tomar decisiones, habrá que definir las acciones que realizará para cada posible decisión. Se utilizará el bloque Move para definir la

26

dirección, potencia y duración del movimiento de los motores para ambos casos, obteniendo un programa como el que se observa en la figura 2.16.

Figura 2.16 Estructura del programa seguidor de línea utilizando el Software Lego Mindstorms NXT

Por defecto, los motores seleccionados dentro del pánel de control del bloque Move son el B en el lado derecho, y C en el lado izquierdo, éstos pueden cambiar si es necesario. En la sección Steering dentro del pánel de uno de los bloques Move se asignará la dirección de giro moviendo la barra de desplazamiento hasta uno de los extremos y en el pánel del otro bloque Move se hará en el sentido contrario. La duración de rotación, asignada en la sección Duration dentro del mismo pánel, deberá ser ilimitada para que únicamente los motores frenen cuando se le indique girar hacia el lado contrario por medio de la toma de decisión. Como último detalle de configuración de los motores, se asignará la potencia dentro de la sección Power utilizando la barra de desplazamiento correspondiente eligiendo un valor entre el rango de 0 y 100. Se recomienda utilizar una potencia baja para la mayoría de las pruebas, ésta podrá cambiar dependiendo de los resultados de las pruebas que se realicen para evaluar el funcionamiento del robot. El pánel de configuración de uno de los motores para este programa, se muestra en la figura 2.17.

Figura 2.17 Pánel de configuración de uno de los motores del programa seguidor de línea.

Por último habrá que descargar el programa al robot ladrillo NXT utilizando el botón descargar del Controlador del Software Lego Mindstorms NXT y probar el funcionamiento del programa creado y corregir o perfeccionar los detalles necesarios para un mejor rendimiento.

27

2.3 Lenguaje de programación leJOS NXJ En esta sección se describen las características principales con las que cuenta el lenguaje de programación leJOS NXJ. De esta forma, se ofrece una idea general sobre el lenguaje que se utilizará antes de comenzar a trabajar con él. Posteriormente se explica cómo generar un programa “Hola mundo” con el lenguaje leJOS NXJ y la forma de descargarlo al ladrillo NXT, explicando la estructura básica de un programa en dicho lenguaje. Asimismo, se incluyen las funciones principales para manejar los diferentes dispositivos del kit Lego Mindstorms NXT. Con el contenido de esta sección, se ofrece un compendio de herramientas que permiten comprender las prácticas de los capítulos 3 y 4, así como conocimientos para generar programas básicos. La información y las funciones del lenguaje leJOS NXJ mencionadas en esta sección fueron extraídas de la página oficial en inglés del proyecto open source “leJOS" [14], hospedado en los servidores de SourceForge1. 2.3.1 Características de leJOS NXJ leJOS NXJ es un ambiente de programación de Java, diseñado para el Kit Lego Mindstorm NXT. Éste cuenta con un reemplazo de firmware sobre el ladrillo NXT que incluye una máquina virtual de Java. Además leJOS NXJ incluye las siguientes características:

Software de Reemplazo de firmware del ladrillo NXT. Software de compilación, descarga de programas para ladrillo NXT,

debugging, y administrador de archivos almacenados en el ladrillo NXT. Una librería de clases Java (classes.jar) que implementan la Interfaz de

Programación de Aplicaciones (API) de leJOS NXJ. Una librería de clase Java para escribir programas que se correrán desde la

computadora con capacidad de comunicarse con los programas de leJOS NXJ ubicados en el ladrillo NXT utilizando Bluetooth o USB.

El reemplazo del firmware estándar Lego Mindstorms eliminará cualquier tipo de archivo almacenado actualmente en el ladrillo NXT. Las instrucciones de reemplazo de firmware se describen en el apéndice A. El firmware estándar puede ser restaurado en cualquier momento utilizando el Software Lego Mindstorms NXT como se describe en el apéndice C.

1 Empresa dedicada a hospedaje de proyectos de desarrollo de software open source.

28

Al reemplazar el firmware, leJOS NXJ instala un nuevo sistema operativo sobre el ladrillo NXT, el cual se describe en el apéndice B. Cabe mencionar que leJOS NXJ no cuenta con un Entorno de Desarrollo Integrado (IDE por sus siglas en inglés), por tal motivo, la comunidad de colaboradores del proyecto leJOS ha creado plugins para programar en lenguaje leJOS NXJ con los software Eclipse y NetBeans (Entornos de Desarrollo Integrado exclusivos del lenguaje de programación Java). Sin embargo, para efectos prácticos, es posible utilizar un editor de texto. Debido a que leJOS NXJ usa el lenguaje estándar de Java, es posible usar una programación orientada a objetos. De este modo se ven ampliados los recursos de programación, como puede ser con la reutilización de código mediante objetos heredados y la administración de recursos que proporciona el recolector de basura de Java, entre otras ventajas del lenguaje Java [15]. Otra de las ventajas que destacan de leJOS NXJ es que éste es de código abierto, lo que permite que la comunidad de colaboradores se extienda a cualquier parte del mundo. De este modo, la comunidad trabaja en conjunto para mejorar y ampliar las posibilidades del lenguaje así como sus herramientas de programación. El desarrollador del lenguaje y creador de la comunidad fue originalmente José Solórzano, sin embargo, quienes mantienen actualmente el proyecto son Paul Andrew y Jürgen Stube, entre otros. Por otro lado, sobre las ventajas que tiene leJOS NXJ sobre el lenguaje oficial NXT-G, se puede mencionar su velocidad. Como se había mencionado anteriormente, los bloques de programación incluyen instrucciones ocultas al programador que en muchos de los casos son innecesarias, haciendo que el tiempo de procesamiento sea mayor. leJOS NXJ, al contrario, cuenta con muchas librerías de instrucciones con funciones específicas, que en términos de programación orientada a objetos llamaremos clases y métodos, las cuales brindan la posibilidad de ver e incluso modificar su código fuente. Cabe mencionar que leJOS NXJ cuenta con la posibilidad de manejar números de punto flotante, característica de la que NXT-G carece al igual que muchos otros lenguajes de programación para el Kit Lego Mindstorm NXT. Esto permite que el programador desarrolle programas más complejos como la creación de algoritmos de inteligencia artificial. 2.3.2 Estructura de un programa en leJOS NXJ Debido a que leJOS NXJ no cuenta con un Entorno de Desarrollo Integrado (IDE), se puede ocupar algún IDE de Java como Eclipse o NetBeans, para los cuales existen plugins que permiten utilizar leJOS NXJ en ellos. Sin embargo, en el presente manual únicamente se utilizará un editor de textos, dado que no se requerirán herramientas de alguno de los IDE mencionados. Cualquier editor de textos es útil para la programación en leJOS NXJ.

29

El editor de textos por defecto del sistema operativo de Microsoft Windows, es el Bloc de Notas. También existen editores de textos diseñados para la programación que facilitan la visualización del código. Un ejemplo de ellos es el software opens source Notepad++2. Quienes han programado en Java se sentirán familiarizados con leJOS NXJ dado que utiliza una estructura muy similar a cualquier otro programa en Java. Para describir la estructura principal de un programa en leJOS NXJ se expone el desarrollo de un programa de ejemplo, el programa conocido como “Hola Mundo” en el cual se desplegará en el display del ladrillo NXT el string “Hola Mundo”, manteniéndolo en pantalla hasta que se presione el botón escape. Primero, se creará un archivo en el editor de texto con el nombre HolaMundo.java; Debe observarse que la extensión establecida por el editor .txt será remplazada por la extensión .java. En el ejemplo, el archivo será guardado dentro del directorio C:\NXJ\HolaMundo. Dentro del archivo anteriormente creado se definirá la clase principal del programa, la cual deberá tener el mismo nombre que el archivo que la contiene. public class HolaMundo

Así como NXT-G requiere un punto de inicio para que el interprete identifique donde empieza el programa, leJOS NXJ ocupa para cumplir este requerimiento el método main escrito de la siguiente manera dentro de la clase principal. public class HolaMundo

public static void main (String[] args)

Dentro del método main se desarrollará el programa principal. Para este ejemplo se utilizará el método System.out.println(), el cual desplegará en el display del ladrillo NXT el argumento que contenga. public class HolaMundo

public static void main (String[] args)

System.out.println("Hola Mundo");

2 Notepad++ puede ser descargado desde su página oficia: http://notepad-plus-plus.org/

30

Al correr este programa se desplegará en display el string “Hola Mundo” e inmediatamente después desaparecerá, terminando el programa en un tiempo imperceptible al ojo humano. Es por ello que será necesario indicarle al programa que espere durante un periodo de tiempo, tal que permita leer el string. En este caso se le dará la instrucción de esperar a que se presione un botón del ladrillo NXT. Para hacer esto, será necesario incluir la clase Button dentro del programa. Button se encuentra dentro del paquete lejos.nxt de leJOS NXJ; Paquete que será necesario incluir en todos los programas que se desarrollen para el Kit Lego Mindstorms NXT utilizando el comando import encabezando cada programa. La clase Button tiene un método llamado waitForPress() que permite detener el programa hasta que un botón sea presionado. La lista completa de clases y métodos que soporta leJOS NXJ se encuentra en la página http://lejos.sourceforge.net/nxt/nxj/api/index.html El programa Hola Mundo completo es: import lejos.nxt.*;

public class HolaMundo

public static void main (String[] args)

System.out.println("Hola Mundo");

Button.waitForPress();

2.3.3 Compilación y descarga de programas al Ladrillo NXT Para compilar y descargar el programa Hola Mundo y todos los demás programas de leJOS NXJ deberá estar instalado el software leJOS NXJ así como un Kit de Desarrollo de Java (JDK)., Las instrucciones de instalación de leJOS se encuentran en el apéndice A. En el presente manual se asumirá que no se ocupa ningún Entorno de Desarrollo Integrado (IDE). Si llegara a ser necesario el empleo de algún IDE se deberá consultar la documentación del entorno correspondiente para determinar cómo compilar y descargar programas. Una vez que el software necesario se encuentre instalado, se procederá a abrir el símbolo de sistema mediante el Menú Inicio de Windows con la opción Ejecutar, posteriormente aparecerá una ventana en donde se escribirá cmd. Al presionar aceptar, se abrirá la venta del símbolo del sistema donde se deberá mover a la carpeta donde se encuentre ubicado el archivo del programa creado. Para el caso del programa HolaMundo del presente manual se escribirá la siguiente línea: cd c:\NXJ\HolaMundo

31

Posteriormente se procederá a utilizar el comando para compilar el programa escribiendo nxjc seguido del nombre del archivo del programa, incluyendo su extensión como se muestra a continuación: nxjc HolaMundo.java

leJOS NXJ recurrirá al compilador estándar de Java, para compilar el programa, remplazando la librería de Java con la propia de leJOS NXJ. Tras ejecutar este comando, no se producirá respuesta alguna, a menos que ocurra algún tipo de error de compilación, con lo que se mostrará la lista de errores correspondientes. Tras esto, se creará el archivo con extensión .class del programa correspondiente. Ahora se procederá a descargar el programa al ladrillo NXT, antes de esto habrá que cerciorarse de que se encuentra conectado el ladrillo NXT por medio del cable USB o Bluetooth. Para descargar el programa al ladrillo NXT, sobre el símbolo de sistema se deberá escribir el comando nxj seguido del nombre del programa, esta vez se omitirá la extensión. nxj HolaMundo

Con esto, la computadora se encargará de enlazar las clases usadas en el programa colectándolas y empaquetándolas en un archivo binario con extensión .nxj. Este proceso es llamado enlace. Posteriormente la computadora descargará el programa en el ladrillo NXT. También existen dos comandos que en conjunto realizan la misma tarea que el comando nxj. El comando nxjlink se encarga de realizar el enlace y el comando nxjupload descarga el archivo binario al ladrillo NXT. Para examinar los parámetros y sintaxis de los comandos nxjc, nxj, nxjlink y nxjupload, se deberá escribir sobre el símbolo del sistema, el comando que se quiera examinar, seguido de –h o –help con lo que se desplegará dicha información. 2.3.4 Uso de dispositivos de interfaz de usuario El Kit Lego Mindstorm NXT puede funcionar como un robot autónomo, que ejecute tareas sin necesidad de comunicarse con las personas. Sin embargo, también surge la necesidad de darle instrucciones mientras realiza alguna tarea en específico, u obtener información de él. Para resolver este problema se ocupan los botones y el display LCD, a los cuales, dentro de este manual se les hace referencia como los componentes de la interfaz de usuario del Kit Lego Mindstorms NXT. leJOS NXJ usa una o más clases para cada uno de ellos, así

32

como para cada dispositivo del Kit Lego Mindstorms NXT. De la misma forma, estas clases contienen métodos que permiten al programador configurarlos y darles funciones específicas.

2.3.4.1 Funciones del Display

Para desplegar datos en el display se usa la clase LCD, la cual contiene métodos que permiten usarlo en modo texto y en modo gráfico. Los métodos de modo texto interpretan el display como una matriz de dieciséis caracteres de ancho y ocho caracteres de alto. Estos son dirigidos usando coordenadas (x,y), así “y” cubre el eje vertical con un rango de 0 a 7, y “x” cubre el eje horizontal con un rango de 0 a 15. Las coordenadas (0,0) están ubicadas en la esquina superior izquierda del display. Los métodos básicos utilizados para el modo texto se muestran a continuación:

void drawString(String str, int x, int y) Despliega un string comenzando con el primer caracter ubicado en las coordenadas (x,y) especificadas. Ejemplo 1 del apéndice G.

void drawInt(int i, int x, int y) Despliega un valor entero comenzando con el primer dígito ubicado en las coordenadas x,y especificadas. Ejemplo 2 del apéndice G.

void drawInt(int i, int places, int x, int y)

Este es el método drawInt sobrecargado. En esta versión del método, donde se le indica un argumento más, el valor entero se desplegará con una alineación hacia la derecha permitiendo usar el número de dígitos indicados en el argumento places. Esto permite actualizar un valor desplegado en el display sin tener que refrescar el display completo. Ejemplo 3 del apéndice G.

void clear() Limpia el display. Ejemplo 4 del apéndice G.

Los métodos de modo gráfico interpretan el display como una matriz de 100 pixeles de ancho y 64 pixeles de alto. De la misma forma que en el modo texto,

33

estos son dirigidos usando coordenadas (x,y), donde “y” cubre el eje vertical con un rango de 0 a 99, y “x” cubre el eje horizontal con un rango de 0 a 63. Las coordenadas (0,0) están ubicadas en la esquina superior izquierda del display. La clase LCD cuenta con un método que permite poner en negro o blanco un píxel, éste es el método setPixel explicado a continuación:

void setPixel(int Color, int x, int y) Establece el color del píxel ubicado en las coordenadas x,y. Donde el color se establece con 1 para negro y 0 para blanco. Ejemplo 5 del apéndice G.

También existe una forma de dibujar figuras de una manera más sencilla utilizando la clase Graphics del paquete javax.microedition.lcdui. Con ella se pueden dibujar líneas, rectángulos y posicionar strings con coordenadas de modo gráfico. Para usar cualquier método de la clase Graphics es necesario importar el paquete javax.microedition.lcdui.Graphics con el comando import como se muestra a continuación: import javax.microedition.lcdui.Graphics;

Así también, habrá que crear una instancia de la misma clase usando el comando new dentro del programa. Graphics G = new Graphics(); Para efectos de demostración, la nueva instancia se ha llamado G, aunque ésta puede tomar cualquier otro nombre siempre y cuando no provoque conflicto con otra clase. A continuación se explican los métodos básicos de la clase Graphics con programas de ejemplos:

void drawLine(int x0,int y0,int x1,int y1) Dibuja una línea en el display con las coordenadas (x0,y0) como punto inicial y (x1,y1) como punto final. Ejemplo 6 del apéndice G.

void drawRect(int x,int y,int width, int height) Dibuja un rectángulo ubicando su esquina superior izquierda sobre las coordenadas (x,y) con ancho y alto establecido por los argumentos width y height respectivamente. Ejemplo 7 del apéndice G.

34

void drawString(String str, int x, int y)

Método similar al que contiene la clase LCD para desplegar un string en el display, con la diferencia que en este caso se utilizan las coordenadas del modo gráfico. Ejemplo 8 del apéndice G.

2.3.4.2 Funciones de botones

Los botones se usan mediante la clase Button, la cual contiene cuatro instancias, una para cada botón:

Button.LEFT Button.RIGHT Button.ENTER Button.ESCAPE

NOTA: En la tabla B.1 del apéndice B, se muestran los botones del ladrillo NXT con sus respectivos nombres a los que se hace referencia. Anteriormente ya se había utilizado el método waitForPress de la clase Button. A continuación se explicará con más detalle este método, así como los métodos de uso frecuente de la clase Button.

int waitForPress ()

Espera a que algún botón del ladrillo NXT sea presionado y soltado. Este método retorna un valor entero dependiendo del botón que haya sido presionado: 1 si se presionó ENTER, 2 si se presionó LEFT, 4 si se presionó RIGHT y 8 si se presionó ESCAPE. Ejemplo 9 del apéndice G.

boolean isPressed() Verifica si el botón asignado se encuentra presionado, retornando un valor de tipo boolean (true si el botón se encuentra presionado y false para el caso contrario). Ejemplo 10 del apéndice G.

void waitForPressAndRelease() Detiene el programa esperando a que un botón en específico sea presionado y soltado. Ejemplo 11 del apéndice G.

35

int readButtons()

Retorna un valor entero dependiendo del botón que haya sido presionado: 1 si se presionó ENTER, 2 si se presionó LEFT, 4 si se presionó RIGHT, 8 si se presionó ESCAPE y 0 si no se encuentra presionado ninguno. Ejemplo 12 del apéndice G.

2.3.5 Funciones de movimiento básico de motores Los motores son controlados mediante la clase Motor, la cual está provista de una instancia para cada puerto de salida:

Motor.A Motor.B Motor.C

Los métodos de la clase Motor que permiten controlar lo motores, se exponen a continuación:

void forward() Genera un movimiento continuo hacia delante. El motor no se detendrá ni cambiará de dirección a menos que se le indique utilizando algún otro método de la clase Motor. Ejemplo 13 del apéndice G.

void backward()

Genera un movimiento continuo hacia atrás. El motor no se detendrá ni cambiará de dirección a menos que se le indique utilizando algún otro método de la clase Motor. Ejemplo 14 del apéndice G.

void reverseDirection()

Hace rotar el motor al sentido contrario al que se encuentre. Sólo hace efecto si el motor se encuentra en movimiento. Ejemplo 15 del apéndice G.

void setSpeed(int speed)

36

Establece la velocidad del motor en grados por segundo. Este método funciona acompañado de un hilo3 encargado de regular la velocidad, el cual se encuentra activado por defecto. Las velocidades que pueden establecerse usando este método son desde 10 hasta 900 grados por segundo. El método no hace diferencia de valores negativos, únicamente elimina el signo. Ejemplo 16 del apéndice G.

void setPower(int power) Establece la potencia del motor, usando un valor de 0 a 100. También admite valores negativos provocando que el motor rote en sentido contrario. Para usar este método es necesario desactivar el hilo regulador de velocidad mediante el método regulateSpeed de la clase Motor. El uso del método setPower, sin haber desactivado el regulador de velocidad, provoca un conflicto debido a que el hilo encargado de regular la velocidad ocupa este método4. Ejemplo 17 del apéndice G.

void stop() Frena el motor inmediatamente cancelando cualquier orden de rotación que se encuentre en progreso. Además, genera una resistencia al movimiento. Ejemplo 18 del apéndice G.

void flt()

Deja de suministrar energía al motor, lo cual provoca el frenado de éste. A diferencia del método stop, flt no opone una resistencia al movimiento. Al usar éste método para detener los motores, se ahorra batería. Ejemplo 19 del apéndice G.

La clase Motor también cuenta con métodos para rotar el motor a posiciones con ángulos específicos así como para obtener información de dichas posiciones haciendo uso del encoder. Estos se explican a continuación.

3 Los Hilos son tareas concurrentes que se encuentran en ejecución al mismo tiempo que el programa. 4 El regulador de velocidad se encuentra activado desde el inicio de todo programa corriendo bajo el firmware de leJOS NXJ.

37

int getTachoCount() Retorna el valor entero del contador del encoder del motor asignado, este valor está dado en grados. Al iniciar cualquier programa en leJOS NXJ, éste se encuentra en cero. Ejemplo 20 del apéndice G.

void resetTachoCount() Reinicia a cero el valor del contador del encoder a un motor asignado. Ejemplo 21 del apéndice G.

void rotate(int angle) Hace rotar el motor una cantidad de grados igual al argumento angle con un error de ±2 grados. Donde 360 grados es una vuelta completa. Este método no retorna el uso del procesador hasta que la rotación se haya completado. Ejemplo 22 del apéndice G.

void rotateTo(int limitAngle) Hace rotar el motor a la posición determinada por el argumento limitAngle, el cual esta dado por grados. La posición final de dicha rotación coincidirá con el contador del encoder con ±2 grados de error. Este método no retorna el uso del procesador hasta que la rotación se haya completado. Ejemplo 23 del apéndice G.

2.3.6 Funciones de movimiento mediante la clase Pilot LeJOS NXJ cuenta con una clase especializada en la navegación para vehículos de dos ruedas con control de motores independientes, como la estructura básica que se describe en las sección 1.5 (Figura 1.14). Dicha clase, llamada Pilot, permite al vehículo recorrer trayectorias precisas. Para ello, es necesario proporcionarle al programa información sobre la estructura física del vehículo, como el diámetro de las ruedas, la distancia entre ambas ruedas y el puerto al que se encuentran conectados los motores. Los métodos de la clase Pilot se encargan de calcular las distancias recorridas utilizando dicha información. Muchos de los métodos contenidos en la clase Pilot son similares a los que contiene la clase Motor, con la diferencia de que los de Pilot sirven para los dos motores de manera simultánea. Para utilizar la clase Pilot se deberá importar el paquete lejos.robotics.navigation mediante el comando import como se muestra a continuación:

import lejos.robotics.navigation.*;

38

Posteriormente se declara la clase Pilot dentro del programa para inicializar su constructor, con lo que se introducirán los detalles físicos de la estructura del vehículo. Pilot pilot = new TachoPilot(diameter, trackWidth, leftMotor, rightMotor);

En donde el parámetro diameter está definido por el valor de punto flotante del diámetro de las ruedas5; trackWidth está definido por el valor de punto flotante de la distancia entre ambas ruedas; leftMotor está definido con la instancia de la clase Motor, correspondiente al puerto en el que está conectado el motor izquierdo y rightMotor con el correspondiente al motor derecho (por ejemplo: Motor.A, Motor.B, Motor.C). Debe aclarase que las unidades de longitud que se utilicen en los parámetros diameter y trackWidth deberán ser las mismas. Nota: los datos de la estructura física del vehículo que se utilizan en los ejemplos de la clase Pilot son los del vehículo ocupado para realizar las pruebas de dichos ejemplos. Las unidades utilizadas fueron centímetros. A continuación se explican los métodos de uso frecuente de la clase Pilot.

void forward() Inicia a mover el vehículo hacia delante, éste no se detendrá hasta que se le dé la instrucción correspondiente. Ejemplo 24 del apéndice G.

void backward()

Inicia a mover el vehículo hacia delante, este no se detendrá hasta que se le dé la instrucción correspondiente. Ejemplo 25 del apéndice G.

void stop()

Detiene inmediatamente ambos motores del vehículo, cancelando cualquier orden de movimiento que se encuentre en progreso. Al igual que el método del mismo nombre de la clase Motor, genera una resistencia al movimiento. Ejemplo 26 del apéndice G.

5 El diámetro de las ruedas de la versión comercial del Kit Lego Mindstorms NXT es de 5.6cm y se encuentra escrito a los costados de cada rueda con unidades en milímetros.

39

void travel(float distance, boolean immediateReturn) Mueve el vehículo la distancia especificada por el argumento float distance dado en un valor de punto flotante utilizando las mismas unidades establecidas en el constructor de la clase Pilot dentro del programa. Un valor positivo lo hace avanzar hacia delante, uno negativo lo hace avanzar hacia atrás. El argumento boolean immediateReturn define si el uso del procesador será retornado inmediatamente después de que inició el movimiento (true), o si será retornado hasta que el movimiento finalice (false). Si se omite este argumento, el valor boolean por defecto será false. Ejemplo 27 del apéndice G.

void rotate(float angle, boolean immediateReturn)

Hace rotar el vehículo sobre su eje central, un ángulo igual al valor de punto flotante establecido en el argumento angle (valor en grados). Un valor positivo hace girar el vehículo hacia la izquierda, uno negativo lo hace girar hacia la derecha. El argumento boolean immediateReturn define si el uso del procesador será retornado inmediatamente después de que inició el movimiento (true), o si será retornado hasta que el movimiento finalice (false). Si se omite este argumento, el valor boolean por defecto será false. Ejemplo 28 del apéndice G.

void setMoveSpeed(float speed)

Establece la velocidad del vehículo de acuerdo al argumento de punto flotante speed, donde su unidad de longitud estará determinada por la unidad establecida en el constructor de la clase Pilot y base de tiempo en segundos. Los rangos máximos y mínimos pueden variar dependiendo de la unidad de medida y las estructura física del vehículo. El método no hace diferencia de valores negativos, únicamente elimina el signo. Ejemplo 29 del apéndice G.

void setTurnSpeed(float speed)

Establece la velocidad de rotación del vehículo de acuerdo al argumento de punto flotante speed, donde su unidad de medida será grados por segundo. El método no hace diferencia de valores negativos, únicamente elimina el signo. Ejemplo 30 del apéndice G.

40

void steer(float turnRate)

Comienza a mover al vehículo a través de una trayectoria curveada formando una circunferencia con su trayectoria. La magnitud de la curvatura es determinada por el argumento de punto flotante turnRate, con un rango entre 100 y -100. Mismo argumento con el que se calcula el diámetro de la circunferencia formada con la trayectoria del vehículo usando la ecuación 2.1.

turnRate-trackWidth100diámetro Ecuación 2.1

Si turnRate es positivo, el eje de giro se encontrará del lado izquierdo del vehículo; si es negativo, el eje del giro se encontrará del lado derecho del vehículo; si es cero, el vehículo se desplazará en línea recta. Ejemplo 31 del apéndice G.

float getTravelDistance()

Retorna el valor de punto flotante de la distancia recorrida por el vehículo desde la última llamada a reset (método descrito más adelante), este valor esta dado por la unidad de longitud establecida en el constructor de la clase Pilot dentro del programa. Al iniciar cualquier programa en leJOS NXJ este valor se encuentra en cero. Ejemplo 32 del apéndice G.

float getAngle()

Retorna el valor de punto flotante del ángulo total de rotación del vehículo desde la última llamada a reset (método descrito más adelante), con unidad en grados. Al iniciar cualquier programa en leJOS NXJ este valor se encuentra en cero. Ejemplo 33 del apéndice G.

void reset()

Reinicia a cero el contador de grados de rotación y distancia recorrida por el vehículo. Ejemplo 34 del apéndice G.

2.3.7 Funciones básicas de sensores Los sensores deben conectarse a uno de los 4 puertos de entrada del ladrillo NXT, y los programas de leJOS NXJ deberán conocer en cuál puerto se encuentra

41

cada uno. Para proveerle esta información, se deberá crear una instancia del puerto del sensor e introducirla en el constructor del sensor correspondiente. Las instancias de los puertos de entrada del ladrillo NXT se definen de la siguiente forma:

SensorPort.S1 SensorPort.S2 SensorPort.S3 SensorPort.S4

2.3.7.1 Sensor de contacto Para usar el sensor de contacto se deberá incluir el siguiente constructor dentro del programa: TouchSensor touch = new TouchSensor(SensorPort);

Donde el argumento SensorPort será definido mediante la instancia del puerto correspondiente. Para verificar si el sensor se encuentra presionado se utiliza el método isPressed descrito a continuación:

bolean isPressed() Retorna el valor boolean true si el sensor se encuentra presionado. De lo contrario, retorna el valor boolean false. Ejemplo 35 del apéndice G.

2.3.7.2 Sensor de luz Para usar el sensor de luz se deberá incluir el siguiente constructor dentro del programa: LightSensor light = new LightSensor(SensorPort, boolean floodlight);

Donde el argumento SensorPort será definido mediante la instancia del puerto correspondiente. El argumente boolean floodlight determinará el modo de funcionamiento del sensor. Si se asigna true para este argumento, se trabajará en modo activo encendiendo el LED, de lo contrario se trabajará en modo pasivo para sensar la luz ambiente.

42

Para verificar el valor sensado se utiliza el método readNormalizedValue descrito a continuación:

Int readNormalizedValue()

Retorna el valor entero de la lectura del sensor en un rango 0 a 1024, debido a que el convertidor analógico-digital que se utiliza es de 10 bits. Este método puede ser usado en modo activo del sensor de luz, así como en modo pasivo. Ejemplo 36 del apéndice G.

2.3.7.3 Sensor ultrasónico Para usar el sensor ultrasónico se deberá incluir el siguiente constructor dentro del programa:

UltrasonicSensor sonic = new UltrasonicSensor(SensorPort);

Donde el argumento SensorPort será definido mediante la instancia del puerto correspondiente. Para verificar la distancia obtenida por el sensor se utiliza el método getDistance descrito a continuación:

int getDistance() Retorna el valor entero de la distancia entre el sensor y la superficie señalada por el sensor, utilizando centímetros como unidad de medida. Cuenta un rango máximo de 170 centímetros y mínimo de 5 centímetros. Ejemplo 37 del apéndice G.

2.3.7.4 Sensor de Sonido Para usar el sensor de sonido se deberá incluir el siguiente constructor dentro del programa:

SoundSensor sonido = new SoundSensor(SensorPort);

Donde el argumento SensorPort será definido mediante la instancia del puerto correspondiente. Para verificar el valor sensado se utiliza el método readValue descrito a continuación:

43

sonido.readValue()

int readValue() Retorna el valor entero de la lectura del sensor de sonido. Utiliza un rango de 0 a 100, donde 0 equivale a 50 dB y 100 equivale a 90 dB. Cabe destacar que la respuesta del sensor no es lineal. Ejemplo 38 del apéndice G.

2.3.8 Funciones de temporizado El paquete lejos.util de leJOS NXJ contiene utilerías que permiten tener control del temporizado. Para las prácticas del presente manual se utilizarán dos clases de este paquete: Stopwatch y Delay. Es necesario importar el paquete lejos.util mediante el comando import para poder hacer uso de éste como se muestra a continuación: import lejos.util.*;

La clase Stopwatch contiene un contador de tiempo que inicia en cero al momento en el que un programa es ejecutado y puede ser consultado y reiniciado mediante sus métodos. Para usar alguno de sus métodos, es necesario crear una instancia de la misma clase, usando el comando new dentro del programa como se muestra a continuación: Stopwatch sw = new Stopwatch();

Los métodos de la clase Stopwatch se describen a continuación:

int elapsed()

Retorna el valor entero del tiempo transcurrido en milisegundos desde que inicia el programa o la última llamada reset. Ejemplo 39 del apéndice G.

void reset()

Reinicia el contador de Stopwatch a cero. Ejemplo 40 del apéndice G.

leJOS NXJ también cuenta con retardos que permiten detener el programa hasta que transcurra una cantidad de tiempo definida. Para utilizarlos se llama a los métodos de la clase Delay del paquete lejos.util.

44

Los métodos de la clase Delay no pueden ser interrumpidos hasta que estos finalicen. Para usar alguno de sus métodos, es necesario crear una instancia de la misma clase usando el comando new dentro del programa como se muestra a continuación: Delay delay = new Delay();

Los métodos de la clase Delay son:

void msDelay(long period) Espera el número de milisegundos especificados por el argumento period.

void usDelay(long period) Espera el número de microsegundo especificados por el argumento period.

void nsDelay(long period)

Espera el número de nanosegundos especificados por el argumento period. El ejemplo 41 del apéndice G muestra un programa en el que se hace uso de los tres métodos de la clase Delay. 2.3.9 Manejo de dispositivos de comunicación LeJOS NXJ ofrece la posibilidad comunicar el ladrillo NXT con otro ladrillo NXT o algún otro dispositivo que comparta uno de los protocolos que éste utiliza. Esto puede ser vía inalámbrica usando Bluetooth, o por medio de alambrado usando el protocolo I2C. Para ambos casos, leJOS NXJ cuenta con clases y métodos dedicados al uso exclusivo de cada protocolo de comunicación. 2.3.9.1 Uso de Bluetooth El ladrillo NXT puede funcionar como dispositivo maestro o esclavo dentro de una comunicación vía Bluetooth, en donde el dispositivo esclavo espera por una conexión solicitada por el dispositivo maestro. El dispositivo maestro iniciará la conexión siempre y cuando el dispositivo esclavo especificado se encuentre en espera por él. Cuando lo conexión se ha establecido, ambos dispositivos pueden usarla para abrir canales de flujo de entrada y salida, así como leer o escribir datos a través de estos. Para iniciar una conexión Bluetooth desde un ladrillo NXT a otro ladrillo NXT, es necesario añadir el NXT esclavo a la lista de dispositivos Bluetooth del ladrillo NXT maestro.

45

Esto se hace dentro del submenú “Bluetooth” del menú principal del sistema operativo leJOS NXJ, en el ladrillo NXT maestro. En éste, al seleccionar la opción “Search”, si el ladrillo NXT esclavo se encuentra con la opción Bluetooth encendida así como su visibilidad, el ladrillo NXT maestro lo encontrará y podrá ser añadido al seleccionar la opción “add”. Ladrillo NXT maestro El programa del ladrillo NXT maestro deberá incluir los paquetes que contienen las clases y métodos necesarios para iniciar la conexión Bluetooth, para esto se incluyen como se muestra a continuación:

import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*; import javax.bluetooth.*;

Posteriormente, dentro del programa podrá crearse una instancia de la clase BTRemoteDevice como se muestra en la siguiente línea:

RemoteDevice btrd = Bluetooth.getKnownDevice(string name);

Con esto, la clase se encargará de identificar el ladrillo NXT esclavo con el nombre que se encuentre como argumento del string name. En caso de que no identifique el nombre especificado, este retornará null. Después de ser identificado el dispositivo, se podrá conectar a éste mediante el método connect como se muestra a continuación:

BTConnection btc = Bluetooth.connect(btrd);

Si la conexión llegara a fallar, el método retornará null. Para identificar este tipo de errores durante la iniciación de la conexión, puede incluirse el siguiente código dentro del programa:

RemoteDevice btrd = Bluetooth.getKnownDevice(name); if (btrd == null) LCD.clear(); LCD.drawString("Sin dispositivo", 0, 0); BTConnection btc = Bluetooth.connect(btrd); if (btc == null) LCD.clear(); LCD.drawString("Error conexion", 0, 0);

Una vez que se ha establecido la conexión, pueden abrirse los canales de flujo de de datos. Para abrir el canal de flujo de entrada se utiliza el método openDataInputStream() dentro de la instancia de conexión creada anteriormente, y

46

para el de flujo de salida se usa el método openDataOutputStream(). Esto puede añadirse dentro del programa como se muestra en las siguientes líneas de código:

DataInputStream dis = btc.openDataInputStream(); DataOutputStream dos = btc.openDataOutputStream();

Para el envío de datos se usa la instancia creada de DataOutputStream con los siguientes métodos:

void writeInt(int v) Escribe el entero especificado a través del canal de flujo de salida.

void writeChar(char v)

Escribe el caracter especificado a través del canal de flujo de salida.

void writeFloat(flota v) Convierte el argumento de punto flotante especificado a tipo entero usando el método floatToIntBits de la clase Float, y de esta manera escribe este valor entero a través del canal de flujo de salida.

void flush() Vacía el canal de flujo de salida y forza cualquier byte almacenado en el buffer de salida a ser escrito en su destinatario. Es necesario usar este método después haber enviado cualquier tipo de dato por el canal de flujo de salida.

Para recibir datos se usa la instancia creada de DataInputStream con los siguientes métodos:

void readInt(int v) Retorna el valor entero leído del canal de flujo de entrada.

void readChar(char v)

Retorna el caracter leído del canal de flujo de entrada.

void readFloat(flota v) Retorna el valor de punto flotante leído del canal de flujo de entrada.

Nota: los métodos de canal de flujo de entrada bloquean el hilo actual, esto significa que no retornan el uso del procesador hasta que se haya leído el dato de entrada. A continuación se muestra un código de ejemplo de escritura y lectura de valores enteros usando las instancias de canales de flujo de datos creadas con anterioridad:

for(int i=0;i<100;i++)

47

LCD.drawInt(i*10, 8, 0, 2); dos.writeInt(i*10); dos.flush(); LCD.drawInt(dis.readInt(),8, 0,3);

Usando las líneas de código del ejemplo anterior se abre un ciclo donde se enviarán 100 valores usando el canal de flujo de salida (dos es la instancia creada de DataOutputStream), desde el 0 hasta 990 con incrementos de 10, y se leerá la respuesta del ladrillo NXT esclavo después de cada valor enviado usando el canal de flujo de entrada (dis es la instancia creada de DataInputStream). Cuando se desee terminar la conexión es necesario cerrar tanto las instancias de flujo de datos, así como la conexión misma usando el método close() como se muestra en el siguiente código:

dis.close(); dos.close(); btc.close();

El programa completo, que funciona para el ladrillo NXT maestro en una comunicación Bluetooth, llamado BTmaestro, se muestra en el ejemplo 42 del apéndice G. Ladrillo NXT esclavo El programa del ladrillo NXT esclavo deberá incluir los paquetes que contienen las clases y métodos necesarios para la comunicación Bluetooth, para esto se incluyen como se muestra a continuación:

import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*;

El ladrillo NXT esclavo esperará por una conexión llamando al método waitForConnection() en la clase Bluetooth, esto se hace mediante la asignación de una variable de referencia, en la que se genere una instancia del método, usando la siguiente línea de código: BTConnection btc = Bluetooth.waitForConnection();

Una vez que se ha establecido la comunicación, pueden abrirse los canales de flujo de entrada y salida de datos usando las mismas líneas de código empleadas para la misma tarea mostradas en el programa del ladrillo NXT maestro. De igual manera, se pueden emplear los mismos métodos para la lectura y escritura de datos.

48

En el ejemplo 43 del apéndice G se muestra el programa completo para el ladrillo NTX esclavo, llamada BTesclavo, el cual trabaja dentro de una comunicación Bluetooth en conjunto con el programa llamado Btmaestro (ejemplo 42) dentro de un ladrillo NXT como dispositivo maestro. 2.3.9.2 Uso de I2C El ladrillo NXT posee la capacidad de comunicarse con dispositivos digitales utilizando el protocolo I2C, como lo hace con el sensor ultrasónico. Del mismo modo, es posible utilizarlo para comunicar el ladrillo NXT con un dispositivo ajeno al Kit Lego Mindstorms NXT, ampliando la capacidad de utilizar más sensores y actuadores. El ladrillo NXT cuenta con cuatro canales de comunicación I2C, uno por cada puerto de entrada. Cada puerto de entrada del ladrillo NXT cuenta con dos líneas digitales. Una de ellas es la línea de datos (SDA), y la otra es la línea de pulsos (SCL). Cabe destacar que dichos puertos solo pueden ser utilizados en modo maestro dentro de una comunicación I2C, lo que obliga a hacer que el ladrillo NXT sea el encargado de controlar el flujo de datos dentro de la comunicación. Como un aspecto importante que permite la comunicación I2C entre dos dispositivos es la preparación del hardware de cada uno de ellos. En el apéndice E se describen los detalles de los conectores de los puertos de entrada del ladrillo NXT. En el capítulo 4 de este manual se muestra el diagrama esquemático de un microcontrolador conectado al ladrillo NXT para el uso de comunicación I2C. La comunicación I2C se efectúa mediante el uso de la clase I2CPort, la cual es una abstracción de los procesos de bajo nivel que controlan los canales de comunicación I2C, dichos procesos fueron descritos en el capítulo 1. En esta sección se explica cómo utilizar los métodos de la clase I2CPort para enviar y recibir datos a través del protocolo I2C. Para utilizar la clase I2CPort dentro de los programas, se hace pasar dicha clase de un objeto a un método con el mismo nombre y posteriormente se le asigna el puerto con el que se utilizará. A continuación se muestran las líneas de código para realizar esta tarea asignando el puerto SensorPort.S2 como ejemplo: I2CPort I2CPort; I2CPort=SensorPort.S2;

Posteriormente se recomienda crear un array del tipo byte, el cual será utilizado como buffer del canal de comunicación, donde se almacenará el dato a enviar o el dato recibido desde algún dispositivo esclavo. La longitud del array será igual al número de bytes que se deseen manejar dentro de la comunicación.

49

De igual manera se crea una variable del tipo byte donde se almacenará la dirección del dispositivo esclavo. Dado que las direcciones dentro del protocolo I2C son de 7 bits, se recomienda asignar dicha dirección con un corrimiento hacia la derecha en un bit, con lo que se perderá el bit menos significativo. De este modo, la dirección que se asigne en el programa del NXT coincidirá con la dirección del dispositivo esclavo correspondiente. Una vez que se han creado las variables necesarias para el manejo de la comunicación, ésta se habilita utilizando el método I2CEnable (int mode), donde el argumento mode es el modo en el que funcionará el puerto. Para la comunicación I2C se asignará STANDARD_MODE en este argumento, como se muestra en la siguiente línea de código: I2Cport.i2cEnable(I2CPort.STANDARD_MODE);

La comunicación se inicia utilizando el método i2cStart cuya descripción se muestra a continuación.

int i2cStart(int address, int internalAddress, int numInternalBytes, byte[ ] buffer, int numBytes, int transferType);

Genera la rutina de inicio de comunicación I2C con el dispositivo esclavo cuya dirección es asignada en el argumento de tipo entero address. Posteriormente envía el bit de lectura o escritura asignado en el argumento de tipo entero transferType, donde 0 indica lectura y 1 indica escritura. Si se indica escritura, envía el array del argumento buffer, cuya longitud deberá establecerse en el argumento entero numBytes. Si se indica lectura, el argumento buffer deberá ser nulo (null). Con esto, el programa dejará la comunicación abierta en la espera de recibir el número de bytes que se indiquen en el argumento numBytes. Los argumentos internalAddress y numInternalBytes representan la dirección del dispositivo maestro y la longitud de dicha dirección respectivamente. Ambos argumentos serán asignados con valor de 0 si se ocupa solo uno de los puertos de entrada del NXT para la comunicación I2C. Este método retorna el valor del estado de la transferencia. Retorna 0 si no existe error, 1 si el dispositivo es inválido, 2 si el dispositivo está ocupado, 4 si existe un error del buffer, 5 si el tamaño del registro de dirección es inválido.

50

Posteriormente, si se desea leer un dato, se deberá revisar que el valor retornado por el método i2cStart sea 0 para poder continuar con la comunicación. Con lo que se procederá a utilizar el método i2cComplete descrito a continuación.

int i2cComplete (byte[] buffer, int numBytes);

Lee el dato recibido almacenándolo en el array del argumento buffer y finaliza la transferencia. El número de bytes que se leerán se establece en el argumento entero numBytes. Este método retorna un entero positivo con el número de bytes recibidos si no existiera algún tipo de error en la transferencia, de lo contrario retorna los siguientes valores enteros negativos, reportando el tipo de error: -1 si el dispositivo es inválido, -2 si el dispositivo está ocupado, -3 si hay un error generado por el canal I2C,-4 si existe un error del buffer.

Cabe mencionar que el proceso de transferencia de datos utilizando el protocolo I2C tarda algunos microsegundos, incluso después de que el programa lea varias líneas de instrucción. Es por ello que es necesario incluir un ciclo de retardo en espera a que el canal I2C se encuentre liberado. El método i2cBusy cumple con lo requerido para dicha tarea, este retorna un 1 si el canal I2C se encuentra ocupado y un 0 si se encuentra liberado. La siguiente línea de código muestra un ejemplo de cómo generar el ciclo mencionado: while (I2CPort.i2cBusy() != 0)

Observe que al utilizar el ciclo, el programa no leerá ninguna otra línea de código hasta que el método i2cBusy retorne 0, indicando que el canal I2C se encuentra liberado. Dicho ciclo se utiliza después de haber llamado al método i2cStart, no es necesario utilizarlo después del método i2cComplete. Para deshabilitar la comunicación por el protocolo I2C se usa el método i2cDisable(). En el ejemplo 44 del apéndice G se muestra un programa, llamado Eco, donde el NXT solicita un dato de un byte al dispositivo esclavo con dirección 0xA4 e inmediatamente después le responde con el mismo dato.

51

Prácticas

básicas En éste capítulo se expone el empleo de algunas funciones, descritas en el capítulo 2, para el desarrollo de prácticas de nivel básico utilizando programación en lenguaje leJOS NXJ. Dichas prácticas están dedicadas específicamente para el uso de dispositivos del Kit Lego Mindstorms NXT, tales como el display LCD, motores, sensor de contacto, sensor de luz, sensor ultrasónico y sensor de sonido. En cada una de las prácticas se describe su objetivo, desarrollo, estructura física a emplear, código de programa y las pruebas realizadas durante su elaboración. Con ello, se espera que este manual pueda comprender algunas de las técnicas para crear aplicaciones de robótica móvil, utilizando el Kit Lego Mindstorms NXT. Se propone que las prácticas de éste capítulo puedan emplearse como material de apoyo para estudiantes cursando alguna de las siguientes Experiencias

III

52

Educativas: Algoritmos Computacionales y Programación; Laboratorio de Robótica; Técnicas de Inteligencia Artificial para Instrumentación.

3.1 Display LCD: Discretización del segmento de una recta 3.1.1 Planteamiento del problema Emplear funciones del lenguaje LeJOS NXJ, para dibujar segmentos de rectas, con cualquier pendiente, sobre el display LCD. El programa a realizar no deberá contener el método drawLine de la clase Graphics. 3.1.2 Objetivo El objetivo de esta práctica es exponer una forma de utilizar el display LCD. Así como para comprender cómo se crean líneas de dibujos sobre el display, pixel a pixel, mediante el empleo de un técnica de discretización del segmento de una recta. 3.1.3 Estructura física a emplear Para esta práctica no se requiere la construcción de una estructura en particular, debido a que el único componente que se utilizará será el display LCD, integrado en el ladrillo NXT. 3.1.4 Fundamentos El segmento de una recta se encuentra definido por sus dos puntos extremos (x0,y0) y (x1,y1). De ahí que la ecuación simplificada de la recta es:

bxmy ii Ecuación 3.1

donde Xi es cualquier valor entero en las abscisas de una recta así como Yi es su correspondiente valor entero en las ordenadas, mientras que m es la pendiente de dicha recta representada por la siguiente ecuación:

01

01

xx

yym

Ecuación 3.2

Con las dos ecuaciones anteriormente mencionadas se deberá desarrollar el método para discretizar el segmento de una recta. Para ello se asignará la nomenclatura Yi+1 representando un paso incremental cuando el valor de Xi aumente en 1. Así, al hacer un incremento en 1 a Xi en la ecuación 3.1 se obtiene la siguiente ecuación:

53

bxmy ii 11 Ecuación 3.3

Posteriormente, al despejar Xi de la ecuación 3.1, sustituyendo en la ecuación 3.3 y simplificando, se obtiene la siguiente ecuación: myy ii 1 Ecuación 3.4

La ecuación 3.4 permite realizar incrementos en una sola variable para generar el segmento de recta que se desee discretizar. 3.1.5 Programación Una vez obtenida la ecuación 3.4, ésta será utilizada para desplegar un punto en el display LCD (mediante el método setPixel de la clase LCD), para cada incremento en 1 de Xi mediante el uso de un ciclo for con X0 como primera posición y X1 como última posición. Deberá tenerse en cuenta que el valor de la pendiente requiere una operación de punto flotante. Por tal motivo, al valor resultante de Yi+1 deberá redondearse al entero más cercano. El algoritmo para generar una recta con puntos extremos (x0,y0) y (x1,y1) se muestra en el método Recta, mostrado a continuación: public void Recta(int x0, int y0, int x1, int y1) int x; float dy, dx, y, m; dy = y1 - y0; //Obtiene pendiente dx = x1 - x0; //de la recta m = dy/dx; y = y0; for( x = x0; x <= x1; x++ ) //Ciclo para generar LCD.setPixel( 1, x, (int)(y)); //la recta usando la y=y+m; //pendiente obtenida

Puede observarse en el método Recta, anteriormente mostrado, que no es necesario incluir la obtención de la pendiente dentro del ciclo for, con el que se genera la recta punto por punto, dado que m resulta ser un valor constante. A continuación se muestra un programa con nombre Dibujo donde se utiliza el método Recta con el que se despliegan diferentes segmentos de rectas en el display LCD.

import lejos.nxt.*;

54

public class Dibujo public static void main(String[] args) //Inicia Programa LCD.drawString("Dibuja Rectas", 2, 3); LCD.drawString("Presiona boton", 1, 4); Button.waitForPress(); //Espera de botón LCD.clear(); //Limpia pantalla Dibujo dibujar= new Dibujo(); //Crea instancia //de clase dibujar.Recta(3,30,96,36); //Llama al método "Recta" Button.waitForPress(); LCD.clear(); dibujar.Recta(0,63,59,21); Button.waitForPress(); LCD.clear(); dibujar.Recta(5,3,30,60); Button.waitForPress(); /************** Método "Recta" ****************/ public void Recta(int x0, int y0, int x1, int y1) int x; //inicializa variables float dy, dx, y, m; dy = y1 - y0; //Obtiene pendiente dx = x1 - x0; //de la recta m = dy/dx; y = y0; for( x = x0; x <= x1; x++ ) //Ciclo para generar LCD.setPixel( 1,x,(int)(y)); //la recta usando la y=y+m; //pendiente obtenida

3.1.6 Pruebas y conclusiones Tras dibujar el segmento de recta que une los puntos (3,30) y (96,36), se observa sobre el display LCD, una línea continua con pendiente m=0.06 (figura 3.1).

55

Figura 3.1 Línea continua, desplegada en el display LCD, utilizando los puntos (3,30) y (90,36).

Ocurre un evento similar tras dibujar el segmento de recta que une los puntos (0,63) y (59,21), observando una línea continua con pendiente m=-0.71 (figura 3.2).

Figura 3.2 Línea continua, desplegada en el display LCD, utilizando los puntos (0,63) y (59,21).

Sin embargo, al dibujar el segmento de recta que une los puntos (5,3) y (30,60), se observa sobre el display LCD, una línea discontinua con pendiente m=2.28 (figura 3.3).

Figura 3.3 Línea discontinua, desplegada en el display LCD, utilizando los puntos (5,3) y (30,60).

Con las observaciones anteriormente descritas, se concluye que la “técnica de discretización para un segmento de recta”, empleada en esta práctica, es útil únicamente para dibujar líneas con valores de pendiente entre el rango menor a 1 y mayor a -1(1>m>-1). Esto es debido a que el método se basa en incrementos discretos en las abscisas, asignando un sólo punto en las ordenadas para cada punto en las abscisas. El método de discretización anteriormente descrito, así como otros métodos de discretización pueden ser encontrados en el libro “Computer graphics: principles and practice” [16].

56

3.1.7 Clases y métodos empleados En la tabla 3.1 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa Dibujo, perteneciente a la práctica “Discretización del segmento de una recta”.

Método Clase Descripción drawString LCD Despliega un String en el display LCD

waitForPress Button Espera a que algún botón sea presionado y soltado.

clear LCD Limpia el display LCD setPixel LCD Despliega un pixel en el display LCD

Tabla 3.1: Resumen de métodos usados en el programa de la práctica “Discretización del segmento de una

recta”

3.2 Prácticas de uso de Motores Los motores del kit Lego Mindstorms NXT pueden ser manipulados de manera independiente o de manera simultánea, dependiendo de las necesidades del robot que se desee construir. Es por ello, que en esta sección se incluye una práctica para cada modo de uso de los motores: la práctica de “Movimiento básico” servirá para hacer una comprobación del funcionamiento del regulador de velocidad de los motores, y en la práctica “Movimiento avanzado de motores mediante la clase Pilot” se expondrán algunas de las posibilidades de movimiento de un vehículo mediante dicha clase. 3.2.1 Movimiento básico de motores 3.2.1.1 Planteamiento del problema Emplear métodos de la clase Motor para comprobar la eficacia del hilo regulador de velocidad con el que cuenta leJOS NXJ. 3.2.1.2 Objetivo El objetivo de esta práctica es exponer una forma de aplicar algunos métodos de la clase Motor en un programa del lenguaje leJOS NXJ, además de demostrar la ventaja que proporciona el empleo del hilo regulador de velocidad de leJOS NXJ. 3.2.1.3 Estructura física a emplear Los elementos principales a usar en esta práctica son: el ladrillo NXT y dos motores del Kit Lego Mindstorms NXT. Sin embargo, puede emplearse la

57

estructura completa de un vehículo como el que se describió en la sección 1.5 de capítulo 1. 3.2.1.4 Fundamentos Para realizar esta práctica es necesario hacer funcionar dos de sus motores simultáneamente mientras se obtiene el registro del contador del encoder de cada uno de ellos periódicamente, para observar su desfase después de un determinado tiempo. Se propone realizar dos pruebas, la primera deberá realizarse utilizando el hilo regulador de velocidad activado, mientras que en la segunda prueba deberá desactivarse. 3.2.1.5 Programación Como prerrequisito para la elaboración de esta práctica, es necesario generar una rutina encargada de obtener una serie de mediciones periódicas del encoder de cada motor. Ésta deberá ser llamada desde el programa principal en dos ocasiones. En la primera, el regulador de velocidad se encontrará activado, y antes de llamarla por segunda vez, se desactivará el mismo. Esta rutina se propone como un método llamado “medicion”. Este será encargado de iniciar el funcionamiento de los motores, tomar registro del contador de los encoder en ambos motores de manera periódica, y desplegar en el display dichos registros. Antes de finalizar el método, ambos motores deberán detenerse y esperar a que se presione un botón para continuar, de este modo, se podrán observar los resultados en el display, dando el tiempo suficiente para analizarlos. Dado que ambos motores realizarán exactamente las mismas funciones simultáneamente, se pueden ahorrar líneas de código al crear un array en donde se incluyan ambos motores utilizando la siguiente línea de código: public static Motor [] m = Motor.B, Motor.C;

De este modo, al invocar un método para el uso de los motores, sólo será necesario llamarlo una sola vez, tal como se mostró en el ejemplo de uso del método stop de la clase Motor en la sección 2.3.5. Por otro lado, para aprovechar las dimensiones del display, podrán desplegarse en este, hasta dieciséis mediciones distribuidas en dos columnas ocupando los ocho renglones del modo de texto del display. Así, cada columna podrá ser dedicada a las mediciones de uno de los dos motores. El intervalo de tiempo que se dedicará entre cada medición será de 200 milisegundos. Esto quiere decir que cada 200 milisegundos se registrará el valor

58

del contador del encoder de ambos motores y se desplegarán en el display, con un total de 8 iteraciones. La forma de satisfacer la necesidad de tomar las mediciones cada 200 milisegundos será ocupando la clase Stopwatch del paquete lejos.util. Con ella, podrá mantenerse el programa inactivo hasta que transcurran 200 milisegundos entre cada medición. Esto puede escribirse dentro el programa como se muestra a continuación: while(sw.elapsed() < 200* j)Thread.yield();

Donde j representa el número de iteración para las mediciones. Así, la primera iteración, cuando j sea igual a 0, representará la primera medición en el milisegundo 0, mientras que en la octava iteración, cuando j sea igual a 7, habrán transcurrido 1400 milisegundos (1.4 segundos). El método “Thread.yield()” permite compartir el uso del procesador a cualquier otro hilo que se encuentre en operación, mientras que el programa no lo requiera. Tras el tiempo transcurrido entre cada iteración, el proceso de medición y despliegue de los datos obtenidos puede realizase mediante las siguientes líneas de código. for (int i = 0; i<2; i++) LCD.drawInt(m[i].getTachoCount(), 7*i, j);

Donde i representa el número de motor a ocupar (motor 0 y motor 1).Con estas líneas de código, se tomará el registro del contador de encoder de cada motor al mismo tiempo en que se despliegan en el display en sus respectivas columnas. El ciclo completo de las ocho iteraciones se muestra a continuación: for( int j = 0; j<8; j++) while(sw.elapsed() < 200* j)Thread.yield(); for (int i = 0; i<2; i++) LCD.drawInt(m[i].getTachoCount(), 7*i, j);

Al método “medición”, además del ciclo anteriormente creado, se le añadirá la función para limpiar el display antes de comenzar las iteraciones, así como el reseteo del stopwatch y del contador de los enconders, posteriormente se encenderán ambos motores. Al finalizar el ciclo de las mediciones, se añadirá la función para detener los motores y la función de espera a que se presione un botón. Tras elaborar el método “medicion”, en el programa principal se asignará la velocidad con la que funcionarán los motores y se utilizará el método

59

regulateSpeed(bolean state), para habilitar o deshabilitar el regulador de velocidad antes de hacer los llamados al método “medicion”. El programa completo, elaborado en esta sección y nombrado “Regulador“, se muestra a continuación: import lejos.nxt.*; import lejos.util.*; public class Regulador Stopwatch sw = new Stopwatch(); public static Motor [] m = Motor.B, Motor.C; public static void main(String[] args) Regulador R =new Regulador(); for( int i = 0; i<2; i++) m[i].setSpeed(900); //define velocidad for( int i = 0; i<2; i++) m[i].regulateSpeed(true); //habilita el hilo regulador de velocidad R.medicion(); //llama al método medición() for( int i = 0; i<2; i++) m[i].regulateSpeed(false); //deshabilita el hilo regulador de velocidad R.medicion(); //llama al método medición() public void medicion() LCD.clear(); sw.reset(); for( int i = 0; i<2; i++) m[i].resetTachoCount(); //reinicia contador de los encoder de los motores for( int i = 0; i<2; i++) m[i].forward(); //Hace funcionar los motores for( int j = 0; j<8; j++) while(sw.elapsed() < 200* j)Thread.yield(); //Espera 200 milisegundos entre cada iteración for (int i = 0; i<2; i++) LCD.drawInt(m[i].getTachoCount(), 7*i, j); //Despliega en display el valor del contador del encoder //correspondiente a cada motor. for( int i = 0; i<2; i++)m[i].stop(); //Frena motores Button.waitForPress();

3.2.1.6 Pruebas y conclusiones Al ejecutar el programa realizado en esta práctica, el display LCD desplegó la información de la figura 3.4 mientras el hilo regulador de velocidad se encontraba

60

activado. Después, se desplegó la información de la figura 3.5 mientras el hilo regulador de velocidad se encontraba desactivado.

Figura 3.4 Mediciones de motores con hilo

regulador de velocidad habilitado

Figura 3.5 Mediciones de motores con hilo

regulador de velocidad deshabilitado En la figura 3.4 se observa que durante el recorrido simultaneo de los motores, mientras el hilo regulador de velocidad se encontraba habilitado, se produce un desfase casi imperceptible del desplazamiento angular entre ambos motores. En la figura 3.5 se observa que durante el recorrido simultaneo de los motores, mientras el hilo regulador de velocidad se encontraba deshabilitado, se produce un desfase de mayor magnitud en el desplazamiento angular entre ambos motores. Al realizar otras pruebas, podrá darse cuenta que al utilizar menor velocidad aplicada a ambos motores, el desfase del desplazamiento angular también disminuye. Con las observaciones anteriormente mencionadas se concluye que el hilo regulador de velocidad habilitado mejora en la fiabilidad del desplazamiento de un vehículo construido con el Kit Lego Mindstorms NXT. 3.2.1.7 Clases y métodos empleados En la tabla 3.2 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa Regulador, perteneciente a la práctica de movimiento básico de motores.

61

Método Clase Descripción

setSpeed Motor Establece la velocidad del motor en grados por segundo.

regulateSpeed Motor Habilito o deshabilita el regulador de velocidad del motor asignado.

resetTachoCount LCD Reinicia a cero el valor del contador del encoder a un motor asignado.

forward Motor Genera un movimiento continuo hacia delante en el motor asignado.

elapsed Stopwatch Retorna el valor entero del tiempo transcurrido en milisegundos desde que inicia el programa o la última llamada reset.

getTachoCount Motor Retorna el valor entero del contador del encoder a un motor asignado.

Clear LCD Limpia el display LCD. drawInt LCD Despliega un entero en el display LCD.

stop Motor Frena el motor inmediatamente cancelando cualquier orden de rotación que se encuentre en progreso.

waitForPress Button Espera a que algún botón sea presionado y soltado.

Tabla 3.2: Resumen de métodos usados en el programa de la práctica “Discretización del segmento de una

recta” 3.2.2 Movimiento de motores mediante la clase Pilot 3.2.2.1 Planteamiento del problema Emplear métodos de la clase Pilot para que un vehículo trace una trayectoria en forma “S” y retorne a su punto de origen. 3.2.2.2 Objetivo El objetivo de esta práctica es exponer una forma de aplicar algunos métodos de la clase Motor en un programa del lenguaje leJOS NXJ. 3.2.2.3 Estructura física a emplear La estructura básica será un vehículo como el que se describió en la sección 1.5 del capítulo 1. 3.2.2.4 Fundamentos Para realizar la trayectoria en forma de “S”, será necesario trazar un “medio círculo” a la vez. Si se utiliza el método steer de la clase Pilot, el vehículo deberá

62

completar 180 grados de rotación para trazar cada “medio círculo”, cuyos diámetros podrán obtenerse utilizando la ecuación 2.1 mostrada en la sección 2.3.6. Al finalizar la trayectoria en forma de “S”, el vehículo puede regresar a su punto de origen en línea recta recorriendo la misma distancia igual a la suma del diámetro de cada “medio círculo”. 3.2.2.5 Programación Para trazar el primer “medio círculo”, se utilizará el método steer de la clase Pilot con un valor de 100 en su argumento turnRate. De este modo, el diámetro del primer “medio círculo” será igual a la distancia entre ambas ruedas del vehículo. Mientras realiza la trayectoria, se utilizará el método getAngle de la clase Pilot, para comprobar que el vehículo no sobrepase los 180 grados de rotación. El código para trazar el primer “medio círculo” se muestra a continuación:

pilot.steer(100f); while(pilot.getAngle()<=180)Thread.yield();

El segundo “medio círculo” deberá efectuarse con dirección contraria al primero, para esto, se utilizará un valor de -100 en el argumento turnRate del método steer. Con ello, ambos medios círculos tendrán el mismo diámetro. Al realizar el desplazamiento en dirección contraria, el ángulo de rotación irá en decremento, iniciando el conteo desde el último valor registrado hasta el momento (180 grados). Por tal motivo, se deberá comprobar que este valor no decremente menos de 0 grados. El código para trazar el segundo “medio círculo” se muestra a continuación: pilot.steer(-100f); while(pilot.getAngle()>=0)Thread.yield();

Por último, el vehículo deberá rotar 90 grados, y desplazarse en línea recta una distancia igual al doble de la distancia entre sus ruedas (para las pruebas de esta práctica, la distancia entre las ruedas del vehículo fue de 11.3cm). A continuación se muestra el código del programa completo, llamado Vehiculo, al cual se le anexó un método para mostrar la distancia recorrida y el ángulo de rotación:

import lejos.nxt.*; import lejos.robotics.navigation.*; class Vehiculo static Pilot pilot = new TachoPilot(5.6f, 11.3f, Motor.C, Motor.B); public static void main(String[] args) Vehiculo Robot = new Vehiculo();

63

pilot.setMoveSpeed(15); pilot.setTurnSpeed(35); pilot.steer(100f); while(pilot.getAngle()<=180) Robot.datos(); pilot.steer(-100f); while(pilot.getAngle()>=0) Robot.datos(); pilot.rotate(-90f,true); while(pilot.isMoving()) Robot.datos(); pilot.travel(22.6f,true); while(pilot.isMoving()) Robot.datos(); pilot.rotate(90f,true); while(pilot.isMoving()) Robot.datos(); pilot.stop(); Button.waitForPress(); public void datos () int u=(int)pilot.getTravelDistance(); int d=(int)((pilot.getTravelDistance()-u)*100); LCD.drawString(u+"."+d,0,0); LCD.drawInt((int)pilot.getAngle(),5,0,1);

3.2.2.6 Pruebas y conclusiones El vehículo se desplazó por una trayectoria como la que se ilustra en la figura 3.6.

Figura 3.6 Trayectoria realizada por

el vehículo de la práctica de movimiento

64

avanzado de motores mediante la clase Pilot. La distancia de la trayectoria en línea recta fue de 22.6cm aproximadamente, este valor puede variar, dependiendo de los desniveles con los que cuente la superficie en donde se realicen las pruebas. Si se utiliza un superficie totalmente plana, la trayectoria será más precisa. 3.2.2.7 Clases y métodos empleados En la tabla 3.3 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa Vehículo, perteneciente a la práctica de Navegación.

Método Clase Descripción

setMoveSpeed Pilot Define la velocidad de desplazamiento del vehículo.

setTurnSpeed Pilot Define la velocidad de rotación del vehículo.

getAngle Pilot Retorna el valor de grados de rotación del vehículo sobre la superficie.

steer Pilot Genera un desplazamiento continuo, haciendo que el vehículo trace una curvatura con su trayectoria.

rotate Pilot Hace girar el vehículo sobre su eje central.

travel Pilot Desplaza el vehículo la distancia especificada.

isMoving Pilot Retorna true si el vehículo se encuentra en movimiento, y false, si se encuentra estático.

getTravelDistance Pilot Retorna el valor de la distancia recorrida por el vehículo

Tabla 3.3 Resumen de métodos usados en el programa de la práctica de Movimiento avanzado de motores

mediante la clase Pilot.

3.3 Sensor de contacto: Robot de evasión de obstáculos 3.3.1 Planteamiento del problema Diseñar un robot que avance en línea recta, capaz de evitar los obstáculos que encuentre en su camino, cambiando su trayectoria. 3.3.2 Objetivo El objetivo de esta práctica es exponer cómo se genera el comportamiento básico de un robot móvil, empleando el sensor de contacto del Kit Lego Mindstorms NXT.

65

3.3.3 Estructura física a emplear La estructura básica será un vehículo como el que se describió en la sección 1.5 del capítulo 1, anexando en la parte frontal el sensor de contacto apuntando al frente junto a una estructura de bumper (parecida a una defensa de automóvil), construida con vigas del kit, con el objetivo de ampliar el área de contacto del sensor. La figura 3.6 muestra un boceto de la estructura anteriormente mencionada para el vehículo de esta práctica.

Figura 3.6 Estructura del robot de evasión de obstáculos

3.3.4 Fundamentos El robot de evasión de obstáculos deberá cumplir con dos funciones principales: avanzar hacia delante y evadir obstáculos al activarse el sensor de contacto. Existen diferentes formas en las que un vehículo evada un obstáculo que se encuentre al frente de éste, cualquier maniobra utilizada es válida. En esta práctica se utilizará la más sencilla, en la que el robot retrocederá pocos centímetros y rotará sobre su eje cambiando la dirección sobre la que este provenga. Posteriormente, se retomará la función de avanzar hacia delante. El desempeño del robot dependerá de su estructura física, del ambiente en el que se encuentre, y las maniobras que utilice así como las velocidades con las que se mueva. Por tal motivo, se recomienda experimentar con los diferentes factores que afectan el desempeño del robot. 3.3.5 Programación Dentro del programa principal, se propone utilizar un ciclo infinito en donde se utilice el método forward de la clase Pilot para dar la indicación de avanzar hacia delante, seguido del método isPresed de la clase TouchSensor, para verificar si se encuentra con un obstáculo. De ser así, se llamará a otro método integrado en el programa, nombrado Evade, con el cual se dará la indicación de retroceder algunos centímetros y posteriormente rotar.

66

Cuando el método Evade haya terminado, el programa regresará a su tarea de avanzar hacia delante, mientras verifica si encuentra otro obstáculo. A continuación se muestra el código del programa elaborado para esta práctica, llamado “Bumper”, mismo que puede ser modificado según las condiciones del ambiente y la estructura de robot: import lejos.nxt.*;

import lejos.robotics.navigation.*;

class Bumper

public static Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B);

public static TouchSensor touch = new TouchSensor(SensorPort.S1);

public static void main(String[] args)

Bumper Robot = new Bumper();

pilot.setMoveSpeed((float)20);

while(true)

pilot.forward();

if (touch.isPressed())

Robot.Evade();

public void Evade ()

pilot.travel(-5);

pilot.rotate(20);

3.3.6 Pruebas y conclusiones La estructura bumper, utilizada en las pruebas de esta práctica, ayudó al robot a encontrar obstáculos siempre y cuando ésta fuera la primera parte de la estructura del vehículo que tocara el obstáculo. Este robot puede mejorarse ubicando más sensores de contacto en otras partes de la estructura y/o ampliando el área de contacto de la estructura bumper. 3.3.7 Clases y métodos empleados En la tabla 3.4 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa Bumper, perteneciente a la práctica Sensor de contacto: Robot de evasión de obstáculos.

Método Clase Descripción

setMoveSpeed Pilot Establece la velocidad del vehículo en centímetros por segundo.

isPressed Button Verifica si el botón asignado se encuentra

67

presionado

isPressed Touch Verifica si el sensor de contacto se encuentra presionado.

forward Pilot Genera un desplazamiento continuo hacia delante sobre el vehículo.

travel Pilot Desplaza el vehículo la distancia especificada.

rotate Pilot Hace girar el vehículo sobre su eje central. Tabla 3.4: Resumen de métodos usados en el programa de la práctica “Robot de evasión de obstáculos”.

3.4 Sensor de sonido: Control de velocidad mediante intensidad de sonido

3.4.1 Planteamiento del problema Diseñar un robot que avance en línea recta, capaz de desminuir su velocidad cuando la intensidad de sonido aumente y cambiar su trayectoria cuando la intensidad de sonido sea muy alta. 3.4.2 Objetivo El objetivo de esta práctica es exponer cómo se genera un comportamiento similar al empleado en la práctica “Robot de evasión de obstáculo”. Empleando, para este caso, el sensor de sonido, cuya cantidad de valores posibles en su respuesta, es mayor a la del sensor de contacto. 3.4.3 Estructura a emplear La estructura básica será un vehículo como el que se describió en la sección 1.5 del capítulo 1, anexando en cualquier parte de la estructura, el sensor de sonido. 3.4.4 Fundamentos Este robot, deberá cumplir con tres funciones principales: avanzar hacia delante, disminuir la velocidad del vehículo al incremento de intensidad de sonido, y cambiar la trayectoria cuando la intensidad de sonido supere un valor establecido. Una forma de disminuir la velocidad mediante la intensidad de sonido, es restando el valor asignado como velocidad máxima menos el valor obtenido por el sensor de sonido. Tomando en cuenta el rango de valores utilizado para definir la velocidad.

68

3.4.5 Programación Dentro del programa principal, se propone utilizar un ciclo infinito en donde se utilice el método forward de la clase Pilot para dar la indicación de avanzar hacia delante, seguido del método readValue de la clase SoundSensor, para registrar el nivel de intensidad sonora. Este último valor registrado, será utilizado para restarlo al valor de velocidad máxima (para las pruebas de ésta práctica fue 60cm/seg). El valor de velocidad será definido mediante el método setMoveSpeed de la clase Pilot. Posteriormente, se evaluará si el valor registrado por el sensor de sonido supera una cantidad establecida (para las pruebas de esta práctica se utilizó 90). De ser así, se da inicio a la rutina para cambiar la trayectoria. A continuación se muestra el código del programa elaborado para esta práctica, llamado VelocidadSonido: import lejos.nxt.*; import lejos.util.*; import lejos.robotics.navigation.*; public class VelocidadSonido public static void main(String[] args) int SPL; Pilot pilot = new TachoPilot(5.6f, 11.3f, Motor.C, Motor.B); SoundSensor sonido = new SoundSensor(SensorPort.S4); while (!Button.ESCAPE.isPressed()) pilot.forward(); SPL=sonido.readValue(); pilot.setMoveSpeed(60-SPL); LCD.drawInt(SPL,3,0,0); if(SPL>90) pilot.stop(); pilot.rotate(180);

3.4.6 Pruebas y conclusiones El robot de esta práctica se probó en un espacio sin obstáculos y se utilizaron diferentes intensidades de la voz para observar su comportamiento. Al variar la intensidad de voz, el robot inmediatamente disminuyó su velocidad. Sin embargo fue necesario producir la voz muy cerca de él, para hacer que cambiara

69

su trayectoria. Este puede solucionarse disminuyendo la cantidad a superar por la intensidad de sonido. 3.4.7 Clases y métodos empleados En la tabla 3.5 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa VelocidadSonido, perteneciente a la práctica Sensor de sonido: Control de velocidad mediante intensidad de sonido.

Método Clase Descripción

setMoveSpeed Pilot Establece la velocidad del vehículo en centímetros por segundo.

readValue SoundSensor Retorna la intensidad de sonido registrada dentro de una escala de 0 a 100.

stop Pilot Detiene el desplazamiento del vehículo. rotate Pilot Hace girar el vehículo sobre su eje central.

Tabla 3.5: Resumen de métodos usados en el programa de la práctica “VelocidadSonido”.

3.5 Sensor ultrasónico: Buscador de objetos cercanos 3.5.1 Planteamiento del problema Diseñar un robot capaz identificar el objeto más cercano a él, en su alrededor, sin desplazarse de su ubicación hasta haber encontrado dicho objeto. Al mismo tiempo, deberá desplegar en el display LCD un barrido de las distancias obtenidas durante la búsqueda. Al finalizar la búsqueda, deberá desplazarse hacia el objeto identificado. 3.5.2 Objetivo El objetivo de ésta práctica es exponer cómo se genera una rutina de búsqueda mediante un barrido de valores obtenidos por el sensor ultrasónico utilizando el lenguaje leJOS NXJ. 3.5.3 Estructura a emplear La estructura básica será un vehículo como el que se describió en la sección 1.5 del capítulo 1, anexando en la parte frontal, el sensor ultrasónico apuntando al frente. 3.5.4 Fundamentos Para generar un barrido del entorno que rodee al robot, éste deberá realizar una rotación completa de 360 grados sobre su eje central.

70

Mientras realiza el movimiento, se tomará una cantidad de lecturas del sensor igual a la cantidad de píxeles que se desee ocupar sobre el display. Para fines prácticos, se recomienda utilizar todo el ancho y alto posible del display (100X64 píxeles). Ajustando a la escala vertical, se obtendrían 100 mediciones (una medición cada 3.6 grados), y ajustando a la escala horizontal cada pixel equivaldría a 2.65cm (170cm como máximo valor posible). La tarea de búsqueda se realizará en tiempo real mientras se efectúan las mediciones, de tal forma que no se requiera almacenar todos los datos. Esto es posible mediante la comparación de cada lectura del sensor con respecto a la menor lectura hasta el momento. Si la última lectura del sensor devuelve un valor, aun menor al que estuviera registrado, el valor de la lectura sustituirá al anterior. De esta forma, también es posible registrar el ángulo de rotación en el que se encuentra la menor lectura. Al finalizar la rotación de 360 grados, cuando el robot obtenga su orientación inicial, podrá rotar hacia la orientación en donde se encontró la menor lectura y desplazarse a través de la misma distancia de dicha lectura. 3.5.5 Programación En el programa realizado para esta práctica, se definieron dos variables importantes de valores enteros. En la primera, llamada distMin, se registrará la última lectura del sensor ultrasónico con menor valor, esta tendrá un valor inicial de 170, igual al máximo valor posible en la lectura de dicho sensor. En la segunda variable, llamada xMin, se registrará la posición horizontal del display LCD donde se haya desplegado el último valor de distMin. Se realizará la rotación del vehículo usando el método rotate de la clase Pilot, con su argumento immediateReturn en estado true. De este modo podrán realizarse las mediciones mientras el robot se encuentra en movimiento. Iniciada la rotación, se definirá un ciclo de 100 iteraciones, cada iteración equivale a una lectura del sensor ultrasónico. La forma de satisfacer la necesidad de tomar las lecturas cada vez que se han rotado 3.6 grados, es utilizando el método getAngle de la clase Pilot. Con éste, el programa quedará inactivo hasta hayan transcurrido los 3.6 grados requeridos por cada iteración. Esto puede escribirse dentro el programa como se muestra a continuación: while(pilot.getAngle()<3.6f*x)Thread.yield();

Donde x representa el número de iteración para las lecturas del sensor. Así, la primera iteración, cuando x sea igual a 0, representará la primera lectura en el grado 0. Mientras que en la iteración 100, cuando x sea igual a 99, el vehículo habrá rotado 356.4 grados aproximadamente.

71

Para obtener la lectura en cada iteración, desplegar en el display el barrido en tiempo real, y finalmente, identificar la lectura del sensor ultrasónico con menor valor, pueden utilizarse las siguientes líneas de código: int dist =sonic.getDistance(); //Lectura actual. LCD.setPixel(1,x,(int)(dist/2.65)); //Despliega el pixel. if (distMin>dist) //Si lectura es menor a //la mínima. distMin=dist; //Lectura actual es //la mínima. xMin=x; //Posición de lectura //mínima.

Al terminar la rotación de 360 grados, el robot puede desplazarse hasta el objeto más cercano usando los datos obtenidos en el barrido, como se muestra en las siguientes líneas de código: pilot.rotate((float)(xMin*3.6)); pilot.travel(distMin-2);

El programa completo de esta práctica, llamado BarridoUltrasonico, se muestra a continuación: import lejos.nxt.*; import lejos.robotics.navigation.*; public class BarridoUltrasonico static UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S2); static Pilot pilot = new TachoPilot(5.6f, 11.3f, Motor.C, Motor.B); static int xMin; //posición de la menor lectura static int distMin=170; //registro de menor lectura public static void main(String[] args) pilot.setTurnSpeed(60); pilot.rotate(360f,true); for (int x=0; x<100;x++) while (pilot.getAngle()<3.6f*x)Thread.yield(); int dist =sonic.getDistance(); LCD.setPixel(1,x,(int)(dist/2.65)); LCD.drawInt(distMin,4, 0, 7); LCD.drawInt((int)(xMin*3.6),4, 10, 7); if (distMin>dist) distMin=dist; xMin=x; pilot.rotate((float)(xMin*3.6)); pilot.travel(distMin-2); Button.waitForPress();

72

3.5.6 Pruebas y conclusiones Las pruebas de esta práctica se realizaron en diferentes espacios con paredes y obstáculos. En la mayoría de los casos, el robot acertaba al identificar el objeto más cercano, exceptuando aquellos que no tuvieran una superficie lisa. Este tipo de prácticas puede ser útil para complementar el analizar de espacios y la construcción de mapas. 3.5.7 Clases y métodos empleados En la tabla 3.6 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa BarridoUltrasonico, perteneciente a la práctica Sensor Ultrasónico: Buscador de objetos cercanos.

Método Clase Descripción

getDistance UlstrasonicSensor Retorna el valor de la distancia leída por en sensor ultrasónico.

setTurnSpeed Pilot Define la velocidad de rotación del vehículo.

getAngle Pilot Retorna el valor de grados de rotación del vehículo sobre la superficie.

rotate Pilot Hace girar el vehículo sobre su eje central.

travel Pilot Desplaza el vehículo la distancia especificada.

Tabla 3.6: Resumen de métodos usados en el programa de la práctica “BarridoUltrasonico”.

3.6 Sensor de luz: Seguidor de línea mediante control on-off 3.6.1 Planteamiento del problema Diseñar un robot capaz de seguir una línea negra sobre una superficie blanca, evitando salirse de la ruta marcada por la misma línea. 3.6.2 Objetivo El objetivo de esta práctica es exponer una forma de emplear el sensor de luz, mediante la programación del lenguaje leJOS NXJ. Además de comprender una descripción más detallada del funcionamiento de un robot seguidor de línea, como la descrita en la sección 2.2.3 “Práctica: Seguidor de línea en lenguaje NXT-G”. En esta práctica también se expone una breve introducción a conceptos básicos de teoría de control, los cuales también se utilizarán en la primer práctica de aplicación del capítulo 4.

73

3.6.3 Estructura a emplear Se utilizará la estructura básica mencionada en la sección 1.5 del presente manual, añadiendo a esta, en su parte inferior delantera, el sensor de luz apuntando hacia abajo con una distancia aproximada de 5 milímetros con respecto a la superficie sobre la que se moverá el robot. 3.6.4 Fundamentos Un robot seguidor de línea, así como muchos otros tipos de robots, utilizan sistemas de control realimentado para manipular sus actuadores. En otras palabras, realizan una o más operaciones que, en presencia de perturbaciones impredecibles, tienden a reducir la diferencia entre la salida de un sistema y alguna entrada de referencia, y las realiza tomando en cuenta esta misma diferencia [17]. Un robot seguidor de línea con base a movimiento zigzag, utiliza un sistema de control de tipo on-off para la manipulación de cada uno de sus motores de manera simétrica. Para describir el funcionamiento de este sistema, se hablará de cada motor por separado. Así, el motor interno a la línea es aquél que se mueve cuando el sensor de luz detecta que se encuentra sobre esta, y el motor externo a la línea es aquel que se mueve cuando el sensor de luz se encuentra fuera de esta. El sistema de control del motor interno a la línea puede describirse del modo siguiente (cabe mencionar, que debido a la simetría de los motores, ambos sistemas son casi idénticos): Se desea que el robot mantenga su centro lo más cerca posible de uno de los bordes de la línea, al que se le llamará borde de referencia, es por ello que el sensor de luz (transductor de retroalimentación) sea colocado en esa ubicación. Esta posición del robot se define como la señal de entrada del sistema (señal de referencia o set point), mientras que la distancia de separación entre el centro del vehículo y el borde de referencia, es la variable controlada. En la figura 3.7 se muestra un diagrama donde se ilustran las partes del robot seguidor de línea anteriormente descritas.

74

Figura 3.7 Robot seguidor de línea

Cuando el sensor de luz indica que se encuentra sobre la línea, la variable controlada se encuentra por debajo de la señal de referencia, por lo que el sistema de control mantendrá encendido el motor interno a la línea (planta o sistema controlado), con una velocidad constante, hasta que esta variable alcance o rebase la señal de referencia, entonces, este motor se detendrá. De manera simétrica, el sistema de control del motor externo a la línea tendrá un funcionamiento parecido, con la diferencia que éste tendrá su variable controlada por debajo de la señal de referencia cuando el sensor indique que se encuentra fuera de la línea. 3.6.5 Programación Al iniciar el programa se deberá utilizar una rutina de calibración para definir el SetPoint. La rutina propuesta para la calibración es la siguiente: public void Calibrar() LCD.drawString("Calibrar BLANCO",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Blanco= light.readNormalizedValue(); LCD.clear(); LCD.drawString("Calibrar NEGRO",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Negro= light.readNormalizedValue(); LCD.clear();

Con esta, se espera que el sensor de luz se coloque sobre la superficie del color indicado, antes de tomar las lecturas del sensor. Cabe mencionar que las variables Blanco y Negro, estarán definidas previamente.

75

Posterior a la calibración, se calculará el valor del SetPoint utilizando la media entre el valor obtenido en las variables Blanco y Negro, como se muestra en la siguiente línea de código: SetPoint=(Blanco+Negro)/2

Posteriormente se definirá la velocidad de los motores y se iniciará con la rutina de control, la cual estará contemplada dentro de un ciclo infinito. Dentro del ciclo se realizará la lectura del sensor de luz periódicamente para determinar si el valor leído es superior o inferior al SetPoint, y encender o apagar el motor correspondiente. Las líneas de código del ciclo de control on-off se muestran a continuación: while (true) LCD.drawInt(light.readNormalizedValue(),5,0,0); if (SetPoint <= light.readNormalizedValue()) Motor.C.forward(); Motor.B.stop(); else Motor.B.forward(); Motor.C.stop();

El código completo del programa, llamado SeguidorOnOff, se muestra a continuación: import lejos.nxt.*; public class SeguidorOnOff static LightSensor light = new LightSensor(SensorPort.S3,true); static int Negro; static int Blanco; static int SetPoint; public static void main(String[] args) LineaOnOff Robot = new LineaOnOff(); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPress(); Robot.Calibrar(); SetPoint=(Blanco+Negro)/2; Motor.B.setSpeed(500); Motor.C.setSpeed(500); while (!Button.ESCAPE.isPressed()) LCD.drawInt(light.readNormalizedValue(),5,0,0); if (SetPoint <= light.readNormalizedValue()) Motor.C.forward(); Motor.B.stop();

76

else Motor.B.forward(); Motor.C.stop(); public void Calibrar() LCD.drawString("Calibrar BLANCO",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Blanco= light.readNormalizedValue(); LCD.clear(); LCD.drawString("Calibrar Negro",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Negro= light.readNormalizedValue(); LCD.clear();

3.6.6 Pruebas y conclusiones El programa se probó inicialmente sin la rutina de calibración, sin embargo fue necesario anexarla, debido a que los cambios de iluminación afectaban la precisión del sistema y era necesario reprogramar el robot. La velocidad de los motores fue la ideal para una línea negra de 2 centímetros de grosor, con ésta el robot no se salía por el borde opuesto al de referencia y fue capaz de recorrer la línea en el menor tiempo posible. Al incrementar la velocidad de los motores, se puede provocar que el robot salga por el borde opuesto a la línea y se desvié de la ruta establecida. Al disminuir demasiado la velocidad, además de aumentar el tiempo de recorrido, también puede provocar que el robot se desvíe de la ruta establecida cuando encuentre curvas pronunciadas. Esta práctica, además de servir como material de apoyo para estudiantes cursando alguna de las Experiencias Educativas mencionadas en la introducción de éste capítulo, puede aplicarse en Teoría de Control. 3.6.7 Clases y métodos empleados En la tabla 3.7 se muestran los métodos del leguaje leJOS NXJ, utilizados en el programa SeguidorOnOff, perteneciente a la práctica “Sensor de luz: Seguidor de línea mediante control on-off”.

Método Clase Descripción waitForPress Button Espera a que algún botón sea

77

presionado y soltado.

LightSensor LightSensor Constructor de la clase LightSensor en donde se establece el puerto del sensor y el modo de uso.

readNormalizedValue LightSensor Retorna el valor entero de la lectura del sensor en un rango 0 a 1024

forward Motor Genera un movimiento continuo hacia delante en el motor asignado.

setSpeed Motor Establece la velocidad del motor en grados por segundo.

Tabla 3.7: Resumen de métodos usados en el programa de la práctica “SeguidorOnOff”.

78

Prácticas

avanzadas En este capítulo se exponen tres prácticas donde se emplean técnicas de programación más avanzadas que las expuestas en las prácticas del capítulo 3, utilizando programación en lenguaje leJOS NXJ. El la primera de ellas se expone el empleo de dos robots, ambos programados en lenguaje leJOS NXJ, para resolver una tarea específica comunicándose mediante comunicación Bluetooth. En la segunda práctica, se emplean conceptos básicos de teoría de control para el control de un robot seguidor de línea. En la tercera y última práctica se desarrolla una interfaz de comunicación entre ladrillo NXT y una cámara CMUcam3 usando un microcontrolador como interfaz. En cada una de las prácticas de este capítulo, se describe su objetivo, desarrollo, estructura física a emplear, código de programa y las pruebas realizadas durante

IV

79

su elaboración. Para el caso de la tercera práctica también se describe el programa empleado en el microcontrolador, así como el diagrama esquemático del circuito requerido.

4.1 Simulación de ciego y lazarillo 4.1.1 Planteamiento del problema Diseñar dos robots que realicen el simulacro de un ciego y un lazarillo. El robot lazarillo deberá contar con sensores que le permitan detectar obstáculos a su paso, así como para identificar el final de un recorrido dentro de un ambiente controlado. Al final de su recorrido, el robot lazarillo deberá indicarle al robot ciego, la ruta realizada durante su recorrido. El robot ciego deberá repetir el mismo trayecto sin usar algún sensor. 4.1.2 Objetivo El objetivo de esta práctica es exponer una forma de aplicar la comunicación Bluetooth entre dos ladrillos NXT para resolver una tarea en conjunto, mediante la programación en lenguaje leJOS NXJ. 4.1.3 Fundamentos El ambiente controlado, sobre el que se realizará la práctica, es una superficie rectangular de color blanca con 110cm de ancho y 66cm largo. En tres de los lados del rectángulo, hay paredes de 3cm de alto para delimitar el espacio y dos obstáculos que deberán evadir los robots (figura 4.0).

Figura 4.0 ambiente controlado de

la práctica “Simulación de ciego y lazarillo”

80

El robot lazarillo iniciará su recorrido desde una marca de color azul, ubicada en el lado sin pared del rectángulo, misma marca con la que identificará el fin del trayecto. Durante el trayecto, el robot lazarillo identificará los obstáculos y las paredes mediante el uso del sensor de contacto. Cada vez que el sensor de contacto se active, realizará una tarea de evasión y registrará la distancia recorrida hasta ese punto. De tal modo, que al identificar la marca azul sobre la superficie, dé por terminado el recorrido, transmita el registro de la ruta sobre la que se haya desplazado, usando la comunicación bluetooth con el robot ciego, y salga de la superficie. El robot ciego, estará a la espera de entablar comunicación con el robot lazarillo, y a que éste le transmita el registro de la ruta sobre la que deba desplazarse. Una vez concluida la transmisión, el robot ciego comenzará desplazarse usando las distancias registradas, repitiendo los grados de rotación realizados por el robot lazarillo, en cada obstáculo que se haya identificado. Se espera, que esta práctica pueda emplearse como material de apoyo para estudiantes cursando alguna de las siguientes Experiencias Educativas: Algoritmos Computacionales y Programación; Laboratorio de Robótica; Técnicas de Inteligencia Artificial para Instrumentación. 4.1.4 Estructura a emplear La estructura básica para el robot ciego será un vehículo como el que se describió en la sección 1.5 del capítulo 1, sin anexar algún sensor. Mientras que el robot lazarillo, tendrá la misma estructura, anexando en la parte frontal el sensor de contacto apuntando al frente junto a una estructura de bumper, como se describió en la sección 3.3.3 de este manual. Además, en su parte inferior delantera, deberá anexarse el sensor de luz apuntando hacia abajo con una distancia aproximada de 5 milímetros con respecto a la superficie sobre la que se moverá el robot. 4.1.5 Programación Para esta práctica se desarrollan dos programas diferentes, una para el robot lazarillo, al que se asigna como dispositivo maestro dentro de la comunicación por Bluetooth, y otro para el robot ciego, al que se asigna como dispositivo esclavo. El programa del robot lazarillo contiene una rutina similar a la empleada en la práctica “Sensor de contacto: Robot de evasión de obstáculos” en la sección 3.4, en donde el robot avanza hacia delante hasta detectar un obstáculo y lo evade usando un método definido dentro del mismo programa. El método de evasión para esta práctica es retroceder 3cm y rotar 25 grados hacia la izquierda. Al método de evasión se añade el registro de la distancia recorrida entre cada

81

obstáculo mediante método getTravelDistance de la clase Pilot, y el almacenaje de dicho valor en un array. El método anteriormente descrito, llamado Evade, se muestra a continuación:

public void Evade () LCD.clear(); LCD.drawString("Evade",0,7); pilot.travel(-3); pilot.rotate(25); tramo++; Recorrido[tramo]=pilot.getTravelDistance()-Recorrido[tramo-1]; LCD.clear();

Cabe mencionar, que las variables Recorrido[ ] y tramo debieron de haberse definido previamente como variables globales. Mientras el robot lazarillo obtenga el valor del sensor de luz equivalente al del color blanco, éste se comportará como el robot de evasión de obstáculos. Al detectar la marca azul sobre la superficie, éste se detendrá, realizará la conexión por Bluetooth con el robot ciego, y enviará el contenido del array Recorrido[ ], para finalizar su tarea. El programa completo del robot lazarillo, llamado Lazarillo, se muestra a continuación: import java.io.IOException; import lejos.robotics.navigation.*; import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*; import javax.bluetooth.*; import lejos.util.*; public class Lazarillo static float[] Recorrido=new float[100]; static int tramo=0; static Pilot pilot = new TachoPilot(5.6f, 11.3f, Motor.C, Motor.B); static TouchSensor touch = new TouchSensor(SensorPort.S1); static LightSensor light = new LightSensor(SensorPort.S3,true); static Delay delay = new Delay(); static SoundSensor sonido = new SoundSensor(SensorPort.S4); public static void main(String[] args) throws Exception Lazarillo Robot = new Lazarillo(); LCD.drawString("LAZARILLO",0,2); LCD.drawString("Presione ENTER",0,4); Button.ENTER.waitForPress(); LCD.clear(); LCD.drawString("Esperando...",0,4); while (sonido.readValue()<90); LCD.clear();

82

while (light.readNormalizedValue()>400) pilot.forward(); LCD.drawString("Avanzando",0,7); if (touch.isPressed()) Robot.Evade(); pilot.stop(); Recorrido[tramo]=pilot.getTravelDistance()-Recorrido[tramo-1]; String name = "NXT"; LCD.clear(); LCD.drawString("Conectando...", 0, 0); RemoteDevice btrd = Bluetooth.getKnownDevice(name); if (btrd == null) LCD.clear(); LCD.drawString("Sin dispositivo", 0, 0); BTConnection btc = Bluetooth.connect(btrd); if (btc == null) LCD.clear(); LCD.drawString("Error conexion", 0, 0); LCD.clear(); LCD.drawString("Connectado", 0, 0); DataInputStream dis = btc.openDataInputStream(); DataOutputStream dos = btc.openDataOutputStream(); dos.writeInt(tramo); // Envía recorrido dos.flush(); for(int i=0;i<=tramo;i++) dos.writeFloat(Recorrido[i]); dos.flush(); LCD.drawString("Cerrando... ", 0, 0); dis.close(); dos.close(); btc.close(); LCD.clear(); LCD.drawString("Finalizado",3, 4); pilot.travel(35); public void Evade () LCD.clear(); LCD.drawString("Evade",0,7); pilot.travel(-3); pilot.rotate(25); tramo++; Recorrido[tramo]=pilot.getTravelDistance()-Recorrido[tramo-1]; LCD.clear();

83

El programa del robot ciego, espera por una conexión Bluetooth solicitada por el robot lazarillo. Cuando logra establecer una conexión Bluetooth, recibe la información enviada desde el robot lazarillo, guardándola en un array. Al finalizar la transmisión, cierra los canales de transmisión Bluetooth y comienza a recorrer las distancias definidas en la información recibida. Al finalizar de recorrer una distancia previa a un obstáculo, el robot ciego rota los mismos grados que haya rotado el robot lazarillo en su rutina de evasión, y continúa con la siguiente distancia hasta terminar el mismo recorrido. El programa completo del robot ciego, llamado Ciego, se muestra a continuación:

import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*; import lejos.robotics.navigation.*; public class Ciego static float[] Recorrido=new float[100]; static int tramo=0; static Pilot pilot = new TachoPilot(5.6f, 11.1f, Motor.C, Motor.B); public static void main(String [] args)throws Exception LCD.drawString("Esperando...",0,0); BTConnection btc = Bluetooth.waitForConnection(); LCD.clear(); LCD.drawString("Conectado",0,0); DataInputStream dis = btc.openDataInputStream(); DataOutputStream dos = btc.openDataOutputStream(); LCD.drawString("Recibiendo",0,1); tramo=dis.readInt(); for(int i=0;i<=tramo;i++) //Recibe recorrido Recorrido[i]=dis.readFloat(); dis.close(); dos.close(); Thread.sleep(100); LCD.clear(); LCD.drawString("Cerrando...",0,0); LCD.refresh(); btc.close(); LCD.clear(); for(int i=1;i<tramo;i++) //Realiza recorrido pilot.travel(Recorrido[i]); pilot.rotate(25);

84

4.1.6 Pruebas y conclusiones Al probar esta práctica se presentaron algunos problemas de precisión cuando el robot ciego intentaba repetir la misma trayectoria que el robot lazarillo. En algunas ocasiones, el robot ciego chocaba con obstáculo con los que el robot lazarillo no chocaba, aun después de repetir el recorrido de las misma distancias. Se puede suponer que estos problemas son originados por diferentes factores acumulados. Uno de los posibles factores que afectaron la precisión en las pruebas realizada de esta práctica, fue el punto de inicio de cada robot. La persona que colocara el robot en ese lugar, no tiene la suficiente precisión para calcular el grado angular hacia donde debe estar dirigido inicialmente, aun si se usan marcas de referencia. Este factor podría disminuir el error, si se diseñara una rutina de autocalibración de punto inicial para el robot mediante el uso del sensor de luz. Otro posible factor que afectó la precisión en las pruebas, es la exactitud en las medidas obtenidas de la estructura de los vehículos. Al no obtener medidas exactas requeridas por la clase pilot, el programa donde se introducen los valores de estas medidas, también realiza cálculos inexactas. El último posible factor contemplado, que afectó la precisión en las pruebas, fue el voltaje de las baterías. Si un robot presenta, en sus baterías, un menor voltaje que las baterías del otro robot, la potencia en sus motores se ve afectada y no recorrerán las mismas distancias. Los posibles problemas que afecten la precisión de esta práctica, no definen la imposibilidad de realizar esta práctica. Sin embargo, requieren un mayor análisis al planteado en el desarrollo de ésta práctica.

4.2 Seguidor de línea con control PI 4.2.1 Planteamiento del problema Diseñar un robot capaz de seguir una línea negra sobre una superficie blanca, recorriendo una distancia determinada, en el menor tiempo posible, evitando salirse de la ruta marcada por la misma línea. Emplear en el control de la velocidad de los motores del robot, un control proporcional integrador (PI) experimental.

85

4.2.2 Objetivo El objetivo de esta práctica es aplicar conocimientos básicos de teoría de control mediante el diseño de un control proporcional integral experimental, para la manipulación de un robot seguidor de línea. Se espera que el algoritmo empleado en esta práctica sea capaz de superar las expectativas de un robot seguidor de línea, basado en un control on-off, como el de la práctica “Sensor de luz: Seguidor de línea mediante control on-off” del capítulo 3. 4.2.3 Fundamentos En el capítulo 3 de este manual, se mencionó qué es un sistema de control realimentado, y se expuso la aplicación del control on-off para un robot seguidor de línea. En esta práctica se expone el control PI, un control más complejo, pero de mejor desempeño que el control on-off en muchas aplicaciones de ingeniería. El control PI, obtiene la señal de error de un sistema realimentado y emplea una operación proporcional y una integral, para corregir dicha señal. Cada operación del control PI, es un término independiente uno del otro, al realizar sus operaciones respectivamente, el control PI suma ambos términos para aplicarlo a la señal de salida del sistema. En la figura 4.1 se muestra de ejemplo, el diagrama de bloques de un sistema de control PI.

Figura 4.1 Diagrama de bloques de un sistema de control PI

El término proporcional hace un cambio a la salida que es proporcional al valor de la señal de error. La operación de este termino se realiza mediante la multiplicación de la señal de error por una constante Kp, llamada ganancia proporcional. En la ecuación 4.1 se muestra la fórmula con la que se obtiene el término proporcional, donde Pterm representa el término proporcional, Kp representa la ganancia integral, y )(e es la función del error con respecto al tiempo.

)(eKP pterm Ecuación 4.1

Si la ganancia proporcional es muy grande, con respecto al valor deseado, el sistema puede volverse inestable. En contraste una ganancia más pequeña que la

86

del valor deseado, termina siendo una respuesta muy pequeña a la salida, para un error muy grande. La contribución del término integral es proporcional tanto a la magnitud del error, como a la duración del mismo. Esta operación se realiza mediante la suma del error instantáneo sobre el tiempo y devuelve el error acumulado que debería haber sido corregido previamente. Este error acumulado se multiplica por una constante c, llamada ganancia integral, y sumado a la salida del controlador junto al término proporcional. En la ecuación 4.2 se muestra la fórmula con la que se obtiene el término integral, donde Iterm representa el término integral, Ki representa la ganancia integral, )(e

es la función del error con respecto al tiempo, y T representa un periodo de tiempo dado.

T

d0

iterm )(eKI Ecuación 4.2

El término integral acelera la corrección de la señal de error y disminuye las oscilaciones durante el estado estacionario del sistema que ocurren con un control proporcional puro. Al aplicar un control PI en el robot seguidor de línea, en vez de manipular el encendido o apagado de los motores, como en el caso del control on-off, se manipula la velocidad de cada uno de ellos, a la que se hará referencia como variable manipulada. Ésta será la cantidad que el controlador modificará para afectar el valor de la variable controlada [17]. En otras palabras, se medirá la distancia de separación entre el centro del vehículo y el borde de referencia (variable controlada) y se aplicará la velocidad requerida (variable manipulada) para corregir o limitar la desviación del valor medio (señal de error) respecto del valor deseado (señal de referencia o setpoint). En conjunto, un sistema de control PI en cada motor del robot seguidor de línea, ofrece las siguientes ventajas:

Disminución de la distancia de separación entre el centro del vehículo y el borde de referencia durante el recorrido, a lo que en teoría de control se refiere como un menor error en estado estacionario.

Mantenimiento de una velocidad constante en tramos de línea recta. Disminución del error generado en tramos de línea curva. Disminución del desplazamiento innecesario provocado por aceleraciones

bruscas, a lo que en teoría de control se refiere como un mayor amortiguamiento.

Disminución del tiempo de retorno al borde de referencia, a lo que en teoría de control se refiere a un menor tiempo de establecimiento.

87

4.2.4 Estructura a emplear Se utilizará la estructura básica mencionada en la sección 1.5 del presente manual, añadiendo a esta, en su parte inferior delantera, el sensor de luz apuntando hacia abajo con una distancia aproximada de 5 milímetros con respecto a la superficie sobre la que se moverá el robot. 4.2.5 Programación Antes de empezar a generar el algoritmo del programa, deberán definirse las variables y constantes que en este se involucrarán. Las variables manipuladas de cada sistema serán las velocidades tanto del motor B y del motor C; la variable controlada, designada por la posición del centro del robot con respecto al borde de referencia, se obtendrá mediante la señal obtenida por el sensor de luz. Éste deberá ser calibrado con un valor máximo (designado por el valor obtenido sobre el blanco), y un valor mínimo (designado por el valor obtenido sobre el negro). Con los valores obtenidos de la calibración deberá establecerse el setpoint, éste representará la posición del borde de referencia y será obtenido por medio de la media de los valores máximo y mínimo (blanco y negro). Además, la operación de control utilizará una señal de error, una ganancia proporcional (Kp) y una ganancia integral (Ki). La señal de error se definirá por la diferencia del setpoint menos el valor sensado, mientras que ambas ganancias se obtendrán de manera experimental. Representando la salida del sistema, se definirán las variables de velocidad para cada motor y una velocidad constante, llamada dentro del programa como velocidad0, ésta última definirá la velocidad de ambos motores cuando la señal de error sea igual a 0. La primera operación que debe realizar el robot es la calibración. En ésta, deberá intervenir el usuario presionando el botón ENTER tras posicionar el sensor de luz justo arriba de la superficie blanca para que el robot adquiera ese valor y lo almacene como su valor máximo. De igual manera, este proceso se repetirá para adquirir el valor mínimo posicionando el sensor sobre la línea negra. Una forma de realizar la operación de calibración para cada uno de los valores anteriormente mencionados, es utilizando el siguiente método incluido dentro del programa, llamado Calibrar: Cabe aclarar que el sensor de luz debió de haberse inicializado previamente.

public void Calibrar() LCD.drawString("Calibrar BLANCO",0,2); LCD.drawString("Presione Enter",0,4);

88

Button.ENTER.waitForPressAndRelease(); Blanco= light.readNormalizedValue(); LCD.clear(); LCD.drawString("Calibrar NEGRO",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Negro= light.readNormalizedValue(); LCD.clear();

Posterior a la obtención del valor máximo y mínimo, es necesario calcular el setpoint como se muestra en la siguiente línea de código:

SetPoint=((Blanco+Negro)/2);

Una vez realizada la calibración y definido el setpoint, los motores del robot podrán encenderse e iniciar el ciclo de seguimiento de la línea. Al principio de este ciclo, el robot obtendrá la lectura del sensor de luz, y con ella calcular la señal de error, como se muestra las siguientes líneas de código: int Sens = light.readNormalizedValue();

Error= SetPoint - Sens; Con estos valores, se procederá a controlar la velocidad de cada motor por separado. Para emplear la misma señal de error en el proceso de control de cada motor puede plantearse una analogía entre la simetría de la estructura física del robot y la simetría entre los valores obtenidos en las mediciones. Puede observarse que al colocar el sensor sobre el color blanco se obtiene un error de signo positivo, y al colocar el sensor sobre el color negro se obtiene un error con la misma magnitud que el caso anterior pero con signo negativo. Con esto, puede establecerse que la velocidad para uno de los motores será igual a la resta de la velocidad0 (velocidad constante) menos la respuesta del control PI. Mientras que la velocidad para el motor contrario, será igual a la suma de la velocidad0 más la respuesta del control PI. Así, al obtener un valor en la señal de error equivalente a 0, ambos motores funcionarán a la misma velocidad. Si este valor cambia, uno de los motores disminuirá su velocidad y el otro la aumentará. El ciclo principal del programa, descrito anteriormente, se muestra en las siguientes líneas de código: while (!Button.ESCAPE.isPressed()) // ciclo: mientras el boton ESCAPE no sea presionado

Sens = light.readNormalizedValue(); // guarda el valor sensado en la variable Sens Error= SetPoint - Sens;

89

// Calcula el error VelocidadB = Velocidad0+Robot.PI();

//controla potencia para el motor B VelocidadC = Velocidad0-Robot.PI(); //controla potencia para el motor C Motor.B.setSpeed((int)VelocidadB); //suministra la potencia controlada al motor B Motor.C.setSpeed((int)VelocidadC); //suministra la potencia controlada al motor C

La instrucción Robot.PI( ), dentro de las líneas de código anteriormente mostradas, hacen llamado al método para obtener respuesta del control PI. Este método es incluido en el mismo programa. Este método, llamado PI, es una adaptación del seudo código para un control PID discreto, desarrollado en el artículo “PID without a PhD” [18]. Para este método se definen nuevas variables: los términos proporcional e integral, el estado integrador, y los valores máximo y mínimo del estado integrador. El estado integrador estará definido por el error acumulado, y éste se representa de manera discreta como la suma de la misma variable más el error. Los valores máximo y mínimo del estado integrador evitan que se acumule un error mayor al máximo valor para la variable controlada, para el caso del robot seguidor de línea, se definirá como máximo un valor igual a la diferencia del setpoint, menos el valor obtenido en la lectura del mínimo valor del sensor de luz (equivalente al color negro), y 0 para el valor mínimo del estado integrador. El término de integración se obtiene mediante la multiplicación del estado integrador por la ganancia de integración. Y finalmente el término proporcional se obtiene por la multiplicación de la señal de error por la ganancia proporcional. A continuación se muestra el método PI, el cual se incluye dentro del programa. public static double PI() double Integral=0; //Estado integrador. double iMax, iMin; //Valor Integral máximo y mínimo. double pTerm; //Término proporcional. double iTerm; //Término integral. pTerm= Kp*Error; //Calcula término proporcional. iMax= SetPoint-Negro; iMin= 0; Integral= Integral + Error; if (Integral > iMax) Integral= iMax; else if (Integral < iMin) Integral= iMin; iTerm= Ki*Integral; //Calcula término integral. return (pTerm + iTerm); //Retorna la suma de ambos términos

90

Se observa que el método anterior, es del tipo double, lo cual hace que retorne un valor del mismo tipo al concluir su ejecución. Con ello, la velocidad calculada para cada motor finalmente es asignada al motor respectivo mediante el método setSpeed de la clase Motor. Cabe mencionar que para disminuir el tiempo de experimentación en la búsqueda de la ganancia proporcional e integral, puede incluirse una rutina que permita modificar esta variable durante el funcionamiento del programa en cuestión, así como el despliegue de información de las variables en tiempo real. El código completo del programa, con nombre LineaPI, se muestra a continuación incluyendo las rutinas de variación de la ganancia proporcional e integral así como el de despliegue de información en tiempo real.

import lejos.nxt.*; import lejos.util.*; public class LineaPI /**************Definición de Variables****************/ static double VelocidadB; // Velocidad motor B static double VelocidadC; // Velocidad motor C static int Negro; // Valor mínimo static int Blanco; // Valor máximo static double Kp=2.0; //Constante proporcional static double Ki=0.00; // Constante integral static int Error; // Señal de error static int SetPoint; // Señal de referencia static int Sens; // Valor sensado static int Velocidad0=450; // Velocidad constante static int x=0; // Variable x para gráfica static Stopwatch sw = new Stopwatch(); static LightSensor light = new LightSensor(SensorPort.S3,true); /*************Inicia programa Main*********************/ public static void main (String[] args) LineaPI Robot= new LineaPI(); int display=0; LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPress(); Robot.Calibrar(); LCD.clear(); SetPoint=((Blanco+Negro)/2); Motor.B.forward(); Motor.C.forward(); sw.reset(); while (!Button.ESCAPE.isPressed()) Sens = light.readNormalizedValue(); Error= SetPoint - Sens;

91

VelocidadB = Velocidad0+Robot.PI(); VelocidadC = Velocidad0-Robot.PI(); Motor.B.setSpeed((int)VelocidadB); Motor.C.setSpeed((int)VelocidadC); if (display==0) Robot.Grafica(); else Robot.Datos(); if (Button.ENTER.isPressed()) Robot.Ajuste(); if (Button.RIGHT.isPressed()) if (display==0) LCD.clear(); display=1; else LCD.clear(); display=0; sw.reset(); Button.RIGHT.waitForPressAndRelease(); //*****************Calibración*************************/ public void Calibrar() LCD.drawString("Calibrar BLANCO",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Blanco= light.readNormalizedValue(); LCD.clear(); LCD.drawString("Calibrar Negro",0,2); LCD.drawString("Presione Enter",0,4); Button.ENTER.waitForPressAndRelease(); Negro= light.readNormalizedValue(); LCD.clear(); //**************Control PI*****************************// public static double PI() double Integral=0; double iMax, iMin; double pTerm; double iTerm; pTerm= Kp*Error; iMax= SetPoint-Negro; iMin= 0; Integral= Integral + Error; if (Integral > iMax) Integral= iMax; else if (Integral < iMin) Integral= iMin;

92

iTerm= Ki*Integral; //Calcula término integral. return (pTerm + iTerm); //****************Ajuste de constantes****************// public void Ajuste() LCD.clear(); Motor.B.stop(); Motor.C.stop(); while (Button.readButtons()>0); while (!Button.ENTER.isPressed()) if (Button.LEFT.isPressed()) Kp=Kp-0.1; Button.LEFT.waitForPressAndRelease(); if (Button.RIGHT.isPressed()) Kp=Kp+0.1; Button.RIGHT.waitForPressAndRelease(); LCD.drawString("Proporcional",0,0); LCD.drawInt((int)Kp,3,0,2); LCD.drawInt((int)((Kp-(int)Kp)*10),0,4,2); LCD.drawString(".",3,2);LCD.drawString("Kp",7,2); while (Button.readButtons()>0); LCD.clear(); while (!Button.ENTER.isPressed()) if (Button.LEFT.isPressed()) Ki=Ki-0.1; Button.LEFT.waitForPressAndRelease(); if (Button.RIGHT.isPressed()) Ki=Ki+0.1; Button.RIGHT.waitForPressAndRelease(); LCD.drawString("Integral",0,0); LCD.drawInt((int)Ki,3,0,2); LCD.drawInt((int)((Ki-(int)Ki)*10),0,4,2); LCD.drawString(".",3,2);LCD.drawString("Ki",7,2); while (Button.readButtons()>0); LCD.clear(); Motor.B.forward(); Motor.C.forward(); //************Muestra los Datos************************// public void Datos() LCD.drawInt(Sens,5,0,0);LCD.drawString("RAW",7,0); LCD.drawInt(Error,5,0,1);LCD.drawString("Error",7,1); LCD.drawInt((int)VelocidadB,5,0,2);LCD.drawString("MotorB",7,2); LCD.drawInt((int)VelocidadC,5,0,3);LCD.drawString("MotorC",7,3); LCD.drawInt((int)Kp,3,0,4); LCD.drawInt((int)((Kp-(int)Kp)*10),0,4,4); LCD.drawString(".",3,4);LCD.drawString("Kp",7,4); LCD.drawInt((int)Ki,3,0,5);

93

LCD.drawInt((int)((Ki-(int)Ki)*10),0,4,5); LCD.drawString(".",3,5);LCD.drawString("Ki",7,5); //***************Muestra Gráfica*************************// public void Grafica() LCD.drawInt(sw.elapsed(),0,0); if (sw.elapsed()>25*x) if (x<99) LCD.setPixel(1,x,(int)((Blanco-Sens)*64/(Blanco-Negro))); x++; else if(sw.elapsed()>5000) LCD.clear(); sw.reset(); x=0;

4.2.6 Pruebas y conclusiones El robot diseñado en esta práctica fue probado sobre un circuito ovalado (figura 4.2), formado por una línea negra de 2 centímetros de ancho, sobre una superficie blanca. Dicho circuito cuenta con 200cm de perímetro, de los cuales, 60cm son en línea recta.

Figura 4.2 Forma del circuito utilizado en las pruebas

de la práctica “Seguidor de línea con control PI”.

Al iniciar las pruebas, se desconocían los valores necesarios para las ganancias del control PI. Por tal motivo, se obtuvieron dichos valores de manera experimental, por tanteo. En la tabla 4.1 se muestra la lista de pruebas realizadas durante la búsqueda de las ganancias del control PI, indicando el número de prueba, los valores de ganancia empleados, y comportamiento del robot en cada caso. Prueba Kp Ki Comportamiento

1° 3 0 Siguió toda la línea con un intenso movimiento en zigzag. 2° 6 0 El movimiento de los motores fue muy brusco, provocando

que el robot saliera por el otro borde de la línea y terminara perdiendo la trayectoria.

94

3° 1 0 Siguió los tramos de línea recta, pero en el tramo de línea curva perdió de la trayectoria.

4° 2 0 Siguió toda la línea con un ligero movimiento en zigzag. 5° 2 0.3 Al comienzo de una línea recta hizo un ligero movimiento en

zigzag, y gradualmente corrigió ese movimiento hasta avanzar sin zigzagueo. Siguió toda la línea en menor tiempo que en las pruebas anteriores.

Tabla 4.1 Pruebas realizadas durante la búsqueda de las ganancias del control PI de la práctica “Seguidor de

línea con control PI”. Con las pruebas realizadas, se comprobó que un control PI mejora el desempeño de un seguidor de línea, siempre y cuando se encuentren los valores aproximados de ganancia proporcional e integral necesarios para el sistema. Esta práctica puede ser empleada para observar el efecto de un control PI cuando se cuenta con conocimientos básico sobre teoría de control. Cabe destacar que la posibilidad de añadir la operación de control derivativo a esta práctica no queda descartada. Se espera que esta práctica pueda emplearse como material de apoyo para estudiantes cursando alguna de las siguientes Experiencias Educativas: Algoritmos Computacionales y Programación; Laboratorio de Robótica; Técnicas de Inteligencia Artificial para Instrumentación; Teoría de Control; Control Digital; Programación Orientada a Objetos.

4.3 Interfaz entre ladrillo NXT y cámara CMUcam3 4.3.1 Planteamiento del problema Diseñar un robot con capacidad de visión para ubicar la posición de un objeto con color específico, a lo largo del eje horizontal, utilizando la cámara CMUcam3. 4.3.2 Objetivo El objetivo fundamental de esta práctica es exponer una forma de emplear el protocolo I2C del Ladrillo NXT, mediante el lenguaje leJOS NXJ, para comunicarse con un dispositivo ajeno al Kit Lego Mindstorms NXT. Para este caso, se empleará la cámara CMUcam3, la cual utiliza el protocolo RS232, por tal motivo en esta práctica también se expone la elaboración de una interfaz de comunicación entre ambos dispositivos. 4.3.3 Fundamentos La cámara CMUcam3 (figura 4.3) es un sensor de visión basado en una computadora embebida programable. Ésta ha sido diseñada específicamente para su uso en pequeños sistemas embebidos, como la robótica. Trabaja sobre un procesador ARM7TDMI con 64Kb de RAM y cuenta con una máxima resolución de

95

352X288 pixeles. Su alimentación es de 6v a 15v de corriente directa y por lo menos con 150mA [19].

Figura 4.3 Cámara CMUcam3

Para ser programada y comunicarse con otros dispositivos, utiliza el protocolo RS232. Para esta práctica se empleará un programa desarrollado para emular el firmware de la cámara CMUcam2, con lo cual se obtendrán datos específicos de las imágenes procesadas por el sensor de visión, suficientes para esta práctica. De este modo, no será necesario profundizar en la programación de la CMUcam3. En el apéndice E se exponen los procedimientos de preparación de la cámara CMUcam3 para emplearla en esta práctica, donde se muestra cómo instalar el programa de emulación del firmware de la cámara CMUcam2, así como la instalación y modo de uso del software “CMUcam2 GUI”, herramienta que permite configurar la cámara desde una computadora. La cámara CMUcam3, emulando el firmware de su predecesora CMUcam2, utiliza una serie de instrucciones compuestas por cadenas de caracteres en código ASCII6 (se expone una lista completa de comandos en el documento “CMUcam2 Vision Sensor: User Guide”) [20]. Ésta se mantiene inactiva y a la escucha de alguna solicitud realizada por un dispositivo maestro. Cuando recibe una instrucción de solicitud, la cámara comienza a realizar la operación solicitada, y devuelve al dispositivo maestro los datos correspondientes. Cuando concluya la transmisión exitosa de una instrucción recibida, la cámara CMUcam3 devolverá la cadena ACK (acrónimo de la palabra en inglés acknowledge). Si existiera algún problema durante la transmisión, o si recibiera una instrucción desconocida, se devolverá la cadena NCK. Para identificar el fin de una cadena recibida o enviada por la CMUcam3, se lee el byte \r (señal de return representada en código ASCII por el número 13). Para establecer una comunicación entre el ladrillo NXT y la cámara CMUcam3, es necesario diseñar una interfaz capaz de traducir datos del protocolo I2C a RS232 y viceversa. En el documento “Lego Mindstorms NXT Camera” [21], se expone una interfaz usando un microcontrolador PIC16F84 programado en lenguaje ensamblador para un programa del ladrillo NXT en lenguaje NXC. En esta práctica se propone diseñar una adaptación de dicha interfaz para un microcontrolador

6 Un caracter en código ASCII esta compuesto de un byte con valores posibles entre 0 y 127.

96

PIC18F4550 (o cualquier PIC de la familia PIC18FXX) programado en lenguaje C y el ladrillo NXT programado en lenguaje leJOS NXJ. Para la transmisión de una instrucción desde el ladrillo NXT a la cámara CMUcam3, el ladrillo NXT envía el byte 128 al puerto I2C del PIC (los valores de byte superiores al 127 no pertenecen al código ASCII), indicando el inicio del envío de una instrucción, desde ese momento, cualquier byte que reciba el PIC por el puerto I2C será enviado por el puerto RS232. Una vez iniciada el envío de la instrucción, el ladrillo NXT envía un byte de la instrucción al puerto I2C del PIC, seguido de la solicitud de lectura del último byte recibido por el PIC, para el diagnóstico de la comunicación. Esta función se repetirá con cada byte de la instrucción. Para finalizar la instrucción, el ladrillo NXT envía el byte \r. La cámara CMUcam3, al recibir el byte \r, comienza a realizar la operación solicitada y envía la cadena de caracteres de la respuesta correspondiente por el puerto RS232l al PIC, este último almacena la respuesta en un array en espera de ser solicitada por el ladrillo NXT. Para leer la respuesta enviada por la cámara CMUcam3, el ladrillo NXT envía el byte 129 al puerto I2C del PIC. Con lo cual, el PIC dejará de enviar por el puerto RS232 cualquier byte recibido por el puerto I2C. Seguido de esto, el ladrillo NXT solicita por el puerto I2C del PIC, byte por byte, el contenido del array donde se haya almacenado la respuesta de la cámara CMUcam3. Al finalizar la transmisión de la respuesta de la cámara CMUcam3 al ladrillo NXT, el ladrillo NXT envía el byte 130 al puerto I2C del PIC para indicar dicha conclusión y permitir el envío de una nueva instrucción. En la figura 4.4 se muestra el ejemplo de secuencia de transmisión de una instrucción enviado por el ladrillo NXT a la cámara CMUcam3 por medio del PIC como interfaz de comunicación.

97

Figura 4.4 Secuencia de transmisión de la instrucción

GV (Get Version)7, y obtención del resultado.

En esta práctica se utilizarán dos instrucciones para el uso de la cámara CMUcam3 con la emulación del firmware de la cámara CMUcam2 [20]:

ST Rmin Rmax Gmin Gmax Bmin Bmax \r Set tracking: permite establecer los parámetros de seguimiento del color RGB (rojo, verde y azul por sus siglas en inglés), con valor máximo y

7 La instrucción GV (Get Version), de la emulación del firmware de la cámara CMUcam2, devuelve la versión del firmware instalado sobre la cámara CMUcam3.

98

mínimo para cada término. Estos valores son guardados para el uso del comando TC, descrito a continuación

TC \r Track a Color: al usar esta instrucción, la cámara CMUcam3, comienza a seguir el color RGB con valores máximos y mínimos establecidos con la instrucción ST. Mientras la cámara CMUcam3 no reciba el byte \r nuevamente, ésta devolverá (por medio del puerto RS232), una cadena de caracteres, llamada Paquete tipo T, con información de la imagen procesada.

El Paquete tipo T, devuelto tras el uso de la instrucción TC, contiene los siguientes parámetros:

T mx my x1 y1 y2 pixels confidence\r

mx: coordenada en x de la media de la masa. my: coordenada en y de la media de la masa. x1: coordenada en x de la esquina superior izquierda de la masa. y1: coordenada en y de la esquina superior izquierda de la masa. x2: coordenada en x de la esquina inferior derecha de la masa. y2: coordenada en x de la esquina inferior derecha de la masa. pixels: Número de pixeles en la región encontrada, escalada y con tope

máximo de 255: (pixeles+4)/8 confidence: Valor obtenido por la operación (número de pixeles / área)*256

del rectángulo comprendido y con tope máximo de 255. 4.3.4 Estructura a emplear e implementación electrónica Se utilizará la estructura básica mencionada en la sección 1.5 del presente manual, añadiendo a ésta en su parte delantera, una estructura capaz de mantener fija la cámara CMUcam3, así como integrar una estructura que soporte el circuito diseñado para la interfaz de comunicación. La interfaz de comunicación empleada para las pruebas de esta práctica se diseñó usando un PIC18F4550 [21] (diagrama en la figura 4.5), con un cristal de 20Mhz. La comunicación RS232 entre el PIC y la cámara CMUcam3 funciona a través del circuito integrado MAX232 [22], para ajustar los niveles de voltaje de la salida del PIC a los requeridos en la entrada de la cámara CMUcam3.

99

Figura 4.5 Diagrama de pines del PIC18F4550.

El PIC se alimenta de 5v regulados por la cámara CMUcam3 (diagrama en la figura 4.6) [19], la cual cuenta con un puerto de alimentación de salida, diseñada principalmente para servomotores. Para alimentar la cámara CMUcam3 puede conectarse una pila de 9v a la entrada correspondiente de alimentación.

Figura 4.6 Diagrama de conectores de la cámara CMUcam3.

De acuerdo al los requerimientos del diseño y las hojas de datos de los componentes, el diagrama esquemático empleado en esta práctica se muestra en la figura 4.7. Las especificaciones de las conexiones de los puestos de entrada del ladrillo NXT se muestran en el apéndice D.

100

Figura 4.5 Diagrama esquemático empleado para la interfaz de comunicación entre el ladrillo NXT y la cámara

CMUcam3.

4.3.5 Programación A continuación se expone la programación empleada para el ladrillo NXT dentro de esta práctica. La programación del PIC, encargado de la comunicación entre el ladrillo NXT y la cámara CMUcam3, se expone en el apéndice F. Para esta práctica se ha creado una clase llamada CMUcam2 (nombre asignado debido a que esta clase soporta instrucciones de la emulación del firmware de la cámara CMUcam2 instalado sobre la cámara CMUcam3), encargada del manejo de la transferencia de instrucciones a la cámara CMUcam3, así como para la lectura e interpretación de los datos obtenidos de la misma. La clase CMUcam2 contiene un método constructor, métodos para el envío y recepción de comandos byte por byte, y métodos para la obtención, manipulación y despliegue de datos específicos obtenidos desde la cámara CMUcam3. La clase CMUcam2 completa se muestra a continuación:

import lejos.nxt.*; class CMUcam2 /***************** Definición de variables ********************/ private I2CPort I2Cport; private byte direccion; private byte[] data = 0x00; public byte[] Lectura=new byte[100]; private byte respuesta; private int ret;

101

/************* Constructor de la clase CMUcam2 ****************/ /* * @param sensorPort Puerto del sensor I2C. * @param direccion Dirección del dispositivo esclavo. */ public CMUcam2(I2CPort sensorPort, byte sensorDireccion) I2Cport = sensorPort; direccion = (byte)(sensorDireccion>>1); //Corrimiento de dirección hacia la derecha I2Cport.i2cEnable(I2CPort.STANDARD_MODE); /********************** Envía de un byte***********************/ // @param valor byte a enviar. public void enviaByte(byte valor) data[0] = valor; I2Cport.i2cStart(direccion, 0x00, 0, data, 1, 1); while (I2Cport.i2cBusy() != 0) Thread.yield(); /********** Obtiene un byte del dispositivo esclavo ***********/ //@return Retorna el valor recibido de 8 bits public byte recibeByte() ret = I2Cport.i2cStart(direccion, 0x00, 0, null,1,0); if (ret != 0) return -1; while (I2Cport.i2cBusy() != 0) Thread.yield(); ret = I2Cport.i2cComplete(data, 1); return ((byte)(0xFF & data[0])); /**** Envía un comando byte por byte al PIC (Método ayuda) ****/ /* * @param comando string de la instrucción a enviar. * @param respuesta variable de verificación del * último byte enviado. * @param return retorna 0 se realiza una transmisión * exitosa; retorna 1 si existe un error en la * transmisión. */ public int enviaBytePorByte(String comando) enviaByte((byte)128); do enviaByte((byte)13); respuesta= recibeByte(); while (respuesta != 13); for (int i=0; i< comando.length(); i++) enviaByte((byte)comando.charAt(i)); respuesta= recibeByte(); if (respuesta != (byte)comando.charAt(i)) return 1; //Error de transmisión enviaByte((byte)13);

102

respuesta= recibeByte(); if(respuesta != 13)return 1; return 0; //Transmisión exitosa /************* Envía un comando completo al PIC ***************/ // @param instrucción string de la instrucción a enviar. public void enviaComando(String instrucción) do ret=enviaBytePorByte(instrucción); while (ret!=0); /************** Lectura de buffer de entrada ******************/ //@return Lectura array del buffer de entrada public byte [] obtenResultado() enviaByte((byte)129); for (int i=0;i<32;i++) Lectura[i]=recibeByte(); enviaByte((byte)130); return (Lectura); /*************** Muestra en display un array ******************/ // @param array buffer de entrada almacenado en un array // declarado por el usuario. public void printArray(byte[] array) int i=0; for (int y=0;y<6;y++) for (int x=0;x<16;x++) LCD.drawString((char)(0xFF & array[i])+"", x, y); i++; /********************** Vacía array ***************************/ // @param array buffer de entrada almacenado en un array // declarado por el usuario. public void clear(byte[] array) for (int i=0;i<array.length;i++) array[i]=0; /**************** Verifica ACK en string **********************/ /* * @param array buffer de entrada almacenado en un array * declarado por el usuario. * @param return retorna 0 si el buffer de entrada contiene * el string "ACK" o ":"; retorna 1 si el buffer de * entrada contiene cualquier string diferente a * "ACK" o ":". */ public int Track_string(byte[] array)

103

String datos = new String(array); if (datos.indexOf("ACK")==-1 || datos.indexOf(":")==-1) return 0; else return 1; /*************** Obtiene centro de masa en X ******************/ /* * @param array buffer de entrada almacenado en un array * declarado por el usuario. * @param centroX valor entero de la coordenada en X del * paquete tipo T enviado por la cámara CMUcam3 */ public int mx(byte[] array) String datos = new String(array); int space=0; boolean flag=false; String temp="0"; for (int i=0;i<datos.length();i++) if(datos.charAt(i)==' ') space++; if (space==1) flag=true; else flag=false; if(datos.charAt(i)>='0' && datos.charAt(i)<='9' && flag==true) temp=temp+datos.charAt(i); int centroX=Integer.parseInt(temp); return (centroX);

Utilizando la clase CMUcam2, mostrada anteriormente, se ha creado el programa de un robot, llamado SeguidorVision, capaz de seguir una pelota de color rojo (los parámetros RGB fueron adquiridos previamente desde el software CMUcam2 GUI), a lo largo del eje horizontal, utilizando la cámara CMUcam3. Al comienzo del programa SeguidorVision se definen las variables a utilizar y los parámetros de comunicación, con las siguientes líneas de código:

byte[] lectura=new byte[32]; // Array para buffer de entrada int centro=0; // Valor de coordenada en x del // centro de masa entregada por el // sensor int umbral=7; // Valor de umbral para el centro // de masa entregada por el sensor CMUcam2 CMU = new CMUcam2((I2CPort)SensorPort.S2,(byte)0xA4);

104

// Constructor de la clase CMUcam2, definiendo el puerto de salida // S2 y la dirección del dispositivo esclavo I2C 0xA4.

CMU.enviaComando("ST 118 194 48 78 58 88 "); // Definición del color RGB a seguir.

Posteriormente, se indica el inicio del seguimiento del color especificado mediante el envío de la instrucción TC a la cámara CMUcam3 y se define el ciclo principal del que no se saldrá mientras que no se presione el botón ESCAPE. Dentro del ciclo principal, se lee el buffer de entrada utilizando el método obtenResultado de la clase CMUcam3, se verifica si éste no contiene el string ACK, y se obtiene el valor de la coordenada en x del centro de la masa, obtenida por la cámara CMUcam3, mediante el método mx de la clase CMUcam2. Este último valor se guarda en la variable centro, la cual será empleada para definir el movimiento de los motores para seguir el color rojo. El código del programa llamado SeguidorVision se muestra a continuación:

import lejos.nxt.*; class SeguidorVision public static void main(String[] args) byte[] lectura=new byte[32]; int centro=0; int umbral=7; Motor.C.setSpeed(80); Motor.B.setSpeed(80); CMUcam2 CMU = new CMUcam2((I2CPort)SensorPort.S2,(byte)0xA4); CMU.enviaComando("ST 118 194 48 78 58 88 "); CMU.enviaComando("TC"); while(!Button.ESCAPE.isPressed()) lectura=CMU.obtenResultado(); CMU.printArray(lectura); int error=CMU.Track_string(lectura); if (error==0) centro=CMU.mx(lectura); LCD.drawInt(centro,5,5,4); CMU.clear(lectura); if (centro>(43+umbral)) Motor.C.backward(); Motor.B.forward(); if (centro<(43-umbral)) Motor.C.forward(); Motor.B.backward(); if(centro<(43+umbral) && centro>(43-umbral)) Motor.C.flt(); Motor.B.flt(); if(centro==0 || centro>87)

105

Motor.C.flt(); Motor.B.flt();

4.3.6 Pruebas y conclusiones Para la elaboración de esta práctica se realizaron pruebas independientes, utilizando la interfaz diseñada en este capítulo, para comunicar el ladrillo NXT con la hyperterminal de una computador por medio del puerto serial, de este modo se evaluó la confiabilidad de la transmisión del la interfaz. Los resultados de estas pruebas mostraron que la comunicación presentaba algunos bytes perdidos, sin embargo los métodos de la clase CMUcam2 se diseñaron para detectar algunos errores durante la transmisión. De este modo, se obtuvo transmisiones exitosas de instrucciones desde el ladrillo NXT a la cámara CMUcam3, pero relativamente lentas. La iluminación del espacio donde se realizaron las pruebas, fue un posible factor que dificultó la obtención de imágenes de la cámara CMUcam3, debido a que ésta era producida por lámparas de tubos fluorescentes (Este tipo de lámparas producen un parpadeo imperceptible al ojo humano). Al ser una iluminación en constante cambio, los colores obtenidos por la cámara también se verían afectados. El robot diseñado en esta práctica realizó la tarea deseada, con algunos retardos producidos por los factores acumulador anteriormente mencionados. La interfaz de comunicación diseñada para esta práctica, puede ser útil para implementar la comunicación entre el ladrillo NXT y otros tipos de dispositivos con comunicación RS232. Se espera que esta práctica pueda emplearse como material de apoyo para estudiantes cursando alguna de las siguientes Experiencias Educativas: Algoritmos Computacionales y Programación; Laboratorio de Robótica; Técnicas de Inteligencia Artificial para Instrumentación; Procesamiento de Imágenes; Programación Orientada a Objetos; Protocolos de Comunicación; Diseño de Interfases.

106

Conclusiones Tras concluir la elaboración de este manual de prácticas, se ha comprobado la versatilidad que ofrece el Kit Lego Mindstorms NXT mediante la programación en lenguaje leJOS NXJ para el aprendizaje y la elaboración proyectos en el campo de la robótica, u otras disciplinas afines. Con ello, también se han encontrado algunas ventajas que ofrece el lenguaje leJOS NXJ sobre el lenguaje oficial NXT-G, en el aprovechamiento de los recursos con los que cuenta el Kit Lego Mindstorm. Entre estas se pueden mencionar las siguientes:

Posibilidad de la lectura y control del contador de los encoders integrados en los motores del kit.

Posibilidad de utilizar los puertos de entrada para comunicar el ladrillo NXT con dispositivos ajenos al kit mediante el protocolo I2C.

Los programas ocupan un menor espacio en la memoria del ladrillo NXT. Es posible utilizar clases existentes del leguaje Java que permiten

desarrollar programas más complejos invirtiendo un menor tiempo. Es posible generar clases en lenguaje Java, compatibles al lenguaje leJOS

NXJ, que facilitan la programación de aplicaciones futuras. Es posible utilizar valores de punto flotante durante la programación. El tiempo de detección de errores en la programación, es disminuido debido

que al ser un lenguaje a texto permite tener una mejor visualización del programa en general.

Gracias a estas ventajas, fue posible realizar las prácticas avanzadas en este manual, donde se aplicaron conocimientos de teoría de control y protocolos de comunicación, de manera separada en cada caso. Sin embargo, se observaron algunas desventajas en la precisión de los resultados obtenidos, esto es debido al bajo costo del Kit Lego Mindstorms NXT, el cual no fue fabricado para garantizar dicha precisión. Esto no desacredita el buen desempeño del kit, en aplicaciones a nivel educativo y de investigación. Por otro lado, para la elaboración de las prácticas de este manual se han creado clases y métodos con la intensión de ofrecer la posibilidad de ser utilizados en aplicaciones futuras. Es por ello, que al concluir la recopilación de información para la elaboración de las prácticas, y el diseño de las mismas, se han contemplado algunas aplicaciones futuras para emplear el Kit Lego Mindstorms NXT o algún otro kit de desarrollo para la robótica. Entre estas, destacan aplicaciones con procesamiento de imágenes y exploración de entornos para creación de mapas mediante robots móviles.

107

Cabe mencionar, que las aplicaciones de este manual, no tan solo sirven en la robótica, sino además puede servir de apoyo para otras Experiencias Educativas que se imparten en la Ingeniería.

108

Apéndice A Instalación de leJOS NXJ y reemplazo del firmware A continuación se muestra la secuencia de pasos a seguir para instalar LeJOS NXJ sobre una computadora con sistema operativo Windows XP, así como para reemplazar el firmware original del ladrillo NXT por el de leJOS NXJ. Antes de comenzar la instalación se deberá contar con drivers para el ladrillo NXT instalados en la computadora. Éste se instala junto con el Software Lego Mindstorm NXT incluido en el kit. Si no se cuenta con dicho software, los drivers podrán ser descargarlo desde el siguiente link:

http://mindstorms.lego.com/en-us/support/files/driver.aspx#Driver

También es necesario contar con un Kit de desarrollo de Java (JDK por sus siglas en inglés “Java Development Kit”) el cuál podrá ser descargado desde:

http://www.oracle.com/technetwork/java/javase/downloads/index.html

La instalación del Driver así como la del Kit de desarrollo de Java serán guiadas por los mismos programas de instalación, por lo que no se requiere explicación detallada de ello.

1. Una vez instaladas los componentes previos se asignarán las variables de

entorno de algunos archivos que serán necesarios llamar desde el símbolo de sistema durante la programación en Java. Para ello se deberá abrir el “Pánel de control” y abrir “Sistema”. Se mostrará la pantalla de la figura A.1 donde se seleccionará la pestaña “Opciones Avanzada”.

Figura A.1 Selección de opciones avanzadas

en ventana Propiedades del sistema

109

2. seleccionar “Variables de entorno” (figura A.2).

Figura A.2 Selección de variables de entorno

3. En la ventana “Variables de entorno” (figura A.3), seleccionar “Path” dentro

de la lista de Variables de sistema y posteriormente presionar modificar.

Figura A.3 Ventana Variables de entorno

4. Aparecerá otra ventana en la que se deberá tener cuidado de no alterar los

valores que se encuentren en ella. Únicamente se agregará un punto y coma (;) al final de la línea de comando de “Valor de variable”, seguido del

110

directorio8 “C:\Archivos de programa\Java\jdk1.6.0_21\bin” omitiendo las comillas. Posteriormente presionar “Aceptar” (figura A.4).

Figura A.4 Ventana para modificar la variable del sistema Path

5. Regresando a la ventana “Variables de entorno”, dentro de la sección

“Variables de sistema” presionar “Nueva”. Con lo que aparecerá una ventana como la que se muestra en la figura A.5 con los parámetros en blanco. En ella se escribirá “JAVA_HOME” omitiendo las comillas, dentro del campo “Nombre de variable” y “C:\Archivos de programa\Java\jdk1.6.0_21” dentro del campo de “Valor de variable”. Posteriormente presionar “Aceptar”.

Figura A.5 Ventana de nueva variable del sistema

6. Repetir el paso anterior introduciendo la variable “javac” con el valor de

variable “C:\Archivos de programa\Java\jdk1.6.0_21\bin\javac”. De la misma forma introducir la variable “java” con el valor de variable “C:\Archivos de programa\Java\jdk1.6.0_21\bin\java”.

A continuación se muestra la tabla A.1 en la que se especifican todas las variables de entorno que se debieron crear o modificar con sus respectivos valores de variable.

Nombre de variable

Valor de Variable

Path C:\Archivos de programa\Java\jdk1.6.0_21\bin JAVA_HOME C:\Archivos de programa\Java\jdk1.6.0_21 javac C:\Archivos de programa\Java\jdk1.6.0_21\bin\javac java C:\Archivos de programa\Java\jdk1.6.0_21\bin\java

Tabla A.1 Variables de entorno necesarias para instalar leJOS NXJ

8 El directorio puede variar dependiendo a la carpeta en la que se haya instalado el JDK y la versión que se esté ocupando del mismo, si éste se desconoce utilice el Explorador de Windows para ubicar la carpeta \Bin del JDK instalado.

111

Una vez que se han establecido todas las trayectorias presionar “Aceptar” a todas las ventanas que se abrieron para guardar los cambios.

7. Ahora se procederá a descargar el programa de instalación de LeJOS NXJ desde la siguiente página: http://sourceforge.net/projects/lejos/files/ donde se elegirá el archivo “leJOS_NXJ_0.8.5-Setup.exe” o en su defecto la versión más nueva de LeJOS NXJ.

8. Ejecutar el archivo descargado en el paso anterior con el que se abrirá la ventana de la figura A.6 donde se presionará “Yes”.

Figura A.6 Ventana de confirmación para

la instalación de leJOS NXJ

y posteriormente en la ventana que aparece presionar “Next >”.

Figura A.7 Ventana de bienvenida al programa de instalación de leJOS NXJ

9. A continuación, deberá aparecer la ventana de la figura A.8 donde se muestra la versión del JDK instalado en la computadora. Si no es así, volver a los pasos del 1 al 6 para asegurarse que esté instalado el JDK y que todas las variables de entorno requeridas estén correctamente escritas.

112

Figura A.8 Ventana de información de la versión del JDK

instalado y el fólder de destino de instalación de leJOS NXJ

En la misma ventana se podrá cambiar la carpeta de destino de instalación presionando “Browse…”. Al terminar presionar “Next >”.

10. En la ventana siguiente se muestra la carpeta donde serán instalados los proyectos leJOS (figura A.9). Por defecto, ésta se ubica en el directorio de usuario. Sin embargo, puede ser cambiada presionando el botón “Browse…”.

Figura A.9 Ventana de información del fólder de destino

para los proyectos de leJOS NXJ

Para el presente manual, la ubicación será “c:\leJOSNXJProjects” para simplificar procesos de compilación y descarga de los programas mientras se use el símbolo de sistema.

113

Al presionar next> se mostrará la información de los directorio de destino para la instalación de LeJOS NXJ como se observa en la figura A.10.

Figura A.10 Ventana de confirmación de los datos de instalación para leJOS NXJ

Si la información es correcta, presionar “next >” con lo que comenzará la instalación.

11. Al concluir la instalación presionar “Finish” (figura A.11).

Figura A.11 Ventana de fin instalación de leJOS NXJ

12. Posteriormente aparecerá una nueva ventana con la que se procederá a reemplazar el firmware original del NXT con el firmware NXJ (figura A.12), en donde se deberá presionar "Start Program".

114

Figura A.12 Ventana de instalación de firmware leJOS NXJ

sobre el ladrillo NXT

13. En este paso es importante asegurarse que el ladrillo NXT se encuentre

encendido y conectado a la computadora por medio del cable USB antes Presionar “Acepta” sobre el mensaje mostrado (figura A13.)

Figura A.13 Mensaje

Posteriormente presionar “Sí” a cuando muestre el mensaje si se desea eliminar todos los archivos del ladrillo NXT (figura A14).

Figura A.14 Ventana previa a borrar todos

los archivos del ladrillo NXT

115

14. El programa procederá a instalar el firmware NXJ y al finalizar se desplegará un mensaje como el de la figura A.15 en el que preguntará si se desea repetir la operación al que se le responderá “Sí” en el caso de utilizar otro dispositivo NXT. Al presionar “No”, el programa se cerrará.

Figura A.15 Ventana para repetir la operación

de instalación de firmware sobre otro ladrillo NXT Nota: puede recurrir al programa de reemplazo del firmware NXJ en cualquier momento. Éste se abre con el archivo “nxjflashg.bat” ubicado en el directorio Bin de LeJOS NXJ. Por defecto éste se encuentra en “C:\Archivos de programa\leJOS NXJ\bin”.

116

Apéndice B Menú del sistema operativo de leJOS NXJ Cuando se enciende el ladrillo NXT, éste despliega sobre el display el logo de leJOS NXJ durante 3 segundos y después muestra el menú principal (figura B.1).

Figura B.1 Menú principal

En la primera línea de la figura A.1, se muestra el icono de voltaje de la batería, el nombre del ladrillo NXT, y el icono del dispositivo BlueTooth (en este ejemplo, el dispositivo BlueTooth se encuentra encendido). En las líneas posteriores se muestra el menú. Para navegar sobre éste, se utilizan los botones del ladrillo NXT como se describe en la tabla B.1. Los botones mencionados en dicha tabla, también son utilizados para la clase Button de leJOS NXJ, haciendo referencia a éstos con los mismos nombres que contiene la tabla. Sin embargo, para este último caso, sus funciones pueden variar dependiendo de las necesidades del proyecto.

Botón Nombre Función

LEFT

Desplaza cursor hacia arriba.

RIGHT

Desplaza cursor hacia abajo.

ENTER

Selecciona la opción señalada por el cursor.

ESCAPE

Sale del menú y regresa al previo. En el menú principal, apaga el Ladrillo NXT

Tabla B.1 Botones del ladrillo NXT

NOTA: mientras algún programa se encuentra en ejecución, al mantener presionados los botones ENTER y ESCAPE se apagará el ladrillo NXT inmediatamente, interrumpiendo cualquier tipo de operación.

117

Al seleccionar la primera opción del menú principal, Run Default, correrá el programa seleccionado como default. El programa default puede ser seleccionado desde el menú Files descrito más adelante. Al seleccionar la segunda opción del menú principal, menú Files, despliega la lista de programas contenidos en la memoria flash del ladrillo NXT. Al seleccionar alguno de ellos, despliega el submenú del archivo con las opciones para ejecutar el programa contenido en dicho archivo, seleccionarlo como default, y la opción de eliminarlo. Al seleccionar la tercera opción del menú principal, menu Bluetooth, se muestra el estado del chip Bluetooth (encendido o apagado), así como su visibilidad. Este menú sirve para buscar y conectarse a otros dispositivos Bluetooth, cambiar su visibilidad y PIN (Número de Identificación Personal por sus siglas en inglés), y desactivar o activar el chip Bluetooth. Al mantener el chip Bluetooth desactivado, se ahorra energía. Al seleccionar la cuarta opción del menú principal, menu Sound, se muestran las opciones para definir el volumen general del ladrillo NXT y el volumen del sonido de los botones dentro del sistema operativo de leJOS NXJ. Al seleccionar la quinta opción del menú principal, menu System, se muestra la siguiente información del estado del sistema:

Memoria flash libre. Memoria RAM libre. Voltaje de batería.

Además muestra las siguientes opciones:

Format: libera la memoria flash, eliminando todos los archivos. Sleep Time: establece el número de minutos antes de que el ladrillo NXT se

apague por ausencia de actividad (algún botón presionado o programa corriendo). Cero significa que nunca se apagará por ausencia de actividad.

Auto run: Si esta opción está activada, el programa default iniciará inmediatamente después de haber encendido el ladrillo NXT, en vez de desplegar el menú principal del sistema operativo. Para deshabilitar esta opción, se debe mantener presionado el botón LEFT durante el arranque del ladrillo NXT.

Al seleccionar la sexta y última opción del menú principal, menu Version, se desplegará la información correspondiente a la versión actual del firmware y del sistema operativo. Éste menú es solo de información.

118

Apéndice C Restablecer firmware original del ladrillo NXT Si se desea restablecer el firmware original con el que contaba el ladrillo NXT por defecto, para volver a programar sobre el lenguaje NXT-G o algún otro lenguaje que soporte ese firmware, podrá realizarse esta tarea siguiendo los siguientes pasos.

1. Mantener desconectado inicialmente el ladrillo NXT del cable USB y encenderlo.

2. Presionar el botón reset hardware durante 5 segundos. Este botón se

encuentra ubicado en la parte superior trasera del ladrillo NXT (figura C.1), dentro de un hueco justo a la altura del conector USB, al que podrá accederse fácilmente usando un clip.

Figura C.1 ubicación del botón reset hardware

3. Conectar el ladrillo NXT a la computadora por medio de cable USB. 4. Abrir el Software Lego Mindstorms NXT y seleccionar “Update NXT

Firmware…” de menú Tools (figura C.2).

Figura C.2 Selección de la herramienta de actualización de firmware del ladrillo NXT

119

5. Sobre la ventana de la herramienta de actualización del firmware (figura C.3), se encontrará una lista de las versiones de firmware disponibles. Seleccionar una de ellas, o en su defecto, la única que se encuentre y presionar el botón Download.

Figura C.3 Herramienta de actualización de firmware del ladrillo NXT

Tras esta operación, el firmware comenzará a descargarse al ladrillo NXT y se desplegará un mensaje al finalizar.

120

Apéndice D Conectores del puerto de entrada del ladrillo NXT El ladrillo NXT cuenta con cuatro puertos de entrada. Cada uno está compuesto por 6 pines. En la figura D.1 se muestra el diagrama esquemático de uno de los puertos de entrada del ladrillo NXT.

Figura D.1 Diagrama esquemático uno de

los puertos de entrada del ladrillo NXT

A continuación se describe cada uno de los pines mostrados en la figura D.1:

Pin 1, ANA: Entrada analógica. Pin 2, GND: Señal de tierra. Pin 3, GND: Señal de tierra. Pin 4, IPAWERA: 4.3v de alimentación de salida. Pin 5, DIGIAI0: Pin digital de entrada y salida. Pin 6, DIGIAI1: Pin digital de entrada y salida. El pin de entrada (ANA), es la entrada analógica, la cual se encuentra conectada a un convertidor analógico–digital de 10bits dentro del procesador AVR. Este también esta conectado al generador de corriente, el cual es usado para alimentar sensores activos. La señal de alimentación de salida (IPOWERA), está conectada internamente a todos los puertos de entrada y salida. La máxima corriente de salida que ésta puede conducir es aproximadamente de 180mA. Los pines digitales de entrada y salida (DIGIAI0 y DIGIAI1), son usados para la comunicación I2C. Donde el pin 5 (DIGIAI0) es usado para la señal SCL y el pin 6 (DIGIAI1) es usado para la señal SDA. Para montar una comunicación I2C, es necesario usar resistencias pull-up de 82KΩ en cada una de estas líneas.

121

Apéndice E Preparación de la cámara CMUcam3 y software CMUcam2 GUI En esta sección de describen los procedimientos realizados previamente a la práctica “Interfaz entre ladrillo NXT y Cámara CMUcam3” en la sección 4.3 de este manual, para preparar la cámara CMUcam3 de acuerdo a las necesidades requeridas de dicha práctica. Todos los programas de esta sección fueron probados bajo el sistema operativo Windows XP.

1. Descargar e instalar el software “LPC2000 Flash Utility”, requerido para programar el procesador de la cámara CMUcam3. Este puede descargarse desde el siguiente enlace:

http://www.nxp.com/files/markets/microcontrollers/philips_flash_utility.zip

2. Descargar el software “CMUcam2 GUI”, el cual permite obtener imágenes

de la cámara CMUcam3 mediante el uso de la emulación del firmware de la cámara CMUcam2. Esta no requiere instalación, y puede ser descargado desde el siguiente enlace

http://www.cs.cmu.edu/~cmucam2/downloads.html

3. Descargar la imagen hexadecimal del programa encargado de emular el

firmware de la cámara CMUcam2 sobre la cámara CMUcam3. Esta se puede encontrar en el siguiente enlace:

http://www.cmucam.org/wiki/cmucam2-emulation El archivo requerido es aquel que soporte una comunicación RS232 de 115,200 baudios, se puede identificar con el nombre Compiled Firmware Image (115,200 8N1).

4. Usar el conector RS232 incluido en la cámara CMUcam3 para conectarlo al puerto serie de la computadora.

5. Alimentar la cámara CMUcam3 con una batería de 9v. 6. Iniciar el software LPC2000 Flash Utility. 7. Encender la cámara CMUcam3 mientras se mantiene presionado el botón

reset, de este modo inicia en modo de programación. 8. Desde el software LPC2000 Flash Utility, en la sección Flash Programming

abrir la imagen hexadecimal descargada previamente y presionar el botón Upload to Flash (Figura E.1).

9. Al terminar de programar la cámara, apagar la cámara y prenderla nuevamente para usarla (esta vez, sin presionar el botón reset).

122

Figura E.1 Software LPC2000 Flash Utility

Para iniciar el software CMUcam2 GUI simplemente abrir el archivo CMUcam2GUI.jar. Al iniciar, solicita el puerto COM al que se encuentra conectada la cámara CMUcam3, y posteriormente se enlaza a la cámara automáticamente. Al abrirlo se muestra una ventana similar a la de la figura E.2.

Figura E.2 Software CMUcam2 GUI

Al presionar el botón Grab Frame se obtiene una imagen desde la cámara, en la cual al seleccionar un pixel sobre dicha imagen, mostrará el color RGB detectado.

123

Apéndice F Programa del PIC18F4550 El programa empleado dentro del PIC18F4550 se ha programado en lenguaje C utilizando el software PCWHD PICC Compiler 4.068. Los parámetros empleados para la comunicación por el puerto RS232 del PIC fueron los siguientes:

115,200 Baudios. 8 bits de datos. 1 bit de paro. Sin paridad. Pin_C6 para TX (transmisión). Pin_C7 para RX (recepción).

Los parámetros empleados para la comunicación por el puerto I2C de PIC fueron los siguientes:

Dispositivo esclavo Dirección 0xA4 Pin_B0 para SDA (Serial DAta). Pin_B1 para SCL (Serial CLock). Control de flujo por hardware.

Nota: Para establecer una mejor recepción de datos por el puerto I2C, durante la ejecución del programa en el PIC18F4550, se limpia periódicamente el bit indicador de sobreflujo de recepción (identificado como el bit SSPOV), así como el bit de estado de buffer lleno (identificado como el bit BF), ubicados en el bit 6 del registro 0xFC6 y el bit 0 del registro 0xFC7 respectivamente9 [22]. De acuerdo a los requerimientos de transmisión de datos descritos en la sección 4.3.3 de este manual, se diseñó el siguiente programa para el PIC18F4550 con el propósito de que éste cumpla como la interfaz de comunicación entre el ladrillo NXT y la cámara CMUcam3:

#include <18F4550.h> #device adc=8 /************Configuración de programación*******************/ #FUSES NOWDT //No Watch Dog Timer #FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale #FUSES HS //High Speed Crystal #FUSES NOPROTECT //Code not protected from reading #FUSES BROWNOUT //Reset when brownout detected

9 La ubicación de los registros mencionados pueden variar dependiendo del PIC que se utilice.

124

#FUSES BORV20 //Brownout reset at 2.0V #FUSES PUT //Power Up Timer #FUSES NOCPD //No EE protection #FUSES NOSTVREN //Stack full will not cause reset #FUSES NODEBUG //No Debug mode for ICD #FUSES LVP //Low Voltage Programming on B5(PIC18) #FUSES NOWRT //Program memory not write protected #FUSES NOWRTD //Data EEPROM not write protected #FUSES NOIESO //Switch Over mode disabled #FUSES NOFCMEN //Fail-safe clock monitor disabled #FUSES NOPBADEN //PORTB pins are configured as digital #FUSES NOWRTC //not registers write protected #FUSES NOWRTB //Boot block not write protected #FUSES NOEBTR //Memory not protected from reads #FUSES NOEBTRB //Boot block not protected from reads #FUSES NOCPB //No Boot Block code protection #FUSES NOMCLR //Master Clear pin used for I/O #FUSES LPT1OSC //Timer1 configured for low-power #FUSES NOXINST //Indexed Addressing mode disabled #FUSES PLL1 //No PLL PreScaler #FUSES CPUDIV1 //No System Clock Postscaler #FUSES NOICPRT //ICPRT disabled #use delay(clock=20000000) #use rs232(baud=115200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) #use i2c(Slave,sda=PIN_b0,scl=PIN_b1,force_hw,address=0xA4) //El último bit de la dirección es despreciado (usar números pares) #bit SSPOV=0xFC6.6 #bit BF=0xFC7.0 /**********************Variables globales***************************/ int estado, i2cRx, RS232_TxFlag=0, i2c_TxFlag=0, i=0, r, clear_flag=1; int rs232_Rx [40]; /******Rutina de tratamiento de la interrupción por I2C*************/ #int_SSP // Interrupción I2C void SSP_isr() estado=i2c_isr_state(); if(estado == 0x80) // Escribe byte por el puerto I2C if (i2c_TxFlag==1) i2c_write(rs232_Rx[i]); // Envía siguiente byte al ladrillo NXT i++; else i2c_write(i2cRx); // Envía último byte recibido if((estado>0)&&(estado<0x80)) // Lee byte por el puerto I2C while((estado>0)&&(estado<0x80))estado=i2c_isr_state(); i2cRx=i2c_read(); // Guarda byte leído switch (i2cRx) case 128: RS232_TxFlag=1; // Inicia envío de instrucción a // CMUcam3 break;

125

case 129: i2c_TxFlag=1; // Inicia envío de respuesta a // ladrillo NXT RS232_TxFlag=0; i=0; break; case 130: i2c_TxFlag=0; // Fin de envío de respuesta a //ladrillo NXT clear_flag=1; reset_cpu(); break; default: if (RS232_TxFlag==1) putc(i2cRx); //envía byte a CMUcam3 else i2cRx=0; //elimina byte recibido por I2C break; /**********************Rutina principal**************************/ void main() setup_adc_ports(NO_ANALOGS); setup_adc(ADC_OFF); setup_psp(PSP_DISABLED); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); setup_timer_1(T1_DISABLED); setup_timer_2(T2_DISABLED,0,1); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); enable_interrupts(INT_SSP); enable_interrupts(GLOBAL); while(true) BF=0; // Bandera de Buffer de I2C lleno. SSPOV=0; // Limpia sobreflujo del buffer de I2C. if(kbhit()) gets(rs232_Rx); // guarda string recibido desde CMUcam3 if (clear_flag==1) // Limpia array de string recibido desde for (r=0;r<50;r++) // desde CMUcam3 rs232_Rx [r]='0'; clear_flag=0;

126

Apéndice G Ejemplos funciones del lenguaje leJOS NXJ Ejemplo 1

String ST1=”Cadena”;

String ST2=”C1”

LCD.drawString(ST1+" "+ST2, 0, 3);

Usando las líneas de código del ejemplo anterior se desplegará en el display el string “Cadena C1” sobre las coordenadas (0,3). Ejemplo 2 int X1=18;

int X2=2;

LCD.drawString(X1+X2+5, 0, 0);

Usando las líneas de código del ejemplo anterior se realizará la operación 18+2+5 desplegando el resultado en el display. En las coordenadas (0,0) se deberá mostrar el número 25.

Ejemplo 3 int X1=15625; for (int i=0;i<6;i++) LCD.drawInt(X1,5,0,0); X1=X1/5; Button.waitForPress();

Usando las líneas de código del ejemplo anterior, se desplegará en el display el valor de la variable X1 y será dividido entre 5, mostrando el resultado sobre las mismas coordenadas en cada iteración. Al ejecutar el ejemplo, se podrá dar cuenta que los dígitos del lado izquierdo desaparecerán al dejar de ser parte del valor correspondiente. Ejemplo 4

LCD.drawString("Cadena", 0, 0); Button.waitForPress(); LCD.clear();

Usando las líneas de código del ejemplo anterior se desplegará en pantalla un string hasta que se presione cualquier botón del ladrillo NXT. Entonces limpiará el display borrando el string.

127

Ejemplo 5

for (int x=20;x<50;x++) for(int y=20;y<50;y++) LCD.setPixel(1,x,y); Button.waitForPress();

Usando las líneas de código del ejemplo anterior, se generará un cuadrado negro con una dimensión de 30 pixeles de ancho por 30 de alto, comenzando en la coordenada 20,20. Ejemplo 6

import lejos.nxt.* import javax.microedition.lcdui.Graphics; public class DibujaLinea public static void main(String[] args) Graphics G = new Graphics(); G.drawLine(8,15,60,60); Button.waitForPress();

El programa del ejemplo anterior, llamado DibujaLinea, desplegará en el display una línea diagonal que inicia en las coordenadas (8,15) y termina en las coordenadas (60,60). Observe que se ha creado una instancia de la clase Graphics, nombrándola G. De este modo, se utilizará G para extraer el método drawLine. También note que se ha importado el paquete javax.microedition.lcdui.Graphics. Ejemplo 7

import lejos.nxt.* import javax.microedition.lcdui.Graphics; public class DibujaRectangulo public static void main(String[] args) Graphics G = new Graphics(); G.drawRect(62, 10, 25, 35); Button.waitForPress();

El programa del ejemplo anterior, llamado DibujaRectangulo, desplegará en el display un rectángulo con su esquina superior izquierda sobre las coordenadas (62,10), con un ancho de 25 pixeles y una altura de 35 pixeles.

128

Ejemplo 8

import lejos.nxt.* import javax.microedition.lcdui.Graphics; public class DibujoTexto public static void main(String[] args) Graphics G = new Graphics(); G.drawString("Cadena",7,32); Button.waitForPress();

En el programa del ejemplo anterior, llamado DibujoTexto, se desplegará el string “Cadena” ubicándolo en las coordenadas de modo gráfico (7,32). Ejemplo 9

import lejos.nxt.*; public class Button1 public static void main(String[] args) LCD.drawInt(Button.waitForPress(), 7, 4); Button.waitForPress();

El programa del ejemplo anterior, llamado Button1, en un principio no desplegará nada en el display hasta que sea presionado algún botón. Entonces se mostrará en las coordenadas (7,4) del display (coordenadas del modo texto), el valor del entero que represente el botón presionado, manteniéndolo hasta que se vuelva a presionar cualquier botón. Ejemplo 10

import lejos.nxt.*; public class Button2 public static void main(String[] args) while (!Button.ESCAPE.isPressed()) //líneas de código del programa...

El programa del ejemplo anterior, llamado Button2, ocupa un ciclo while para repetir una misma tarea una cantidad indeterminada. Saldrá del ciclo hasta que el botón ESCAPE sea presionado. Se propone probar alguna tarea para añadirla dentro del ciclo while del programa Button2.

129

Nota: evite usar el método waitForPress dentro del ciclo while del programa Botton2, debido a que causa conflicto con el método isPressed, evitando salir del ciclo. Ejemplo 11

import lejos.nxt.*; public class Button3 public static void main(String[] args) LCD.drawString("Presione ESCAPE", 0, 0); Button.ESCAPE.waitForPressAndRelease();

El programa del ejemplo anterior, llamado Button3, despliega en pantalla un string en el display. No concluirá el programa hasta que el botón ESCAPE sea presionado. Ejemplo 12

import lejos.nxt.*; public class Button4 public static void main(String[] args) while (true) LCD.drawInt(Button.readButtons(), 0, 0);

El programa del ejemplo anterior, llamado Button4, desplegará en el display el valor entero correspondiente al botón presionado. Si no hay ninguno presionado, desplegará un 0. Ejemplo 13

Motor.C.forward(); Button.waitForPress(); Motor.C.stop();

Usando las líneas de código del ejemplo anterior hará que el motor conectado al puerto C avance hacia delante hasta que se presione un botón, entonces, se detendrá mediante el método stop, el cual se describe más adelante. Ejmplo 14

Motor.C. backward(); Button.waitForPress(); Motor.C.stop();

130

Usando las líneas de código del ejemplo anterior hará que el motor conectado al puerto C avance hacia atrás hasta que se presione un botón, entonces, se detendrá. Ejemplo 15

Motor.C.backward(); Button.waitForPress(); Motor.C.reverseDirection();

Usando las líneas de código del ejemplo anterior se hará rotar el motor hacia delante hasta que sea presionado un botón, entonces cambiará de sentido de rotación. Ejemplo 16

Motor.C.setSpeed(350); Motor.C.forward();

Usando las líneas de código del ejemplo anterior se hará rotar el motor con una velocidad de 350 grados por segundo. Ejemplo 17

int power=30; Motor.C.regulateSpeed(false); Motor.C.setPower(power); Motor.C.forward();

Al usar las líneas del código del ejemplo anterior se desactiva el regulador de velocidad, establece la potencia con el valor de la variable power y avanza hacia delante con la potencia establecida en la instrucción anterior. Ejemplo 18

Motor [] m = Motor.A, Motor.B, Motor.C; for( int i = 0; i<3; i++) m[i].stop();

Usando las líneas de código del ejemplo anterior se generará un arreglo, nombrado m, de tres valores constantes, asignando a cada uno de ellos un puerto de salida. Posteriormente, mediante el ciclo for se frenarán los motores que se encuentren dentro de ducho arreglo. Ejemplo 19

import lejos.nxt.*;

131

public class MotorFlt public static void main(String[] args) Motor.C.setSpeed(900); Motor.C.forward(); Button.waitForPress(); Motor.C.flt(); Button.waitForPress();

El programa del ejemplo anterior, llamado MotorFlt, hace rotar hacia delante el motor conectado al puerto C, con la velocidad máxima, hasta que sea presionado un botón. Entonces, se observará que se detiene gradualmente. Para salir del programa se presiona otra vez un botón. Ejemplo 20

import lejos.nxt.*; public class Tacometro public static void main(String[] args) Motor.C.forward(); int count = 0; while( count < 500 ) count = Motor.C.getTachoCount(); Motor.C.flt(); LCD.drawInt(count , 0, 1); while(!Button.ESCAPE.isPressed()) LCD.drawInt(Motor.C.getTachoCount(), 7,1 );

El programa del ejemplo anterior, llamado Tacometro, sirve para comprobar el retardo de frenado de un motor usando el método flt mediante el uso del encoder. Primero se hace rotar el motor conectado al puerto C. Este se detendrá usando el método flt hasta que el valor obtenido por el método getTachoCount corresponda a 500 grados. Entonces, mientras el motor siga rotando, aun después de haberle dado la instrucción de frenado, se observará en el display la cantidad de grados de rotación durante el tiempo de frenado. Ejemplo 21

import lejos.nxt.*; public class ReiniciaEncoder public static void main(String[] args) Motor.C.setSpeed(100); Motor.C.forward(); while(true) LCD.drawInt(Motor.C.getTachoCount(),5,0,0); if (Button.ENTER.isPressed())

132

Motor.C.resetTachoCount();

El programa del ejemplo anterior, llamado ReiniciaEncoder, hace rotar un motor a una velocidad de 100 grados por segundo durante un tiempo indeterminado. Al mismo tiempo, se despliega en el display el valor del contador del encoder. Si se presiona el botón ENTER, podrá observarse que el contador se reinicia a cero. Ejemplo 22

import lejos.nxt.*; public class RotarMotor public static void main(String[] args) Motor.C.rotate(5*360); LCD.drawInt(Motor.C.getTachoCount(),0,0); Button.waitForPress();

El programa del ejemplo anterior, llamado RotarMotor, rotará el motor hasta completar 5 vueltas, entonces se mostrará en el display la cantidad de grados que rotó en total (aproximadamente 1800 grados). Ejemplo 23

import lejos.nxt.*; public class RotarMotora2 public static void main(String[] args) Motor.C.rotateTo(180); LCD.drawInt(Motor.C.getTachoCount(),4,0,0); Button.waitForPress(); Motor.C.rotateTo(0); LCD.drawInt(Motor.C.getTachoCount(),4,0,0); Button.waitForPress();

El programa del ejemplo anterior, llamado RotarMotor2, rotará el motor hasta llegar al ángulo 180, entonces mostrará en el display el contador del encoder coincidiendo con 180 grados. Al presionar un botón, el motor regresará al punto de origen (cero grados). Ejemplo 24

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

133

public class Navegador1 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.forward(); Button.waitForPress();

En el programa del ejemplo anterior, llamado Navegador1, hace avanzar al vehículo hacia delante, durante un tiempo, indeterminado hasta que se presione un botón. Observe que se ha importado el paquete lejos.robotics.navigation, así también se ha declarado la clase Pilot creando el objeto pilot (leJOS, al igual que Java, hace distinción entre mayúsculas y minúsculas), al que se hará referencia para invocar los métodos.

Ejemplo 25

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador2 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.backward(); Button.waitForPress();

En el programa del ejemplo anterior, llamado Navegador2, se hace avanzar al vehículo hacia atrás durante un tiempo indeterminado hasta que se presione un botón. Ejemplo 26

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador3 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.forward(); Button.waitForPress(); pilot.stop(); LCD.drawString("EN ESPERA", 3, 3); Button.waitForPress();

El programa del ejemplo anterior, llamado Navegador3, hará avanzar el vehículo hacia delante hasta que se presione un botón, entonces los motores se detendrán

134

inmediatamente y se mostrará en el display el string “EN ESPERA” hasta que se vuelva a presionar un botón para terminar el programa. Ejemplo 27

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador4 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.travel(40); pilot.travel(-40,true); while(!Button.ESCAPE.isPressed());

El programa del ejemplo anterior, llamado Navegador4, hace avanzar el vehículo 40 centímetros y posteriormente 40 hacia atrás. Durante el primer recorrido, mientras avanza hacia delante, el movimiento no se podrá interrumpir. En el segundo recorrido, mientras avanza hacia atrás, el movimiento puede interrumpirse y finalizar el programa presionando el botón ESCAPE. Si el programa Navegador4 se modificara eliminando el ciclo while del final, el vehículo no se movería hacia atrás, dado que el programa concluiría inmediatamente después de haberse dado la instrucción para realizar dicho movimiento. Ejemplo 28

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador5 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); for(int i=0;i<4;i++) pilot.travel(30); pilot.rotate(90);

El programa del ejemplo anterior, llamado Navegador5, hará que el vehículo avance 30 centímetros y posteriormente que rote 90 grados hacia su izquierda sobre su eje central. Esta tarea la repetirá 4 veces. Trazando, con su trayectoria, un cuadrado de 30 centímetros de lado.

135

Ejemplo 29

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador6 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.setMoveSpeed(10); pilot.forward(); Button.waitForPress();

El programa del ejemplo anterior, llamado Navegador6, designa una velocidad de movimiento del vehículo de 10 centímetros por segundo, con la cual avanzará hacia delante en un tiempo indeterminado hasta que se presione un botón.

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador7 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.setTurnSpeed(20); pilot.rotate(360);

El programa del ejemplo anterior, llamado Navegador7, designa una velocidad de rotación del vehículo a 20 grados por segundo, con la cual rotará sobre su eje central 360 grados. El movimiento deberá terminar después de 18 segundos aproximadamente. Ejemplo 31 import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador8 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); int R=0; while (true) pilot.steer(R); if (Button.LEFT.isPressed()) R=R -10; Button.LEFT.waitForPressAndRelease(); if (Button.RIGHT.isPressed()) R=R+10;

136

Button.RIGHT.waitForPressAndRelease(); LCD.drawInt(R,4,0,0); LCD.drawInt(Motor.C.getSpeed(),4,0,4); LCD.drawInt(Motor.B.getSpeed(),4,5,4);

El programa del ejemplo anterior, llamado Navegador8, hace mover al robot utilizando el método steer con la variable R para el argumento turnRate. Dicha variable inicialmente se encuentra en cero, haciendo que el vehículo avance en línea recta. Al presionar el botón LEFT o RIGHT, la variable R incrementará o decrementará en 10, haciendo que el vehículo avance en curva con la dirección correspondiente al valor de R. En el display se desplegará el valor de la variable R, y la velocidad de cada rueda en centímetros por segundo. Con esto, se podrá observar la relación de velocidad entre ellas. Cuando R toma un valor extremo (100 ó -100), podrá observarse que la rueda del lado correspondiente a su eje de giro se detendrá por completo. Ejemplo 32

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador9 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.3f, Motor.C, Motor.B); pilot.setMoveSpeed((float)1); pilot.forward(); while (!Button.ESCAPE.isPressed()) int u=(int)pilot.getTravelDistance(); int d=(int)((pilot.getTravelDistance()-u)*100); LCD.drawString(u+"."+d,0,0);

El programa del ejemplo anterior, llamado Navegador9, hace avanzar el vehículo hacia delante con una velocidad de 1 centímetro por segundo mientras se despliega en el display la distancia recorrida por éste. Dado que en el display no se pueden mostrar variables de punto flotante, se designó la variable int u para las unidades, y la variable int d para los decimales. Posteriormente estas dos variables pueden ser desplegadas en el display como un string.

137

Ejemplo 33

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador10 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.setTurnSpeed((float)20); pilot.rotate((float)360,true); while (!Button.ESCAPE.isPressed()) LCD.drawInt((int)pilot.getAngle(),6,0,0);

El programa del ejemplo anterior, llamado Navegador10, hace rotar el vehículo 360 grados sobre su eje central con una velocidad de 20 grados por segundo mientras se despliega en el display el valor entero del ángulo total de rotación. Ejemplo 34

import lejos.nxt.*; import lejos.robotics.navigation.*; public class Navegador11 public static void main(String[] args) Pilot pilot = new TachoPilot(5.6f, 11.4f, Motor.C, Motor.B); pilot.setMoveSpeed(20); pilot.forward(); while(true) LCD.drawInt((int)pilot.getTravelDistance(),5,0,0); if (Button.ENTER.isPressed()) pilot.reset();

El programa del ejemplo anterior, llamado Navegador11, hace avanzar el vehículo hacia delante a una velocidad de 20 centímetros por segundo durante un tiempo indeterminado. Al mismo tiempo, se despliega en el display el valor del contador de la distancia recorrida. Si se presiona el botón ENTER, podrá observarse que el contador se reinicia a cero. Ejemplo 35

import lejos.nxt.*; public class SensorContacto public static void main(String[] args) TouchSensor touch = new TouchSensor(SensorPort.S1); while(!Button.ESCAPE.isPressed())

138

if (touch.isPressed()) LCD.drawInt(1, 8, 3); else LCD.drawInt(0, 8, 3);

En el programa del ejemplo anterior, llamado SensorContacto, se incluye el constructor del sensor de contacto conectado al puerto de entrada 1 creando una instancia al sensor llamada touch, con la que se llamará al método isPressed. Posteriormente se crea un ciclo while del que se saldrá únicamente si se presiona el botón ESCAPE. Dentro de éste se estará comprobando si el sensor de contacto es presionado, si es así se despliega 1 en el display, de lo contrario se despliega 0. Ejemplo 36 import lejos.nxt.*; public class SensorLuz public static void main(String[] args) LightSensor light = new LightSensor(SensorPort.S3,true); while (!Button.ESCAPE.isPressed()) LCD.drawInt(light.readNormalizedValue(),4, 0, 0);

En el programa del ejemplo anterior, llamado SensorLuz, se incluye el constructor del sensor de luz conectado al puerto de entrada 3 creando una instancia al sensor llamada light, con la que se llamará al método readNormalizedValue. Observe que el sensor de luz se utiliza en modo activo. Posteriormente se crea un ciclo while del que se saldrá únicamente si se presiona el botón ESCAPE. Dentro de éste, se estará desplegando en el display el valor entero de la lectura del sensor. Ejemplo 37

import lejos.nxt.*; public class SensorUltrasonico public static void main(String[] args) UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S2); while (!Button.ESCAPE.isPressed()) LCD.drawInt(sonic.getDistance(),4, 0, 0);

En el programa del ejemplo anterior, llamado SensorUltrasonico, se incluye el constructor del sensor ultrasónico conectado al puerto de entrada 2 creando una instancia al sensor llamada sonic, con la que se llamará al método getDistance. Posteriormente se crea un ciclo while del que se saldrá únicamente si se presiona

139

el botón ESCAPE. Dentro de éste se estará desplegando en el display el valor entero de la lectura del sensor. Ejemplo 38 import lejos.nxt.*; public class SensorSonido public static void main(String[] args) SoundSensor sonido = new SoundSensor(SensorPort.S4); while (!Button.ESCAPE.isPressed()) LCD.drawInt(sonido.readValue(),4, 0, 0);

En el programa del ejemplo anterior, llamado SensorSonido, se incluye el constructor del sensor ultrasónico conectado al puerto de entrada 4 creando una instancia al sensor llamada sound, con la que se llamará al método readValue. Posteriormente se crea un ciclo while del que se saldrá únicamente si se presiona el botón ESCAPE. Dentro de éste se estará desplegando en el display el valor entero de la lectura del sensor. Ejemplo 39

import lejos.nxt.*; import lejos.util.*; public class Segundos1 public static void main(String[] args) Stopwatch sw = new Stopwatch(); while(!Button.ESCAPE.isPressed()) LCD.drawInt(sw.elapsed()/1000,5,0,0);

El programa del ejemplo anterior, llamado Segundos1, muestra en el display los segundos transcurridos desde que inicia el programa. Observe que el valor retornado por el método elapsed es dividido entre mil para hacer la conversión de milisegundos a segundos. Ejemplo 40 import lejos.nxt.*; import lejos.util.*; public class Segundos2 public static void main(String[] args) Stopwatch sw = new Stopwatch(); while(!Button.ESCAPE.isPressed()) LCD.drawInt(sw.elapsed()/1000,5,0,0);

140

if (Button.ENTER.isPressed()) sw.reset(); Button.ENTER.waitForPressAndRelease();

El programa del ejemplo anterior, llamado Segundos2, muestra en el display los segundos transcurridos desde que inicia el programa o la última llamada a reset. Al presionar el botón ENTER, se reinicia el contador a cero. Ejemplo 41

import lejos.nxt.*; import lejos.util.*; public class Retardos public static void main(String[] args) Delay delay = new Delay(); Stopwatch sw = new Stopwatch(); delay.msDelay((int)1e3); LCD.drawInt(sw.elapsed(),0,0); delay.usDelay((int)1e6); LCD.drawInt(sw.elapsed(),0,1); delay.nsDelay((int)1e9); LCD.drawInt(sw.elapsed(),0,2); Button.waitForPress();

En el programa del ejemplo anterior, llamado Retardos, se utilizan los tres métodos de la clase Delay para generar retardos de un segundo con cada método. Tras ocurrir un retardo se muestra en el display el tiempo transcurrido en milisegundos, sumando con el último retardo 3000 milisegundos. Ejemplo 42

import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*; import javax.bluetooth.*; public class BTmaestro public static void main(String[] args) throws Exception String name = "NXT"; LCD.clear(); LCD.drawString("Conectando...", 0, 0); RemoteDevice btrd = Bluetooth.getKnownDevice(name); if (btrd == null) LCD.clear();

141

LCD.drawString("Sin dispositivo", 0, 0); BTConnection btc = Bluetooth.connect(btrd); if (btc == null) LCD.clear(); LCD.drawString("Error conexion", 0, 0); LCD.clear(); LCD.drawString("Conectado", 0, 0); DataInputStream dis = btc.openDataInputStream(); DataOutputStream dos = btc.openDataOutputStream(); for(int i=0;i<100;i++) LCD.drawInt(i*10, 8, 0, 2); dos.writeInt(i*10); dos.flush(); LCD.drawInt(dis.readInt(),8, 0,3); dos.writeChar('Q'); dos.flush(); LCD.drawString(dis.readChar() + "",0,1); Thread.sleep(2000); LCD.drawString("Cerrando...", 0, 0); dis.close(); dos.close(); btc.close(); LCD.clear(); LCD.drawString("Finalizado",3, 4); Thread.sleep(1000);

El programa del ejemplo anterior, llamado BTmaestro, funciona para el ladrillo NXT maestro dentro de una comunicación Bluetooth. Éste envía al dispositivo esclavo una serie de valores enteros consecutivos desde el 0 hasta el 99, esperando la respuesta de un valor entero enviado desde el dispositivo esclavo. Ejemplo 43

import lejos.nxt.*; import lejos.nxt.comm.*; import java.io.*; public class BTesclavo public static void main(String [] args) throws Exception

142

while (!Button.ESCAPE.isPressed()) LCD.drawString("Esperando...",0,0);

BTConnection btc = Bluetooth.waitForConnection(); LCD.clear(); LCD.drawString("Conectado",0,0); DataInputStream dis = btc.openDataInputStream(); DataOutputStream dos = btc.openDataOutputStream(); for(int i=0;i<100;i++) int n = dis.readInt(); LCD.drawInt(n,7,0,2); dos.writeInt(-n); dos.flush(); LCD.drawString(dis.readChar() + "",0,1); dos.writeChar('R'); dos.flush(); Thread.sleep(2000); dis.close(); dos.close(); Thread.sleep(100); LCD.clear(); LCD.drawString("Cerrando...",0,0); btc.close(); LCD.clear();

El programa del ejemplo anterior, llamado BTesclavo, funciona para el ladrillo NXT esclavo dentro de una comunicación Bluetooth en conjunto con el programa llamado Btmaestro (ejemplo 42) dentro de un ladrillo NXT como dispositivo maestro. Este espera por un valor entero enviado desde el dispositivo maestro y envía el mismo valor entero con signo negativo. Ejemplo 44 import lejos.nxt.*; public class Eco public static void main(String[] args) I2CPort I2CPort; //Convierte la clase I2CPort en método I2CPort=SensorPort.S2; //puerto de entrada byte address=(byte)(0xA4>>1); //dirección del esclavo

143

byte[] data=0x00; //dato de buffer I2CPort.i2cEnable(I2CPort.STANDARD_MODE); //Habilita el canal de comunicación I2C while (!Button.ESCAPE.isPressed()) int ret = I2CPort.i2cStart(address, 0x00, 0, null,1,0); //Solicita un dato de un byte al dispositivo esclavo if (ret == 0) while (I2CPort.i2cBusy() != 0); //espera hasta que el

//canal de comunicación //se libere

ret = I2CPort.i2cComplete(data, 1); //guarda el dato leído en data LCD.drawString("Recibe: " +(char)(0xFF & data[0]), 1, 1); //muestra en display el dato recibido I2CPort.i2cStart(address, 0x00, 0, data, 1, 1); //devuelve el dato recibido al esclavo while (I2CPort.i2cBusy() != 0); //espera hasta que el

//canal de comunicación //se libere

I2CPort.i2cDisable(); //deshabilita el canal de comunicación I2C

El programa del ejemplo anterior, llamado Eco, sirve para un dispositivo un ladrillo NXT dentro de una comunicación I2C. Éste solicita un dato de un byte al dispositivo esclavo con dirección 0xA4 e inmediatamente después le responde con el mismo dato.

144

Bibliografía

[1] Giulio Ferrari, Andy Gombos, Søren Hilmer, Jürgen Stuber, Mick Porter,

Jamie Waldinger, Dario Laverde. “Programming Lego Mindstorms with Java” first edition. United States of America: Syngress Publishing. 2002.

[2] Henrik Hautop Lund, Patrizia Marti. “Physical and Conceptual Constructions in Advanced Learning Environments” [En línea]. Odense M, Denmark, (2004). Disponible en web: <citeseerx.ist.psu.edu/viewdoc/summary?doi=10 .1.1.71.8344>. [Consulta: 5 de Noviembre de 2010].

[3] Juan Antonio Breña Moral. “Develop leJOS programs Step by Step” [En línea]. Madrid, España (2009). Disponible en web: <http://www.juanantonio.i nfo/lejos-ebook/>. [Consulta: 7 de Noviembre de 2010].

[4] The Lego Group, “LEGO® MINDSTORMS® NXT: Hardware Developer Kit” version 1.00 [En línea]. United States, (2006). Disponible en web: <http://mi ndstorms.lego.com/en-us/support/files/default.aspx#Advanced >. [Consulta: 18 de Noviembre de 2010].

[5] Matthias Paul Scholz. “Advanced NXT: The Da Vinci Inventions Book” first edition. EUA: Technology in Action Press. 2007.

[6] Subir Kumar Saha. “Introducción a la robótica” primera edición. México:

McGraw Hill. 2008.

[7] Sabri Cetinkunt. “Mecatrónica” primera edición. México: Grupo Editorial Patria. 2007.

[8] Gonzalo Sabala. “Robótica: guía teórica y práctica” primera edición.

Argentina: USER. 2007.

[9] Siemens Semiconductors. “Silicon NPN Phototransistor: SFH 309” Data Sheet [en línea]. Disponible en wep: <http://www.datasheetarchive.com/SF H309-4-datasheet.html>. [Consulta: 13 de Diciembre de 2010].

[10] Michael Gasperi, Philippe Hurbain, Isabelle Hurbain. “Extreme NXT:

Extending the LEGO MINDSTORMS NXT to the Next Level” first edition. EUA: Technology in Action Press. 2007.

[11] Dominique Paret, Carl Fenger. “The I2C Bus: From Theory to Practice”,

second edition. England: John Wiley & Sons Ltd. 2000.

[12] David j. Perdue. “The unofficial Lego Mindstorms NXT inventors’s guide” first edition. United States of America: No Starch Press. 2008

145

[13] Martijn Boogart, Jonathan A. Daudelin, Brian I. Davis, Jim Nelly, David Levy, Lou Morris, Far Rhode, Rick Rhode, Matthias Paul Scholz, Christopher R. Smith, Rob Torok. “The Lego Mindstorms NXT idea book” first edition. United Status of America: No Starch Press. 2007.

[14] Tim Rinkens, Juan Antonio Breña Moral. “LEJOS, Java for LEGO

Mindstorms” [en línea]. <http://lejos.sourceforge.net> [Consulta: 5 de Noviembre de 2010].

[15] Bruce Eckel. “Thinking in Java”, second edition. Unidad States of America:

Prentice-Hall. 2000.

[16] James D. Foley, Andries van Dam, Steven K. Feiner, John F. Hughes. ““Computer graphics: principles and practice” second edition. United States of America: Addison-Wesley. 2006.

[17] Katsuhiko Ogata. “Ingeniería de Control Moderna” cuarta edición. Person Educación: España. 2007.

[18] Tim Wescott “PID without a PhD” [en línea]. <http://igor.chudov.com/manual

s/Servo-Tuning/PID-without-a-PhD.pdf> [Consulta: 13 de Abril de 2010].

[19] Carnegie Mellon University, Robotics Institute. “CMUcam3 Datasheet”. [En línea] United States, (2007). Disponible en wep: <http://www.cmucam.org/at tachment/wiki/Documentation/CMUcam3_datasheet.pdf>. [Consulta: 7 de Abril de 2011].

[20] Carnegie Mellon University, Robotics Institute. “CMUcam2 Vision Sensor:

User Guide”. [En línea] United States, (2003). Disponible en wep: <http://ww w.cs.cmu.edu/~cmucam2/CMUcam2_manual.pdf >. [Consulta: 7 de Abril de 2011].

[21] Leo den Hartog. “Lego Mindstorms NXT Camera”, Semester Thesis, ETH

Zurich, Computer Engineering and Networks Laboratory. [En línea] Switzerland, (2008). Disponible en wep: < ftp://ftp.tik.ee.ethz.ch/pub/student s/2008-HS/SA-2008-18.pdf >. [Consulta: 7 de Abril de 2011].

[22] Microchip. “PIC18F2455/2550/4455/18F4550” Data Sheet [en línea].

Disponible en wep: <http://www.microchip.com/wwwproducts/Devices.aspx? dDocName=en010300>. [Consulta: 22 de Febrero de 2011].