29 dicembre 2017

Creo il mio ROBOT con Raspberry Pi #003 - Filmare!


Benvenuti nel terzo episodio della serie #Ro-Pi: creo il mio ROBOT con Raspberry Pi!

Continua la serie di appuntamenti con il fai-da-te smanettoso in compagnia dei single board computer della fondazione inglese raspberry pi. In particolare, partendo da quanto costruito nella prima e nella seconda puntata, oggi vedremo come ampliare il nostro robottino (in grado di muoversi sul piano e controllare la presenza di ostacoli sul suo percorso) con una videocamera per filmare e osservare ciò che lo circonda.

Prima di entrare nel merito, vi riporto il video abbinato a questo articolo che ho pubblicato sul mio canale YouTube nel quale viene mostrato il risultato in esecuzione e spiegato a grandi linee il funzionamento di questa seconda estensione al progetto:


Il terzo step di questa avventura, perciò, è quello di collegare la pi-camera cosicché possa essere sfruttata dall'utilizzatore per filmare o anche solo osservare ciò che il robot stesso ha davanti a sé (eventualmente anche da remoto).


Componenti hardware necessarie


Una volta realizzata la base vista nella prima puntata ed il primo ampliamento del secondo episodio, per ampliare ancora una volta il nostro robot occorreranno:
  • la pi-camera (o un'altra camera compatibile) con un cavo di collegamento sufficientemente lungo per facilitarne il posizionamento sul robot
  • uno o più servo motori (o un kit apposito) utile per facilitare gli spostamenti della camera stessa

Prima di passare all'argomento principale, però, come fatto anche nella scorsa puntata, possiamo andare a migliorare il nostro robottino: in particolare ne rivedremo la disposizione dei cavi sulla breadboad. Quando abbiamo visto come controllare i sensori di distanza, abbiamo sfruttare dei resistori per portare la tensione dai 5V richiesti dai PIN di questi sensori ai 3.3V supportati dai PIN del Raspberry Pi. La soluzione prevedeva l'uso di almeno un paio di resistori per ogni sensore che, oltre ad occupare parecchio spazio sulla breadboard, avevano anche lo svantaggio di dover essere sempre isolati l'uno dall'altro con cura... E tra urti della macchina, movimenti, etc, non era una situazione facilissima da gestire. Per questo motivo una miglioria può essere l'adozione di un level shifter.


Un level shifter è un chip molto economico (costa meno di 1€) che consente di tradurre segnali da un voltaggio ad un altro, in modo bidirezionale. Questo componente permette di posizionare da un lato fino a 4 PIN che scambiano segnali ad un voltaggio "alto" e dall'altro lato i corrispettivi 4 PIN che invece lavorano ad un voltaggio più "basso". E' chiaro, quindi, che un componente come questo non solo occupa uno spazio decisamente ridotto rispetto ad inserire resistori e quant'altro sulla breadboard per ogni PIN che lo richiede, ma va anche a migliorare la qualità stessa del segnale scambiato. Con un solo chip, quindi, è possibile liberare gran parte della superficie dapprima occupata dai vari resistori, ed anche razionalizzare meglio la disposizione dei cavi. Su di un lato è possibile avere sempre a disposizione corrente a 3.3C, mentre sull'altro a 5V; ed il level shifter si occuperà di tradurre all'occorrenza da un lato all'altro e viceversa.



A questo punto si può passare alla pi-camera, essa non richiede sforzi particolari per essere controllata visto che la fondazione e la community forniscono librerie già pronte all'uso che vanno al più integrate nel nostro codice. In un vecchio tutorial vi avevo già mostrato come sfruttarla per creare una semplice camera di videosorveglianza. Per questo motivo non mi soffermerei più di tanto su questo aspetto, e per saperne di più magari date un'occhiata a quel tutorial se per caso ancora non lo avete visto.
Sul sito della documentazione ufficiale è possibile trovare tutti i dettagli per controllare la camera, scattare foto, girare filmati e quant'altro.


Quello che vogliamo realizzare per Ro-Pi, però, è un sistema che permetta non solo di guardare in modo fisso in avanti, quanto più avere un minimo di flessibilità per esplorare l'ambiente circostante. Per consentire alla camera di muoversi possiamo affidarci ad uno o più servo motori. Essi possono essere collegati direttamente alla pi-camera, oppure (rendendo il tutto più pulito ed elegante) tramite un semplicissimo ed economico kit (che costa circa 2€).


Kit come quello riportato in figura permettono di posizionare la camera al di sopra di una struttura che, combinando più servo motori, ne consente la rotazione su due assi (circa 180° ciascuno). I servo motori hanno un costo accessibile, sui 2€ ognuno se venduti in pacchi da 4-5.

Nota: rimanendo in tema costi, la pi-camera ufficiale ha un prezzo che supera i 20€ a cui aggiungere quello per un cavo più lungo di connessione con il raspberry pi (circa 7-8€). Esistono versioni compatibili che forniscono camera + cavo lungo per circa 15€.

Arrivati a questo punto sappiamo controllare la camera, ma come fare per farla muovere per farci sbirciare attorno? Qui vengono in aiuto i servo motori.
Un servo è composto da diverse componenti tra cui un potenziometro, un circuito di controllo, un motore ed una serie di ingranaggi che riducono la velocità del motore stesso. Da essi spunta un perno che è in grado di ruotare tra 0 e 180°, mantenendo stabilmente la posizione ottenuta. E questa caratteristica lo distingue da ad esempio uno step motor. I servo sono utilizzati spesso per muovere gambe e braccia di robot o in generale qualsiasi componente che si vuole articolare in qualche modo, come nel caso della nostra struttura che fa da supporto alla camera.


Ogni servo ha 3 cavi colorati: il cavo marrone è il ground, il cavo rosso è la tensione da 5V ed il cavo arancio serve per il segnale. Quest'ultimo cavo è indispensabile per applicare un segnale impulsivo o PWM (Pulse Wave Modulation). I raspberry pi hanno due PIN PWM dedicati (il 12° ed il 32°) e possono essere utilizzati, uno per servo, anche se in teoria qualsiasi altro PIN può essere programmato a mo' di PWM ed utilizzato per lo scopo.
I servo necessitano di corrente a 5V che abbiamo organizzato su un lato della breadboard, ed i cavi + e - vanno posizionati lì per ogni servo, invece il PIN del segnale può essere abbinato direttamente ad un qualsiasi PIN del raspberry pi.


Per fare in modo che il servo faccia girare gli ingranaggi interni, cosicché il perno ruoti nei 180° a disposizione, occorre fare in modo di inviare una pulsazione per un certo periodo di tempo. Quello che succede è che viene inviato un segnale ON per un intervallo di tempo, seguito da un intervallo di OFF cioè assenza di segnale per un altro intervallo; il tutto ripetuto più volte al secondo. La relazione che c'è tra la lunghezza dell'intervallo ON e quello OFF è chiamata duty cycle ed è misurata in % tra tempo di ON e quello di OFF. Questa % specifica l'angolo verso cui il servo motore ruoterà. La maggior parte dei servo motori lavora tra il 2% ed il 12% di duty cycle, con un frequenza di ripetizioni del segnale di 50Hz (o 3000 cicli al minuto).
Con un impulso di durata pari a 1.5 ms il perno del servomotore si pone esattamente al centro del suo intervallo di rotazione, cioè 90°. Da questo punto, il perno può ruotare in senso antiorario se l'impulso fornito ha una durata inferiore a 1.5ms (fino a 0°) ed in senso orario se l'impulso fornito ha durata superiore a 1.5ms (fino a 180°).

Tra le varie informazioni, il dato più importante è che il servo motore lavora tra duty cycle di 2 e 12%, quindi una differenza di 10. Questo significa che occorrerà fare in modo di convertire l'ampiezza degli angoli in valori compresi tra 0 e 10. Banalmente 180° / 18 = 10; quindi dividendo per 18 qualsiasi angolo si voglia, si otterrà il valore in base 10 da utilizzare per far muovere il servo.
Il duty cycle desiderato sarà dato dall'angolo / 18 + 2 (il servo lavora tra 2% e 12%).


Il codice seguente mostra come far spostare in automatico la camera tramite i servo

import RPi.GPIO as GPIO
from time import sleep

# imposta servo PIN e setta come OUT
servoPin1 = 12
servoPin2 = 32
GPIO.setmode(GPIO.BOARD)
GPIO.setup(servoPin1, GPIO.OUT)
GPIO.setup(servoPin2, GPIO.OUT)

# activate pin
def activate(pin) :
    GPIO.output(pin, GPIO.HIGH)

# deactivate pin
def deactivate(pin) :
    GPIO.output(pin, GPIO.LOW)

# start pulse wave modulation
def startPWM(pin, freq) :
   pwm = GPIO.PWM(pin, freq)
   pwm.start(0)
   return pwm

# stop pulse wave modulation
def stopPWM(pwm) :
   pwm.stop()

# imposta angolo
def setAngle(angle, pwm, pin) :
   duty = angle / 18 + 2
   activate(pin)
   pwm.ChangeDutyCycle(duty)
   sleep(.5)
   deactivate(pin)
   pwm.ChangeDutyCycle(0)

# avvia modulazione sui servo PIN con frequenza 50Hz
pwm1 = startPWM(servoPin1, 50)
pwm2 = startPWM(servoPin2, 50)

# muovi i servo negli angoli desiderati
setAngle(90, pwm1, servoPin1)
setAngle(90, pwm2, servoPin2)
sleep(1)
setAngle(0, pwm1, servoPin1)
setAngle(0, pwm2, servoPin2)
sleep(1)
setAngle(180, pwm1, servoPin1)
setAngle(180, pwm2, servoPin2)
sleep(1)
setAngle(90, pwm1, servoPin1)
setAngle(90, pwm2, servoPin2)

# ferma modulazione ed esci
stopPWM(pwm1)
stopPWM(pwm2)

GPIO.cleanup()


Il codice seguente mostra invece come muovere i servo controllandone la direzione con la tastiera. Le parti comuni con il precedente codice non sono riportate

[...] # import
import curses

# cattura la tastiera
screen = curses.initscr()
curses.noecho()
curses.cbreak()
screen.keypad(True)

[...] # imposta servo PIN

[...] # funzioni di avvio/stop modulazione e angolo

# su
def up () :
   setAngle(0, pwm1, servoPin1)

# giu
def down() :
   setAngle(180, pwm1, servoPin1)

# muovi a sinistra
def left() :
   setAngle(180, pwm2, servoPin2)

# muovi a destra
def right() :
   setAngle(0, pwm2, servoPin2)

# torna alla posizione iniziale
def reset() :
   setAngle(90, pwm1, servoPin1)
   setAngle(90, pwm2, servoPin2)

# stop
def stop() :
   deactivate(servoPin1)
   deactivate(servoPin2)
   pwm1.ChangeDutyCycle(0)
   pwm2.ChangeDutyCycle(0)

# avvia modulazione
pwm1 = startPWM(servoPin1, 50)
pwm2 = startPWM(servoPin2, 50)

# cicla
try:
    while True:
        stop()
        char = screen.getch()
        #reset
        if char == ord('r'):
            reset()
        #quit
        if char == ord('q'):
            break
        #up
        elif char == curses.KEY_UP:
            up()
        #down
        elif char == curses.KEY_DOWN:
            down()
        #right
        elif char == curses.KEY_RIGHT:
            right()
        # left
        elif char == curses.KEY_LEFT:
            left()

# uscita dal ciclo
finally:
    curses.nocbreak()
    screen.keypad(0)
    curses.echo()
    curses.endwin()

[...] # pulizia e uscita


Conclusioni


Bene, termina qui il terzo episodio della serie.

Nelle prossime puntate vedremo come sfruttare in maniera più utile la camera, e inizieremo a mettere assieme tutte le componenti fin qui aggiunte a Ro-Pi: i motori, i sensori di distanza, il LED di controllo e la camera controllata dai servo!

A presto :-)

Nessun commento:

Posta un commento