Corso di Robotica A
Prof. G.Gini
Anno Accademico 2001-2002
Definizione di un ambiente
per Robot Lego MindStorms™
Marco Mariotti
[email protected]
INDICE
CAP I TO LO 1 I N T RO D U ZI O N E
O B IE T T IV I
CAP I TO LO 2 I L RO B O T
1
1
2
I L K IT L E GO M IN D S T O R M S ™
R CX
SENSORI
2
3
3
Sensore di contatto
Sensore di luce
Sensore di rotazione
4
4
5
ATTUATORI
C O M U N IC A Z IO N E T R A R C X
C AR A T T E R IS T IC H E D E I R O B O T
5
6
6
Movimento
Sensori di contatto
Sensore di luce
Sensore di rotazione
6
6
7
7
CAP I TO LO 3 I L SO F T WA R E
I L S IS T E M A O P E R A T IV O
RCX Code
LeJOS 2.0
A M B IE N T E D I S V I L U P P O
JKD1.4
Eclipse
LeJOS plugin
I N S T A L L A Z IO N E D E L S O F T W A R E D I S V I L U P P O
LeJOS
Eclipse
LeJOS Plugin
Importare in Eclipse un progetto esistente
CAP I TO LO 4 L ’ A RCH I T E TT UR A
R E Q U IS IT I
S U B S U M P T IO N A R C H IT E C T U R E
Arbitrator
Behaviours
Task switcher
Sensori
Comunicazione
Attuatori
Diagramma di interazione
CAP I TO LO 5 B E H A VI O RS
M O V IM E N T A Z IO N E D I B A S E
Descrizione dell’ambiente
Seguire una linea con un sensore di luce
B e h a vi o r : F o l l o w L i n e
Ruotare agli incroci
B e h a vi o r : R o t a t o r _ t i m e
B e h a vi o r : R o t a t o r _ a n gl e
L O C A L I Z Z A Z IO N E
8
8
8
8
10
10
10
10
11
11
11
11
12
13
13
13
15
15
16
17
17
18
19
20
20
20
21
21
23
23
23
24
I
INDICE
Rilevare la propria posizione - Rilevare gli oggetti
B e h a vi o r : C r o s s F o u n d
P IA N IF IC A Z IO N E D E L T R A C C IA T O
Tracciare un percorso
B e h a vi o r : S o l ve P a t h
Esplorare la mappa - Raggiungere una posizione
24
24
24
24
24
25
B e h a vi o r : E xp l o r e Al l
25
Interazione con l’utente
25
B e h a vi o r : B u t t o n I n p u t
25
CAP I TO LO 6 CO NC L US I O NI
26
APP EN D IC E
27
JOSX.PLATFORM
rcx
27
27
ColorSensor.java
C o l o r S e n s o r Li s t e n e r . j a va
Li gh t S e n s o r . j a v a
27
28
29
J O S X . R O B O T IC S
29
actuator
29
S i mp l e A c t u a t o r . j a v a
R o v e r A c t u a t o r . j a va
R o t a t i o n A c t u a t o r . j a va
S e r vo . j a v a
fsm
29
30
31
31
33
Arbitrator.java
B e h a vi o r . j a v a
behavior
A c t i o n . j a va
ButtonInput.java
C r o s s F o u n d . j a va
E xp l o r e Al l . j a v a
F o l l o w Li n e . j a v a
Rotator_angle.java
R o t a t o r _ t i m e . j a va
SolvePath.java
robots
PathFinder.java
P a t h F i n d e r _ a n gl e . j a v a
va r i a b l e \ R o b C o m m . j a va
world
Direction.java
D i r e c t i o n C o n s t a n t s . j a va
GridMap.java
Path.java
Position.java
PositionConstants.java
33
35
37
37
37
39
42
45
48
50
53
57
57
59
62
63
63
64
65
66
67
68
II
CAPITOLO 1 INTRODUZIONE
CAPITOLO 1
INTRODUZIONE
Obiettivi
L’obiettivo di questo progetto è quello di fornire un ambiente di
sviluppo per il sistema Lego MindStorms. In particolare si vuole
ottenere un sistema modulare di programmazione non orientato verso un
compito specifico ma che dia la possibilità, t ramite la scelta degli
opportuni moduli, di poter ottenere comportamenti diversi del robot
utilizzando sempre la stessa struttura di base.
La modularità offre molti vantaggi, innanzitutto un sistema così
definito porta naturalmente a dividere il problema p rincipale in
sottoproblemi più piccoli e più semplici da risolvere. Inoltre permette di
lavorare facilmente anche con robot dalle caratteristiche fisiche
differenti: è sufficiente ridefinire i moduli che usano in modo diretto
l’hardware mentre si possono r iutilizzare senza modifiche i moduli di
più alto livello (che ad esempio svolgono computazioni e risolvono
algoritmi – i.e. ricerca di un cammino).
Si otterranno così robot diversi ma con gli stessi comportamenti e
che utilizzano moduli (porzioni di codice ) esattamente identici.
1
CAPITOLO 2 IL ROBOT
CAPITOLO 2
IL ROBOT
Dal momento che il progetto riguarda la realizzazione di agenti
embodied (cioè agenti dotati di un “corpo fisico” che gli permette di
interagire con l’ambiente) la struttura e le potenzialità di tale corpo
condizionano le capacità e le strategie di controllo degli agenti stessi.
In questo capitolo dunque vengono presentate le caratteristiche salienti
di tali robot.
Il kit Lego MindStorms ™
I robot sono stati costruiti utilizzando il kit Robotic Invention
System 1.5, ed il kit Ultimate Accessory Set : in Figura 1 sono presentati
i principali componenti, mentre nei paragrafi successivi ne vengono
analizzate le caratteristiche principali..
Figura 1 - Componenti fondamentali del kit
2
CAPITOLO 2 IL ROBOT
RCX
L’RCX, sviluppato presso il
MIT
di
Boston
nel
1987,
costituisce il cuore del sistema
Mindstorms.
Basato su un microcontrollore
HITACHI H8/3293 (della famiglia
H8/300) dotato di 32 Kb di
memoria RAM, l’RCX è dotat o di
tre porte di ingresso a cui
collegare altrettanti sensori e tre
porte di uscita a cui collegare tre
attuatori
(tipicamente motori).
Figura 2 - RCX
Questi componenti sono analizzati
nei paragrafi successivi.
Oltre a questi è dotato di una porta ad infrarossi per la
programmazione da PC (utilizzabile anche per la comunicazione tra più
RCX), un piccolo display a cristalli liquidi che può visualizzare cinque
cifre alfanumeriche, un altoparlante interno e quattro pulsanti per
controllarne le principali funzioni:
 On–Off per l’accensione e lo spegnimento
 Run per avviare l’esecuzione del programma
 Prgm per la selezione del programma da eseguire
 View per monitorare lo stato dei sensori
I pulsanti sono controllati dal sistema operativo è possono essere
utilizzati con diverse fu nzionalità (vedi paragrafo comportamenti).
Sensori
L’RCX presenta tre porte di ingresso per il collegamento di sensori
che possono essere alimentati ( modalità attiva ) o meno (modalità
passiva) attraverso tali porte.
La lettura di un sensore in modalità pa ssiva avviene ponendo
sull’ingresso una tensione di 5V e misurando la caduta di tensione
dovuta alla resistenza applicata dal sensore stesso; la tensione
sull’ingresso viene poi discretizzata fornendo un valore in modalità raw
che varia da 0000 (0V) a FFFF (5V), anche se dubito venga utilizzato
un convertitore A/D a 16 bit. Nel caso di un sensore in modalità attiva
si alternano un ciclo di alimentazione in cui l’ingresso viene tenuto ad
una tensione di 8V per circa 3 millisecondi ed un ciclo di lettura dell a
durata di circa 0.1 millisecondi analogo a quella descritta in
precedenza.
3
CAPITOLO 2 IL ROBOT
Sensore di contatto
Costituito sostanzialmente da
un interruttore in serie ad una
resistenza, il sensore di contatto è
l’unico ad essere utilizzato solo in
modalità
passiva.
Qu ando
l’ingresso su cui è montato viene
configurato
come
sensore
di
contatto esso assume il valore 1 se
il sensore è premuto, a 0 se
rilasciato.
Sensore di luce
Figura 3 - Sensore di contatto
Il sensore di luce è costituito da un fotodiodo e da una piccola
sorgente di luce (LED). Quan do questa è spenta il sensore viene
utilizzato in modalità passiva e permette di valutare la luminosità
dell’ambiente (per esempio se si
vuole dirigere il robot verso la zona
più scura della stanza); quando è
accesa invece il sensore viene
utilizzato in modalità attiva e
permette di valutare la quantità di
luce riflessa dalla superficie posta
di fronte al sensore, che sarà tanto
maggiore quanto più il colore è
chiaro (utilizzata ad esempio per
seguire
una
linea).
Quando
l’ingresso su cui è montato viene
configurato come sensore di luce la
il range di valori letti varia
Figura 4 - Sensore di luce
nell’intervallo [0,100] , anche se
sperimentalmente in modalità attiva
i valori non sono mai inferiori a 20 o superiori a 70, richiedendo così
una calibrazione preliminare in base alle caratter istiche dell’ambiente.
Un difetto di tale sensore è che le variazioni di valori sono continue
ossia non si verificano salti ma il valore letto varia sempre di una unità
alla volta.
4
CAPITOLO 2 IL ROBOT
Sensore di rotazione
Il sensore di rotazione, utilizzato
esclusivamente in modalità attiva
permette di misurare la rotazione
dell’asse inserito con la precisione di
1/16
di
giro
(22.5°).
Quando
l’ingresso su cui è montato viene
configurato
come
sensore
di
rotazione si comporta come un
contatore che può essere inizializzato
al
valore
desiderato
e
viene
incrementato o decrementato di una
unità in corrispondenza di una
rotazione dell’asse del sensore in
senso orario o antiorario.
Figura 5 - Sensore di rotazione
Attuatori
L’RCX presenta inoltre tre porte
di uscita utilizzabili per il colle gamento dei motori (semplici motori lineari) che possono essere con trollati definendo la potenza con cui
vengono alimentati che determina la
velocità di rotazione e una dire zione di rotazione:
 Forward: il motore gira in avanti
(dipende da come il motore viene
collegato all’RCX)
 Backward: stesse considerazioni
del punto precedente.
 Float: il motore non ruota, ma
non oppone resistenza.
 Stop: il motore è bloccato (viene
alimentato per tenere il rotore
fermo).
Figura 6 - Motore
5
CAPITOLO 2 IL ROBOT
Comunicazione tra RCX
La comunicazione fra RCX avviene tramite la porta a infrarossi
posta frontalmente sull’RCX ad una velocità tipica di 2400 bit/s.
La trasmissione di un byte, dato il grande rumore presente sul mezzo
trasmissivo (luce ambiente), è piuttosto elaborata per evitare errori nel
messaggio. Ogni byt e è prece duto da un bit di start e seguito da un bit
di end ed al termine del pacchetto viene aggiunto un bit di parità (11 bit
di messaggio); viene inoltre trasmesso un pacchetto complementare al
precedente, portando complessivamente ad un pacchetto di 22 bit che
contiene un numero uguale di zeri e di uno, permettendo così al
ricevitore di compensare il segnale costante dovuto alla luce ambiente
(bias) sottraendo il valore medio del segnale trasmesso.
Il rapporto fra bit di informazione e bit di canale porta dunque ad
una velocità di trasmissione che si aggira sui 900 bit/s.
Caratteristiche dei robot
Per meglio analizzare la flessibilità e le potenzialità della
subsumption architecture sono stati utilizzati robot di dimensioni e
caratteristiche diverse, e con compo rtamenti differenti così da rendere
più significativa la flessibilità e la configurabilità data dalla
architettura costruita.
Passiamo ora ad una breve presentazione delle principali
caratteristiche meccaniche dei robot, rimandando alla guida fornita con
il kit MindStorms per una completa descrizione dei passi di costruzione.
Movimento
Il movimento viene realizzato attraverso due cingoli azionati da
motori indipendenti, permettendo così una rotazione del robot attorno al
proprio asse. Allo scopo di contene re il più possibile gli attriti i cingoli
sono azionati tramite una sola coppia d’ingranaggi, mentre esternamente
sono sostenuti da una struttura che mantiene la giusta tensione; in
questo modo si è ottenuta una buona durata delle batterie.
Il robot così r ealizzato riesce a ruotare su se stesso, anche se la
rotazione è accompagnata da una lieve traslazione del robot dovuta all a
superficie leggermente scivolosa del campo, su cui i cingoli non
riescono ad ottenere la massima trazione.
Sensori di contatto
I robot presenta uno o due dei sensori di contatto posti frontalment e
per rivelare l’urto con eventuali ostacoli. Il contatto viene trasmesso al
sensore attraverso degli appositi “paraurti” e dato il particolare compito
da svolgere questi dovranno essere robus ti, per non subire deformazioni
e contemporaneamente anche sensibili per evitare spostamenti degli
oggetti dovuti alla mancata identificazione.
6
CAPITOLO 2 IL ROBOT
Sensore di luce
Ciascun robot presenta un sensore di luce puntato verso il basso che
permette, utilizzato in mod alità attiva, di rilevare il “colore” del campo.
L’esperienza ha mostrato che si riescono ad ottenere delle prestazioni
(in termini di velocità di inseguimento della linea e numero di
smarrimenti della stessa) tanto migliori quanto più il sensore di luce è
lontano dal centro di istantanea rotazione del robot, fermo restando il
fatto che dovrà essere posizionato esattamente sull’asse di simmetria
del robot. Purtroppo il sensore non è molto sensibile né preciso, è
quindi necessaria una calibrazione dei color i prima di utilizzarlo.
Sensore di rotazione
In uno dei due robot, infine, è stato utilizzato anche un sensore di
rotazione per misurare con precisione la rotazione del robot attorno al
proprio asse.
Infatti grazie alla catena di ingranaggi indicata in Figura 7 e
costituita sostanzialmente da una coppia di differenziali, è possibile
sottrarre le rotazioni dei due motori (e quindi dei due cingoli) collegati
agli assi M1 ed M2 ottenendo così una rotazione dell’asse D montato sul
sensore proporzionale alla rotazione del robot attorno al proprio asse.
Figura 7 – Cinematismo del sensore di rotazione
Quando il robot avanza oppure arretra su un percorso rettilineo l e
velocità di rotazione dei motori sono ident iche e dunque la loro
differenza è nulla e l’asse D resta praticamente fermo; viceversa quando
il robot ruota le velocità saranno uguali ed opposte e dunque daranno
una differenza non nulla che porta ad una rotazione dell’asse D e può
essere rilevata trami te il sensore.
7
CAPITOLO 3 IL SOFTWARE
CAPITOLO 3
IL SOFTWARE
In questa sezione verrà analizzata tutta l’architettura software usata
per poter programmare l’ RCX del Lego. Si parlerà quindi in primo
luogo del sistema operativo dell’RCX facendo una breve carrellata sui
S.O. disponibili; poi verranno discusse le AP I disponibili per
controllare il robot; infine si parlerà dell’ambiente di sviluppo usato su
PC.
Il sistema operativo
RCX Code
Il kit Lego MindStorms include il sistema operativo RCX Code
basato sulle librerie spirit.ocx, e’ sostanzialmente un interprete
bytecode in grado di eseguire si programmi residenti sia comandi
impartiti direttamente da PC tramite la torretta a infrarosso. Purtroppo
sia il sistema operativo sia il sistema operativo che l’interfaccia di
programmazione forniti presentano però delle limitazioni piuttosto
pesanti, legate ad esempio al ridotto numero di variabili utilizzabili (al
massimo 32), alla ridotta quantità di memoria utente lasciata libera
dall’interprete (circa 6KB disponibili) ed all’impossibi lità di scrivere
programmi complessi (ad esempio con più cicli innestati). Per superare
le limitazioni imposte dal sistema operativo originale sono state
proposte diverse alternative, come ad esempio l’ NQC il LegOS, oppure
LeJOS, basato su Java e utilizzat o per lo sviluppo del progetto.
LeJOS 2.0
LeJOS nasce dal progetto di una TINY Virtual Machine per il
mattoncino RCX originalmente guidato da Jose Solorzano. Entrambi i
progetti sono ospitati da SourceForge e sono completament e
OpenSource.
TinyVM e’ un sostituto del firmware Lego MindStorms RCX
microcontroller basato su Java. TinyVM occupa circa 10Kb nella
memoria dell’RCX; inoltre i programmi che vengono caricati sono
notevolmente compressi prima del download sull’RCX. Mediamente un
piccolo programma ha a disposizione 16Kb di memoria RAM.
Sostanzialmente si tratta di un’interprete di bytecode scritti in Java.
8
CAPITOLO 3 IL SOFTWARE
Le caratteristiche di TinyVM sono:












Linguaggio di programmazione Object Oriented (Java)
Preemtive thread
Eccezioni
Sincronizzazione
Array multidimensionali
Ricorsione
Accesso ai bottoni RCX
Non e’ necessario un cross -compilatore
Timers
Numeri Random
Caratteri per LCD
Persistenza degli oggetti
LeJOS fondamentalmente utilizza i metodi nativi scritti per la Tiny
Virtual Machine e li estende fornendo oggetti di alto livello per il
controllo degli attuatori e dei sensori. Le classi di LeJOS sono divise in
4 package a seconda della loro utilità:
josx.platform.rcx: è il package fondamentale, contiene tuti gli
oggetti per controllare attuatori, sensori e bottoni, ossia le librerie di
base per far muovere il robot.
josx.rcxcomm : in questo package sono raggruppate tutte le classi
per la comunicazione, a partire da quelle che controllano la porta
infrarossi, a quelle che implementano i protocolli.
josx.robotics : alcune classi per il controllo avanzato del robot
come un navigatore che utilizza motori e sensori di rotazione per
spostare il robot.
josx.util: alcune classi di utilità per gestire le strutture di memoria
come array e vettori, con un occhio di rig uardo all’uso della memoria
(ad esempio usare variabili di tipo short (2 byte) invece che int (4 byte)
Una caratteristica molto importante di Java è che nel programma da
caricare sull’RCX vengono incluse solo le classi che effettivamente
vengono importate, in questo modo si ottimizza in modo trasparente
l’utilizzo di memoria (con gli altri SO è necessario rimuovere dal SO il
codice che non si utilizza e ricompilare lo stesso).
9
CAPITOLO 3 IL SOFTWARE
Ambiente di Sviluppo
L’ambiente di sviluppo che è stato utilizzato è costitui to da quattro
componenti, tutti scaricabili gratuitamente da internet:
LeJOS 2.0 (http://www.lejos.org , http://sourgeforce.net )
JDK 1.4 (http://java.sun.com )
Eclipse (http://www.eclipse.org )
LeJOS Eclipse Plugin : un plugin per Eclipse con delle utilità per
sviluppare codice per RCX MindStorms. ( http://www.eclipse.org)
JKD1.4
Librerie, compilatori, utilità, tutto il necessario per sviluppare in
Java. E’ possibile scaricarlo gratuitamente dal sito di sun.
Eclipse
Eclipse è un IDE per la programmazione in Java sviluppato
inizialmente da IBM, poi donato al mondo OpenSource.
Indubbiamente il fatto di essere gratuito costituisce un fondamentale
vantaggio, inoltre Eclipse è basato su un’architettura a plugin che
permette di estenderne le funzionalità. La scelta è ricaduta su questo
tool soprattutto per il fatto che è disponibile un plugin appositamente
studiato per sviluppare applicazione per LeJOS.
LeJOS plugin
Si tratta di un semplice plugin per Eclipse; è possibile scaricarlo e
trovare le informazioni necessarie per l’installazione al sito ufficiale di
Eclipse sotto la categoria Plugins.
Fondamentalmente questo plugin ha tre funzionalità:
Creazione automatica di progetti Eclipse per LeJOS, che includono
automaticamente tutta le librerie necessarie.
Compilare direttamente in Eclipse i bytecode da inviare al
mattoncino RCX (è necessario un compilatore speciale, non javac)
Inviare direttamente dal tool i bytecode compilati al mattoncino
attraverso la porta infrarossi.
10
CAPITOLO 3 IL SOFTWARE
Installazione del Software di Sviluppo
LeJOS
Creare una cartella sul root del disco rigido (nome c onsigliato:
C:\Robot\ )
Estrarre nella cartella creata il file “ lejos_win32_2_0_0.zip ”
presente sul CD in posizione Software\LeJOS\ .
Per la documentazione eseguire le stesse operazioni con il file
“lejos_win32_2_0_0.doc.zip ” (si consiglia di non sovrascri vere i file).
Verificare la creazione della cartella C:\Robot\lejos\ .
Eclipse
E’ richiesta la presenza della Java Virtual Machine (JVM) di Sun (la
Microsoft JVM non funziona).
Se non presente va installata: è possibile scaricarla gratuitament e
dal sito http://java.sun.com oppure utilizzare la versione 1.3.1 fornita
sul CD lanciando l’eseguibile “ j2sdk-1_3_1_03-win.exe” presente nella
cartella S oftware\JVM\ .
Ora si può passare all’installazione di Ecplise: è suffici ent e
scompattare in una qualsiasi cartella del disco rigido il file “ eclipseSDK-2.0-win32.zip ” presente sul CD in posizione Software\Eclipse\
(cartella consigliata: C:\Robot\ ).
Verificare la creazione della cartella C:\Robot\eclipse\ .
Per lanciare il programma fare doppio clic sull’eseguibil e
Eclipse.exe (Se dovessero esserci errori nell’avvio potrebbero essere
dovuti al fatto che la JVM di default è quella di Microsoft - già presente
nei Windows precedenti a XP – si consiglia di disinstallare la Sun JVM ,
riavviare il computer, installare nuovamente la Sun JVM e riavviare di
nuovo il computer).
LeJOS Plugin
Eclipse deve essere configurato per l’uso che se ne vuole fare
tramite plug-in. In questa fase installiamo il plug -in per LeJOS presente
sul CD nella cartella Software\Eclipse\Plugin\ .
La procedura è molto semplice: chiudere Eclipse, estrarre il file
“org.lejos_1.0.1.zip ”, presente nella cartella cui sopra, nella cartella del
disco rigido C:\Robot\eclipse\plugins\ .
Verificare la creazione della car tella C:\Robot\eclipse\plugins\
org.lejos_1.0.1 .
11
CAPITOLO 3 IL SOFTWARE
Il plugin risulta ora installato, per verificarlo avviare Eclipse, dalla
barra degli strumenti selezionare File…New…Project…: se nella finestra
di destra è possibile selezionare LeJOS Project il plugin è st ato
correttamente riconosciuto.
Importare in Eclipse un progetto esistente
Una vota avviato Eclipse vediamo come caricarvi un progetto tra
quelli forniti sul CD nella cartella Project\ (ad esempio: Arbitrator)
Creare la cartella C:\Robot\project\ e copiarvi la cartella Arbitrator
presente sul CD.
In Eclipse selezionare File…Import… , poi selezionare Existing
Project into WorkSpace e Next>
Tramite il tasto B rowse… cercare la cartella Arbitrator e ciccare su
Finish per terminare.
12
CAPITOLO 4 L’ARCHITETTURA
CAPITOLO 4
L’ARCHITETTURA
Requisiti
Le caratteristiche che richiediamo al robot sono:
 Goal multipli, ad esempio il robot dovrà cercare di raggiungere
un certo punto nel mondo, evitando gli ostacoli e nel minor tempo
possibile.
 Sensori multipli: il robot può usare contemporaneam ente più
sensori e in modo differente
 Robustezza: i sensori non sono precisi e gli eventi del mondo non
sono prevedibili
 Estensibilità: deve essere possibile usare più o meno sensori e
robot differenti con caratteristiche differenti sugli attuatori e sui
sensori
Avere la possibilità di cambiare Goal o Robot semplicement e
cambiando la configurazione del sistema.
Per raggiungere tali obiettivi è necessaria una forte modularità e
scalabilità nell’architettura del sistema:
Comportamenti complessi non sono neces sariamente prodotti da
sistemi di controllo complessi, ma possono essere scomposti in molti
comportamenti semplici.
È necessario scomporre il compito globale in comportamenti che
vanno dai più bassi che operano direttamente su attuatori e sensori e in
altri di più alto livello che raccolgono informazioni da questi e
prendono decisioni più complesse. Questa architettura presenta una
robustezza intrinseca distribuendo i compiti via via più complessi su
behavior più ad alto livello.
Subsumption architecture
Si è scelto di implementare un modello che il più possibile rispetti i
principi della Subsumption Architecture
poiché questa soddisfa i
requisiti imposti al nostro sistema.
Il compito globale è visto come una stratificazione di compiti, ad
esempio un caso concreto è quello di un robot che debba esplorare tutt a
la griglia alla ricerca di oggetti che si trovano agli incroci, deve essere
in grado di evitarli e comunque visitare tutte le caselle.
13
CAPITOLO 4 L’ARCHITETTURA
Pianif icare
l’esplorazion e
Decider e il prossimo
punto in cui andare
evitando gli ostacoli
Riconoscere gli
ostacoli e crearne una
mappa
Ruotare agli
incroci
Muoversi lungo
la linea nera
Muoversi sugli
incroci
Ritor nare sulla
linea nera
Figura 8 - Architettura dei Behavior
Attraverso questa architettura è possibile connettere strettamente
percezione e azione, collegando concretamente un robot al suo mondo.
In questo modo abbiamo diversi livelli di competenza, cioè dei livelli
che specificano un a classe desiderata di comportamenti per il robot.
Possiamo osservare ad esempio che fin quando il robot dovrà muoversi
in un mondo costituito da una griglia con righe nere e incroci argentati i
tre comportamenti più bassi, cioè quelli direttamente collega ti con il
mondo saranno sempre validi; se ad esempio dopo che il robot ha
esplorato la mappa e sa dove si trovano gli ostacoli deve tornare
indietro e spostarli, sarà sufficiente sostituire il comportamento che
Pianifica l’esplorazione in modo che dica al robot di andare dove si
trovano gli ostacoli, e il comportamento che si occupa di evitarli con
uno che li spinga fuori dalla griglia.
I principi su cui si basa la Subsumption Architecture :





La computazione è organizzata sotto forma di una rete asincrona
di elementi computazionalmente attivi e indipendenti chiamati
behaviors
I vari moduli si scambiano messaggi lungo una rete prestabilita
Non esiste organizzazione gerarchica, i diversi strati ( behaviors)
funzionano in parallelo.
Esiste un meccanismo di in ibizione tra comportamenti basato
sulle priorità.
Non esiste separazione tra percezione, unità centrale e sistema di
attuazione
14
CAPITOLO 4 L’ARCHITETTURA


Il modello del mondo è distribuito lungo l’intera rete, non esiste
un modello centrale
E’
possibile
includere
nuove
funzionalità
al
sistema
semplicemente aggiungendo nuovi moduli (behaviors)
Passiamo ora alla descrizione dei vari moduli che costituiscono
questa architettura. Ogni modulo è una classe Java con determinate
caratteristiche e metodi. Ora si analizzeranno le caratteris tiche salienti
dei moduli e il loro scopo.
Arbitrator
Questo modulo è il cuore dell’architettura. Il suo compito è quello di
mandare in esecuzione comportamenti che in risposta ad un evento
diventano attivi cioè richiedono di essere eseguiti.
Quando più comportamenti diventano attivi contemporaneamente
l’arbitrator li esegue in ordine di priorità. In questo modo viene
implementato il sistema di inibizione dei comportamenti, ossia solo
quello a priorità più elevata viene effettivamente eseguito. Quando
termina la sua computazione viene di nuovo controllato quali
comportamenti richiedono di essere attivati e di nuovo viene eseguit o
quello a priorità più elevata.
Se durante l’esecuzione di un comportamento un altro con priorità
più elevata richiede l’esecuzio ne il primo viene sospeso e la sua
esecuzione viene ripresa successivamente.
Per fare ciò l’ arbitrator mantiene un array contenente l’elenco dei
behaviors attualmente funzionanti, l’ordine all’interno di tale array ne
determina la priorità (indice minore significa priorità più alta). I
comportamenti possono essere sostituiti a runtime semplicemente
cambiando gli elementi contenuti nell’array (vedi Task Switcher ).
Behaviours
Il behavior nasce come un’entità di alto livello dotata della capacità
di essere attivata o disattivata in maniere selettiva e di racchiudere al
suo interno una machina a stati finiti (AFSM).
Le caratteristiche di un comportamento sono:
Un behavior si occupa di risolvere un compito semplice e preciso
I singoli behaviors sono indipendenti, rispondono ad eventi del
mondo esterno (sensori) e controllano gli attuatori.
Ogni behavior contiene una o più azioni e controlla tramite variabili
interne lo svolgimento delle stesse.
I behaviors si scambiano messaggi lungo delle connessioni che sono
stabilite a priori e costituiscono la struttura del robot, e permettono in
un certo modo di mantenere la rappresentazione del mondo e dello stato
del robot.
15
CAPITOLO 4 L’ARCHITETTURA
Ogni behavior è caratterizzato da una priorità. Tipicamente i
comportamenti di più basso livello, c ioè quelli che si occupano del moto
e comandano direttamente gli attuatori hanno priorità più bassa, mentre
quelli di più alto livello che si occupano di risolvere compiti più astratti
hanno priorità più elevata (bisogna prima pensare cosa fare prima di
farlo!)
Un concetto fondamentale è che i singoli comportamenti decidono
autonomamente quando attivarsi, ossia in seguito ad un evento (sensori)
o ad un segnale (altri comportamenti) richiedono all’ Arbitrator di
essere eseguiti e quando verrà il loro turno s aranno eseguiti.
Abbiamo detto che un behavior contiene una macchina a stati finiti
ossia il compito che deve svolgere viene suddiviso in più azioni che
vengono eseguite in sequenza. Ogni azione può dare un comando agli
attuatori o risolvere un algoritmo, non è importante ciò che fa, la
questione che è garantita l’esecuzione completa di ogni singola azione,
ossia il behavior non può essere interrotto durante l’esecuzione di una
singola azione (cioè di uno stato della AFSM). Il passaggio da
un’azione alla successiva può essere determinato da tre fattori:
 Esecuzione temporizzata: l’azione viene eseguita e poi il robot
attende il tempo che l’azione ha deciso prima di passare ad eseguire
la successiva o l’azione di un altro comportamento.
 Eventi: l’azione vien e eseguita ripetutamente fino a quando un
determinato evento (scatenato dai sensori) non avviene, allora si
passa ad eseguire l’azione successiva.
 Segnali: l’azione viene eseguita ripetutamente fino a quando un
altro comportamento non invia un segnale (mes saggio) che viene
propagato dalla rete a questo comportamento, allora si passa ad
eseguire l’azione successiva.
Task switcher
Questo modulo si occupa di gestire l’insieme dei comportamenti
attivi nel modulo di arbitraggio. Si chiama Task Switcher poiché grazie
a lui è possibile passare ad eseguire differenti TASK ossia avere diversi
Goal durante la vita del robot. Infatti, la particolare architettura studiata
permette di cambiare il comportamento globale del robot semplicement e
sostituendo alcuni dei compor tamenti attivi. Se ad esempio invece che
evitare gli ostacoli li si vuole spingere è sufficiente cambiare il modulo
che si occupa di evitarli con quello che permette di spingerli. Quindi il
robot permette di avere diversi Goal durante la sua vita; il cambi amento
del Task è attivato dal raggiungimento di un Goal (od obiettivo) oppure
da un qualunque evento o qualunque segnale. Sostanzialmente non è
altro che un particolare behavior a priorità più elevata di tutti gli altri
che è in grado di lavorare sulla st ruttura del modulo di arbitraggio.
16
CAPITOLO 4 L’ARCHITETTURA
Sensori
I sensori costituiscono il mezzo con il quale il robot è in grado di
“comprendere” il mondo esterno.
Il modo in cui vengono utilizzati è sostanzialmente un modello ad
eventi ossia quando un sensore registra un cambiamento del valore che
sta leggendo genera un evento.
Un qualunque modulo si può mettere in ascolto di un sensore
dichiarando di estendere un interfaccia SensorListener quindi
implementando un metodo opportuno ( stateChanged ) che il sistema
operativo si occupa di chiamare ogni qualvolta si genera quel
determinato evento. Esiste una unica limitazione che permetto di avere
al massimo 8 ascoltatori per ogni evento dettata dalla limitata capacità
elaborativi del mattoncino RCX.
I sensori sono un componente fondamentale, quindi oltre ai sensori
di base è stato implementato un sistema per cui è possibile definirne di
nuovi e più complessi, basati su un sensore di base o su un insieme di
questi, ma che effettuano una prima interpretazione dell’evento
registrato. Ad esempio il sensore di luce reagisce ad ogni minimo
cambiamento della luce registrata dal sensore, ma se a noi interessa
registrare solo determinati colori come il bianco e il nero basta definire
nel modulo Sensor un nuovo sensore che risveglia tutti i comportamenti
che lo ascoltano solo quando il sensore capta il colore bianco oppure il
nero.
In questo modo si risparmia molta capacità elaborativa e inoltre non
ci si deve preoccupare nei comportamenti di gestire sensori avanzati.
Il sistema per defini re nuovi sensori è molto semplice. E’ necessario
implementare
un
interfaccia
chiamata
ad
esempio
AdvancedSensorListener che obbliga a definire un metodo chiamat o
addListener. Il nuovo sensore deve mantenere un elenco dei componenti
(tipicamente behavior) che vogliono “ascoltarlo” e ogni volta che il
sensore registra un cambiamento chiamare in ordine i metodi
stateChanged di tutti i behavior contenuti nell’elenco. La chiamato al
metodo addListener del sensore non fa altro che aggiungere un elemento
all’elenco degli ascoltatori.
Comunicazione
La comunicazione tra i vari comportamenti è un'altra caratteristica
fondamentale della Subsumtion architecture, è grazie alla comuni cazione che è possibile mantenere una rappresentazione del mondo
esterno e una coscienza dello stato del robot; grazie a queste due
caratteristiche il robot è in grado di risolvere problemi complessi
spezzettandoli in tanti problemi semplici.
Il sistema di comunicazione che è stato implementato è basato sul
concetto di segnale molto simile a quello di Unix. Quando un behavior
17
CAPITOLO 4 L’ARCHITETTURA
lo ritiene necessario può inviare un segnale che può essere un singolo
bit così come un oggetto molto complesso chiamando il metodo
sendSignal(nomeSegnale,valore) del modulo di comunicazione.
Quando invece un comportame nto vuole ricevere un segnale di un
certo tipo deve fare tre cose:
 dichiarare di essere in ascolto di quel segnale chiamando il
metodo
addSignalListener(nomeSegnale)
del
modulo
di
comunicazione
 implementare l’interfaccia signalListener
 dichiarare il metodo receiveSignal(nomeSeganle,valore) che verrà
chiamato quando qualche altro comportamento invia il segnale che si
sta ascoltando
Il modulo di comunicazione si occuperà di propagare un segnale
inviato da un behavior a tutti quelli che lo ascoltano semplicem ente
chiamando i metodi receiveSignal dei comportamenti.
La rete di comunicazione è fissa e decisa a compile time. E’
facilmente configurabile dal momento che i segnali hanno dei nomi
simbolici, quindi può essere modificata in base ai comportamenti
presenti nel sistema.
Attuatori
Gli attuatori sono gli elementi che permettono al robot di muoversi .
Sono direttamente controllati dai comportamenti di basso livello. LeJOS
mette a disposizione delle primitive per comandare direttamente gli
attuatori, e normalmente, i behavior utilizzano direttamente queste. E’
comunque possibile definire attuatori di più alto livello, soprattutto se
si utilizza un robot che deve compiere movimenti complessi che
prevedono l’utilizzo di più motori contemporaneamente. E’ sufficient e
aggiungere i metodi necessari nel modulo che gestisce gli attuatori, si
tratterà comunque di metodi di utilità che compiono funzioni complesse.
Ad esempio è stato definito un metodo per effettuare rotazioni in robot
con due motori e un altro metodo per f ar andare in avanti il robot. In
ogni caso è sconsigliato l’uso di questa tecnica perché piuttosto rigida,
infatti, prevede che il robot abbia determinate caratteristiche.
L’utilizzo degli attuatori in ogni caso dipende dalle caratteristiche
costruttive d el robot (numero di motori, loro disposizione, raggio di
rotazione…) per cui non è possibile avere un modulo in grado di
pilotare robot con caratteristiche molto differenti.
Il modulo degli attuatori e i comportamenti di basso livello (che si
occupano del movimento) sono per forza di cose strettamente dipendenti
dalle caratteristiche del robot.
18
CAPITOLO 4 L’ARCHITETTURA
Diagramma di interazione
Con il seguente diagramma si vuole dare un’idea di come i vari
moduli precedentemente descritti interagiscano tra loro e vadano a
costituire la Subsumption Architecture.
Communicat ion
sendSignal
recei veSignal
Arbitrator
Behavior
Behavior
Behavior
Behavior
Behavior
Behavior
Richiest e di
esecuzione
movimento
stateChanged
Actuator
motori
Sensor
sensori
cpu
Figura 9 – Diagramma di interazione
Le frecce tratteggiate rappresentano delle chiamate di funzione. Si
può notare che mentre per Communication e Sensor è stato poss ibile
indicare il nome della funzione, per il modulo Actuator, a causa della
sua forte dipendenza dalla forma del robot, non è possibile unificare il
modo con cui i comportamenti interagiscono con lui.
I behavior sono collegati con l’Arbitrator con delle f recce continue,
infatti in questo caso non sono i singoli comportamenti a chiamare un
metodo dell’Arbitrator, ma è lui stesso che ciclicamente interroga i
diversi comportamenti alla ricerca di quelli che richiedono di essere
eseguiti (ogni comportamento ha un metodo takeControl che serve a
questo scopo).
I rettangoli con sfondo grigio rappresentano le risorse fisiche del
robot e le frecce grandi indicano da chi sono controllate.
19
Error! Reference source not found.
CAPITOLO 5
BEHAVIORS
In questo capitolo verrà alla luce cosa effettivamente è in grado di
fare il robot attraverso la descrizione dei singoli comportamenti che
concorrono alla capacità elaborativa generale.
Una possibile classificazione legata al tipo di problema risolto è:
 Movimentazione di base.
 Localizzazione
 Pianificazione del movimento
Movimentazione di base
I comportamenti che appartengono a questa categoria sono quelli che
effettivamente controllano gli attuatori. Il loro compito è fondamentale;
infatti nonostante svolgano compiti concettualmente semplici è su di
loro che tutti gli altri si appoggiano per far muovere il robot. Questi
comportamenti dipendono fortemente dalle caratteristiche del mondo in
cui il robot si deve muovere, ma anche dalle caratteristiche fisiche del
robot, anche se queste ultime possono essere inclus e nel modulo
Actuator se le capacità motorie dei robot sono simili.
Descrizione dell’ambiente
La struttura dell’ambiente in cui i robot si spostano è costituita da
una griglia tracciata con del nastro nero su fondo bianco, e con delle
piastrine argentate posizionate in corrispondenza degli incroci. La
struttura del campo così realizzato è mostrata in Figura 10 (la struttura
del campo effettivamente utilizzato si discosta leggermente da quella
qui indicata, non abbiamo infatti cons iderato i semicerchi laterali). In
questo modo il sensore di luce usato registra tre valori sufficientement e
differenti per il fondo, le linee e gli incroci
La griglia è orientata, esistono 4 direzioni fondamentali come
mostrato nella Figura 10. Ogni posizione viene identificata usando un
sistema di coordinate cartesiane con origine in basso a sinistra.
20
Error! Reference source not found.
(0,5)
(5,5)
NORD
OVEST
(0,2)
EST
SUD
(0,1)
(1,1)
home(0,0)
(1,0)
(2,0)
(0,5)
Figura 10 – Struttura dell’ambiente
Seguire una linea con un sensore di luce
Behavior: Follow Line
Questo particolare comportamento è in grado di seguire la linea nera
usando un solo sensore di luce per capire se si è posizionati
correttamente (nero) o si sta uscendo dalla linea.
Analizzando come il robot segue una linea chiusa (ad esempi o
circolare) tracciata su un campo si nota che avanza fino a che il sensore
di luce si trova sopra la riga, e nel momento in cui questa viene smarrit a
(cioè quando il sensore di luce “legge” il colore bianco) il robot viene
fatto ruotare attorno al p roprio asse, sempre dalla stessa parte, fino ad
incontrare nuovamente la linea nera come mostrato nella Figura 11, in
cui la striscia nera rappresenta la linea da seguire, il cerchietto rosso
rappresenta il sensore di luce, mentre la linea rossa rappresenta la
traiettoria seguita dal sensore di luce durante il movimento del robot.
Figura 11 - Prima strategia per l'inseguimento di una linea
21
Error! Reference source not found.
Il robot in definitiva si muove alternando avanzamenti e rotazi oni
sempre nella direzione della curva.
Un secondo problema, seppur meno critico del precedente, nasce
dall’impossibilità di mantenere sempre lo stesso lato di inseguimento
durante una serie di movimenti complessi come quelli richiesti al robot
nello spost amento sulla griglia.
Se invece prendiamo in considerazione una linea retta è necessario
cambiare il lato di inseguimento ad ogni smarrimento della linea, di
modo che questa viene seguita dal robot con una traiettoria a zig -zag
come mostrato in Figura 12.
Figura 12 - Seconda strategia per l'inseguimento di una linea
E’ facile notare che la prossima direzione in cui il robot deve
ruotare per rincontrare la linea è uguale alla penultima rotazione c he ha
effettuato. Si tratta di una sorta di apprendimento: registrando le ultime
due rotazioni che hanno permesso di incontrare la linea si può predire
con una certa sicurezza da che parte si trova la linea rispetto al sensore
di rotazione.
Purtroppo a ca usa di slittamenti o imperfezioni nel piano, è possibile
che tale algoritmo non si riveli vero; il robot in ogni caso commette
errori ogni volta che si passa dall’inseguimento di una linea retta ad una
linea curva e viceversa. Per questi motivi è necessari o limitare la prima
rotazione che potrebbe essere errata e cercare la linea dall’altra parte.
Il comportamento ottiene ciò facendo ruotare il robot prima dalla
parte in cui pensa ci sia la linea per un certo tempo; se la linea non
viene trovata, il robot r uota dalla parte opposta.
L’algoritmo è sviluppato per piccoli allontanamenti dalla linea nera
e deve sicuramente terminare (linea nera trovata) alla seconda
rotazione; nonostante questa apparente limitazione, il behavior
FollowLine si è rivelato abbastanz a robusto.
22
Error! Reference source not found.
Ruotare agli incroci
Behavior: Rotator_t ime
Giunto in prossimità di un incrocio il robot deve decidere in qual e
delle quattro possibili direzioni muoversi dopo averlo superato.
Il movimento consiste in una rotazione del robot su se stesso, fin o a
che il sensore di luce si ritrova sulla linea nera che deve seguire.
Questo comportamento necessità come input la nuova posizione da
raggiungere che deve essere adiacente (uguale a meno di ±1 rispetto alla
coordinata x oppure y) alla posizione corrente e che viene calcolata dai
behavior di esplorazione.
In base quindi a direzione corrente, posizione corrente e nuova
posizione da raggiungere il comportamento calcola se il robot deve
ruotare in senso orario o antiorario e se l’angolo di rotazione deve
essere di 90 º o di 180 º.
Il movimento avviene in due tranche: prima una rotazione a tempo
che ci assicura di portare il sensore di luce sul bianco e
successivamente una rotazione, sempre nello stesso senso che termina
quando il sensore di luce rileva la lin ea nera.
Behavior: Rotator_angle
Questo comportamento è interscambiabile con il precedente se nel
robot è presente un sensore di rotazione collegato al cinematismo
mostrato in Figura 7.
Utilizzando gli stessi input del behavior precedente viene calcolato
se il robot deve ruotare di ± 90° o ± 180°; questo valore viene passato
ad un attuatore avanzato che ne ricava di quanto il valore del sensore di
rotazione deve aumentare o diminuire e fa ruotare il robot fino a
raggiungerlo.
Questo comportamento necessita di una calibrazione preliminare
infatti il fattore di proporzionalità che passa tra l’angolo desiderato e i
valori del sensore di rotazione dipende strettamente dal sistema di
movimentazione del robot e dal tipo di ingranaggi usati nel
cinematismo.
23
Error! Reference source not found.
Localizzazione
Rilevare la propria posizione - Rilevare gli oggetti
Behavior: CrossFound
Il comportamento CrossFound è molto semplice ma fondamentale per
il robot: viene attivato dal passaggio su di un incrocio (il sensore di
luce rileva il colore “silver”) oppure dal rilevamento di un ostacolo
nella posizione verso cui ero diretto.
In entrambi i casi il behavior aggiorna la posizione corrente e si fa
carico di settare alcune variabili che permettono al robot di decidere
come muoversi e quali comportamenti attivare.
Nel caso di rilevamento dell’ostacolo vengono aggiornate la mappa
di esplorazione e la mappa degli oggetti e viene detto al robot di andare
indietro: non posso raggiungere la posizione occupata dall’ostacolo,
quindi torno indietro a quella precedente.
Nel caso di passaggio per un incrocio, aggiorno solo la mappa di
esplorazione e setto una variabile che dà il via al comportamento di
pianificazione che calcola la nuova posizione da raggiungere.
Pianificazione del tracciato
Tracciare un percorso
Behavior: Sol vePat h
Questo comportamento ha come obiettivo il calcolo del percorso
(ovvero dei punti adiacenti, cioè uguali a meno di ±1 rispetto alla
coordinata x oppure y) per raggiungere il punto di destinazione.
La nuova posizione viene selezionata tra quelle adiacenti con i
valori di x o y più vicini a quelli del punto di destinazione. Devono
inoltre essere fatti dei controlli sul nuovo punto selezionato
controllando sulla mappa degli oggetti che non sia occupato da un
ostacolo.
Viene inoltre memorizzata l’ultima posizione occupata dal robot che
deve essere l’ultima scelta possibile tra le posizione adiacenti per poter
uscire dai vicoli ciechi.
24
Error! Reference source not found.
Esplorare la mappa- Raggiungere una posizione
Behavior: Explore All
Il comportamento di esplorazione ha come obiettivo quello di
passare per tutti i punti della mappa; se è presente un ostacolo viene
rilevato e la sua posizione è memorizzata dai comportamenti precedenti
(CrossFound).
Terminata l’esplorazione il robot rimane in attese di nuovi punti da
raggiungere che devono essere inseriti a mano dall’utente; il robot è
quindi in grado di raggiungerli evitando gli ostacoli memorizzato nella
mappa degli oggetti. E’ possibile anche in questa fase inserire nuovi
ostacoli che verranno trovati , memorizzati ed evitati in nuove
esplorazioni.
Interazione con l’utente
Behavior: ButtonInput
Questo comportamento permette all’utente di inserire una nuova
destinazione utilizzando i bottoni presenti sull’RCX.
La procedura di inserimento è molto sempli ce:
1. Premere il tasto PRGM per accedere alla procedure di
programmazione; a questo punto il display dovrebbe mostrare
quattro zeri
2. Premendo ripetutamente il
bottone VIEW
è possibile
incrementare il valore visualizzato sul display; la cifra di sinistra
rappresenta l’ascissa della posizione di destinazione, mentre la
cifra di destra la coordinata, ad ogni pressione del pulsante VIEW
la posizione si incrementa di una unità.
3. Se si vuole memorizzare la posizione e sufficiente premere il
pulsante PRGM a questo pun to si può tornare al punto 2 o
avanzare al punto 4
4. Alla seconda pressione del pulsante PRGM il robot riparte e si
dirigerà alla posizione memorizzata al passa precedente.
E’ possibile memorizzare più posizione fino ad un massimo di
MAX_X *MAX_Y, dove max_x e max_y sono le dimensioni massime
della griglia.
25
CAPITOLO 6 CONCLUSIONI
CAPITOLO 6
CONCLUSIONI
L’architettura sviluppata si è dimostrata adatta alle capacità
dell’hardware disponibile ed efficiente per gli scopi del progetto. In
particolar modo si è riusciti a risolvere com piti complessi riducendoli
ad un insieme di comportamenti semplici; questo facilita notevolmente
la scrittura del codice permettendo di ottenere più facilmente e più
velocemente un prodotto funzionante.
Il lato più interessante è secondo me l’indipendenza che si raggiunge
tra il robot (inteso come dispositivo elettro -meccanico) e il compor tamento (ovvero ciò che si vuol far fare al robot), il tutto ottenuto con
un’architettura robusta e molto versatile.
Contiamo di poterla presto di poterla condividere sul Web e farla
sviluppare dal mondo OpenSource.
26
APPENDICE
APPENDICE
Riportiamo in appendice il codice Java raggruppato per package;
questa struttura rispecchia quella da noi definita e che si ritrova nel
programma Ecplise fornito con il CD.
josx.platform
rcx
ColorSensor.java
package josx.platform.rcx;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class ColorSensor implements ListenerCaller{
private int value;
private int soglia;
private boolean oldColor=false;
private ColorSensorListener[] sListeners;
private short lastListener=0;
private Sensor lightSensor;
public ColorSensor(Sensor lightSensor,int value,int soglia){
this.lightSensor=lightSensor;
this.value=value;
this.soglia=soglia;
}
public boolean isTheColor(){
int aValue = Sensor.readSensorValue(lightSensor.getId(),1);
return ((aValue < value + soglia) && (aValue > value - soglia));
}
public synchronized void addSensorListener(ColorSensorListener
listener){
if(sListeners==null)
sListeners = new ColorSensorListener[3];
sListeners[lastListener++]=listener;
27
APPENDICE
ListenerThread.get().addSensorToMask(lightSensor.getId(),this);
}
public int getID(){
return value;
}
public void callListeners() {
int newValue = Sensor.readSensorValue(lightSensor.getId(),1);
if((newValue < value + soglia) &&
(newValue > value - soglia)){
if(!oldColor){
oldColor=true;
for(int i=0;i<lastListener;i++){
sListeners[i].colorChanged(value);
}
}
}else oldColor=false;
}
}
ColorSensorListener.java
package josx.platform.rcx;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public interface ColorSensorListener {
public void colorChanged(int color);
}
28
APPENDICE
LightSensor. java
package josx.platform.rcx;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class LightSensor {
public static boolean stateLight(int sensValue, int colValue, int
soglia){
if ((sensValue < colValue + soglia) &&
(sensValue > colValue - soglia)){
return true;
}
return false;
}
}
josx.robotics
actuator
Simple Act uator. java
package josx.robotics.actuators;
import josx.platform.rcx.Motor;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public interface SimpleActuator {
public void rotate(int power,boolean clockWise);
public void backward(int power);
public void forward(int power);
public void flt();
public void stop();
}
29
APPENDICE
Rover Act uator. java
package josx.robotics.actuators;
import josx.platform.rcx.Motor;
/**
* @author caimi_mariotti
*
* A simple controller for the motors, very light but need two
* motors connected in an inverse way ad a robot that can rotate
* on its center
*/
public class RoverActuator implements SimpleActuator{
public Motor LEFT_MOTOR = Motor.A;
public Motor RIGHT_MOTOR = Motor.C;
public RoverActuator(Motor leftMotor,Motor rightMotor){
LEFT_MOTOR=leftMotor;
RIGHT_MOTOR=rightMotor;
}
public void rotate(int power,boolean clockWise){
if(clockWise){
RIGHT_MOTOR.setPower(power);
LEFT_MOTOR.setPower(power);
RIGHT_MOTOR.backward();
LEFT_MOTOR.forward();
}else{
RIGHT_MOTOR.setPower(power);
LEFT_MOTOR.setPower(power);
RIGHT_MOTOR.forward();
LEFT_MOTOR.backward();
}
}
public void backward(int power){
RIGHT_MOTOR.setPower(power);
LEFT_MOTOR.setPower(power);
RIGHT_MOTOR.backward();
LEFT_MOTOR.backward();
}
public void forward(int power){
RIGHT_MOTOR.setPower(power);
LEFT_MOTOR.setPower(power);
RIGHT_MOTOR.forward();
LEFT_MOTOR.forward();
}
public void flt(){
RIGHT_MOTOR.flt();
LEFT_MOTOR.flt();
}
public void stop(){
RIGHT_MOTOR.stop();
LEFT_MOTOR.stop();
}
}
30
APPENDICE
Rotation Actuator. java
package josx.robotics.actuators;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public interface RotationActuator extends SimpleActuator {
public void rotate(int power,int angle);
}
Servo. java
package josx.robotics.actuators;
import josx.platform.rcx.Motor;
import josx.platform.rcx.Sensor;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Servo extends RoverActuator implements RotationActuator {
Sensor rotationSensor;
int ratio;
public Servo(Motor leftMotor,Motor rightMotor,Sensor
rotationSensor,int ratio){
super(leftMotor,rightMotor);
this.rotationSensor=rotationSensor;
this.ratio=ratio;
}
public void setRatio(int ratio){
this.ratio=ratio;
}
/**
* @see josx.robotics.actuators.RotationActuator#rotate(int, int)
*/
public void rotate(int power, int angle) {
rotationSensor.setPreviousValue(1000);
31
APPENDICE
if(angle>0){
rotate(power,true);
while(Sensor.readSensorValue(rotationSensor.getId(),1)
<1000+(ratio*angle/360));
stop();
}
else{
rotate(power,false);
while(Sensor.readSensorValue(rotationSensor.getId(),1)
>1000+(ratio*angle/360));
stop();
}
}
}
32
APPENDICE
fsm
Arbitrator. java
package josx.robotics.fsm;
/**
* Arbitrator controls which behavior should currently be active in
* a behavior control system. Make sure to call start() after the
* Arbitrator is instantiated.
* @see robotics.rcx.Behavior
* @author caimi_mariotti
* @version 0.1 18-Sept-2002
*/
public class Arbitrator {
private
private
private
private
Behavior[] behavior;
final int NONE = 99;
int currentBehavior;
BehaviorAction actionThread;
/**
* Allocates an Arbitrator object and initializes it with an array
of
* Behavior objects. The highest index in the Behavior array will
have the
* highest order behavior level, and hence will suppress all lower
level
* behaviors if it becomes active. The Behaviors in an Arbitrator
can not
* be changed once the arbitrator is initialized.<BR>
* <B>NOTE:</B> Once the Arbitrator is initialized, the method
start() must be
* called to begin the arbitration.
* @param behavior An array of Behavior objects.
*/
public Arbitrator(Behavior[] behaviors) {
this.behavior = behaviors;
currentBehavior = NONE;
actionThread = new BehaviorAction();
actionThread.start();
}
/**
* This method starts the arbitration of Behaviors.
* Modifying the start() method is not recomended. <BR>
* Note: Arbitrator does not run in a seperate thread, and hence the
start()
* method will never return.
*/
public void start() {
int totalBehaviors = behavior.length - 1;
while (true) {
// Check through all behavior.takeControl() starting at
highest level behavior
for (int i = 0; i <= totalBehaviors; i++) {
if (behavior[i].takeControl()) {
if (i != actionThread.current) {
33
APPENDICE
// Make currentBehavior this one
actionThread.execute(i);
// Run the currentBehavior.behaviorAction()
Thread.yield();
}
break; // Breaks out of for() loop
}
}
}
}
/**
* This class handles the action() methods of the Behaviors.
*/
private class BehaviorAction extends Thread {
public boolean done = true;
public int current = NONE;
Object synch = new Object();
public void run() {
int curr = current;
while (true) {
if (curr != NONE) {
if (behavior[curr].hasMoreActions()) {
done = false;
int sleepMillis = (behavior[curr].nextAction()).act();
try {
sleep(sleepMillis);
} catch (InterruptedException e) {
}
done = true;
} else {
behavior[curr].reset();
current = NONE;
done = true;
}
}
Thread.yield();
synchronized (synch) {
if (curr != current) {
if (curr != NONE)
behavior[curr].reset();
curr = current;
}
}
}
}
public void execute(int index) {
synchronized (synch) {
current = index;
}
}
}
}
34
APPENDICE
Behavior. java
package josx.robotics.fsm;
import josx.robotics.behaviors.Action;
/**
* The Behavior interface represents an object embodying a specific
* behavior belonging to a robot. Each behavior must define three
things: <BR>
* 1) The circumstances to make this behavior seize control of the
robot.
* e.g. When the touch sensor determines the robot has collided with an
object.<BR>
* 2) The action to exhibit when this behavior takes control.
* e.g. Back up and turn.<BR>
* 3) The actions to perform when another behavior has seized control
from this
* behavior.
* e.g. Stop the current movement and update coordinates.<BR>
* These are represented by defining the methods takeControl(),
action(),
* and suppress() respectively. <BR>
* A behavior control system has one or more Behavior objects. When you
have defined
* these objects, create an array of them and use that array to
initialize an
* Arbitrator object.
*
* @see robotics.rcx.Arbitrator
* @author <a href="mailto:[email protected]">Brian Bagnall</a>
* @version 0.1 27-July-2001
*/
public interface Behavior {
/**
* Returns a boolean to indicate if this behavior should seize
control of the robot.
* For example, a robot that reacts if a touch sensor is pressed:
<BR>
* public boolean takeControl() { <BR>
*
return Sensor.S1.readBooleanValue(); <BR>
* } <BR>
* @return boolean Indicates if this Behavior should seize control.
*/
public boolean takeControl();
/**
* The code in action() represents the actual action of the robot
when this
* behavior becomes active. It can be as complex as navigating
around a
* room, or as simple as playing a tune.<BR>
* <B>The contract for implementing this method is:</B><BR>
* Any action can be started in this method. This method should not
start a
* never ending loop. This method can return on its own, or when the
suppress()
* method is called; but it must return eventually. The action can
run in
35
APPENDICE
* a seperate thread if the designer wishes it, and can therefore
continue
* running after this method call returns.
*/
/**
* Returns the next action to execute in the finite state machine
*
*/
public Action nextAction();
/**
* test if there are more actions to execute in the fsm
*/
public boolean hasMoreActions();
/**
* The code in suppress() should stop the current behavior. This can
include
* stopping motors, or even calling methods to update internal data
(such
* as navigational coordinates). <BR>
* <B>The contract for implementing this method is:</B><BR>
* This method will stop the action running in this Behavior class.
This method
* will <I>not</I> return until that action has been stopped. It is
acceptable for a
* delay to occur while the action() method finishes up.
*/
public void suppress();
/**
* Reset the finite state machine to the initial status
*/
public void reset();
}
36
APPENDICE
behavior
Action. java
package josx.robotics.behaviors;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public interface Action {
public int act();
}
ButtonInput. java
package josx.robotics.behaviors;
import
import
import
import
import
import
import
josx.platform.rcx.Button;
josx.platform.rcx.ButtonListener;
josx.platform.rcx.LCD;
josx.robotics.actuators.SimpleActuator;
josx.robotics.fsm.Behavior;
josx.robotics.world.GridMap;
josx.robotics.world.Path;
/**
* @author caimi_mariotti
*
* This behavior makes the robot rotate until he founds the black line
* rembers the last turns to optimize the following of a straight line
* and a curve line too.
*/
public class ButtonInput implements Behavior, ButtonListener {
Action action;
boolean takeControl = false;
boolean run = true;
GridMap objectMap;
SimpleActuator actuator;
Path path;
byte newpos = 0;
/**
* Constructor for ButtonInput.
*/
public ButtonInput(Path path, SimpleActuator actuator, GridMap
objectMap) {
Button.VIEW.addButtonListener(this);
Button.PRGM.addButtonListener(this);
37
APPENDICE
this.objectMap = objectMap;
this.actuator = actuator;
this.path = path;
createActions();
}
private void createActions() {
action = new Action() {
public int act() {
actuator.stop();
actuator.flt();
return 0;
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*
* da notare che se la current position è
* unguale alla next non mi devo muovere, quindi perde il
controllo.
*
*/
public boolean takeControl() {
return takeControl;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return action;
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
return true;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
}
public void buttonPressed(Button b) {
}
public void buttonReleased(Button b) {
if (takeControl) {
if (Button.VIEW.getId() == b.getId()) {
run = false;
newpos++;
if (newpos % 10 > (objectMap.maxY() - 1)) {
newpos+=4;
}
38
APPENDICE
if (newpos
> (objectMap.maxX() - 1) * 10 + (objectMap.maxY() - 1))
newpos = 0;
LCD.showNumber(newpos);
} else if (Button.PRGM.getId() == b.getId()) {
if (!run) {
path.addPosition((int) newpos / 10, (int) newpos % 10);
run = true;
} else {
takeControl = false;
run = true;
}
}
} else if (Button.PRGM.getId() == b.getId()) {
LCD.showNumber(0000);
takeControl = true;
}
}
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
}
}
CrossFound. java
package josx.robotics.behaviors;
import josx.platform.rcx.ColorSensor;
import josx.platform.rcx.ColorSensorListener;
//import josx.platform.rcx.LCD;
import josx.platform.rcx.Sensor;
import josx.platform.rcx.SensorListener;
import josx.robotics.actuators.SimpleActuator;
import josx.robotics.fsm.Behavior;
import josx.robotics.robots.variable.RobComm;
import josx.robotics.world.Direction;
import josx.robotics.world.GridMap;
import josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class CrossFound
implements Behavior, ColorSensorListener, SensorListener {
//Action
Position
Position
Position
action;
lastPosition;
currentPosition;
nextPosition;
39
APPENDICE
Direction currentDirection;
GridMap exploreMap;
GridMap objectMap;
//boolean takeControl = false;
SimpleActuator actuator;
ColorSensor silver;
Sensor bumper;
/**
* Constructor for CrossFound
*/
public CrossFound(
ColorSensor silverSensor,
Sensor bumperSensor,
Position lastPosition,
Position currentPosition,
Position nextPosition,
Direction currentDirection,
GridMap exploreMap,
GridMap objectMap,
SimpleActuator actuator) {
this.currentDirection = currentDirection;
this.currentPosition = currentPosition;
this.lastPosition = lastPosition;
this.nextPosition = nextPosition;
this.exploreMap = exploreMap;
this.objectMap = objectMap;
this.actuator = actuator;
silver = silverSensor;
bumper = bumperSensor;
silver.addSensorListener(this);
bumper.addSensorListener(this);
//createActions();
}
/*
private void createActions() {
action = new Action() {
public int act() {
actuator.flt();
return 0;
}
};
}
*/
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*/
public boolean takeControl() {
return false;
//return takeControl;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
40
APPENDICE
public Action nextAction() {
//return action;
return null;
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
//takeControl = false;
return false;
//return true;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
}
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
}
/**
* ColorSensorListener Methods
*/
boolean bumped = false;
public void colorChanged(int sensorID) {
if (RobComm.crossDone && sensorID == silver.getID()) {
if (RobComm.forward) {
lastPosition.setPosition(
currentPosition.getX(),
currentPosition.getY());
currentPosition.add(currentDirection);
} else {
currentPosition.subtract(currentDirection);
actuator.flt();
}
RobComm.forward = true;
//takeControl = true;
/*
LCD.showNumber(
currentPosition.getX() * 1000
+ currentPosition.getY() * 100
+ nextPosition.getX() * 10
+ nextPosition.getY());
*/
exploreMap.set(currentPosition);
RobComm.explore = true;
for(int i=0;i==1000;i++){
}
RobComm.solvePath = true;
RobComm.crossDone = false;
bumped = false;
}
}
41
APPENDICE
/**
* SensorListener Methods
*/
public void stateChanged(Sensor bumper, int aOldValue, int
aNewValue) {
if (!bumped
&& this.bumper.getId() == bumper.getId()
&& aNewValue != 0) {
bumped = true;
if (RobComm.forward)
currentPosition.add(currentDirection);
else
currentPosition.subtract(currentDirection);
RobComm.forward = false;
RobComm.explore = true;
//takeControl = true;
/*
LCD.showNumber(
currentPosition.getX() * 1000
+ currentPosition.getY() * 100
+ nextPosition.getX() * 10
+ nextPosition.getY());
*/
exploreMap.set(RobComm.currentPosition);
objectMap.set(RobComm.currentPosition);
}
}
}
Explore All.java
package josx.robotics.behaviors;
import
import
import
import
import
import
josx.robotics.actuators.SimpleActuator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.GridMap;
josx.robotics.world.Path;
josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* This behavior makes the robot rotate until he founds the black line
* rembers the last turns to optimize the following of a straight line
* and a curve line too.
*/
public class ExploreAll implements Behavior {
Action[] aArray;
int lastAction = 0;
boolean takeControl = false;
boolean exploreAll = false;
GridMap exploreMap;
Position currentPosition;
Position finalPosition;
Path path;
SimpleActuator actuator;
42
APPENDICE
/**
* Constructor for ExploreAll
*/
public ExploreAll(
SimpleActuator actuator,
Path path,
GridMap exploreMap,
Position finalPosition,
Position currentPosition) {
this.exploreMap = exploreMap;
this.currentPosition = currentPosition;
this.finalPosition = finalPosition;
this.path = path;
this.actuator = actuator;
createActions();
}
private void createActions() {
aArray = new Action[3];
aArray[0] = new Action() {
private int nextPos = 0;
Position pos = new Position(0,0);
int i = 1;
int j = 0;
int incr = 1;
public int act() {
pos=nextPosition();
while(null!=pos){
if(exploreMap.isFree(pos)){
finalPosition.setPosition(pos.getX(),pos.getY());
RobComm.explore = false;
takeControl = false;
return 0;
}
pos=nextPosition();
}
lastAction++;
return 0;
}
private Position nextPosition() {
if (i < 0 || i >= exploreMap.maxX()) {
incr = -incr;
j++;
i += incr;
}
if (j < exploreMap.maxY()) {
pos.setPosition(i,j);
i += incr;
return pos;
}
return null;
}
};
43
APPENDICE
aArray[1] = new Action() {
public int act() {
Position position = path.nextPosition();
if (null != position) {
finalPosition.setPosition(position.getX(),
position.getY());
RobComm.explore = false;
takeControl = false;
return 0;
}
lastAction++;
return 0;
}
};
aArray[2] = new Action() {
public int act() {
actuator.stop();
actuator.flt();
lastAction--;
return 0;
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*/
public boolean takeControl() {
if (RobComm.explore) {
if (currentPosition.equals(finalPosition)) {
takeControl = true;
} else {
RobComm.explore = false;
}
}
return takeControl;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return aArray[lastAction];
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
return lastAction < aArray.length;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
lastAction = 0;
}
44
APPENDICE
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
}
}
Follow Line.java
package josx.robotics.behaviors;
import
import
import
import
import
import
import
josx.platform.rcx.ColorSensor;
josx.platform.rcx.ColorSensorListener;
josx.robotics.actuators.SimpleActuator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.util.Timer;
josx.util.TimerListener;
/**
* @author caimi_mariotti
*
* This behavior makes the robot rotate until he founds the black line
* rembers the last turns to optimize the following of a straight line
* and a curve line too.
*/
public class FollowLine implements Behavior, ColorSensorListener,
TimerListener {
Action[] aArray;
int lastAction=0;
boolean clockwise=true;
boolean rightRotation=true;
SimpleActuator actuator;
ColorSensor white;
ColorSensor black;
ColorSensor silver;
int time = 500;
Timer timer;
/**
* Constructor for FollowLine.
*/
public FollowLine(SimpleActuator act,ColorSensor whiteSensor,
ColorSensor blackSensor,ColorSensor silverSensor){
actuator=act;
white = whiteSensor;
black = blackSensor;
silver = silverSensor;
white.addSensorListener(this);
black.addSensorListener(this);
silver.addSensorListener(this);
timer=new Timer(time,this);
createActions();
}
45
APPENDICE
private void createActions(){
aArray = new Action[3];
aArray[0]=new Action(){
public int act(){
time = 500;
if (black.isTheColor())
rightRotation = !clockwise;
if(RobComm.forward)
actuator.forward(4);
else
actuator.backward(2);
if (white.isTheColor())
lastAction=2;
return 0;
}
};
aArray[1]=new Action(){
public int act(){
timer.stop();
timer.setDelay(time);
clockwise = rightRotation;
lastAction++;
return 50;
}
};
aArray[2]=new Action(){
public int act(){
actuator.rotate(3,clockwise);
timer.start();
return 0;
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*
* da notare che se la current position è
* unguale alla next non mi devo muovere, quindi perde il
controllo.
*
*/
public boolean takeControl() {
return true;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return aArray[lastAction];
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
46
APPENDICE
return lastAction < aArray.length;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
lastAction=0;
}
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
lastAction=0;
}
public void timedOut(){
clockwise=!clockwise;
timer.stop();
time = time+1000;
timer.setDelay(time);
}
/**
* ColorSensorListener Methods
*/
public void colorChanged (int sensorID){
if (sensorID == white.getID()) {
timer.stop();
lastAction=1;
}
if(sensorID == black.getID()){
timer.stop();
lastAction=0;
}
if(sensorID == silver.getID()){
timer.stop();
lastAction=0;
}
}
}
47
APPENDICE
Rotator_angle.java
package josx.robotics.behaviors;
import
import
import
import
import
import
josx.platform.rcx.ColorSensor;
josx.platform.rcx.ColorSensorListener;
josx.robotics.actuators.RotationActuator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.Direction;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Rotator_angle implements Behavior, ColorSensorListener {
boolean takeControl = false;
Action[] aArray;
int lastAction=0;
RotationActuator rotAct;
ColorSensor white;
ColorSensor black;
/**
* Constructor for Rotator_angle
*/
public Rotator_angle(RotationActuator actuator,ColorSensor
whiteSensor,
ColorSensor blackSensor){
this.rotAct=actuator;
white=whiteSensor;
black=blackSensor;
white.addSensorListener(this);
black.addSensorListener(this);
createActions();
}
private void createActions(){
aArray = new Action[2];
////ATTENZIONE!!!
aArray[0]=new Action(){
public int act(){
//TextLCD.print("rO_t");
//LCD.showProgramNumber(0);
rotAct.forward(2);
lastAction++;
return 50;
}
};
48
APPENDICE
aArray[1]=new Action(){
public int act(){
//TextLCD.print("rO_t");
//LCD.showProgramNumber(1);
rotAct.flt();
Direction curr = RobComm.currentDirection;
Direction next =
RobComm.currentPosition.compare(RobComm.nextPosition);
int rot = curr.compare(next);
if (rot != 0){
rotAct.rotate(2,rot*90);
if (rot == 1)
RobComm.currentDirection.rotate(true);
else if (rot == -1)
RobComm.currentDirection.rotate(false);
else{
RobComm.currentDirection.rotate(true);
RobComm.currentDirection.rotate(true);
}
}
RobComm.crossDone = true;
lastAction++;
takeControl = false;
return 0;
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*/
public boolean takeControl() {
return takeControl;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return aArray[lastAction];
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
return lastAction < aArray.length;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
rotAct.flt();
}
/**
49
APPENDICE
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
lastAction=0;
}
/**
* ColorSensorListener Methods
*/
public void colorChanged (int sensorID){
if(!RobComm.crossDone &&
(sensorID == black.getID() || sensorID == white.getID())){
takeControl=true;
}
}
}
Rotator_time.java
package josx.robotics.behaviors;
import
import
import
import
import
import
josx.platform.rcx.ColorSensor;
josx.platform.rcx.ColorSensorListener;
josx.robotics.actuators.SimpleActuator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.Direction;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Rotator_time implements Behavior, ColorSensorListener {
boolean takeControl = false;
Action[] aArray;
int lastAction=0;
boolean clockWise=true;
boolean whitePassed=false;
SimpleActuator actuator;
ColorSensor white;
ColorSensor black;
/**
* Constructor for Rotator_time
*/
public Rotator_time(SimpleActuator act,ColorSensor whiteSensor,
ColorSensor blackSensor){
actuator=act;
white=whiteSensor;
black=blackSensor;
white.addSensorListener(this);
black.addSensorListener(this);
50
APPENDICE
createActions();
}
private void createActions(){
aArray = new Action[4];
////ATTENZIONE!!!
aArray[0]=new Action(){
public int act(){
//TextLCD.print("rO_t");
//LCD.showProgramNumber(0);
actuator.flt();
lastAction++;
return 1;
}
};
aArray[1]=new Action(){
public int act(){
//TextLCD.print("rO_t");
//LCD.showProgramNumber(1);
actuator.flt();
Direction curr = RobComm.currentDirection;
Direction next =
RobComm.currentPosition.compare(RobComm.nextPosition);
int rot=curr.compare(next);
if(rot==0){
RobComm.crossDone = true;
lastAction = 4;
takeControl = false;
return 0;
}
else if (rot == 1)
{
clockWise=true;
}
else if (rot == -1)
{
clockWise=false;
}
else{
if(RobComm.currentPosition.getX()==0||
RobComm.currentPosition.getY()+1
==RobComm.exploreMap.maxY()){
if(rot==-2){
clockWise=true;
}else{
clockWise=false;
}
}else{
if(rot==-2){
clockWise=false;
}else{
clockWise=true;
}
}
}
51
APPENDICE
actuator.rotate(2,clockWise);
lastAction++;
return 600;
}
};
aArray[2]=new Action(){
public int act(){
//LCD.showProgramNumber(2);
whitePassed=true;
actuator.rotate(2,clockWise);
return 0;
}
};
aArray[3]=new Action(){
public int act(){
//LCD.showProgramNumber(3);
whitePassed=false;
//aggiorno direzione corrente
RobComm.currentDirection.rotate(clockWise);
lastAction = 1;
return 0;
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*/
public boolean takeControl() {
return takeControl;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return aArray[lastAction];
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
return lastAction < aArray.length;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
actuator.flt();
}
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
lastAction=0;
}
52
APPENDICE
/**
* ColorSensorListener Methods
*/
public void colorChanged (int sensorID){
if(whitePassed && sensorID == black.getID()){
lastAction = 3;
}
else
if(!RobComm.crossDone &&
(sensorID == black.getID() || sensorID == white.getID())){
takeControl=true;
}
}
}
Sol vePath. java
package josx.robotics.behaviors;
import
import
import
import
import
import
josx.platform.rcx.LCD;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.Direction;
josx.robotics.world.GridMap;
josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class SolvePath implements Behavior {
boolean takeControl = false;
Action action;
Position lastPosition;
Position currentPosition;
Position nextPosition;
Position finalPosition;
Direction currentDirection;
GridMap objectMap;
/**
* Constructor for SolvePath
*/
public SolvePath(
Position lastPosition,
Position currentPosition,
Position nextPosition,
Position finalPosition,
Direction currentDirection,
GridMap objectMap) {
this.objectMap = objectMap;
this.currentDirection = currentDirection;
53
APPENDICE
this.lastPosition = lastPosition;
this.currentPosition = currentPosition;
this.nextPosition = nextPosition;
this.finalPosition = finalPosition;
createActions();
}
private void createActions() {
action = new Action() {
public int act() {
RobComm.pathSolved = solvePath();
LCD.showNumber(
currentPosition.getX() * 1000
+ currentPosition.getY() * 100
+ nextPosition.getX() * 10
+ nextPosition.getY());
RobComm.solvePath = false;
return 0;
}
public boolean solvePath() {
if (currentPosition.equals(finalPosition))
return true;
int xDiff = 1;
int yDiff = 1;
if (lastPosition.getX() > currentPosition.getX())
xDiff = -1;
if (lastPosition.getY() > currentPosition.getY())
yDiff = -1;
boolean xIs = false;
boolean yIs = false;
if (currentPosition.getY() < finalPosition.getY()) {
yIs = true;
} else if (currentPosition.getY() > finalPosition.getY()) {
yDiff = -1;
yIs = true;
}
if (currentPosition.getX() < finalPosition.getX()) {
xIs = true;
} else if (currentPosition.getX() > finalPosition.getX()) {
xDiff = -1;
xIs = true;
}
if (currentDirection.equals(Direction.EST)
|| currentDirection.equals(Direction.OVEST)) {
if (yIs)
goTo(
currentPosition.getX(),
currentPosition.getY() + yDiff,
xDiff,
-yDiff,
false);
54
APPENDICE
else if (xIs)
goTo(
currentPosition.getX() + xDiff,
currentPosition.getY(),
xDiff,
-yDiff,
false);
} else if (
currentDirection.equals(Direction.NORD)
|| currentDirection.equals(Direction.SUD)) {
if (xIs)
goTo(
currentPosition.getX() + xDiff,
currentPosition.getY(),
-xDiff,
yDiff,
false);
else if (yIs)
goTo(
currentPosition.getX(),
currentPosition.getY() + yDiff,
-xDiff,
yDiff,
false);
}
return false;
}
private void goTo(
int x,
int y,
int xIncr,
int yIncr,
boolean lastTry) {
if (!objectMap.isFree(currentPosition.getX(), y)
|| (!lastTry
&& (lastTry =
(lastPosition.getX() == currentPosition.getX()
&& lastPosition.getY() == y)
? true
: lastTry))) {
goTo(
currentPosition.getX() + xIncr,
currentPosition.getY(),
-xIncr,
yIncr,
lastTry);
} else if (
!objectMap.isFree(x, currentPosition.getY())
|| (!lastTry
&& (lastTry =
(lastPosition.getX() == currentPosition.getX()
&& lastPosition.getY() == y)
? true
: lastTry))) {
55
APPENDICE
goTo(
currentPosition.getX(),
currentPosition.getY() + yIncr,
xIncr,
-yIncr,
lastTry);
} else
nextPosition.setPosition(x, y);
}
};
}
/**
* @see josx.robotics.fsm.Behavior#takeControl()
*/
public boolean takeControl() {
return RobComm.solvePath;
}
/**
* @see josx.robotics.fsm.Behavior#nextAction()
*/
public Action nextAction() {
return action;
}
/**
* @see josx.robotics.fsm.Behavior#hasMoreActions()
*/
public boolean hasMoreActions() {
return true;
}
/**
* @see josx.robotics.fsm.Behavior#suppress()
*/
public void suppress() {
}
/**
* @see josx.robotics.fsm.Behavior#reset()
*/
public void reset() {
}
}
56
APPENDICE
robots
PathFinder. java
package josx.robotics.robots;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
josx.platform.rcx.ColorSensor;
josx.platform.rcx.Motor;
josx.platform.rcx.Sensor;
josx.robotics.actuators.RoverActuator;
josx.robotics.actuators.SimpleActuator;
josx.robotics.behaviors.CrossFound;
josx.robotics.behaviors.ExploreAll;
josx.robotics.behaviors.FollowLine;
josx.robotics.behaviors.Rotator_time;
josx.robotics.behaviors.SolvePath;
josx.robotics.fsm.Arbitrator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.Direction;
josx.robotics.world.GridMap;
josx.robotics.world.Path;
josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class PathFinder {
public static Sensor BUMP = Sensor.S1;
public static Sensor LIGHT = Sensor.S2;
public
public
public
public
public
public
static
static
static
static
static
static
short
short
short
short
short
short
valBlack = 29;
valWhite = 50;
valSilver = 60;
sogliaWhite = 4;
sogliaBlack = 8;
sogliaSilver = 4;
public static void main(String[] args) {
BUMP.setTypeAndMode(1, 0x20);
BUMP.passivate();
LIGHT.setTypeAndMode(3, 0x80);
LIGHT.activate();
ColorSensor wSensor = new ColorSensor(LIGHT, valWhite,
sogliaWhite);
ColorSensor bSensor = new ColorSensor(LIGHT, valBlack,
sogliaBlack);
ColorSensor sSensor = new ColorSensor(LIGHT, valSilver,
sogliaSilver);
57
APPENDICE
RobComm.forward = true;
RobComm.crossDone = true;
RobComm.pathSolved = false;
RobComm.currentPosition = new Position(0, 0);
RobComm.lastPosition = new Position(0, 0);
RobComm.finalPosition = new Position(0, 0);
RobComm.solvePath = true;
RobComm.explore = true;
RobComm.nextPosition = new Position(0, 0);
RobComm.currentDirection = new Direction(Direction.EST);
RobComm.objectMap = new GridMap(6, 6);
RobComm.objectMap.reset();
RobComm.exploreMap = new GridMap(6, 6);
RobComm.exploreMap.reset();
RobComm.exploreMap.set(RobComm.currentPosition);
Path path = new Path(3);
path.addPosition(0, 0);
path.addPosition(5, 5);
//path.addPosition(0, 5);
SimpleActuator act = new RoverActuator(Motor.A, Motor.C);
/* NOTE: low level behaviors should have higher index
* number in the array
*/
Behavior[] bArray = new Behavior[5]; //ATTENZIONE!!
bArray[4] = new FollowLine(act, wSensor, bSensor, sSensor);
bArray[3] = new Rotator_time(act, wSensor, bSensor);
bArray[2] =
new CrossFound(
sSensor,
BUMP,
RobComm.lastPosition,
RobComm.currentPosition,
RobComm.nextPosition,
RobComm.currentDirection,
RobComm.exploreMap,
RobComm.objectMap,
act);
bArray[1] =
new SolvePath(
RobComm.lastPosition,
RobComm.currentPosition,
RobComm.nextPosition,
RobComm.finalPosition,
RobComm.currentDirection,
RobComm.objectMap);
58
APPENDICE
bArray[0] =
new ExploreAll(
act,
path,
RobComm.exploreMap,
RobComm.finalPosition,
RobComm.currentPosition);
//bArray[0] = new ButtonInput(path,act,RobComm.objectMap);
Arbitrator arby = new Arbitrator(bArray);
arby.start();
}
}
PathFinder_angl e.java
package josx.robotics.robots;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
josx.platform.rcx.ColorSensor;
josx.platform.rcx.Motor;
josx.platform.rcx.Sensor;
josx.robotics.actuators.RotationActuator;
josx.robotics.actuators.Servo;
josx.robotics.behaviors.ButtonInput;
josx.robotics.behaviors.CrossFound;
josx.robotics.behaviors.ExploreAll;
josx.robotics.behaviors.FollowLine;
josx.robotics.behaviors.Rotator_angle;
josx.robotics.behaviors.SolvePath;
josx.robotics.fsm.Arbitrator;
josx.robotics.fsm.Behavior;
josx.robotics.robots.variable.RobComm;
josx.robotics.world.Direction;
josx.robotics.world.GridMap;
josx.robotics.world.Path;
josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class PathFinder_angle {
public static Sensor BUMP = Sensor.S1;
public static Sensor LIGHT = Sensor.S2;
public static Sensor ROT = Sensor.S3;
public
public
public
public
public
public
static
static
static
static
static
static
short
short
short
short
short
short
valBlack = 29;
valWhite = 50;
valSilver = 60;
sogliaWhite = 4;
sogliaBlack = 8;
sogliaSilver = 4;
59
APPENDICE
public static void main(String[] args) {
BUMP.setTypeAndMode(1, 0x20);
BUMP.passivate();
LIGHT.setTypeAndMode(3, 0x80);
LIGHT.activate();
ROT.setTypeAndMode(4, 0xE0);
ROT.activate();
ColorSensor wSensor = new ColorSensor(LIGHT, valWhite,
sogliaWhite);
ColorSensor bSensor = new ColorSensor(LIGHT, valBlack,
sogliaBlack);
ColorSensor sSensor = new ColorSensor(LIGHT, valSilver,
sogliaSilver);
RobComm.forward = true;
RobComm.crossDone = true;
RobComm.pathSolved = false;
RobComm.currentPosition = new Position(0, 0);
RobComm.lastPosition = new Position(0, 0);
RobComm.finalPosition = new Position(0, 0);
RobComm.solvePath = true;
RobComm.explore = true;
RobComm.nextPosition = new Position(0, 0);
RobComm.currentDirection = new Direction(Direction.EST);
RobComm.objectMap = new GridMap(6, 6);
RobComm.objectMap.reset();
RobComm.exploreMap = new GridMap(6, 6);
RobComm.exploreMap.reset();
RobComm.exploreMap.set(RobComm.currentPosition);
Path path = new Path(3);
RotationActuator act = new Servo(Motor.A, Motor.C, ROT, 100);
/* NOTE: low level behaviors should have higher index
* number in the array i.e. b2 is higher level than b1.
*/
Behavior[] bArray = new Behavior[6]; //ATTENZIONE!!
bArray[5] = new FollowLine(act, wSensor, bSensor, sSensor);
bArray[4] = new Rotator_angle(act, wSensor, bSensor);
bArray[3] =
new CrossFound(
sSensor,
BUMP,
RobComm.lastPosition,
RobComm.currentPosition,
RobComm.nextPosition,
RobComm.currentDirection,
RobComm.exploreMap,
RobComm.objectMap,
act);
60
APPENDICE
bArray[2] =
new SolvePath(
RobComm.lastPosition,
RobComm.currentPosition,
RobComm.nextPosition,
RobComm.finalPosition,
RobComm.currentDirection,
RobComm.objectMap);
bArray[1] =
new ExploreAll(
act,
path,
RobComm.exploreMap,
RobComm.finalPosition,
RobComm.currentPosition);
bArray[0] = new ButtonInput(path, act, RobComm.objectMap);
Arbitrator arby = new Arbitrator(bArray);
arby.start();
}
}
61
APPENDICE
variable\ RobComm. java
package josx.robotics.robots.variable;
import
import
import
import
josx.robotics.world.Direction;
josx.robotics.world.GridMap;
josx.robotics.world.Path;
josx.robotics.world.Position;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class RobComm {
public static Position lastPosition;
public static Position nextPosition;
public static Position finalPosition;
public static Position currentPosition;
public static Direction currentDirection;
public static boolean forward;
public static GridMap objectMap;
public static GridMap exploreMap;
public static Path path;
public
public
public
public
static
static
static
static
boolean
boolean
boolean
boolean
crossDone;
pathSolved;
solvePath;
explore;
}
62
APPENDICE
world
Direction. java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Direction extends DirectionConstants {
private byte direction;
Direction(byte direction) {
this.direction = direction;
}
public Direction(Direction direction) {
this.direction = direction.toByte();
}
public int toInt() {
return (int) direction;
}
private byte toByte() {
return direction;
}
public String toString() {
if (equals(Direction.NORD))
return "nOrd";
if (equals(Direction.SUD))
return "SUd ";
if (equals(Direction.EST))
return "ESt ";
if (equals(Direction.OVEST))
return "OVES";
return "0";
}
public char printChDir() {
if (equals(Direction.NORD))
return 'n';
if (equals(Direction.SUD))
return 'S';
if (equals(Direction.EST))
return 'E';
if (equals(Direction.OVEST))
return 'O';
return 'P';
}
63
APPENDICE
public int compare(Direction dir) {
if (((toByte() + 1) % 4) == dir.toByte())
return +1;
else if (((toByte() + 3) % 4) == dir.toByte())
return -1;
else
return (int) toByte() - dir.toByte();
}
public void set(Direction newDirection) {
direction = newDirection.toByte();
}
public boolean equals(Direction dir) {
return this.direction == dir.direction;
}
public void rotate(boolean right) {
if (right)
direction = (byte) ((direction + 1) % 4);
else
direction = (byte) ((direction + 3) % 4);
}
}
DirectionConstant s.java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class DirectionConstants {
public
public
public
public
static
static
static
static
Direction
Direction
Direction
Direction
NORD = new Direction((byte) 0);
EST = new Direction((byte) 1);
SUD = new Direction((byte) 2);
OVEST = new Direction((byte) 3);
}
64
APPENDICE
GridMap.java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class GridMap {
private boolean[][] map;
public GridMap(int x, int y) {
map = new boolean[x][y];
}
public boolean isFree(int x, int y) {
if (isIn(x, y))
return !map[x][y];
return false;
}
public boolean isFree(Position position) {
return isFree(position.getX(), position.getY());
}
public void reset() {
for (int i = 0; i < map.length; i++)
for (int j = 0; j < map[i].length; j++)
map[i][j] = false;
}
private boolean isIn(int x, int y) {
return (x >= 0 && x < map.length && y >= 0 && y < map[0].length);
}
public void set(int x, int y) {
if (isIn(x, y))
map[x][y] = true;
}
public void set(Position position) {
set(position.getX(), position.getY());
}
public int maxX() {
return map.length;
}
public int maxY() {
return map[0].length;
}
}
65
APPENDICE
Path. java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Path {
private Position[] positions;
private int nextPosition = 0;
private int firstFree = 0;
private boolean full = false;
public Path(int dimension) {
positions = new Position[dimension];
for (int i = 0; i < dimension; i++)
positions[i] = new Position(0, 0);
}
public boolean addPosition(int x, int y) {
synchronized (this) {
if (!full) {
positions[firstFree].setPosition(x, y);
int next = (firstFree + 1) % positions.length;
if (next == nextPosition)
full = true;
else
firstFree = next;
return true;
}
return false;
}
}
public Position nextPosition() {
synchronized (this) {
Position position = null;
if (nextPosition != firstFree) {
position = positions[nextPosition];
nextPosition = (nextPosition + 1) % positions.length;
}
full = false;
return position;
}
}
}
66
APPENDICE
Position. java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class Position {
private byte x;
private byte y;
public Position(int x, int y) {
this.x = (byte) x;
this.y = (byte) y;
}
public int getX() {
return (int) x;
}
public int getY() {
return (int) y;
}
public Position setPosition(int X, int Y) {
this.x = (byte) X;
this.y = (byte) Y;
return this;
}
public boolean equals(Position position) {
return (getX() == position.getX() && getY() == position.getY());
}
public void add(Direction direction) {
if (direction.compare(Direction.NORD) == 0)
y++;
else if (direction.compare(Direction.SUD) == 0)
y--;
else if (direction.compare(Direction.OVEST) == 0)
x--;
else if (direction.compare(Direction.EST) == 0)
x++;
}
public void subtract(Direction direction) {
if (direction.compare(Direction.NORD) == 0)
y--;
else if (direction.compare(Direction.SUD) == 0)
y++;
else if (direction.compare(Direction.OVEST) == 0)
x++;
else if (direction.compare(Direction.EST) == 0)
x--;
}
67
APPENDICE
public Direction compare(Position nextPos) {
if (getY() < nextPos.getY())
return Direction.NORD;
if (getY() > nextPos.getY())
return Direction.SUD;
if (getX() > nextPos.getX())
return Direction.OVEST;
if (getX() < nextPos.getX())
return Direction.EST;
return Direction.NORD;
}
}
PositionConstants. java
package josx.robotics.world;
/**
* @author caimi_mariotti
*
* To change this generated comment edit the template variable
"typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class PositionConstants {
public
public
public
public
static
static
static
static
byte
byte
byte
byte
NORD = 0x00;
EST = 0x01;
SUD = 0x02;
OVEST = 0x03;
}
68