RaspiTank

Da raspibo.

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 differenziale mosso tramite un servo.

Indice

Prima versione

Raspitank finale.jpg

Il RaspiTank aveva 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

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 alla porta CSI del 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 è il circuito già usato prima con 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.

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

Lo trovate su GitLab https://gitlab.com/oloturia/raspitank

Contiene:

  • audio.sh Piccolo script per lo streaming audio (serve una scheda sonora USB con microfono)
  • raspitank.ino Nella nuova versione un ATmega8 si occupa dei relay tramite seriale.
  • roboy.py Il codice sul Raspberry
  • videoday.sh Due script in bash per far partire lo streaming video normale...
  • videonig.sh ...e con filtro notturno

I video e altre foto

Il RaspiTank con il RaspiArm

Il video della prima scampagnata del RaspiTank

Il video della prima escursione notturna

Raspitank insegue Ardubottino.png

Scappa, Ardubottino, scappa!! (questa foto è quel che si vede dalla camera del RaspiTank)

Strumenti personali
Namespace

Varianti
Azioni
Navigazione
Strumenti