Sei sulla pagina 1di 39

Robotica con ladrillos LEGO

Jose Mara Ca nas Plaza


jmplaza@gsyc.escet.urjc.es
Abril 2003

Indice del curso 1

Indice del curso


Introduccion
Elementos del kit
Programacion con codigo RCX
Programacion con NQC
Programacion con C y BrickOS
Programacion con Java y LeJOS
Conclusiones
c 2003 GSyC-URJC Robotica con ladrillos LEGO

Indice del curso (cont.) 74


Programacion con C y
BrickOS
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS

Indice 75

Indice
BrickOS
Acceso a los sensores
Acceso a los actuadores
Multitarea
Comunicaciones
Ejemplos
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 76
BrickOS
Sistema operativo libre (Markus L. Noga)
Antes se llamaba LegOS
Carga dinamica de programas (desde la version 0.2.x)
Gestion de memoria dinamica (acceso a toda la memoria)
Manejadores para todos los subsistemas (p.e. display)
Uso completo del lenguaje
Tama no reducido (6 Kbytes)
Gestion de tareas (con prioridad)
Semaforos POSIX
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 77
Arquitectura software usando BrickOS
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 78
Detalles
No es un sistema operativo de tiempo real
Sistema operativo libre
Carga dinamica de programas
Gestion de memoria dinamica
Manejadores para todos los subsistemas
Tama no reducido (6 Kbytes)
Gestion de tareas (con prioridad)
Semaforos POSIX
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 79
Como se programa desde GNU/Linux?
Se genera una version cruzada de gcc para el H8 (binutils)
Se compila el propio BrickOS
Se edita el programa (Emacs p.e.) y se compila con el gcc cruzado
Se descargan los programas con las herramientas de BrickOS:
firmd3: descarga el rmware de BrickOS
dll: descarga un programa
Existen paquetes Debian y RedHat de todo
Tambien existen distribuciones para MS-Windows
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 80
Ejemplo en C sobre BrickOS
#include <conio.h> if(SENSOR_1<0xf000)
#include <unistd.h> dir = 0;
#include <dsensor.h> else
#include <dmotor.h> dir = 1;
wakeup_t colision(wakeup_t dato) { motor_a_dir(rev);
lcd_refresh(); motor_c_dir(rev);
return TOUCH_1 || TOUCH_2; msleep(500);
} motor_a_speed(MAX_SPEED);
int main(int argc, char *argv[]) { motor_c_speed(MAX_SPEED);
while(1) { if(dir==1)
motor_a_speed(MAX_SPEED); motor_c_dir(fwd); cputs("Izqda");
motor_c_speed(MAX_SPEED); else
motor_a_dir(fwd); motor_c_dir(fwd); motor_a_dir(fwd); cputs("Drcha");
wait_event(&colision,0); msleep(500); } }
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 81
Mas detalles del RCX
16Kbytes de memoria ROM interna
32 Kbytes de RAM externa
El rmware original dejaba unicamente 6kbytes libres
Direcciones de 16 bits
16 registros de 8 bits (R0H,R0L, . . . R7H, R7L)
1 registro contador de 16 bits (PC)
1 registro de 8 bits para condiciones (CCR)
El registro R7 se usa como puntero de pila (SP)
Los accesos a la pila se realizan siempre con palabras de 2 bytes
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
BrickOS 82
Funcionamiento basico de BrickOS
El kernel BrickOS es monoltico
La ROM llama a kmain que inicializa el kernel
systime handler revisa todos los dispositivos en cada interrupcion del timer
(cada 1 milisegundo):
1. Motores
2. Sonido
3. LNP (LegOS Network Protocol)
4. Botones
5. Actualizacion del indicador de batera
6. Actualizacion del LCD
7. Manejo de tareas (20 milisegundos por defecto)
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Motores en legOS 83
Manejo de Motores en legOS
Manejo de los puertos de salida (A,B,C)
I/O mapeada en memoria (dir 0xF000) (cadenas 01, 10, 00, 11)
Estado: Direccion + Velocidad
motor_X_dir(enum MotorDirection) controla la direccion:
fwd: hacia delante
rev: hacia atras
break: bloquea el motor (impide que gire)
off: lo detiene pero puede girar libremente
Ojo! break: gasta las pilas
motor_X_speed(int speed) controla la velocidad del motor.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Motores en legOS 84
La velocidad es un entero entre MIN_SPEED y MAX_SPEED (0 255
seg un direct-motor.h).
Se puede usar el sensor de rotacion para ver cuando tiene traccion.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Motores en legOS 85
Ejemplo
void motor_driver() {
int rover_speed;
rover_speed=0; /*evitar empezar con negativos*/
while(rover_speed<MAX_SPEED) { /*va incrementando*/
motor_a_speed(rover_speed);
motor_c_speed(rover_speed);
lcd_int(rover_speed); /*pinto la velocidad*/
lcd_refresh();
motor_a_dir(fwd);
motor_c_dir(fwd);
msleep(500); /*para notar la aceleracion*/
rover_speed+=20; /*incremento velocidad */
}
cputs("brake"); /*prueba de frenos*/
lcd_refresh();
motor_a_speed(brake);
motor_c_speed(brake);
sleep(3);
cputs("off");
lcd_refresh();
motor_a_speed(off);
motor_c_speed(off);
sleep(3);
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 86
Manejo de Sensores en legOS
El H8 incluye un conversor A/D (8 canales)
Los valores de los sensores se obtienen del conversor
Se usan solo 4 canales (AN0-AN1 entradas del RCX y AN3 batera)
Los sensores de rotacion y luz son activos (alimentacion)
Se puede especicar una entrada como:
Pasiva: ds passive(&SENSOR X)
Activa: ds active(&SENSOR X)
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 87
El sensor de contacto
Son sensores pasivos
El SO mide cada 0.1ms
Se puede trabajar con el valor crudo o procesado
Las lecturas crudas de los sensores van te oricamente de 0x000 a
0xFFFF
Procesdo: macro TOUCH X 1 pulsado, 0 no-pulsado
Crudo: macro SENSOR_X donde X puede ser 1,2 o 3
El valor no es muy util (en principio != 0 es pulsado)
Con cputw(SENSOR_X) se puede imprimir su valor
SENSOR_X < 0x2000 debera devolver 1 (pulsado) o 0 (no pulsado)
Ejemplo crudo y preprocesado:
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 88
if (SENSOR_1 < 0xF000) { if (TOUCH_1) {
// pulsado // pulsado
} }
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 89
Ejemplo
void sensor_toque() {
cputs("toque"); /*indico en que funcion estoy*/
lcd_refresh();
msleep(500);
motor_a_speed(100);
motor_c_speed(100);
while(1) {
motor_a_dir(fwd);
motor_c_dir(fwd);
cputw(SENSOR_1); /*muestro el valor*/
lcd_refresh();
if(SENSOR_1!=0) { /*sensor tocado*/
motor_a_dir(rev);
motor_c_dir(rev);
sleep(1);
}
}
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 90
El sensor de luz
Variable LIGHT_X donde X puede ser 1,2 o 3
Se actualiza cada milisegundo
La escala de valores depende del modo
ds_init() inicializa todos los sensores, los pone en modo pasivo e
incia la conversion A/D.
Para cambiar de modo: ds_passive(&SENSOR_X), ds_active(&SENSOR_X)
(void ds_active(volatile unsigned *sensor))
Pasivo: Detectar luz
Activo: Emite luz y recibe (buscar objeto cerca con poca diferencia
con el fondo)
Las lecturas crudas en modo activo estan en el rango 50-300 (norma-
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 91
lizadas 0-100)
Las lecturas crudas en modo pasivo van de 220 a 280
Uso del sensor:
if (LIGHT_1 < LIGHT_MAX) { if (SENSOR_1 < 0xf000)
// casi siempre // en deteccion de lneas
} }
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 92
Ejemplo
void sensor_luz() {
cputs("luz"); /*indico en funcion estoy*/
lcd_refresh();
msleep(500);
ds_active(&SENSOR_2); /*activo la conversion A/D*/
while(1) {
lcd_int(LIGHT_2);
lcd_refresh();
msleep(250);
if (LIGHT_2 < 50)
cputs("oscuro");
else
cputs("claro");
lcd_refresh();
msleep(250);
}
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 93
El sensor de rotacion
Detecta 1/16 de una rotacion (sin mecanica)
Es activo: ds active(&SENSOR X)
En modo pasivo no mide nada
Es contador relativo: acumulativo
ds rotation set(&sensor, int valor) ja un valor
ds rotation on(&sensor) comienza a contar
Uso del sensor (activacion):
ds_active(&SENSOR_3);
ds_rotation_set(&SENSOR_3, 0);
ds_rotation_on(&SENSOR_3);
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Manejo de Sensores en legOS 94
Ejemplo
void sensor_rotacion() {
ds_active(&SENSOR_3); /* lo activo*/
ds_rotation_on(&SENSOR_3);
ds_rotation_set(&SENSOR_3, 0);
while (!PRESSED(button_state(), BUTTON_VIEW)) {
lcd_int(ROTATION_3);
lcd_refresh();
msleep(20);
}
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Pantalla y botones en legOS 95
Pantalla y botones en legOS
Manejo del Display
En la 0.2.x el refresh es automatico cada 100 ms.
lcd int(x) escribe un entero
lcd show(enum lcd segment), lcd hide(enum lcd segment): ico-
nos y segmentos
lcd clear()
cputs(char *s) escribe 5 caracteres (no borra si es menor)
PRESSED(button state(), NAME) para BUTTON VIEW y BUTTON RUN
Hay que controlar el RELEASED(button state(), NAME)
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Pantalla y botones en legOS 96
Botones en BrickOS
Se acceden en el chero direct-button.h
Los nombres de los botones son: BUTTON_ONOFF, BUTTON_RUN, BUTTON_VIEW
y BUTTON_PROGRAM
PRESSED(button_state(), NAME) y RELEASED(button_state(),NAME)
devuelven True o False si el boton esta en la posicion adecuada.
Durante la ejecucion de un programa los botones pueden emplearse
para cualquier cosa (peligro: si se cuelga hay que quitar las bateras)
Solucion: Dejar un boton para matar todas las tareas
Ojo! Con la programacion: problema con bucle (detecta muchas ve-
ces).
Solucion: debounde (msleep) o wait_until (threads)
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Pantalla y botones en legOS 97
Ejemplo interaccion
/* Sacado de simple-rover.c */
wakeup_t button_press_wakeup(wakeup_t data) {
return PRESSED(button_state(),data); }
wakeup_t button_release_wakeup(wakeup_t data) {
return RELEASED(button_state(),data); }
[...]
int task_swapper() {
wait_event(&button_press_wakeup,BUTTON_PROGRAM); /*espera por el boton PROGRAM*/
wait_event(&button_release_wakeup,BUTTON_PROGRAM);/*espera que lo suelten*/
t2=execi(&motor_driver,2,DEFAULT_STACK_SIZE); /*arranca el motor*/
wait_event(&button_press_wakeup,BUTTON_PROGRAM); /*espera pulsacion de PROGRAM*/
wait_event(&button_release_wakeup,BUTTON_PROGRAM);/*espera que lo suelten*/
kill(t2);
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 98
Multitarea en legOS
Especialmente conveniente en robotica
Dos funciones basicas:
extern pid_t execi(int funcion (int, char **), int argc, char **argv,
priority_t priority, size_t stack_size);
kill(pid_t);
La funcion debe recibir un entero y un char**
La prioridad debe ser menor de PRI HIGHEST (20 por defecto)
legOS se encarga de parar los motores, sensores, etc. al nalizar el programa
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 99
Tareas en BrickOS
Los robots habitualmente realizan varias tareas a la vez.
La interfaz de tareas en BrickOS esta en unistd.h
BrickOS proporciona mecanismos de sincronizacion: semaforos decla-
rados del tipo typedef usigned char sem_t
BrickOS tambien proporciona mecanismos para gestionar las tareas:
suspenderlas, activarlas, etc.
Es una de las areas que se esta mejorando (esta documentacion
esta referida al legOS 0.1.7.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 100
Arrancar tareas
extern pid_t execi (int (*code_start) (int, char**),
int argc, char **argv, priority_t priority,
size_t stack_size);
int (*code_start)(int, char**): direccion de comienzo (&nombre_funcion).
int argc, char **argv argumentos de la tarea.
size_t stack_size tama no de pila.
Devuelve -1 si falla y en otro caso el pid de la nueva tarea
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 101
Esperando por eventos
Declarar una funcion de tipo wakeup_t que devuelva cero en condi-
ciones normales y no cero cuando la condicion por la que se quiere
esperar se cumple.
Usando la funcion wait_event una tarea se auto-suspende hasta que
la funcion (de tipo wakeup_t es no-nula)
extern wakeup_t wait_event(wakeup_t(*wakeup) (wakeup_t), wakeup_t data)
wait_event devolvera el valor devuelto por la funcion de tipo wakeup_t.
El SO se encarga de comprobar esta funcion cada vez que pase por
la tarea que activo esa espera.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 102
Otras operaciones con tareas
extern void exit(int code) sale de una tarea devolviendo code.
extern void yield(void) cede el resto del tiempo de su rodaja.
extern unsigned int sleep/msleep(unsigned int sec/msec)
retrasan la ejecucion el n umero de segundos/milisegundos indicado.
Devuelven el n umero de segundos/milisegundos restantes si son inte-
rrumpidas.
extern void kill(pid_t pid) termina un proceso.
extern void killall(priority_t p) termina todos los procesos
de prioridad de prioridad menor que p.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 103
Ejemplo
/* Sacado de simple-rover.c */
wakeup_t button_press_wakeup(wakeup_t data) {
return PRESSED(button_state(),data); }
wakeup_t button_release_wakeup(wakeup_t data) {
return RELEASED(button_state(),data); }
[...]
int task_swapper() {
wait_event(&button_press_wakeup,BUTTON_PROGRAM); /*espera por el boton PROGRAM*/
wait_event(&button_release_wakeup,BUTTON_PROGRAM);/*espera que lo suelten*/
t2=execi(&motor_driver,2,DEFAULT_STACK_SIZE); /*arranca el motor*/
wait_event(&button_press_wakeup,BUTTON_PROGRAM); /*espera pulsacion de PROGRAM*/
wait_event(&button_release_wakeup,BUTTON_PROGRAM);/*espera que lo suelten*/
kill(t2);
}
[...]
int main()
{
t1=execi(&task_swapper,1,DEFAULT_STACK_SIZE);
tm_start(); /*starts t1, which controls t2-t4 */
return 0;
}
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 104
Semaforos en legOS
extern int sem_init(sem_t* sem,int pshsared,unsigned int value)
inicializa el objeto apuntado por sem.
extern int sem_wait(sem_t * sem) suspende a la tarea llamante
hasta que el semaforo sem tenga valor no cero. Entonces decrementa
atomicamente el contador del semaforo.
extern int sem_trywait(sem_t* sem) si el contador es cero de-
vuelve EAGAIN y sino lo decrementa atomicamente devolviendo 0.
extern int sem_post(sem_t* sem) incrementa atomicamente el
contador del semaforo apuntado por sem. Es no-bloqueante.
extern inline int sem_getvalue(sem_t* sem, int* sval) de-
vuelve el valor del semaforo en sval.
extern inline int sem_destroy(sem_t* sem) destruye el semafo-
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 105
ro.
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Multitarea en legOS 106
Consejos para usar tareas
Al destruir un semaforo no debe haber tareas esperando en el.
La funcion que se pasa como parametro en execi debe tener los
parametros adecuados (int y **char).
Sus vloores por defecto son 0 y NULL
Despues de llamar a execi hay que llamar a tm_start() para arranar
el manejador de tareas.
Las prioridade asignada a cada tarea en execi debe ser unica. (par-
che?)
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
LNP: legOS Network Protocol 107
LNP: legOS Network Protocol
Comunicacion entre robots y con el PC
En el PC se necesita el lnpd (demonio) y el lnpp (biblioteca)
Dos niveles: integridad (errores) y logico (direcciones)
El logico es similar a UDP: sin errores . . . si llegan
La torre puede estar en otro PC
lnp address: direccion LNP (8 bits)
lnp mask: cuantos bits son direccion y cuantos mascara
#define MI_PUERTO 2 #define DIR_DESTINO (DESTINO << 4 | PUERTO_DEST)
#define DESTINO 0x8 #define DIR_DESTINO2 (DESTINO << 4 | PUERTO_DEST)
#define PUERTO_DEST 0x7
#define PUERTO_DEST2 0x8
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
LNP: legOS Network Protocol 108
Enviando/Recibiendo en LNP
En el PC los programas se conectan usando TCP:
lnp init(tcp hostname, tcp port, lnp address, lnp mask, flags)
En el RCX esta siempre inicializado.
Conviene jar el rango:
lnp logical range(range) (0 cerca, 1 lejos)
Se crea un manejador para cada puerto donde recibir datos:
addr handler 1(const unsigned char *data, unsigned char length,
unsigned char src);
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
LNP: legOS Network Protocol 109
Uso creativo de LNP: la baliza
La localizacion es un problema
Soluciones: odometra, triangulacion, . . .
Usar el puerto IR como baliza
Se puede usar mas de una?
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS
Simuladores: EmulegOS, LegoSim 110
Simuladores: EmulegOS, LegoSim
Simulador de legOS
Software libre
El interfaz se puede separar del simulador
El interfaz es un applet Java
Librera que implementa el interfaz de Bric-
kOS
Generar aplicaciones de GNU/Linux
La E/S se simula como cadenas de texto
c 2003 GSyC-URJC Robotica con ladrillos LEGO: Programacion con C y BrickOS

Potrebbero piacerti anche