Una Introduzione ad Arduino
Transcript
Una Introduzione ad Arduino
Introduzione La Testa Robotica Una Introduzione ad Arduino Prof. Michele Scarpiniti Dipartimento di Ingegneria dell’Informazione, Elettronica e Telecomunicazioni “Sapienza” Università di Roma http://ispac.diet.uniroma1.it/scarpiniti/index.htm [email protected] M. Scarpiniti Una Introduzione ad Arduino 1 / 69 Introduzione La Testa Robotica 1 Introduzione Introduzione L’IDE Gli esempi 2 La Testa Robotica L’hardware Il software Il funzionamento M. Scarpiniti Una Introduzione ad Arduino 2 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Introduzione Introduzione M. Scarpiniti Una Introduzione ad Arduino 3 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Introduzione L’obiettivo di queste slides è di introdurre ed illustrare il funzionamento della scheda Arduino attraverso semplici esempi. Si vedrà, in particolare, l’applicazione di Arduino nel controllare una testa robotica in grado di localizzare una sorgente in movimento all’interno di una stanza attraverso una coppia di microfoni (orecchi) e una coppia di webcam (occhi). Maggiori informazioni su Arduino e accessori possono essere reperite al link http://www.arduino.com/ da cui è anche possibile scaricare l’ambiente grafico (IDE) per la programmazione del dispositivo. M. Scarpiniti Una Introduzione ad Arduino 4 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Arduino in sintesi Arduino è una piccola scheda equipaggiata con un microcontrollore e una circuiteria di contorno, utile per creare rapidamente prototipi. Con Arduino si possono realizzare in maniera relativamente rapida e semplice, piccoli dispositivi per il controllo di sensori e attuatori. Completamente italiano, il progetto di Arduino è stato sviluppato presso l’Interaction Design Institute, un istituto di formazione post-dottorale con sede a Ivrea, fondato da Olivetti e Telecom Italia. M. Scarpiniti Una Introduzione ad Arduino 5 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La storia di Arduino Il nome della scheda deriva da quello di un bar di Ivrea frequentato da alcuni dei fondatori del progetto. Il progetto ha preso avvio in Italia ad Ivrea, nel 2005, con lo scopo di rendere disponibile, a progetti di Interaction design realizzati da studenti, un device per il controllo che fosse più economico rispetto ad altri sistemi di prototipazione disponibili all’epoca. I progettisti sono riusciti nell’intento di creare una piattaforma di semplice utilizzo ma che, al tempo stesso, permettesse una significativa riduzione dei costi rispetto a molti prodotti disponibili sul mercato. A ottobre 2008 erano già stati venduti più di 50.000 esemplari di Arduino in tutto il mondo. A partire dal 2015, per via di alcune scelte dei creatori del progetto, il marchio Arduino è disponibile solo sul mercato americano. Nel resto del mondo, le stesse schede vengono commercializzate con il nuovo marchio Genuino. M. Scarpiniti Una Introduzione ad Arduino 6 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Arduino UNO (Genuino UNO) La scheda più comune e diffusa della famiglia Arduino è senz’altro Arduino UNO (ovvero Genuino UNO, in Europa), equipaggiato di un micro-controllore ATmega328P della Atmel. E’ poi presente una porta USB per interagire con il PC e caricare il software e vari connettori per l’I/O, in particolare: 1 14 connettori per l’I/O digitale (numerati da 0 a 13); 2 6 connettori specificamente dedicati a ingressi di segnali analogici (collegati quindi ad una ADC). M. Scarpiniti Una Introduzione ad Arduino 7 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Arduino UNO: specifiche tecniche Le specifiche tecniche di Arduino UNO sono riportate nella seguente tabella. Microcontrollore Operating Voltage Input Voltage (recommended) Input Voltage (limit) Digital I/O Pins PWM Digital I/O Pins Analog Input Pins DC Current per I/O Pin DC Current for 3.3V Pin Flash Memory SRAM EEPROM Clock Speed Length Width Weight M. Scarpiniti Una Introduzione ad Arduino ATmega328P 5V 7-12 V 6-20 V 14 (of which 6 provide PWM output) 6 6 20 mA 50 mA 32 KB of which 0.5 KB used by bootloader 2 KB 1 KB 16 MHz 68.6 mm 53.4 mm 25 g 8 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Arduino UNO: l’anatomia della scheda Descriviamo nello specifico le varie parti e i vari componenti della scheda Arduino UNO. M. Scarpiniti 1 Pin digitali: per leggere da sensori e scrivere su attuatori; 2 LED pin 13: utile per il debugging ; 3 LED accensione: indica se la scheda è accesa; 4 Microcontrollore ATmega: il cuore della scheda; 5 Ingressi analogici: per leggere valori analogici; 6 Pin alimentazione GND e 5V: per fornire l’alimentazione a ai dispositivi collegati; 7 Connettore alimentazione: per alimentare la scheda se non connessa tramite porta USB. Accetta tensioni nell’intervallo 7-12 V; 8 LED di TX e RX: indicano la comunicazione tra la scheda e il computer; 9 Porta USB: per collegare la scheda al computer; 10 Tasto di Reset: per resettare il microcontrollore. Una Introduzione ad Arduino 9 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Altre schede della famiglia Arduino Esistono diverse varianti alla scheda Arduino/Genuino UNO. Le più diffuse sono elencate nella seguente tabella. Entry Level Enhanced Features Internet of Things Genuino UNO, Genuino 101, Genuino MICRO Genuino MEGA, Genuino ZERO, Genuino Yùn Genuino MKR1000 In particolare, l’ultima scheda è stata progettata per offrire una soluzione pratica e conveniente per coloro i quali cercano di aggiungere ai loro progetti la connettività Wi-Fi con il minimo sforzo. M. Scarpiniti Una Introduzione ad Arduino 10 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi L’IDE di Arduino L’ambiente di sviluppo integrato (IDE) di Arduino è un’applicazione multi-piattaforma scritta in Java, che include un editore di testo dotato di syntax highlighting, il controllo delle parentesi e indentazione automatica. L’editor è inoltre in grado di compilare e lanciare il programma eseguibile in una sola passata e con un solo click. La sintassi è C-like, ma molto più intuitiva. L’IDE è utilizzata per scrivere i codici sorgenti che contengono tutte le istruzioni necessarie affinché il computer possa controllare tutte le funzionalità di Arduino. Tali codici sono chiamati sketch. L’IDE si occupa anche di trasferire tali istruzioni sulla scheda (upload) in modo tale che Arduino funzioni anche se non più connesso al calcolatore. M. Scarpiniti Una Introduzione ad Arduino 11 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi L’IDE di Arduino: il Monitor Seriale L’IDE di Arduino mette a disposizione uno strumento molto comodo, chiamato monitor seriale. Il monitor seriale è uno strumento che permette di leggere i dati che Arduino comunica tramite la porta seriale (COM) al computer e consente anche di stampare alcuni messaggi utili per testare il corretto funzionamento dell’applicazione. L’interfaccia del monitor seriale è rappresentata di seguito. M. Scarpiniti Una Introduzione ad Arduino 12 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Le funzioni principali dell’IDE di Arduino Quando si apre l’IDE per scrivere un nuovo sketch, sul prompt compaiono le definizioni di due funzioni da editare, che risultano fondamentali per il corretto funzionamento del dispositivo void setup () { // put your setup code here , to run once : } void loop () { // put your main code here , to run repeatedly : } Tali funzioni si occupano delle inizializzazioni di tutte le variabili e della definizione dei compiti che la scheda dovrà ripetere durante l’intero funzionamento. Eventuali altre funzioni, da utilizzare all’interno di loop(), possono essere definite a seguire. M. Scarpiniti Una Introduzione ad Arduino 13 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La funzione setup() La funzione setup() viene chiamata all’inizio del programma e viene utilizzata per inizializzare tutte le variabili, i vari pin e per linkare tutte le librerie necessarie. Questa funzione viene eseguita una sola volta, all’avvio dell’applicazione. Segue un esempio: int buttonPin = 3; void setup () { Serial . begin (9600) ; pinMode ( buttonPin , INPUT ) ; } In questo esempio, si crea una variabile buttonPin, che rappresenta il terzo pin digitale, impostandola come un input e abilitando la comunicazione seriale a 9600 b/s. L’intento è dunque, di leggere il livello di tensione (basso o alto) dal pin 3. M. Scarpiniti Una Introduzione ad Arduino 14 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La funzione loop() La funzione loop(), chiamata subito dopo la setup(), è iterata costantemente per tutta la durata dell’applicazione, eseguendo ad ogni ciclo le istruzioni presenti nel suo blocco. Tale funzione è dunque utilizzata per la descrizione dei compiti che la scheda dovrà compiere. Segue un esempio: void loop () { if ( digitalRead ( buttonPin ) == HIGH ) Serial . println ( ’H ’) ; else Serial . println ( ’L ’) ; delay (1000) ; } In tale esempio, si legge la tensione dal pin 3, impostato precedentemente, e se la tensione è alta si scrive H nel monitor seriale, altrimenti L. Dopo ogni lettura del pin si attende un secondo. M. Scarpiniti Una Introduzione ad Arduino 15 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Le funzioni principali Nelle funzioni precedenti sono state utilizzate alcune funzioni fondamentali, di seguito descritte. Serial.begin(speed): setta la velocità di trasmissione tramite la porta seriale al valore indicato; pinMode(pin, mode): configura il comportamento (INPUT o OUTPUT) del pin indicato; digitalRead(pin): legge il valore digitale (HIGH o LOW) dal pin indicato; Serial.println(’stringa’): stampa un valore o una stringa sul monitor seriale, andando poi a capo; delay(ms): mette in pausa il programma per un numero di millisecondi specificato. M. Scarpiniti Una Introduzione ad Arduino 16 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Le librerie principali Molte funzionalità utili nelle applicazioni, sono raccolte in librerie di funzioni disponibili nell’IDE. Un esempio è la libreria Serial utilizzata precedentemente, che consente la comunicazione seriale, oppure la Servo che implementa i metodi per la gestione dei servomeccanismi. Per utilizzare nell’IDE una certa libreria, la si deve includere con la direttiva # include < Libreria .h > Ad esempio, se volessi utilizzare la libreria Servo: # include < Servo .h > void setup () { // il mio codice } void loop () { // il mio codice } M. Scarpiniti Una Introduzione ad Arduino 17 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Le librerie principali Anche se esistono alcune librerie specifiche solo per alcuni modelli di Arduino, è disponibile un insieme di librerie standard funzionanti su tutti i dispositivi. Nello specifico Libreria EEPROM Ethernet Firmata GSM LiquidCrystal SD Serial Servo SPI SoftwareSerial Stepper TFT WiFi Wire M. Scarpiniti Descrizione Lettura e scrittura su supporti permanenti Per utilizzare una scheda di rete opzionale Per comunicare attraverso un protocollo seriale standard Per utilizzare una scheda GSM opzionale Per il controllo di display a cristalli liquidi (LCD) Lettura e scrittura su SD card Per le comunicazioni seriali con PC e serial monitor Per il controllo dei servo-motori Per comunicare attraverso il bus Serial Peripheral Interface (SPI) Per le comunicazioni seriali verso i pin digitali Per il controllo di motorini passo-passo Per comandare schermi TFT Per la connessione wireless attraverso un’apposita scheda Per ricevere/inviare dati dalle interfacce TWI/I2C Una Introduzione ad Arduino 18 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La libreria Serial La libreria Serial è fondamentale per qualsiasi applicazione, poiché abilita la comunicazione da e verso computer ed inoltre abilita l’utilizzo del monitor seriale. Non è possibile immaginare di lavorare senza. Per questo motivo, tale libreria è predefinita nell’IDE di Arduino e non è necessaria includerla manualmente con la direttiva #include. Tale libreria include 21 funzioni. Oltre la begin(), le più importanti sono: available(): restituisce il numero di byte disponibili nel buffer di lettura (che ne può contenere fino a 64); end(): termina la comunicazione seriale; peek(): restituisce il byte (o carattere) seriale successivo , senza rimuoverlo dal buffer; print(), println(): stampa un valore (o una stringa) sulla porta seriale o monitor seriale. Il secondo comando manda a capo; read(): legge i dati dalla connessione seriale; setTimeout(): configura il tempo massimo (in millisecondi) di attesa per la comunicazione seriale; write(): scrive dati binari alla porta seriale. M. Scarpiniti Una Introduzione ad Arduino 19 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La libreria Serial Tale libreria (Serial) è quindi utilizzata per la comunicazione tra la scheda Arduino e il computer o altri dispositivi. Tutte le schede Arduino hanno almeno una porta seriale (anche nota come UART o USART). La comunicazione avviene sul pin digitale 0 (RX, per la trasmissione) e sul pin digitale 1 (TX, trasmissione) e con il computer tramite la porta USB. Quindi, se si utilizzano le funzioni di tale libreria, non è possibile utilizzare i pin 0 e 1 per l’ingresso o l’uscita digitale da eventuali sensori/attuatori. È anche possibile utilizzare l’ambiente monitor seriale, incorporato in Arduino, per comunicare con la scheda. Facendo clic sul pulsante del monitor seriale nella barra degli strumenti e selezionando la stessa velocità di trasmissione utilizzata nella chiamata di Serial.begin(), si aprirà una finestra in cui è possibile stampare (scrivere) valori ricevuti (trasmessi) dalla scheda. M. Scarpiniti Una Introduzione ad Arduino 20 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi La modulazione PWM La modulazione a larghezza di impulso (Pulse Width Modulation o PWM) può essere usata in Arduino per ottenere valori analogici a partire da segnali digitali. Un segnale PWM è, in effetti, un’onda quadra che varia da 0 V a 5 V, con frequenza costante, ma in cui è variabile la frazione di tempo in cui il segnale è attivo a 5 V (detto dutycycle), che può variare tra lo 0 e il 100 %. La modulazione PWM è implementata in Arduino tramite la funzione analogWrite ( pin , dutyCycle ) dove dutyCycle è un valore tra 0 e 255, e pin è uno dei pin PWM (3, 5, 6, 9, 10 o 11). Il comando analogWrite non fornisce alcun controllo sulla frequenza. Il valore della tensione di uscita del pin sarà una frazione dei 5 V, proporzionale al dutycycle: è quindi possibile far variare con continuità una tensione da 0 a 5 V. M. Scarpiniti Una Introduzione ad Arduino 21 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Alcuni semplici esempi L’IDE di Arduino mette a disposizione un certo numero di esempi molto utili per chi è alle prime armi. Infatti, attraverso questi esempi, è possibile imparare l’utilizzo delle funzioni e delle librerie principali. Tali esempi sono suddivisi per categoria (analogico, digitale, comunicazione, controllo, sensori, display, USB, ecc.) e sono accessibili dal menù File => Esempi. Alcuni di questi esempi sono descritti approfonditamente nel testo “Genuino Projects Book”, che è venduto insieme allo starter kit, che contiene, oltre alla scheda Genuino UNO, numerosi altri componenti per implementare gli esempi proposti. E’ comunque possibile trovare numerosi altri esempi in rete oppure nei tanti testi dedicati alla scheda, facilmente reperibili in commercio. M. Scarpiniti Una Introduzione ad Arduino 22 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Far lampeggiare un LED Il primo classico esempio consiste nel far lampeggiare un LED. Si collega un LED rosso, con in serie un resistore, tra il pin digitale 13 e il pin GND, come schematicamente indicato nelle figure seguenti. Si programma quindi la scheda per inviare alternativamente una tensione alta (5 V) e una bassa (0 V) al pin 13. Il resistore R serve per limitare il valore della corrente che scorre nel LED, per non bruciarlo ed il suo valore è calcolato come R= V − VLED 5 − 1.7 ≡ = 220 Ω, I 15 · 10−3 in cui I è la corrente (di solito intorno ai 15 ÷ 20 mA) e VLED = 1.7 V per il LED rosso. M. Scarpiniti Una Introduzione ad Arduino 23 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Far lampeggiare un LED Lo sketch di Arduino che fa lampeggiare il LED è il seguente. void setup () { pinMode (13 , OUTPUT ) ; output } // pin digitale 13 impostato come Si inizializza, cioè, il pin digitale 13 come uscita. void loop () { digitalWrite (13 , HIGH ) ; delay (1000) ; digitalWrite (13 , LOW ) ; delay (1000) ; } // // // // si si si si accende il LED aspetta 1 secondo spegne il LED apsetta 1 secondo Si ripete ciclicamente: si invia tensione alta in uscita al pin 13 (LED acceso), si aspetta 1 secondo, si invia tensione bassa (LED spento), si aspetta 1 secondo e cosı̀ via finché non si stacca la scheda. Quindi ogni secondo il LED si accende e si spegne. M. Scarpiniti Una Introduzione ad Arduino 24 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Far sfumare l’intensità di un LED Si vuol far variare con continuità l’intensità in un LED, aumentandola gradualmente fino ad un valore massimo e poi diminuendola fino a spegnere il LED e poi di nuovo aumentandola. A tal proposito si utilizza la modulazione PWM. Il LED sarà dunque collegato ad un pin analogico (il pin 9), controllato con un dutycycle variabile da 0 a 255 e di nuovo a 0, attraverso la funzione analogWrite(pin, dutyCycle). Anche in questo caso il resistore R1 è determinato come nel caso precedente. M. Scarpiniti Una Introduzione ad Arduino 25 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Far sfumare l’intensità di un LED Lo sketch di Arduino inizia con la definizione di tre variabile che indicano il numero del pin a cui collegare il LED, il valore della luminosità iniziale del LED e di quanto far aumentare la luminosità dopo un certo intervallo temporale. Si sceglie il pin numero 9 che è uno di quelli abilitati alla modulazione PWM, con cui si farà variare l’intensità. int led = 9; int brightness = 0; int fadeAmount = 5; // il pin PWM del LED // luminosità del LED // variazione della luminosità del LED La funzione setup() imposta semplicemente il pin a cui è collegato il LED (il numero 9) come un pin di uscita. void setup () { pinMode ( led , OUTPUT ) ; } M. Scarpiniti Una Introduzione ad Arduino // il pin 9 è impostato come output 26 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Far sfumare l’intensità di un LED La funzione loop() imposta inizialmente il valore di luminosità iniziale (pari a zero) e quindi il LED risulta spento. Si aumenta quindi l’intensità del valore prestabilito fadeAmount, si aspetta per 30 millisecondi e si reimposta la nuova luminosità con analogWrite(). Se nel frattempo si è raggiunta la luminosità massima, cioè l’onda quadra è diventata una costante con dutycycle pari a 255, la variabile fadeAmount viene impostata negativa in modo da diminuire gradualmente l’intensità. Viceversa, quando il dutycycle è divenuto nullo, la variabile fadeAmount viene reimpostata positiva per aumentare l’intensità e cosı̀ via. void loop () { analogWrite ( led , brightness ) ; // impostare la luminosità brightness = brightness + fadeAmount ; luminosità // cambio di if ( brightness == 0 || brightness == 255) { // raggiunto il limite massimo si inverte la luminosità fadeAmount = - fadeAmount ; } delay (30) ; // aspetta 30 millisecondi } M. Scarpiniti Una Introduzione ad Arduino 27 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Rilevatore di vibrazione Il prossimo esempio, consiste in un rilevatore di vibrazione attraverso un sensore piezoelettrico. Se il sensore piezoelettrico vibra, produce una tensione ai suoi capi che può essere letta attraverso un pin di ingresso analogico della scheda. Il sensore è collegato ai pin GND e A0 (il primo ingresso analogico disponibile). Si collegherà in seguito un LED al pin digitale 13, che si accende se il sensore rileva una vibrazione sufficientemente grande. Per proteggere il sensore piezoelettrico da tensioni e correnti eccessive, viene inserito in parallelo un resistore da 1 MΩ. M. Scarpiniti Una Introduzione ad Arduino 28 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Rilevatore di vibrazione Lo sketch di Arduino inizia con la definizione di tre costanti che indicano il numero del pin a cui collegare il LED, il numero del pin analogico a cui collegare il sensore e la soglia per determinare se c’è stata una vibrazione consistente. Si dichiarano poi una variabile per leggere il valore del sensore e una variabile che indica lo stato del LED, inizializzata a LOW. const int ledPin = 13; // LED colleg . al pin digitale 13 const int knockSensor = A0 ; // piezo colleg . al pin analogico 0 const int threshold = 100; // soglia int sensorReading = 0; int ledState = LOW ; // valore del piezo La funzione setup() imposta semplicemente il pin a cui è collegato il LED (il numero 13) come un pin di uscita e inizializza una comunicazione seriale a 9600 baud. void setup () { pinMode ( ledPin , OUTPUT ) ; Serial . begin (9600) ; } M. Scarpiniti Una Introduzione ad Arduino // pin del LED impostato OUTPUT // si utilizza la porta seriale 29 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Rilevatore di vibrazione La funzione loop() legge inizialmente il valore di tensione generato dal sensore, attraverso la funzione analogRead() che restituisce un intero compreso tra 0 e 1023. Se questo valore supera il valore di soglia threshold, lo stato del LED viene cambiato in HIGH e quindi acceso tramite digitalWrite(), mentre nel monitor seriale viene stampato un messaggio. Si attenono poi 100 millisecondi per evitare problemi di sincronizzazione e il programma termina. void loop () { sensorReading = analogRead ( knockSensor ) ; del sensore // legge il valore if ( sensorReading >= threshold ) { // se supera la ledState = ! ledState ; // stato del LED digitalWrite ( ledPin , ledState ) ; // accende il Serial . println ( " Knock ! " ) ; // stampa " Knock !" seriale } delay (100) ; // aspetta 100 millisecondi soglia è HIGH LED sul monitor } M. Scarpiniti Una Introduzione ad Arduino 30 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Utilizzo di un accelerometro Nel prossimo esempio si vuole leggere i dati relativi alle accelerazioni nelle tre direzioni x, y e z attraverso un accelerometro della famiglia ADXL3xx (prodotti principalmente da SparkFun e Adafruit), che è un sensore analogico. Lo schema di collegamento è rappresentato nelle seguenti figure, anche se è possibile avere configurazioni differenti per sensori di diversi produttori (attenzione ai valori di alimentazione). L’accelerometro della famiglia ADXL3xx è collegato a tutti i pin analogici A0-A5, secondo il seguente schema ADXL3xx Pin Arduino Pin Analogico M. Scarpiniti Self-Test 0 Una Introduzione ad Arduino z-axis 1 y-axis 2 x-axis 3 Ground 4 VDD 5 31 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Utilizzo di un accelerometro Si dichiarano tutti e 5 i pin da utilizzare. Poiché si vuole utilizzare i pin A4 e A5 come alimentazione, li si dichiarerà come pin digitali (rispettivamente il 18 e 19). const const const const const int int int int int groundpin = 18; powerpin = 19; xpin = A3 ; ypin = A2 ; zpin = A1 ; // // // // // pin A4 -- Ground pin A5 -- VDD x - axis y - axis z - axis A questo punto, si inizializza una comunicazione seriale a 9600 baud, si impostano i pin 18 e 19 come uscite, settandole a livello LOW (Ground) e HIGH (VDD). void setup () { Serial . begin (9600) ; // Non necessario se si utilizzano i pin 5 V e Gnd : pinMode ( groundpin , OUTPUT ) ; pinMode ( powerpin , OUTPUT ) ; digitalWrite ( groundpin , LOW ) ; digitalWrite ( powerpin , HIGH ) ; } Se per l’alimentazione si utilizzano i pin 5V e Gnd è possibile commentare le righe precedenti e la dichiarazione dei pin 18 e 19. M. Scarpiniti Una Introduzione ad Arduino 32 / 69 Introduzione La Testa Robotica Introduzione L’IDE Gli esempi Utilizzo di un accelerometro Nella funzione loop() si legge semplicemente i valori dei tre pin A1, A2 e A3 attraverso al funzione analogRead(), stampando il valore sul monitor seriale insieme ad una tablatura per incolonnare i risultati. Si attendono quindi 100 millisecondi e si ricomincia a leggere e stampare i nuovi valori finché non si spegne la scheda. void loop () { Serial . print ( analogRead ( xpin ) ) ; Serial . print ( " \ t " ) ; Serial . print ( analogRead ( ypin ) ) ; Serial . print ( " \ t " ) ; Serial . print ( analogRead ( zpin ) ) ; Serial . println () ; // Stampa del valore lungo x // Stampa di una tablatura // Stampa del valore lungo y // Stampa del valore lungo z delay (100) ; // Si attendono 100 millisecondi prima della prossima lettura } Si osservi che non si è utilizzato il pin di self-test. M. Scarpiniti Una Introduzione ad Arduino 33 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La Testa Robotica La Testa Robotica M. Scarpiniti Una Introduzione ad Arduino 34 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La testa robotica: Bender L’applicazione principale consiste nel costruire una testa robotica in grado di seguire una sorgente sonora in movimento all’interno di un ambiente. La testa robotica, denominata Bender, ha dimensione di 21 x 16 x 13 cm ed è equipaggiata con due microfoni, che fungono da orecchi, e due webcam, che fungono da occhi. E’ poi presente un algoritmo di localizzazione binaurale, basato sulle registrazioni dei due microfoni e un algoritmo di face detection, basato una sola webcam, che viene utilizzato per correggere gli errori dovuti al riverbero dell’ambiente. M. Scarpiniti Una Introduzione ad Arduino 35 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La testa robotica: Bender Più in dettaglio, i microfoni utilizzati sono due AKG-c562cm, che sono microfoni a condensatore con eccellenti caratteristiche di radiazione, come messo in evidenza dalla figura seguente a sinistra. Le webcam utilizzate sono due comuni webcam a bassa risoluzione 640 x 480. I movimenti sono controllati da tre servomotori, precisamente un servomotore digitale (pan del collo), un micro-servomotore digitale (tilt degli occhi) e un micro-servomotore analogico (pan degli occhi). M. Scarpiniti Una Introduzione ad Arduino 36 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La testa robotica: Bender I tre servomotori devono essere alimentati con una alimentazione in continua di 5 V. Poiché tali attuatori non assorbono troppa corrente, è possibile alimentarli direttamente tramite la scheda Arduino, utilizzano i pin di alimentazione 5V e GND. Tali pin sono collegati alle linee di alimentazione della breadboard ai cui verranno successivamente collegate le alimentazioni dei servomotori. Nel caso sia richiesta molta corrente da parte di un attuatore, sarà necessario utilizzare un alimentatore esterno. I servomotori possiedono un terzo cavo (di colore giallo) per il loro controllo. Si utilizzeranno a tal proposito i pin: 3: per il pan del collo; 5: per il pan degli occhi; 6: per il tilt degli occhi. M. Scarpiniti Una Introduzione ad Arduino 37 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La libreria Servo Per utilizzare i servomotori, è necessario utilizzare alcune funzioni contenute nella libreria Servo, già presente nella distribuzione installata dell’IDE di Arduino. Per creare un oggetto di tale classe, si dichiara una variabile di tipo Servo: Servo myservo ; Le sei funzioni della libreria sono elencate di seguito: attach(pin): collega la variabile myservo al pin indicato; read(): legge l’angolo corrente del servomotore; write(angle): sposta il servomotore all’angolo indicato. Valore compreso tra 0 e 180, dove 90 indica la posizione centrale di riposo; writeMicroseconds(us): invia al servomotore un valore in microsecondi con un conseguente spostamento del dispositivo. Di solito è un valore compreso tra 1000 e 2000 µs, con valore centrale pari a 1500 µs, ma può dipendere dal particolare servomotore; attached(): verifica se la variabile myservo è collegata a qualche pin; detach(): scollega la variabile myservo dal pin in cui era collegata. M. Scarpiniti Una Introduzione ad Arduino 38 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Lo sketch in Arduino Lo sketch Arduino per utilizzare la testa robotica è il seguente: # include < Servo .h > Servo eyepan ; Servo eyetilt ; Servo neckpan ; Si dichiarano quindi le tre variabili eyepan, eyetilt e neckpan che descrivono i tre servomotori. void setup () { eyepan . attach (5) ; eyetilt . attach (6) ; neckpan . attach (3) ; eyepan . w r i t e M i c r o s e c o n d s (1280) ; eyetilt . w r i t e M i c r o s e c o n d s (1680) ; neckpan . w r i t e M i c r o s e c o n d s (1475) ; Serial . begin (9600) ; } La funzione setup(), collega le tre variabili ai pin 3, 5 e 6, inizializzando la posizione dei servomotori. Si apre poi una comunicazione seriale a 9600 baud. M. Scarpiniti Una Introduzione ad Arduino 39 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Lo sketch in Arduino Viene quindi creato un buffer di sei elementi di tipo carattere: char buf []={0 ,0 ,0 ,0 ,0 ,0}; Non appena sono disponibili 6 byte, vengono memorizzati nel buffer appena creato. La funzione readBytes() restituisce caratteri. Questi caratteri sono convertiti in numeri interi con la funzione convert() e quindi passati al writeMicroseconds() che fa spostare i servomotori quanto specificato. void loop () { if ( Serial . available () >5) { int num = Serial . readBytes ( buf ,6) ; if ( num ==6) { int * val = convert ( buf ) ; eyepan . w r i t e M i c r o s e c o n d s ( val [0]) ; eyetilt . w r i t e M i c r o s e c o n d s ( val [1]) ; neckpan . w r i t e M i c r o s e c o n d s ( val [2]) ; } } } Si noti che i 6 caratteri, cioè 6 byte, equivalgono a 3 interi (un intero è 2 byte). M. Scarpiniti Una Introduzione ad Arduino 40 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Lo sketch in Arduino La conversione dai 6 caratteri e ai 3 interi è effettuata con la seguente funzione. int * convert ( char arr []) { void * vpt = arr ; int * pt = ( int *) vpt ; return pt ; } In pratica si dichiara un puntatore all’array di caratteri e si esegue un’operazione di casting (conversione di tipo) verso il tipo intero: int * pt = ( int *) vpt ; Alla fine si restituisce tale puntatore. M. Scarpiniti Una Introduzione ad Arduino 41 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Il driver della testa robotica consiste in un file di header, denominato BenderWin.h, che contiene alcune funzioni per la gestione della stessa, come, ad esempio, l’inizializzazione, la connessione, la lettura e l’invio della posizione e la limitazione dell’escursione massima consentita dei servomotori. Nelle applicazioni che utilizzeranno la testa robotica, si dovrà includere il file BenderWin.h. Tale file è scritto per il sistema operativo Windows, ma è disponibile anche la versione BENDER.h per Mac OS e Linux. Le funzioni implementate sono le seguenti: void void bool int bool void Serial ( char * portName ) ; Serial () ; IsConnected () ; ReadData ( char * buffer , unsigned int nbChar ) ; WriteData ( uint16_t * buffer , unsigned int nbChar ) ; constrain ( uint16_t & eyepan , uint16_t & eyetilt , uint16_t & neckpan ) ; void sendAngles ( uint16_t eyepan , uint16_t eyetilt , uint16_t neckpan ) ; double map ( double x , double a , double b , double c , double d ) ; void c o m p u t e A n d S e n d A n g l e s ( double x0 , double y0 , double z0 ) ; M. Scarpiniti Una Introduzione ad Arduino 42 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Si inizia con l’inclusione di tutte le librerie necessarie e la definizione di una variabile ARDUINO WAIT TIME impostata a 2 secondi. # include < windows .h > # include < stdlib .h > # include < stdio .h > # include < stdint .h > # include < tchar .h > # include < string > # include < unistd .h > # include < fcntl .h > # include < math .h > # define A R D U I N O _ W A I T _ T I M E 2000 Si passa quindi alla dichiarazione di alcune variabili utili per la gestione della comunicazione con la testa robotica. HANDLE hSerial ; bool connected ; COMSTAT status ; DWORD errors ; M. Scarpiniti // // // // // HANDLE è un tipo di dato trasparente usato come PUNTATORE ad una risorsa Stato della connessione Informazione sulla connessione Mantiene traccia dell ’ ultimo errore Una Introduzione ad Arduino 43 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La prima funzione Serial(portName), crea una connessione attraverso la porta indicata da portName. void Serial ( char * portName ) { connected = false ; // Non ancora connessi hSerial = CreateFile ( portName , // Crea una connessione GENERIC_READ | GENERIC_WRITE , // Tipo di connessione 0, // Condivisioni NULL , // Non può generare processi figli OPEN_EXISTING , // Apre la risorsa solo se esiste FILE_ATTRIBUTE_NORMAL , // Attributi base NULL ) ; // Eventuale handle se non esiste la risorsa La funzione CreateFile(), definita in windows.h, ha il compito di creare la connessione seriale M. Scarpiniti Una Introduzione ad Arduino 44 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender A questo punto si verifica la connessione. Se non si è trovata la porta indicata attraverso la funzione GetLastError(), viene stampato il relativo messaggio di errore, altrimenti c’è stato un errore generico e si stampa semplicemente “ERRORE!!!”. if ( hSerial == I N V A L I D _ H A N D L E _ V A L U E ) // Verifica la connessione { // Se si è riscontrato un errore if ( GetLastError () == E R R O R _ F I L E _ N O T _ F O U N D ) printf ( " ERRORE : la porta % s non è disponibile .\ n " , portName ) ; else printf ( " ERRORE !!! " ) ; } M. Scarpiniti Una Introduzione ad Arduino 45 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Altrimenti, se non ci sono stati errori di connessione, si inizializzano i parametri della comunicazione seriale e se c’è effettivamente comunicazione (verificato tramite la funzione GetCommState()) tali parametri vengono impostati. else { // Se connesso si impostano i parametri DCB d cb Se r ia lP ar a ms = {0}; if (! GetCommState ( hSerial , & d cb S er ia l Pa ra ms ) ) // Stato corrente printf ( " Non si sono letti i parametri attuali ! " ) ; else { // Parametri per la scheda Arduino d cb Se ri a lP ar am s . BaudRate = CBR_9600 ; d cb Se ri a lP ar am s . ByteSize =8; d cb Se ri a lP ar am s . StopBits = ONESTOPBIT ; d cb Se ri a lP ar am s . Parity = NOPARITY ; In particolare vengono impostati il rate di trasmissione (BaudRate), quanti bit da trasmettere per byte (ByteSize), il numero di stop bits (StopBits) e se c’è il controllo di parità (Parity). M. Scarpiniti Una Introduzione ad Arduino 46 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Con la funzione SetCommState() si settano i parametri sulla scheda. Se c’è stato un errore si stampa un messaggio di errore, altrimenti si imposta lo stato della connessione (true) e si attendono due secondi prima di fare altro. // Verifica dell ’ applicazione dei parametri if (! SetCommState ( hSerial , & dc b Se ri al P ar am s ) ) { printf ( " ATTENZIONE : non sono stati impostati i parametri della Porta Seriale " ) ; } else { connected = true ; // Se tutto è andato bene Sleep ( A R D U I N O _ W A I T _ T I M E ) ; // Attende 2 s prima di resettare Arduino } } } } M. Scarpiniti Una Introduzione ad Arduino 47 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione Serial() (senza argomenti) si occupa di disconnettere la scheda, se precedentemente era connessa. void Serial () { if ( connected ) // Se era connessa { connected = false ; // Ora è disconnessa CloseHandle ( hSerial ) ; // L ’ handle viene chiuso } } La funzione CloseHandle(), definita in windows.h, si occupa di chiudere l’handel relativo ad un certo numero di oggetti, tra cui le connessioni seriali. Accetta come argomento di ingresso l’handle relativo alla risorsa da chiudere. M. Scarpiniti Una Introduzione ad Arduino 48 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione ReadData() legge un numero nbChar di caratteri, salvandoli nel vettore buffer. Dopo aver verificato lo stato della comunicazione, se sono arrivati un certo numero di caratteri maggiori di quelli da leggere, si pone la variabile di appoggio toRead al valore desiderato nbChar. int ReadData ( char * buffer , unsigned int nbChar ) { DWORD bytesRead ; // Numero di byte letti unsigned int toRead ; // Numero di byte da leggere Clea rCommErr or ( hSerial , & errors , & status ) ; porta seriale // Stato della if ( status . cbInQue >0) // Se è stato letto qualcosa { if ( status . cbInQue > nbChar ) // Se sono arrivati sufficienti dati { toRead = nbChar ; } M. Scarpiniti Una Introduzione ad Arduino 49 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Altrimenti si pone la variabile di appoggio toRead al numero di caratteri correntemente letti e li si legge con la funzione ReadFile(). else { toRead = status . cbInQue ; } // Cerca di leggere il numero di dati richiesto if ( ReadFile ( hSerial , buffer , toRead , & bytesRead , NULL ) && bytesRead != 0) { return bytesRead ; } } return -1; // Se non è stato letto nulla } M. Scarpiniti Una Introduzione ad Arduino 50 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione WriteData() invia alla porta seriale un buffer di dati lungo nbChar. A tal proposito, utilizza la funzione WriteFile() e se c’è stato un errore nella scrittura dei dati, si chiude la comunicazione con la funzione ClearCommError(). Ritorna un valore booleano a seconda dell’esito della scrittura dei dati. bool WriteData ( uint16_t * buffer , unsigned int nbChar ) { DWORD bytesSend ; // Cerca di scrivere il buffer sulla porta seriale if (! WriteFile ( hSerial , ( void *) buffer , nbChar , & bytesSend , 0) ) { Clea rCommErr or ( hSerial , & errors , & status ) ; // Status della porta seriale return false ; } else return true ; } M. Scarpiniti Una Introduzione ad Arduino 51 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione IsConnected() verifica semplicemente lo stato della connessione. bool IsConnected () { // Ritorna semplicemente lo stato della connessione return connected ; } La funzione map(), associa al valore x, il seguente valore y : y= (x − a) (d − c) + c. b−a Tale valore serve a mappare gli angoli di rotazione dei servomotori all’interno di un intervallo massimo di escursione, al fine di evitare danni meccanici. double map ( double x , double a , double b , double c , double return (( x - a ) *( d - c ) /( b - a ) ) + c ; } M. Scarpiniti Una Introduzione ad Arduino d){ 52 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione constrain() vincola la massima escursione che i servomotori potranno compiere, in modo da evitare angoli pericolosi che potrebbero danneggiare i dispositivi. void constrain ( uint16_t eyepan , uint16_t eyetilt , uint16_t neckpan ) { eyepan = eyepan <950?950:( eyepan >1610?1610: eyepan ) ; eyetilt = eyetilt <1370?1370:( eyetilt >1970?1970: eyetilt ) ; neckpan = neckpan <750?750:( neckpan >2200?2200: neckpan ) ; } Tali limiti corrispondono a: pan dell’occhio: [950 − −160] µs ≈ 70◦ ; tilt dell’occhio: [1370 − −1970] µs ≈ 70◦ ; pan del collo: [750 − −2200] µs ≈ 160◦ . M. Scarpiniti Una Introduzione ad Arduino 53 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender La funzione sendAngles invia i dati relativi agli angoli dei tre servomotori attraverso la connessione seriale. Tale funzione, dopo aver vincolato la massima escursione tramite constrain(), scrive i tre angoli (contenuti nell’array data) utilizzando la funzione WriteData() vista precedentemente. Nel caso di errore stampa un messaggio. void sendAngles ( uint16_t eyepan , uint16_t eyetilt , uint16_t neckpan ) { uint16_t data [3]; data [0]= eyepan ; data [1]= eyetilt ; data [2]= neckpan ; constrain ( data [0] , data [1] , data [2]) ; // Limita le escursioni if ( IsConnected () ) { // Se è connesso mando i dati WriteData (& data [0] , 2) ; WriteData (& data [1] , 2) ; WriteData (& data [2] , 2) ; } else printf ( " Il dispositivo NON è connesso .\ n " ) ; } M. Scarpiniti Una Introduzione ad Arduino 54 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Infine la funzione computeAndSendAngles() trasforma una coordinata cartesiana [x0 , y0 , z0 ], espressa nella terna di riferimento solidale alla testa, negli angoli da passare ai servomotori per orientare la testa e gli occhi verso il punto in questione, inviando tali dati attraverso la connessione seriale. void c o m p u t e A n d S e n d A n g l e s ( double x0 , double y0 , double z0 ) { double x = x0 ; double y = y0 -24; // Le webcam sono a 24 cm dalla base double z = z0 -5.5; // Le webcam distano 5.5 cm dall ’ asse double r = sqrt ( x * x + z * z ) ; // Uso le coordinate polari double phi = atan2 (y , r ) ; // Angolo del tilt dell ’ occhio double max35 = (( double ) 35/180) * M_PI ; double max4 = (( double ) 4/9) * M_PI ; if ( phi > max35 ) phi = max35 ; else if ( phi < - max35 ) phi = - max35 ; M. Scarpiniti Una Introduzione ad Arduino // 35 gradi // 80 gradi // -35 <= phi <= 35 55 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender Si controlla che gli angoli non eccedono i limiti impostati (nel caso impostando il valore massimo) e si stampa sul prompt il valore dei tre angoli. double thetaneck = atan2 (x , z ) ; // Pan del collo double thetaeye ; // Pan dell ’ occhio if ( thetaneck > max4 ) { thetaeye = thetaneck - max4 ; thetaneck = max4 ; // -35 <= thetaeye <= 35 if ( thetaeye > max35 * M_PI ) // -80 <= thetaneck <= 80 thetaeye = max35 * M_PI ; } else if ( thetaneck < - max4 ) { thetaeye = thetaneck + max4 ; thetaneck = - max4 ; if ( thetaeye < - max35 * M_PI ) thetaeye = - max35 * M_PI ; } else thetaeye =0; printf ( " Angles ( eyepan , tilt , neckpan ) : % f % f % f \ n \ n " , thetaeye , phi , thetaneck ) ; M. Scarpiniti Una Introduzione ad Arduino 56 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il driver per Bender A questo punto, i tre valori calcolati vengono convertiti nel valore corretto da passare alla scheda attraverso la funzione map() e trasformati in interi a 16 bit. Infine, la terna appena calcolata viene inviata alla scheda con la precedente funzione sendAngles(). double neckpand = map ( thetaneck , - max4 , max4 ,750 ,2200) ; double eyepand = map ( thetaeye , - max35 , max35 ,1610 ,950) ; double eyetiltd = map ( phi , - max35 , max35 ,1990 ,1370) ; uint16_t neckpan =( uint16_t ) ( neckpand ) ; uint16_t eyepan =( uint16_t ) ( eyepand ) ; uint16_t eyetilt =( uint16_t ) ( eyetiltd ) ; sendAngles ( eyepan , eyetilt , neckpan ) ; } M. Scarpiniti Una Introduzione ad Arduino 57 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il funzionamento della testa robotica La testa robotica funziona con due algoritmi di localizzazione che lavorano congiuntamente. 1 localizzazione binaurale: utilizza l’audio raccolto dai due microfoni per localizzare la coordinata del parlatore. E’ basata sulle due grandezze seguenti: ILD (Interaural Level Difference): differenza del livello sonoro tra i due sensori; ITD (Interaural Time Difference): differenza tra i tempi di arrivo dell’onda sonora ai due sensori. 2 face detection: utilizza una delle due webcam. Utilizza il metodo di Viola-Jones, basato sull’estrazione delle features di Haar. Purtroppo, negli ambienti indoor reali il riverbero introduce un errore nella localizzazione acustica. A tal proposito, per correggere tale errore (angolare) si utilizza il face detector: la testa si “aggancia” alla faccia e la segue per qualche istante. M. Scarpiniti Una Introduzione ad Arduino 58 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La localizzazione binaurale Il modello della localizzazione binaurale è il seguente: xl [n] = hl [n] ∗ s[n] + ηl [n], xr [n] = hr [n] ∗ s[n] + ηr [n], in cui s[n] è il segnale del parlatore, hl [n] e hr [n] sono le risposte impulsive del canale sinistro e destro, xl [n] e xr [n] sono i segnali raccolti dal sensore sinistro e destro, e ηl [n], ηr [n] sono due eventuali rumori additivi. Per il calcolo dell’ILD e ITD, si effettua la Short Time Fourier Transform (STFT) dei segnali xl [n] e xr [n], ottenendo per l’n-esimo frame Xln (ω, θ, φ) , Xrn (ω, θ, φ) , in cui ω è la frequenza, θ e φ sono gli angoli di elevazione e azimut, rispettivamente. M. Scarpiniti Una Introduzione ad Arduino 59 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La localizzazione binaurale: ILD L’Interaural Level Difference o ILD è calcolato, per il generico frame n, nel seguente modo: n Xr (ω, θ, φ) n . ILD (ω, θ, φ) = 20 log10 n X (ω, θ, φ) l Esprime dunque, in dB, la differenza dei moduli del segnale ricevuto al sensore destro rispetto a quello ricevuto al sensore sinistro. Descrive il fenomeno dell’effetto ombra provocato dalla testa. Tale indice diviene ambiguo al di sotto di circa 1.5 kHz in quanto l’effetto ombra provocato dalla testa è nullo. M. Scarpiniti Una Introduzione ad Arduino 60 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La localizzazione binaurale: ITD L’Interaural Time Difference o ITD è calcolato, per il generico frame n, nel seguente modo: X n (ω, θ, φ) 1 ∠ rn + 2πp . ITD n (ω, θ, φ) = ω Xl (ω, θ, φ) Esprime dunque, la differenza di fase tra il segnale ricevuto al sensore destro e quello ricevuto al sensore sinistro. La variabile p costituisce un fattore di molteplicità della fase e, purtroppo, non è noto a priori. Tale indice diviene ambiguo al di sopra di circa 1.5 kHz per via della molteplicità. M. Scarpiniti Una Introduzione ad Arduino 61 / 69 L’hardware Il software Il funzionamento Introduzione La Testa Robotica La localizzazione binaurale: le HRTF di riferimento La localizzazione avviene per confronto degli indicatori ILD e ITD calcolati con quello relativo a un database di riferimento di HRTF (Head Related Transfer Function). Tali funzioni sono valutate mantenendo fissa l’elevazione θ: HRTFrn (ω, φ) , ILD (ω, φ) = 20 log10 HRTFln (ω, φ) 1 HRTFrn (ω, φ) n ∠ + 2πp . ITD (ω, φ) = ω HRTFln (ω, φ) n Esiste un database, il CIPIC, che contiene 45 HRTF registrati su persone reali e un manichino KEMAR. In particolare, per la localizzazione Bender utilizza il soggetto 21, corrispondente al manichino. M. Scarpiniti Una Introduzione ad Arduino 62 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La localizzazione binaurale: le HRTF di riferimento hange dramatically across azimuth [3]. Smoothing across azimuth with a ant Q filter was performed on the ILD lookup set in order to better represent mits of human interaural level difference perception. More specifically, a sian filter was employed, as indicated in the CIPIC database [5]. omparison between the ILD and ITD lookup sets and the estimated ILD TD allows to estimate the azimuth of the sound source. In particular ILD ploited to find the correct value of the unwrapping factor p and to select zimuth value minimizing the difference between the ITD-only and ILDestimates. This p-estimation procedure was repeated for each available time e. A time average across frames was performed and the results graphed. final azimuth estimations selected were those displaying a minimum in the T T ence function that was consistent across frequencies. n simulations with n,pthe s an example, fig. 2 shows the results obtained in e placed at different azimuth angles. Joint exploitation of ILD and ITD s to obtain an azimuth estimate which is correct over the whole frequency L and for different positions of the source. n Si effettua la differenza tra i valori di ILD e ITD calcolati dai segnali registrati T e le HRTF, prendendo i valori minimi θnL (ω) e θn,p (ω): al minimo corrisponderà T l’angolo corrispondente al parlatore. Rimane una ambiguità su θn,p (ω) dovuta alla scelta di p. Tale ambiguità è risolta prendendo il valore di p che rende minimo l’ITD, cioè θ (ω) = θ (ω)p=argmin θT (ω) } p { n,p Infine si effettua la media dei valori θ (ω) e θnT (ω) su tutti i frame n, ottenendo le stime finali θL (ω) e θT (ω) per ogni frequenza. ILD ILD ILD ILD 8 8 8 8 6 6 6 6 6 4 2 4 2 4 2 4 2 4 2 ITD Frequency [kHz] ILD 8 ITD ITD ITD 8 6 8 6 8 6 8 6 8 6 4 4 4 4 4 2 2 2 2 Joint Joint 8 6 8 6 4 2 −80 0 Joint 4 2 80 M. Scarpiniti −80 0 80 2 Joint 8 6 8 6 4 2 4 2 Mediando quindi in frequenza, è possibile determinare la direzione media θL e θT . Utilizzando congiuntamente queste due informazioni (media) è dunque possibile ottenere una stima θ della direzione del parlatore. ITD −80 0 80 −80 0 Azimuth [degrees] Joint 8 6 4 2 80 −80 0 80 Una Introduzione ad Arduino 2. Source azimuth estimate in an anechoic room and Gaussian noise: ILD, ITD 63 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento La localizzazione binaurale E’ possibile riassumere la localizzazione binaurale implementata, nei passi riportati nel seguente algoritmo: 1: 2: 3: 4: 5: 6: 7: 8: 9: for n = 1 to Nf do Calcolo di ILD n (ω, θ, φ) e ITD n (ω, θ, φ); Calcolo dei riferimenti ILD n (ω, φ) e ITD n (ω, φ); T Stima delle differenze θnL (ω) e θn,p (ω); Stima del valore ottimale di p e quindi di θnT (ω); end for Media su tutti i frames, ottenendo θL (ω) e θT (ω); Media su tutte le frequenze, ottenendo θL e θT ; Stima della direzione di arrivo θ basata sull’utilizzo congiunto di θL e θT . Nf indica il numero totale di frames utilizzati. M. Scarpiniti Una Introduzione ad Arduino 64 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il face detector Il riconoscimento facciale è ottenuto tramite il metodo di Viola-Jones (VJM), implementato in C nella librerie OpenCV. Il processo di rilevazione classifica le immagini basandosi sui valori di specifiche feature piuttosto che sul valore di intensità dei singoli pixel. I concetti chiave sono quattro: 1 Haar features: ottenute per somme e differenze delle somme dei valori dei pixel in zone chiare e scure; 2 integral image, per la rilevazione veloce delle feature: determina la presenza o assenza di centinaia di queste features per tutta l’immagine e con diverse scalature; 3 il metodo di Machine Learning AdaBoost: la procedura seleziona vari classificatori deboli e ne fa una combinazione pesata, cosi da ottenere un classificatore robusto; 4 un classificatore a cascata per la combinazione efficiente delle molteplici features: combina una serie di classificatori AdaBoost in cascata, catena particolarmente efficiente per la classificazione di porzioni di immagini. M. Scarpiniti Una Introduzione ad Arduino 65 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il face detector: tracking Una volta estratta la regione video contenente la rivelazione della faccia, è necessario manipolare gli angoli di tilt e pan della testa facendo si che la faccia stessa venga fatta posizionare, in vari step, sempre al centro del video in esame. La realizzazione del controllo è realizzata con un sistema di retroazione ad anello chiuso in cui una coppia di controlli proporzionali è impiegata per correggere la differenza di posizione tra il centro della faccia rilevata e il centro del frame video. Uno dei due regolatori correggerà la differenza in direzione orizzontale, l’altro in direzione verticale. Tale sistema è rappresentato nella seguente figura. Proportional controller Input coordinates M. Scarpiniti e t Una Introduzione ad Arduino Kp u t Head actuators Output coordinates 66 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Il software per la localizzazione audio-video Le funzioni utilizzate per la localizzazione sono contenute nel file recording.cpp, mentre i prototipi delle funzioni che utilizzano l’audio e il video sono contenuti nel file recording.h: int camdetect () ; void detectAndDraw ( Mat & img , C a s c a d e C l a s s i f i e r & cascade , C a s c a d e C l a s s i f i e r & nestedCascade , double scale ) ; static int reco rdCallba ck ( ) ; static int playCallback ( ) ; double STFT ( SAMPLE chLeft [] , SAMPLE chRight [] , int signalLength , int hopSize ) ; Tali funzioni attivano la wecam, identificano le facce (disegnando un rettangolo in corrispondenza del volto) e registrano l’audio. La localizzazione è effettuata all’interno della funzione STFT(), che calcola anche la STFT dei segnali registrati. M. Scarpiniti Una Introduzione ad Arduino 67 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Integrazione audio-video CAPITOLO 5. LA TESTA ROBOTICA: PARTE AUDIO 97 L’algoritmo di face tracking andrà a coadiuvare quello basato sull’audio, in modo da perseguire una stima dell’angolo di arrivo attraverso il matching delle informazioni provenienti dalla catena video e da quella audio. Una volta localizzata la provenienza del suono, e mossa la testa di conseguenza, la funzione camdetect() darà luogo alla localizzazione video. La testa, a partire dall’angolo stimato dal lato audio, affinerà la stima inquadrando il volto del soggetto pronunciante la parola. La localizzazione video durerà qualche secondo, dopo di che si procederà nuovamente al controllo della soglia sui frame, per dar luogo eventualmente a una nuova localizzazione audio. L’intero procedimento è illustrato nel diagramma a lato. Figura 5.7: Diagramma di flusso dell’algoritmo di localizzazione M. Scarpiniti Una Introduzione ad Arduino 68 / 69 Introduzione La Testa Robotica L’hardware Il software Il funzionamento Bibliografia Arduino Tutorials. https://www.arduino.cc/en/Tutorial/HomePage. M. Margolis. Arduino - Progetti e soluzioni. O’Reilly, 2013. M. Banzi e M. Shiloh. Arduino – La guida ufficiale. Tecniche Nuove, 2015. M. Schmidt. Il manuale di Arduino. APOGEO, 2011. S. Majocchi. Arduino UNO. Programmazione avanzata e librerie di sistema. Vispa, 2012. D. J. Russell. Introduction to Embedded Systems: Using ANSI C and the Arduino Development Environment. Morgan and Claypool Publishers, 2010. M. Scarpiniti Una Introduzione ad Arduino 69 / 69