Marvin: progetto e sviluppo di un robot real-time guidato
by user
Comments
Transcript
Marvin: progetto e sviluppo di un robot real-time guidato
Università Politecnica delle Marche Facoltà di Ingegneria Dipartimento di Ingegneria dell’Informazione Corso di Laurea Triennale in Ingegneria Informatica e dell’Automazione Marvin: progetto e sviluppo di un robot real-time guidato da informazioni visive Tesi di: Francesco Di Benedetto Relatore: Prof. Aldo Franco Dragoni Correlatore: Dott. Andrea Claudi Anno Accademico 2011/2012 Università Politecnica delle Marche Facoltà di Ingegneria Dipartimento di Ingegneria dell’Informazione Corso di Laurea Triennale in Ingegneria Informatica e dell’Automazione Marvin: progetto e sviluppo di un robot real-time guidato da informazioni visive Tesi di: Francesco Di Benedetto Relatore: Prof. Aldo Franco Dragoni Correlatore: Dott. Andrea Claudi Anno Accademico 2011/2012 Dipartimento di Ingegneria dell’Informazione Università Politecnica delle Marche Facoltà di Ingegneria Via Brecce Bianche – 60131 Ancona (AN), Italy Al mio caro nonno Guido Ringraziamenti Il paragrafo dedicato ai ringraziamenti è sicuramente il più bello da scrivere ma anche il più complesso. Voglio ringraziare mio nonno Guido, a cui questa tesi è dedicata, che avrebbe dato di tutto per vedermi finalmente Dottore: a lui va il mio pensiero più grande e spero che da lassù possa comunque gioirne. Non posso però mettere in secondo piano tutta la mia famiglia che in questi anni mi ha sempre supportato e fatto coraggio, per me è stato fondamentale considerando soprattutto il mio carattere! Grazie Mamma e grazie Papà per la vostra vicinanza e comprensione sulle quali ho sempre potuto contare. Non posso nemmeno dimenticare tutti gli amici che durante questo percorso mi sono stati vicino e con cui ho condiviso i momenti più belli di questi anni universitari, tra gli altri uno speciale ringraziamento va a Francesco e Luca. Un sincero ringraziamento al Prof. Aldo Franco Dragoni per avermi dato la possibilità di lavorare a questo bellissimo progetto che mi ha coinvolto per interi mesi, risultando un’esperienza fantastica ed ottima per concludere questo percorso di studio di primo livello. Questa tesi mi ha permesso di sperimentare argomenti che hanno spaziato dall’Informatica all’Automazione, passando per l’Elettronica. Un doveroso ringraziamento va anche ai ragazzi del dipartimento del DII e principalmente al laboratorio di Intelligenza Artificiale e Sistemi in Tempo Reale, tra i quali il correlatore Andrea Claudi. Lui è stato una figura fondamentale per lo sviluppo di questo lavoro essendo sempre presente con il suo supporto e i suoi consigli. Ancona, Luglio 2012 Francesco Di Benedetto v Sommario Nel linguaggio comune, un robot è un’apparecchiatura artificiale che compie determinate azioni sia basandosi sulle istruzioni assegnate, sia autonomamente. In questo lavoro il robot realizzato prevede esclusivamente un funzionamento autonomo. Esso è concepito per fornire una base hardware semplice e affidabile, rendendo possibile sviluppare in maniera indipendente applicazioni di alto livello basate sul sistema operativo mobile Android, disponibile tipicamente su smartphone e tablet. Le modalità di utilizzo del robot denominato Marvin - dall’androide paranoico di Guida galattica per gli autostoppisti scritto da Douglas Adams - sono molteplici e vanno dall’aspetto puramente didattico e di ricerca in ambito universitario fino a scenari più complessi quali la videosorveglianza e l’assistenza personale. Risulta interessante notare come la progettazione di questo sistema embedded ponga su piani diametralmente opposti l’attuazione di un comando dall’applicazione, e quindi dal cervello, del robot seguendo la filosofia del Plug & Play; qualsiasi possessore di uno smartphone potrebbe sviluppare la sua applicazione utilizzando le primitive di movimento esposte dal robot in maniera separata dall’hardware essendo certo sia che il dispositivo venga supportato, sia che l’applicazione con possa attuare i suoi comandi attraverso un sistema hardware di una certa complessità - utilizzando un set di API. vi Indice 1. Introduzione 1.1. Stato dell’arte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Obiettivi della tesi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3. Struttura del lavoro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Componenti di Marvin 2.1. Flex Full . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Convertitore TTL-USB PL2303 . . . . . . . . . . . . . . . 2.3. FTDI Vinculum-II . . . . . . . . . . . . . . . . . . . . . . 2.4. Libreria Open Accessory . . . . . . . . . . . . . . . . . . . 2.5. Arduino Mega ADK . . . . . . . . . . . . . . . . . . . . . 2.6. Control Module basato su chip Motion Processor MC3410 2.7. Servomotori DFRobotics DF15SR e Hitec HS-485HB . . . 2.8. Smartphone Android . . . . . . . . . . . . . . . . . . . . . 2.9. Circuito di Alimentazione, led e breadboard . . . . . . . . 3. Ambienti di sviluppo 3.1. Eclipse Indigo con Android SDK . . . 3.2. RT-Druid e ERIKA Enterprise v1.6.1 . 3.3. Microchip MPLAB v8.43 . . . . . . . 3.4. Arduino IDE v1.0 . . . . . . . . . . . . 3.5. Vinculum-II IDE v1.4.4 . . . . . . . . 3.6. RealTerm v2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 3 . . . . . . . . . 4 4 9 9 11 11 14 14 17 18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 20 24 29 30 32 34 4. Analisi dei Requisiti e Progettazione del Sistema 4.1. Analisi dei Requisiti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. Requisiti Funzionali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1. Dotare il robot dei movimenti spaziali tramite API portabili . . . . 4.2.2. Garantire un canale di comunicazione con ogni dispositivo Android 4.2.3. Garantire una comunicazione affidabile con Arduino . . . . . . . . 4.2.4. Implementare algoritmi di visione su Android . . . . . . . . . . . . 4.2.5. Aggiungere il motore che permette uno snodo “di testa” . . . . . . 4.3. Requisiti Aggiuntivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1. Elaborazione tramite RTOS ERIKA . . . . . . . . . . . . . . . . . 4.3.2. Migliorare il programma di visione . . . . . . . . . . . . . . . . . . 4.3.3. Sostituire il Motion Processor . . . . . . . . . . . . . . . . . . . . . 4.3.4. Implementare un controllore PID . . . . . . . . . . . . . . . . . . . 4.3.5. Comunicazione wifi tramite Android . . . . . . . . . . . . . . . . . 4.4. Architettura e funzionamento del sistema . . . . . . . . . . . . . . . . . . 4.5. Protocolli di comunicazione . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5.1. Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5.2. Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 36 37 37 37 38 38 38 38 39 39 39 40 40 40 41 41 42 . . . . . . . . . . . . . . . . . . vii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Indice 4.5.3. Flex Full Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. Implementazione del Software 5.1. Librerie Flex Full . . . . . . . . . . . . . . . . . . . . 5.1.1. Libreria EEuart.h . . . . . . . . . . . . . . . 5.1.2. Libreria Arduino.h . . . . . . . . . . . . . . . 5.1.3. Libreria Utility.h . . . . . . . . . . . . . . . 5.1.4. Libreria serialConsole.h . . . . . . . . . . 5.1.5. Libreria Servo.h . . . . . . . . . . . . . . . . 5.1.6. Libreria PWM.h . . . . . . . . . . . . . . . . . 5.1.7. Libreria PID.h . . . . . . . . . . . . . . . . . 5.2. Componenti Hardware Flex Full . . . . . . . . . . . 5.2.1. Oscillatori . . . . . . . . . . . . . . . . . . . . 5.2.2. USART . . . . . . . . . . . . . . . . . . . . . 5.2.3. Modulazione a Larghezza di Impulso (PWM) 5.3. RTOS ERIKA Enterprise . . . . . . . . . . . . . . . 5.4. Implementazione dell’applicazione ERIKA . . . . . . 5.5. Firmware Arduino . . . . . . . . . . . . . . . . . . . 5.6. Applicazioni Android . . . . . . . . . . . . . . . . . . 5.6.1. Caratteristiche comuni tra le due applicazioni 5.6.2. Differenze tra le due applicazioni Android . . 5.7. OpenCV e Linear Binary Pattern (LBP) . . . . . . . 43 . . . . . . . . . . . . . . . . . . . 44 44 44 45 45 45 46 46 47 47 47 49 51 54 58 60 62 62 66 75 6. Il Sistema di Controllo 6.1. Il controllore PID a tempo continuo: generalità . . . . . . . . . . . . . . . . . . . . . . . . 6.2. Il controllore PID a tempo discreto: generalità . . . . . . . . . . . . . . . . . . . . . . . . 6.3. Taratura dei parametri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 77 80 81 7. Conclusioni e sviluppi futuri 7.1. Test di funzionamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2. Obiettivi raggiunti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3. Sviluppi futuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 83 84 85 A. Dettagli di cablaggio 87 B. Software utilizzato 92 viii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elenco delle figure 1.1. AndroEEbot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2.1. Flex Full e Flex Base . . . . . . . . . . . . . . . . . . . . . . 2.2. Pinout del dsPIC . . . . . . . . . . . . . . . . . . . . . . . . 2.3. Funzioni e connettori della scheda Flex Full . . . . . . . . . 2.4. Microchip ICD 2 . . . . . . . . . . . . . . . . . . . . . . . . 2.5. Pinout della scheda Flex Full . . . . . . . . . . . . . . . . . 2.6. Retro e pinout del PL2303 . . . . . . . . . . . . . . . . . . . 2.7. Fronte del PL2303 . . . . . . . . . . . . . . . . . . . . . . . 2.8. Vinculum-II . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.9. Modulo di debug V2DIP2 . . . . . . . . . . . . . . . . . . . 2.10. Arduino Mega ADK . . . . . . . . . . . . . . . . . . . . . . 2.11. Pinout ATMega2560 . . . . . . . . . . . . . . . . . . . . . . 2.12. Hardware per il controllo del movimento di AndroEEBot . . 2.13. Esempio di onda rettangolare generata con la PWM . . . . 2.14. Servomotore HS-485HB . . . . . . . . . . . . . . . . . . . . 2.15. Corrispondenza tra angoli e tempi di T-on in microsecondi . 2.16. DF15SR con “Horns” . . . . . . . . . . . . . . . . . . . . . . 2.17. HTC Evo 3D . . . . . . . . . . . . . . . . . . . . . . . . . . 2.18. Regolatore di tensione LM7805CV . . . . . . . . . . . . . . 2.19. Circuito regolatore di tensione positiva stabilizzata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 6 7 7 8 9 9 9 10 11 13 14 15 15 15 16 17 18 19 3.1. Architettura di Android . . . . . . . . . . 3.2. Ciclo di vita di una activity . . . . . . . . 3.3. Repository Android . . . . . . . . . . . . 3.4. Librerie Android . . . . . . . . . . . . . . 3.5. Proprietà del progetto e link delle librerie 3.6. Repository RT-Druid . . . . . . . . . . . . 3.7. Link librerie MPLAB . . . . . . . . . . . . 3.8. Build del progetto . . . . . . . . . . . . . 3.9. RT-Druid IDE . . . . . . . . . . . . . . . 3.10. Log della programmazione del dsPIC . . . 3.11. Setup ICD2 e MPLAB . . . . . . . . . . . 3.12. Selezione prototype board . . . . . . . . . 3.13. Vinculum IDE toolchain . . . . . . . . . . 3.14. Architettura Vinculum-II . . . . . . . . . 3.15. IDE del Vinculum-II . . . . . . . . . . . . 3.16. Ricezione dati dalla scheda Flex . . . . . . 3.17. Invio dati alla scheda Flex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 21 22 23 23 25 26 27 27 29 30 31 33 33 34 35 35 4.1. Assi di Marvin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. Flowchart per l’algoritmo di controllo del movimento . . . . . . . . . . . . . . . . . . . . . 38 39 . . . . . . . . . . . . . . . . . ix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elenco delle figure 4.3. L’architettura a 4 livelli di Marvin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 5.1. Determinazione di Fosc . . . . . . . . . . . . . . . . . . . . . . . 5.2. Architettura hardware di una periferica USART . . . . . . . . . 5.3. Il protocollo UART . . . . . . . . . . . . . . . . . . . . . . . . . 5.4. Generazione dell’onda PWM . . . . . . . . . . . . . . . . . . . 5.5. Modifica del valore di duty cycle “on-the-fly” . . . . . . . . . . 5.6. API del kernel Erika Enterprise . . . . . . . . . . . . . . . . . . 5.7. Algoritmo di individuazione di un volto . . . . . . . . . . . . . 5.8. Calcolo delle coordinate e degli errori nell’applicazione “Marvin 5.9. Architettura della libreria OpenCV . . . . . . . . . . . . . . . . 5.10. Funzionamento dell’algoritmo LBP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PID” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 50 51 52 53 57 66 72 75 76 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 78 79 79 80 82 7.1. Architettura finale di Marvin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2. Marvin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 86 A.1. A.2. A.3. A.4. A.5. A.6. A.7. A.8. A.9. 87 88 88 89 89 90 90 91 91 Controllore PID . . . . . . . . . . . . . . . . . . . . . . . . Azioni proporzionali a confronto per un ingresso a gradino Azioni integrali a confronto per un ingresso a gradino . . Azioni derivatrici a confronto per un ingresso a gradino . Schema a blocchi di un sistema a dati campionati . . . . . Ku e Tu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Connessione Flex-Arduino tramite USART1 . . . . . . . . . . . . . . . Connessione Flex-Convertitore tramite USART2 . . . . . . . . . . . . Connessione Flex-Servomotore di testa tramite PWM1 . . . . . . . . . Dettaglio del servomotore di testa . . . . . . . . . . . . . . . . . . . . Connessione Flex-servomotori di propulsione tramite PWM2 e PWM3 Dettaglio dei servomotori di propulsione alloggiati sotto la struttura . Connessione Flex e Arduino ai led presenti sulla breadboard . . . . . . Circuito di alimentazione e linee di tensione . . . . . . . . . . . . . . . Connessione Arduino-Smartphone tramite cavo USB . . . . . . . . . . x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elenco delle tabelle 2.1. Coppia di stallo per il servo DF15SR a diversi valori di tensione . . . . . . . . . . . . . . . 17 4.1. 4.2. 4.3. 4.4. Formato Formato Formato Formato A.1. A.2. A.3. A.4. A.5. A.6. A.7. Connessione Flex-Arduino tramite USART1 . . . . . . . . . . Connessione Flex-Convertitore tramite USART2 . . . . . . . Connessione Flex-Servomotore di testa tramite PWM1 . . . . Connessione Flex-servomotore di propulsione tramite PWM2 Connessione Flex-servomotore di propulsione tramite PWM3 Connessioni Flex-led . . . . . . . . . . . . . . . . . . . . . . . Connessioni Arduino-led . . . . . . . . . . . . . . . . . . . . . del del del del pacchetto pacchetto pacchetto pacchetto trasmesso trasmesso trasmesso trasmesso dall’applicazione Android “Marvin Grid View” . dall’applicazione Android “Marvin PID” . . . . . da Arduino nell’applicazione “Marvin Grid View” da Arduino nell’applicazione “Marvin PID” . . . xi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 42 43 43 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 87 88 89 89 90 90 Capitolo 1. Introduzione Nel linguaggio comune, un robot è un’apparecchiatura artificiale che compie determinate azioni sia basandosi sulle istruzioni assegnate, sia autonomamente. In questo lavoro il robot realizzato prevede esclusivamente un funzionamento autonomo. Esso è concepito per fornire una base hardware semplice e affidabile, rendendo possibile sviluppare in maniera indipendente applicazioni di “alto livello” basate sul sistema operativo mobile Android, disponibile tipicamente su smartphone e tablet. Le modalità di utilizzo del robot denominato Marvin – dall’androide paranoico di Guida galattica per gli autostoppisti scritto da Douglas Adams – sono molteplici e vanno dall’aspetto puramente didattico e di ricerca in ambito universitario fino a scenari più complessi quali la videosorveglianza e l’assistenza personale. Risulta interessante notare come la progettazione di questo sistema embedded ponga su piani diametralmente opposti l’attuazione di un comando dall’applicazione, e quindi dal “cervello”, del robot seguendo la filosofia del “Plug & Play”; qualsiasi possessore di uno smartphone potrebbe sviluppare la sua applicazione utilizzando le primitive di movimento esposte dal robot in maniera separata dall’hardware essendo certo sia che il dispositivo venga supportato, sia che l’applicazione con possa attuare i suoi comandi attraverso un sistema hardware di una certa complessità - utilizzando un set di API. 1.1. Stato dell’arte Lo sviluppo di Marvin nasce dalle ceneri di AndroEEbot (vedi fig. 1.1), dotato di funzionamento simile ma di architettura più complessa. La tesi ha come obiettivo una riprogettazione totale del sistema, con l’utilizzo di componenti hardware più idonei, accessibili e poco costosi e in grado di interfacciarsi con dispositivi di origini diverse. AndroEEbot basava i suoi spostamenti sul Motion Processor e riceveva dati solo da particolari telefoni Android come il Nexus S, interfacciando questi componenti con il microcontrollore Flex Full sul quale è in esecuzione Erika Enterprise, un sistema operativo real-time embedded. Attraverso il Vinculum II, dispositivo dotato di due porte usb e una porta USART, i tre dispositivi sopra citati erano messi in comunicazione. Il Vinculum-II quindi era impiegato come hub tra i vari dispositivi. 1 Capitolo 1. Introduzione Figura 1.1.: AndroEEbot 1.2. Obiettivi della tesi Gli obiettivi e la direzioni intraprese per migliorare il progetto esistente e aggiungere nuove funzionalità per evolvere il precedente sistema sono stati molteplici tra cui: 1. Definizione e studio del sistema e degli ambienti di sviluppo necessari: acquisizione e padronanza tale che la loro configurazione diventi una procedura standardizzata da inserire in un “how-to”. 2. Semplificazione del “sistema a basso livello”. Questo significa avere a disposizione un layer di funzionalità costituite dalla programmazione delle schede per lo svolgimento dei compiti di comunicazione, elaborazione dati, strutturazione del sistema real time in task, progettazione del sistema con componenti più idonei alla robotica, ecc. . . 3. Realizzazione di librerie per accedere comodamente a numerose funzioni della Flex, tra cui la comunicazione USART (Universal Synchronous-Asynchronous Receiver/Transmitter), PWM (Pulse Width Modulation), I/O digitale, comando dei servomotori. 4. Realizzazione e testing di strumenti di Debug in Run Time per la Flex Full attraverso la libreria Serial Console. 5. Utilizzo e modifica della libreria Microbridge ADB per comunicare con qualsiasi dispositivo Android e microcontrollore Arduino. 6. Aggiunta di features all’applicazione Android per fornire a Marvin migliori funzionalità di inseguimento di volti tramite libreria OpenCV e controllore PID implementato sulla Flex Full. 7. Progettazione e implementazione di controllori PID per il controllo in anello chiuso dei movimenti spaziali del robot mediante un kernel real-time . 8. Aggiunta dello “snodo di testa” e miglioramento della meccanica rispetto al precedente AndroEEbot. Risulta quindi evidente come il lavoro svolto ha coinvolto diversi fronti portando novità al precedente sistema come il Plug & Play degli smartphone, la possibilità di utilizzare due microcontrollori per la lettura di sensori o l’utilizzo di attuatori come i motori a corrente continua oppure servomotori attraverso PWM, e infine le importanti funzioni di debug a run time originariamente non disponibili per la Flex concludendo con la realizzazione di controllori PID per il controllo automatico del movimento del robot attraverso varie politiche di controllo. 2 Capitolo 1. Introduzione 1.3. Struttura del lavoro I diversi capitoli della tesi possono essere riassunti per macro-argomenti in maniera da avere una panoramica generale su come il lavoro è stato impostato. Nel capitolo 2 viene fornita una descrizione dettagliata sui dispositivi hardware e l’elettronica utilizzata per familiarizzare con gli strumenti di lavoro. Il capitolo 3 presenta gli ambienti di sviluppo riepilogando tutte le informazioni necessarie per l’installazione, l’importazione dei progetti esistenti e fornendo indicazioni sull’utilizzo per addolcire il più possibile la curva di apprendimento dei numerosi strumenti. Nel capitolo 4 si parlerà della progettazione sia hardware sia software che è stata necessaria per ricostruire dalle fondamenta il sistema, venendo illustrate l’analisi dei requisiti e il funzionamento di Marvin, unitamente una schematizzazione a livelli del sistema per spiegarne a fondo l’architettura. Il capitolo 5 tratta le scelte implementative e le soluzioni progettuali per l’hardware e il software, verranno spiegate le problematiche incontrate e soluzioni progettuali intraprese nonché spiegato a fondo il funzionamento del codice su Flex, Arduino e Android. Nel Capitolo 6 è affrontato il problema del Controllo Automatico mediante PID a tempo discreto implementato in maniera congiunta tra Android e Flex Full, oltre alle conoscenze matematiche e operative necessarie per tarare i parametri del controllore. Infine il capitolo 7 contiene i test e i risultati ottenuti con il nuovo sistema embedded e software progettato e realizzato. Il capitolo contiene anche idee e spunti di riflessioni per sviluppi futuri di Marvin. Seguono due appendici utili a livello pratico, cioè lo schema dei collegamenti elettrici e l’elenco dei software utilizzati. 3 Capitolo 2. Componenti di Marvin Nelle applicazioni dei sistemi embedded, l’ambiente di sviluppo è caratterizzato da hardware e software. A differenza dei sistemi PC general-purpose, dove grazie all’astrazione dei sistemi operativi e ai linguaggi di programmazione di alto livello, viene tendenzialmente ignorato l’hardware sottostante, nei sistemi embedded l’hardware rappresenta un elemento fondamentale del sistema stesso, che lo caratterizza, e vincola il progettista nelle scelte e nelle soluzioni da adottare In questo capitolo viene effettuata una panoramica sulle caratteristiche dell’hardware utilizzato per comprendere al meglio gli strumenti di lavoro. 2.1. Flex Full Flex è una scheda embedded realizzata da Evidence srl, basata sui microcontrollori della famiglia dsPIC DSC (Digital Signal Controller) di Microchip. Si tratta di una evaluation board che permette lo sviluppo e il testing di applicazioni real-time per dsPIC DSC; è dotata di un’architettura hardware modulare e fornisce supporto al kernel real-time Erika Enterprise. La Flex è il “cervello” di Marvin, in quanto le sono affidati i compiti di coordinazione dei vari dispositivi ad essa connessi, nonché l’esecuzione del codice di controllo del robot. La Flex Full board adotta un’architettura modulare composta dalla board madre, su cui sono già presenti tutte le funzionalità del microcontrollore, e da schede figlie chiamate daughter boards, che estendono e semplificano l’accesso ad alcuni dispositivi e l’uso di alcune funzionalità. La Demo Daughter Board, ad esempio, fornisce supporto alla scrittura su display LCD, all’utilizzo di sensoristica come trasmettitori e ricevitori infrarossi, sensori di temperatura e luminosità, e facilità l’utilizzo delle periferiche di comunicazione attraverso bus I 2 C o SPI. Figura 2.1.: Flex Full e Flex Base 4 Capitolo 2. Componenti di Marvin La Famiglia delle board Flex, come si nota in fig. 2.1, è composta da due board: la Flex Full – utilizzata nel presente lavoro – e la Flex Base. La Flex Base differisce dalla versione Full per l’assenza della porta USB e del PIC 18 predisposto alla sua gestione (e non direttamente programmabile dall’utente). Inoltre lo stadio di alimentazione della Flex Base accetta un range di tensioni di ingresso da 7 a 12 V, meno ampio rispetto ai 9-36 V della versione Full. Sempre dalla fig. 2.1, si può notare come i pin disponibili nel microcontrollore dsPIC33FJ256MC710 della famiglia dsPIC DSC di Microchip vengano esportati attraverso connettori standard a passo 2.54 mm, agevolando l’interconnessione con circuiti elettronici esterni. La caratteristica che rende la Flex Full un buon hardware su quale sviluppare la logica di controllo di Marvin è individuabile nella possibilità di avere da una parte il sistema operativo Real-Time Erika Enterprise, con il quale organizzare l’esecuzione del codice in tasks, avendo così a disposizioni su un microcontrollore il concetto di programmazione concorrente – cosa molto rara nei sistemi embedded attuali – e dall’altra parte la possibilità di programmare a basso livello il dsPIC in linguaggio C o Assembly, avendo a disposizione le periferiche tipiche di un microcontrollore. Le caratteristiche principali del dsPIC33FJ256MC710 sono riassunte qui di seguito [1]: • Architettura: 16-bit, di tipo Modified Harvard • Massima velocità della CPU : 80 Mhz corrispondenti a 40 MIPS • KB dedicati alla Memoria Programma: 256 • Tensione di funzionamento: da 3 a 3.6 Volt • Pin disponibili: 100 • Oscillatori interni: 2 oscillatori, per due diverse modalità di funzionamento, rispettivamente 7.39 MHz e 512 KHz (con possibilità di usare un PLL – Phase Loop Locker) per incrementare la frequenza fino a 80 MHz • Periferiche di Comunicazioni digitali: 2 USART, 2 SPI, 2 I2C • PWM (Pulse Width Modulation): 2 Moduli PWM dedicati al controllo dei motori, PWM con risoluzione di 16 bits e possibilità di utilizzare 8 canali • DMA (Direct Memory Access): 8 canali, 2 Kbytes DMA buffer area (DMA RAM) per memorizzare i dati trasferiti via DMA così è possibile il trasferimento di informazioni tra RAM e periferiche mentre la CPU sta eseguendo codice (no cycle stealing); la maggior parte delle periferiche supporta DMA. • Real-Time Clock Circuit (RTCC) Hardware: non disponibile all’interno ma è possibile usare una configurazione per usare un RTCC esterno • Timers: 9 Timers a 16 bits, oppure 8 utilizzabili in coppia per ottenere 4 timers a 32 bits • Output Voltage: i pin digitali hanno uscite che al massimo raggiungono i 3.6 volt, perciò la logica Alto(H)/Basso(L) rispetta lo standard 3.3/0 volt. • Moduli ECAN: 2 • ADC (Convertitore Analogico Digitale): 2 5 Capitolo 2. Componenti di Marvin Figura 2.2.: Pinout del dsPIC 6 Capitolo 2. Componenti di Marvin I 100 pin del dsPIC sono riportati totalmente sulla Flex Full sui connettori CON6 e CON5 con la stessa numerazione che si può trovare sul dsPIC (vedi fig. 2.2): ad esempio la Ricezione della USART 1 sul dsPIC è multiplexata sulla linea 52 che corrisponde proprio al pin P52 sul connettore CON6 della Flex. In fig. 2.3 sono evidenziati i connettori e le principali funzionalità della board Flex Full. Per completezza, in fig. 2.5 è riportata la piedinatura della Flex Full. Figura 2.3.: Funzioni e connettori della scheda Flex Full La programmazione e il debug del microcontrollore dsPIC avviene attraverso delle apposite interfacce: In-Circuit Serial Programming (ICSP), Enhanced ICSP e Joint Test Action Group (JTAG). La ICSP è un’interfaccia proprietaria di Microchip, appositamente sviluppata per la programmazione dei propri dispositivi. È quindi integrata nel core del MCU, dove una macchina a stati regola la scrittura in memoria; oltre alla programmazione, la ICSP gestisce anche un canale per il debugging sul chip (in-circuit debugging). Il dispositivo MPLAB ICD2 (vedi fig. 2.4) è necessario per eseguire le operazioni di programmazione e debug del MCU. Figura 2.4.: Microchip ICD 2 Purtroppo data la complessità del sistema operativo Erika, non è possibile eseguire le operazioni di Debug attraverso ICD2 e MPLAB. Per questo motivo è stato sviluppato durante questo lavoro di tesi anche una libreria chiamata serialConsole.h, che va compilata insieme al programma di controllo del 7 Capitolo 2. Componenti di Marvin robot sulla Flex, che permette di comunicare verso un PC attraverso un Convertitore TTL-USB basato sul chip PL2303. Figura 2.5.: Pinout della scheda Flex Full 8 Capitolo 2. Componenti di Marvin 2.2. Convertitore TTL-USB PL2303 Questo semplice convertitore presenta i pin per l’USART TX, RX e GND. Tali pin sono stati collegati alla Flex Full ed utilizzati con la seconda USART della scheda, mentre si pone lato PC come porta seriale Virtuale (VCOM su sistemi Windows). Figura 2.6.: Retro e pinout del PL2303 Figura 2.7.: Fronte del PL2303 Per visualizzare le stringhe di Debug è necessario utilizzare la libreria serialConsole.h e un programma per la ricezione tramite porta seriale. Per questo lavoro è stato utilizzato Real Term in quanto completo e preciso nonché freeware. Si nota dalla foto come questo chip possa anche fornire due linee di alimentazione (3.3 e 5 V) a dispositivi ad esso collegati, anche se questa caratteristica non è stata utilizzata per il progetto di Marvin. I driver del chip PL2303, affinchè esso sia riconosciuto come VCOM, sono reperibili senza difficoltà per i sistemi operativi Linux, Windows Vista e Windows 7. 2.3. FTDI Vinculum-II Il Vinculum-II (VNC2) è la seconda versione della famiglia Vinculum di FTDI; si tratta di un controller embedded dual USB host. È dotato di un microcontrollore a 16-bit con memoria Flash da 256KB e 16KB di RAM. Il VNC2 fornisce la funzionalità di interfaccia USB Host per una varietà di dispositivi USB. Figura 2.8.: Vinculum-II La comunicazione con dispositivi non-USB, come microcontrollori, può essere realizzata utilizzando le interfacce USART, SPI oppure parallel FIFO (è presente inoltre anche un modulo PWM) e tutti i segnali sono prelevabili attraverso 24 pin 2.54 mm configurabili. 9 Capitolo 2. Componenti di Marvin Il VNC2 è un dispositivo completamente programmabile e permette la realizzazione di firmware personalizzati secondo le proprie esigenze, utilizzando l’apposito ambiente di sviluppo Vinculum- II IDE. Il linguaggio di programmazione utilizzato è il C e per permettere la connettività e il funzionamento dei vari dispositivi – come l’istanziamento dei driver – è necessario utilizzare le primitive del VOS, il sistema operativo Real-Time del Vinculum che gestisce e schedula i tasks. La programmazione del Vinculum-II avviene per mezzo di un apposito dispositivo, il VNC Debugger/Programmer Module progettato per fornire la connettività tra l’ambiente di sviluppo Vinculum-II IDE e i pin dell’interfaccia di debug presenti nel V2DIP2. Figura 2.9.: Modulo di debug V2DIP2 Il modulo di debug VNC2 è una mini-scheda con un connettore USB miniB usato per connettersi all’ambiente di sviluppo sul PC; il connettore femmina a 6-vie è utilizzato per connettere il modulo di debug al dispositivo VNC2, dotato di un connettore maschio compatibile. Questo dispositivo comunque non è stato impiegato nella versione finale di Marvin mentre è stato utilizzato come interfaccia per lo scambio di messaggi tra Flex Full, smartphone Nexus S e Control Module per AndroEEbot. Le ragioni che hanno spinto alla sostituzione di questo modulo con il microcontrollore Arduino sono da individuarsi nell’aspetto Plug & Play verso tutti gli smartphone Android che Arduino permette e che il Vinculum limita. Infatti seppur entrambi i dispositivi supportano la libreria Open Accessory di Google solo Arduino supporta MicroBidge ADB. Le due librerie differiscono molto per compatibilità dei dispositivi. Infatti la Microbridge ADB è supportata da qualsiasi dispositivo Android con versione >= 1.5, mentre la Open Accessory è attualmente supportata, solo da un paio di smartphone (Nexus S e Nexus One) e qualche tablet (come il Motorola Xoom). Ciò impone un pesante vincolo progettuale e di costo, e limita il concetto di Plug & Play. Con Arduino diverse applicazioni Android, create per comandare semplicemente l’hardware sottostante, possono essere condivise e impiegate su un qualsiasi smartphone; ciò non potrebbe accadere utilizzando il Vinculum-II. 10 Capitolo 2. Componenti di Marvin 2.4. Libreria Open Accessory Open Accessory viene distribuita da Google per l’interfacciamento tra il mondo dei sistemi embedded e il sistema operativo Android e i dispositivi che lo ospitano. La libreria viene distribuita in due versioni: come Add-on, per le versioni 2.3.4+ di Android (è un backporting) e dalla 3.0+ come libreria ufficiale. Open Accessory che permette ad un dispositivo Android che la supporti di poter usare la porta USB per scambiare dati con altri dispositivi. Questa libreria permette di utilizzare il dispositivo Android come USB Accessory e USB Host. Nel primo caso, che è quello specifico di questa tesi, il dispositivo viene riconosciuto come un accessorio, cioè è atto a svolgere il compito di slave e quindi non può enumerare altri device ad esso collegati attraverso la porta USB e fornire loro alimentazione elettrica. Il secondo caso invece permette al dispositivo di figurare come Master. In generale attraverso questa libreria il dispositivo Android può interfacciarsi con altri dispositivi USB come Mouse, Tastiere, Fotocamere, Schede di Sviluppo ecc... La libreria è stata rilasciata verso la fine del 2011 e sono tutt’ora pochi i dispositivi che la utilizzano appieno come riporta prorio Google1 stessa poiché oltre a delle feature software è necessario che i produttori di smartphone implementino via hardware la possibilità di utilizzare l’Open Accessory: Tipicamente gli smartphone che la supportano appartengono alla Fascia Alta della gamma e perciò richiedono costi elevati, nell’ordine dei 300A C2 . 2.5. Arduino Mega ADK Arduino Mega ADK è una scheda di sviluppo basata sul microcontrollore ATmega2560. Esso differisce dalla versione Arduino Mega per la presenza di una interfaccia USB Host usata per connettere smartphone basati sul sistema operativo mobile Android. L’interfaccia USB è realizzata mediate l’integrato MAX3421e. Figura 2.10.: Arduino Mega ADK Tra le caratteristiche più interessanti di questo microcontrollore si ha che esso dispone di 54 pin I/O, un oscillatore interno a 16 MHz, bottone di reset, un ambiente di sviluppo di semplice utilizzo basato su 1 Note: Accessory mode is ultimately dependent on the device’s hardware and not all devices will support accessory mode. Devices that support accessory mode can be filtered using a <uses-feature> element in your corresponding application’s Android manifest.For more information, see the USB Accessory Developer Guide. (dal sito dedicato alla libreria Open Accessory, ultima consultazione – Luglio 2012) 2 Dato ricavato da ricerche condotte su negozi online nell’anno 2012 11 Capitolo 2. Componenti di Marvin Processing, programmazione tramite linguaggio ad alto livello chiamato Wiring, supporto per la libreria Open Accessory di Google per comunicare con Android. Questo seconda board di sviluppo è stata scelta in sostituzione del precedente Vinculum-II sul robot Marvin per la sua predisposizione naturale per la comunicazione con Android. Infatti oltre a supportare nativamente la libreria Open Accessory di Google supporta (seppur con leggere modifiche) la libreria Microbridge ADB che rende possibile l’interfacciamento di qualsiasi dispositivo Android (versione 1.5 o maggiore) senza bisogno di permessi di root sul dispositivo. L’USB Host, che nel nostro caso è proprio Arduino Mega ADK, tramite l’utilizzo del protocollo ADB (Android Debug Bridge) permette di aprire una Shell sul telefono o inviare direttamente comandi tramite essa, trasmettere file tra PC e Android, leggere i log di debug (logcat), aprire porte TCP e sockets Unix. L’ultima caratteristica è quella usata dalla libreria Microbridge che utilizza sockets TCP per stabilire un canale di connessione bidirezionale (pipe) tra un dispositivo Android e Arduino, l’applicazione Android si comporta da Server ascoltando una determinata porta scelta dal programmatore e Arduino si connette a quella porta tramite ADB comportandosi da Client; la libreria permette di effettuare riconnessioni automatiche nell’eventualità che il device USB venga scollegato da Arduino o per altre cause come crash dell’applicazione Android, etc... aumentando di molto l’affidabilità e permettendo il “Plug & Play” concetto che si è voluto realizzare dai primi momenti del progetto di Marvin. Si fa notare che per lo sviluppo del progetto non è necessario usare proprio l’Arduino Mega ADK, infatti basterebbe utilizzare un qualsiasi Arduino con annessa la USB Shield che permette di avere le stesse caratteristiche della board presentata (USB Host) ma attraverso un’architettura modulare del tipo mother board + daughter board; questa soluzione è stata scartata in fase di progetto perché la versione ADK rispetto alla USB Shield permette di avere funzionalità integrate senza perdere pin e senza bisogno di ulteriori fonti di alimentazione. In fase progettuale l’utilizzo di un secondo microcontrollore di facile accesso come Arduino è stata un motivata anche dall’aumento delle capacità computazionali e di comunicazione del robot. In questo modo infatti, risulta possibile dividere carichi di lavoro tra 2 CPU, già messe in comunicazione mediante la libreria Arduino.h sulla Flex e nel file AndroidFlexComunication.ino su Arduino, risulta possibile utilizzare sensoristica avanzata in maniera molto semplice grazie alle potenzialità di Wiring e per le altre periferiche aggiunte con la board, principalmente: • 15 PWM • 16 Ingressi Analogici • 3 USART • 1 porta seriale Virtuale (VCOM) integrata nell’ambiente di sviluppo di Arduino • 1 modulo SPI 12 Capitolo 2. Componenti di Marvin Figura 2.11.: Pinout ATMega2560 13 Capitolo 2. Componenti di Marvin 2.6. Control Module basato su chip Motion Processor MC3410 AndroEEBot è stato progettato per interfacciarsi con il Control Module del robot ER1 sviluppato dalla Evolution Robotics nel 2002. Il Control Module gestiva il movimento robot attraverso 2 motori passo passo Shinano Kenshi SST58D3820 da 3.4 Volt, 1.8 gradi/step, 2 Ampere/fase, pilotati in modalità bipolare. I motori erano collegati al Control Module con due connettori seriali X.21 ed il connettore 1 era associato alla ruota destra. (a) Control Module del robot ER1 (b) Motore passo-passo Shinano Kenshi Figura 2.12.: Hardware per il controllo del movimento di AndroEEBot Il programmatore si interfaccia al modulo di controllo attraverso la porta USB e invia i comandi di direzione e velocità tramite sequenze di byte che verranno poi interpretate dalla circuiteria interna e attuate dai motori passo-passo. Il Control Module è fondamentalmente composto da 3 chip, due motion processor MC3410 che si occupano di trasformare i byte ricevuti in ingresso tramite porta USB in comandi di direzione, traiettoria e velocità ,e da un chip FTDI RS232-USB utilizzato come convertitore USB → Seriale secondo lo standard RS232. Proprio la presenza di questo chip ha permesso di optare per il Vinculum-II nel precedente AndroEEBot, anch’esso appartenente alla famiglia di chip della FTDI. Questo componente è stato inizialmente utilizzato nell’architettura di Marvin come dimostrato in alcuni video presi in fase di sviluppo ma poi è stato sostituito perché riduceva di molto i tempi di risposta del sistema, spesso risultava non essere preciso e generalmente rappresenta una sorta di “black box” che avrebbe potuto rallentare lo sviluppo e l’evoluzione di Marvin. Il Motion Control e i motori passo-passo sono perciò stati sostituiti con dei Servomotori a rotazione continua ad alta coppia: i DF15SR. 2.7. Servomotori DFRobotics DF15SR e Hitec HS-485HB I Servomotori sono dei motori DC (motori a corrente continua) che includono al loro interno ingranaggi di riduzione o moltiplica della coppia, un circuito di controllo e un potenziometro utilizzato come sensore di posizione. Attraverso queste caratteristiche essi risultano molto semplici da utilizzare e da installare. Per poter essere controllati questi motori hanno bisogno di un segnale di controllo PWM con duty cycle variabile rispetto ad un periodo fissato; entrambi i motori utilizzati necessitano di un periodo si 20 ms per il segnale PWM. 14 Capitolo 2. Componenti di Marvin Il duty cycle è la frazione di tempo che un segnale passa in uno stato attivo in proporzione al tempo totale considerato (periodo). In presenza di un segnale sotto forma di onda rettangolare, il duty cycle è il rapporto tra la durata del segnale “alto” (τ ) e il periodo totale del segnale (T ), e serve a esprimere per quanta porzione di periodo il segnale è a livello alto intendendo con alto il livello “attivo”. Il duty cycle è definito come d = Tτ , dove τ è la porzione di periodo a livello alto (detta anche T-on) e T è il periodo totale. Figura 2.13.: Esempio di onda rettangolare generata con la PWM I servomotori utilizzati nella costruzione di Marvin sono di due tipologie, un servomotore standard (angolo di rotazione 0-180°) Hitec HS-485HB per lo snodo di testa alimentato a 5 V, coppia di stallo 4.8 Kg·cm, ingranaggi in karbonite e drain massimo di corrente di 150 mA senza carico. I tempi T-on sono riportati nello schema sottostante. Per questo particolare servomotore il valore di duty cycle definisce l’angolo in cui il motore deve posizionarsi. (a) Rivestimento del servo HS-485HB (b) Riduttori in karbonite Figura 2.14.: Servomotore HS-485HB Figura 2.15.: Corrispondenza tra angoli e tempi di T-on in microsecondi 15 Capitolo 2. Componenti di Marvin Si noti che 1.5 ms corrisponde alla posizione 0° del servo. Variando il tempo di T-on del duty cycle si possono ottenere i vari valori dell’angolo che il braccio del servo deve raggiungere. Si riporta il cablaggio: • Nero: Massa • Rosso: Alimentazione (4.8 – 6 V) • Giallo: Segnale PWM Per la propulsione di Marvin, invece, sono stati utilizzati due servomotori a rotazione continua DF15SR della DFRobotics che, a differenza del servomotore Hitec, permettono la rotazione continua (0-360°); il valore di duty cycle determina il verso di rotazione, e non la posizione come accade per il precedente modello. Con valori di T-on di 1.5 ms il motore si ferma, con T-on pari a 2.4 ms esso ruota alla massima velocità in senso orario mentre per T-on pari a 0.6 ms esso ruota alla massima velocità in senso antiorario, tecnicamente per tutti i valori di T-on compresi nei range sopra descritti la velocità del motore dovrebbe aumentare o diminuire linearmente, anche se data la limitazione di valori imponibili è difficile notare cambiamenti di velocità apprezzabili; in ogni caso attraverso un controllore PID con mapping dei valori è possibile notare concretamente l’accelerazione o la decelerazione dei motori. Figura 2.16.: DF15SR con “Horns” I servi di propulsione sono alimentati a 5 V, coppia di stallo 10 Kg*cm, e drain massimo di corrente di 1.2 A senza carico alla massima tensione di alimentazione (7.2 V). Si riporta il cablaggio: • Marrone: Massa • Rosso: Alimentazione (4.8 – 7.2 V) • Arancione: Segnale PWM La particolarità di questo motore sta nel fatto di poter ottenere diversi valori di coppia di stallo per diversi valori di tensione: 16 Capitolo 2. Componenti di Marvin Coppia 10 Kg·cm 13 Kg·cm 15 Kg·cm Tensione 4.8 V 6V 7.2 V Tabella 2.1.: Coppia di stallo per il servo DF15SR a diversi valori di tensione 2.8. Smartphone Android Uno smartphone è un “telefono multimediale/intelligente” cioè un dispositivo che incorpora le funzionalità di un telefono cellulare con quelle di un PDA, su questi dispositivi è possibile utilizzare un sistema operativo ed installare e sviluppare applicazioni che ne sfruttano la sensoristica e le peculiarità. Il progetto di Marvin ha previsto fin da subito l’utilizzo di uno smartphone che potesse interfacciarsi con le componenti elettroniche e meccaniche sottostanti attraverso API portabili su diversi dispositivi così da permettere il Plug & Play di applicazioni capaci di utilizzare il robot per effettuare movimenti, mentre l’elaborazione è affidata all’applicazione che può essere progettata indipendentemente dall’hardware sottostante. L’applicazione sviluppata prevede l’utilizzo del sistema operativo Android tramite il quale è possibile accedere alla videocamera posteriore di cui ogni smartphone è dotato, ed applicare sui vari frame catturati filtri e algoritmi volti messi a disposizione dalla libreria di visione artificiale Open CV per Android. Inizialmente AndroEEbot prevedeva la sola compatibilità con lo smartphone Nexus S, ora attraverso la libreria Microbridge ADB è possibile utilizzare un qualsiasi smartphone con Android >= 1.5. Vari dispositivi sono stati messi alla prova tra cui • LG Optimus One • HTC Desire • HTC Evo 3D Su ognuno di essi la compatibilità è stata verificata con successo, ma al di là del testing l’HTC Evo 3D è stato utilizzato come riferimento per il sistema poiché dotato di caratteristiche hardware nettamente migliori e per la possibilità di utilizzare ben due fotocamere per la visione stereoscopica. (a) HTC Evo 3D (b) Particolare delle fotocamere stereoscopiche Figura 2.17.: HTC Evo 3D Caratteristiche e sensoristica: 17 Capitolo 2. Componenti di Marvin • Giroscopio • Accelerometro • Sensore di prossimità • Sensore di luminosità • GPS integrato • Doppia fotocamera da 5 megapixel con autofocus e doppio flash a LED • Apertura f/2.2 • Registrazioni video in 2D/3D con risoluzione fino a 720p • Fotocamera frontale da 1.3 megapixel a fuoco fisso • CPU dual-core Qualcomm MSM8260 a 1.2 Ghz • GPU Adreno 220 • RAM 1 GB 2.9. Circuito di Alimentazione, led e breadboard Il robot è costituito, oltre che dai microcontrollori e dagli attuatori, anche da alcune parti elettroniche. La circuiteria è stata realizzata mediante una breadboard per permettere una facile prototipizzazione. Il circuito di alimentazione è diviso in due linee di tensione mediante il regolatore di tensione lineare LM7805CV, dove nel pin di input entra la linea 12 V e dal pin di output esce la 5 V. Figura 2.18.: Regolatore di tensione LM7805CV Il regolatore di tensione lineare, mediante una serie di transistor, attua la regolazione per dissipazione della tensione in eccesso. La potenza dissipata è: P = (Ving − Vout ) · (Idrain ) 18 (2.1) Capitolo 2. Componenti di Marvin Risulta chiaro, anche con un calcolo sommario, che se la linea a 5 V è utilizzata per alimentare i tre servomotori con un drain di corrente pari a circa 800 mA e la caduta di potenziale è 7 V, la potenza dissipata sarà pari nel caso peggiore 7 V * 0.8 A = 5.6 W. Per cui risulta necessario inserire un dissipatore affinché la potenza sviluppata dal componente, se troppo elevata, non lo distrugga. Figura 2.19.: Circuito regolatore di tensione positiva stabilizzata Lo schema del circuito di alimentazione risulta essere: • C1: 1000 uF, elettrolitico 16 V • C2: 47 nF, poliestere metallizzato • C4: 470 uF, elettrolitico 16V • IC1: LM7805CV, dove E = IN, M = GND e U = OUT I condensatori hanno la seguente funzione: • C1: livella la tensione a monte del circuito. • C2: questi due condensatori servono per togliere i disturbi in alta frequenza, il più usato è quello da 100 nF. C’è una cosa più importante della sua capacità, ed è la posizione: devono essere il più vicino possibile al regolatore per svolgere bene il loro compito. • C3: serve a “pulire” la tensione in uscita. Nel circuito sono presenti anche 3 LED che servono per indicare gli stati della macchina ed hanno la seguente funzionalità: • Rosso: controllato da Arduino indica che l’applicazione Android sta inviando dei dati; serve per verificare che l’applicazione stia effettivamente inviando i byte. Inoltre indica che tali byte sono ricevuti e processati da Arduino. • Verde: controllato da Arduino indica che la Flex ha inviato una richiesta tramite USART 1 ad Arduino, ed Arduino ha processato e risposto tramite l’invio di byte alla Flex. • Arancione: controllato dalla Flex indica che i dati ricevuti da Arduino corrispondono effettivamente a dati utili per il movimento dei motori. 19 Capitolo 3. Ambienti di sviluppo Nel campo dell’informatica per ambiente di sviluppo si intende uno o più software che aiutano il progettista/programmatore nello sviluppo e nel testing del codice. Normalmente un ambiente di sviluppo consiste in un editor di codice sorgente, un compilatore e/o un interprete, un tool di building automatico e solitamente, un debugger. Per lo sviluppo di Marvin gli ambienti di sviluppo utilizzati sono stati molteplici.Saranno quindi descritti i vari tools e, in maniera concisa, verrà spiegato come installare e configurare tali software di sviluppo1 . 3.1. Eclipse Indigo con Android SDK L’ambiente di sviluppo per le applicazioni Android è basato su Eclipse, IDE flessibile multi- linguaggio e multi-piattaforma open source che mediante l’utilizzo del plugin Android SDK diventa un validissimo mezzo tramite il quale programmare, compilare, generare .apk (Android Package file: pacchetti eseguibili per il sistema operativo Android), emulare terminali e mandare in esecuzione in debug, sul proprio dispositivo, le applicazioni. Android è un sistema operativo basato sul kernel Linux 2.6, appositamente realizzato per dispositivi portatili. L’architettura è suddivisa in 5 componenti. Al di sopra del kernel Linux ci sono le librerie C/C++ utilizzabili dalle applicazioni; ad un livello superiore si trova l’Application Framework, che fornisce le API e i servizi che possono essere utilizzati dalle applicazioni (chiamate anche App); queste ultime si trovano in cima all’architettura, nel livello Application, e sono sviluppate usando il linguaggio di programmazione Java. Figura 3.1.: Architettura di Android 1 Affinché tutti i tools di sviluppo possano funzionare correttamente è consigliato lavorare in ambiente Windows Vista o Windows Seven 20 Capitolo 3. Ambienti di sviluppo Il componente chiave del framework Android è l’Android Runtime, che contiene le librerie di sistema e la Virtual Machine Dalvik, una macchina virtuale appositamente progettata e ottimizzata da Google secondo le caratteristiche hardware dei dispositivi mobile. Lo sviluppo delle applicazioni Android avviene utilizzando l’IDE Eclipse per Java, con installato il plugin Android Develoment Tools (ADT); in aggiunta, è necessario il pacchetto Android SDK che contiene i vari strumenti di sviluppo e le librerie per le versioni di Android, necessari per compilare l’applicazione realizzata nel formato .apk. Sono quattro i tipi di componenti delle applicazioni: Activity, Service, Content Providers, Broadcast Receivers. Una Activity visualizza una schermata singola; in una applicazione più attività funzionano in contemporanea per costruire un’interfaccia grafica più complessa, ma ognuna è indipendente dalle altre. In applicazioni multi-activity, ne esiste una identificata come principale, che viene mostrata all’utente quando l’applicazione è lanciata per la prima volta; le attività possono poi avviare altre attività per svolgere determinate operazioni; in queste situazioni l’attività precedente è fermata e il sistema si occupa di memorizzarne lo stato nello stack. Il ciclo di vita delle Activity è il seguente: Figura 3.2.: Ciclo di vita di una activity 21 Capitolo 3. Ambienti di sviluppo Il prerequisito per l’installazione dell’Android SDK su Eclipse è l’avere sul proprio PC il JDK (Java Developer Kit). Successivamente si passa all’installazione dell’Android SDK tramite l’installer che però non contiene l’ambiente di sviluppo completo ma solo il nucleo dei tools utili a scaricare il resto del package dell’SDK. Nel momento in cui si installa il software è utile prendere nota della directory di installazione che verrà richiesta durante l’installazione dell’ADT, il plugin per Eclipse. Ad installazione avvenuta verrà visualizzata la finestra in fig. 3.3: Figura 3.3.: Repository Android Verranno automaticamente selezionati i pacchetti consigliati e raccomandati da installare. Nello specifico per la corretta compilazione ed esecuzione dell’applicazione Android “Marvin Face Follow” e “Marvin PID” sono stati utilizzati i file di Android 2.3.3 con le Google API 10 rev.2. Una volta ottenuti tali file è possibile passare all’installazione dell’ADT per Eclipse che viene installato tramite il menù Help > Install New Software di Eclipse e digitando nel campo “Work with” l’URL https//dl-ssl.google.com/android/eclipse/. Dare invio dopo aver selezionato tutti i plugin che compariranno nel repository. La fase successiva è la configurazione dell’ADT tramite il menù Windows > Preferences e selezionando Android dal pannello a sinistra. Inserire la locazione nel file system dell’Android SDK precedentemente installato attraverso il tasto Browse e successivamente confermare le modifiche, come illustrato in fig. 3.4. L’ultimo passaggio è l’importazione dell’applicazione “Marvin Face Follow”, o equivalentemente “Marvin PID”, nel workspace selezionato: dal menù File > Import scegliere la directory contenente i file del progetto (Android > Marvin Face Follow e le librerie Open CV) utilizzando l’opzione “Importa come progetto esistente” e quindi spuntare “Copy project into workspace” e dare invio. Dopo aver importato il progetto è necessario verificare che il link alle librerie Open CV sia stato mantenuto: quindi andare in proprietà del progetto cliccando con tasto destro sul progetto nel package explorer di Eclipse, segliere Properties > Android e confrontare la schermata con la fig. 3.5. 22 Capitolo 3. Ambienti di sviluppo Figura 3.4.: Librerie Android Figura 3.5.: Proprietà del progetto e link delle librerie 23 Capitolo 3. Ambienti di sviluppo Se dovessero mancare le librerie OpenCV si può aggiungerle con il tasto “Add” e ricercarle tra i progetti importati. 3.2. RT-Druid e ERIKA Enterprise v1.6.1 RT-Druid è l’ambiente di sviluppo per la Flex Board, o meglio per ERIKA RTOS. Infatti la particolare architettura della Flex impone di utilizzare RT-Druid come unico IDE sia per la scrittura dei Task e dei componenti del sistema Real-Time che per la scrittura a basso livello delle istruzioni del microcontrollore dsPIC. RT-Druid ha una struttura modulare, integrata nel framework Eclipse attraverso lo strumento dei plugin, è composto da un modulo per la generazione del codice, e uno per l’analisi di schedulabilità. Il code generator è distribuito anche in versione stand-alone indipendente da Eclipse, si tratta essenzialmente di un compilatore per OIL (il linguaggio dello standard OSEK ) che, in base alle specifiche definite nel file OIL di configurazione del sistema, costruisce la struttura necessaria alla compilazione di ERIKA (makefiles, inizializzazione delle strutture dati, ecc.). Il plugin per l’analisi della schedulabilità permette di modellare, analizzare e simulare il comportamento temporale del sistema embedded progettato, ma è disponibile solo nelle versioni commerciali di ERIKA. RT-Druid è corredato di una serie di esempi, disponibili per varie di architetture hardware: ciò agevola la realizzazione delle applicazioni, potendo prendere spunto da tali esempi di codice. Nel caso della Flex è possibile trovare tali esempi nel menù File > New > RT-Druid OIL e C/C++ project dove, dando un nome al progetto e cliccando su “next”, è possibile avere un elenco di hardware ed esempi collegati; i più interessanti sono quelli per il pic30 che corrispondono ai progetti utili per l’architettura dsPIC. Una applicazione per la Flex Board, come detto, è dipendente sia dai file del sistema operativo ERIKA sia dal codice di basso livello del microcontrollore. Proprio per questo tutti gli oggetti che vengono utilizzati dal sistema operativo devono essere definiti nel file OIL (di solito config.oil), che attraverso una particolare sintassi, istanzia gli oggetti (ad esempio gli Alarm, i Task, le Risorse) da utilizzare e definisce nell’oggetto OS le caratteristiche globali dell’applicazione, come la presenza di possibili stack non condivisi dai task in esecuzione (MultiStack), del tipo di Kernel da utilizzare e le specifiche sul microcontrollore attraverso la proprietà MCU_Data. La scrittura del file OIL porta alla generazione del codice di configurazione per ERIKA Enterprise(EE), del codice sorgente e dei makefiles richiesti per compilare l’applicazione. Definito il file OIL si possono usare gli oggetti istanziati nei file .c che costituiscono il codice sorgente dell’applicazione. Questi oggetti sono richiamati mediante opportuna sintassi: ad esempio un task è istanziato con la keyword TASK(nometask) e richiamato attraverso particolari API messe a disposizione da ERIKA tra cui la semplice ActivateTask(nometask). La configurazione di questo ambiente di sviluppo su Windows prevede l’installazione di ERIKA Enterprise e RT-Druid. In questa tesi è stata utilizzata l’ultima versione disponibile di RT-Druid la 1.6.1. Su Windows è necessario anche installare Cygwin 1.7.9, che viene utilizzato dall’IDE per compilare i file sorgenti, e Java Runtime Environment versione 6 o superiore. I passi necessari a configurare correttamente l’IDE sono: 1. Installare Java Runtime Environment 2. Installare Cygwin nella sua directory originaria C:/cygwin. 3. Copiare RT Druid ed Erika 1.6.1 in una directory. Se si usa Vista è necessario che non ci siano spazi nella directory di installazione, ad esempio è consigliabile installare tutto in C:/Evidence 4. Quando si seleziona il workspace di lavoro per RT-Druid non bisogna usare spazi. Ad esempio, è possibile posizionare il workspace in C:/Users/yourusername/workspace 24 Capitolo 3. Ambienti di sviluppo 5. È importante non creare mai delle directory, oltre a quelle specificate, con cui lavorare con questo IDE in altri path tranne la directory Users, quindi scegliere sempre C:/Users/yourusername/ per lavorare senza problemi 6. Una volta installato il pacchetto contenente Erika e RT-Druid 1.6.1 bisogna effettuare l’aggiornamento dei plugin da Eclipse, quindi andare su “Help > Install New Software” ed utilizzare il seguente repository http://download.tuxfamily.org/erika/webdownload/rtdruid_161_nb/, selezionando tutti gli aggiornamenti Figura 3.6.: Repository RT-Druid Per ultimare l’installazione è necessario installare il Compilatore per il dsPIC montato a bordo della Flex Full e che avrà il compito di trasformare il codice scritto per la macchina e quello di ERIKA in codice oggetto, producendo un .cof che, attraverso l’IDE MPLAB della Microchip, finalmente programmerà la scheda di sviluppo. Il compilatore testato e funzionante con RT Druid 1.6.1 è l’MPLAB C Compiler for PIC24 and dsPIC v3.31 versione Combo, edizione per studenti, installato nella directory di default. Utilizzando questo compilatore tutte le vecchie versioni di RT Druid non sono più funzionanti perciò è consigliabile lavorare con 1.6.1. Dopo aver finito di installare il compilatore, ritornare su RT-Druid e andare su Windows > Preferences e dal menù laterale della schermata che si aprirà cercare RT Druid -> Oil -> dsPIC e verificare che la schermata corrisponda alla fig. 3.7: 25 Capitolo 3. Ambienti di sviluppo Figura 3.7.: Link librerie MPLAB 26 Capitolo 3. Ambienti di sviluppo Infine andare su Project e togliere la spunta su Build Automatically; ogni volta che si vorrà fare la build del progetto dalla finestra Project Explorer andare su Build Project (vedi fig. 3.8). Figura 3.8.: Build del progetto La Build si compone di due parti. Prima di tutto viene creato e compilato il sistema ERIKA; questo richiede generalmente più tempo rispetto alla seconda parte che è invece solo la traduzione in codice oggetto delle istruzioni presenti nei file .c o .h del progetto. Se il file OIL non ha subito modifiche recenti la Build fa partire solo la seconda. L’ambiente di sviluppo RT-Druid non supporta alcune funzionalità offerte da Eclipse, come il completamento automatico o il supporto completo a tutti i valori dei registri del microcontrollore, anche un ambiente correttamente configurato mostrerà a schermo falsi errori, come è possibile notare nella schermata sottostante: Figura 3.9.: RT-Druid IDE 27 Capitolo 3. Ambienti di sviluppo questo non compromette la compilazione del file .cof, infatti gli “errori” vengono “risolti” in fase di compilazione.Tuttavia rendono questo IDE può risultare confusionario e di difficile accesso. Per importare il progetto “Marvin Face Follow” (o equivalentemente Marvin PID) si procede attraverso il menù File > Import > existing project into Workspace e si seleziona Marvin spuntando la copia del progetto nel workspace. Dopo aver importato il progetto è necessario spostarsi nel Project Explorer e selezionare dal menu a tendina Clean Project e successivamente Build Project per ottenere un nuovo file .cof. Il log della corretta compilazione di Marvin PID, ma è equivalente per il Face Follow, è il seguente che include entrambi gli step cioè la compilazione di ERIKA e del codice .c [2] **** Build of configuration Default for project Marvin PID **** C :\ Users \ WISTA \ flexworkspace \ Marvin PID \ Debug \ make_launcher . bat all C :\ cygwin \ bin \ bash found ! cygwin warning : MS - DOS style path detected : C :\ Users \ WISTA \ flexworkspace \ Marvin PID \ Debug Preferred POSIX equivalent is : / cygdrive / c / Users /.../ Marvin PID / Debug CYGWIN environment variable option " n o do s f i l e w a r n i n g " turns off this warning . Consult the user ’ s guide for more details about POSIX paths : http :// cygwin . com / cygwin - ug - net / using . html # using - pathnames Using erika files in / cygdrive / c / EVIDEN ~2/ eclipse / plugins /.../ ee_files Looking for the MPLAB C30 directory ... ... Using C :\ Program Files \ Microchip \ mplabc30 \ v3 .25 CPP eecfg . c CPP code . c CPP ee_hal_s tructs . c CPP ee_context . c CPP ee_irq . c CPP ee_utils . c CPP ee_alcancel . c CPP ee_altick . c CPP ee_alget . c CPP ee_alsetabs . c CPP ee_alsetrel . c CPP ee_irqsc . c CPP ee_rqexchg . c CPP ee_rqinsert . c CPP ee_schedule . c CPP ee_thact . c CPP ee_thendin . c CPP ee_mutex . c CPP ee_internal . c CPP ee_utils . c AR libee . a LD OBJDUMP ************************************ Compilation terminated successfully ! **** Build Finished **** 28 Capitolo 3. Ambienti di sviluppo 3.3. Microchip MPLAB v8.43 Per la programmazione del dsPIC, a bordo della scheda FLEX, si utilizza il programmatore Microchip MPLAB ICD2 collegando, con un apposito cavo, i due dispositivi attraverso la porta ICD2. La Microchip mette a disposizione l’ambiente di sviluppo MPLAB IDE per programmare il microcontrollore, sono integrate diverse funzionalità: gestione dei file e organizzazione dei progetti, editor per i sorgenti, compilatori, programmazione e debugging con MPLAB ICD2. In questo lavoro l’IDE è utilizzato solamente nella gestione dei file, importando il file dell’applicativo realizzato con RT-Druid per programmare il dsPIC. Per programmare il dsPIC è necessario andare su File > Import e scegliere il .cof generato da RTDruid (nella directory Debug del progetto). Successivamente dopo aver collegato fisicamente PC e Flex Full andare nel menù Programmer > Select Programmer MPLAB ICD2; successivamente dopo che la comunicazione è stata stabilita seguire i seguenti passaggi: Programmer > Connect e successivamente Programmer > Program Figura 3.10.: Log della programmazione del dsPIC Una volta collegato l’ICD2 il dsPIC è tenuto in uno stato di “Reset”, per sbloccarlo è sufficiente scollegare l’ICD2 oppure utilizzare Programmer > Release from Reset. Per poter utilizzare correttamente questa procedura è necessario effettuare una configurazione per l’ICD2 cioè andare in Configure > Select Device e settare i parametri come in fig. 3.11: 29 Capitolo 3. Ambienti di sviluppo Figura 3.11.: Setup ICD2 e MPLAB 3.4. Arduino IDE v1.0 Arduino IDE è l’ambiente di sviluppo per il microcontrollore Arduino, basato su Processing, un software nato in origine come ambiente di sviluppo Java. Arduino IDE è molto semplice da utilizzare e da configurare e questo risulta un grande punto a favore di questo progetto open-source italiano. Una volta ottenuto l’ambiente di sviluppo non c’è bisogno di nessuna installazione, basta lanciare Arduino.exe per iniziare a lavorare sull’IDE. Per utilizzare la Board Arduino Mega ADK sono necessari dei drivers di comunicazione USB tra il PC e la board che una volta installati faranno apparire Arduino come una porta seriale virtuale VCOM. Fatto questo bisogna definire in Tools > Board la board “Arduino Mega 2565 or Mega ADK” e definire in Tools > Serial Port a quale porta seriale è associato Arduino così da poter uploadare il codice e aprire il Serial Monitor (vedi fig. 3.12). Il codice per Arduino di solito viene compilato nel momento in cui viene inviato alla Board attraverso l’icona Upload senza bisogno di un programmatore esterno, in quanto ogni board Arduino è fornita di un Bootloader: un piccolo programma che permette di programmare il microcontrollore Atmel via USB. Il bootloader è attivo per alcuni secondi nel momento in cui la board viene resettata e viene avviato ogni qual volta c’è del nuovo codice sulla board. Dopo aver completato l’upload i led TX e RX sulla board lampeggeranno e il codice srà quindi compilato ed inviato sulla board. Essa inizierà da subito la sua esecuzione, non esiste modo -come succede programmando la Flex- di mantenere la board in uno stato di Reset. 30 Capitolo 3. Ambienti di sviluppo Figura 3.12.: Selezione prototype board 31 Capitolo 3. Ambienti di sviluppo La libreria Microbridge ADB deve essere installata insieme all’IDE e per fare ciò basta copiare la cartella nella directory libraries di Arduino. Per utilizzare questa libreria con Arduino IDE 1.0 e la Mega ADK bisogna effettuare delle modifiche ai file che la compongono: • Adb.h, usb.cpp, max3421e.cpp : la stringa Wiring.h va sostituita con Arduino.h, poiché Wiring.h ha subito una ridenominazione passando alla versione 1.0 dell’IDE • Nel file max3421e.cpp alla riga 387 bisogna sostituire questo codice // Wait until the PLL is stable while (!( max3421e_read ( MAX_REG _USBIRQ ) & bmOSCOKIRQ ) ) { // Timeout after 256 attempts . tmp ++; if ( tmp == 0) return ( false ) ; } Con questo: // Wait until the PLL is stable while (!( max3421e_read ( MAX_REG _USBIRQ ) & bmOSCOKIRQ ) ) Per evitare che l’oscillatore interno non effettui un corretto setup. Per effettuare il debug sulla board bisogna utilizzare la funzionalità Serial Monitor, infatti dall’Arduino IDE non si ha la possibilità di impostare breakpoints ed effettuare una esecuzione del codice step-by-step. Per questo Arduino ha a disposizione dei comandi per abilitare la porta seriale (comunicazione lato board via USART) per inviare stringhe, caratteri di controllo oppure risultati di elaborazione sullo schermo del PC. La comunicazione seriale è abilitata con le istruzioni Wiring Serial.Begin(baudrate) e istruzioni di scrittura come Serial.println(stringa,formato). Il Monitor Seriale viene aperto nell’IDE attraverso l’icona Si può affermare dunque il Serial Monitor è il mezzo built-in di Arduino equivalente alla libreria SerialConsole.h e del convertitore PL2303 usato per il debug della Flex. Per importare il progetto esistente di Marvin è sufficiente aprire il file Marvin.ino (File > Open) ed effettuare l’upload sulla Board attraverso il comando Upload dell’IDE. 3.5. Vinculum-II IDE v1.4.4 FTDI mette a disposizione una serie di firmware precompilati per alcune semplici applicazioni realizzabili con il chip Vinculum-II; per applicazioni più complesse è necessario realizzare un proprio firmware che deve essere programmato nel chip. A tal fine FTDI ha creato il Vinculum-II Toolchain, un insieme di strumenti software necessari per la compilazione e lo sviluppo di applicazioni per il VNC2, che includono: un compilatore C, assembler, linker, debugger e un IDE. Lo sviluppo di applicazioni per il VNC2 è facilitato dall’utilizzo di kernel, driver di dispositivo e librerie di runtime fornite nel Vinculum-II Toolchain. I vari strumenti della toolchain sono delle applicazioni console, e come tali vengono integrate nell’IDE. 32 Capitolo 3. Ambienti di sviluppo Figura 3.13.: Vinculum IDE toolchain Il Vinculum-II IDE è il software utilizzato nello sviluppo delle applicazioni per il VNC2. Gestisce i file di progetto e permette di creare l’output binario dell’applicazione usando gli strumenti integrati nella toolchain: • il compilatore VinC; • l’assembler VinAsm; • il linker VinL; • il debugger VinDbg; Figura 3.14.: Architettura Vinculum-II La programmazione del firmware per il VNC2 avviene utilizzando il linguaggio C; l’architettura del firmware è composta da tre livelli: • il VOS Kernel, responsabile della gestione delle risorse hardware, degli interrupt e dello scheduling dei thread; 33 Capitolo 3. Ambienti di sviluppo • i driver FTDI, forniscono un’interfaccia di programmazione per il controllo delle risorse hardware. • le librerie FTDI, che mettono a disposizione un insieme di funzioni C per operazioni comuni di input/output e gestione dinamica della memoria. IL VNC2 RTOS (VOS) kernel è un sistema operativo preemptive, multi-tasking, basato su priorità; esegue i task secondo l’ordine deciso dallo scheduler sulle priorità di ognuno; inoltre fornisce anche meccanismi di sincronizzazione tra i task come semafori e sezioni critiche. Il C non è l’unico linguaggio con cui è possibile programmare il Vinculum II. Infatti esiste anche un insieme di API semplificate chiamate Vinco libraries che sono inspirate al linguaggio Wiring - simile a quello usato per programmare Arduino - che se da un lato semplificano l’accesso alla piattaforma dall’altra non permettono di usare appieno tutte le funzioni del RTOS VOS. L’installazione di questo IDE è molto semplice ma essendo stato escluso dal progetto l’utilizzo del Vinculum-II, non si approfondirà l’argomento. Si rende presente comunque che il Vinculum IDE ha strumenti ottimi per il debugging del codice attraverso i quali è possibile monitorare quali task sono in esecuzione ed il loro stato, è possibile leggere il valore di tutte le variabili e effettuare un run del codice step-by-step per mezzo della definizione di un massimo di 3 breakpoints. Figura 3.15.: IDE del Vinculum-II 3.6. RealTerm v2.0 RealTerm è uno dei tanti programmi freeware per leggere dati da porta seriale. Essenzialmente esso è stato scelto per la sua facilità di installazione ed per le molte opzioni che mette a disposizione; viene utilizzato come strumento di debug per la Flex. Le impostazioni per avere una corretta configurazione sono: • Nel tab Display spuntare “new line mode” in maniera tale che il programma processi la sequenza di escape \n, inviata attraverso la libreria serialConsole.h, come un “a capo” . • Nel tab Port impostare come porta seriale il convertitore Prolific e come Baud 115200, che è il baud rate di comunicazione della Flex. Fatto questo è possibile ricevere ed inviare correttamente i dati utilizzato il tab “Send”. 34 Capitolo 3. Ambienti di sviluppo Figura 3.16.: Ricezione dati dalla scheda Flex Un esempio dell’utilizzo della libreria in combinazione con RealTerm può essere di questo genere: La libreria permette di inviare stringhe al PC senza aspettarsi una risposta attraverso la sintassi S en dS tr i ng Re pl y ( " \ nTesto \ n " ,PC , NoReply , false , false , UARTx ) ; oppure SendNumReply (1234 , PC , NoReply , , false , false , UARTx ) ; È anche possibile bloccare l’esecuzione del codice sulla Flex (come se si avessero dei breakpoints) finché non vengono inviati un numero di caratteri sufficienti dal PC verso la board attraverso l’istruzione S en dS tr i ng Re pl y ( " \ nTesto con risposta \ n " ,PC , Reply ,& reply [0] ,1 , UARTx ) ; oppure SendNumReply (1234 , PC , Reply ,& reply [0] ,1 , UARTx ) ; Figura 3.17.: Invio dati alla scheda Flex che inviano rispettivamente una stringa o un numero e si aspettano 1 carattere di risposta che la Flex salverà nell’array reply partendo dalla posizione 0. 35 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema Il sistema da realizzare è composto da diversi componenti hardware e software per questo la realizzazione è stata condotta attraverso layer di funzionalità. Ogni layer deve essere in grado di fornire interfacce ai livelli sovrastanti ed essere descritto mediante input ed output. Il capitolo inizia con una panoramica sull’analisi dei requisiti del sistema, ovvero cosa deve fare il sistema per poi spiegare le funzionalità dei vari layer realizzati. 4.1. Analisi dei Requisiti Vi sono due diversi tipi di requisiti sul sistema, quelli funzionali – cioè necessari per rispondere al quesito riguardante cosa il robot “deve fare” – e i requisiti aggiuntivi o non funzionali, che forniscono vincoli addizionali, permettendo un ulteriore sviluppo del progetto nel tempo. I requisiti sono schematizzati qui di seguito. Requisiti funzionali: • Dotare il robot dei movimenti spaziali tramite API portabili • Garantire un canale di comunicazione con ogni dispositivo Android • Garantire una comunicazione affidabile con Arduino • Implementare algoritmi di visione e decisione del movimento su dispositivo mobili dotati di Android OS • Aggiungere il motore che permette uno snodo “di testa” Requisiti aggiuntivi: • Gestire l’elaborazione della scheda Flex Full attraverso l’RTOS ERIKA in maniera intelligente • Migliorare il programma di visione attraverso un semplice algoritmo di ricerca volti • Sostituire il Motion Processor con motori controllabili • Implementare un controllore PID • Rendere più accessibile la scheda Flex creando API per la gestione ad hoc delle periferiche sulla falsariga di Wiring • Comunicazione wifi tramite Android OS 36 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema 4.2. Requisiti Funzionali 4.2.1. Dotare il robot dei movimenti spaziali tramite API portabili Il sistema deve essere in grado di controllare i movimenti del robot; ricevuto in ingresso il comando che identifica il movimento da eseguire, dovrà azionare i motori per muovere opportunamente il robot. I comandi che il sistema deve gestire sono: • avanti • indietro • gira a destra • gira a sinistra • stop • muovi la testa verso l’alto • muovi la testa verso il basso In un primo momento si è implementato questa caratteristica così come era stato fatto su AndroEEBot, cioè utilizzando il Motion Processor ma, come precedentemente anticipato, questa versione è stata modificata per raggiungere il requisito aggiuntivo relativo alla sostituzione del Motion Processor. E’ stato necessario pensare all’elettronica di alimentazione e lo studio dei servomotori e la scrittura della libreria Servo.h per permettere il controllo del robot attraverso comandi inviati dallo smartphone. 4.2.2. Garantire un canale di comunicazione con ogni dispositivo Android Questo requisito è stato raggiunto attraverso l’inserimento nel sistema di Arduino poiché compatibile con la libreria Microbridge ADB, che è risultata la libreria migliore per gestire una comunicazione affidabile con qualsiasi smartphone. A livello applicativo sono state incluse in Android le classi: • AbstractServerListener • Client • ServerListener • Server Per gestire la comunicazione con Android, inoltre, da parte di Arduino bisogna includere gli header SPI.h, Adb.h. Nel setup() bisogna inizializzare l’ADB subsystem così: ADB :: init () ; connection = ADB :: addConnection ( " tcp :4568 " , true , ad b Ev en tH a nd le r ) ; Il polling dell’ADB deve essere inserito in loop() per ricevere i dati ad ogni ciclo di elaborazione, con l’istruzione ADB::poll(), che richiamerà l’unica funzione implementata, tra quelle disponibili nella libreria, cioè void a db Ev en tH a nd le r ( Connection * connection , adb_eventType event , uint16_t length , uint8_t * data ) filtrando solo gli eventi di tipo ADB_CONNECTION_RECEIVE. 37 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema 4.2.3. Garantire una comunicazione affidabile con Arduino Il requisito è raggiunto attraverso l’implementazione lato Flex della libreria Arduino.h che contiene API per richiedere dati al microcontrollore, mentre lato Arduino la verifica di ricezione di informazioni dalla Flex è ottenuta mediante l’API serialEvent() che viene richiamata automaticamente del sistema Arduino ad ogni ciclo di loop() e verifica quali tipi di dato sono presenti nel registro. La ricezione avviene dalla porta seriale 1 a cui la Flex è fisicamente collegata. 4.2.4. Implementare algoritmi di visione su Android Il requisito relativo all’implementazione di algoritmi di visione e decisione del movimento su dispositivo Android è stato soddisfatto ereditando quasi in toto l’applicazione dal precedente progetto. L’applicazione utilizza l’algoritmo detectMultiScale() tramite il quale è possibile identificare, dato un frame, un oggetto corrispondente ad alcune caratteristiche descritte nel file “cascade”, scritto in formato XML. Nel progetto è stato utilizzato come cascade lbpcascade_frontalface.xml, permettendo il riconoscimento di volti in posizione frontale attraverso l’algoritmo di face detection Local Binary Pattern (LBP). 4.2.5. Aggiungere il motore che permette uno snodo “di testa” AndroEEBot effettuava movimenti solo sull’asse X e Z (vedi figura seguente), con Marvin si è reso possibile il movimento anche sull’asse Y attraverso l’aggiunta del servomotore HS-485HB e lo studio ed implementazione del modulo PWM che ha portato alla scrittura delle librerie PWM.h e Servo.h. Figura 4.1.: Assi di Marvin 4.3. Requisiti Aggiuntivi I requisiti aggiuntivi rappresentano una sorta di sperimentazione e una dimostrazione di come sia possibile evolvere e migliorare un’idea di partenza attraverso passi incrementali. Definiti gli obiettivi principali pian piano ci si è resi conto di come migliorare e aggiungere diversi aspetti al sistema; questi requisiti risultano essere degli ulteriori vincoli. 38 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema 4.3.1. Elaborazione tramite RTOS ERIKA Il multithreading e lo sviluppo di thread concorrenti/collaborativi sono possibili anche su microcontrollore attraverso l’uso ottimale di ERIKA, seppur l’applicazione Marvin e AndroEEBot ne fanno un utilizzo semplice, istanziando un thread periodico. Il lavoro si è orientato principalmente nel capire come utilizzare al meglio l’RTOS e successivamente si è ipotizzata una suddivisione dell’applicazione in thread secondo una politica “Produttore-Consumatore”. 4.3.2. Migliorare il programma di visione AndroEEBot necessitava di un volto che fosse presente nella scena per applicare l’inseguimento. Estendendo questo concetto ad un contesto più reale risulta necessario che il robot debba effettuare una, seppur semplice, ricerca di eventuali volti intorno ad esso. Il semplice algoritmo implementato nell’applicazione Android permette di definire due intervalli di tempo, regolabili in fase di programmazione: startBehaviour e stopBehaviour che indicano un numero di cicli entro i quali fermare o mantenere attiva la ricerca di un volto. Il comportamento implementato consiste nel far ruotare su se stesso il robot; il verso di rotazione dipende dall’ultimo spostamento che è stato registrato. Se il volto prima di sparire dal frame si è spostato verso destra, ad esempio, il robot girerà verso destra nella speranza di trovare da quella parte il volto. Il comportamento di ricerca funziona anche nel caso in cui all’avvio del robot nessun volto dominante è stato mai inquadrato nella scena, allora si impone un verso di rotazione a priori che nel caso di questa tesi è destra. Figura 4.2.: Flowchart per l’algoritmo di controllo del movimento 4.3.3. Sostituire il Motion Processor Questo requisito è di fondamentale importanza per lo sviluppo futuro, il controllo e la personalizzazione del progetto Marvin. Come spiegato in precedenza, seppure il Motion Processor sia un’ottima soluzione tecnica, esso limitano l’aspetto real-time dell’applicazione delegando ad un altro “processore” il compito di eseguire i comandi di controllo. La soluzione è stata quella di inserire i servomotori DFRobotic ma in futuro, attraverso la libreria PWM.h, l’API digitalWrite() e una circuiteria composta da un ponte H, sarà possibile sostituire i servo con motori DC più performanti. 39 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema 4.3.4. Implementare un controllore PID La Meccanica, l’Elettronica e l’Informatica sono le scienze su cui l’Automazione si basa; avendo seguito lo sviluppo del robot in tutte queste fasi è sembrato naturale cercare di attuare un Controllo per lo spostamento nello spazio di Marvin. Non avendo a disposizione il modello matematico del robot, l’unico controllo applicabile è stato quello PID realizzato mediante l’implementazione dell’azione proporzionale prima, e poi dell’azione proporzionale ed integratrice poi. Rendere più accessibile la scheda Flex creando API per la gestione ad hoc delle periferiche sulla falsariga di Wiring: lo sviluppo del progetto in questo senso è stato dettato sia come evoluzione naturale dello studio della scheda Flex e del microcontrollore dsPIC, sia come “emulazione” di Wiring: il linguaggio di programmazione embedded di Arduino. API semplici sono la forza del progetto Arduino poiché rendono accessibile la scheda di sviluppo in poco tempo. 4.3.5. Comunicazione wifi tramite Android Questa caratteristica disponibile già per AndroEEBot è stata mantenuta, ma non testata, anche in Marvin; essa permette la comunicazione con un applicazione ad hoc su un PC attraverso la rete wifi di cui ogni smartphone è dotato. 4.4. Architettura e funzionamento del sistema L’architettura del sistema è divisa in 4 livelli dal più basso al più alto: • Attuazione: è composto dall’elettronica, della meccanica e da tutti gli attuatori del sistema, ovvero dai 3 servomotori • Controllo: comprende l’ MCU dsPIC che gestisce il movimento del robot, l’interfacciamento con la sensoristica e il sistema operativo Real Time ERIKA; • Comunicazione: composto da Arduino e dalla libreria Microbridge ADB, questo livello funge anche da espansione del sistema. Infatti è possibile sfruttare Arduino come una seconda logica di controllo da affiancare alla Flex per controllare l’attuazione di comandi verso il mondo esterno • Comando: è il livello rappresentato dallo smartphone che attraverso applicazioni Java permette di controllare i livelli sottostanti e comunicare con PC attraverso wifi. Uno schema grafico dell’architettura è visibile in fig.4.3 Il ciclo di funzionamento di Marvin è composto da diverse fasi di elaborazione gestite indipendentemente da i vari livelli architetturali sopra descritti, l’elaborazione parte dal livello di Comando che analizzando i frame video decide quali spostamenti imporre ai livelli sottostanti e/o inviare i dati tramite wifi al PC. I dati sono inviati al layer di comunicazione in maniera asincrona, proprio come se il livello superiore fosse un enorme sensore; i comandi di spostamento, espressi come singolo byte o insieme di essi, vengono inviati tramite microbridge ADB ad Arduino che li conserva in una variabile globale, intanto esso resta in attesa di interrupt sulla Serial 1 (è opportuno ricordare che Arduino ha 4 porte seriali USART) oppure potrebbe elaborare altri dati provenienti da un eventuale sensoristica. Indipendentemente dai livelli superiori sulla board Flex Full , dopo l’inizializzazione delle periferiche, ogni 100 ms viene mandato in esecuzione da ERIKA un task periodico che richiede i dati via USART 1 ad Arduino. La risposta che proviene da quest’ultimo è l’ultimo dato di movimento che Android ha inviato al layer di comunicazione in modo asincrono. La Flex quindi elabora questi dati attivando, in base al 40 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema tipo di applicazione presente sul layer di comando il controllo PID oppure la semplice generazione dei corrispondenti comandi di attuazione per i servomotori. La Flex durante un suo ciclo di elaborazione può anche ricevere dati dalla sensoristica o inviare, tramite USART 2, informazioni di debug verso Arduino o PC. Figura 4.3.: L’architettura a 4 livelli di Marvin 4.5. Protocolli di comunicazione Data la complessità del sistema, è fondamentale definire quali sono i dati che vengono scambiati tra i vari componenti e le interfacce utilizzate. Il sistema è stato concepito per funzionare in due differenti modalità, spostamento “manuale” e PID: è necessario, dunque, distinguere queste due istanze, creando due opportuni protocolli di comunicazione. 4.5.1. Android Esistono due versioni dell’applicazione Android e sono “Marvin Grid View” e “Marvin PID”. “Marvin Grid View” calcola gli spostamenti in base alla posizione del volto rispetto a delle aree predefinite nel frame. La definizione predefinita delle aree è detta Grid View, e divide il frame in cinque aree distinte; una centrale, due aree laterali – destra e sinistra –, un’area superiore ed una inferiore. La 41 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema presenza di un volto in ciascuna di queste aree induce la generazione di un particolare pacchetto che è formattato come segue: 1 byte: MODE OxOF 1 byte: DIRECTION OxOn Tabella 4.1.: Formato del pacchetto trasmesso dall’applicazione Android “Marvin Grid View” MODE serve per notificare ad Arduino quale applicazione è in esecuzione su Android. Il byte associato all’applicazione “Grid View” è 0x0F. DIRECTION, invece contiene un byte di spostamento calcolato da Android e può assumere i seguenti valori: • 0x00 STOP • 0x01 AVANTI • 0x02 INDIETRO • 0x03 GIRA A DESTRA • 0x04 GIRA A SINISTRA • 0x05 SNODO DI TESTA VERSO L’ALTO • 0x06 SNODO DI TESTA VERSO IL BASSO La seconda applicazione, “Marvin PID”, è invece pensata per sfruttare il PID e perciò utilizza una diversa formattazione del pacchetto: 1 byte: MODE OxOC sX errorX 15 byte: ERROR sY errorY sZ errorZ Tabella 4.2.: Formato del pacchetto trasmesso dall’applicazione Android “Marvin PID” Nell’applicazione “Marvin PID” MODE è pari a 0x0C. I successivi 15 byte, ERROR, possono essere interpretati a blocchi di 5 byte. Ogni blocco contiene: • sK: 1 byte che contiene il segno dell’errore sull’asse K; • errorK: 4 byte che forniscono, quando interpretati, un float che rappresenta l’errore in valore assoluto sull’asse K. 4.5.2. Arduino Nel layer di comunicazione i due differenti comportamenti di controllo sono contemporaneamente presenti e il byte MODE inviato da Android determina quale comportamento implementare. I comandi che Android invia vengono captati da Arduino attraverso void adbEventHandler(), che filtra solo gli eventi di tipo ADB_CONNECTION_RECEIVE come spiegato in precedenza; in particolare il byte MODE viene salvato nella variabile globale MODE e riutilizzato in fase di risposta alla Flex. I dati inviati da Android sono contenuti nell’array data. if ( event == A D B _ C O N N E C T I O N _ R E C E I V E ) { MODE = data [0]; /* The Application is using the Grid View */ if ( MODE != 0 x0C ) 42 Capitolo 4. Analisi dei Requisiti e Progettazione del Sistema { // data in position data [1] contains robot ’s direction direction = data [1]; } /* The application is using the PID View */ else for ( i =0; i <15; i ++) directions [ i ]= data [ i +1]; } Nella funzione void serialEvent1(), associata agli interrupt della Serial1, il seguente frammento di codice Arduino verifica che la Flex abbia richiesto l’invio dei dati di direzione, e risponde in base al MODE precedentemente salvato. Arduino antepone al pacchetto un byte di controllo per l’integrità dei dati. if ( Serial1 . read () == 0 x7F ) { Serial1 . write (0 x0A ) ; /* The Application is using the PID View */ if ( MODE != 0 x0C ) Serial1 . write (( byte ) direction ) ; /* The application is using the Grid View */ else for ( j =0; j <15; j ++) Serial1 . write ( directions [ j ]) ; } 1 byte: MODE OxOF 1 byte: DIRECTION direction Tabella 4.3.: Formato del pacchetto trasmesso da Arduino nell’applicazione “Marvin Grid View” 1 byte: CTRL OxOA 15 byte: ERROR directions[j] Tabella 4.4.: Formato del pacchetto trasmesso da Arduino nell’applicazione “Marvin PID” 4.5.3. Flex Full Board La Flex Full, come detto, gestisce la comunicazione percependo Arduino e Android come un unico sensore a cui richiedere, ogni 100 ms, i dati di spostamento attraverso il task periodico denominato TaskSend. Sulla Flex esistono due diverse implementazioni del firmware a seconda di quale applicazione Android si sta utilizzando, poiché varia il numero di dati da richiedere ad Arduino. La richiesta viene fatta usando la funzione Arduinio_Data_Request() contenuta in Arduino.h. Per l’applicazione “Grid View”, la funzione è: A r d u i n o _ D a t a _ R e q u e s t (& arduinoData [0] , 2 , UART1 ) ; Invece, per l’applicazione “PID”, la funzione è: A r d u i n o _ D a t a _ R e q u e s t (& arduinoData [0] , 16 , UART1 ) ; Il byte di controllo inviato da Arduino viene verificato in entrambi i casi dalla Flex e se esso corrisponde al carattere di controllo atteso – 0x0A – allora esso farà illuminare il led Arancione (pin 25). if ( arduinoData [0] == 0 x0A ) ledBlink (25 ,1 ,100) ; 43 Capitolo 5. Implementazione del Software Al termine della fase di progettazione e definizione del problema, dei requisiti e di vincoli si passa alla fase di sperimentazione/studio e programmazione sia dei componenti embedded sia dell’applicazione ad alto livello. Apre il capitolo una descrizione delle librerie e delle API scritte per la Flex. Si passa poi alle caratteristiche hardware sfruttate; segue una descrizione di ERIKA Enterprise con uno sguardo alle sue peculiarità. In seguito viene descritto il firmware per Arduino. L’implementazione per smartphone chiude il capitolo. 5.1. Librerie Flex Full Il lavoro maggiore si è concentrato sullo studio della scheda Flex Full partendo dal suo nucleo il dsPIC. Come già detto si può fare a meno dell’architettura modulare - soprattutto se la parte elettronica da realizzare è ridotta all’osso – tutte le funzionalità sono già presenti sul microcontrollore e la flex esporta tutti i 100 pin all’esterno. L’obiettivo principale è stato quello di semplificare l’accesso a funzionalità ricorrenti come le porte USART di comunicazione, il PWM, il comando servomotori e motori DC e dotare la scheda di strumenti di debug a run time. Le librerie realizzate ed utilizzabili senza troppe difficoltà, in quanto poco dipendenti dall’applicazione Marvin, sono descritte qui di seguito. 5.1.1. Libreria EEuart.h // - - - - - - - - - - - - - - - - - Public functions - - - - - - - - - - - - - - void ee_usb_init () void ee_usb_write ( BYTE * ch , BYTE len , BYTE UART ) void ee_usb_read ( BYTE * data , BYTE UART ) void e e_ us b_ re a d_ le n ( BYTE * data , BYTE len , BYTE UART ) // - - - - - - - - - - - - - - - - - Internal functions - - - - - - - - - - - - - - void EE_UART1_Init ( EE_UINT32 baud , EE_UINT16 format , EE_UINT16 mode ) EE_INT8 EE_UART1_Send ( unsigned char data ) EE_INT8 E E_ U A R T 1 _ R e c ei v e ( unsigned char * data ) void EE_UART2_Init ( EE_UINT32 baud , EE_UINT16 format , EE_UINT16 mode ) EE_INT8 EE_UART2_Send ( unsigned char data ) EE_INT8 E E_ U A R T 2 _ R e c ei v e ( unsigned char * data ) Con questa libreria è possibile inviare dati attraverso le due porte USART di cui la Flex Full è dotata. ee_usb_init() attiva ed imposta il modulo hardware della USART1 e USART2 con baudrate a 115200 baud. Attraverso ee_usb_write() vengono inviati tutti i byte contenuti nel vettore data attraverso la porta USART selezionata (1 o 2). ee_usb_read() e ee_usb_read_len() permettono di leggere rispettivamente un byte o len byte e salvarli nel vettore data. 44 Capitolo 5. Implementazione del Software Queste letture sono “bloccanti” cioè la macchina resta bloccata su queste funzioni finché non riceve esattamente len byte. L’implementazione delle API pubbliche viene fatta attraverso le chiamate alle funzioni private che implementano a livello hardware la lettura/scrittura dei byte nei registri opportuni. 5.1.2. Libreria Arduino.h void A r d u i n o _ D a t a _ R e q u e s t ( BYTE reply [] , BYTE len , BYTE UART ) float c o n v e r t 4 B y t e T O 1 F l o a t ( EE_INT32 byteArray []) ; Arduino_Data_Request() permette di leggere len byte da Arduino e li salva nell’array reply. Convert4ByteTO1Float() converte un array formato da 4 byte nel corrispondente numero float; è utilizzata per passare da Android alla Flex un numero in float sotto forma di byte e poi ricostruirlo sulla Flex. 5.1.3. Libreria Utility.h void mydelay ( int ONEMILLION ) ; void pinMode ( BYTE pin , BYTE mode ) ; void digitalWrite ( BYTE pin , BYTE voltage ) ; void ledBlink ( BYTE pin , BYTE times , int delay ) ; float potenza ( float base , float esponente ) ; double map ( double value , double in_min , double in_max , double out_min , double out_max ) ; mydelay() è un semplice delay, utile soprattutto in fase di debug, che blocca la macchina per diversi cicli di clock; con valori di 2000-3000 si riesce a bloccare la macchina per 1-2 secondi, ma non garantisce la scansione precisa del tempo. pinMode(), come su Arduino, attiva un determinato pin come input o output attraverso il parametro mode, ogni pin sulla Flex è multiplexato, cioè può svolgere diverse funzionalità. Quindi solo alcuni pin possono essere dati in ingresso a questa funzione e sono tutti quelli di PORTB [pin 20-27] e 2 di PORTA [pin 91-92]. digitalWrite() permette di cambiare lo stato logico di un pin precedentemente attivato con pinMode() ponendolo HIGH o LOW, questa funzione può essere usata per accendere e spegnere un led oppure per inviare segnali a logiche elettroniche di controllo esterne (es. flip-flop). ledBlink() è appositamente pensata per far “blinkare” un led impostando il pin a cui esso è associato, il numero di blink richiesti e il tempo di blinking di solito tra i 100 e i 1000 ms, funziona sui pin attivabili con pinMode(). potenza() effettua l’elevamento a potenza anche con esponenti negativi, è stata necessaria implementarla poiché la libreria math.h della Flex non restituisce risultati corretti con esponenti negativi. map() permette di effettuare un mapping di una variabile che varia in un insieme di valori A ad un altro insieme di valori B. 5.1.4. Libreria serialConsole.h void Se n dS tr in g Re pl y ( char string [] , BYTE ArduinoDebug , BYTE WithReply , BYTE reply [] , BYTE len , BYTE UART ) ; void SendNumReply ( unsigned long int number , BYTE ArduinoDebug , BYTE WithReply , BYTE reply [] , BYTE len , BYTE UART ) ; È la libreria che permette di inviare stringhe e numeri tramite porta USART. Le API sono dotate di diversi parametri ed possibile inviare solo dati oppure bloccare la macchina in attesa di una risposta attraverso il byte WithReply impostato a true e definendo quanti byte leggere prima di sbloccarsi e dove salvarli. Si può scegliere anche se effettuare debug tramite il serial Monitor di Arduino utilizzando la costante “Arduino” oppure “PC” per debug con il convertitore. 45 Capitolo 5. Implementazione del Software Se si comunica con Arduino si deve impostare il byte UART a UART1 mentre se si comunica con il PC impostare il byte come UART2. 5.1.5. Libreria Servo.h void void void void void void void S e r v o M o d u l e _ i n i t () ; S er vo Mo to r _i ni t ( BYTE motor ) ; Servo_Neutral ( BYTE motor ) ; Servo_plus90 ( BYTE motor ) ; Servo_minus90 ( BYTE motor ) ; S e r v o _ S e t _ P o s i t i o n ( BYTE motor , float hightime , float period ) ; Servo_Move ( BYTE motor , float increment , float period ) ; La libreria è basata sulla libreria PWM.h. Queste API sono delle funzioni pubbliche della PWM ma i due insieme di API sono separati perché potrebbe essere necessario utilizzare la PWM non solo per utilizzare i motori, ma anche per altre periferiche fisiche ad esempio variare la luminosità di un led. ServoModule_init() è la prima funzione da chiamare per attivare il modulo hardware dei servomotori al modulo hardware sono associati ben 8 uscite PWM. In Marvin ne vengono utilizzate solo 3 e il periodo del PWM è impostato di default a 20 ms. ServoMotor_init() dando le costanti HEAD, DX o SX attiva l’uscita PWM corrispondente che fa parte dell’insieme degli 8 PWM attivabili dal modulo PWM inizializzato. Servo_Neutral() in base al PWM scelto imposta il duty cycle a 1.5 ms questo significa che i motori associati a tale uscita PWM ritornano in posizione neutra, i motore di testa quindi si pone all’angolo 0° mentre i motori di propulsione smettono di girare. Servo_plus90() e Servo_minus90() sono funzioni pensate per il motore di testa e impostano rispettivamente il duty cycle a 2.4 ms e 0.6 ms che corrispondono a +90 e -90 gradi. Se applicate ai motori di propulsione questa API ha l’effetto di far ruotare in avanti o indietro i motori alla massima velocità. Servo_Set_Position() è l’API migliore per controllare la posizione dello snodo di testa, infatti dato un valore di hightime (T-on) esso si sposterà precisamente all’angolo corrispondente. E’ anche l’API più utile per controllare i motori di propulsione in quanto specificando 1.5 < T-on < 2.4 si controlla la velocità di rotazione dei servi di propulsione in senso orario mentre con 0.6 < T-on < 1.5 si controlla la velocità di rotazione dei servi di propulsione in senso antiorario. Servo_Move() è l’API ideale per muovere con precisione il motore di testa infatti questa funzione ricorda in automatico la posizione precedente del motore e permette di dare un incremento positivo o negativo allo snodo di testa così da effettuare micromovimenti partendo da una posizione fissata. 5.1.6. Libreria PWM.h void void void void void PWM_module_on () ; Enable_PWM ( int PWM ) ; P W M _ S e t _ D u t y _ C y c l e ( int PWM , float hightime , float period ) ; P W M _ M o d u l e _ c l o s e () ; Disable_PWM ( int PWM ) ; Questa libreria controlla il PWM hardware. PWM_module_on() attiva il modulo PWM impostando il periodo dell’onda quadra a 20 ms. Enable_PWM() attiva una particolare uscita PWM da 1 a 3 del modulo PWM PWM_Set_Duty_Cycle() imposta il registro P1DCx corrispondente ad una precisa uscita PWM PWM_Module_close() disabilita il modulo PWM spegnendo tutte le uscite PWM Disable_PWM() disabilita un preciso output PWM 46 Capitolo 5. Implementazione del Software 5.1.7. Libreria PID.h float float float float float float PI_pid_Y ( float error ) P_pid_Y ( float error ) PI_pid_Z ( float error ) P_pid_Z ( float error ) PI_pid_X ( float error ) P_pid_X ( float error ) Contiene l’implementazione del controllore PID e le costanti impostate per i 3 assi di movimento: • Y associato al motore di testa • Z associato al movimento avanti/dietro dei motori di propulsione • X associato al movimento destra/sinistra dei motori di propulsione Questa libreria utilizza la libreria Servo.h ma l’utilizzo di questa maschera il comando diretto dei motori. Per utilizzare questa libreria bisogna utilizzare anche l’applicazione Android corrispondente. Le API sono sempre le stesse sui tre assi e sono: • PI_pid_K() utilizza il controllore proporzionale ed integrativo sull’asse K • P_pid_K() utilizza il controllore proporzionale sull’asse K 5.2. Componenti Hardware Flex Full 5.2.1. Oscillatori Un oscillatore è un circuito elettronico che genera forme d’onda di frequenza, forma e ampiezza di molteplici tipi senza un segnale di ingresso. Gli oscillatori nella loro vastità sono impiegati in innumerevoli applicazioni che spaziano dalla temporizzazione di circuiti digitali e non, alla generazione di portanti per le telecomunicazioni, agli strumenti elettromedicali, ecc. Gli oscillatori possono dividersi in due principali categorie: • Armonici o sinusoidali (o, più propriamente, quasi-sinusoidali); • A rilassamento o bloccati; Gli oscillatori armonici producono un segnale di andamento sinusoidale (o quanto più possibile prossimo ad esso). Essenzialmente si tratta di un amplificatore in cui l’uscita è riportata all’ingresso con una retroazione positiva, attraverso un filtro passa-banda stretto. Quando il circuito è acceso, l’amplificatore produce inevitabilmente in uscita del rumore. Il circuito di reazione riporta in ingresso le componenti del rumore di frequenza determinata, le quali vengono amplificate. Il ciclo si ripete fino al raggiungimento del regime di funzionamento. Essi sono suddivisibili in tre principali categorie in base agli elementi circuitali utilizzati che cono RC, LC e Quarzati. Il dsPIC utilizza un oscillatore RC, composto esclusivamente da resistori e condensatori. Il dsPIC integra due oscillatori uno principale, FRC – Fast RC – a frequenza di 7.37 MHz e un oscillatore secondario LP – Low Power – a 37.768 Khz e sono previste 7 diverse configurazioni per gestirli via software: • FRC Oscillator • FRC Oscillator con PLL • Primary Oscillator (XT, HS, EC) 47 Capitolo 5. Implementazione del Software • Primary Oscillator con PLL • Secondary Oscillator (LP) • LPRC Oscillator • FRC Oscillator con Postscaler La frequenza dell’FRC può essere modificata via software attraverso il circuito PLL (Phase Loop Locker) che ne incrementa di molto la frequenza i base portandola al massimo fino a 80 MHz. Il Primary Oscillator può essere: • XT: Risonatore in Cristallo e Ceramica, genera armoniche da 3 MHz a 10 MHz esso è connesso ai pin OSC1 e OSC2 • HS: Cristallo ad alta velocità (High Speed) con frequenze di clock generabili da 10 MHz a 40 MHz • EC: Identifica un segnale di clock dato da un oscillatore esterno che può variare nel range di frequenze 0.8 – 64 MHz L’oscillatore secondario (LP) è studiato per bassi regimi di potenza e per questo usa un cristallo che oscilla a frequenze sensibilmente più basse rispetto a l’FRC; esso è collegato ai pin SOSCI e SOSCO. LPRC – Low Power RC – è una diversa modalità di impiego dell’LP ed è usato come timer per il Watchdog1 e FSCM (Fail Safe Clock Monitor). La scelta della modalità è fatta attraverso i “Configuration Bits” FOSCSEL<2:0> e FOSC<1:0> in una situazione di Power-on o Reset. Per ottenere la frequenza di funzionamento del dispositivo Fc , la frequenza di output dell’oscillatore (oppure oscillatore + PLL) Fosc va divisa per 2: Fosc (5.1) 2 Come detto, sia FRC sia LP possono utilizzare PLL per generare frequenze maggiori ma visto l’utilizzo che si fa del dsPIC, in questa tesi si adopererà solo l’FRC e si indicherà con FOSC il suo output. Fin indica la frequenza d’ingresso del PLL. Essa è divisa da un Prescaler, chiamato N1 che effettua divisioni di 2, 3, ..., 33 prima che esso diventi input del PLL; a questo punto esso è indicato com Vco . Per impostare il fattore del prescaler N 1 bisogna impostare i bits PLLPRE del registro CLKDIV. Il PLL Feedback divisor PLLDIV del registro PLLFBD è denotato con M e rappresenta il fattore per il quale l’ingresso del PLL, Vco , viene moltiplicato. Infine l’output del PLL è diviso ancora di un fattore deciso dal Postscaler, chiamato N 2, il cui valore è impostato nei bit PLLPOST del registro CLKDIV. N 2 può essere 2, 4 oppure 8. Per cui, Fosc è dato dalla formula: Fc = Fosc = Fin · 1 letteralmente: M N1 · N2 (5.2) cane da guardia. È un sistema di temporizzazione hardware che permette alla CPU la rilevazione di un loop infinito o di una situazione di deadlock,tale rilevazione può consentire di prendere dei provvedimenti per correggere la situazione, generalmente effettuando un reset del sistema 48 Capitolo 5. Implementazione del Software Figura 5.1.: Determinazione di Fosc La frequenza più importante è ovviamente Fc , ovvero quella di funzionamento del dispositivo. Nel presente lavoro essa è impostata al massimo valore disponibile, 40 MHz. Per ottenere questo valore è necessario che Fosc sia di 80 MHz per cui utilizzo: • XT con PLL in questo modo ottengo circa 10 MHz • N 1 = 2, cioè CLKDIVbits.PLLPRE = 0, questo porta la frequenza a 5 MHz • M = 32, cioè PLLFBDbits.PLLDIV=0x1E (78 in decimale), ottenendo una frequenza di 160 MHz • N 2 = 2, cioè CLKDIVbits.PLLPOST = 0 ottenendo finalmente Fosc = 80 MHz, da cui Fc = MHz 80 2 =40 5.2.2. USART È la periferica per la comunicazione seriale asincrona e le sue funzioni sono contenute nel file eeuart.h. Le varie funzionalità sono precedute dall’istruzione __INLINE__ al fine di rendere le chiamate più veloci; infatti il compilatore di fronte a questa keyword sostituirà la chiamata alla funzione con il codice corrispondente. La Flex Full possiede due porte USART indipendenti denominate UART1 e UART2 e ogni porta può essere configurata in maniera separata dall’altra. Per le funzionalità necessarie in questa tesi esse sono configurate allo stesso modo, cioè con il protocollo 8-N-1-None ovvero: • 8 Data Bits • Parity None • 1 Stop Bit • Flow Control None 49 Capitolo 5. Implementazione del Software Figura 5.2.: Architettura hardware di una periferica USART Il diagramma semplificato della periferica vede 3 componenti principali: il Baud Rate Generator, in Trasmettitore Asincrono e il Ricevitore Asincrono. L’inizializzazione dei registri della periferica vengono effettuati dalla funzione ee_usb_init() che richiama EE_UART1_Init() e EE_UART2_Init(). L’attivazione della periferica è regolato dal bit UARTEN del registro UxMODE: se esso è pari a 0 la periferica è disattivata, risulta buona norma disabilitare gli interrupt che tale periferica può generare per la ricezione e l’invio di byte attraverso i bit UxRXIE ed UxTXIE del registro IEC0 infine fare una pulizia dei flag degli interrupt dedicati ovviamente alla ricezione e alla trasmissione nel registro IFS0. Importantissimo per stabilire una comunicazione è definire il Baud Rate, che rappresenta il numero di simboli che viene trasmesso in un secondo. Per simbolo non si intende un solo bit ma in insieme di essi; per cui questa misura differisce dalla misura bps cioè bits per secondo. Per la Flex esso è impostato a 115200 baud/s per ottenere questo valore viene utilizzata la seguente formula nel caso di BRGH = 0: Fc −1 16 · DesideredBaud Questa formula è riportata nel codice del microcontrollore in questo modo: U xBRG = (5.3) U1BRG = (2500000 ul / baud ) - 1; // With BRGH = 0 U2BRG = (2500000 ul / baud ) - 1; // With BRGH = 0 dove: Fc 40 · 106 = = 2500000 ⇒ baud = 115000 (5.4) 16 16 Successivamente all’impostazione del Baud Rate è necessario definire i pin di output che assumeranno il carattere di RX e TX. Essi sono stati scelti in accordo con il manuale di riferimento impostando i pin corrispondenti ai bit 2 e 3 per la UART1 e ai bit 4 e 5 per la UART2 sulla porta di I/O digitale PORTF2 . La funzione EE_UART1_Init() ha una struttura molto flessibile permettendo di impostare il Baud Rate desiderato, la modalità di funzionamento (con o senza Flow Control ) e il byteformat per modificare il protocollo 8-N-1. 2 Vedi figura 3 50 Capitolo 5. Implementazione del Software Figura 5.3.: Il protocollo UART Il registro UxRSR è lo shift register3 , che si occupa della ricezione. I dati sono ricevuti sul pin UxRX e sono inviati al Data Recovery Block che opera ad un velocità pari a 16 volte il Baud Rate; dopo che è stato acquisito lo Stop Bit del protocollo seriale, i dati ricevuti nel registro UxRSR sono trasferiti in una coda FIFO. Essa può contenere al massimo 4 word contemporaneamente; se arriva una quinta word e il registro non è stato letto si entrerà in una situazione di buffer overrun, che deve essere gestita via software. Il registro UxTSR invece è lo shift register che si occupa dell’invio dei dati verso il mondo esterno. Esso riceve i dati dal buffer FIFO UxTXREG che viene caricato via software con i dati in uscita. Esso è caricato con nuovi dati se e solo se è stato inviato lo Stop Bit dall’UxTSR. 5.2.3. Modulazione a Larghezza di Impulso (PWM) La modulazione di larghezza di impulso è un tipo di modulazione digitale che permette di ottenere una tensione media variabile, dipendente dal rapporto tra la durata dell’impulso positivo e di quello negativo. Tale modulazione è utilizzata per protocolli di comunicazione in cui l’informazione è codificata sotto forma di durata nel tempo di ciascun impulso. Questa modalità è quella utilizzata dalla logica dei servomotori, logica in cui la durata nel tempo di ciascun impulso determina il comportamento dell’attuatore. La periferica del dsPIC utilizzata per generare la PWM è MCPWM1 che può generare output multipli e sincronizzati rendendo la periferica ottimale per controllare diverse tipologie di motori. Alcune delle features del modulo MCPWM1 sono: • fino ad otto output PWM con quattro generatori di duty cycle • possibilità di attivare o disattivare manualmente le singole uscite PWM • possibilità di cambiare la PWM “on-the-fly” • Dead time generabile via hardware • Diverse modalità di utilizzo: – Single event mode – Edge-aligned mode 3 registro a scorrimento costituito da una catena di celle di memoria ad 1 bit interconnesse tra loro. Ad ogni impulso di clock consentono lo scorrimento dei bit da una cella a quella immediatamente adiacente, al fine di convertire i bit arrivati in forma seriale a quella parallela. 51 Capitolo 5. Implementazione del Software – Center-aligned mode – Center-aligned mode with double updates – Complementary output mode – Independent output mode Il dsPIC ha anche un secondo modulo per il controllo motori tramite PWM, MCPWM2, leggermente diverso rispetto a MCPWM1. Tale modulo non è stato utilizzato nella progettazione di Marvin. L’MCPWM1 è stato utilizzato nella modalità Free Running Mode Edge-aligned. La generazione degli interrupts PWM dipende dalle modalità operative quindi dai bits PTMOD (Time Base Mode) nel registro P1TCON (Time Base Control Register) e dai bits di PTOPS (Time Base Output Postscaler) del registro P1TCON. In modalità Free Running un interrupt è generato quando il registro PWM P1TMR (Time Base Register) viene resettato a 0 a causa del raggiungimento del valore impostato nel registro PTPER. I bits del postscaler sono utilizzati in questa modalità per ridurre la frequenza degli eventi di interrupt. Per generare un’onda rettangolare, oltre al periodo – che definisce ogni quanti secondi il segnale viene generato mediante interrupt – bisogna generare un corretto duty cycle mediante la scrittura dell’apposito valore nel registro P1DCx. I registri di duty cycle sono 4, ognuno legato a una coppia di uscite PWMxH/L. Nel progetto di Marvin sono utilizzati in primi tre registri di duty cycle dell’MCPWM1 cioè P1DC1, P1DC2 e P1DC3. Il valore scritto nel registro permette di generare un’onda PWM con opportuno duty cycle; infatti il segnale PWM è alto all’inizio del periodo, cioè quando P1TMR = 0, dopodiché esso verrà incrementato fino a raggiungere il valore contenuto nel registro P1DCx. Il segnale torna quindi basso e viene generato finché P1TMR non raggiunge il valore di PTPER. A questo punto l’onda PWM è stata completamente generata e il ciclo si ripete. Figura 5.4.: Generazione dell’onda PWM Una caratteristica interessante è la possibilità di cambiare il valore di duty cycle “on-the-fly” impostando il bit IUE = 1 nel registro PWM1CON2. Tale impostazione ha l’effetto di eliminare l’attesa che il registro P1TMR raggiunga il valore di PTPER, e permette immediati aggiornamenti della larghezza dell’onda rettangolare. Abilitando questa feature si va incontro a tre possibili casi: 52 Capitolo 5. Implementazione del Software Figura 5.5.: Modifica del valore di duty cycle “on-the-fly” 1. se l’uscita PWM è attiva nel momento in cui in nuovo valore di duty cycle è scritto nel corrispondente registro ed il nuovo valore è maggiore rispetto al corrente valore del registro P1TMR allora la larghezza dell’onda rettangolare è estesa. 2. se l’uscita PWM è attiva nel momento in cui in nuovo valore di duty cycle è scritto nel corrispondente registro ed il nuovo valore è minore rispetto al corrente valore del registro P1TMR allora la larghezza dell’onda rettangolare è diminuita. 3. se l’uscita PWM non è attiva nel momento in cui il nuovo valore di duty cycle è scritto nel corrispondente registro, ed il nuovo valore è maggiore rispetto al corrente valore del registro P1TMR allora l’uscita del PWM viene immediatamente attivata e rimane attiva per il valore impostato nel registro duty cycle. Il controllo dei servomotori viene fatto basandosi su un periodo di 20 ms. Questo valore è proporzionale alla frequenza di funzionamento del dsPIC, Fc per cui è necessario imporre questa frequenza in base alle considerazioni precedenti. Affinché sia generato un segnale periodico con periodo di 20 ms, il registro PTPER associato alla periferica PWM deve contenere un valore in bit opportuno. La formula che permette di calcolare questo valore è: P T P ER = Fc −1 Fpwm · prescaler (5.5) Il prescaler serve a ridurre il valore calcolato quando Fc Fpwm . Poiché il registro PTPER è composto da 15 bit il massimo valore rappresentabile è 215 − 1 = 32767. La frequenza della PWM richiesta in questo progetto è di 50 Hz. Sappiamo infatti che il periodo richiesto è di 20 ms, per cui la frequenza può essere determinata semplicemente come: 1 1 = = 50Hz (5.6) T 20 · 10−3 Risulta evidente che Fc Fpwm , quindi è necessario un prescaler elevato. Si utilizzerà il prescaler massimo, con rapporto 1:64, in questo modo: Fpwm = 40 · 106 − 1 = 12499 (5.7) 50 · 64 Non rimane ora che impostare il valore corretto per il duty cycle, in modo da ottenere l’onda PWM desiderata. Tale valore può essere ricavato in base ad un semplice ragionamento basato sulle proporzioni. P T P ER = 53 Capitolo 5. Implementazione del Software Infatti quello che si vuole ottenere è un segnale di periodo T che sia “alto” per un tempo pari a T − on. Allora è sufficiente impostare la seguente relazione: T − on : T = P 1DCx : P T P ER 2 (5.8) da cui si ricava: P T P ER · T − on T Per i tre valori limite dei servomotori scelti per Marvin, si ha dunque che: P 1DCx = 2 · • +90° corrisponde ad un T-on di 0.6 ms, ovvero P 1DCx = 2 · • 0° corrisponde ad un T-on di 1.5 ms, ovvero P 1DCx = 2 · 12499·0.6 20 12499·1.5 20 • -90° corrisponde ad un T-on di 2.4 ms, ovvero P 1DCx = 2 · (5.9) = 750 = 1875 12499·2.4 20 = 3000 La formula 5.9 è stata inserita nella libreria Servo.h. In questo modo è la Flex che, dato un T-on, calcola il valore di P1DCx e muove di conseguenza i motori. 5.3. RTOS ERIKA Enterprise ERIKA (Embedded Real tIme Kernel Architecture) è un kernel di piccole dimensioni con piene funzionalità real-time, progettato per supportare applicazioni embedded su piccoli microcontrollori con scarsa potenza di calcolo e memoria limitata, ma caratterizzate da vincoli temporali. È distribuito in doppia licenza (commerciale e GNU GPL con linking exception) da Evidence srl, uno spin-off del laboratorio ReTiS della Scuola Superiore Sant’Anna di Pisa. L’architettura del kernel ERIKA comprende due parti principali: il Kernel Layer e l’Hardware Abstraction Layer (HAL). Il Kernel Layer contiene un’insieme di moduli che implementano la gestione dei task e le varie strategie di schedulazione real-time disponibili (FP ed EDF). Questo livello esporta per il livello di applicazione un insieme di API RTOS per la gestione di Task, Alarms, Resources e Semaphores. L’HAL racchiude la parte di codice dipendente dall’hardware, ad esempio la gestione delle interruzioni e i cambi di contesto. Uno degli aspetti interessanti di ERIKA è il supporto alle API definite dallo standard OSEK (Offene Systeme und deren Schnittstellen für die Elektronik im Kraft-fahrzeug - open system and the corresponding interfaces for automotive electronics), un progetto congiunto di alcune industrie automobilistiche per lo sviluppo di sistemi di controllo distribuiti nei veicoli. Il consorzio prende il nome di OSEK/VDX (VDX sta per Vehicle Distributed eXecutive) poiché si occupa della definizione di un insieme di API per sistemi operativi real-time (OSEK) e di sistemi per la gestione di rete (VDX). Gli standard OSEK/VDX sono orientati ad ottenere: portabilità e riutilizzo delle applicazioni software, scalabilità tra differenti requisiti per adattarsi alle particolari esigenze dell’applicazione (usando le conformance classes), configurazione del sistema attraverso il linguaggio OIL (OSEK Implementation Language), allocazione statica delle risorse del sistema durante la compilazione dell’applicazione. ERIKA RTOS ha un footprint massimo di 4 Kb ed ha a disposizione diversi Kernel che vengono tipicamente divisi in: • Kernels standard OSEK/VDX, attraverso le Conformance Classes BCC1, BCC2, ECC1 ed ECC2 • Kernels non starndard FP, EDF e FRSH Le differenze principali tra queste tipologie di kernel sono le API che vengono implementate: i kernels non standard utilizzano solo un insieme ridotto di API per consentire il multithreading mentre quelli che implementano le Conformance Class aggiungono all’insieme di API minimali altre funzionalità tipiche dello standard OSEK. 54 Capitolo 5. Implementazione del Software È possibile fare porting da un tipo di kernel non standard ad uno OSEK e viceversa. Evidence ha provveduto ha stilare un documento di compatibilità relativamente al porting di applicazioni. [3] Le Conformance Classes BCC1, BCC2, ECC1 ed ECC2 servono per implementare i diversi comportamenti del sistema; le classi di conformità BCC1 e BCC2 utilizzano meno spazio possibile e sono le più indicate per realizzare piccoli sistemi a tasks concorrenti anche per la possibilità di avere la condivisione dello stack tra i vari task. Le classi di conformità ECC1 e ECC2 sono scelte per supportare task più complessi in quanto supportano le primitive di sincronizzazione e per questo ogni task deve essere dotato di uno stack separato. Ovviamente ciò peggiora il footprint in memoria del sistema. BCC1 Fornisce i Task Base, essi non hanno primitive bloccanti, possono terminare ed essere prelazionati, possono condividere lo stack e può esserci una sola attivazione del task. BCC2 Sono uguali ai BCC1 ma supportano attivazioni multiple. Per creare questo tipo di Tasks va specificato nel Kernel che si utilizza la BCC2 con l’istruzione OS myOS { KERNEL_TYPE = BCC2 ; } e le attivazione multiple sono specificate nell’oggetto Task, come da esempio TASK Task { ACTIVATION = 4; }; ECC1 Forniscono i Task Estesi che a differenza dei Tasks Base supportano gli Eventi e i Counting Semaphores per effettuare sincronizzazioni. ECC1 non supporta le attivazioni multiple. OS myOS { KERNEL_TYPE = ECC1 ; } Mentre gli eventi sono così definiti TASK Task { PRIORITY = 0 x01 ; ACTIVATION = 1; SCHEDULE = FULL ; AUTOSTART = TRUE ; STACK = PRIVATE { SYS_SIZE = 1024; }; EVENT = " TimerEvent " ; EVENT = " ButtonEvent " ; }; EVENT TimerEvent { MASK = AUTO ; }; EVENT ButtonEvent { MASK = AUTO ; }; È necessario comunque definire nel file .c la maschera EventMaskType mask; 55 Capitolo 5. Implementazione del Software ECC2 ECC2 è simile a ECC1 con la differenza che sono supportate le attivazioni multiple OS myOS { KERNEL_TYPE = ECC2 ; } TASK Task { ACTIVATION = 4; }; Nel documento [4] è possibile trovare la documentazione delle API di ERIKA, mentre per l’implementazione su microcontrollore e scrittura del file OIL si rimanda a [5]. Il Kernel Type può essere anche impostato a FP, EDF o FRSH, che sono trattate come Conformance Classes non appartenenti allo standard OSEK/VDX: • Fixed Priority (FP): supporta il multithreading a priorità fissa con più di 1 task per ogni priorità, e con più di una pending activation per ogni task. In questa Conformance Class le risorse vengono gestite tramite Immediate Priority Ceiling. • Earliest Deadline First(EDF): provvede al supporto per lo scheduler EDF. Ogni task ha una propria deadline relativa che viene valutata al momento dell’attivazione del task. La deadline viene codificata usando un circular timer. In questa Conformance Class le risorse vengono gestite tramite Stack Resource Protocol (SRP). • Frescor Scheduler (FRSH): include l’implementazione di uno scheduler EDF e, a livello superiore, l’implementazione dello IRIS scheduler. Le API disponibili sono elencate in fig. 5.6, per le specifiche delle varie primitive si rimanda al manuale di ERIKA [4] 56 Capitolo 5. Implementazione del Software Figura 5.6.: API del kernel Erika Enterprise 57 Capitolo 5. Implementazione del Software 5.4. Implementazione dell’applicazione ERIKA Marvin utilizza solo una parte delle caratteristiche di ERIKA. L’applicazione istanzia un singolo task periodico utilizzando attivamente gli oggetti Alarm, Counter e Task definiti nel file OIL: CPU mySystem { OS myOs { EE_OPT = " DEBUG " ; CPU_DATA = PIC30 { APP_SRC = " code . c " ; MULTI_STACK = FALSE ; ICD2 = TRUE ; }; MCU_DATA = PIC30 { MODEL = P I C3 3F J2 5 6M C7 1 0 ; }; BOARD_DATA = EE_FLEX { USELEDS = TRUE ; }; KERNEL_TYPE = FP ; }; } L’oggetto CPU imposta le proprietà CPU_DATA e MCU_Data per funzionare con il dsPIC ed utilizzare il programmatore ICD2. APP_SRC indica quale file .c contiene il codice sorgente; in questo lavoro esso è chiamato semplicemente code.c. Il kernel utilizzato è l’FP. TASK TaskSend { PRIORITY = 1; STACK = SHARED ; SCHEDULE = FULL ; }; L’oggetto TASK istanzia TaskSend che rappresenta il task periodico. Come si può notare la periodicità non è espressa attraverso istruzioni nell’oggetto TASK: essa infatti è data dall’oggetto ALARM: COUNTER myCounter ; ALARM AlarmSend { COUNTER = " myCounter " ; ACTION = ACTIVATETASK { TASK = " TaskSend " ; }; }; L’ALARM è legato ad un oggetto COUNTER (contatore), che dopo un determinato numero di tick richiama l’oggetto ALARM in cui è specificata una ACTION che, per la nostra applicazione, corrisponde all’attivazione del task TaskSend. Una seconda versione più complessa del Kernel, studiata e non testata in questo progetto, prevede un utilizzo più intelligente delle potenzialità di ERIKA. Nell’applicazione sono implementati 4 Task periodici, dove TaskRead ha il compito di effettuare la lettura dei dati da Arduino e gli altri 3 tasks, ognuno associato ad un asse, hanno il compito di eseguire le azioni di movimento4 . Di seguito il file OIL associato all’applicazione appena descritta. CPU mySystem { OS myOs { EE_OPT = " DEBUG " ; CPU_DATA = PIC30 { APP_SRC = " code . c " ; MULTI_STACK = FALSE ; ICD2 = TRUE ; 4 Vedi code.c in Source > Flex Full > Marvin PID-flex Task sperimentale 58 Capitolo 5. Implementazione del Software }; MCU_DATA = PIC30 { MODEL = P I C3 3F J2 5 6M C7 1 0 ; }; BOARD_DATA = EE_FLEX { TYPE = DEMO { OPTIONS = ALL ; }; }; KERNEL_TYPE = FP ; }; TASK TaskRead { PRIORITY = 1; // A high number corresponds to a high priority STACK = SHARED ; ACTIVATION = 1; SCHEDULE = FULL ; R E S O U R C E = " Motor_X " ; R E S O U R C E = " Motor_Y " ; R E S O U R C E = " Motor_Z " ; }; TASK TaskMotor_X { PRIORITY = 1; ACTIVATION = 1; STACK = SHARED ; SCHEDULE = FULL ; R E S O U R C E = " Motor_X " ; }; TASK TaskMotor_Y { PRIORITY = 1; ACTIVATION = 1; STACK = SHARED ; SCHEDULE = FULL ; R E S O U R C E = " Motor_Y " ; }; TASK TaskMotor_Z { PRIORITY = 1; ACTIVATION = 1; STACK = SHARED ; SCHEDULE = FULL ; R E S O U R C E = " Motor_Z " ; }; COUNTER COUNTER COUNTER COUNTER CounterRead ; Counter_X ; Counter_Y ; Counter_Z ; ALARM Alarm_Read { COUNTER = " CounterRead " ; ACTION = ACTIVATETASK { TASK = " TaskRead " ; }; }; ALARM Alarm_onX { COUNTER = " Counter_X " ; ACTION = ACTIVATETASK { TASK = " TaskMotor_X " ; }; }; 59 Capitolo 5. Implementazione del Software ALARM Alarm_onY { COUNTER = " Counter_Y " ; ACTION = ACTIVATETASK { TASK = " TaskMotor_Y " ; }; }; ALARM Alarm_onZ { COUNTER = " Counter_Z " ; ACTION = ACTIVATETASK { TASK = " TaskMotor_Z " ; }; }; R E S O U R C E Motor_X { R E S O U R CE P R O P E R T Y = STANDARD ; }; R E S O U R C E Motor_Y { R E S O U R CE P R O P E R T Y = STANDARD ; }; R E S O U R C E Motor_Z { R E S O U R CE P R O P E R T Y = STANDARD ; }; } L’oggetto Resource è utilizzato per le sezioni critiche ovvero semafori binari tali che il task di lettura dei dati risulti avere priorità alta e non essere prelazionato dagli altri tasks se i dati di movimento non sono disponibili; esso rilascerà le risorse sbloccando i task solo dopo la corretta ricezione da Arduino. Gli oggetti Resources sono definiti dallo statement: R E S O U R C E name { R E S O U R C E P R O P ER T Y = STANDARD ; }; e vanno incluse nell’oggetto Task che le utilizzerà. Il concetto di risorsa critica è fondamentale per proteggere la variabile condivisa arduinoData in modo che i task dei motori non accedano alla variabile per leggere i dati se essa non è stata riempita precedentemente dal task di lettura. 5.5. Firmware Arduino Il firmware realizzato per Arduino ricopre il ruolo di hub per lo scambio di informazioni dal livello di Comando a quello di Controllo. Per questo motivo è stato progettato secondo una logica ad interrupt. Un firmware Arduino si compone di due funzioni: void setup(), in cui vanno inserite le inizializzazioni o le azioni da eseguire una sola volta, e void loop() che contiene le azioni da svolgere ciclicamente. Queste due funzioni, in fase di compilazione, verranno inserite in una tipica struttura di un programma C, in cui compare un main che chiama le funzioni sopra citate. Il firmware realizzato per questa tesi effettua in setup le inizializzazioni dei led, delle porte seriali Serial e Serial1 e dell’ADB subsystem void setup () { // Set pins as output used for LED pinMode (22 , OUTPUT ) ; pinMode (24 , OUTPUT ) ; // Init serial port Serial . begin (57600) ; // Computer & serial monitor DEBUG Comunication Serial1 . begin (115200) ; // PIN 18 & 19 , FLEX BAUDRATE : 115200 // Init the ADB subsystem . ADB :: init () ; // Open an ADB stream to the phone ’s shell . Auto - reconnect . connection = ADB :: addConnection ( " tcp :4568 " , true , ad b Ev en tH a nd le r ) ; } 60 Capitolo 5. Implementazione del Software loop() è composta dall’unica istruzione di polling dell’ADB subsystem in quanto la ricezione dei dati sulla porta seriale è gestita da Arduino attraverso gli interrupt. Ogni volta il cui la periferica di comunicazione riceve dati, automaticamente effettua una callback a serialEvent1(), funzione che si occupa di gestire tale interruzione. void loop () { // serialEvent1 () is called in automatic , so // in the loop the serialEvent1 is not present . // Poll the ADB subsystem . ADB :: poll () ; } Risulta quindi importante definire bene come interpretare e smistare i dati in arrivo su Serial1. Il protocollo di comunicazione suddivide i tipi di messaggi in due categorie: Request Message e Debug Message. I Request Message sono la parte fondamentale del protocollo di comunicazione attraverso i quali Arduino risponde alla Flex inviando i dati ricevuti da Android; i Debug Message, invece, servono per utilizzare Arduino come strumento di Debug per la Flex attraverso il Serial Monitor presente nell’IDE. I messaggi di debug sono divisi in due tipi: stringhe e numeri. void serialEvent1 () { int i =0 , j =0; byte msg [100]; int msgNumber =0; byte messageType =0 x00 ; u n s i g n e d long int number ; byte fine =0 x00 ; // A request data message // Arduino sends the last stored command ( direction ) to Flex if ( Serial1 . read () == 0 x7F ) { Serial1 . write (0 x0A ) ; if ( MODE != 0 x0C ) { Serial1 . write (( byte ) direction ) ; } else { for ( j =0; j <15; j ++) Serial1 . write ( directions [ j ]) ; } // Uses the GREEN led to notify the reception of the message d i g i t a l W r i t e (24 , HIGH ) ; delay (10) ; d i g i t a l W r i t e (24 , LOW ) ; delay (10) ; } // A debug message // Arduino write the number or the string received from Flex to the serial monitor else if ( Serial1 . read () == 0 xA7 ) { /* HEADER PROCESSING */ // Pop from the UART queque the lengh of payload that is in the position 1 of RX buffer msgNumber = Serial1 . read () ; // Pop from the UART queque the code command that is in the position 2 of RX buffer messageType = Serial1 . read () ; 61 Capitolo 5. Implementazione del Software /* PAYLOAD PROCESSING */ for ( i =0; i < msgNumber ; i ++) { msg [ i ]= Serial1 . read () ; } // A String from Flex : processing ASCII byte values if ( messageType == 0 xBB ) { Serial . println ( " -- STRING MESSAGE - - " ) ; for ( i =0; i < msgNumber ; i ++) Serial . write ( msg [ i ]) ; Serial . println ( " " ) ; } // A number from Flex : processing DECIMAL values else if ( messageType == 0 xDD ) { Serial . println ( " -- NUMBER MESSAGE - - " ) ; // Take the integer value back from the 4 byte array from Flex number = ( ( msg [0] << 24) + ( msg [1] << 16) + ( msg [2] << 8) + ( msg [3] ) ) ; Serial . println ( number , DEC ) ; } } } Per inviare messaggi di Debug dalla Flex verso Arduino bisogna utilizzare la libreria serialConsole.h. Una tipica impostazione per fare questo è: S en dS tr i ng Re pl y ( " \ nTesto \ n " , Arduino , NoReply , false , false , UART1 ) ; oppure: SendNumReply (1234 , Arduino , NoReply , false , false , UART1 ) ; dove si è ipotizzato che Arduino sia collegato alla UART1 della Flex. 5.6. Applicazioni Android Le applicazioni realizzate per lo smartphone sono due. Seppur condividano molte funzionalità di base, esse differiscono nella classe ER1Direction. Questa classe, infatti, è predisposta come punto finale dell’elaborazione dei dati acquisiti per mezzo delle altre classi. Escludendo le classi AbstrasctServerListner, Server, ServerListner e Client che implementano la Microbridge ADB, si ha: • FdActivity: si occupa di impostare l’applicazione per tutti gli stati richiesti da Android • FdView: estende CvClientView ed è il cuore dell’applicazione, si occupa principalmente di gestire i thread della comunicazione wifi e di effettuare il riconoscimento dei volti tramite l’algoritmo LBP ed il metodo per scegliere il volto dominante sulla scena. • CvClientView: contiene il thread che operativamente utilizza l’algoritmo per il riconoscimento volti e il calcolo degli FPS tramite la classe FpsMeter. • FpsMeter: contiene i metodi per calcolare il numero di Frame per Secondo (FPS) cioè la frequenza di cattura delle immagini attraverso la fotocamera. 5.6.1. Caratteristiche comuni tra le due applicazioni Android Manifest Il Manifest elenca i componenti del sistema che vengono utilizzati dall’applicazione; per esempio, se una apk richiede la connessione alla rete, lo notifica nel Manifest e, qualora la connessione non sia disponibile, l’applicazione verrà bloccata a run-time. 62 Capitolo 5. Implementazione del Software <? xml version = " 1.0 " encoding = " utf -8 " ? > < manifest xmlns:android = " http: // schemas . android . com / apk / res / android " package = " it . univpm . dii . marvinPID " a n d r o i d : v e r s i o n C o d e = " 1 " a n d r o i d : v e r s i o n N a m e = " 1.0 " > < uses - permission android:name = " android . permission . INTERNET " / > < supports - s c r e e n s a n d r o i d : r e s i z e a b l e = " true " a n d r o i d : s m a l l S c r e e n s = " true " a n d r o i d : n o r m a l S c r e e n s = " true " a n d r o i d : l a r g e S c r e e n s = " true " a n d r o i d : a n y D e n s i t y = " true " /> < application android:label = " @string / app_name " android:icon = " @drawable / icon " > < activity android:name = " it . univpm . dii . marvinPID . FdActivity " android:label = " @string / app_name " a n d r o i d : s c r e e n O r i e n t a t i o n = " landscape " a n d r o i d : c o n f i g C h a n g e s = " keyb oardHidde n | orientation " > < intent - filter > < action android:name = " android . intent . action . MAIN " / > < category android:name = " android . intent . category . LAUNCHER " / > </ intent - filter > </ activity > < menu xmlns:android = " http: // schemas . android . com / apk / res / android " > < item android:id = " @ + id / serverIP " android:title = " @string / ServerIP " / > < item android:id = " @ + id / help " android:title = " @string / help " / > </ menu > </ application > < uses - sdk a n d r o i d : m i n S d k V e r s i o n = " 10 " / > < uses - permission android:name = " android . permission . CAMERA " / > < uses - feature android:name = " android . hardware . camera " / > < uses - feature android:name = " android . hardware . camera . autofocus " / > </ manifest > In questo manifest vengono riportati l’orientamento dell’applicazione, cioè Landscape, e i permessi di cui necessita, cioè l’accesso alla fotocamera e al modulo wifi. Comunicazione USB La logica di comunicazione è contenuta nelle classi che realizzano il modello client-server attraverso l’ADB, l’obiettivo è stato utilizzare le API messe a disposizione all’interno dell’applicazione sostituendole a quelle dell’Open Accessory. Nella FdActivity viene instanziato l’oggetto mServer come pubblico e statico attraverso l’istruzione public static Server mServer = null ; Nel metodo di Android onCreate(), che viene chiamato dal sistema operativo al lancio dell’applicazione, mServer viene associato ad un oggetto Server che comunica sulla porta 4568 dopodichè esso viene avviato attraverso il metodo start() mServer = new Server (4568) ; // Use ADK port mServer . start () ; Tramite comunicazione USB possono essere inviati solo byte e questo avviene attraverso il metodo mServer.send(), definito nella classe Server: public void send ( byte [] data ) throws IOException { for ( Client client : clients ) client . send ( data ) ; } il metodo richiama il metodo send della classe Client: 63 Capitolo 5. Implementazione del Software public void send ( byte [] data ) throws IOException { try { output . write ( data ) ; output . flush () ; } catch ( So c ke tE xc e pt io n ex ) { // Broken socket , disconnect close () ; server . d i s c o n n e c t C l i e n t ( this ) ; } } Elaborazione Video Le funzionalità del sistema di visione sono rese disponibili dalla libreria OpenCV appositamente compilata per il sistema operativo Android. La classe FdView è la classe centrale dell’applicazione e come tale viene creata all’avvio; il metodo processFrame() contiene l’algoritmo per l’elaborazione e l’estrazione di un volto da una scena @Override protected Bitmap processFrame ( VideoCapture capture ) { capture . retrieve ( mRgba , Highgui . C V _ C A P _ A N D R O I D _ C O L O R _ F R A M E _ R G B A ) ; capture . retrieve ( mGray , Highgui . C V _ C A P _ A N D R O I D _ G R E Y _ F R A M E ) ; if ( mCascade != null ) { // clear previous detected faces array faces . clear () ; // equalize gray image histogram Imgproc . equalizeHist ( mGray , mGray ) ; // invoke face detection algorithm , starting from a face dimension of 90 x90 mCascade . d e t e c t M u l t i S c a l e ( mGray , faces , 1.3 , 3 , 0 , DEFSIZE ) ; // search each face on history face detected array for ( Rect r : faces ) { add = true ; for ( CFace a : hFaces ) { if ( a . updatePos ( r ) ) { add = false ; break ; } } // face not found into history array , new face if ( add ) hFaces . add ( new CFace ( r ) ) ; // print a green rectangle around each face if ( develop ) Core . rectangle ( mRgba , r . tl () , r . br () , new Scalar (0 , 255 , 0 , 255) , 1) ; } // initialize array variables newFace = null ; maxT =0; hcopy . clear () ; // for each faces into history faces array , removes old face ( not yet detected ) and search dominant face ( most time present ) for ( CFace a : hFaces ) { if ( a . aging () ) { hcopy . add ( a ) ; } else if ( a . findBest () ) { 64 Capitolo 5. Implementazione del Software newFace = a ; } } hFaces . removeAll ( hcopy ) ; // if a face was detected if ( newFace != null ) { // draws a red rectangle around face if ( develop ) Core . rectangle ( mRgba , newFace . pos . tl () , newFace . pos . br () , new Scalar (255 ,0 ,0 ,255) ,2) ; if ( dominantFace == null || newFace . id != dominantFace . id || repeat > REPEAT_LIMIT ) { // send face to server when first one detected or new face or need to re send dominantFace = newFace ; repeat =0; try { // interrupt server response wait thread waitResp = false ; // create bitmap image to send dominant = Bitmap . createBitmap ( mRgba . cols () , mRgba . rows () , Bitmap . Config . ARGB_8888 ) ; Utils . matToBitmap ( mRgba , dominant ) ; dominant = Bitmap . createBitmap ( dominant , newFace . pos .x , newFace . pos .y , newFace . pos . width , newFace . pos . height ) ; faceCheck = " " ; sendImg = true ; } catch ( Exception e ) { Log . e ( TAG , " bitmap error " + e . toString () ) ; } Log . i ( TAG , " new Face " ) ; } else if ( newFace . id == dominantFace . id && repeat <= REPEAT_LIMIT ) { // on same dominant face , count time until re - send to server repeat ++; } } else { // no dominant faces , reset variables dominantFace = null ; dominant = null ; sendImg = false ; waitResp = false ; repeat =0; } } if ( develop ) { // create image to send back to caller bmp = Bitmap . createBitmap ( mRgba . cols () , mRgba . rows () , Bitmap . Config . ARGB_8888 ); if ( Utils . matToBitmap ( mRgba , bmp ) ) return bmp ; bmp . recycle () ; } return null ; } Il video catturato con le istruzioni alle righe 2 e 3, viene elaborato equalizzandone l’istogramma, quindi viene eseguito l’algoritmo di face-detection tramite l’istruzione di riga 10. La scansione ricerca i volti con una dimensione minima di 110x110 pixel; ad ogni iterazione la dimensione viene incrementata di 65 Capitolo 5. Implementazione del Software un fattore 1,3. La variabile faces contiene la lista delle posizioni di tutti i volti individuati nel frame corrente; il passo successivo è quello di cercare ogni volto individuato nei frame precedenti, aggiornandone le informazioni per mezzo del metodo updatePos(); il vettore hFaces contiene le informazioni relative ai volti individuati nel frame attuale e in quelli precedenti. Il tipo di dati CFace è stato creato per memorizzare le informazioni relative ai volti individuati, ovvero la posizione e il tempo di permanenza sulla scena. Dopo aver aggiornato lo storico con i nuovi volti individuati, questo viene scansionato alla ricerca del volto dominante, eliminando i volti non più presenti nella scena. L’operazione di ricerca del volto dominante si basa sul tempo di permanenza nella scena; ogni volto individuato viene ricercato nello storico: se la posizione attuale del vertice superiore sinistro è in un intorno della posizione precedente, i due volti confrontati sono marcati come identici (vedi fig. 5.7) Figura 5.7.: Algoritmo di individuazione di un volto Il metodo updatePos() della classe CFace si occupa di confrontare le posizioni dei due volti, restituendo il valore false se non sono compatibili, altrimenti provvede ad aggiornare la posizione corrente e il tempo di permanenza. La variabile noFace viene utilizzata per eliminare, con un metodo aging, i volti non più presenti nella scena per un certo numero di frame consecutivi. Nella selezione del volto dominante, con il metodo findBest si cerca il volto con il valore massimo della variabile time. 5.6.2. Differenze tra le due applicazioni Android Marvin Grid View Nell’applicazione “Marvin Grid View” la classe ER1Direction utilizza la Grid View, quindi calcola il movimento da imporre al robot in base alla posizione e rispetto alle aree ben definite. Il listato successivo riporta il modo in cui le aree sono definite il metodo init() che produce la suddivisione nel momento in cui un’istanza ER1Direction viene creata . public void init ( int width , int height ) { this . heigh = height ; this . width = width ; output = " " ; direction = cSTOP ; paint = new Paint () ; 66 Capitolo 5. Implementazione del Software paint . setColor ( Color . RED ) ; paint . setTextSize (20) ; limitLeft = width /4; limitRight = limitLeft *3; limitTop = height /4; limitBottom = limitTop *3; } public void updateLimits ( int width , int height ) { this . heigh = height ; this . width = width ; limitLeft = width /4; limitRight = limitLeft *3; limitTop = height /4; limitBottom = limitTop *3; } Il seguente listato, invece, contiene il metodo calc() che elabora la posizione ricevuta da parametro e stabilisce il comando da inviare al robot. Esso controlla per prima cosa l’altezza del volto dominante; nel caso fosse maggiore di quella massima, 160px, viene inviato il comando di muovere il robot all’indietro per allontanarsi dal soggetto. Altrimenti se è minore di 120px, il robot si avvicinerà al soggetto. Questo per mantenere il volto del soggetto né troppo vicino né troppo distante dal robot. Viene controllata la posizione rispetto alle zone prestabilite, quindi sono inviati i comandi per mantenere il volto nella zona centrale del frame. public void calc ( Rect a ) { if ( a != null ) { if ( a . height >160) { direction = cREVERSE ; output = " REVERSE " ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } else if ( a . height <120) { direction = cFORWARD ; output = " FORWARD " ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } else { if ( a . tl () .x < limitLeft ) { output = " LEFT " ; direction = cLEFT ; r es ea rc h Co mm an d = cLEFT ; r e s e a r c h M o v e m en t = " LEFT RESEARCH ... " ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } else if ( a . br () .x > limitRight ) { output = " RIGHT " ; direction = cRIGHT ; r es ea rc h Co mm an d = cRIGHT ; r e s e a r c h M o v e m en t = " RIGHT RESEARCH ... " ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } else if ( a . tl () .y < limitTop ) { output = " UP " ; direction = cUP ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } else if ( a . br () .y > limitBottom ) { output = " DOWN " ; direction = cDOWN ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; 67 Capitolo 5. Implementazione del Software } else { output = " STOP " ; direction = cSTOP ; startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; } } } else { /* USE THIS SE NON HAI BISOGNO DELLA RICERCA / direction = cSTOP ; output =" STOP "; */ /* ricerca di un volto dopo n iterazioni */ startBehavior - -; if ( startBehavior %10 == 0) r es e a r c h M o v e m en t = " THE RESEARCH WILL BEGIN IN " + startBehavior + " ITER ... " ; if ( startBehavior <= 0) // BEGIN THE RESEARCH TO THE RIGHT SIDE { // this will last until the endBehavior reach 0 r e s e a r c h M o v e m en t = " RIGHT RESEARCH ... " ; r es ea rc h Co mm an d = cRIGHT ; endBehavior - -; } if ( endBehavior <= 0) { // Now the robot stops r e s e a r c h M o v e m en t = " RESEARCH STOPPED " ; r es ea rc h Co mm an d = cSTOP ; // begin new cycle of research behavior startBehavior = D E F A U L T _ S T A R T _ D U R A T I O N ; endBehavior = D E F A U L T _ E N D _ D U R A T I O N ; } output = r e s e a r c h M o v e m e n t ; direction = r es ea rc h Co mm an d ; } try { FdActivity . mServer . send ( new byte []{0 x0F , direction }) ; } catch ( Exception e ) { } } Si nota come se a è null viene avviato l’algoritmo di ricerca. In ogni caso il comando di movimento viene inviato attraverso l’mServer verso Arduino nel blocco try/catch. La direzione che il robot deve seguire è calcolata in base alla posizione dei vertici evidenziati: viene utilizzato il vertice inferiore destro (a.br) quando il volto si trova nella zona destra o inferiore del frame, in tutte le altre situazioni si usa il vertice superiore sinistro(a.tl). I comandi sono costanti di tipo byte: public public public public public public public final final final final final final final static static static static static static static byte byte byte byte byte byte byte cSTOP cFORWARD cREVERSE cLEFT cRIGHT cUP cDOWN = = = = = = = 0 x00 ; 0 x01 ; 0 x02 ; 0 x03 ; 0 x04 ; 0 x05 ; 0 x06 ; essi vengono inviati tramite il metodo send() di mServer FdActivity . mServer . send ( new byte []{0 x0F , direction }) ; che prende in ingresso un array di byte, creato al momento dell’invio inserendo due byte. In questo caso si sta usando, dunque, il protocollo semplificato. Dal lato Flex invece essi vengono gestiti dal TaskSend e da uno switch: 68 Capitolo 5. Implementazione del Software A r d u i n o _ D a t a _ R e q u e s t (& arduinoData [0] ,2 ,1) ; if ( arduinoData [0] == 0 x0A ) ledBlink (25 ,1 ,100) ; switch ( arduinoData [1]) { case 0 x00 : // stop Servo_Neutral ( DX ) ; Servo_Neutral ( SX ) ; break ; case 0 x01 : // forward Servo_minus90 ( DX ) ; Servo_plus90 ( SX ) ; break ; case 0 x02 : // reverse Servo_plus90 ( DX ) ; Servo_minus90 ( SX ) ; break ; case 0 x03 : // left Servo_minus90 ( DX ) ; Servo_minus90 ( SX ) ; break ; case 0 x04 : // right Servo_plus90 ( DX ) ; Servo_plus90 ( SX ) ; break ; case 0 x05 : // up Servo_Neutral ( DX ) ; Servo_Neutral ( SX ) ; Servo_Move ( HEAD , DEFAULT_INCREMENT ,20) ; break ; case 0 x06 : // down Servo_Neutral ( DX ) ; Servo_Neutral ( SX ) ; Servo_Move ( HEAD , - DEFAULT_INCREMENT ,20) ; break ; } L’istruzione Servo_plus90(motor) è equivalente a Servo_Set_Position(motor,2.4,20), mentre Servo_minus90(mot è equivalente alla Servo_Set_Position(motor,0.6,20). La visualizzazione a griglia viene disegnata a schermo dal metodo draw(): public void draw ( Canvas canvas , float offsetx , float offsety ) { canvas . drawLine ( limitLeft + offsetx , offsety , limitLeft + offsetx , totH + offsety , paint ) ; canvas . drawLine ( limitRight + offsetx , offsety , limitRight + offsetx , totH + offsety , paint ) ; canvas . drawLine ( limitLeft + offsetx , limitTop + offsety , limitRight + offsetx , limitTop + offsety , paint ) ; canvas . drawLine ( limitLeft + offsetx , limitBottom + offsety , limitRight + offsetx , limitBottom + offsety , paint ) ; canvas . drawText ( output + " " + FdView . faceCheck , 20 + offsetx , 90 , paint ) ; } Marvin PID A differenza dell’applicazione precedente, mutuata in buona parte dal lavoro su AndroEEBot e quindi considerabile come Demo per l’utilizzo di singoli comandi alla parte hardware, questa applicazione è stata progettata per funzionare attraverso la legge di controllo PID. Non vengono utilizzati dei comandi di spostamento: è l’implementazione digitale del controllo che impone il giusto movimento al robot, attraverso gli errori calcolati sugli assi X, Y, Z affinché il volto sia sempre mantenuto al centro del frame. 69 Capitolo 5. Implementazione del Software Per errore si intende la differenza tra un set-point -cioè il riferimento da raggiungere- e la posizione attuale. La fase di inizializzazione della classe ER1Direction è: public void init ( int width , int height ) { this . heigh = height ; this . width = width ; output = " " ; paint = new Paint () ; paint . setColor ( Color . RED ) ; paint . setTextSize (20) ; paintRECT = new Paint () ; paintRECT . setColor ( Color . RED ) ; paintRECT . setStyle ( Paint . Style . STROKE ) ; } public void updateLimits ( int width , int height ) { this . heigh = height ; this . width = width ; } Qui vengono recuperati l’altezza e la larghezza totali del frame da elaborare. Segue quindi una parte di calcoli per ricavare gli errori sui tre assi e convertirli prima da float a interi attraverso un metodo della classe Float, e successivamente suddividendoli in 4 byte per poter essere inviati dall’mServer verso Arduino. public void calc ( Rect a , float offsetx , float offsety ) { if ( a != null ) { output = " PID " ; data [0]=0 x0C ; coordX = ( float ) (( a . tl () . x ) +( a . width /2) ) + offsetx ; coordY = ( float ) (( a . tl () . y ) +( a . height /2) ) + offsety ; ErrorX =( int ) (( width /2) -( a . tl () . x +( a . width /2) ) ) ; ErrorY =( int ) (( heigh /2) -( a . tl () . y +( a . height /2) ) ) ; ErrorZ =( int ) (( SetPoint_onZ ) -( a . height ) ) ; // USED to decide the direction of the robot : 0 - > UP 1 - > DOWN if ( ErrorY > 0) Ydirection =0; else Ydirection =1; // USED to decide the direction of the robot : 1 - > positive ( LEFT ) 0 - > negative ( RIGHT ) if ( ErrorX > 0) Xdirection =1; else Xdirection =0; // USED to decide the direction of the robot : 0 - > positive ( FORWARD ) 1 - > negative ( BACKWARD ) if ( ErrorZ > 0) Zdirection =0; // else Zdirection =1; // Now i take the absolute values of errors ErrorY = Math . abs ( ErrorY ) ; ErrorX = Math . abs ( ErrorX ) ; ErrorZ = Math . abs ( ErrorZ ) ; // Limita l ’ errore sull ’ asse Z . Infatti il volto p u diventare molto grosso se // vicino alla telecamera e quindi l ’ errore p u crescere molto rispetto // in Forward rispetto a backward c o s allora unifico i valori if ( Zdirection ==1 && ErrorZ >=15) ErrorZ =14; 70 Capitolo 5. Implementazione del Software // inserisco delle tolleranze su ogni asse if ( ErrorY <=4 ) ErrorY =0; else ErrorY = ErrorY -4; // else eliminate the acceptable zone if ( ErrorX <=10 ) ErrorX =0; else ErrorX = ErrorX -10; // else eliminate the acceptable zone if ( ErrorZ <=5 ) ErrorZ =0; else ErrorZ = ErrorZ -5; // else eliminate the acceptable zone // Normalize the errors : ErrorK / ErrorMax_K ErrorMax_X = (( width /2+ offsetx ) /2) ; ErrorMax_Y = (( width /2+ offsetx ) /2) ; ErrorMax_Z = 20; con K ={ X ,Y , Z } ErrorX = ErrorX / ErrorMax_X ; ErrorY = ErrorY / ErrorMax_Y ; ErrorZ = ErrorZ / ErrorMax_Z ; /* transfomate the FLOAT ErrorX , ErrorY , ErroZ into a 4 bytes array and put them in data [] */ // java method that with some bit ’s masks convert the float bits to bit and // display bit with a int -> see official reference api int Xint = Float . floatT oIntBits ( ErrorX ) ; int Yint = Float . floatT oIntBits ( ErrorY ) ; int Zint = Float . floatT oIntBits ( ErrorZ ) ; // now i separate the int Xint into 4 bytes array msg [0] = (( Xint >> 24) & 0 xFF ) ; msg [1] = (( Xint >> 16) & 0 xFF ) ; msg [2] = (( Xint >> 8) & 0 XFF ) ; msg [3] = (( Xint & 0 XFF ) ) ; // copy into the data [] array , that will be sended to flex , Xdirection and 0 -3 msg byte data [1]=( byte ) Xdirection ; data [2]=( byte ) msg [0]; data [3]=( byte ) msg [1]; data [4]=( byte ) msg [2]; data [5]=( byte ) msg [3]; // now i separate the int Yint into 4 bytes array msg [0] = (( Yint >> 24) & 0 xFF ) ; msg [1] = (( Yint >> 16) & 0 xFF ) ; msg [2] = (( Yint >> 8) & 0 XFF ) ; msg [3] = (( Yint & 0 XFF ) ) ; // copy into the data [] array , that will be sended to flex , Ydirection and 0 -3 msg byte data [6]=( byte ) Ydirection ; data [7]=( byte ) msg [0]; data [8]=( byte ) msg [1]; data [9]=( byte ) msg [2]; data [10]=( byte ) msg [3]; // now i separate the int Yint into 4 bytes array msg [0] = (( Zint >> 24) & 0 xFF ) ; msg [1] = (( Zint >> 16) & 0 xFF ) ; msg [2] = (( Zint >> 8) & 0 XFF ) ; 71 Capitolo 5. Implementazione del Software msg [3] = (( Zint & 0 XFF ) ) ; // copy into the data [] array , that will be sended to flex , Ydirection and 0 -3 msg byte data [11]=( byte ) Zdirection ; data [12]=( byte ) msg [0]; data [13]=( byte ) msg [1]; data [14]=( byte ) msg [2]; data [15]=( byte ) msg [3]; // // // // QUINDI QUELLO CHE HO ORA E ’ UN ARRAY DI 4 BYTE CHE RAPPRESENTANO IL FLOAT A QUESTO PUNTO LO INVIO AD ARDUINO CHE LO PASSA COSI ’ COM ’E ’ ALLA FLEX SU CC E SS IV AM EN T E LA FLEX RICOMPATTERA ’ QUESTI 4 BYTE IN UN FLOAT CON UN METODO CHE PUOI VEDERE SUL CODICE DELLA FLEX } else { int j =0; output = " PID STOPPED " ; data [0] = 0 x0C ; // put the signal of stop data [1] = 0 x0B ; data [2] = 0 x12 ; data [3] = 0 x0B ; // for the draw ErrorX = ErrorY = ErrorZ =0; coordX = coordY = -999.0 f ; } try { // Structure of data []: Control byte for Arduino [0] - X [1 -5] - Y [6 -11] - Z [12 -16] FdActivity . mServer . send ( data ) ; } catch ( Exception e ) { } } CoordX e CoordY calcolano le coordinate sull’asse X e Y, in pixel, della posizione del volto dominante. Esse verranno poi sottratte – senza offset – al set-point sui rispettivi assi per ottenere gli errori. I dati che rappresentano gli errori e i segni di ogni errore vengono inviati nel blocco try/catch. (a) Set-point e calcolo delle coordinate (b) Calcolo degli errori sui tre assi di controllo Figura 5.8.: Calcolo delle coordinate e degli errori nell’applicazione “Marvin PID” Anche il metodo draw() si presenta differente in quanto non deve più mostrare un riferimento a “griglia” 72 Capitolo 5. Implementazione del Software ma visualizzare, per ogni asse, il set-point da raggiungere in maniera tale che il volto, rappresentato dal punto in verde, sia sempre tenuto al centro del frame. public void draw ( Canvas canvas , float offsetx , float offsety ) { Paint punto = new Paint () ; punto . setColor ( Color . GREEN ) ; punto . setStr okeWidth (10) ; // print a line parallel to the y - axis canvas . drawLine ( width /2+ offsetx , 0+ offsety , width /2+ offsetx , heigh + offsety , paint ) ; // print a line parallel to the x - axis canvas . drawLine (0+ offsetx , heigh /2+ offsety , width + offsetx , heigh /2+ offsety , paint ) ; // print the Acceptable zone where error = 0 canvas . drawRect ( width /2+ offsetx -10 , heigh /2+ offsety -4 , width /2+ offsetx +10 , offsety +4 , paintRECT ) ; heigh /2+ canvas . drawText ( output + " " + FdView . faceCheck , 20 + offsetx , 90 , paint ) ; // print the point on Y if ( coordX != -999.0 f && coordY != -999.0 f ) { canvas . drawPoint ( coordX , coordY , punto ) ; canvas . drawText ( " ERROR on X : " + ErrorX , 20 + offsetx , 200 , paint ) ; canvas . drawText ( " ERROR on Y : " + ErrorY , 20 + offsetx , 220 , paint ) ; canvas . drawText ( " ERROR on Z : " + ErrorZ , 20 + offsetx , 240 , paint ) ; if ( Xdirection == 0) stringaX = " RIGHT " ; else stringaX = " LEFT " ; if ( Ydirection == 0) stringaY = " UP " ; else stringaY = " DOWN " ; if ( Zdirection == 0) stringaZ = " FORWARD " ; else stringaZ = " BACKWARD " ; canvas . drawText ( stringaX , 240+ offsetx , 200 , paint ) ; canvas . drawText ( stringaY , 240 + offsetx , 220 , paint ) ; canvas . drawText ( stringaZ , 240 + offsetx , 240 , paint ) ; } } Il SetPoint dell’asse Z viene disegnato a schermo nella classe FdView dal codice // draw a blue line next to the left angle of the dominant face to visualize the set point on Z (=130 px ) CFace temp = new CFace ( newFace . pos ) ; Point bo = temp . pos . tl () ; bo . y +=130; // draws a blue line over the red rectange on the dominant face if ( develop ) Core . line ( mRgba , newFace . pos . tl () ,bo , new Scalar (0 ,0 ,255 ,255) , 4) ; Dal lato Flex invece tale comportamento è gestito, sempre da TaskSend, in questo modo TASK ( TaskSend ) { BYTE i =0; BYTE arduinoData [20]; float DutyCycle =0; EE_INT32 datoX [4]; 73 Capitolo 5. Implementazione del Software BYTE Xsign ; float Er ro r_ o n_ X_ ax i s =0; EE_INT32 datoY [4]; BYTE Ysign ; float Er ro r_ o n_ Y_ ax i s =0; EE_INT32 datoZ [4]; BYTE Zsign ; float Er ro r_ o n_ Z_ ax i s =0; EE_led_on () ; A r d u i n o _ D a t a _ R e q u e s t (& arduinoData [0] ,16 , UART1 ) ; if ( arduinoData [0] == 0 x0A ) ledBlink (25 ,1 ,100) ; /* VERIFY IF THE PID IS ACTIVE OR NOT , THE PID IS NOT ACTIVE IF NO DOMINAT FACE IS FOUND ON THE SCREEN */ if ( arduinoData [1] == 0 x0B && arduinoData [2]== 0 x12 && arduinoData [3] == 0 x0B ) { // So the PID is stopped then put DX e SX motors in a neutral position : stop the motor Servo_Neutral ( DX ) ; Servo_Neutral ( SX ) ; } else { // X AXIS : DIRECTIONS OF TURNING THE ROBOT Xsign = arduinoData [1]; for ( i =0; i <4; i ++) datoX [ i ]= arduinoData [ i +2]; // Y AXIS -> " HEAD MOTOR " CORRESPONDING TO THE Y AXIS ON THE SMARTPHONE Ysign = arduinoData [6]; for ( i =0; i <4; i ++) datoY [ i ]= arduinoData [ i +7]; // Z AXIS : DIRECTIONS FORN FORWARD / BACKWARD THE ROBOT Zsign = arduinoData [11]; for ( i =0; i <4; i ++) datoZ [ i ]= arduinoData [ i +12]; /* Converting the 4 Byte to 1 Float */ E rr or _o n _X _a xi s = c o n v e r t 4 B y t e T O 1 F l o a t ( datoX ) ; E rr or _o n _Y _a xi s = c o n v e r t 4 B y t e T O 1 F l o a t ( datoY ) ; E rr or _o n _Z _a xi s = c o n v e r t 4 B y t e T O 1 F l o a t ( datoZ ) ; // Seleziona quale movimento effettuare con i motori di propulsione , infatti essendone solo 2 // che vanno a gestire 4 movimenti ( AVANTI , DIETRO , DX , SX ) essi devono essere " condivisi " // la politica attuare la direzione in cui l ’ errore massimo in maniera da avere una // compensazione media accettabile if ( E r ro r_ on _Z _ ax is >= E rr or _o n_ X _a xi s ) // based on absolute value { /* Set the right sign of Z float used for the PI_pid controller , Ysign is used in the P_pid function instead */ if ( Zsign == 1) Er ro r _o n_ Z_ a xi s *= -1; DutyCycle = P_pid_Z ( Er ro r_ o n_ Z_ ax i s ) ; // attua il movimento Avanti o Indietro ( in questo caso i due motori fanno sempre la stessa cosa : sono coordinati ) 74 Capitolo 5. Implementazione del Software S e r v o _ S e t _ P o s i t i o n (2 , DutyCycle ,20) ; // motore DX // find the opposite duty cycle float O p p o s i t e _ D u t y C y c l e = map ( DutyCycle ,0.6 ,2.4 ,2.4 ,0.6) ; S e r v o _ S e t _ P o s i t i o n (3 , Opposite_DutyCycle ,20) ; } else { /* Set the right sign of X float used for the PI_pid controller , Ysign is used in the P_pid function instead */ if ( Xsign == 1) Er ro r _o n_ X_ a xi s *= -1; DutyCycle = PI_pid_X ( E r ro r_ on _X _ ax is ) ; // attua il movimento Gira a DX o Gira a SX S e r v o _ S e t _ P o s i t i o n (2 , DutyCycle ,20) ; S e r v o _ S e t _ P o s i t i o n (3 , DutyCycle ,20) ; } // Actuate always the positioning on the HEAD motor , because this motor is not shared between different movements if ( Ysign == 1) Er ro r _o n_ Y_ a xi s *= -1; DutyCycle = PI_pid_Y ( E r ro r_ on _Y _ ax is ) ; S er v o _ S e t _ P o s i t i o n (1 , DutyCycle ,20) ; } EE_led_off () ; mydelay (200) ; } 5.7. OpenCV e Linear Binary Pattern (LBP) OpenCV (Open Source Computer Vision) è una libreria open-source contenente centinaia di funzioni per l’analisi di immagini e video, nel contesto della computer vision. A partire dalla versione OpenCV 2.2 è stato aggiunto il supporto per il sistema operativo Android. Figura 5.9.: Architettura della libreria OpenCV Il progetto Marvin affronta l’argomento della Visione Artificiale e nello specifico quello della face detection. Esistono diversi algoritmi per l’elaborazione di un frame video; tra di essi l’algoritmo di 75 Capitolo 5. Implementazione del Software Viola-Jones risulta il più affidabile e robusto per la localizzazione dei volti ed oggetti, sia in immagini statiche sia in video. Diverse caratteristiche rendono questo metodo veloce e poco soggetto ai problemi di illuminazione, rotazione, scalatura e parziale occlusione dei soggetti inquadrati. Il metodo Local Binary Pattern (LBP) è un diverso metodo per la localizzazione di oggetti che prende spunto da Viola Jones e ne migliora alcune caratteristiche; esso usa un codice binario per descrivere un pattern della texture locale, costruito confrontando i valori di grigio dei pixel vicini al punto centrale, preso come riferimento. Nella versione originaria del metodo LBP viene utilizzata una matrice di 3x3 pixel: gli 8 pixel che circondano il pixel centrale vengono confrontati con il valore in scala di grigio di quest’ultimo; il risultato è un numero binario, usato come descrittore della texture. Figura 5.10.: Funzionamento dell’algoritmo LBP Il metodo descritto è disponibile in versione migliorata tanto da implementare la ricerca di uniform pattern (cioè pattern ricorrenti) oppure adattare la matrice di ricerca 3x3 ad una dimensione personalizzata. 76 Capitolo 6. Il Sistema di Controllo Il controllo automatico si prefigge di modificare il comportamento del sistema da controllare, le sue uscite, attraverso la manipolazione delle grandezze d’ingresso. Il controllo del sistema in esame viene affidato ad un altro sistema costruito appositamente, detto sistema controllante o controllore, che viene progettato dopo uno studio preliminare del sistema da controllare per individuarne il modello matematico esatto, servendosi degli strumenti messi a punto dalla teoria dei sistemi. 6.1. Il controllore PID a tempo continuo: generalità Il modello matematico di un sistema da controllare è determinato da dinamiche complesse e dai fenomeni fisici, meccanici ed elettrici, che intervengono nel funzionamento del sistema. Per questo motivo è molto difficile ricavare un modello matematico di un sistema da controllare. Il caso di Marvin è esempio di quanto appena detto. Per quanto il problema possa essere affrontato con discreto successo mediante tecniche di identificazione e modellistica, in questo lavoro si è scelto un approccio più semplice e molto diffuso nei processi industriali, ovvero l’utilizzo di un controllore PID. Questo controllore prende il nome dalle tre azioni che costituiscono la sua risposta impulsiva che nel dominio di Laplace è: 1 + τd · s R(s) = Kp · 1 + τi · s (6.1) u(s) = R(s) · e(s) (6.2) per cui: mentre l’uscita nel dominio del tempo è: 1 u(t) = Kp · e(t) + τi Z 0 ∞ de(t) e(t)dt + τd · dt (6.3) Il controllore acquisisce in ingresso un valore dal processo, e lo confronta con un valore di riferimento. La differenza, il segnale di errore e(t), viene quindi usata per determinare il valore della variabile di uscita del controllore u(t), che è la variabile manipolabile del processo. Il PID regola l’uscita in base a: • il valore del segnale di errore (azione proporzionale); • i valori passati del segnale di errore (azione integrale); • la velocità di variazione del segnale di errore (azione derivativa). 77 Capitolo 6. Il Sistema di Controllo Figura 6.1.: Controllore PID Il contributo delle azioni del PID può essere riassunto come segue. Azione Proporzionale: è l’azione principale ed il suo scopo è quello di modificare l’uscita in modo che essa sia proporzionale all’errore rilevato. Questa azione aumenta la prontezza del sistema aggiungendo guadagno, che in termini di risposta in frequenza corrisponde allo spostamento verso destra della pulsazione di attraversamento, provocando un aumento della banda passante e una diminuzione del tempo di salita. In questo modo il transitorio risulta essere più breve. Se il processo ha più di 3 poli l’aumento incontrollato del guadagno porta il sistema inevitabilmente all’instabilità. Figura 6.2.: Azioni proporzionali a confronto per un ingresso a gradino Azione Integrale: migliora il tipo del sistema, rendendolo almeno di tipo 1. Questo significa che a regime permanente l’errore rispetto ad un ingresso a gradino risulta essere nullo, in quanto l’ordine dell’ingresso a gradino è pari a 0. Inoltre rende il sistema astatico rispetto a disturbi a gradino che agiscono in catena diretta. Lo svantaggio è riscontrabile attraverso l’inserimento del polo in s = 0 che corrisponde a un ritardo di fase di -90° che potrebbe abbassare il margine di fase al di sotto dei 40° (minimo margine di fase consigliato). Il vantaggio apprezzabile che apporta questo termine è lo smorzamento delle oscillazioni introdotte dal termine proporzionale anche se questo è apprezzabile per alte frequenze infatti 1 1 s s=jω = jω , che per ω → ∞ da contributo pari a 0. 78 Capitolo 6. Il Sistema di Controllo Effettivamente il termine integrale peggiora anche la prontezza del sistema attraverso il polo in s = 0. Per rendersene conto utilizzando le nozioni dell’analisi modale notiamo che se un modo aperiodico è collocato nell’origine esso tenderà a non estinguersi mai o comunque molto lentamente. Figura 6.3.: Azioni integrali a confronto per un ingresso a gradino Azione Derivatrice: è l’azione che la maggior parte delle volte non viene inserita durante la realizzazione del controllore PID rendendolo di fatto un PI, questa azione ha lo scopo di migliorare la stabilità del sistema in quanto aggiunge uno zero in s = 0 che corrisponde in termini di risposta armonica ad un aumento di fase pari a +90°. Non viene quasi mai utilizzata; infatti l’operazione di derivata, non essendo un operatore lineare, potrebbe far divergere l’uscita ad infinito se sul sistema agisce del rumore. Inoltre tende ad amplificare i disturbi alle alte frequenze. I vantaggi, comunque, non sono trascurabili. Se l’errore tende a diminuire, la correzione viene aumentata non proporzionalmente, ma in base alla velocità di variazione. L’azione derivatrice può accelerare o decelerare l’intervento del regolatore in modo dinamico seguendo la tendenza dell’errore e “prevede” nella prossima lettura l’andamento dell’errore. Figura 6.4.: Azioni derivatrici a confronto per un ingresso a gradino La formula 6.3 deve essere discretizzata per poter essere inserita in un sistema di controllo digitale, come il dsPIC, poiché tale sistema accetta ingressi campionati e quantizzati, dovendo essi essere elaborati da un calcolatore. 79 Capitolo 6. Il Sistema di Controllo Il tipico schema di controllo di un sistema digitale a dati campionati è il seguente Figura 6.5.: Schema a blocchi di un sistema a dati campionati Si noti la presenza del blocco A/D e D/A che effettuano rispettivamente la conversione analogicodigitale, che nel nostro sistema può essere individuato in Android, e il blocco digitale-analogico che trasforma dei segnali numerici in segnali che variano nel tempo che nel nostro sistema è visto come il modulo MCPWM1. I servomotori sono rappresentati dal blocco di attuazione mentre il blocco impianto è il processo “Marvin”. Il clock è rappresentato dalla periodicità del task di acquisizione garantita del RTOS ERIKA e impostato a 100 ms, infine Android figura anche come trasduttore. 6.2. Il controllore PID a tempo discreto: generalità La discretizzazione non interessa il termine proporzionale, poiché esso è una moltiplicazione, ma è necessaria per l’integrale e la derivata. Esistono diversi metodi per discretizzarli ma in questa tesi sono stati scelti quelli più semplici: Z ∞ ∞ X e(k) · Tc (6.4) e(t)dt ' 0 k=0 e(k) − e(k − 1) de(t) ' dt Tc (6.5) avendo realizzato un controllore P e uno PI il termine derivato non è stato utilizzato. Si nota che il termine integrale richiede ad ogni istante k di calcolare una sommatoria. Tale somma risulta un’operazione computazionalmente dispendiosa. Se si utilizza la versione ricorsiva dell’algoritmo, che tiene memoria dell’uscita passata u(k − 1), è possibile migliorare la situazione. Attraverso alcune semplici sostituzioni si perviene così a questo algoritmo per il controllore PI: Tc · e(k − 1) (6.6) u(k) = u(k − 1) + Kp · e(k) − e(k − 1) + τi banalmente il controllore P è invece: u(k) = Kp · e(k) (6.7) Nel caso specifico si somma all’uscita, u(k), una costante pari a 1,5, utile a traslare i valori dell’uscita verso i valori di ingresso del servomotore. Infatti per u(k) = 0, e senza questa costante il servomotore funzionerebbe con un duty cycle pari a 0,6, che non corrisponde alla posizione “neutra” dell’attuatore, così come l’uscita del controllore richiederebbe. L’algoritmo di calcolo del P e del PI è implementato in questo modo sulla Flex per un generico asse K: float P_pid_K ( float error ) 80 Capitolo 6. Il Sistema di Controllo { float U = 0; U = Kp_K *( error ) + 1.5; return U ; } float PI_pid_K ( float error ) { float U = 0; Ki_K = ( Tc / Ti_K ) ; float q1 = 1; float q2 = ( -1 + Ki_K ) ; U = ( lastU_K + Kp_K * ( q1 * ( error ) + q2 * ( lastError_K ) ) ) + 1.5; lastU_K = U - 1.5; lastError_K = error ; return U ; } L’algoritmo PID è semplice da utilizzare ma risulta complicata la taratura dei parametri. Per ogni asse è necessario trovare empiricamente i parametri Kp , τi , τd , non possedendo il modello matematico del processo. Essi dipendono fortemente dall’assetto del sistema e dai componenti utilizzati. Questa caratteristica del controllore PID si scontra leggermente con il concetto di Plug & Play che è alla base della progettazione di Marvin: infatti sostituendo lo smartphone potrebbe essere necessario ritarare i parametri del PI e i margini di tolleranza. Segue quindi una metodologia di taratura che può essere replicata in diversi contesti. 6.3. Taratura dei parametri Per tarare i parametri ci si è basati sulla seconda tecnica di Ziegler e Nichols – che prevede la taratura in catena chiusa – dove i parametri vengono trovati con il controllore collegato al sistema. Questa tecnica si basa sul trovare il guadagno critico Ku , cioè quel guadagno che porta la variabile controllata a presentare oscillazioni sostenute, oscillazioni, cioè, che non si esauriscono nel tempo. Ovviamente il guadagno critico si trova imponendo τi = τd = 0. Il Ku è importante perché fornisce una misura del ritardo e della dinamica del processo. Successivamente viene osservato il Tu , cioè il periodo di oscillazione. A partire da questi dati si applicano delle formule semi-empiriche per la taratura dei parametri. Per ogni asse sono stati eseguiti i seguenti passi: 1. Partendo dal controllore P, ponendo a zero le altre azioni, si è trovato Ku , cioè il guadagno che portasse all’oscillazione il sistema. Per trovare Ku si è incrementato per tentativi il guadagno partendo da un valore basso. 2. Le oscillazioni attorno al set-point, per ogni asse, sono state misurate “ad occhio”, non avendo avuto il tempo costruire una corretta misura dell’uscita oppure un esperimento di simulazione. In ogni caso trovato il Ku si misura il periodo di tempo che intercorre tra un’oscillazione e la successiva, indicandolo come Tu . 81 Capitolo 6. Il Sistema di Controllo Figura 6.6.: Ku e Tu 3. Trovati Ku e Tu si procede al calcolo dei parametri Kp , τi e τd , secondo la tabella costruita da Ziegler e Nichols. Controllore Kp τi τd P 0, 5Ku - - PI 0, 45Ku Tu 1,2 - PID 0, 6Ku Tu 2 Tu 8 4. Quindi si testa il sistema inserendo l’algoritmo completo del controllore desiderato con i parametri calcolati. 5. Si conclude il lavoro di taratura con un ulteriore fine tuning dei parametri calcolati per adattarli in maniera migliore al processo. I parametri calcolati per lo smartphone di riferimento sono: • Asse Y: Kp = 0, 17, τi = 1, 25, τd = 0; • Asse X: Kp = 0, 4, τi = 2, τd = 0; • Asse Z: Kp = 1, τi = 0, τd = 0;1 1È stato tarato anche un controllore PI con i seguenti parametri Kp = 1, 35, τi = 0, 5 ma non è usato nella build finale. 82 Capitolo 7. Conclusioni e sviluppi futuri In questo ultimo capitolo vengono riportati i test di funzionamento sul prototipo realizzato e un riepilogo dei risultati ottenuti, concludendo il lavoro con uno sguardo al futuro del progetto. 7.1. Test di funzionamento I test di funzionamento sono stati eseguiti attraverso diversi step. Nelle fasi iniziali di lavoro è stato necessario concentrarsi sull’importazione e sul setup del precedente sistema, nello studio e nella comprensione approfondita di come è stato sviluppato. Lo studio ha portato all’individuazione dei punti deboli del sistema. Da qui si è partiti per la progettazione e l’acquisto dei nuovi componenti: in primis Arduino e i Servomotori. I test di funzionamento si sono spostati sulla Flex, portando a capire come il microcontrollore è implementato all’interno della architettura progettata da Evidence, in che modo effettuare il debug e come l’architettura del dsPIC integrata nella Flex potessero essere utilizzate per lo sviluppo. I test di funzionamento si sono concentrati su come i vari tasks vengono schedulati da ERIKA e come creare i diversi oggetti del sistema operativo quali risorse, os, cpu, kernel ecc.. Contemporaneamente sono state sviluppate le librerie serialConsole.h, eeuart.h e utility.h. Lo studio del Vinculum II e del suo IDE è stato il secondo step anche successivamente è stato sostituito da Arduino e dalla libreria Microbridge ADB. Ulteriori test sono stati effettuati sia su Arduino creando Marvin.ino e la libreria Arduino.h sulla Flex per effettuare le comunicazioni UART. Diversi giorni sono stati impiegati per portare il sistema a funzionare nella configurazione: Arduino– Vinculum-II–Flex–Control Module, poi scartata per le basse prestazioni. Quindi si è passati, nel terzo step, allo studio delle periferiche tra cui timers, oscillatori e PWM, attraverso test di funzionamento sui motori, prima provando senza carico e successivamente inserendo un carico -la ruota- per studiarne le prestazioni in termini di velocità e stabilità. In contemporanea con le periferiche si è anche condotto lo studio dell’alimentazione creando il circuito di potenza e dividendo la breadboard in due linee di alimentazione. Test sull’elettronica sono stati condotti attraverso un multimetro e un oscilloscopio per verificare la forma d’onda della PWM. Una volta terminati i vari test sul lato hardware e scelta la configurazione Arduino-Flex- Servomotori si è passati allo studio di Android, attraverso debug dell’applicazione già esistente e progettando un diverso tipo di controllo del robot, non più secondo Aree ma utilizzando algoritmi tipici dell’automatica. I restanti test si sono concentratati appunto sull’applicazione ad alto livello e sulla la taratura dei parametri del PID. Sono stati ipotizzati anche test formali al fine di estrarre risultati numerici dal prototipo realizzato. L’idea di base consiste nel calcolare la velocità massima che un volto può assumere rimanendo riconoscibile e continuando ad essere inseguito dal sistema. La velocità è legata al numero di frames che il sistema riesce ad elaborare, e quindi si è stimato che processando 10 frame al secondo, un volto viene riconosciuto se ha una velocità minore di 10 cm/s. 83 Capitolo 7. Conclusioni e sviluppi futuri Studi più precisi possono essere condotti costruendo un sistema di test, come ad esempio una foto di un volto mossa da un motore a rotazione continua, dove sarebbe possibile impostare la velocità di rotazione del motore. Noto il diametro della ghiera e la velocità di rotazione, si potrebbe verificare effettivamente la velocità massima di riconoscimento dei volti dato un tetto massimo di frames per secondo. 7.2. Obiettivi raggiunti Attraverso i test eseguiti si è notato un generale raggiungimento di tutti i requisiti e degli obiettivi prefissati. Infatti il sistema è stato riprogettato rendendolo più “snello” e capace di evolversi nel tempo senza troppe difficoltà. La scheda Flex è stata studiata e si è dimostrato come sia possibile usarne le periferiche in maniera interessante, nonchè creare delle librerie che permettano una sempre più facile prototipizzazione ed accesso all’hardware. Il sistema ERIKA è stato approfondito attraverso esperimenti e ricerche in maniera tale da poter usare tutti gli oggetti avanzati e non, messi a disposizione per il programmatore dimostrando come anche su un sistema embedded e dalla capacità di calcolo ridotta sia possibile effettuare uno scheduling real-time. Il lavoro di ricerca si è spostato anche sul versante Arduino ed Android, mostrando le potenzialità e gli ambiti di applicazione soprattutto con la libreria Microbridge ADB, capace di eliminare il problema dei pochi smartphone compatibili e OpenCV per gli algoritmi di visione. Infine si è implementato un controllore PID funzionante, permettendo un ottimo inseguimento di volti, riutilizzabile all’occasione in molti ambiti diversi, tra quelli più affini -come l’inseguimento di oggetti- o per scopi più complessi manipolando gli errori e i Set points. Infine per una gestione manuale dei comandi di spostamento è stata costruita una sorta di “demo” sia in ambito Android che su Flex per inviare e ricevere i singoli comandi di direzione. Figura 7.1.: Architettura finale di Marvin 84 Capitolo 7. Conclusioni e sviluppi futuri 7.3. Sviluppi futuri Marvin è da considerarsi un vero è proprio laboratorio mobile, su cui sperimentare sia applicazioni a basso livello tra cui l’estensione della sensoristica e delle periferiche utilizzabili tra cui SPI, CAN bus, Bottoni, LCD l’inserimento un nuovo motore per l’attuazione lineare del movimento di testa in maniera tale che la testa, oltre a spazzare angoli, si muova linearmente attraverso un sistema meccanico che dia piena libertà di movimento lungo l’asse Y e sensoristica varia. E’ auspicabile la sostituzione del layer di Comunicazione attraverso porting della libreria ADB sulla Flex, oppure utilizzando un modulo bluetooth per scambiare i dati tra Flex e Android. Ad alto livello è possibile avere uno scenario molto più ampio di possibili applicazioni. Ad esempio, si potrebbe utilizzare il protocollo Voice XML per permettere di processare informazioni vocali e quindi estrapolare da essi comandi e attraverso un TTS (text-to-speech) “dare la parola” a Marvin utilizzando l’altoparlante dello smartphone. L’intelligenza artificiale trova grandi campi di applicazione su robot mobili tra cui la possibilità di tracciare percorsi, riconoscere path e imparare attraverso reti neurali. L’ambito di controllo automatico può essere esplorato oppure affiancato alla sensoristica esistente per l’esplorazione di ambienti sconosciuti e la creazione di mappe. Infine è possibile approfondire la computer vision sia implementando nuovi algoritmi di visioni tra quelli disponibili in OpenCV sia idearne dei nuovi. 85 Capitolo 7. Conclusioni e sviluppi futuri Figura 7.2.: Marvin 86 Appendice A. Dettagli di cablaggio Flex P51 (U1TX) P52 (U1RX) GND Arduino 19 (RX1) 18 (TX1) GND Tabella A.1.: Connessione Flex-Arduino tramite USART1 Figura A.1.: Connessione Flex-Arduino tramite USART1 Flex P50 (U2TX) P49 (U2RX) GND Convertitore PL2303 RX TX GND Tabella A.2.: Connessione Flex-Convertitore tramite USART2 87 Appendice A. Dettagli di cablaggio Figura A.2.: Connessione Flex-Convertitore tramite USART2 Flex P94 (PWM1H) +5V GND Hitec HB-485HS YELLOW RED BLACK Tabella A.3.: Connessione Flex-Servomotore di testa tramite PWM1 Figura A.3.: Connessione Flex-Servomotore di testa tramite PWM1 88 Appendice A. Dettagli di cablaggio Figura A.4.: Dettaglio del servomotore di testa Flex P99 (PWM2H) +5V da breadboard GND DFRobotic (SX) ORANGE RED BROWN Tabella A.4.: Connessione Flex-servomotore di propulsione tramite PWM2 Flex P3 (PWM3H) +5V da breadboard GND DFRobotic (DX) ORANGE RED BROWN Tabella A.5.: Connessione Flex-servomotore di propulsione tramite PWM3 Figura A.5.: Connessione Flex-servomotori di propulsione tramite PWM2 e PWM3 89 Appendice A. Dettagli di cablaggio Figura A.6.: Dettaglio dei servomotori di propulsione alloggiati sotto la struttura Flex P25 (DGTL-OUTP) P26 (DGTL-OUTP) Led ORANGE YELLOW Tabella A.6.: Connessioni Flex-led Arduino 24 (Digital) 22 (Digital) Led GREEN RED Tabella A.7.: Connessioni Arduino-led Figura A.7.: Connessione Flex e Arduino ai led presenti sulla breadboard 90 Appendice A. Dettagli di cablaggio Figura A.8.: Circuito di alimentazione e linee di tensione Figura A.9.: Connessione Arduino-Smartphone tramite cavo USB 91 Appendice B. Software utilizzato In questa Appendice vengono riportate le versioni dei software utilizzati nello sviluppo di Marvin1 . Programmazione della scheda FLEX • Java JRE 1.6.0_27 a 32-bit • Cygwin 1.7.9 • ERIKA Enterprise e RT-Druid 1.6.1 • Microchip MPLAB IDE v8.43 • Microchip C30 Compiler v3.30c • RealTerm per il Debug Programmazione della scheda Arduino • Arduino 1.0 • Microbridge ADB (modificata) Programmazione dell’applicazione Android • Eclipse IDE for Java Developers, INDIGO (v3.7.1) • Android SDK • ADT Plugin per Eclipse • Android 2.3.4 (Google API 10) installati tramite l’Android SDK Manager Programmazione del Vinculum-II • Vinculum-II toolchain v 1.4.4 • Drivers per V2DIP 1I software citati sono tutti disponibili nella cartella “software” nel CD della tesi. 92 Bibliografia [1] “dsPIC33FJXXXMCX06/X08/X10 Motor Control Family Data Sheet.” [Online]. Available: http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en024663#1 [2] “Microchip dsPIC – wiki page for ERIKA.” [Online]. Available: http://erika.tuxfamily.org/wiki/ index.php?title=Microchip_dsPIC [3] “Erika Enterprise Conformance Classes Comparison Guide.” [Online]. Available: tuxfamily.org/download/manuals/pdf/ee_porting_1_0_1.pdf [4] “Erika Enterprise reference manual.” [Online]. Available: manuals/pdf/ee_refman_1_4_4.pdf http://erika. http://erika.tuxfamily.org/download/ [5] “RT-Druid reference manual.” [Online]. Available: http://erika.tuxfamily.org/download/manuals/ pdf/rtdruid_refman_1_5.0.pdf 93