UNIVERSITÀ DEGLI STUDI DI TRIESTE
              FACOLTÀ DI INGEGNERIA
 Dipartimento di Elettrotecnica, Elettronica ed Info...
2
Alla mia famiglia...




                       3
4
Indice generale
1. Introduzione..............................................................................................
1. Introduzione
L'utilizzo di robot mobili negli ambienti interni è diventato oggigiorno di uso corrente. Essi non
vengono...
2. La robotica
Da secoli l'uomo sogna di costruite dispositivi che siano in grado di imitare il comportamento degli
esseri...
Non voglio mettere in dubbio pareri sicuramente più autorevoli dei miei ma vorrei aprire una
piccola discussione a tal pro...
•   Domotica: scienza che si occupa dello studio delle tecnologie atte a migliorare la qualità
       della vita nella cas...
propriocettivi sono in grado di misurare grandezze proprie del robot come la posizione, la velocità
dei giunti o delle ruo...
3. Basi del progetto
É possibile scomporre il progetto in due parti fondamentali: quella di navigazione e quella di
autolo...
creating new possibilities for distributed and collaborative sensing and control.
             Player supports a wide vari...
sotto certi vincoli imposti al problema.
Date quindi due sequenze di campioni 
                                  X = x1, ...
Illustrazione 4: Ordine di calcolo delle 
                            distanze D(i,j).

Si può notare che una corrisponden...
Illustrazione 5: Predecessori per 
                                  D(i,j) (cella grigia). Tra questi 
                  ...
indipendente l'una dall'altra. L'algoritmo è stato realizzato in modo da lavorare con immagini in
bianco e nero. Questo se...
if (la cella è all'interno dei bordi)

                 costo(i,j) = |pixel(Y,i)-pixel(X,j)|
                   +min(costo...
Image Processing Library (IPL) sviluppata da Intel e oggi integrata nella libreria commerciale IIPP
(Intel Integrated Perf...
4. Il sistema sviluppato
Il sistema sviluppato ha richiesto la realizzazione di due componenti “separati”: navigazione e 
...
BufferedImage image = null;
       image = new PngImage().read(new java.io.File(nome_file));


Una volta che l'immagine è ...
•   I poligoni interni non devono avere uno spessore troppo sottile: una condizione accettabile è
       che i poligoni ab...
Illustrazione 15: Estrazione dalla mappa dei contorni


4.1.1.3. Proiezione dei vertici
L'obbiettivo è la scomposizione de...
ognuno dei due metodi viene utilizzato un ulteriore metodo “eliminaFalsePositive” per eliminare
quelle linee che erroneame...
18 la linea “l2” (generata dalla proiezione dei vertici) interseca una linea di bordo “l1” nel vertice
“nV”. Solo la linea...
Illustrazione 20: Poligoni e loro baricentri


4.1.1.6. Costruzione del grafo
A partire dai poligoni si può creare il graf...
pesi in modo tale da poter recuperare il percorso. Si analizzano i vettori dei pesi (dall'ultimo fino al
primo) consideran...
4.2. Localizzazione mediante riferimenti visivi
Si necessita di una telecamera o più semplicemente di una webcam per la vi...
coppia. Le informazione contenute sono in ordine: Ordinata del centro del landmark Rosso,
       Ascissa del centro del la...
immagini su cui erano stati fatti in precedenza i test. Il tutto era stato realizzato per il funzionamento
in ambiente Win...
Illustrazione 25: Rappresentazione grafica delle grandezze definite dal problema
4.2.1. Analisi geometrica del problema del posizionamento
Tutte le relazioni matematiche adibite al calcolo della distanza...
I                            I       a                             a
  OP y cos − y =a ⇒OP y =                        ...
Illustrazione 26: Triangolazione e angolo del robot

Nel grafico vengono rappresentati tre sistemi di riferimento: il sist...
Resta ancora da definire l'angolo con cui è ruotato il robot. Se si considera il sistema di riferimento
(xII,yII) è possib...
Illustrazione 31: θ: R in III° quad. in (xI,yI)            Illustrazione 32: θ: R in II° quad. in (xI,yI)
e B in II° quad....
Illustrazione 33: Immagine acquisita dalla webcam


4.2.2.2. Ricerca dei landmark e verifica dei ritrovamenti
Per scoprire...
//80 valore di soglia, 255 valore da imporre al pixel se il
      //colore del pixel supera la soglia, OpenCV.THRESH_BINAR...
Illustrazione 36: Algoritmo "blobs" per il 
                       riconoscimento dei contorni

Il metodo, come è facile n...
Illustrazione 37:  
                                      Landmark Blu e Rosso


4.2.3. Caricamento strutture dati della l...
per richiedere da Java il caricamento dell'immagine (o del landmark a seconda della posizione nel
vettore) con i dati corr...
l'immagine da caricare ma anche la posizione dell'angolo superiore sinistro dell'immagine su quella
acquisita e il colore ...
if (Double.isInfinite(posX) || Double.isInfinite(posY) ||
       Double.isInfinite(theta) || Double.isNaN(posX) || Double....
Illustrazione 38: Caricamento mappa

Il caricamento della mappa e la sua elaborazione sono operazioni che possono impiegar...
visualizzata una dialog che richiede l'inserimento del tempo minimo (in secondi) per la ricerca dei
landmark e l'aggiornam...
5. Risultati sperimentali
Tutte le misure effettuate sono stare realizzate nella stessa stanza e con le stesse condizioni ...
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Tesi De Franceschi Daniel
Upcoming SlideShare
Loading in …5
×

Tesi De Franceschi Daniel

2,359 views

Published on

Navigazione di un robot mobile e autolocalizzazione con riferimenti visivi

Published in: Education, Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,359
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
28
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Tesi De Franceschi Daniel

  1. 1. UNIVERSITÀ DEGLI STUDI DI TRIESTE FACOLTÀ DI INGEGNERIA Dipartimento di Elettrotecnica, Elettronica ed Informatica ________________ Tesi di Laurea in PROGRAMMAZIONE DEI CALCOLATORI Navigazione di un robot mobile e autolocalizzazione con riferimenti visivi Laureando: Relatore: De Franceschi Daniel Prof. Massimiliano Nolich Anno Accademico 2008-2009 1
  2. 2. 2
  3. 3. Alla mia famiglia... 3
  4. 4. 4
  5. 5. Indice generale 1. Introduzione......................................................................................................................................6 2. La robotica........................................................................................................................................7 3. Basi del progetto.............................................................................................................................11 3.1. Player­Stage.............................................................................................................................11 3.2. Tesi precedente........................................................................................................................12 3.2.1. Dynamic Time Warping..................................................................................................12 3.2.2. Dynamic Planar Warping................................................................................................15 3.3. OpenCV..................................................................................................................................17 4. Il sistema sviluppato.......................................................................................................................19 4.1. Navigazione.............................................................................................................................19 4.1.1. Calcolo del percorso minimo data una mappa dell'ambiente...........................................19 4.1.1.1. Caricamento della mappa..........................................................................................19 4.1.1.2. Estrazione dei vertici................................................................................................20 4.1.1.3. Proiezione dei vertici................................................................................................22 4.1.1.4. Decomposizione delle linee in segmenti..................................................................23 4.1.1.5. Generazione dei poligoni.........................................................................................24 4.1.1.6. Costruzione del grafo...............................................................................................25 4.1.1.7. Calcolo del percorso minimo tra due punti tramite l'algoritmo di Dijkstra..............25 4.1.2. Navigazione del robot......................................................................................................26 4.2. Localizzazione mediante riferimenti visivi.............................................................................27 4.2.1. Analisi geometrica del problema del posizionamento.....................................................31 4.2.2. Detect dei landmark........................................................................................................35 4.2.2.1. Acquisizione dell'immagine.....................................................................................35 4.2.2.2. Ricerca dei landmark e verifica dei ritrovamenti....................................................36 4.2.2.3. Salvataggio dei landmark........................................................................................38 4.2.3. Caricamento strutture dati della libreria nativa...............................................................39 4.2.4. Richiamo degli algoritmi di calcolo................................................................................41 4.2.5. Prima verifica “algoritmica” dei risultati........................................................................41 4.3. Integrazione Robot mobile e autolocalizzazione....................................................................42 4.3.1. Interfaccia del software....................................................................................................42 5. Risultati sperimentali......................................................................................................................45 6. Conclusioni.....................................................................................................................................54 7. Appendice: How To........................................................................................................................56 7.1. Installazione di Player­Stage su Ubuntu/Linux 9.10................................................................56 7.2. Installazione librerie OpenCV su Ubuntu/Linux 9.10.............................................................56 7.3. Utilizzo delle JNI in Ubuntu 9.10............................................................................................57 7.4. Osservazione sul detect dei landmark.....................................................................................59 8. Bibliografia.....................................................................................................................................60 5
  6. 6. 1. Introduzione L'utilizzo di robot mobili negli ambienti interni è diventato oggigiorno di uso corrente. Essi non vengono solo adibiti al trasporto ma anche a funzionalità più “semplici” come la pulizia degli ambienti. È nata pertanto l'esigenza di fornire ai robot la capacità di autolocalizzarsi negli spazi di lavoro con tecniche relativamente semplici e poco costose. Uno dei metodi utilizzati è l'odometria che stima la posizione del veicolo su ruote a partire dal numero di giri percorsi dalle ruote. È noto che gli odometri sono affetti ad errori che diventano via via sempre più grandi man mano che il robot si muove nell'ambiente. Pertanto la posizione reale e quella stimata tendono nel tempo a discostarsi sempre di più. Risulta necessario affiancare a questo strumento altri metodi per permettere di resettare l'errore odometrico. In questa tesi è stato sviluppato un algoritmo di navigazione in ambiente interno per un robot mobile che utilizza un algoritmo di localizzazione tramite riferimenti visivi per resettare periodicamente l'errore odometrico. La base del progetto è una tesi precedente [1] in cui è stato realizzato un algoritmo in grado, mediante il calcolo della deformazione di landmark visuali posti nell'ambiente, di determinare la distanza tra il punto di visione e i landmark. Si è pensato di sfruttare l'algoritmo per la localizzazione durante la navigazione. É stata sistemata una webcam a basso costo sul robot e sono stati posizionati dei riferimenti visivi sul soffitto: questo ha consentito di sviluppare un algoritmo per stimare la posizione del robot e quindi consentire allo stesso di autolocalizzarsi. É stato necessario studiare la geometria del problema (robot – riferimenti visivi). Per implementare il metodo proposto bisogna posizionare dei landmark (semplici fogli stampati) sul soffitto e utilizzare una webcam a basso costo per le acquisizioni delle immagini. È inoltre richiesto un personal computer le cui risorse di calcolo possono essere anche limitate. Nel progetto sono stati utilizzati Player [2], OpenCV [3][4] e Java [5]. La tesi si articola secondo la seguente struttura. • Il secondo capitolo tratta in modo generico di alcuni aspetti legati ai robot e ai robot mobili e alla loro evoluzione nel tempo. • Il terzo capitolo descrive le basi del progetto cioè i punti di partenza dalla quale si è sviluppato tutto il resto. • Il quarto capitolo evidenzia cosa è stato sviluppato (navigazione e autolocalizzazione) e mette in luce eventuali problematiche che è stato necessario risolvere lungo il percorso di sviluppo. • Il quinto capitolo espone i test che sono stati effettuati e i risultati sperimentali conseguiti. • Le conclusioni sono presentate nel sesto capitolo e mettono in luce quali siano i limiti del metodo ed eventuali possibili soluzioni. • L'appendice affronta alcune problematiche che riguardano la configurazione dell'ambiente di sviluppo e l'installazione del sistema sviluppato. 6
  7. 7. 2. La robotica Da secoli l'uomo sogna di costruite dispositivi che siano in grado di imitare il comportamento degli esseri viventi. Il termine “robot” ha origine dal termine ceco “lavoro forzato” o “servo”. Il termine è stato introdotto dallo scrittore Karel Čapek nel suo dramma teatrale “I robot universali di Rossum” del 1921 dove diede vita a qualcosa di molto simile al mostro di Frankenstein cioè una creatura generata grazie a una combinazione tra chimica e biologia piuttosto che con metodi meccanici. «Il vecchio Rossum, grande filosofo, [...] cercò di imitare con una sintesi chimica la sostanza viva detta protoplasma finché un bel giorno scoprì una sostanza il cui comportamento era del tutto uguale a quello della sostanza viva sebbene presentasse una differente composizione chimica, era l'anno 1932 [...]. Per esempio, poteva ottenere una medusa con il cervello di Socrate oppure un lombrico lungo cinquanta metri. Ma poiché non aveva nemmeno un pochino di spirito, si ficcò in testa che avrebbe fabbricato un normale vertebrato addirittura l'uomo. [...] Doveva essere un uomo, visse tre giorni completi. Il vecchio Rossum non aveva un briciolo di gusto. Quel che fece era terribile. Ma dentro aveva tutto quello che ha un uomo. Davvero, un lavoro proprio da certosino. E allora venne l'ingegner Rossum, il nipote del vecchio. Una testa geniale. Appena vide quel che stava facendo il vecchio, disse: È assurdo fabbricare un uomo in dieci anni. Se non lo fabbricherai più rapidamente della natura, ce ne possiamo benissimo infischiare di tutta questa roba. [...] Gli bastò dare un'occhiata all'anatomia per capire subito che si trattava d'una cosa troppo complicata e che un buon ingegnere l'avrebbe realizzata in modo più semplice. [...] Quale operaio è migliore dal punto di vista pratico? È quello che costa meno. Quello che ha meno bisogni. Il giovane Rossum inventò l'operaio con il minor numero di bisogni. Dovette semplificarlo. Eliminò tutto quello che non serviva direttamente al lavoro. Insomma, eliminò l'uomo e fabbricò il Robot.» [6] Oggi le credenze popolari riguardo ai robot meccanici non sono molto differenti da quelle creazioni biologiche romanzesche. Non c'è limite alle possibili definizioni (data la profondità del suo significato) del termine “Robot”: «Un manipolatore riprogrammabile e multifunzionale progettato per spostare materiali, componenti, attrezzi o dispositivi specializzati attraverso vari movimenti programmati per la realizzazione di vari compiti» [7] «Macchina automatica capace di svolgere con opportuni comandi, alcune funzioni o attività proprie dell'uomo; automa meccanico» [8] «Dispositivo meccanico che riproduce i movimenti e, generalmente l'aspetto esterno dell'uomo e degli animali. In cibernetica apparecchio automatico programmabile, destinato a sostituire autonomamente l'uomo in alcune attività manuali, specialmente quelle faticose, pericolose o costose, di alcuni settori dell'industria e della ricerca scientifica» [9] Appare evidente come queste definizioni siano abbastanza distanti e relegate ad ambiti specifici. 7
  8. 8. Non voglio mettere in dubbio pareri sicuramente più autorevoli dei miei ma vorrei aprire una piccola discussione a tal proposito. La concezione sul cos'è un robot è mutata nel tempo: inizialmente concepito come essere “biologico” per poi essere relegato all'ambito puramente meccanico. E se questa nuova definizione fosse troppo restrittiva? Al giorno d'oggi virus e batteri vengono modificati e quindi riprogrammati per svolgere compiti specifici: mi chiedo se questi non possano essere considerati loro stessi dei robot. Di conseguenza, non tutti gli esseri biologici sono a loro volta considerabili robot? Nella normale concezione di “dispositivo meccanico” la differenza tra una comune macchina e un robot potrebbe apparire piuttosto sfumata: entrambi devono essere alimentati, necessitano di attuatori e di meccanismi per poter interagire con l'ambiente e di una interfaccia utente per l'interazione con l'operatore [10]. Cos'è quindi che li distingue? L'avvento della microelettronica è stato il passo fondamentale per il cambio del paradigma per la progettazione di macchine: nasce la meccatronica cioè il paradigma moderno per la progettazione delle macchine. Ciò che cambia è l'introduzione di sensori e meccanismi di controllo all'interno dei dispositivi, che siano lavatrici, forni a microonde, fotocamere o robot industriali. La meccatronica applicata alla progettazione dei robot introduce assieme al controllo anche una più o meno sviluppata forma di intelligenza. La nascita e lo sviluppo della robotica si deve principalmente al bisogno di macchine utili nell'industria. Da questo bisogno è nata la robotica industriale e l'automazione industriale. In questo caso l'ambiente è altamente strutturato e al posto degli operatori umani vengono utilizzati degli operatori esperti (cioè formati all'uso) che normalmente sono in grado di eseguire procedure ben definite e ripetitive. La presenza umana in questi ambienti di lavoro è ben delimitata. Successivamente la robotica industriale venne esportata nella cosiddetta robotica di servizio che differisce dalla robotica industriale per una maggior condivisione dello spazio di lavoro tra uomo e robot, per le maggiori capacità percettive e per comportamenti più reattivi. La robotica di servizio si è sviluppata su vari rami che vanno dalle applicazioni spaziali, alla robotica biomedica, alla robotica da guerra ma anche per applicazioni domestiche o di intrattenimento. Un robot autonomo è una macchina capace di accettare ed eseguire comandi o missioni in ambienti non completamente strutturati senza l'intervento dell'uomo. La problematica è di pianificare i comportamenti del robot in un ambiente non noto a priori o variabile nel tempo in funzione della richiesta di esecuzione di un determinato compito. I campi fondamentali per la progettazione e la realizzazione dei robot sono sopratutto l'ingegneria e l'informatica e di conseguenza anche la matematica. Molto spesso, però, si richiede a questi dispositivi di compiere delle operazioni o raggiungere determinati obbiettivi in modo più o meno autonomo e pertanto risulta di fondamentale importanza anche il campo dell'intelligenza artificiale che si può definire come l'abilità di un computer di svolgere funzioni e ragionamenti tipici della mente umana. L'espressione “intelligenza artificiale” fu coniata nel 1956 dal matematico americano John McCarthy. Lo scopo di questa disciplina sarebbe stato quello di “far fare alle macchine delle cose che richiederebbero l'intelligenza se fossero fatte dagli uomini” [11]. Si può pertanto affermare che la robotica cognitiva è diventata sempre di più interdisciplinare e oggigiorno necessita anche di discipline umanistiche (per esempio biologia, linguistica...) per raggiungere obbiettivi in campi molto diversi fra loro. Al giorno d'oggi infatti la robotica viene utilizzata negli ambiti più disparati. Giusto per citarne qualcuno: • Biorobotica: studio dei sistemi biologici dal punto di vista “biomeccatronico” per lo sviluppo di dispositivi per applicazioni biomediche o sviluppo di metodologie e tecnologie innovative per la progettazione e realizzazione di macchine e sistemi bioispirati e biomimetici (ad esempio umanoidi ed animaloidi). 8
  9. 9. • Domotica: scienza che si occupa dello studio delle tecnologie atte a migliorare la qualità della vita nella casa e negli ambienti antropizzati. • Robotica industriale: tipologia di robot utilizzata nella produzione industriale. • Robotica spaziale • Robotica evoluzionistica • Competizioni robotiche • … Per un robot “mobile” [12][13] quello che fa la differenza è ovviamente l'ambiente operativo: il robot dovrebbe essere in grado di operare autonomamente in ambienti non strutturati e/o dinamici e parzialmente osservabili (lo spazio di lavoro può essere più ampio di quello immediatamente osservabile). Molto spesso possono essere inaccessibili all'uomo perché per esempio pericolosi o impossibili da raggiungere. I robot mobili si suddividono in varie categorie: i robot terrestri, robot sottomarini e i robot aerei. I robot terrestri possono usufruire di varie tipologie di strumenti di locomozione, non solamente ruote ma anche zampe oppure l'unione ruote/zampe. I robot sottomarini possono spostarsi mediante eliche o getti d'acqua mentre i robot aerei tramite ali - fisse o rotanti - oppure possono assumere strutture tipo dirigibili o palloni. Illustrazione 1: Spirit, Mars Exploration Rover A Un robot in generale è costituito da una struttura meccanica, da attuatori, sensori e sistemi di controllo. Gli attuatori si possono dividere in tre diverse tipologie: attuatori elettrici, idraulici e pneumatici. A seconda dell'uso che se ne deve fare si prediligerà una tipologia rispetto all'altra tenendo in considerazione pregi e difetti delle varie categorie. Per poter “percepire” l'ambiente il robot deve possedere dei sensori cioè dei dispositivi capaci di percepire e misurare proprietà fisiche dell'ambiente quali pressione, temperatura, distanze, luminosità... Un grande problema è l'incertezza delle informazioni che il robot “riceve” dai sensori non solo dovuta all'imperfezione dei sensori stessi ma anche dalle condizioni ambientali che possono essere anche molto variabili. I sensori si suddividono in due grandi categorie: sensori propriocettivi ed eterocettivi. I sensori 9
  10. 10. propriocettivi sono in grado di misurare grandezze proprie del robot come la posizione, la velocità dei giunti o delle ruote. I sensori eterocettivi sono in grado di misurare grandezze dell'ambiente dove il robot opera e si scompongo in quattro categorie: sensori di forza/coppia e tattili, sensori di prossimità o distanza, sistemi di visione e sensori speciali per applicazioni specifiche. Il sistema di controllo è il cervello del sistema, esso decide quali azioni deve compiere in base alle informazioni che riceve dai sensori e agli algoritmi di controllo che implementa. Il robot disponibile nel laboratorio SMARTLAB è costituito da una piattaforma robotica circolare dotata di due motori in corrente continua con odometri, ha tre sensori ad ultrasuoni ed è comandato da un processore ARM. Come si vede nell'illustrazione 2 in cima al robot è posizionata la webcam utilizzata per la localizzazione del robot tramite i riferimenti visivi. Illustrazione 2: Robot su cui sono stati effettuati i test di autolocalizzazione 10
  11. 11. 3. Basi del progetto É possibile scomporre il progetto in due parti fondamentali: quella di navigazione e quella di autolocalizzazione. La parte di navigazione utilizza software open source del calibro di “Player” e “Stage” studiati per il corso di “Sistemi operativi per la robotica”. Tutto il lavoro è stato svolto in ambiente GNU/Linux (Ubuntu 9.10 Karmic Koala). “Ubuntu è un sistema operativo libero e gratuito basato su GNU/Linux, che unisce stabilità, sicurezza e facilità di utilizzo. È perfetto per computer portatili, desktop e server e fornisce applicazioni adatte ad ogni esigenza, per l'uso in casa, a scuola o a lavoro. Ubuntu prende il nome da un'antica parola africana che significa umanità agli altri, oppure io sono ciò che sono per merito di ciò che siamo tutti. La distribuzione Ubuntu migra lo spirito di Ubuntu nel mondo del software.” [14] Per la parte di autolocalizzazione viene fatto un uso piuttosto massiccio delle librerie OpenCV [3] che sono librerie orientate alla “computer vision”. I linguaggi di programmazione utilizzati sono stati C++ e Java. Per la stesura della tesi è stato utilizzato OpenOffice. Come riportato dalla home italiana del progetto: “OpenOffice.org è una suite per ufficio completa, rilasciata con una licenza libera e Open Source che ne consente la distribuzione gratuita. Legge e scrive file nei formati utilizzati dai prodotti più diffusi sul mercato e, a garanzia della futura accessibilità dei dati, nel formato OpenDocument, standard ISO. Consente inoltre l'esportazione in formato PDF. OpenOffice.org è liberamente, gratuitamente e legalmente utilizzabile in ogni contesto, pubblico, privato, professionale e aziendale.” [15] 3.1. Player-Stage Lo scopo dei due software è riassumibile come estratto dalla home page del progetto [2]: The Player Project creates Free Software that enables research in robot and sensor systems. The Player robot server is probably the most widely used robot control interface in the world. Its simulation backends, Stage and Gazebo, are also very widely used. Released under the GNU General Public License, all code from the Player/Stage project is free to use, distribute and modify. Player is developed by an international team of robotics researchers and used at labs around the world. Software: • Player robot device interface Player provides a network interface to a variety of robot and sensor hardware. Player's client/server model allows robot control programs to be written in any programming language and to run on any computer with a network connection to the robot. Player supports multiple concurrent client connections to devices, 11
  12. 12. creating new possibilities for distributed and collaborative sensing and control. Player supports a wide variety of mobile robots and accessories. Look here for a list of currently supported components. • Stage multiple robot simulator Stage simulates a population of mobile robots moving in and sensing a two- dimensional bitmapped environment. Various sensor models are provided, including sonar, scanning laser rangefinder, pan-tilt-zoom camera with color blob detection and odometry. Stage devices present a standard Player interface so few or no changes are required to move between simulation and hardware. Many controllers designed in Stage have been demonstrated to work on real robots. • Gazebo 3D multiple robot simulator Gazebo is a multi-robot simulator for outdoor environments. Like Stage, it is capable of simulating a population of robots, sensors and objects, but does so in a three-dimensional world. It generates both realistic sensor feedback and physically plausible interactions between objects (it includes an accurate simulation of rigid-body physics). Gazebo presents a standard Player interface in addition to its own native interface. Controllers written for the Stage simulator can generally be used with Gazebo without modification (and vise- versa). Player Project software runs on Linux, Solaris, *BSD and Mac OSX (Darwin). 3.2. Tesi precedente La parte di localizzazione è basata sul lavoro di un altro tesista – Luca Camilotti – che nella sua tesi si è occupato degli algoritmi visuali per la localizzazione dei robot mobili [1] tramite la costruzione dell'algoritmo Dynamic Planar Warping. Lo scopo che ci si prefigge è il calcolo della posizione del robot tramite la determinazione della distanza dai landmark posti sul soffitto. La distanza dai landmark viene ricavata appunto mediante l'algoritmo Dynamic Planar Warping (DPW) che fa uso a sua volta dell'algoritmo Dynamic Time Warping (DTW). 3.2.1. Dynamic Time Warping Il Dynamic Time Warping (DTW) è un metodo ben collaudato ed è utilizzato comunemente nel campo del riconoscimento dei suoni o “speech recognition”. Esso consiste nella ricerca della corrispondenza ottima tra i campioni di due sequenze monodimensionali che rappresentano solitamente l'andamento temporale di due segnali. La ricerca è implementata con un algoritmo di programmazione dinamica, che è in grado di semplificare la complessità del problema ad un ordine polinomiale. Si considerino due sequenze di campioni. Tanto per fissare le idee, esse potrebbero essere ad  esempio le risposte impulsive di due sistemi. Si supponga di sapere a priori che i due segnali sono  legati da una certa relazione, ad esempio hanno lo stesso andamento nel tempo ma uno è più  “dilatato” rispetto all'altro, oppure uno presenta un rumore che ne altera la forma, etc. Si vuole  trovare una corrispondenza tra le due sequenze, ovvero si vuole mappare un segnale nell'altro. Il  DTW si presta perfettamente a questo compito, perché trova la corrispondenza migliore possibile  12
  13. 13. sotto certi vincoli imposti al problema. Date quindi due sequenze di campioni  X = x1, x 2,... , x n   e   Y = y 1, y 2,... , y m  ,  e definita una funzione di costo  cost  x j , y i = f  x j , y i  che esprima quantitativamente la  distanza tra una coppia di campioni, la corrispondenza ottima può essere determinata con la  programmazione dinamica poiché vale la relazione ricorsiva                       Di , j=cost  x j , y i min { Di−1, j , Di−1, j−1 , Di , j−1}   dove D m , n è la distanza di Dynamic Time Warping tra la sequenza X e la sequenza Y. Illustrazione 3: Matrice per la costruzione del  cammino ottimo. Si immagini di costruire una matrice come quella rappresentata in figura 3. Se viene marcata la  cella (i, j) significa che il campione i­esimo della sequenza Y è stato associato (o mappato) con il  campione j­esimo della sequenza X. Come si vede dal disegno, sono presenti due segmenti inclinati  che demarcano i limiti superiore e inferiore oltre ai quali il cammino di mappatura non può andare.  Tali limiti vengono assegnati ad hoc sulla base delle caratteristiche del problema. L'algoritmo ha complessità polinomiale perché si devono eseguire al più  n⋅m operazioni. Infatti  per calcolare la distanza  Di , j basta calcolare la funzione costo per  y i e  x j e sommare la  distanza minima dei tre predecessori   D i−1, j , Di−1, j−1 , D i , j−1 (3.2.1­1) Calcolando le distanza nell'ordine opportuno, ovvero colonna per colonna e dal basso verso l'alto, ad  ogni passo sono già stati calcolati tutti i predecessori. 13
  14. 14. Illustrazione 4: Ordine di calcolo delle  distanze D(i,j). Si può notare che una corrispondenza costruita in questo modo è del tipo “molti a molti”, poiché ad  un punto nell'ascissa possono venir associati molti punti sull'ordinata, e viceversa. Ciò significa che  la corrispondenza ottima trovata è bidirezionale, ovvero rappresenta la mappatura ottima di X con Y,  ma allo stesso tempo è anche quella di Y con X (a parità di vincoli). Questa caratteristica è molto  importante. Infatti, da un punto di vista matematico, ne consegue che alla fine del processo si  trovano due applicazioni  F :  j∈ℕi∈ℕ  e   G :i∈ℕ j∈ℕ , ma nessuna delle due in generale è una funzione, per le ragioni spiegate sopra. Quindi per rappresentare la sequenza mappata è necessario operare delle scelte tra più possibilità  qualora ad un campione della sequenza test fossero associati più campioni della sequenza di  riferimento (si supponga di voler mappare quest'ultima in quella di test). E' ragionevole a questo  punto prendere, tra le varie possibilità, quella più vicina al campione test in termini di valore della  funzione di costo. Un'attenzione particolare va rivolta ai vincoli del problema. In primo luogo, la relazione ricorsiva  (3.2.1­1) vincola la mappatura ad essere monotona, cioè a procedere incrementando almeno uno tra  gli indici i e j. Questo garantisce che il cammino non possa mai “tornare indietro” (cosa che avrebbe  poco senso). I limiti superiore ed inferiore, rappresentati dai due segmenti inclinati nella matrice di  ricerca in figura 3, regolano l'entità della “deformazione” ammessa. Più le zone proibite per il  cammino vengono ridotte, più la sequenza X può venir deformata per combaciare con Y e viceversa  (trattandosi di una mappatura bidirezionale). Infine vanno poste in risalto le condizioni al contorno. Normalmente si opera la ricerca fissando gli estremi delle due sequenze, ovvero si associa a priori y 1 con x 1 , e y m con x n . Dopo aver calcolato tutte le distanze, si procede con il backtracking per la ricerca del cammino ottimo partendo dalla cella (m,n) e andando a ritroso. Il fatto che il cammino si completi con la cella (1,1) è intrinsecamente garantito dal fatto che è l'unica cella a non avere predecessori. 14
  15. 15. Illustrazione 5: Predecessori per  D(i,j) (cella grigia). Tra questi  viene selezionato come precedente  quello che ha valore minimo della  distanza D. 3.2.2. Dynamic Planar Warping L'algoritmo per la mappatura di immagini si basa sull'algoritmo Dinamic Time Warping (DTW). L'idea fondamentale è quella di considerare un'immagine come un vettore i cui elementi sono le righe di pixel dell'immagine stessa. Da un punto di vista formale, sia A un'immagine (in scala di grigi) tale A={ai , j con i , j=1,.. , N } Il fatto di considerare, per ora, immagini quadrate è per comodità puramente notazionale. Tutti i ragionamenti che si faranno si potranno facilmente estendere al caso generale di immagini rettangolari. Si indichi con a(i, :) l'i-esima riga di pixel dell'immagine A. In tal senso è possibile scrivere A=[ a i ,1 , a i ,2 , a i ,3 , ... , a i , N  ] T Quindi, date due immagini A e B tali che A=[ a i ,1 , a i ,2 , a i ,3 , ... , a i , N  ] T (3.2.2-1) B=[ b i ,1 , bi ,2 , bi ,3 , ... , b i , M  ] T (3.2.2-2) si intende applicare l'algoritmo DTW fornendo come sequenze di campioni le due immagini A e B, espresse nelle forme (3.2.2-1) e (3.2.2-2). Come detto all'inizio, ogni campione è in realtà un'intera riga di pixel. Date queste premesse, per completare l'algoritmo basta definire la funzione di costo, cioè l'espressione della distanza tra due campioni. Ancora una volta si fa uso del Dynamic Time Warping. Trattandosi infatti del confronto tra due righe di pixel, è immediato pensare al confronto tra due sequenze monodimensionali di campioni. In questo modo la distanza risulta quella data dalla mappatura ottima tra le due righe. Dal punto di vista della distorsione dell'immagine, le righe vengono deformate in modo 15
  16. 16. indipendente l'una dall'altra. L'algoritmo è stato realizzato in modo da lavorare con immagini in bianco e nero. Questo semplifica la gestione delle immagini e facilita la definizione della distanza tra due pixel. Inizialmente si può considerare il valore assoluto della differenza tra le due intensità, cost  x j , y i =∣ x j− y i ∣ Sebbene sia un modo di operare che introduce numerose ambiguità, i risultati ottenuti con questa scelta sono interessanti. E' importante tener conto che questo tipo di algoritmo permette di elaborare in tempi più che ragionevoli delle immagini di dimensioni normali, cosa che in generale non avviene per gli algoritmi multistadio. Resta pur sempre vero che il modello multistadio garantisce la monotonia e la continuità del warping, ma, per ora, si tratta di un modello puramente teorico e inapplicabile nella pratica. Dal punto di vista computazionale, l'algoritmo presentato ha complessità di tipo polinomiale. Per fissare le idee, si supponga che sia A che B abbiano dimensioni NxN . Trattandosi di applicare l'algoritmo DTW in modo nidificato, il “guscio” più esterno dell'algoritmo richiede al più N 2 operazioni. Tuttavia ognuna di queste operazioni è a sua volta un esecuzione dell'algoritmo DTW ( 2 N operazioni). In totale, la complessità risulta essere O N 2⋅N 2 =O N 4 Di seguito è riportato lo pseudo codice dell'algoritmo. /* Dynamic Planar Warping */ for (j=1; j<=N; j++) for (i=1; i<=N; i++) if (la cella è all'interno dei bordi) distanza(i,j) = distanzaDTW(A(i,:),B(j,:)) +min(distanza(i-1,j), distanza(i,j-1),distanza(i-1,j-1); backtracking(i,j) = precedente_con_distanza_minima; end if end for end for Lo pseudo codice fa uso della procedura distanzaDTW che implementa l'algoritmo DTW monodimensionale. Chiaramente, oltre a memorizzare il backtracking per ogni associazione tra le righe, è necessario memorizzare la mappatura dei pixel all'interno della riga stessa. Questo compito è affidato alla procedura che calcola la DTW monodimensionale. La mappatura ottima delle due righe confrontate viene salvata in una lista concatenata facente parte della struttura dati relativa alla cella (i, j). /* Dynamic Time Warping */ for (j=1; j<=N; j++) for (i=1; i<=N; i++) 16
  17. 17. if (la cella è all'interno dei bordi) costo(i,j) = |pixel(Y,i)-pixel(X,j)| +min(costo(i-1,j),costo(i,j-1),costo(i-1,j-1); backtracking(i,j) = precedente_con_costo_minimo; end if end for end for memorizza_backtracking_in_lista; A titolo esemplificativo sono riportati di seguito alcuni esempi di mappature elaborate con questo algoritmo. Illustrazione 7:   Illustrazione 6:   Illustrazione 8:   Immagine Pattern Immagine acquisita Immagine  ricostruita Illustrazione 9:  Illustrazione 10:  Illustrazione 11:   Immagine Pattern Immagine  Immagine  acquisita ricostruita La matrice di deformazione viene utilizzata nelle formule (4.2.1-1) e (4.2.1-2) del capitolo successivo. 3.3. OpenCV Per le operazioni di detect e per una buona parte delle operazioni di calcolo vengono utilizzate le “OpenCV” [4] - Open Computer Vision - che sono librerie (orientate appunto alla computer vision) open source e multipiattaforma. Per l'uomo riuscire ad estrarre informazioni oggettive da un'immagine è un'operazione che normalmente potrebbe essere considerata banale ma altrettanto banale non è se a farlo è un sistema automatico. Le librerie OpenCV nascono per raccogliere le funzionalità degli algoritmi più utilizzati in questo ambito. È basata almeno parzialmente sulla Intel 17
  18. 18. Image Processing Library (IPL) sviluppata da Intel e oggi integrata nella libreria commerciale IIPP (Intel Integrated Performance Primitives). La licenza utilizzata è in stile BSD. Per l'utilizzo dei metodi più utilizzati delle OpenCV tramite Java è stato necessario l'utilizzo di un porting Java delle librerie: “Processing” [16]. Per l'installazione delle librerie e l'utilizzo del porting Java si rimanda all'apposita appendice. 18
  19. 19. 4. Il sistema sviluppato Il sistema sviluppato ha richiesto la realizzazione di due componenti “separati”: navigazione e  autolocalizzazione. Infine è stato necessario unire le due parti. 4.1. Navigazione Per quanto riguarda la navigazione del robot l'obbiettivo che ci si è proposti era la realizzazione di un “algoritmo” di navigazione con percorso minimo tra due punti data la mappa dell'ambiente. Si è supposto che il robot potesse muoversi liberamente nell'ambiente. Il problema è stato scomposto in due sottoproblemi: il calcolo del percorso minimo tra due punti data la mappa e la navigazione vera e propria. 4.1.1. Calcolo del percorso minimo data una mappa dell'ambiente Il processo è formato da varie operazioni che possono essere distinte in: caricamento della mappa, estrazione dei vertici della mappa, proiezione dei vertici, decomposizione delle linee, generazione dei poligoni, creazione del grafo e calcolo del percorso minimo tramite Dijkstra. 4.1.1.1. Caricamento della mappa Illustrazione 12: Mappa originale dell'ambiente Il caricamento dell'immagine avviene per semplicità tramite un package esterno alla Java SE. Questo package permette di caricare il contenuto dell'immagine in un oggetto del tipo BufferedImage. 19
  20. 20. BufferedImage image = null; image = new PngImage().read(new java.io.File(nome_file)); Una volta che l'immagine è stata caricata, si prosegue alla realizzazione di una matrice di boolean con un numero di colonne pari al numero di pixel in larghezza e un numero di righe pari al numero di pixel in altezza dell'immagine. Ogni elemento della matrice viene posto a “true” se il pixel corrispondente nell'immagine è occupato da un “muro” o da un qualsiasi ostacolo. 4.1.1.2. Estrazione dei vertici Il passo successivo è il riconoscimento e lo sfoltimento dei vertici. Da un file di testo opportunamente formattato vengono caricate le configurazioni che verranno utilizzate come strumento di confronto per gli elementi della matrice di boolean. 1 1 1 0 0 0 0 0 0 -1 0 1 0 0 0 0 0 0 0 0 1 1 1 -1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 -1 0 1 0 0 0 1 0 0 1 0 0 1 0 -1 0 1 0 1 0 0 1 0 0 0 0 0 0 -1 0 1 0 … Ogni riga rappresenta una configurazione: i primi 9 valori rappresentano una matrice 3x3 di pixel, seguono due coppie di due valori ciascuno che indicano la direzione in cui spostarsi se viene trovata la configurazione nell'elemento della matrice dell'immagine e l'ultimo valore indica se il punto può essere considerato o meno un vertice. Nel caso della prima riga la configurazione indica la seguente situazione: Cioè il “muro” è rappresentato dalle caselle nere mentre la casella grigia è la casella che l'algoritmo sta considerando. Seguendo il muro posso muovermi nella cella (-1, 0) oppure nella cella (1,0) rispetto al punto considerato. L'ultimo “0” indica che il punto non è considerato un vertice. Un vettore conterrà i riferimenti di un insieme di oggetti del tipo “Configurazione” che conterranno le informazioni appena caricate dal file di testo. Il metodo “trovaContorni” (ricorsivo) analizza riga per riga della matrice dell'”immagine” fintanto che non trova un pixel vuoto. Una volta individuato, viene chiamato il metodo “seguiContorno” (ricorsivo) che segue il contorno del “poligono” finché non torna al punto di partenza. Per come è stato costruito l'algoritmo di ricerca del contorno e di conseguenza per come è fatto il file di configurazione è necessario che la mappa venga realizzata in modo tale che: • Deve esserci un bordo esterno di “muro” di almeno un pixel sui bordi dell'immagine. 20
  21. 21. • I poligoni interni non devono avere uno spessore troppo sottile: una condizione accettabile è che i poligoni abbiano almeno dimensione 3x3. Il risultato della sua elaborazione è rappresentato dai segmenti del contorno degli ostacoli che ha trovato lungo il cammino. A questo punto è però necessario “eliminare” temporaneamente il poligono trovato per poter continuare la scansione: se non venisse oscurato il poligono trovato si rischierebbe di “processarlo” altre volte. Per compiere questa operazione viene chiamato il metodo “cancellaPoligono” (anche questa ricorsivo) che non fa altro che partendo dal punto di partenza (in questo caso quello individuato da “trovaContorni”) oscurare tutti i pixel adiacenti e gli adiacenti degli adiacenti fintanto che non non ha più punti da oscurare poiché gli altri punti adiacenti sono già stati oscurati o sono nativamente oscurati. Ovviamente questa operazione viene fatta su una matrice ausiliaria per non perdere la matrice originale. Il metodo “trovaContorni” continuerà la scansione fintanto che non ci saranno più zone “vuote”. A questo punto però è necessario svuotare la zona esterna trasformandola in vuota e richiamare nuovamente il metodo “trovaContorni” incaricandolo questa volta di trovare non più le zone vuote ma quelle piene in modo tale da riuscire a scovare eventuali catene di zone piene, vuote, piene, vuote una dentro l'altra. Il risultato di tutte queste operazioni è il “recupero” dei poligoni contenuti nella mappa. Molti di questi poligoni potrebbero però essere facilmente semplificati: poiché l'immagine è una bitmap e al suo interno non sono presenti linee ma pixel è necessario ridurre il numero di vertici per alleggerire il carico computazionale delle operazioni successive. Il metodo predisposto a questo scopo è il metodo “cancellaVertici” che verifica se gruppi di tre vertici sono o meno allineati, cancellando l'intermedio nel caso questo accada. L'ultimo procedimento che a livello teorico avrebbe dovuto funzionare a livello pratico portava in alcuni casi all'approssimazione troppo brutale di alcune “tipologie” di poligoni pertanto il confronto è stato costruito in modo tale da considerare non più tre punti bensì quattro. Il risultato che si ottiene è un insieme di vettori di linee che rappresentano il contorno dei vari poligoni: un vettore per ogni poligono. L'approssimazione è ovviamente più marcata qualora siano presenti degli ostacoli obliqui rispetto agli assi dell'immagine oppure in presenza di poligoni che siano o contengano degli elementi curvilinei. Nell'esempio che si è scelto di utilizzare si può vedere come venga approssimata una circonferenza. Illustrazione 13:  Illustrazione 14:   Ostacolo "tondo" Ostacolo "tondo"  approssimato Il risultato ottenuto è visibile nell'immagine successiva: 21
  22. 22. Illustrazione 15: Estrazione dalla mappa dei contorni 4.1.1.3. Proiezione dei vertici L'obbiettivo è la scomposizione delle parti vuote della mappa (adesso formata solamente da linee ed eventualmente vertici) in poligoni (rettangoli e triangoli). In questo passo si generano tutte quelle linee (proiezione dei vertici precedentemente individuati) che una volta scomposti saranno i segmenti che formano il contorno dei poligoni interni e che saranno necessari per la costruzione del grafo. Il metodo “creaLineeOriz” scorre tutti i vertici, genera due linee orizzontali una diretta verso sinistra, l'altra verso destra verificando quali intersezioni abbiano con gli ostacoli presenti sulla mappa. Se queste linee “esistono” le inserisce in un vettore per poter essere successivamente elaborate. Non tutte le proiezioni saranno considerate valide per esempio non ci può essere proiezione verso sinistra di un vertice che è coperto a sinistra da un muro. Illustrazione 16:   Esempio proiezione  vertice Il metodo “creaLineeVert” fa esattamente la stessa cosa, solo che per le linee verticali. All'interno di 22
  23. 23. ognuno dei due metodi viene utilizzato un ulteriore metodo “eliminaFalsePositive” per eliminare quelle linee che erroneamente potrebbero venire generate in presenza di ostacoli di forma e di struttura “particolari”. Illustrazione 17: I vertici sono stati proiettati 4.1.1.4. Decomposizione delle linee in segmenti Il passo precedente ha permesso di ottenere le linee di “prolungamento” dei vertici, ma l'obbiettivo è di ottenere i segmenti che formano i vari poligoni all'interno della mappa. É quindi necessario decomporre le linee. Illustrazione 18: Esempio   decomposizione Il metodo “decomposizione” scansiona una alla volta le linee verticali trovando le linee orizzontali che si intersecano con le prime e le scompone a partire dalle intersezioni. Ovviamente ci sono dei casi in cui le linee non devono essere decomposte: se una linea verticale interseca una linea orizzontale in uno dei suoi due vertici solamente la linea verticale verrà decomposta. Nell'immagine 23
  24. 24. 18 la linea “l2” (generata dalla proiezione dei vertici) interseca una linea di bordo “l1” nel vertice “nV”. Solo la linea “l1” in questa passata verrà decomposta in due segmenti con un vertice in comune cioè “nV”. La linea “l2” verrà comunque decomposta in una passata successiva e con lei anche la linea “l3”. Si vengono pertanto a formare non solo nuovi segmenti ma anche nuovi vertici sia internamente alle zone “vuote” sia sui bordi. Illustrazione 19: Vecchi e nuovi vertici 4.1.1.5. Generazione dei poligoni É necessario scoprire i segmenti che fanno parte dei vari poligoni e generare una nuova struttura che contenga solo i vertici dei poligoni per poi poter calcolare i baricentri. Per semplicità, si è scelto di utilizzare un metodo per nulla ottimizzato: a partire da un vertice, scorre le linee e trova quelle che hanno il vertice stesso come estremo, poi “elimina” quelle che hanno direzione verso ”l'alto” e verso ”sinistra” e sulle linee restanti verifica a coppie se ci sono linee che uniscono i restanti estremi (nel caso del triangolo) o due linee che partendo dagli estremi precedenti si incontrano nello stesso punto. Come contenitore è stata costruita una classe “Poligono” in grado peraltro di calcolare il baricentro dagli estremi che sono stati memorizzati. Poiché le “figure” che si ricavano sono solamente rettangoli e triangoli la classe necessita di saper determinare solamente il baricentro di triangoli e rettangoli. 24
  25. 25. Illustrazione 20: Poligoni e loro baricentri 4.1.1.6. Costruzione del grafo A partire dai poligoni si può creare il grafo che sarà formato da nodi e da archi congiungenti i vari nodi. Ogni arco deve essere pesato cioè bisogna assegnargli un valore che indichi in questo caso la distanza tra i due nodi che altro non è che la distanza tra i due baricentri dei poligoni associati ai due nodi (purchè i due poligoni siano adiacenti). Due poligoni vengono considerati adiacenti se hanno almeno un estremo in comune. Il grafo è formato da oggetti di tipo “Nodo” il cui contenuto è il baricentro del poligono oltre che due vettori, uno contenente i collegamenti tra nodi (riferimenti a oggetti di tipo Nodi) e il secondo che contiene invece il peso associato a ogni collegamento. Il metodo “creaGrafo” non fa altro che chiamare per ogni poligono il metodo “creaCollegamento” il cui compito è la verifica che il Nodo associato al poligono sia già stato inserito nel vettore dei nodi, altrimenti lo inserisce, verifica che siano stati inseriti per il nodo tutti i collegamenti con gli altri nodi e associa a ogni collegamento il peso corrispondente. 4.1.1.7. Calcolo del percorso minimo tra due punti tramite l'algoritmo di Dijkstra L'algoritmo di Dijkstra permette di calcolare il percorso minimo che unisce due nodi di un grafo. Ad ogni nodo del grafo viene associato inizialmente un peso infinito tranne al nodo di partenza che riceve un peso pari a zero. Si aggiorna (se necessario) il peso dei nodi “adiacenti” al nodo di partenza con il peso dell'arco che unisce i due nodi, questo ovviamente se il nuovo peso è minore del precedente. Si rende “definitivo” come prossimo nodo il nodo che ha peso minimo. (ovviamente scartando i nodi già resi definitivi). Si aggiornano i pesi dei nodi adiacenti al nuovo attuale (il nuovo peso sarà dato dalla somma del peso precedente con il peso del collegamento unente i due nodi) scartando però i nodi che sono stati già resi definitivi. Alla fine del processo si ottiene non solo il peso minimo per il percorso tra i due nodi (partenza e destinazione) ma anche tutti i pesi minimi tra il nodo di partenza e tutti gli altri nodi. A ogni passata memorizzo lo stato del vettore dei 25
  26. 26. pesi in modo tale da poter recuperare il percorso. Si analizzano i vettori dei pesi (dall'ultimo fino al primo) considerando prima la posizione occupata dal nodo di arrivo e andando a ritroso nei vettori fino a trovare il nodo che ha causato la modifica del peso del nodo di arrivo. Si considera adesso il nodo che ha causato la modifica del peso per la successiva analisi fintanto che non si arriva al primo vettore. La lista dei nodi ottenuta rappresenta (invertita) il percorso che ha peso minimo. Illustrazione 21: Percorso "minimo" tra due punti Come si può facilmente notare i segmenti che definiscono il percorso sovrastano in alcuni casi degli ostacoli oppure sono a distanza troppo ravvicinata da essi. Questi problemi però vengono risolti dall'algoritmo di navigazione che è stato costruito in modo tale che il robot mobile si mantenga sufficientemente distante dai muri e sia in grado di evitare gli ostacoli. 4.1.2. Navigazione del robot Essenzialmente si può scomporre il problema della ”guida” del robot in tre operazioni: 1) Lettura dei sensori 2) Presa di decisione 3) Esecuzione della decisione L'algoritmo per la percorrenza di una singola tratta è formato essenzialmente da un ciclo ”infinito” al cui interno sono presenti vari ”componenti”: 1) Lettura dei sensori 2) Verifica dell'arrivo 3) Rotazione verso l'obbiettivo (se nella vicinanza non ci sono ostacoli) oppure allontanamento dall'ostacolo (se il robot è troppo vicino a un ostacolo) 26
  27. 27. 4.2. Localizzazione mediante riferimenti visivi Si necessita di una telecamera o più semplicemente di una webcam per la visualizzazione dell'ambiente. Grazie all'utilizzo di landmark piani di forma conosciuta (nel nostro caso circonferenze “colorate”) posti sul soffitto in posizioni note è possibile in base alla deformazione con la quale la webcam vede il marker determinare prima lo sforzo necessario per deformare il marker originale per ottenere quello visualizzato e poi grazie al primo passaggio la distanza della webcam dal marker. Con un unico marker sapendo l'altezza del soffitto rispetto alla webcam sarebbe possibile determinare solo una circonferenza lungo la quale è posizionato il robot che sostiene la webcam. Si potrebbe pensare allora di usa due marker ma anche in questo caso non si riuscirebbe a determinare la posizione poiché dalla triangolazione si otterrebbero due punti. Se i marker però sono diversi fra loro (nel nostro caso sono di colore diverso) e si conosce quale dei due sta “prima” dell'altro posso determinare su quale dei due punti è posizionata la webcam. I landmark utilizzati per gli esperimenti sono formati da due circonferenze concentriche di raggi diversi la cui corona esterna è colorata di nero e la superficie racchiusa dalla circonferenza più piccola è stata colorata o di rosso o di blu. Illustrazione 22: Landmark Blu Inoltre si è supposto (per come è costruito l'algoritmo messo a punto nella tesi precedente) che l'asse delle ascisse fosse parallela con il segmento unente i centri dei due landmark (rosso e blu) e che la crescenza dei valori andasse dal landmark rosso a quello blu. È necessario conoscere la posizione assoluta dei landmark posti sul soffitto e poiché per semplicità si è fatto coincidere la direzione dell'asse delle ascisse con la congiungente i due centri, l'ordinata è fissata a zero per entrambi pertanto ciò che varia è solamente l'ascissa. Non è necessario che i landmark siano di dimensione prefissata, ma è fondamentale effettuare il salvataggio di un pattern cioè un'immagine di uno dei due landmark posizionando la webcam a piombo dal centro del ladmark a distanza dal soffitto uguale a quella che si userà per effettuare le misurazioni e rivolta esattamente verso l'alto. Il risultato è un immagine che contiene il landmark esattamente al centro dell'immagine. Quale sia il landmark scelto come pattern non ha alcuna rilevanza poiché gli algoritmi di “confronto” trasformano il pattern e i landmark rilevati in scala di grigi una volta che ne è stato identificato il tipo. I dati fondamentali che sono necessari all'algoritmo per determinare la posizione sono: • Posizione dei landmark: La posizione dei landmark deve essere memorizzata all'interno di un file di testo chiamato “landmark.txt”. A livello teorico l'algoritmo era stato studiato per essere in grado di gestire diverse coppie di landmark diverse fra loro per il numero di cerchi neri presenti nella vicinanza della coppia. Si è deciso di permettere l'uso di un'unica coppia perciò della lista presente nel file gli unici valori “interessanti” sono quelli della prima 27
  28. 28. coppia. Le informazione contenute sono in ordine: Ordinata del centro del landmark Rosso, Ascissa del centro del landmark Rosso (0 nel nostro caso), Ordinata del centro del landmark Blu, Ascissa del centro del landmark Blu (0 nel nostro caso). • Caratteristiche della webcam: Devono essere memorizzate all'interno di un file di configurazione di nome “parametri.txt”. I dati necessari memorizzati sono: ◦ φ: angolo “verticale” tra l'asse di rotazione verticale della webcam e il centro della visione (nel caso dei test 65°). ◦ θy: angolo di visione verticale (nel caso dei test 39°). ◦ h: distanza dal fuoco della webcam al soffitto (nel caso dei test 2,345 [m]). ◦ θx: angolo di visione orizzontale (nel caso dei test 50°). ◦ RisoluzioneX: Risoluzione orizzontale della webcam (nel caso dei test 640 [px]). Illustrazione 23: Angoli “fondamentali”  della Webcam • Pattern: Immagine di nome “pattern.JPG” realizzata con angolo φ=90° con la webcam posizionata all'altezza h in coincidenza con la proiezione sul pavimento del centro di uno dei due landmark (non ha alcuna importanza il colore). Illustrazione  24: Pattern Dal progetto precedente abbiamo “ereditato” oltre che la tesi (di fondamentale importanza per capire a grandi linee l'idea di funzionamento) un file C++ con i metodi necessari per il detect dei landmark (che poi non sono stati utilizzati) e i metodi per il calcolo delle distanze e della triangolazione (che hanno subito importanti variazioni) e un file Java per il richiamo dei metodi della classe in C++. Sono stati forniti inoltre anche tutti i file di configurazione, il pattern e le 28
  29. 29. immagini su cui erano stati fatti in precedenza i test. Il tutto era stato realizzato per il funzionamento in ambiente Windows pertanto la libreria compilata è una “dll”. Il fatto che il “main” sia realizzato in Java e utilizzi librerie native non dovrebbe stupire più di tanto anche se introduce una certa complicazione e rende il Java che di per se sarebbe un linguaggio multi-piattaforma dipendente dalla presenza delle librerie native nel sistema in uso. Come è noto il Java è un linguaggio multi- piattaforma nel senso che il codice viene “compilato” in un linguaggio intermedio detto bytecode che viene poi interpretato dalla JVM (Java Virtual Machine). La JVM è invece dipendente dal sistema operativo: se esiste la JVM per un certo sistema operativo di norma posso eseguire lo stesso codice compilato su macchine diverse e con SO diverso allo stesso modo purché sia installata la JVM su quel sistema. Questo ovviamente ha delle implicazioni piuttosto importanti come la riduzione dei tempi di sviluppi su sistemi diversi. L'uso della libreria nativa da programma Java può essere fatto in modo differente: nel caso della tesi precedente è stato utilizzato SWIG [17], nella implementazione realizzata invece è stata utilizzata la JNI [18] (Java Native Interface). Questo ha reso necessario la modifica della libreria in C++ anche perché ulteriori complicazioni sono state introdotte dall'inserimento del codice Java di “richiamo” all'interno di un Package. Si rimanda all'appendice per i due “How to” per l'installazione delle librerie OpenCV su Ubuntu 9.10 Linux e la compilazione e l'uso delle librerie native con Java sempre sullo stesso sistema. Una delle parti che ha richiesto più tempo è stato l'analisi e lo studio della geometria del problema. Per quanto le formule trigonometriche ricavate siano piuttosto elementari hanno richiesto comunque una certa attenzione soprattutto in considerazione dei diversi sistemi di riferimento che sono stati considerati. Una volta installate correttamente le OpenCV il passo successivo è stato la costruzione del package contenente tutte le classi adibite all'operazione di detect dei landmark e al richiamo dei metodi nativi. All'interno del package sono state inserite varie classi alcune delle quali sono adibite a puri contenitori di informazioni. É possibile scomporre il procedimento in vari sotto-passi: detect dei landmark, caricamento nelle strutture dati della libreria nativa, richiamo degli algoritmi di calcolo e prima verifica “algoritmica” dei risultati. 29
  30. 30. Illustrazione 25: Rappresentazione grafica delle grandezze definite dal problema
  31. 31. 4.2.1. Analisi geometrica del problema del posizionamento Tutte le relazioni matematiche adibite al calcolo della distanza dai landmark, la triangolazione e il calcolo dell'angolo del robot sono state ricavate e adeguatamente modificate. La retta passante per il segmento HP y rappresenta il soffitto della stanza di test “posto” all'altezza h dalla webcam posizionata nel punto O. La webcam è rivolta verso l'alto con un certo angolo φ (rispetto al centro della visione). θx e θy rappresentano gli angoli di apertura orizzontale e verticale rispettivamente della webcam. L'angolo γy invece è l'angolo di un generico punto sul soffitto rispetto al pavimento. Il piano posto in MQ e parallelo all'asse x uscente dal punto O è il piano dell'immagine. Vengono fatte le seguenti definizioni: a :=OA=OC −AC b := MA= AQ=MC sin  2  g :=GD=DI =a tan  x  2 y   := − 2 2 ma OC = h e AC =MC sin   −=MC cos  , inoltre MC =HC −HM con sin  2  sin −  2 h e HM =h tan − . HC =tan −=h = 2  tg  cos − 2 Si ottiene pertanto che h 1 b 1 a= −h tan  cos  e =h tan sin  . sin tan  2 tan  Dalle definizioni precedenti si può dedurre che b y I = a tan − y  2 ( tan  y = h ma HP y =MP y  HM = y−h tg  ) h HP y  y =arctan   y −h tan  RP y tan − y = , RP y =WP Y cos  , WP Y =TPY −TW , TP Y = y tan , OR TW =h−HM tan =hh tan  tan=h tantan 1 WP Y = y tan −h tan  tan1 RPY = y tan −h tan  tan1cos   inoltre OR =OK sin − =OK cos  , OK =HM  MP y DK , HM =−h tan  2 MP y= y DK =h tan  OK =h tan  y−h tan  OR = h tan  y−h tan  cos   y tan −h tan  tan1cos  y tan −htan  tan 1 tan − y = = h tan  y −h tan cos  h tan  y−h tan  Definendo G :=−h tan tan 1 e F :=h tan −tan si ottiene che y tanG I b y tanG tan − y = . Si ricava che y = a y F 2 y F Per la “x” vale invece: I I I I HP x OD z I x =GDDP x =g DP x h :OD z=HP x : DP x DP Ix = OD z =OP y sin  y  h 31
  32. 32. I I a a OP y cos − y =a ⇒OP y = OD z = sin  y  cos− y  cos − y  hg I a sin  y  FH : h=GD :OD z ⇒ FH = DP = HP x HP x = x−FH a x h cos− y  sin  y  cos − y  hg HP x = x− a sin  y  cos − y  a sin  y  hg asin  y  DP Ix =  x− = x− g h cos− y  a h cos− y  sin  y  cos − y  a sin  y  xI= x h cos − y  Quello che si cerca è d =  HP x 2HP y 2  che rappresenta la distanza sul piano del soffitto tra O b tan  yG y I = a 2 y F proiettato e il punto  HP x , HP y  baricentro del landmark. A partire da a sin  y  xI= x h cos − y  sostituendo y= y r  y c e x= x r x c dove  x c , y x  è la coordinata del baricentro del landmark e x r e y r sono le posizioni dei pixel nell'immagine pattern si ottiene che a sin  y  b tan  y c  y r G xI=  x c x r  e y I = a . Mettendo in risalto  x c , y x  si h cos − y  2  y c  y r F ha I b a Ga tan y r− y −  y r F  2 (4.2.1-1) yc= I b y − −a tg 2 h cos − y  x c= x I − xr (4.2.1-2) a sin  y  In pratica le due formule precedenti legano il baricentro del landmark con le coordinate dei pixel nell'immagine pattern e il pixel corrispondente nell'immagine di test. Applicando le relazioni per i punti del landmark si ottengono varie stime sulla posizione del baricentro. Il valore del baricentro considerato è la media di quelli calcolati. Questa operazione deve essere ripetuta per entrambi i landmark ottenendo pertanto due distanze. Conoscendo la loro posizione nell'ambiente si può triangolare la posizione del robot. Il posizionamento del robot però non è dato solo dalle sue coordinate rispetto al sistema assoluto ma anche dal suo angolo di rotazione che può essere descritto come l'angolo compreso tra l'asse x del sistema di riferimento assoluto e l'asse x del sistema di riferimento del robot. 32
  33. 33. Illustrazione 26: Triangolazione e angolo del robot Nel grafico vengono rappresentati tre sistemi di riferimento: il sistema (x,y) che rappresenta quello assoluto (per semplicità si è fatto “coincidere” l'asse delle ordinate con la congiungente i centri dei landmark), il sistema (xI,yI) che è il sistema di riferimento del robot e infine il sistema di riferimento (xII,yII) che rappresenta la traslazione di (xI,yI) sul landmark rosso. Grazie alle operazioni precedenti siamo a conoscenza delle coordinate dei due landmark rispetto al sistema di riferimento del robot (xI,yI). Pertanto oltre che i segmenti “dr” e “db” e la distanza tra i due landmark si viene a conoscenza degli angoli interni al triangolo (grazie a Carnot). db2 RB 2−dr 2  R=arccos   2 RB db dr 2 RB2−db2  B=arccos   2 RB dr db 2dr 2− RB2 =arccos  2 db dr La determinazione delle coordinate del robot presuppone due casi: se i landmark sono paralleli a uno degli assi del sistema di riferimento del robot come si è visto dai risultati sperimentali (che verranno discussi in seguito) molto spesso si verificano dei problemi nella determinazione della posizione, altrimenti si profilano altre due casistiche: il landmark rosso può essere a sinistra del blu o viceversa. Nel primo caso si ottiene che x= R.X cos B  dr y=R.Y −sin B  dr mentre nel secondo: x= B.X −cos R  db y=B.Y sin R  db dove (B.X, B.Y) e (R.X, R.Y) sono le coordinate assolute del ladmark blu e rosso rispettivamente. 33
  34. 34. Resta ancora da definire l'angolo con cui è ruotato il robot. Se si considera il sistema di riferimento (xII,yII) è possibile individuare 16 possibili configurazioni: 4 per la posizione del landmark rosso rispetto al sistema di riferimento (xI,yI) e per ognuna di queste 4 configurazioni per la posizione del landmark blu rispetto al sistema di riferimento (xII,yII). In realtà è possibile riassumere questi 16 casi nel seguente modo. Si definiscono x II :=x I − x I B B R II I I y B := y B − y R Dalle quali si ricavano le seguenti relazioni: II yB =arctan − II  nel caso che il landmark blu si trovi nel I° o IV° quadrante rispetto al sistema di xB II II riferimento (x ,y ) oppure II II y yB =signarctan − B  −arctan − II  nel caso che il landmark blu si trovi nel II° o III° II xB xB quadrante rispetto al sistema di riferimento (xII,yII). Illustrazione 27: θ: R in I° quad. in (xI,yI) Illustrazione 28: θ: R in I° quad. in (xI,yI) e B in I° quad. in (xII,yII) e B in III° quad. in (xII,yII) Illustrazione 29: θ: R in IV° quad. in   Illustrazione 30: θ: R in III° quad. in (xI,yI) (xI,yI) e B in IV° quad. in (xII,yII) e B in IV° quad. in (xII,yII) 34
  35. 35. Illustrazione 31: θ: R in III° quad. in (xI,yI) Illustrazione 32: θ: R in II° quad. in (xI,yI) e B in II° quad. in (xII,yII) e B in II° quad. in (xII,yII) 4.2.2. Detect dei landmark L'operazione di detect si articola in un numero piuttosto limitato di passaggi e questo è dovuto alla bontà delle librerie OpenCV che permettono di sfruttare algoritmi molto complicati in modo facile e molto intuitivo. Il porting utilizzato permette di sfruttare solo alcune delle potenzialità dell'ambiente ma che sono risultate sufficienti allo scopo che ci si era preposti. Si possono schematizzare le seguenti operazioni: acquisizione dell'immagine, ricerca dei landmark e verifica dei ritrovamenti e salvataggio dei landmark. 4.2.2.1. Acquisizione dell'immagine Per prima cosa è necessario istanziare un oggetto di tipo OpenCV: OpenCV ocv = new OpenCV(); Il passo successivo è invece l'acquisizione dell'immagine dalla webcam. Questa operazione necessita prima di tutto della comunicazione all'oggetto “ocv” della classe OpenCV della risoluzione con la quale si vuole acquisire. È possibile anche specificare al metodo anche l'indice della camera che si vuole usare per la cattura: nel nostro caso però abbiamo considerato che la webcam da utilizzare fosse quella in posizione “0”. Nel caso si cercasse di acquisire con risoluzioni superiori a quelle supportate dalla webcam si otterrebbero delle immagini con la risoluzione richiesta ma le aree sovrabbondanti verranno acquisite come zone nere. Una volta inizializzata si può richiedere la cattura vera e propria dell'immagine che verrà memorizzata in un buffer interno delle OpenCV. ocv.capture(risX, risY); ocv.read(); Per tener traccia dei risultati fin qui conseguiti l'immagine viene salvata su un file con estensione “png” in una directory che può essere definita dall'utente (le istruzioni sono illustrate nella sezione che riguarda l'interfaccia). Viene istanziato un oggetto della classe “BufferedImage” da cui viene “estratto” un riferimento a un oggetto di tipo “Graphics” che ci permette di scrivere pixel per pixel il colore associato al punto. A questo punto è possibile salvare su file l'immagine vera e propria tramite la classe “ImageIO”. 35
  36. 36. Illustrazione 33: Immagine acquisita dalla webcam 4.2.2.2. Ricerca dei landmark e verifica dei ritrovamenti Per scoprire la posizione dei landmark all'interno dell'immagine per prima cosa si trasforma l'immagine in scala di grigi: ocv.convert(OpenCV.GRAY); Illustrazione 34: Immagine acquisita "convertita" in  scala di grigi Poi si impone all'immagine in scala di grigi una soglia “binaria”: se il colore in ogni punto supera una certa soglia il colore del punto viene settato a un valore passato per parametro altrimenti viene settato al valore 0. La soglia dipende dalla luminosità ambientale. Si è dunque cercato di mantenere più costante possibile la luminosità dell'ambiente in modo da poter mantenere sempre lo stesso livello di soglia e rendere più “ripetibili” i test. ocv.threshold(80, 255, OpenCV.THRESH_BINARY); 36
  37. 37. //80 valore di soglia, 255 valore da imporre al pixel se il //colore del pixel supera la soglia, OpenCV.THRESH_BINARY //tipo di thresholding Illustrazione 35: Treshold "binario" applicato  all'immagine in scala di grigi Una volta effettuata questa operazione si ha la speranza di aver “eliminato” tutti gli oggetti estranei ai landmark. L'ipotesi non è che il soffitto sia interamente “bianco”, ma che sul soffitto non ci siano oggetti o simboli di dimensione tale da poter essere confusi con i landmark. Come è possibile vedere dall'illustrazione 33 i test sono stati effettuati su un soffitto “piastrellato” e puntinato. Si può notare anche la presenza di due fonti di illuminazione al neon che date le notevoli dimensioni non possono essere fraintese con i landmark posti al centro del soffitto. Ora si vogliono determinare i contorni degli oggetti rilevati e le loro posizioni. Si utilizza pertanto il metodo “blobs” dell'oggetto “ocv” che riceve in input vari parametri: area minima (in pixel) accettata per l'oggetto, massima area (in pixel) accettata per l'oggetto, massimo numero di oggetti rilevabili, valore booleano che consente o meno il “ritrovamento” di oggetti interamente contenuti in altri. Blob[] blobs = ocv.blobs(minX*minY, maxX * maxY, 100, true, OpenCV.MAX_VERTICES); Per motivi ignoti si è stati costretti a permettere il ritrovamento di oggetti inscritti: in caso contrario il metodo si rifiutava di recuperare correttamente i marker nonostante sulle immagini non rilevasse altre “figure” oltre a loro. 37
  38. 38. Illustrazione 36: Algoritmo "blobs" per il  riconoscimento dei contorni Il metodo, come è facile notare, restituisce un vettore di “Blob”. Ogni oggetto “Blob” contiene al suo interno un vettore di oggetti di tipo “Point” che rappresentano appunto i punti che formano il contorno. Quello che si vuole ottenere è un rettangolo entro il quale è contenuto il landmark. Pertanto per ogni oggetto “Blob” del vettore vengono analizzati i suoi punti e memorizzate le coordinate dei punti che sono più “esterne” rispetto al contorno. Si ottengono pertanto le coordinate di due punti che rappresentano gli estremi di un rettangolo che contiene il presunto landmark. Il fatto che l'algoritmo di “blobs” elimini le figure che hanno area (in pixel) minori del parametro non mi assicura comunque che le dimensioni del landmark siano “razionali”. Per esempio potrei ottenere un rettangolo di dimensione 90x2 pixel cioè 180 pixel avendo però fissato che le dimensioni X e Y non debbano essere superiori a 50 pixel per lato. Poiché non si è riusciti a testare la condizione di figure inscritte per sicurezza si è costruito un algoritmo in grado di verificare questa eventualità e nel caso eliminare la figura più interna. A questo punto è necessario estrarre un quadrato avente come centro il centro del rettangolo e come lato una dimensione fissata per ogni presunto landmark (la dimensione è fissata all'interno della libreria: è possibile modificarla ma poi è necessario ricompilarla). Si rende necessaria inoltre la memorizzazione dell'angolo superiore sinistro di ogni landmark trovato: la motivazione diverrà chiara in seguito. Quello che bisogna verificare adesso è il colore del landmark. Siamo a conoscenza che ogni landmark sarà più o meno centrato all'interno di ogni quadrato estratto. Considerare solo il colore del punto centrale non è però una buona politica: ecco perché viene analizzato un “intorno” del punto: per la precisione viene considerato un quadratino di dimensione 3x3 pixel avente centro il pixel centrale del quadrato e viene verificata la componente RGB predominate nell'area. 4.2.2.3. Salvataggio dei landmark É il momento di salvare su file immagine (“png”) i due landmark per una successiva elaborazione. Il metodo è praticamente equivalente a come è stato descritto nel caso del salvataggio dell'immagine acquisita. 38
  39. 39. Illustrazione 37:   Landmark Blu e Rosso 4.2.3. Caricamento strutture dati della libreria nativa Poiché non è stato utilizzato l'algoritmo di “detect” della libreria nativa è necessario precaricare manualmente i dati dei landmark nelle apposite strutture prima di chiamare i metodi di calcolo. Se la procedura non ha dato buon esito e questo può essere dovuto alla mancanza dei landmark nel campo visivo della webcam oppure a condizioni sfavorevoli di illuminazione dell'ambiente non è possibile calcolare la propria posizione ma si dovrà contare solo sui dati forniti dall'odometria. Vengono effettuati pertanto dei controlli sul rilevamento dei landmark. La struttura che la libreria nativa utilizza per la memorizzazione delle informazioni che ci interessano è così realizzata: typedef struct { IplImage* img; int Xcorner; int Ycorner; double x; double y; double distanza; double angolo; long int costo; char colore; } U; U array[10]; // Struttura dati per la gestione delle immagini Ecco una rapida spiegazione delle variabili contenute all'interno della struttura dati: • IplImage* img: Un puntatore a una struttura di tipo IplImage. Questa struttura oltre a contenere l'immagine vera e propria contiene delle informazioni fondamentali sull'immagine: larghezza, altezza, profondità, dimensione, origine e altri dati. • Xcorner: posizione X dell'angolo superiore sinistro del landmark rispetto all'immagine acquisita da cui è stato estratto. • Ycorner: posizione Y dell'angolo superiore sinistro del landmark rispetto all'immagine acquisita da cui è stato estratto. • X, y , distanza, angolo: questi valori verranno riempiti in un momento successivo dagli algoritmi di calcolo. • Costo: anche questo campo verrà riempito successivamente e rappresenta il “costo” necessario per trasformare il pattern nel landmark ricavato dall'immagine acquisita. • Colore: è un carattere che identifica il colore del landmark e può essere 'r', 'b' o 'v'. Gli algoritmi di calcolo richiedono che l'array venga riempito in maniera ben determinata: nella posizione 0 deve essere inserita l'immagine così come è stata acquisita dalla webcam, nella posizione 1 invece deve venir inserito il landmark rosso e nella posizione 2 il landmark blu corredati dai dati necessari. L'idea iniziale era di passare la struttura IplImage direttamente dal programma Java ma poiché era decisamente più semplice far caricare direttamente alla libreria nativa il file immagine si è optato per questa soluzione. È stato necessario realizzare alcuni metodi 39
  40. 40. per richiedere da Java il caricamento dell'immagine (o del landmark a seconda della posizione nel vettore) con i dati correlati (se necessari). Effettuare il caricamento dell'immagine da C++ è piuttosto simile al corrispondente metodo in Java: IplImage * img = 0; img = cvLoadImage(fileName); È importante però fare una precisazione: ogni qualvolta si passa dei parametri alla libreria nativa dal programma Java in verità non si sta passando l'equivalente del tipo che si vorrebbe passare in Java al C++. Se per esempio si decidesse di passare un intero, la libreria nativa riceverebbe un jint, jboolean per un bool, jstring per una stringa e così via. Queste primitive sono definite all'interno della libreria “jni.h” e fintanto si trattano jint o jboolean non si necessita di alcun tipo di conversione mentre il discorso cambia radicalmente nel caso di stringhe poiché i metodi delle librerie OpenCV si aspettano di ricevere un vettore di caratteri (char). La necessità di convertire pertanto i due formati diventa di basilare importanza. Ci vengono in aiuto dei metodi della classe JNIEnv. Ogni qualvolta definisco un metodo nativo che deve essere richiamato da Java, nel sorgente C++ avrà due parametri in più rispetto a quanto è stato definito in Java. Questi parametri sono JNIEnv* jenv, jobject job. Il primo parametro è un puntatore a un oggetto JNIEnv che fornisce una serie di funzioni che possono essere invocate nel codice nativo per accedere all'ambiente Java. Tra le funzioni ci sono quelle adibite alla conversione di stringhe. Il secondo parametro è il riferimento all'oggetto chiamante. const char *fileName = jenv->GetStringUTFChars(fileNameJ,0); La stringa però terminato l'utilizzo deve anche essere rilasciata: jenv->ReleaseStringUTFChars(fileNameJ, fileName); All'interno della libreria si fa spesso riferimento a dei file di configurazione (nello specifico “pattern.JPG”, “landmark.txt” e “parametri.txt”) che per come è stato scritto il “progetto” originale necessitano di trovarsi nella stessa direcory da cui viene lanciato il programma Java. Nel nostro caso però visto che ci si trova a lavorare con i package e per rendere il tutto più flessibile si è deciso di eliminare questa limitazione. Prima di poter richiamare un qualunque metodo della libreria nativa adibita a funzioni di calcolo sulle immagini è necessario passarle il percorso contenente i files di configurazione. setConfigurationPath("/percorso/file/configurazione/"); Ora è possibile richiamare dal programma Java i metodi adibiti al caricamento delle strutture dati: loadImg (path + fileName, 0, true); loadMarker (path + "RED.png", red.getxCorner(), red.getyCorner(), 'r', true); loadMarker (path + "BLUE.png", blue.getxCorner(), blue.getyCorner(), 'b', true); Questi tre metodi sono metodi nativi e sono ovviamente implementati nella libreria nativa. Il primo metodo richiede il caricamento nella posizione 0 dell'array per la gestione delle immagini l'immagine che è stata acquisita (percorso e nome file). L'ultimo parametro semplicemente specifica se visualizzare o meno messaggi per il debugging. Il secondo metodo invece specifica non solo 40
  41. 41. l'immagine da caricare ma anche la posizione dell'angolo superiore sinistro dell'immagine su quella acquisita e il colore del landmark. In base al colore il metodo posizionerà l'immagine e i rispettivi dati nella posizione corrispondente dell'array. 4.2.4. Richiamo degli algoritmi di calcolo A questo punto è possibile richiamare gli algoritmi di calcolo: int ok = algoritmo(1); Il metodo (sempre nativo) deve essere applicato ai due landmark in successione: il parametro specifica a quale landmark applicare le procedure di calcolo e rappresenta la sua posizione all'interno dell'array delle “immagini”. Il valore restituito è un valore intero: se 1 significa che l'operazione è andata a buon fine altrimenti restituisce 0. L'operazione può non andare a buon fine nel caso che l'immagine non possa essere caricata. I risultati dell'elaborazione vengono inseriti nell'array nella posizione corrispondente al landmark che si sta processando nei campi che in precedenza non ci si è preoccupati a ragione di riempire. Si può ora richiamare il metodo di triangolazione (sempre nativo): ok = triangola(0.0, 1); Il metodo necessita di due parametri. Il primo è un coefficiente di compensazione dell'errore sistematico sulla distanza ai landmark, infatti se si rileva che l'errore è percentuale nei confronti della distanza è possibile correggere il valore e teoricamente rendere più precisa la misura. Se per esempio in tutte le misurazioni riesco a determinare che l'errore sulla distanza è all'incirca del 7% in difetto posso inserire un coefficiente di errore di 0,07. Il valore può variare nell'intervallo [-1, 1]. Il secondo parametro è il valore che sarebbe stato ritornato dalla procedure di “detect” originale se si fosse deciso di utilizzarla. Il suo valore rappresenta un identificatore alla coppia di landmark acquisita. Abbiamo già fatto notare come a livello teorico nell'algoritmo originale si potessero utilizzare più coppie di landmark. Usando più coppie era però necessario conoscere le posizioni dei vari landmark rispetto allo stesso sistema di riferimento. Le posizioni sono contenute all'interno del file di configurazione “landmark.txt” ma poiché nel nostro caso il landmark è unico il problema del riconoscimento della coppia non sussiste, pertanto in qualunque caso si usano le informazioni della prima coppia. Il metodo restituisce un valore intero: 1 se i landmark necessari all'elaborazione sono stati individuati, 0 altrimenti. 4.2.5. Prima verifica “algoritmica” dei risultati Se l'operazione precedente è andata a buon fine posso ricevere i valori calcolati tramite altri tre metodi nativi: double posX = pos_X(); double posY = pos_Y(); double theta = pos_Theta(); Sfortunatamente ci sono delle situazioni (che verranno messe in luce successivamente) per la quale gli algoritmi restituiscono dei valori non validi. Pertanto prima di aggiornare la posizione si verifica che i valori ricevuti non siano di tipo “Nan” (Not a Number). 41
  42. 42. if (Double.isInfinite(posX) || Double.isInfinite(posY) || Double.isInfinite(theta) || Double.isNaN(posX) || Double.isNaN(posY) || Double.isNaN(theta)) { return new Results(false, 0, 0, 0, "Un valore è infinito"); } else return new Results(true, posX, posY, theta, "Dovrebbe essere ok"); “Results” non è altro che un classe che viene utilizzata come contenitore per i risultati. 4.3. Integrazione Robot mobile e autolocalizzazione Le due componenti sono state aggregate in modo tale che fosse possibile sfruttarle in maniera autonoma: la navigazione può funzionare anche senza la localizzazione, la localizzazione può essere usata senza per forza utilizzare il robot mobile. Questo ovviamente ha avuto delle notevoli ripercussioni sul tempo necessario allo svolgimento dei test per la localizzazione: posizionato il robot nel punto voluto e con l'angolo appropriato (era necessario che la webcam visualizzasse i landmark) bastava semplicemente mandare in esecuzione il “Main” della localizzazione per ottenere in una cartella a scelta l'immagine acquisita, le immagini dei landamark estratti e un file di testo con i risultati. 4.3.1. Interfaccia del software Nonostante fosse possibile realizzare un'applicazione a console era un po' uno spreco oltre che scomodo non utilizzare le capacità “grafiche” di Java nel realizzare in modo semplice e veloce le interfacce grafiche. La realizzazione di interfacce in Java è un argomento molto complesso e anche molto vasto. Dato che non sono uno specialista in questo campo non voglio fare un trattazione su come si costruiscono le interfacce in Java ma solo come sono state graficamente realizzate senza aver la pretesa di aver costruito qualcosa di funzionale e rispettoso degli standard. Una precisazione piuttosto importante riguarda i percorsi delle directory che sono fondamentali per il corretto funzionamento del software. I percorsi devono essere necessariamente passati per parametro al programma nel caso che non si utilizzi la macchina su cui sono state eseguite le prove (il che è ovviamente “logico”). I parametri sono nell'ordine: • Percorso della directory che contiene la libreria nativa e i due file di configurazione necessari al funzionamento della libreria stessa e il pattern: “landmark.txt”, “parametri.txt”, “pattern.JPG”. • Percorso dove salvare le immagini temporanee acquisite ed elaborate dal software. Un esempio di “lancio” in esecuzione potrebbe essere: java -jar Robot.jar /library/path /img/temp/path Una volta mandato in esecuzione l'applicativo viene visualizzata una finestra in cui si richiede il nome e il percorso di un file di mappa “.world” da caricare (altro non è che un file di configurazione di mappa di Stage). 42
  43. 43. Illustrazione 38: Caricamento mappa Il caricamento della mappa e la sua elaborazione sono operazioni che possono impiegare molto tempo pertanto si è deciso di inserire una form con barra di scorrimento a “rimbalzo” dotata di una label dove viene visualizzato il nome dell'operazione corrente. Questa form non specifica il tempo rimanente per lo svolgimento delle operazioni ma almeno il programma non appare in uno stato di “stallo”. Illustrazione 39: Elaborazione della mappa Una volta terminate queste operazioni appare un'altra finestra in cui viene visualizzata la mappa dell'ambiente, la posizione del robot oltre ad altre informazioni che possono essere visualizzate o nascoste mediante apposite check-box. Se si vuole mettere in evidenza alcuni aspetti della mappa o dell'elaborazione si può modificare a piacimento il colore di alcune componenti oppure ripristinare il “tema” originale. Cliccando sulla mappa si posiziona il punto di arrivo del robot. Cliccando poi sul bottone “Calcola” il software calcola il percorso minimo tra la posizione attuale del robot e la posizione di arrivo. Durante questa operazione viene visualizzata un'altra form in cui viene evidenziata l'esecuzione dell'elaborazione corrente. Al termine sulla mappa compare il percorso che è stato calcolato. Basta premere il bottone “Run!” per far percorrere al robot il percorso. In qualunque momento è possibile stoppare il robot tramite un'altra pressione sullo stesso pulsante. Un'altra porzione dell'interfaccia è riservata all'aggiornamento della posizione “odometrica” tramite i riferimenti visivi. Alla pressione del pulsante “Modifica tempo per aggiornamento” viene 43
  44. 44. visualizzata una dialog che richiede l'inserimento del tempo minimo (in secondi) per la ricerca dei landmark e l'aggiornamento della posizione. Questo non significa che esattamente ogni tot secondi il robot cercherà di autolocalizzarsi ma che non tenterà il riposizionamento prima del numero di secondi impostati. Se il numero di secondi impostato è zero il robot non effettuerà mai il riposizionamento. Illustrazione 40: Interfaccia principale 44
  45. 45. 5. Risultati sperimentali Tutte le misure effettuate sono stare realizzate nella stessa stanza e con le stesse condizioni di luce e ovviamente con la stessa webcam. I parametri che sono stati inseriti nei file di configurazione sono rispettivamente: parametri.txt φ 65° θy 39° h 2,345 [m] θx 50° RisoluzioneX 640 [px] landmark.txt X_Rosso -0,305 [m] Y_Rosso 0,00 [m] X_Blu +0,305 [m] Y_Blu 0,00 [m] È stata fatta una particolare attenzione nel posizionamento dei landmark sul soffitto. Sul soffitto non erano presenti oggetti o figure che potessero essere facilmente confuse con i landmark posizionati però era fondamentale posizionare i landmark in modo tale da facilitare il più possibile le misure: nel nostro caso era fondamentale che la congiungente i centri dei due landmark fosse parallela all'ascissa del sistema di riferimento pertanto si è scelto di far in modo che le “piastrelle” sul soffitto avessero le loro due dimensioni parallele e ortogonali rispettivamente all'asse delle ascisse. Ne è risultato pertanto che i muri della stanza di test fossero ortogonali e paralleli agli assi di riferimento. Di conseguenza è stato possibile seguire anche le piastrelle sul pavimento. Per comodità è stato proiettato il sistema di riferimento dal soffitto al pavimento dove era possibile con comodità effettuare le misurazioni. La webcam è stata posizionata sul robot all'altezza (fuoco) di 0,405 [m] e all'incirca al centro del robot mobile. Poiché l'altezza del soffitto che è stata misurata era di 2,750 [m] ne è risultato che l'altezza della webcam dal soffitto fosse appunto di 2,345 [m]. La risoluzione della webcam ovviamente era nota mentre gli altri parametri sono stati “stimati” tenendo conto della distanza e dello spazio visualizzato. Supponendo infatti che la distanza dal muro dalla webcam sia “d” e la lunghezza orizzontale visualizzata sia “l” risulta evidente che l'angolo θx sia: l θ x =2 artanh   2d Lo stesso ragionamento si può applicare all'angolo verticale. 45

×