Robot Arm serial control protocol permette di controllare i 5 servo del braccio robot co un unico comando.
Nel precedente articolo che hai letto sul robot arm controllato con Arduino hai notato che ciascun servo riceve il comando in modo indipendente, questo tipo di movimento presenta alcuni inconvenienti.
Ho iniziato a studiare quale potesse essere il modo migliore per impartire comandi al braccio robot in questa fase.
Ho trovato una soluzione realizzando il progetto: Robot Arm serial control protocol.
Come funziona Robot Arm serial control protocol
Il robot arm serial control protocol non è un vero e proprio protocollo ma più un sistema per impartire, al braccio robot, i comandi di posizione di tutti i servo contemporaneamente.
I comandi, gradi dei servo, li inserisci sempre dall’interfaccia seriale di Arduino o di qualsiasi altro sistema di comunicazione seriale, seguendo la seguente sintassi:
[servo1] [servo2] [servo3] [servo4] [servo5]
ricordando le associazioni:
- [servo1] è il servo di base;
- [servo2] corrisponde al servo della spalla, ossia a destra del braccio guardandolo dal retro;
- [servo3] controlla il servo connesso al gomito, ossia il servo a sinistra guardandolo dal retro;
- [servo4] è il servo del polso;
- [servo5] la pinza o gripper;
per cui inviando una stringa così formattata:
90 90 90 90 75
ottieni:
- base in posizione 90°
- spalla in posizione 90°
- gomito in posizione 90°
- polso in posizione 90°
- pinza chiusa
Se da questa posizione provassi ad inviare la linea comandi:
90 90 90 90 0
otterresti che la sola pinza si aprirebbe e tutti gli altri movimenti resterebbero invariati.
Vantaggi
Certamente il vantaggio maggiore nell’utilizzo di una stringa per inviare al robot arm la posizione di tutti i servo rende più rapido il controllo, inoltre, ti permette di scrivere delle sequenze relativamente semplici di passi da pompiere, come ad esempio:
180 90 90 90 75
90 90 90 90 50
0 90 90 90 25
90 90 90 90 0
ti permetterà di aprite la pinza da chiusa alla massima apertura in 4 passaggi e contemporaneamente la base si sposterà da 90° a 180° poi nuovamente a 90° ed a 0° per terminare a 90°.
Ho voluto portarti il solo esempio di due servo, ma puoi, modificare anche tutti i movimenti dei servo contemporaneamente.
Svantaggi
Uno degli svantaggi, in termini di tempo, è dato dal fatto che anche per spostare un solo servo alla volta devi comunque scrivere tutta la sequenza di gradi degli altri;
un secondo svantaggio è che, essendo un sistema posizionale, ossia ogni grado relativo al corrispondente servo è definito per posizione, se dimentichi un valore o inverti una posizione sposti il servo sbagliato, occorre fare attenzione;
un terzo svantaggio è rappresentato dalle posizioni limite: se ad esempio il servo del gomito può muoversi da 140 a 80 quando la spalla è in posizione 90° mentre può aumentare la sua corsa se la spalla si posiziona a 40° dovrai far attenzione a quale valore di riferimento imposti per quest’ultima prima di spostare il gomito.
Quest’ultimo limite era presente anche nel precedente articolo e si tratta di un vincolo meccanico.
Lo sketch del robot arm serial control protocol
Andando oltre i vantaggi e svantaggi di questa soluzione per il robot arm serial control protocol puoi leggere lo sketch e la sua descrizione nei passaggi sotto:
#include <Servo.h> Servo base; // 1 0 - 180 Servo shoulder; // 2 140 - 37 Servo elbow; // 3 65 - 140 Servo wrist_rot; // 4 0 - 180 Servo gripper; // 5 0 - 75 byte pos[5] = {90,90,90,90,75}; String inputString = ""; bool stringComplete = false; void setup() { Serial.begin(9600); base.attach(11); shoulder.attach(10); elbow.attach(9); wrist_rot.attach(6); gripper.attach(5); base.write( pos[0] ); shoulder.write( pos[1] ); elbow.write( pos[2] ); wrist_rot.write( pos[3] ); gripper.write( pos[4] ); inputString.reserve(200); Serial.println( "**************** START ****************" ); } void loop() { if (stringComplete) { Serial.print("Input: "); Serial.println(inputString); inputString = getPos( inputString," ",0 ); Serial.print("Buffer0: "); Serial.println(inputString); inputString = getPos( inputString," ",1 ); Serial.print("Buffer1: "); Serial.println(inputString); inputString = getPos( inputString," ",2 ); Serial.print("Buffer2: "); Serial.println(inputString); inputString = getPos( inputString," ",3 ); Serial.print("Buffer3: "); Serial.println(inputString); inputString = getPos( inputString," ",4 ); base.write(pos[0]); shoulder.write(pos[1]); elbow.write(pos[2]); wrist_rot.write(pos[3]); gripper.write(pos[4]); inputString = ""; stringComplete = false; } } String getPos( String input, String sep, byte posIndex ) { byte i = input.indexOf(sep); if ( i > 0 ) { pos[posIndex] = input.substring(0,i).toInt(); return input.substring(i+1); } } void serialEvent() { while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read(); // add it to the inputString: inputString += inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it: if (inChar == '\n') { stringComplete = true; } } }
e dopo averlo compreso potrai anche decidere di gestire un protocollo più adatto allo scopo e non soggetto agli aspetti svantaggiosi offerti da questa versione.
Descrizione dello sketch
Inizia con la linea 01: non varia rispetto ai precedenti sketch, includi la libreria Servo.h;
linee 03-07: imposta le istanze di tipo Servo con cui controllerai i diversi Servo;
linea 09: definisci un array di 5 posizioni di tipo byte in cui memorizzare i valori iniziali di posizione dei servo;
linee 11-12: definisci due variabili di servizio, la prima è di tipo String e la seconda è una booleana di controllo;
linee 14-31: imposti la funzione setup() di arduino in modo che inizializzi la comunicazione seriale esegua l’attach di tutti i servo alle rispettive istanze e posizioni ciascun servo al grado desiderato di startup;
linea 33: definisci la funzione loop() di Arduino;
linea 34: controlla se la variabile booleana stringComplete ha valore true in tal caso vuol dire che l’intera stringa è stata ricevuta. Ricorda che la ricezione dei comandi dalla seriale l’hai demandata alla funzione serialEvent() definita alla linea 70 ed invariata rispetto a quella degli alri sketch;
linee 36-37: la prima linea si preoccupa di scrivere sul monitor seriale, come controllo di ciò che ha ricevuto, il valore della stringa d input ricevuta, ti restituisce il valore di inputString. La seconda linea si preoccupa di inviare alla funzione getPos() il valore della stringa di input, il separatore scelto ( lo spazio in questo caso ) e l’indice in cui posizionare il valore da leggere e da memorizzare nell’array pos[] definito alla linea 09;
linee 39-49: eseguono le medesime operazioni delle due precednti per le 4 volte successive e, avrai notato, che l’unico valore a cambiare è rappresentato dall’indice, o posizione, passato alla funzione getPos();
Notice
Nota che ciascuna delle linee che richiama la getPos() memorizza la risposta nella stessa variabile inputString in modo da riutilizzarla e rielaborarla ad ogni chiamata;
linee 51-55: esegue il posizionamento di ciascun servo, similmente a come è già avvenuto per le linee 23-27;
linea 57: ripulisci il valore di inputString dopo aver eseguito il parsing o meglio dopo averla usata per posizionare i servo;
linea 58: imposta nuovamente la variabile stringComplete a false, in questo modo al ciclo di loop() successivo non entrerà nell’IF a meno che non sia stata ricevuta un’altra stringa comandi;
La funzione getPos()
la funzione getPos() è definita alla linea 62 come funzione di tipo String in quanto restituisce una risultato di questa tipologia ogni volta che la richiami, vediamo come si comporta;
linea 63: imposta una variabile di tipo byte definita come i contenente la posizione restituita dal metodo indexOf eseguito sulla stringa di input ed usando come separatore il valore di sep, che sai essere spazio;
linea 64: controlla che i non sia nullo e sia superiore a 0;
linee 65: ho scritto volutamente “linee 65” in quanto sono concentrate in una sola linea più istruzioni che servono ad estrarre dalla stringa passata il valore numerico relativo ai gradi del servo e memorizzarli nella posizione desiderata dell’array pos[]. Per ottenere questo tipo di risultato prima utilizzi il metodo substring() a cui passi il valore iniziale 0 ed il valore di i come punto finale.
Il risultato, che corrisponde, ad esempio, al primo numero della stringa viene convertito dal metodo toInt() in un valore numerico intero e memorizzato nell’array pos[] alla posizione di indice inviata come ultimo parametro della funzione;
linea 58: restituisci la stringa troncata della parte già elaborata, usi il metodo substring() a cui passi come indice il valore successivo alla i e lasciando il secondo parametro libero indichi alla funzione che sia l’ultima posizione;
Grazie a questo sistema il return della funzione è sempre la stringa restante da elaborare e puoi ripassarla nuovamente alla funzione getPos() con un indice differente.
La sequenza
Ho realizzato una semplice sequenza che ti permette di controllare il braccio robot per afferrare un oggetto:
in modo specifico l’oggetto scelto è un cilindro di plastica del diametro di circa 4cm appartenente ad un giocattolo per bambini.
Ecco la sequenza che puoi passare al tuo robot arm serial control protocol:
90 90 90 90 0
90 60 90 90 0
90 40 90 90 37
90 40 70 90 37
90 100 70 90 37
90 55 70 90 37
90 51 70 90 37
90 49 70 90 37
90 45 70 90 37
90 45 70 90 0
90 45 90 90 0
90 90 90 90 75
per ottenere quanto vedi nel video seguente:
Nei prossimi articoli proverai a cambiare alcune finzioni dello sketch proposto per migliorare il posizionamento e l’invio di comandi attraverso l’interfaccia seriale.
2 commenti
Ciao Mauro,
sono un beginner e ho un problema riguardo il posizionamento di default del roboarm al momento in cui lo attivo.
Per spiegarti meglio cosa intendo dovrei inserire il link ad un video ma ho letto che non posso farlo.
In sintesi, appena accendo il roboarm il braccio si stende completamente e si abbassa fino a toccare con la pinza la superficie su cui poggia. E ovviamente da quel punto in poi e’ ingestibile.
Immagino sia dovuto alla errata calibrazione del perno dei due servo braccio e gomito anche se ricordo in fase di montaggio di essere stato attento a come ruotare i perni seguendo le istruzioni. C’e’ un modo per non smontarlo di nuovo? Grazie
Autore
Ciao Fabio,
se lo hai montato tu puoi smontarlo ed eseguire tutti i controlli del caso.