Differenze tra le versioni di "RaspiTank"
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 | + | 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 == | ||
+ | |||
+ | [[File: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 | ||
[[File:RaspiTank_inlavorazione.jpeg]] | [[File:RaspiTank_inlavorazione.jpeg]] | ||
== Il nostro amico servocomando == | == 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. | + | 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[https://github.com/richardghirst/PiBits/tree/master/ServoBlaster]. 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 == | ||
+ | |||
+ | [[File:Raspitank interior.jpg]] | ||
+ | |||
+ | Per l'azionamento del motore abbiamo riciclato i circuiti già fatti per la [[RaspiCar]] ovvero questi. | ||
+ | |||
+ | [[File: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è. | ||
+ | |||
+ | [[File: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 | + | == 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[http://sourceforge.net/projects/mjpg-streamer/] 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[https://github.com/richardghirst/PiBits/issues/11]) | |
+ | == Il codice, mal scritto e mal commentato == | ||
+ | #!/usr/bin/python | ||
+ | import os | ||
+ | import wiringpi | ||
+ | import curses | ||
+ | import time | ||
+ | import sys | ||
+ | |||
+ | #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 | ||
+ | |||
+ | #Per registrare i comandi aggiungere il nome di un file (che sara' sovrascritto) | ||
+ | #i comandi sono salvati in formato XML | ||
+ | |||
+ | def avanti(): | ||
+ | motor.digitalWrite(10,motor.HIGH) | ||
+ | |||
+ | def indietro(): | ||
+ | motor.digitalWrite(11,motor.HIGH) | ||
+ | |||
+ | def stop(): | ||
+ | motor.digitalWrite(10,motor.LOW) | ||
+ | motor.digitalWrite(11,motor.LOW) | ||
+ | |||
+ | def antiorario(): | ||
+ | motor.digitalWrite(14,motor.HIGH) | ||
+ | os.system("echo 0=240 > /dev/servoblaster") | ||
+ | time.sleep(0.5) | ||
+ | motor.digitalWrite(14,motor.LOW) | ||
+ | |||
+ | def orario(): | ||
+ | motor.digitalWrite(14,motor.HIGH) | ||
+ | os.system("echo 0=60 > /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) | ||
+ | |||
+ | |||
+ | servodstart = os.system("ps -ae |grep servod >/dev/null") #controlla che servod sia stato lanciato | ||
+ | if servodstart != 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__": | ||
+ | recording = False | ||
+ | if len(sys.argv) > 2: | ||
+ | print "troppi parametri" | ||
+ | quit() | ||
+ | elif len(sys.argv) == 2: | ||
+ | filecom = open(sys.argv[1],'w') | ||
+ | recording = True | ||
+ | filecom.write("<listacomandi>") | ||
+ | motion = '' | ||
+ | direction ='straight' | ||
+ | stdscr = curses.initscr() | ||
+ | curses.cbreak() | ||
+ | stdscr.keypad(1) | ||
+ | stdscr.addstr(0,5,"Press 'q' to quit, up/down = Motion, left/right = Direct, space = Lights, home = 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() | ||
+ | key = '' | ||
+ | light = False | ||
+ | timepress = 0 | ||
+ | while key != ord('q'): | ||
+ | key = stdscr.getch() | ||
+ | if key == ord(' '): #spazio accende e spegne le luci | ||
+ | if light: | ||
+ | motor.digitalWrite(15,motor.LOW) | ||
+ | time.sleep(0.5) | ||
+ | light = False | ||
+ | stdscr.addstr(6,5,"Lights:off") | ||
+ | else: | ||
+ | motor.digitalWrite(15,motor.HIGH) | ||
+ | time.sleep(0.5) | ||
+ | light = True | ||
+ | stdscr.addstr(6,5,"Lights:on ") | ||
+ | elif key == curses.KEY_HOME: | ||
+ | videostart = os.system("ps -ae|grep raspivid > /dev/null") | ||
+ | if videostart !=0: | ||
+ | stdscr.addstr(7,5,"Stream:on USE nc raspitank.local 9999 |mplayer -fps 150 -demuxer h264es -") | ||
+ | os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -o - |nc -l 9999 &') | ||
+ | else: | ||
+ | stdscr.addstr(7,5,"Stream:off ") | ||
+ | os.system('killall raspivid >/dev/null') | ||
+ | os.system('killall nc >/dev/null') | ||
+ | elif key == curses.KEY_UP: | ||
+ | if motion == 'down': | ||
+ | if recording: | ||
+ | filecom.write("<ordine>"+str(time.time()-timepress)+"</ordine></com>") | ||
+ | stdscr.addstr(6,5,str(time.time() - timepress)) | ||
+ | stop() | ||
+ | motion = '' | ||
+ | time.sleep(0.2) | ||
+ | elif motion == '': | ||
+ | if recording: | ||
+ | filecom.write("<com><ordine>bk</ordine>") | ||
+ | timepress = time.time() | ||
+ | avanti() | ||
+ | motion = 'up' | ||
+ | stdscr.addstr(2,5,"Motion:"+motion+" ") | ||
+ | stdscr.refresh() | ||
+ | elif key == curses.KEY_DOWN: | ||
+ | if motion == 'up': | ||
+ | if recording: | ||
+ | filecom.write("<ordine>"+str(time.time()-timepress)+"</ordine></com>") | ||
+ | stdscr.addstr(4,5,str(time.time() - timepress)) | ||
+ | stop() | ||
+ | motion = '' | ||
+ | time.sleep(0.2) | ||
+ | elif motion == '': | ||
+ | if recording: | ||
+ | filecom.write("<com><ordine>fd</ordine>") | ||
+ | timepress = time.time() | ||
+ | indietro() | ||
+ | motion = 'down' | ||
+ | stdscr.addstr(2,5,"Motion:"+motion+" ") | ||
+ | stdscr.refresh() | ||
+ | elif key == curses.KEY_LEFT and not (motion == 'up' or motion == 'down'): | ||
+ | stop() | ||
+ | motion = '' | ||
+ | if recording: | ||
+ | filecom.write("<com><ordine>rt</ordine></com>") | ||
+ | if direction == 'right': | ||
+ | dritto() | ||
+ | direction = 'straight' | ||
+ | time.sleep(0.2) | ||
+ | elif direction == 'straight': | ||
+ | direction = 'left' | ||
+ | orario() | ||
+ | else: | ||
+ | direction = 'left' | ||
+ | time.sleep(0.2) | ||
+ | stdscr.addstr(2,5,"Motion:"+motion+" ") | ||
+ | stdscr.addstr(3,5,"Direct:"+direction+" ") | ||
+ | stdscr.refresh() | ||
+ | elif key == curses.KEY_RIGHT and not (motion == 'up' or motion == 'down'): | ||
+ | stop() | ||
+ | motion = '' | ||
+ | if recording: | ||
+ | filecom.write("<com><ordine>lt</ordine></com>") | ||
+ | if direction == 'left': | ||
+ | dritto() | ||
+ | direction ='straight' | ||
+ | time.sleep(0.2) | ||
+ | elif direction == 'straight': | ||
+ | direction = 'right' | ||
+ | antiorario() | ||
+ | else: | ||
+ | direction = 'right' | ||
+ | time.sleep(0.2) | ||
+ | stdscr.addstr(2,5,"Motion:"+motion+" ") | ||
+ | stdscr.addstr(3,5,"Direct:"+direction+" ") | ||
+ | stdscr.refresh() | ||
+ | os.system('killall raspivid >/dev/null') | ||
+ | os.system('killall nc > /dev/null') | ||
+ | stop() | ||
+ | curses.endwin() | ||
+ | if recording: | ||
+ | filecom.write("<com><ordine>quit</ordine></com>") | ||
+ | filecom.write("</listacomandi>") | ||
[[Category:Progetti]] | [[Category:Progetti]] |
Versione delle 22:27, 14 ago 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
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
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
Per l'azionamento del motore abbiamo riciclato i circuiti già fatti per la RaspiCar ovvero questi.
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è.
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
#!/usr/bin/python import os import wiringpi import curses import time import sys #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 #Per registrare i comandi aggiungere il nome di un file (che sara' sovrascritto) #i comandi sono salvati in formato XML def avanti(): motor.digitalWrite(10,motor.HIGH) def indietro(): motor.digitalWrite(11,motor.HIGH) def stop(): motor.digitalWrite(10,motor.LOW) motor.digitalWrite(11,motor.LOW) def antiorario(): motor.digitalWrite(14,motor.HIGH) os.system("echo 0=240 > /dev/servoblaster") time.sleep(0.5) motor.digitalWrite(14,motor.LOW) def orario(): motor.digitalWrite(14,motor.HIGH) os.system("echo 0=60 > /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) servodstart = os.system("ps -ae |grep servod >/dev/null") #controlla che servod sia stato lanciato if servodstart != 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__": recording = False if len(sys.argv) > 2: print "troppi parametri" quit() elif len(sys.argv) == 2: filecom = open(sys.argv[1],'w') recording = True filecom.write("<listacomandi>") motion = direction ='straight' stdscr = curses.initscr() curses.cbreak() stdscr.keypad(1) stdscr.addstr(0,5,"Press 'q' to quit, up/down = Motion, left/right = Direct, space = Lights, home = 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() key = light = False timepress = 0 while key != ord('q'): key = stdscr.getch() if key == ord(' '): #spazio accende e spegne le luci if light: motor.digitalWrite(15,motor.LOW) time.sleep(0.5) light = False stdscr.addstr(6,5,"Lights:off") else: motor.digitalWrite(15,motor.HIGH) time.sleep(0.5) light = True stdscr.addstr(6,5,"Lights:on ") elif key == curses.KEY_HOME: videostart = os.system("ps -ae|grep raspivid > /dev/null") if videostart !=0: stdscr.addstr(7,5,"Stream:on USE nc raspitank.local 9999 |mplayer -fps 150 -demuxer h264es -") os.system('raspivid -t 0 -fps 15 -w 640 -h 480 -rot 180 -o - |nc -l 9999 &') else: stdscr.addstr(7,5,"Stream:off ") os.system('killall raspivid >/dev/null') os.system('killall nc >/dev/null') elif key == curses.KEY_UP: if motion == 'down': if recording: filecom.write("<ordine>"+str(time.time()-timepress)+"</ordine></com>") stdscr.addstr(6,5,str(time.time() - timepress)) stop() motion = time.sleep(0.2) elif motion == : if recording: filecom.write("<com><ordine>bk</ordine>") timepress = time.time() avanti() motion = 'up' stdscr.addstr(2,5,"Motion:"+motion+" ") stdscr.refresh() elif key == curses.KEY_DOWN: if motion == 'up': if recording: filecom.write("<ordine>"+str(time.time()-timepress)+"</ordine></com>") stdscr.addstr(4,5,str(time.time() - timepress)) stop() motion = time.sleep(0.2) elif motion == : if recording: filecom.write("<com><ordine>fd</ordine>") timepress = time.time() indietro() motion = 'down' stdscr.addstr(2,5,"Motion:"+motion+" ") stdscr.refresh() elif key == curses.KEY_LEFT and not (motion == 'up' or motion == 'down'): stop() motion = if recording: filecom.write("<com><ordine>rt</ordine></com>") if direction == 'right': dritto() direction = 'straight' time.sleep(0.2) elif direction == 'straight': direction = 'left' orario() else: direction = 'left' time.sleep(0.2) stdscr.addstr(2,5,"Motion:"+motion+" ") stdscr.addstr(3,5,"Direct:"+direction+" ") stdscr.refresh() elif key == curses.KEY_RIGHT and not (motion == 'up' or motion == 'down'): stop() motion = if recording: filecom.write("<com><ordine>lt</ordine></com>") if direction == 'left': dritto() direction ='straight' time.sleep(0.2) elif direction == 'straight': direction = 'right' antiorario() else: direction = 'right' time.sleep(0.2) stdscr.addstr(2,5,"Motion:"+motion+" ") stdscr.addstr(3,5,"Direct:"+direction+" ") stdscr.refresh() os.system('killall raspivid >/dev/null') os.system('killall nc > /dev/null') stop() curses.endwin() if recording: filecom.write("<com><ordine>quit</ordine></com>") filecom.write("</listacomandi>")