Differenze tra le versioni di "RaspiTank"

Da raspibo.
Jump to navigation Jump to search
Riga 1: Riga 1:
 
Il RaspiTank è un tentativo di integrare il Raspberry Pi con un vecchio giocattolo a motore elettrico con i cingoli. Il controllo del motore è analogo a quello della [[RaspiCar]] mentre lo sterzo è affidato a un ingranaggio tramite un servo.
 
Il RaspiTank è un tentativo di integrare il Raspberry Pi con un vecchio giocattolo a motore elettrico con i cingoli. Il controllo del motore è analogo a quello della [[RaspiCar]] mentre lo sterzo è affidato a un ingranaggio tramite un servo.
  
== Prima versione ==
+
= Prima versione =
  
 
[[File:Raspitank finale.jpg]]
 
[[File:Raspitank finale.jpg]]
Riga 219: Riga 219:
 
  motor.digitalWrite(15,motor.LOW)
 
  motor.digitalWrite(15,motor.LOW)
  
== Il rudimentale sensore della distanza ==
+
= RaspiTank con RaspiArm =
  
[[File:Irsensor.JPG]]
+
Tutto è iniziato quando ho trovato un braccio meccanico in scatola di montaggio[http://www.owirobots.com/store/index.php?l=product_detail&p=110]. Originariamente era filoguidato da un sistema di interruttori a levette, ma il fatto che avesse i contatti esterni con i pin perfettamente compatibili con quelli dei cavetti per la breadboard lo rendeva particolarmente irresistibile.
 +
Inizialmente il gioco è stato eliminare il controller e usare la GPIO del Raspberry Pi per comandarlo. Essendo composto da cinque motori, e dovendo comunque muovere il motore della base, ci sarebbero dovuti essere sette relè: due per determinare il senso della corrente e cinque per attivare ognuno dei motori. L'ultimo relè avrebbe mosso alternativamente il motore n.5 del braccio o il motore dei cingoli.
  
Questo aggeggio è composto da un LED infrarosso (a destra) e un sensore (a sinistra). Ogni intervallo di uno o più secondi uno script scrive quante volte il sensore è stato colpito. Più un oggetto è vicino più infrarossi sono riflessi e quindi il sensore dà risultati più alti. Per funzionare funziona, ma è da tarare. Inoltre i pin liberi sul RaspiTank cominciano a scarseggiare. Al momento non è ancora stato montato.
+
==Il nostro nuovo amico ULN2803==
  
== La prima escursione! ==  
+
ULN2803 è un driver che permette di dirigere i 5V del Raspberry Pi in 7 uscite diverse comandate dai pin della GPIO. In pratica sostituisce tutto quel circuito composto dal diodo e il transistor. Lo potete intravedere in questa foto, al centro della breadboard.
 +
 
 +
[[File:RaspiArm.jpeg]]
 +
 
 +
Non sto a scendere nel dettaglio del circuito: in pratica è solo il circuito già usato prima che ha più motori.
 +
 
 +
==L'unione dei due==
 +
 
 +
[[File:Raspitank_arm.jpeg]]
 +
 
 +
Una volta riportato il circuito della breadboard su una millefori è bastato fissare il braccio sulla base. Le luci sono state sostituite da un led già integrato sulla pinza. L'alimentazione del motore non è più dato da due pile torcia nella sede originale del carro ma sono le quattro che stanno dentro al braccio. Essendo raddoppiato il voltaggio adesso va molto più veloce.
 +
Purtroppo tutta la struttura ha sbilanciato l'andatura e adesso non procede più dritto come prima.
 +
 
 +
==Il codice! Sempre più mal scritto! Sempre più mal commentato!==
 +
#!/usr/bin/python
 +
import os
 +
import wiringpi
 +
import curses
 +
import time
 +
import sys
 +
import threading
 +
#PIN da usare
 +
#11 luci
 +
#12 circuito avanti
 +
#10 circuito indietro
 +
#3 motore braccio
 +
#4 motore braccio
 +
#5 motore braccio
 +
#6 motore braccio
 +
#14 motore braccio
 +
#8 on/off servo
 +
#7 PWM servo (servo 0 ServoBlaster)
 +
class getkey(threading.Thread):
 +
        key = ''
 +
        light = False
 +
        motion = 'stop'
 +
        direction = 'straight'
 +
        mstr = ['stop ','stop ','stop ','stop ','stop ']
 +
        def run(self):
 +
                while self.key != 27:
 +
                        self.key = stdscr.getch()
 +
                        if self.key == ord(' '): #spazio accende e spegne le luci
 +
                                if self.light:
 +
                                        motor.digitalWrite(11,motor.LOW)
 +
                                        time.sleep(0.5)
 +
                                        self.light = False
 +
                                        stdscr.addstr(6,5,"Lights:off")
 +
                                else:
 +
                                        motor.digitalWrite(11,motor.HIGH)
 +
                                        time.sleep(0.5)
 +
                                        self.light = True
 +
                                        stdscr.addstr(6,5,"Lights:on ")
 +
                        elif self.key == curses.KEY_HOME or self.key == curses.KEY_SHOME: #HOME accende lo stream, SHIFT+HOME accende lo stream notturno
 +
                                self.videostart = os.system("ps -ae|grep raspivid > /dev/null")
 +
                                if self.videostart !=0:
 +
                                        stdscr.addstr(7,5,"Stream:on USE nc raspitank.local 9999 |mplayer -fps 150 -demuxer h264es -")
 +
                                        if self.key == curses.KEY_HOME:
 +
                                                os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -o - |nc -l 9999 &')
 +
                                        else:
 +
                                                os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -ex night -o - |nc -l 9999 &')
 +
                                else:
 +
                                        stdscr.addstr(7,5,"Stream:off                                                                ")
 +
                                        os.system('killall raspivid >/dev/null')
 +
                                        os.system('killall nc >/dev/null')
 +
                        elif self.key == curses.KEY_UP: #FRECCE muovono il robot
 +
                                if self.motion == 'back':
 +
                                        stop()
 +
                                        self.motion = 'stop'
 +
                                        time.sleep(0.2)
 +
                                elif self.motion == 'stop':
 +
                                        avanti()
 +
                                        self.motion = 'forward'
 +
                        #      stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
 +
                        #      stdscr.refresh()
 +
                        elif self.key == curses.KEY_DOWN:
 +
                                if self.motion == 'forward':
 +
                                        stop()
 +
                                        self.motion = 'stop'
 +
                                        time.sleep(0.2)
 +
                                elif self.motion == 'stop':
 +
                                        indietro()
 +
                                        self.motion = 'back'
 +
                        #      stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
 +
                        #      stdscr.refresh()
 +
                        elif self.key == curses.KEY_LEFT:
 +
                                stop()
 +
                                self.motion = 'stop'
 +
                                if self.direction == 'right':
 +
                                        dritto()
 +
                                        self.direction = 'straight'
 +
                                        time.sleep(0.2)
 +
                                elif self.direction == 'straight':
 +
                                        self.direction = 'left'
 +
                                        orario()
 +
                                else:
 +
                                        self.direction = 'left'
 +
                                        time.sleep(0.2)
 +
                        #      stdscr.refresh()
 +
                        elif self.key == curses.KEY_RIGHT:
 +
                                stop()
 +
                                self.motion = 'stop'
 +
                                if self.direction == 'left':
 +
                                        dritto()
 +
                                        self.direction ='straight'
 +
                                        time.sleep(0.2)
 +
                                elif self.direction == 'straight':
 +
                                        self.direction = 'right'
 +
                                        antiorario()
 +
                                else:
 +
                                        self.direction = 'right'
 +
                                        time.sleep(0.2)
 +
                        #      stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
 +
                        #      stdscr.addstr(3,5,"Direct:"+self.direction+"        ")
 +
                        #      stdscr.refresh()
 +
                        elif self.key == ord('q'):
 +
                                arm(0,True)
 +
                        elif self.key == ord('a'):
 +
                                arm(0,False)
 +
                        elif self.key == ord('s'):
 +
                                arm(3,True)
 +
                        elif self.key == ord('w'):
 +
                                arm(3,False)
 +
                        elif self.key == ord('d'):
 +
                                arm(4,True)
 +
                        elif self.key == ord('e'):
 +
                                arm(4,False)
 +
                        elif self.key == ord('f'):
 +
                                arm(2,True)
 +
                        elif self.key == ord('r'):
 +
                                arm(2,False)
 +
                        elif self.key == ord('x'):
 +
                                arm(1,True)
 +
                        elif self.key == ord('z'):
 +
                                arm(1,False)
 +
                        elif self.key == 263:
 +
                                stop()
 +
                                self.motion = 'stop'   
 +
                        stdscr.addstr(3,5,"Motion:"+self.motion+"    ")
 +
                        stdscr.addstr(4,5,"Direct:"+self.direction+"        ")
 +
                        stdscr.addstr(5,5,"Motor1:"+self.mstr[0]+" Motor2:"+self.mstr[3]+" Motor3:"+self.mstr[4]+" Motor4:"+self.mstr[2]+" Motor5:"+self.mstr[1])
 +
                        stdscr.refresh()
 +
                exit()
 +
 +
def arm(armot,updn):
 +
        stop()
 +
        getkey.motion = 'stop'
 +
        for mtr in motors:
 +
                if mtr[0] != motors[armot][0]:
 +
                        mtr[1] = 0
 +
        if motors[armot][1] == 0 and updn:
 +
                motors[armot][1] = 1
 +
                avanti()
 +
                motor.digitalWrite(motors[armot][0],motor.HIGH)
 +
                if armot == 0:
 +
                        getkey.mstr[0] = 'open '
 +
                elif armot == 1:
 +
                        getkey.mstr[1] = 'right'
 +
                else:
 +
                        getkey.mstr[armot] = 'down '           
 +
        elif motors[armot][1] == -1 and updn:
 +
                motors[armot][1] = 0
 +
                stop()
 +
        elif motors[armot][1] == 0 and not updn:
 +
                motors[armot][1] = -1
 +
                indietro()
 +
                motor.digitalWrite(motors[armot][0],motor.HIGH)
 +
                if armot == 0:
 +
                        getkey.mstr[0] = 'close'
 +
                elif armot == 1:
 +
                        getkey.mstr[1] = 'left '
 +
                else:
 +
                        getkey.mstr[armot] = 'up  '
 +
        elif motors[armot][1] == 1 and not updn:
 +
                motors[armot][1] = 0
 +
                stop()
 +
        else:
 +
                motors[armot][1] = 0
 +
                stop()
 +
 +
def orario():
 +
        motor.digitalWrite(8,motor.HIGH)
 +
        os.system("echo 0=60 > /dev/servoblaster")
 +
        time.sleep(0.5)
 +
        motor.digitalWrite(8,motor.LOW)
 +
def indietro():
 +
        motor.digitalWrite(10,motor.HIGH)
 +
def avanti():
 +
        motor.digitalWrite(12,motor.HIGH)
 +
def stop():
 +
        motor.digitalWrite(10,motor.LOW)
 +
        motor.digitalWrite(12,motor.LOW)
 +
        motor.digitalWrite(5,motor.LOW)
 +
        motor.digitalWrite(6,motor.LOW)
 +
        motor.digitalWrite(4,motor.LOW)
 +
        motor.digitalWrite(3,motor.LOW)
 +
        motor.digitalWrite(14,motor.LOW)
 +
        getkey.mstr = ['stop ','stop ','stop ','stop ','stop ']
 +
def antiorario():
 +
        motor.digitalWrite(8,motor.HIGH)
 +
        os.system("echo 0=240 > /dev/servoblaster")
 +
        time.sleep(0.5)
 +
        motor.digitalWrite(8,motor.LOW)
 +
def dritto():
 +
        motor.digitalWrite(8,motor.HIGH)
 +
        os.system("echo 0=145 > /dev/servoblaster")
 +
        time.sleep(0.5)
 +
        motor.digitalWrite(8,motor.LOW)
 +
 +
if os.system("ps -ae |grep servod > /dev/null") !=0:
 +
        os.system("sudo /home/pi/PiBits/ServoBlaster/servod > /dev/null")
 +
motor = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)
 +
motor.pinMode(10,motor.OUTPUT) #motore direzione 1
 +
motor.pinMode(12,motor.OUTPUT) #motore direzione -1
 +
motor.pinMode(8,motor.OUTPUT) #accensione servo
 +
motor.pinMode(11,motor.OUTPUT) #luci
 +
motor.pinMode(5,motor.OUTPUT) #motori braccio
 +
motor.pinMode(6,motor.OUTPUT)
 +
motor.pinMode(4,motor.OUTPUT)
 +
motor.pinMode(3,motor.OUTPUT)
 +
motor.pinMode(14,motor.OUTPUT)
 +
stop()
 +
dritto()
 +
if __name__=="__main__":
 +
        stdscr = curses.initscr()
 +
        curses.cbreak()
 +
        curses.noecho()
 +
        curses.curs_set(0)
 +
        stdscr.keypad(1)
 +
        motors = [[6,0],[5,0],[4,0],[3,0],[14,0]]
 +
        stdscr.addstr(0,5,"Press ESC to quit, up/down = Motion, left/right = Direct, space = Lights, BACKSPACE = stop")
 +
        stdscr.addstr(1,5,"home = Stream, shift+home = Night stream, q/a w/s e/d r/f z/x = Arm motors")
 +
        stdscr.addstr(3,5,"Motion:")
 +
        stdscr.addstr(4,5,"Direct:straight")
 +
        stdscr.addstr(5,5,"Motor1:stop  Motor2:stop  Motor3:stop  Motor4:stop  Motor5:stop")
 +
        stdscr.addstr(6,5,"Lights:off")
 +
        stdscr.addstr(7,5,"Stream:off")
 +
        stdscr.refresh()
 +
        getkey = getkey()
 +
        getkey.start()
 +
        key=''
 +
        secondpass = time.time()
 +
        while getkey.key != 27:
 +
                if time.time() - secondpass > 1:
 +
                        link = os.popen('iwconfig wlan0 |grep "Link Quality"') #deprecated
 +
                        stdscr.addstr(15,5,link.read().strip())
 +
                        stdscr.refresh()
 +
                        secondpass = time.time()
 +
        os.system('killall raspivid >/dev/null')
 +
        os.system('killall nc > /dev/null')
 +
        stop()
 +
        curses.endwin()
 +
        motor.digitalWrite(11,motor.LOW)
 +
 
 +
= I video =
 +
 
 +
[http://www.youtube.com/watch?v=H2Sz7JgYbbQ&feature=c4-overview&list=UUQXrH5o9He3H7HF4WZPP13Q Il RaspiTank con il RaspiArm]
  
 
[http://www.youtube.com/watch?v=QB28DayPIVk Il video della prima scampagnata del RaspiTank]
 
[http://www.youtube.com/watch?v=QB28DayPIVk Il video della prima scampagnata del RaspiTank]

Versione delle 05:22, 16 nov 2013

Il RaspiTank è un tentativo di integrare il Raspberry Pi con un vecchio giocattolo a motore elettrico con i cingoli. Il controllo del motore è analogo a quello della RaspiCar mentre lo sterzo è affidato a un ingranaggio tramite un servo.

Prima versione

Raspitank finale.jpg

Al momento il RaspiTank ha le seguenti caratteristiche:

  • Motore alimentato da due batterie tipo D, alloggiate nel vano originale del giocattolo
  • Raspberry Pi modello A, con Wi-Fi dongle, alimentato da 4 pile AA; garantisce qualche ora di autonomia, a seconda dell'uso
  • Marcia avanti e indietro
  • Sterzo ottenuto facendo girare un cingolo solo alla volta, usando il differenziale originale del giocattolo mosso da un servocomando
  • Streaming dalla camera frontale
  • Luci a LED accendibili e spegnibili
  • Si può comandare da remoto tramite ssh con le frecce

Le caratteristiche in lavorazione sono:

  • Possibilità di registrare un percorso (è già possibile, ma è stato testato pochissimo)
  • Sensore della distanza

RaspiTank inlavorazione.jpeg

Il nostro amico servocomando

I servi si muovono grazie a un impulso PWM come descritto in questa pagina: Servo (radio control). A cicli di 20ms, a seconda dell'ampiezza dell'impulso, il servo si metterà in una posizione. Per il RaspiTank abbiamo usato la libreria ServoBlaster[1]. Poiché useremo (per ora) solo un servo abbiamo disabilitato tutti gli altri pin utilizzati dal demone di Servoblaster che altrimenti sarebbero stati inutilizzabili per altri scopi.

Il groviglio di fili, ovvero la parte elettronica

Raspitank interior.jpg

Per l'azionamento del motore abbiamo riciclato i circuiti già fatti per la RaspiCar ovvero questi.

RaspiCar Schema Motor.png

La tensione data al motore circola in un senso o nell'altro a seconda di come scattano i due relè. Se il primo è acceso e il secondo è spento il motore girerà in un senso, vice versa se il primo è spento e il secondo è acceso il motore girerà nell'altro senso. Il motore resta spento se i due relè sono entrambi accesi o spenti. Questo è il circuito per far scattare il singolo relè.

RaspiCar Schema Relay.png

Per via di una interferenza che portava il servo a muoversi da solo durante la marcia, abbiamo usato un ulteriore relè che alimenta il servo solo quando si deve muovere. Il circuito per l'alimentazione del servo è lo stesso degli altri due relè. Per l'illuminazione abbiamo usato una luce alimentata da USB a due LED. Abbiamo collegato la terra alla terra della USB e il +5V al pin 15 wiringpi (pin 8 fisico). Nonostante siano solo 3,3V l'illuminazione è sufficiente per vedere diversi centimetri nell'oscurità ed evitare gli ostacoli.

La camera

Inizialmente il RaspiTank usava una webcam recuperata da un EeePC della ASUS collegata alla porta USB. Con il passaggio dal Raspberry Pi modello B al modello A è venuta meno una porta USB e aggiungere un HUB sarebbe stato scomodo. Abbiamo così deciso di non usare MjpegStreamer[2] e di usare la camera da attaccare direttamente al Raspberry Pi. Questo ci ha costretti a disabilitare un pin di ServoBlaster (vedete la issue qui[3])

Il codice, mal scritto e mal commentato

La libreria usata per la gestione della GPIO è wiringpi [4]. Per l'interfaccia testuale abbiamo usato curses. Il codice accetta un argomento, il nome del file xml su cui scrivere le azioni registrate. Se non è specificato non verrà registrato nulla. Al momento esiste uno script per riprodurre le azioni salvate o scritte a mano. Purtroppo il motore del RaspiTank non è molto preciso e quindi la registrazione, al momento, non è molto affidabile.

Le frecce su e giù controllano il motore. Per fermare il RaspiTank occorre dare il controllo contrario (i.e. se sta avanzando occorre premere giù). Le frecce destra e sinistra fanno spostare l'ingranaggio del differenziale. Non è possibile cambiare la direzione mentre il RaspiTank sta muovendosi. Il tasto spazio accende e spegne le luci. Il tasto home accende la camera e fa partire lo streaming.

#!/usr/bin/python
import os
import wiringpi
import curses
import time
import sys
import threading

#PIN da usare
#11 controllo relay avanti
#10 controllo relay indietro
#14 controllo relay servo
#7 PWM servo (servo n.0 ServoBlaster)
#I relay devono essere alimentati da +5V
#Il servo puo' essere alimentato da +3,3V

class getkey(threading.Thread):
	key = 
	light = False
	motion = 'stop'
	direction = 'straight'
	def run(self):
		while self.key != ord('q'):
			self.key = stdscr.getch()
			if self.key == ord(' '): #spazio accende e spegne le luci
				if self.light:
					motor.digitalWrite(15,motor.LOW)
					time.sleep(0.5)
					self.light = False
					stdscr.addstr(6,5,"Lights:off")
				else:
					motor.digitalWrite(15,motor.HIGH)
					time.sleep(0.5)
					self.light = True
					stdscr.addstr(6,5,"Lights:on ")
			elif self.key == curses.KEY_HOME or self.key == curses.KEY_SHOME:
				self.videostart = os.system("ps -ae|grep raspivid > /dev/null")
				if self.videostart !=0:
					stdscr.addstr(7,5,"Stream:on USE nc raspitank.local 9999 |mplayer -fps 150 -demuxer h264es -")
					if self.key == curses.KEY_HOME:
						os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -o - |nc -l 9999 &')
					else:
						os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -ex night -o - |nc -l 9999 &')
				else:
					stdscr.addstr(7,5,"Stream:off                                                                 ")
					os.system('killall raspivid >/dev/null')
					os.system('killall nc >/dev/null')
			elif self.key == curses.KEY_UP:
				if self.motion == 'down':
					stop()
					self.motion = 'stop'
					time.sleep(0.2)
				elif self.motion == 'stop':
					avanti()
					self.motion = 'up'
				stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
				stdscr.refresh()
			elif self.key == curses.KEY_DOWN:
				if self.motion == 'up':
					stop()
					self.motion = 'stop'
					time.sleep(0.2)
				elif self.motion == 'stop':
					indietro()
					self.motion = 'down'
				stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
				stdscr.refresh()
			elif self.key == curses.KEY_LEFT:
				stop()
				self.motion = 'stop'
				if self.direction == 'right':
					dritto()
					self.direction = 'straight'
					time.sleep(0.2)
				elif self.direction == 'straight':
					self.direction = 'left'
					orario()
				else:
					self.direction = 'left'
					time.sleep(0.2)
				stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
				stdscr.addstr(3,5,"Direct:"+self.direction+"         ")
				stdscr.refresh()
			elif self.key == curses.KEY_RIGHT:
				stop()
				self.motion = 'stop'
				if self.direction == 'left':
					dritto()
					self.direction ='straight'
					time.sleep(0.2)
				elif self.direction == 'straight':
					self.direction = 'right'
					antiorario()
				else:
					self.direction = 'right'
					time.sleep(0.2)
				stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
				stdscr.addstr(3,5,"Direct:"+self.direction+"         ")
				stdscr.refresh()

		exit()

def antiorario():
	motor.digitalWrite(14,motor.HIGH)
	os.system("echo 0=60 > /dev/servoblaster")
	time.sleep(0.5)
	motor.digitalWrite(14,motor.LOW)	

def indietro():
	motor.digitalWrite(10,motor.HIGH)

def avanti():
	motor.digitalWrite(11,motor.HIGH)

def stop():
	motor.digitalWrite(10,motor.LOW)
	motor.digitalWrite(11,motor.LOW)
	
def orario():
	motor.digitalWrite(14,motor.HIGH)
	os.system("echo 0=240 > /dev/servoblaster")
	time.sleep(0.5)
	motor.digitalWrite(14,motor.LOW)

def dritto():
	motor.digitalWrite(14,motor.HIGH)
	os.system("echo 0=145 > /dev/servoblaster")
	time.sleep(0.5)
	motor.digitalWrite(14,motor.LOW)

if os.system("ps -ae |grep servod > /dev/null") !=0:
	os.system("sudo servod > /dev/null")
motor = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)
motor.pinMode(10,motor.OUTPUT) #motore direzione 1
motor.pinMode(11,motor.OUTPUT) #motore direzione -1
motor.pinMode(14,motor.OUTPUT) #accensione servo
motor.pinMode(15,motor.OUTPUT) #luci
motor.digitalWrite(12,motor.LOW)
stop()
dritto()
if __name__=="__main__":
	stdscr = curses.initscr()
	curses.cbreak()
	curses.noecho()
	stdscr.keypad(1)
	stdscr.addstr(0,5,"Press 'q' to quit, up/down = Motion, left/right = Direct, space = Lights")
	stdscr.addstr(1,5,"home = Stream, shift+home = Night stream")
	stdscr.addstr(2,5,"Motion:")
	stdscr.addstr(3,5,"Direct:straight")
	stdscr.addstr(6,5,"Lights:off")
	stdscr.addstr(7,5,"Stream:off")
	curses.curs_set(0)
	stdscr.refresh()
	getkey = getkey()
	getkey.start()
	key=
	secondpass = time.time()
	while getkey.key != ord('q'):
		if time.time() - secondpass > 1:
			link = os.popen('iwconfig wlan0 |grep "Link Quality"') #deprecated
			stdscr.addstr(15,5,link.read().strip())
			stdscr.refresh()
			secondpass = time.time()
	os.system('killall raspivid >/dev/null')
	os.system('killall nc > /dev/null')
	stop()
	curses.endwin()
	motor.digitalWrite(15,motor.LOW)

RaspiTank con RaspiArm

Tutto è iniziato quando ho trovato un braccio meccanico in scatola di montaggio[5]. Originariamente era filoguidato da un sistema di interruttori a levette, ma il fatto che avesse i contatti esterni con i pin perfettamente compatibili con quelli dei cavetti per la breadboard lo rendeva particolarmente irresistibile. Inizialmente il gioco è stato eliminare il controller e usare la GPIO del Raspberry Pi per comandarlo. Essendo composto da cinque motori, e dovendo comunque muovere il motore della base, ci sarebbero dovuti essere sette relè: due per determinare il senso della corrente e cinque per attivare ognuno dei motori. L'ultimo relè avrebbe mosso alternativamente il motore n.5 del braccio o il motore dei cingoli.

Il nostro nuovo amico ULN2803

ULN2803 è un driver che permette di dirigere i 5V del Raspberry Pi in 7 uscite diverse comandate dai pin della GPIO. In pratica sostituisce tutto quel circuito composto dal diodo e il transistor. Lo potete intravedere in questa foto, al centro della breadboard.

RaspiArm.jpeg

Non sto a scendere nel dettaglio del circuito: in pratica è solo il circuito già usato prima che ha più motori.

L'unione dei due

Raspitank arm.jpeg

Una volta riportato il circuito della breadboard su una millefori è bastato fissare il braccio sulla base. Le luci sono state sostituite da un led già integrato sulla pinza. L'alimentazione del motore non è più dato da due pile torcia nella sede originale del carro ma sono le quattro che stanno dentro al braccio. Essendo raddoppiato il voltaggio adesso va molto più veloce. Purtroppo tutta la struttura ha sbilanciato l'andatura e adesso non procede più dritto come prima.

Il codice! Sempre più mal scritto! Sempre più mal commentato!

#!/usr/bin/python
import os
import wiringpi
import curses
import time
import sys
import threading
#PIN da usare
#11 luci
#12 circuito avanti
#10 circuito indietro
#3 motore braccio
#4 motore braccio
#5 motore braccio
#6 motore braccio
#14 motore braccio
#8 on/off servo
#7 PWM servo (servo 0 ServoBlaster)
class getkey(threading.Thread):
       key = 
       light = False
       motion = 'stop'
       direction = 'straight'
       mstr = ['stop ','stop ','stop ','stop ','stop ']
       def run(self):
               while self.key != 27:
                       self.key = stdscr.getch()
                       if self.key == ord(' '): #spazio accende e spegne le luci
                               if self.light:
                                       motor.digitalWrite(11,motor.LOW)
                                       time.sleep(0.5)
                                       self.light = False
                                       stdscr.addstr(6,5,"Lights:off")
                               else:
                                       motor.digitalWrite(11,motor.HIGH)
                                       time.sleep(0.5)
                                       self.light = True
                                       stdscr.addstr(6,5,"Lights:on ")
                       elif self.key == curses.KEY_HOME or self.key == curses.KEY_SHOME: #HOME accende lo stream, SHIFT+HOME accende lo stream notturno
                               self.videostart = os.system("ps -ae|grep raspivid > /dev/null")
                               if self.videostart !=0:
                                       stdscr.addstr(7,5,"Stream:on USE nc raspitank.local 9999 |mplayer -fps 150 -demuxer h264es -")
                                       if self.key == curses.KEY_HOME:
                                               os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -o - |nc -l 9999 &')
                                       else:
                                               os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -ex night -o - |nc -l 9999 &')
                               else:
                                       stdscr.addstr(7,5,"Stream:off                                                                 ")
                                       os.system('killall raspivid >/dev/null')
                                       os.system('killall nc >/dev/null')
                       elif self.key == curses.KEY_UP: #FRECCE muovono il robot
                               if self.motion == 'back':
                                       stop()
                                       self.motion = 'stop'
                                       time.sleep(0.2)
                               elif self.motion == 'stop':
                                       avanti()
                                       self.motion = 'forward'
                       #       stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
                       #       stdscr.refresh()
                       elif self.key == curses.KEY_DOWN:
                               if self.motion == 'forward':
                                       stop()
                                       self.motion = 'stop'
                                       time.sleep(0.2)
                               elif self.motion == 'stop':
                                       indietro()
                                       self.motion = 'back'
                       #       stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
                       #       stdscr.refresh()
                       elif self.key == curses.KEY_LEFT:
                               stop()
                               self.motion = 'stop'
                               if self.direction == 'right':
                                       dritto()
                                       self.direction = 'straight'
                                       time.sleep(0.2)
                               elif self.direction == 'straight':
                                       self.direction = 'left'
                                       orario()
                               else:
                                       self.direction = 'left'
                                       time.sleep(0.2)
                       #       stdscr.refresh()
                       elif self.key == curses.KEY_RIGHT:
                               stop()
                               self.motion = 'stop'
                               if self.direction == 'left':
                                       dritto()
                                       self.direction ='straight'
                                       time.sleep(0.2)
                               elif self.direction == 'straight':
                                       self.direction = 'right'
                                       antiorario()
                               else:
                                       self.direction = 'right'
                                       time.sleep(0.2)
                       #       stdscr.addstr(2,5,"Motion:"+self.motion+"    ")
                       #       stdscr.addstr(3,5,"Direct:"+self.direction+"         ")
                       #       stdscr.refresh()
                       elif self.key == ord('q'):
                               arm(0,True)
                       elif self.key == ord('a'):
                               arm(0,False)
                       elif self.key == ord('s'):
                               arm(3,True)
                       elif self.key == ord('w'):
                               arm(3,False)
                       elif self.key == ord('d'):
                               arm(4,True)
                       elif self.key == ord('e'):
                               arm(4,False)
                       elif self.key == ord('f'):
                               arm(2,True)
                       elif self.key == ord('r'):
                               arm(2,False)
                       elif self.key == ord('x'):
                               arm(1,True)
                       elif self.key == ord('z'):
                               arm(1,False)
                       elif self.key == 263:
                               stop()
                               self.motion = 'stop'    
                       stdscr.addstr(3,5,"Motion:"+self.motion+"    ")
                       stdscr.addstr(4,5,"Direct:"+self.direction+"         ")
                       stdscr.addstr(5,5,"Motor1:"+self.mstr[0]+" Motor2:"+self.mstr[3]+" Motor3:"+self.mstr[4]+" Motor4:"+self.mstr[2]+" Motor5:"+self.mstr[1])
                       stdscr.refresh()
               exit()

def arm(armot,updn):
       stop()
       getkey.motion = 'stop'
       for mtr in motors:
               if mtr[0] != motors[armot][0]:
                       mtr[1] = 0
       if motors[armot][1] == 0 and updn:
               motors[armot][1] = 1
               avanti()
               motor.digitalWrite(motors[armot][0],motor.HIGH)
               if armot == 0:
                       getkey.mstr[0] = 'open '
               elif armot == 1:
                       getkey.mstr[1] = 'right'
               else:
                       getkey.mstr[armot] = 'down '            
       elif motors[armot][1] == -1 and updn:
               motors[armot][1] = 0
               stop()
       elif motors[armot][1] == 0 and not updn:
               motors[armot][1] = -1
               indietro()
               motor.digitalWrite(motors[armot][0],motor.HIGH)
               if armot == 0:
                       getkey.mstr[0] = 'close'
               elif armot == 1:
                       getkey.mstr[1] = 'left '
               else:
                       getkey.mstr[armot] = 'up   '
       elif motors[armot][1] == 1 and not updn:
               motors[armot][1] = 0
               stop()
       else:
               motors[armot][1] = 0
               stop()

def orario():
       motor.digitalWrite(8,motor.HIGH)
       os.system("echo 0=60 > /dev/servoblaster")
       time.sleep(0.5)
       motor.digitalWrite(8,motor.LOW) 
def indietro():
       motor.digitalWrite(10,motor.HIGH)
def avanti():
       motor.digitalWrite(12,motor.HIGH)
def stop():
       motor.digitalWrite(10,motor.LOW)
       motor.digitalWrite(12,motor.LOW)
       motor.digitalWrite(5,motor.LOW)
       motor.digitalWrite(6,motor.LOW)
       motor.digitalWrite(4,motor.LOW)
       motor.digitalWrite(3,motor.LOW)
       motor.digitalWrite(14,motor.LOW)
       getkey.mstr = ['stop ','stop ','stop ','stop ','stop ']
def antiorario():
       motor.digitalWrite(8,motor.HIGH)
       os.system("echo 0=240 > /dev/servoblaster")
       time.sleep(0.5)
       motor.digitalWrite(8,motor.LOW)
def dritto():
       motor.digitalWrite(8,motor.HIGH)
       os.system("echo 0=145 > /dev/servoblaster")
       time.sleep(0.5)
       motor.digitalWrite(8,motor.LOW)

if os.system("ps -ae |grep servod > /dev/null") !=0:
       os.system("sudo /home/pi/PiBits/ServoBlaster/servod > /dev/null")
motor = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)
motor.pinMode(10,motor.OUTPUT) #motore direzione 1
motor.pinMode(12,motor.OUTPUT) #motore direzione -1
motor.pinMode(8,motor.OUTPUT) #accensione servo
motor.pinMode(11,motor.OUTPUT) #luci
motor.pinMode(5,motor.OUTPUT) #motori braccio
motor.pinMode(6,motor.OUTPUT)
motor.pinMode(4,motor.OUTPUT)
motor.pinMode(3,motor.OUTPUT)
motor.pinMode(14,motor.OUTPUT)
stop()
dritto()
if __name__=="__main__":
       stdscr = curses.initscr()
       curses.cbreak()
       curses.noecho()
       curses.curs_set(0)
       stdscr.keypad(1)
       motors = [[6,0],[5,0],[4,0],[3,0],[14,0]]
       stdscr.addstr(0,5,"Press ESC to quit, up/down = Motion, left/right = Direct, space = Lights, BACKSPACE = stop")
       stdscr.addstr(1,5,"home = Stream, shift+home = Night stream, q/a w/s e/d r/f z/x = Arm motors")
       stdscr.addstr(3,5,"Motion:")
       stdscr.addstr(4,5,"Direct:straight")
       stdscr.addstr(5,5,"Motor1:stop  Motor2:stop  Motor3:stop  Motor4:stop  Motor5:stop")
       stdscr.addstr(6,5,"Lights:off")
       stdscr.addstr(7,5,"Stream:off")
       stdscr.refresh()
       getkey = getkey()
       getkey.start()
       key=
       secondpass = time.time()
       while getkey.key != 27:
               if time.time() - secondpass > 1:
                       link = os.popen('iwconfig wlan0 |grep "Link Quality"') #deprecated
                       stdscr.addstr(15,5,link.read().strip())
                       stdscr.refresh()
                       secondpass = time.time()
       os.system('killall raspivid >/dev/null')
       os.system('killall nc > /dev/null')
       stop()
       curses.endwin()
       motor.digitalWrite(11,motor.LOW)

I video

Il RaspiTank con il RaspiArm

Il video della prima scampagnata del RaspiTank

Il video della prima escursione notturna