CORSO BRACCIO ROBOT LEZIONE 5

INDICE

  1. Avvertenze
  2. Note sul Copyright
  3. Controllare un Braccio Robot via BT con PC e Arduino UNO
  4. Progetto 09: Controllo e Comando Braccio Robot BT con PC ed HC-05
  5. Analisi degli sketch del Progetto 09 – Controllo e Comando Braccio Robot
  6. Controllare un Braccio Robot con Controller PCA9685 e Arduino UNO
  7. Descrizione della Scheda Controllo Servomotori PCA9865
  8. Progetto 10: Uso della Scheda PCA9685 nel controllo dei Servomotori
  9. Analisi dello sketch del Progetto 10 – Test della scheda PCA9685
  10. Progetto 11: Braccio Robot controllato da scheda PCA9685
  11. Analisi degli sketch del Progetto 11 – Braccio Robot controllato da scheda PCA9685

Avvertenze

Relativamente agli aspetti di sicurezza, poiché i progetti sono basati su alimentazione elettrica in bassissima tensione erogata dalla porta usb del pc o da batterie di supporto o alimentatori con al massimo 9V in uscita, non ci sono particolari rischi di natura elettrica. È comunque doveroso precisare che eventuali cortocircuiti causati in fase di esercitazione potrebbero produrre danni al pc, agli arredi ed in casi estremi anche a ustioni, per tale ragione ogni qual volta si assembla un circuito, o si fanno modifiche su di esso, occorrerà farlo in assenza di alimentazione e al termine dell’esercitazione occorrerà provvedere alla disalimentazione del circuito rimuovendo sia il cavo usb di collegamento al pc che eventuali batterie dai preposti vani o connettori di alimentazione esterna. Inoltre, sempre per ragioni di sicurezza, è fortemente consigliato eseguire i progetti su tappeti isolanti e resistenti al calore acquistabili in un qualsiasi negozio di elettronica o anche sui siti web specializzati.

Al termine delle esercitazioni è opportuno lavarsi le mani, in quanto i componenti elettronici potrebbero avere residui di lavorazione che potrebbero arrecare danno se ingeriti o se a contatto con occhi, bocca, pelle, etc. Sebbene i singoli progetti siano stati testati e sicuri, chi decide di seguire quanto riportato nel presente documento, si assume la piena responsabilità di quanto potrebbe accadere nell’esecuzione delle esercitazioni previste nello stesso. Per i ragazzi più giovani e/o alle prime esperienze nel campo dell’Elettronica, si consiglia di eseguire le esercitazioni con l’aiuto ed in presenza di un adulto.

Roberto Francavilla

Controllare un Braccio Robot via BT con PC e Arduino UNO

Dopo la Lezione 4 che è stata dedicata ad approfondimenti e progetti sull’uso del Modulo HC-05, con questa Lezione riprendiamo i nostri progetti per il controllo e comando di un Braccio Robot. Desidero evidenziarvi che le esperienze viste saranno applicate in parte anche nei prossimi progetti e comunque saranno molto utili per i vostri futuri progetti.
Prima di passare alla lezione vera e propria, desideravo rendervi note alcune modifiche che ho apportato al mio Braccio Robot che si sono rese necessarie in quanto ho visto che le prestazioni del braccio non erano quelle sperate, infatti sin dalla fase di assemblaggio del braccio avevo notato che vi erano degli elementi sottodimensionati, in particolare i servomotori che producevano il movimento indicato con S2 ed S3.
Il nostro Braccio Robot possiamo rappresentarlo come segue:

– S1 – ROTAZIONE BASE
– S2 – ROTAZIONE BRACCIO
– S3 – ROTAZIONE 1 AVANBRACCIO
– S4 – ROTAZIONE 2 AVANBRACCIO
– S5 – ROTAZIONE 1 POLSO
– S6 – ROTAZIONE 2 POLSO
– S7 – APERTURA/CHIUSURA PINZA (NON RAPPRESENTATA GRAFICAMENTE)
Per quanto detto prima, facendo riferimento alla Lezione 2 sull’Assemblaggio, ho modificato i due servomotori S2 ed S3, sostituendoli e quindi passando da due MG 906R a due TD 8120MG, che riescono a sviluppare una coppia molto più elevata e quindi più idonei al tipo di Braccio Robot utilizzato.
Di seguito le caratteristiche dei due servo a confronto:

Come è possibile osservare ho sostituito un servo con una coppia di circa 10 kg∙cm, con uno che ne ha praticamente il doppio.

Ritornando alla nostra lezione, quindi, abbiamo che per movimentare un braccio robot tipo quello da me acquistato a 7DOF, occorrono, considerando anche il servo utilizzato per movimentare le pinze, in totale 7 servo motori, questo non è un numero elevato per Arduino, in quanto abbiamo 14 PIN digitali che possiamo utilizzare. Infatti, il progetto che segue vi mostra come, con le opportune librerie, possiamo gestire direttamente da Arduino i 7 servomotori.

Progetto 09: Controllo e Comando Braccio Robot via BT con PC ed HC-05

Anche per questo progetto si utilizzerà Processing per creare una fantastica interfaccia grafica che riporta graficamente in 3D sul monitor i movimenti comandati ad Arduino per pilotare il nostro braccio robotico.

Ovviamente, come ogni mio progetto, esso è da considerarsi solo come punto di partenza anche per i vostri sviluppi e miglioramenti.

Per questo progetto ci occorre:

Andiamo a realizzare quanto rappresentato dagli schemi di montaggio sotto, come ben potete osservare sono in sostanza due sistemi separati, li rappresenterò separatamente anche per migliorarne la lettura, uno che si occupa della comunicazione Bluetooth e l’altro del comando e controllo del braccio robot, i due circuiti vengono montati sulle due basette separate e poi entrambe si interfacceranno con Arduino. L’alimentazione dei 7 servo motori è fatta attraverso un alimentatore stabilizzato con tensione a 5 V.
Vediamo lo schema da realizzare per la parte relativa alla comunicazione Bluetooth con l’HC-05. Come è possibile osservare è sostanzialmente identico a quanto fatto per il Progetto 5

Per quanto riguarda lo schema da realizzare per il controllo dei servomotori del braccio, potete fare riferimento allo schema sotto. Come è possibile osservare, poiché gestisco tutti i collegamenti con una basetta da 400 fori, per ricevere conferma che la basetta è alimentata, ho inserito un LED (con la propria resistenza di protezione) fra il positivo ed il negativo della basetta stessa.

Durante il montaggio dei circuiti fate attenzione ai colori dei cavi del servo (marrone = GND, rosso = Vcc e arancio = Segnale) ed inoltre poiché sicuramente necessiterete di prolunghe, fate in modo che nei punti di giunzione non vi si creino distaccamenti di cavi mettendo del nastro adesivo di carta.

Nota: per far si che i due circuiti abbiano lo stesso riferimento a massa, i due negativi delle basette vanno collegati. Occorre fare attenzione: se non avete un buon alimentatore, tale collegamento può provocare l’entrata in protezione del PC, per cui in questo caso occorre tenerle separate (per il PC, basta staccarlo dalla rete per 10-15 sec e riattaccarlo). 

Lo sketch da caricare su Arduino è il seguente:

Lo sketch da aprire in Processing sul PC è il seguente:

/*
BeMaker.org - BRACCIO ROBOTICO
Progetto 09 – CONTROLLO BRACCIO VIA BT CON PC E ARDUINO CON HC-05
*/
import processing.serial.*;  //Importo la libreria perle comunicazioni seriali
Serial myPort;               //Definisco l'oggetto myPort
String Stato;                //Definisce la variabile Stato come stringa che darà indiazioni su Arduino
int vel=2;                   //Fattore moltiplicativo angoli di rotazione
float rotazioneS1;  //Definisco la variabile di rotazione di S1
float rotazioneS2;  //Definisco la variabile di rotazione di S2
float rotazioneS3;  //Definisco la variabile di rotazione di S3
float rotazioneS4;  //Definisco la variabile di rotazione di S4
float rotazioneS5;  //Definisco la variabile di rotazione di S5
float rotazioneS6;  //Definisco la variabile di rotazione di S6
float rotazioneS7;  //Definisco la variabile di rotazione di S7
void setup() {
  String portName = "COM10";  //Associo la porta COM effettivamete utilizzata per la comunicazione - vedere Lezione 4 del Corso per come fare
  myPort = new Serial(this, portName, 9600);  //Creazione e Settaggio della porta
  myPort.bufferUntil('\n');                // Definisce il carattere terminale della stringa da leggere
  size( 1500, 800, P3D ); //Creao una finestra 3D x=1500, y=800 z= indefinito
  ambientLight(0,0,255);  //Aggiungo una luce ambientale bianca diffusa
  pointLight(255, 0, 0, width/2, height/2, 400);  //Aggiungo un proiettore di luce biancaa metà lunghezza della finestra per dare le ombre per la profondità
  noStroke();          //Non definisco i contorni dei solidi che andrò a disegnare
  rotazioneS1=PI/2;    //Servo S1 BASE a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180    
  rotazioneS2=PI/2;    //Servo S2 Braccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS3=PI/2;    //Servo S3 AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS4=PI/2;    //Servo S4 Rotazione AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS5=PI/2;    //Servo S5 Rotazione Polso a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS6=PI/2;    //Servo S6 Rotazione Pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS7=PI/2;    //Servo S7 presa pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  myPort.write(("Pp")+'\n');  //Viene inviata la stringa 'Pp' alla porta BT per sincerarci che partiamo da una condizione nota
  delay(100);
}
void serialEvent (Serial myPort){       // Controlla se ci sono dati sulla porta seriale
  Stato = myPort.readStringUntil('\n'); //Legge la stringa proveniente da Arduino e la mette nella variabile Pacchetto
}

void draw() {
  background(0);
  lights();
  //Posiziono il testo per sito
  fill(255);    //Seleziono il colore Bianco     
  textSize(18);                        //Dimensione testo
  text("WWW.BEMAKER.ORG", 50, 780);   //Posiziona testo "WWW.BEMAKER.ORG"
  text(Stato, 500, 780);               //Posiziona testo Stato
  //Posiziono il testo per la legenda dei tasti di comando
  textSize(30); //Dimensione testo
  text("LEGENDA", 1200, 50);   //Posiziona testo "LEGENDA"
  text("a = S1 <-", 1100, 100);   //Posiziona testo "S1 <-"
  text("s = S1 ->", 1330, 100);   //Posiziona testo "S1 <-"
  text("e = S2 ^", 1100, 200);   //Posiziona testo "e = S2 ^"
  text("d = S2 v", 1330, 200);   //Posiziona testo "d = S2 v"
  text("r = S3 ^", 1100, 300);   //Posiziona testo "e = S3 ^"
  text("f = S3 v", 1330, 300);   //Posiziona testo "d = S3 v"
  text("t = S4 <-", 1100, 400);   //Posiziona testo "e = S4 ^"
  text("g = S4 ->", 1330, 400);   //Posiziona testo "d = S4 v"
  text("y = S5 ^", 1100, 500);   //Posiziona testo "e = S5 ^"
  text("h = S5 v", 1330, 500);   //Posiziona testo "d = S5 v"
  text("u = S6 <-", 1100, 600);   //Posiziona testo "e = S6 <-"
  text("j = S6 ->", 1330, 600);   //Posiziona testo "d = S6 ->"
  text("k = S7 <->", 1100, 700);   //Posiziona testo "d = S7 <->"
  text("l = S7 >-<", 1330, 700);   //Posiziona testo "d = S7 >-<"
        
  // Disegno il Braccio Robot in 3D
  //Disegno la base
  translate(300,700,0);  //Sposto il riferimento del disegno verso destra (x) di 200, verso il basso (y) di 600 e z=0
  rotateY(rotazioneS1);  //Ruoto lungo l'asse y
  fill(0,127,255);       //Seleziono il colore blu
  box(60);               //Disegno alle nuove coordinate un cubo di lato 60
  //Disegno il giunto di rotazione del braccio
  translate( 0,-60,0 );   //Sposto il riferimento solo verso l'alto lungo la y
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( rotazioneS2 ); //Ruoto lungo l'asse X
  sphere(30);             //Disegno una sfera
  //Disegno il braccio
  translate( 0,0,140 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,255,0);       //Seleziono il colore Giallo
  box( 60, 60, 220 );    //Disegno un parallelepipedo 60x60x220
  //Disegno il giunto di rotazione dell'avanbraccio
  translate( 0,0,140 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( rotazioneS3 ); //Ruoto lungo l'asse X
  rotateZ( rotazioneS4 ); //Ruoto lungo l'asse z
  sphere(30);             //Disegno una sfera
  //Disegno l'avanbraccio
  translate( 0,0,-110 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,255,0);       //Seleziono il colore Giallo
  box( 60, 60, 150 );    //Disegno un parallelepipedo 60x60x220
  //Disegno il giunto di rotazione del polso
  translate( 0,0,-110 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateY( rotazioneS5 ); //Ruoto lungo l'asse y
  rotateZ( rotazioneS6 ); //Ruoto lungo l'asse z
  sphere(30);             //Disegno una sfera
  //Disegno la pinza inferiore
  translate( 0,-75,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  box( 20, 130, 20 );    //Disegno un parallelepipedo
  //Disegno il giunto di rotazione della pinza superiore
  translate( 0,40,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( rotazioneS7 ); //Ruoto lungo l'asse x
  sphere(10);             //Disegno una sfera
  //Disegno la pinza superiore
  translate( 0,50,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  rotateX( -PI/2 );       //Ruoto lungo l'asse X
  box( 20, 20, 80 );    //Disegno un parallelepipedo
  translate( 0,-40,40 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  rotateX( PI/2 );       //Ruoto lungo l'asse X
  box( 20, 20, 80 );    //Disegno un parallelepipedo
 }
void keyPressed() {
  if (key == 's') {
    rotazioneS1=rotazioneS1+(vel*PI/180);
    float grad_rot_1= map(rotazioneS1, 0, PI, 0, 180);
    String a1="A";
    int a2 = parseInt(grad_rot_1);
    String testoA = a1+a2;
    myPort.write((testoA)+'\n');
    print("Rotazione Servo 1 = "); print(rotazioneS1); print("    "); print(grad_rot_1); print("    "); println(testoA); //debug
  } 
  else if (key == 'a') {
    rotazioneS1=rotazioneS1-(vel*PI/180);
    float grad_rot_1= map(rotazioneS1, 0, PI, 0, 180);
    String a1="A";
    int a2 = parseInt(grad_rot_1);
    String testoA = a1+a2;
    myPort.write((testoA)+'\n');
    print("Rotazione Servo 1 = "); print(rotazioneS1); print("    "); print(grad_rot_1); print("    "); println(testoA); //debug
  }
  if (key == 'e') {
    rotazioneS2=rotazioneS2+(vel*PI/180);
    float grad_rot_2= map(rotazioneS2, 0, PI, 0, 180);
    String b1="B";
    int b2 = parseInt(grad_rot_2);
    String testoB = b1+b2;
    myPort.write((testoB)+'\n');
    print("Rotazione Servo 2 = "); print(rotazioneS2); print("    "); print(grad_rot_2); print("    "); println(testoB); //debug
  } 
  else if (key == 'd') {
    rotazioneS2=rotazioneS2-(vel*PI/180);
    float grad_rot_2= map(rotazioneS2, 0, PI, 0, 180);
    String b1="B";
    int b2 = parseInt(grad_rot_2);
    String testoB = b1+b2;
    myPort.write((testoB)+'\n');
    print("Rotazione Servo 2 = "); print(rotazioneS2); print("    "); print(grad_rot_2); print("    "); println(testoB); //debug
  }
  if (key == 'r') {
    rotazioneS3=rotazioneS3+(vel*PI/180);
    float grad_rot_3= map(rotazioneS3, 0, PI, 0, 180);
    String c1="C";
    int c2 = parseInt(grad_rot_3);
    String testoC = c1+c2;
    myPort.write((testoC)+'\n');
    print("Rotazione Servo 3 = "); print(rotazioneS3); print("    "); print(grad_rot_3); print("    "); println(testoC); //debug
  } 
  else if (key == 'f') {
    rotazioneS3=rotazioneS3-(vel*PI/180);
    float grad_rot_3= map(rotazioneS3, 0, PI, 0, 180);
    String c1="C";
    int c2 = parseInt(grad_rot_3);
    String testoC = c1+c2;
    myPort.write((testoC)+'\n');
    print("Rotazione Servo 3 = "); print(rotazioneS3); print("    "); print(grad_rot_3); print("    "); println(testoC); //debug
  }
  if (key == 'g') {
    rotazioneS4=rotazioneS4+(vel*PI/180);
    float grad_rot_4= map(rotazioneS4, 0, PI, 0, 180);
    String d1="D";
    int d2 = parseInt(grad_rot_4);
    String testoD = d1+d2;
    myPort.write((testoD)+'\n');
    print("Rotazione Servo 4 = "); print(rotazioneS4); print("    "); print(grad_rot_4); print("    "); println(testoD); //debug
  } 
  else if (key == 't') {
    rotazioneS4=rotazioneS4-(vel*PI/180);
    float grad_rot_4= map(rotazioneS4, 0, PI, 0, 180);
    String d1="D";
    int d2 = parseInt(grad_rot_4);
    String testoD = d1+d2;
    myPort.write((testoD)+'\n');
    print("Rotazione Servo 4 = "); print(rotazioneS4); print("    "); print(grad_rot_4); print("    "); println(testoD); //debug
  }
  if (key == 'h') {
    rotazioneS5=rotazioneS5+(vel*PI/180);
    float grad_rot_5= map(rotazioneS5, 0, PI, 0, 180);
    String e1="E";
    int e2 = parseInt(grad_rot_5);
    String testoE = e1+e2;
    myPort.write((testoE)+'\n');
    print("Rotazione Servo 5 = "); print(rotazioneS5); print("    "); print(grad_rot_5); print("    "); println(testoE); //debug
  } 
  else if (key == 'y') {
    rotazioneS5=rotazioneS5-(vel*PI/180);
    float grad_rot_5= map(rotazioneS5, 0, PI, 0, 180);
    String e1="E";
    int e2 = parseInt(grad_rot_5);
    String testoE = e1+e2;
    myPort.write((testoE)+'\n');
    print("Rotazione Servo 5 = "); print(rotazioneS5); print("    "); print(grad_rot_5); print("    "); println(testoE); //debug
  }
  if (key == 'u') {
    rotazioneS6=rotazioneS6+(vel*PI/180);
    float grad_rot_6= map(rotazioneS6, 0, PI, 0, 180);
    String f1="F";
    int f2 = parseInt(grad_rot_6);
    String testoF = f1+f2;
    myPort.write((testoF)+'\n');
    print("Rotazione Servo 6 = "); print(rotazioneS6); print("    "); print(grad_rot_6); print("    "); println(testoF); //debug
  } 
  else if (key == 'j') {
    rotazioneS6=rotazioneS6-(vel*PI/180);
    float grad_rot_6= map(rotazioneS6, 0, PI, 0, 180);
    String f1="F";
    int f2 = parseInt(grad_rot_6);
    String testoF = f1+f2;
    myPort.write((testoF)+'\n');
    print("Rotazione Servo 6 = "); print(rotazioneS6); print("    "); print(grad_rot_6); print("    "); println(testoF); //debug
  }
  if (key == 'k') {
    rotazioneS7=rotazioneS7+(vel*PI/180);
    float grad_rot_7= map(rotazioneS7, 0, PI, 0, 180);
    String g1="G";
    int g2 = parseInt(grad_rot_7);
    String testoG = g1+g2;
    myPort.write((testoG)+'\n');
    print("Rotazione Servo 7 = "); print(rotazioneS7); print("    "); print(grad_rot_7); print("    "); println(testoG); //debug
  } 
  else if (key == 'l') {
    rotazioneS7=rotazioneS7-(vel*PI/180);
    float grad_rot_7= map(rotazioneS7, 0, PI, 0, 180);
    String g1="G";
    int g2 = parseInt(grad_rot_7);
    String testoG = g1+g2;
    myPort.write((testoG)+'\n');
    print("Rotazione Servo 7 = "); print(rotazioneS7); print("    "); print(grad_rot_7); print("    "); println(testoG); //debug
  }
}

Analisi degli sketch del Progetto 09 - Controllo e Comando Braccio Robot via BT

Gli sketch non presentano particolari difficoltà meritevoli di note, solo quello per Processing mi ha richiesto tanta pazienza nella costruzione della rappresentazione grafica 3D del braccio ed in particolare nell’uso del “  translate( x,y,z );   “ cioè dell’istruzione che consente lo spostamento dei riferimenti degli assi cartesiani utili poi alla realizzazione delle figure complesse.

Controllare un Braccio Robot con Controller PCA9685 e Arduino UNO

Un metodo alternativo per il controllo dei servo motori del Braccio Robot è quello che utilizza una scheda specifica realizzata dalla NXP Semiconductors il cui nome commerciale è PCA9685, quindi torniamo al controllo via cavo, o anche detto cablato. Questa scheda è nata per pilotare LED con segnali PWM, ma può essere impiegata tranquillamente anche per i servo.
Sui principali marketplace la scheda PCA9685 si trova già montata ed è simile a questa in foto:

E’ possibile formare “catene” di schede di controllo collegate in serie per incrementare il numero di PIN PWM disponibili, tutti gestiti tramite l’I2C, per fare questo occorre modificare l’indirizzo della scheda e per modificarlo occorre fare delle piccole saldature nella parte alta a destra dove c’è scritto “I2C Address”, in particolare:

(negli schemi sopra alcuni esempi di modifica dell’indirizzo I2C)

Descrizione della Scheda Controllo Servomotori PCA9865

La singola scheda PCA9685 è in grado di comandare, in maniera indipendente, fino a 16 canali output ed il tipo di segnale è PWM, con una risoluzione di 12 bit (ovvero 4096 livelli, contro i 256 di Arduino) ed è applicabile, come abbiamo detto, sia per comandare i servomotori di un robot che LED.

Il protocollo di comunicazione è l’ I2C ed è utilizzabile, oltre che con Arduino, anche con altri microcontrollori che dispongono di tale interfaccia.

Le principali caratteristiche sono:

  • Alimentazione da 3 V a 5.5 V
  • bus I2C (indirizzo di default 0x40 modificabile con appositi jumper)
  • 16 canali di uscita a 12 bit (4096 valori)
  • frequenza del PWM programmabile da 24Hz fino a 1526 Hz
  • ogni canale d’uscita eroga max 25mA a 5V
  • duty cycle 0% – 100%
  • Tutti gli output lavorano alla stessa frequenza impostata.

Analizzziamo i singoli PIN.

PIN di alimentazione:

GND – massa, o terra e sono tra loro tutti collegati.

VCC – PIN di alimentazione direttamente connesso al PCA9685 (l’alimentazione può andare da 3 a 5 VCC) .

V+ – questi pin sono tutti in comune. V+ è opzionale e serve solo ad alimentare gli eventuali servomotori collegati (solitamente si utilizza una tensionedi di 5/6 VCC, ma si può arrivare fino a 12 VCC).

PIN di controllo:

SCL – I2C clock; si può usare con la logica a 3V o 5V ed ha già una resistenza di pullup da 10k collegata a VCC.

SDA – I2C data; ed ha le stesse caratteristiche del pin SCL.

OE – Output Enable; si può utilizzare per disabilitare tutte le uscite. Se messo alto le disabilita, se messo basso le abilita. Normalmente è messo basso con una resistenza. Questo pin è opzionale e si può anche non utilizzare.

Porte di Output:

Sono disponibili 16 porte di uscita. Ogni porta ha 3 PIN: V +, GND e l’uscita PWM. Ogni PWM funziona in modo completamente indipendente, ma devono avere tutti la stessa frequenza. Quindi, ad esempio, dato che per i LED solitamente si utilizza una frequenza di 1.0 KHz, ma per i servomotori di solito se ne usa una a 60 Hz, non si possono utilizzare alcune porte di uscita per i LED a 1,0 KHz ed altre a 60 Hz per i servomotori.

La corrente massima per PIN PWM erogabile è di 25 mA più che sufficiente per il segnale al servomotore. In serie alle uscite sono collegate già delle resistenze da 220 ohm se si desidera utilizzare la scheda per controllare dei LED.

Per Arduino esistono anche delle Shield diverse, sempre con il PCA9685, di seguito due esempi:

Vediamo ora come connettere la scheda PCA9685 ad Arduino. La connessione è molto semplice, vediamo il PIN out di Arduino (figura in basso)

e notiamo che i PIN D18 e D19 sono rispettivamente l’SDA e l’SCL per le comunicazioni I2C, alcuni usano collegare l’SDA e l’SCL rispettivamente sui PIN analogici ed in particolare sull’A4 e sull’A5, in quanto repliche dei PIN D18 e D19, ma io sono tradizionalista, per cui li collegherò ai classici PIN D18 e D10.

Per quanto riguarda invece il GND e il Vcc della scheda essi andranno collegati rispettivamente al GND di Arduino ed ai 5V dello stesso Arduino.

Per collegare a “catena” o anche in detto in “serie” più schede PCA9685, occorre riportare i collegamenti terminali come mostrato in figura in basso. L’alimentazione unica dei servo dovrà essere esterna (la figura mostra solo due schede, ma ovviamente va replicata nel caso necessitiamo connettere più schede):

Progetto 10: Uso della Scheda PCA9685 nel controllo dei Servomotori

Questo primo progetto, con l’uso della scheda PCA9685, lo dedichiamo a conoscere la scheda ed a fare un po’ di pratica con la parte coding, infatti l’uso della scheda richiede la libreria “Adafruit PWM Servo Drive librery” che potete istallare attraverso la “Gestione librerie” e una volta istallata la libreria, potete caricare lo sketch di esempio dal percorso:

“File” –> “Esempi” –> “Adafruit PWM Servo Driver Library” –> “servo”

analizzando il file di esempio vi accorgerete che la scheda è possibile gestirla in ogni sua componente, ovviamente noi ci fermeremo ad un livello più alto, lasciando ad ognuno di voi la curiosità di sperimentare ed approfondire l’argomento. Il progetto comunque ci metterà in condizioni di conoscere a sufficienza la parte coding per utilizzare al meglio la PCA9685.

Per questo progetto ci occorre:

Lo schema da realizzare è il seguente:

Nel mio caso ho solo due servo SG90, per cui ho collegato i due servo sulle porte 0 e 1, se ne avete più di due collegateli in ordine numerico.

Lo sketch da caricare su Arduino è il seguente:

Analisi dello sketch del Progetto 10 – Test della scheda PCA9685

Ogni riga dello sketch è commentata, per cui non troverete difficoltà nel comprendere cosa fanno le singole istruzioni, desidero però solo evidenziarvi due cose:

  • I parametri SERVOMIN e SERVOMAX  dipendono dai servo utilizzati per il vostro progetto, personalmente ho eseguito il progetto con degli SG90 di origine cinese ed i valori utilizzati nello sketch hanno funzionato, magari nel vostro caso dovrete modificarli sia nel valore minimo che massimo per far compiere al servo la corretta rotazione prevista, ricordatevi che sono valori che vanno da 0 a 4095.
  • Alla variabile numeroservo bisogna assegnare il numero totale di servo che sono stati collegati alla scheda PCA9685, questo per far si che tutte le porte interessate siano attivate ciclicamente.

Progetto 11: Braccio Robot controllato da scheda PCA9685

Dopo aver fatto la conoscenza della scheda PCA0685 con il progetto precedente, passiamo al controllo del nostro Braccio Robot. Per questo progetto utilizzeremo un’interfaccia grafica già utilizzata in precedenza, ovviamente modificandola opportunamente. L’interfaccia grafica sarà con Processing, un ambiente di sviluppo molto simile all’IDE di Arduino, anzi possiamo dire che sono la stessa famiglia visto che l’IDE di Arduino nasce dall’IDE di Processing.

Vi accorgerete della semplicità d’uso della PCA9685 appena completerete il montaggio dei circuiti e visionando lo sketch per Arduino, non si può dire la stessa cosa per lo sketch su Processing per l’interfaccia grafica 3D che invece mi ha richiesto diverse ore di lavoro anche per sistemare alcuni bug presenti nella versione precedente, ora tutti sistemati.

Per questo progetto ci occorre:

Lo schema di montaggio è identico a quello del progetto precedente:

La batteria a 5V (che utilizza la stessa grafica di una batteria a 9V) sta ad indicare che la scheda richiede un’alimentazione esterna da 5V. Infatti come più volte ricordato, anche nelle Lezioni precedenti, Arduino non è in grado di alimentare tutti i servomotori utilizzati dal Braccio Robot per cui è necessaria un’alimentazione esterna.

Lo sketch da caricare su Arduino è il seguente:

Lo sketch da caricare su Processing è invece:

/*
BeMaker.org - BRACCIO ROBOTICO
Progetto 11 – ROBOT ARM CONTROLLED BY PCA9685 AND ARDUINO
*/
import processing.serial.*;  //Importo la libreria perle comunicazioni seriali
Serial myPort;               //Definisco l'oggetto myPort
String Stato = "";                //Definisce la variabile Stato come stringa che darà indiazioni su Arduino
float vel=0.5;                   //Fattore moltiplicativo angoli di rotazione
float rotazioneS1 = 0.0;  //Definisco la variabile di rotazione di S1
float rotazioneS2 = 0.0;  //Definisco la variabile di rotazione di S2
float rotazioneS3 = 0.0;  //Definisco la variabile di rotazione di S3
float rotazioneS4 = 0.0;  //Definisco la variabile di rotazione di S4
float rotazioneS5 = 0.0;  //Definisco la variabile di rotazione di S5
float rotazioneS6 = 0.0;  //Definisco la variabile di rotazione di S6
float rotazioneS7 = 0.0;  //Definisco la variabile di rotazione di S7
int grad_rot_1 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_2 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_3 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_4 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_5 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_6 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
int grad_rot_7 = 0;     //Definisco la variabile intera che rappresenta la corrispondente rotazione in gradi
String testoA = "";      //Defnisco una variabile stringa
String testoB = "";      //Defnisco una variabile stringa
String testoC = "";      //Defnisco una variabile stringa
String testoD = "";      //Defnisco una variabile stringa
String testoE = "";      //Defnisco una variabile stringa
String testoF = "";      //Defnisco una variabile stringa
String testoG = "";      //Defnisco una variabile stringa
void setup() {
  String portName = "COM8";  //Associo la porta COM effettivamete utilizzata per la comunicazione - vedere Lezione 4 del Corso per come fare
  myPort = new Serial(this, portName, 9600);  //Creazione e Settaggio della porta
  myPort.bufferUntil('\n');                // Definisce il carattere terminale della stringa da leggere
  size( 1600, 900, P3D ); //Creao una finestra 3D x=1500, y=800 z= indefinito
  ambientLight(0,0,255);  //Aggiungo una luce ambientale bianca diffusa
  pointLight(255, 0, 0, width/2, height/2, 400);  //Aggiungo un proiettore di luce biancaa metà lunghezza della finestra per dare le ombre per la profondità
  noStroke();          //Non definisco i contorni dei solidi che andrò a disegnare
  rotazioneS1=PI/2;    //Servo S1 BASE a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180    
  rotazioneS2=PI/2;    //Servo S2 Braccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS3=PI/2;    //Servo S3 AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS4=PI/2;    //Servo S4 Rotazione AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS5=PI/2;    //Servo S5 Rotazione Polso a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS6=PI/2;    //Servo S6 Rotazione Pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  rotazioneS7=PI/2;    //Servo S7 presa pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  myPort.write(("Pp")+'\n');  //Viene inviata la stringa 'Pp' alla porta BT per sincerarci che partiamo da una condizione nota
  delay(100);
}
void draw() {
  background(0);
  lights();
  if (myPort.available() > 0) { 
    Stato = myPort.readStringUntil('\n'); //Legge la stringa proveniente da Arduino e la mette nella variabile stato
    if (Stato != null) {  //debug
      text(Stato, 400, 880);   //Posiziona testo Stato
      print(Stato);
    }  
  }
  DisegnaLegenda();
  
  //CONTROLLA QUANDO VIENE PREMUTO IL TASTO SINISTRO DEL MOUSE SU UN DETERMINATO RETTANGOLO
  //1090,70,180,50 - Xgra: rad = 180 : pi
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>70 && mouseY<70+50){
    IlluminaTasto(1090,70,180,50);
    rotazioneS1 = RotazioneGiuntoPos(rotazioneS1);
    grad_rot_1 = parseInt(rotazioneS1 * 180 / PI);
    String a1="A";
    int a2 = parseInt(grad_rot_1);
    testoA = a1+a2;    
    myPort.write((testoA)+'\n');
    print("Rotazione Servo 1 = "); print(rotazioneS1); print("    "); print(grad_rot_1); print("    "); println(testoA); //debug
    delay(10);
  }
  //1320,70,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>70 && mouseY<70+50){
    IlluminaTasto(1320,70,180,50);
    rotazioneS1 = RotazioneGiuntoNeg(rotazioneS1);
    grad_rot_1 = parseInt(rotazioneS1 * 180 / PI);
    String a1="A";
    int a2 = parseInt(grad_rot_1);
    String testoA = a1+a2;
    myPort.write((testoA)+'\n');
    print("Rotazione Servo 1 = "); print(rotazioneS1); print("    "); print(grad_rot_1); print("    "); println(testoA); //debug
    delay(10);
  }
  //1090,170,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>170 && mouseY<170+50){
    IlluminaTasto(1090,170,180,50);
    rotazioneS2 = RotazioneGiuntoPos(rotazioneS2);
    grad_rot_2 = parseInt(rotazioneS2 * 180 / PI);
    String b1="B";
    int b2 = parseInt(grad_rot_2);
    String testoB = b1+b2;
    myPort.write((testoB)+'\n');
    print("Rotazione Servo 2 = "); print(rotazioneS2); print("    "); print(grad_rot_2); print("    "); println(testoB); //debug
    delay(10);
  }
   //1320,170,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>170 && mouseY<170+50){
    IlluminaTasto(1320,170,180,50);
    rotazioneS2 = RotazioneGiuntoNeg(rotazioneS2);
    grad_rot_2 = parseInt(rotazioneS2 * 180 / PI);
    String b1="B";
    int b2 = parseInt(grad_rot_2);    stroke(255,0,0);                   //Il contorno del tasto si colora di Rosso
    String testoB = b1+b2;
    myPort.write((testoB)+'\n');
    print("Rotazione Servo 2 = "); print(rotazioneS2); print("    "); print(grad_rot_2); print("    "); println(testoB); //debug
    delay(10);
  }
  //1090,270,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>270 && mouseY<270+50){
    IlluminaTasto(1090,270,180,50);
    rotazioneS3 = RotazioneGiuntoPos(rotazioneS3);
    grad_rot_3 = parseInt(rotazioneS3 * 180 / PI);
    String c1="C";
    int c2 = parseInt(grad_rot_3);
    String testoC = c1+c2;
    myPort.write((testoC)+'\n');
    print("Rotazione Servo 3 = "); print(rotazioneS3); print("    "); print(grad_rot_3); print("    "); println(testoC); //debug
    delay(10);
  }
   //1320,270,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>270 && mouseY<270+50){
    IlluminaTasto(1320,270,180,50);
    rotazioneS3 = RotazioneGiuntoNeg(rotazioneS3);
    grad_rot_3 = parseInt(rotazioneS3 * 180 / PI);
    String c1="C";
    int c2 = parseInt(grad_rot_3);
    String testoC = c1+c2;
    myPort.write((testoC)+'\n');
    print("Rotazione Servo 3 = "); print(rotazioneS3); print("    "); print(grad_rot_3); print("    "); println(testoC); //debug
    delay(10);
  }
  //1090,370,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>370 && mouseY<370+50){
    IlluminaTasto(1090,370,180,50);
    rotazioneS4 = RotazioneGiuntoPos(rotazioneS4);
    grad_rot_4 = parseInt(rotazioneS4 * 180 / PI);
    String d1="D";
    int d2 = parseInt(grad_rot_4);
    String testoD = d1+d2;
    myPort.write((testoD)+'\n');
    print("Rotazione Servo 4 = "); print(rotazioneS4); print("    "); print(grad_rot_4); print("    "); println(testoD); //debug
    delay(10);
  }
   //1320,370,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>370 && mouseY<370+50){
    IlluminaTasto(1320,370,180,50);
    rotazioneS4 = RotazioneGiuntoNeg(rotazioneS4);
    grad_rot_4 = parseInt(rotazioneS4 * 180 / PI);
    String d1="D";
    int d2 = parseInt(grad_rot_4);
    String testoD = d1+d2;
    myPort.write((testoD)+'\n');
    print("Rotazione Servo 4 = "); print(rotazioneS4); print("    "); print(grad_rot_4); print("    "); println(testoD); //debug
    delay(10);
  }
  //1090,470,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>470 && mouseY<470+50){
    IlluminaTasto(1090,470,180,50);
    rotazioneS5 = RotazioneGiuntoPos(rotazioneS5);
    grad_rot_5 = parseInt(rotazioneS5 * 180 / PI);
    String e1="E";
    int e2 = parseInt(grad_rot_5);
    String testoE = e1+e2;
    myPort.write((testoE)+'\n');
    print("Rotazione Servo 5 = "); print(rotazioneS5); print("    "); print(grad_rot_5); print("    "); println(testoE); //debug
    delay(10);
  }
   //1320,470,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>470 && mouseY<470+50){
    IlluminaTasto(1320,470,180,50);
    rotazioneS5 = RotazioneGiuntoNeg(rotazioneS5);
    grad_rot_5 = parseInt(rotazioneS5 * 180 / PI);
    String e1="E";
    int e2 = parseInt(grad_rot_5);
    String testoE = e1+e2;
    myPort.write((testoE)+'\n');
    print("Rotazione Servo 5 = "); print(rotazioneS5); print("    "); print(grad_rot_5); print("    "); println(testoE); //debug
    delay(10);
  } 
  //1090,570,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>570 && mouseY<570+50){
    IlluminaTasto(1090,570,180,50);
    rotazioneS6 = RotazioneGiuntoPos(rotazioneS6);
    grad_rot_6 = parseInt(rotazioneS6 * 180 / PI);
    String f1="F";
    int f2 = parseInt(grad_rot_6);
    String testoF = f1+f2;
    myPort.write((testoF)+'\n');
    print("Rotazione Servo 6 = "); print(rotazioneS6); print("    "); print(grad_rot_6); print("    "); println(testoF); //debug
    delay(10);
  }
   //1320,570,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>570 && mouseY<570+50){
    IlluminaTasto(1320,570,180,50);
    rotazioneS6 = RotazioneGiuntoNeg(rotazioneS6);
    grad_rot_6 = parseInt(rotazioneS6 * 180 / PI);
    String f1="F";
    int f2 = parseInt(grad_rot_6);
    String testoF = f1+f2;
    myPort.write((testoF)+'\n');
    print("Rotazione Servo 6 = "); print(rotazioneS6); print("    "); print(grad_rot_6); print("    "); println(testoF); //debug
    delay(10);
  } 
  //1090,670,180,50
  if(mousePressed && mouseX>1090 && mouseX<1090+180 && mouseY>670 && mouseY<670+50){
    IlluminaTasto(1090,670,180,50);
    rotazioneS7 = RotazioneGiuntoPos(rotazioneS7);
    grad_rot_7 = parseInt(rotazioneS7 * 180 / PI);
    String g1="G";
    int g2 = parseInt(grad_rot_7);
    String testoG = g1+g2;
    myPort.write((testoG)+'\n');
    print("Rotazione Servo 7 = "); print(rotazioneS7); print("    "); print(grad_rot_7); print("    "); println(testoG); //debug
    delay(10);
  }
   //1320,670,180,50
  if(mousePressed && mouseX>1320 && mouseX<1320+180 && mouseY>670 && mouseY<670+50){
    IlluminaTasto(1320,670,180,50);
    rotazioneS7 = RotazioneGiuntoNeg(rotazioneS7);
    grad_rot_7 = parseInt(rotazioneS7 * 180 / PI);
    String g1="G";
    int g2 = parseInt(grad_rot_7);
    String testoG = g1+g2;
    myPort.write((testoG)+'\n');
    print("Rotazione Servo 7 = "); print(rotazioneS7); print("    "); print(grad_rot_7); print("    "); println(testoG); //debug
    delay(10);
  } 
  //1205,770,180,50 - TASTO RESET
  if(mousePressed && mouseX>1205 && mouseX<1205+180 && mouseY>770 && mouseY<770+50){
    IlluminaTasto(1205,770,180,50);
    TastoReset();
    rotazioneS1=PI/2;    //Servo S1 BASE a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180    
    rotazioneS2=PI/2;    //Servo S2 Braccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
    rotazioneS3=PI/2;    //Servo S3 AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
    rotazioneS4=PI/2;    //Servo S4 Rotazione AvanBraccio a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
    rotazioneS5=PI/2;    //Servo S5 Rotazione Polso a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
    rotazioneS6=PI/2;    //Servo S6 Rotazione Pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
    rotazioneS7=PI/2;    //Servo S7 presa pinza a riposo si trova a 90° per cui ruota da 0 a 90 e da 90 a 180
  }  
  DisegnaBraccio();
}
void TastoReset() {    
    String testoA = "A90";
    myPort.write((testoA)+'\n');
    print("Rotazione Servo 1 = "); print(PI/2); print("    "); print(90); print("    "); println(testoA); //debug
    delay(200);
    
    String testoB = "B90";
    myPort.write((testoB)+'\n');
    print("Rotazione Servo 2 = "); print(PI/2); print("    "); print(90); print("    "); println(testoB); //debug
    delay(200);
    String testoC = "C90";
    myPort.write((testoC)+'\n');
    print("Rotazione Servo 3 = "); print(PI/2); print("    "); print(90); print("    "); println(testoC); //debug
    delay(200);
    String testoD = "D90";
    myPort.write((testoD)+'\n');
    print("Rotazione Servo 4 = "); print(PI/2); print("    "); print(90); print("    "); println(testoD); //debug
    delay(200);
    String testoE = "E90";
    myPort.write((testoE)+'\n');
    print("Rotazione Servo 5 = "); print(PI/2); print("    "); print(90); print("    "); println(testoE); //debug
    delay(200);
    String testoF = "F90";
    myPort.write((testoF)+'\n');
    print("Rotazione Servo 6 = "); print(PI/2); print("    "); print(90); print("    "); println(testoF); //debug
    delay(200);
    String testoG = "G90";
    myPort.write((testoG)+'\n');
    print("Rotazione Servo 7 = "); print(PI/2); print("    "); print(90); print("    "); println(testoG); //debug
    delay(200);
}
  
void DisegnaLegenda() {
  //Posiziono il testo per sito
  fill(255);    //Seleziono il colore Bianco     
  textSize(20);                        //Dimensione testo
  text("WWW.BEMAKER.ORG", 50, 880);   //Posiziona testo "WWW.BEMAKER.ORG"
 // text(Stato, 500, 880);               //Posiziona testo Stato
  //Posiziono il testo per la legenda dei click di mouse di comando
  textSize(30); //Dimensione testo
  fill(255);    //Seleziono il colore Bianco     
  text("CLICK MOUSE", 1200, 50);   //Posiziona testo "LEGENDA"
  //prima riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,70,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("a = S1 <-", 1100, 100);   //Posiziona testo "S1 <-"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,70,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("s = S1 ->", 1330, 100);   //Posiziona testo "S1 <-"
  //seconda riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,170,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("e = S2 v", 1100, 200);   //Posiziona testo "e = S2 ^"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,170,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("d = S2 ^", 1330, 200);   //Posiziona testo "d = S2 v"
  //terza riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,270,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("r = S3 ^", 1100, 300);   //Posiziona testo "e = S3 ^"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,270,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("f = S3 v", 1330, 300);   //Posiziona testo "d = S3 v"
  //quarta riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,370,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("t = S4 <-", 1100, 400);   //Posiziona testo "e = S4 ^"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,370,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("g = S4 ->", 1330, 400);   //Posiziona testo "d = S4 v"
  //quinta riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,470,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("y = S5 <-", 1100, 500);   //Posiziona testo "e = S5 ^"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,470,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("h = S5 ->", 1330, 500);   //Posiziona testo "d = S5 v"
  //sesta riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,570,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("u = S6 <-", 1100, 600);   //Posiziona testo "e = S6 <-"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,570,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("j = S6 ->", 1330, 600);   //Posiziona testo "d = S6 ->"
  //settima riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1090,670,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("k = S7 <->", 1100, 700);   //Posiziona testo "d = S7 <->"
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1320,670,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("l = S7 >-<", 1330, 700);   //Posiziona testo "d = S7 >-<"
  //ottava riga
  fill(0,123,184);    //Seleziono il colore Bianco     
  rect(1205,770,180,50);
  fill(255);    //Seleziono il colore Bianco     
  text("RESET", 1250, 800);   //Posiziona testo "d = S7 <->"
  fill(255);    //Seleziono il colore Bianco     
}
void DisegnaBraccio() {
  // Disegno il Braccio Robot in 3D
  //Disegno la base
  noStroke();
  translate(300,700,0);  //Sposto il riferimento del disegno verso destra (x) di 200, verso il basso (y) di 600 e z=0
  rotateY(rotazioneS1);  //Ruoto lungo l'asse y
  fill(0,127,255);       //Seleziono il colore blu
  box(60);               //Disegno alle nuove coordinate un cubo di lato 60
  //Disegno il giunto di rotazione del braccio
  translate( 0,-60,0 );   //Sposto il riferimento solo verso l'alto lungo la y
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( PI-rotazioneS2 ); //Ruoto lungo l'asse X
  sphere(30);             //Disegno una sfera
  //Disegno il braccio
  translate( 0,0,140 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,255,0);       //Seleziono il colore Giallo
  box( 60, 60, 220 );    //Disegno un parallelepipedo 60x60x220
  //Disegno il giunto di rotazione dell'avanbraccio
  translate( 0,0,140 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( rotazioneS3 ); //Ruoto lungo l'asse X
  rotateZ( rotazioneS4 ); //Ruoto lungo l'asse z
  sphere(30);             //Disegno una sfera
  //Disegno l'avanbraccio
  translate( 0,0,-110 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,255,0);       //Seleziono il colore Giallo
  box( 60, 60, 150 );    //Disegno un parallelepipedo 60x60x220
  //Disegno il giunto di rotazione del polso
  translate( 0,0,-110 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( rotazioneS5 ); //Ruoto lungo l'asse y
  rotateY( rotazioneS6 ); //Ruoto lungo l'asse z
  sphere(30);             //Disegno una sfera
  //Disegno la pinza inferiore
  translate( 0,-70,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  box( 20, 130, 20 );    //Disegno un parallelepipedo
  //Disegno il giunto di rotazione della pinza superiore
  translate( 0,35,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(255,0,0);          //Seleziono il colore Rosso
  rotateX( PI-rotazioneS7 ); //Ruoto lungo l'asse x
  sphere(10);             //Disegno una sfera
  //Disegno la pinza superiore
  translate( 0,50,0 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  rotateX( -PI/2 );       //Ruoto lungo l'asse X
  box( 20, 20, 80 );    //Disegno un parallelepipedo
  translate( 0,-40,40 );  //Sposto il riferimento solo verso l'alto lungo la z
  fill(70,70,70);          //Seleziono il colore Rosso
  rotateX( PI/2 );       //Ruoto lungo l'asse X
  box( 20, 20, 80 );    //Disegno un parallelepipedo  
}
void IlluminaTasto(int xa, int xb, int xc, int xd) {
    stroke(255,0,0);                   //Il contorno del tasto si colora di Rosso
    strokeWeight(10);                  //lo spessore del contorno diventa da 10
    noFill();                          //Nessun riempimento (trasparente)
    rect(xa,xb,xc,xd);        //rettangolo del tasto ON
}
float RotazioneGiuntoPos(float rg1) {
  rg1 = rg1+(vel*PI/180);           //calcolo la rotazione giunto in radianti, facendola incrementare di un grad per volta
  int rg2 = parseInt(rg1 * 180/PI);                 //converto i valori da rad in grad
  if (rg2<0){                        //limitare le rotazioni per valori <0
    rg2 = 0;                         //inserisco un valore zero anche quando con il mouse si vuole dare una rotazione negativa
    rg1 = 0.0;          
    } else if (rg2>180){             //limitare le rotazioni per valori >180°
      rg2 = 180;                     //inserisco 180 gradi anche quando con il muose si vuole dare una rotazione maggiore
      rg1 = PI;
    }
    return rg1;
}
float RotazioneGiuntoNeg(float rg1) {
  rg1 = rg1-(vel*PI/180);           //calcolo la rotazione giunto in radianti, facendola incrementare di un grad per volta
  int rg2 = parseInt(rg1 * 180/PI);                 //converto i valori da rad in grad
  if (rg2<0){                        //limitare le rotazioni per valori <0
    rg2 = 0;                         //inserisco un valore zero anche quando con il mouse si vuole dare una rotazione negativa
    rg1 = 0.0;          
    } else if (rg2>180){             //limitare le rotazioni per valori >180°
      rg2 = 180;                     //inserisco 180 gradi anche quando con il muose si vuole dare una rotazione maggiore
      rg1 = PI;
    }
    return rg1;
}

Il Video Progetto che segue mostra tutti i passaggi in dettaglio.

Analisi degli sketch del Progetto 11 – Braccio Robot controllato da scheda PCA9685

Analizziamo per primo lo sketch per Arduino, ogni riga dello sketch è commentata, per cui non troverete difficoltà nel comprendere cosa fanno le singole istruzioni. Lo sketch del Progetto 11, come è possibile osservare, è sostanzialmente identico allo sketch del progetto precedente, unica sensibile differenza è che nell’attuale, dovendo discriminare a quale servomotore arrivano i comandi, una volta catturata la stringa di comandi inviata da Processing, c’è lo “switch…case…” che indirizza il comando al servomotore corretto.

Lo sketch per Processing, per ovvie ragioni, è molto più complesso, ma solo per la costruzione grafica del braccio e per avere i movimenti correttamente rappresentati. Anzi, a tal proposito, mi sono accorto, sviluppando il Progetto 11, che lo sketch per Processing del Progetto 09, dove si rappresenta sempre un Braccio Robot 3D, presentava dei bug, in particolare non venivano rappresentati correttamente i movimenti dei servomotori del braccio (servo 2) e della pinza (servo 7). In questa versione dello sketch sono stati corretti tali bug.

Vediamo da un punto di vista di flusso logico come è stato strutturato lo sketch:

PS: poiché già so’ che qualcuno si lamenterà per non aver utilizzato la simbologia standard dei Diagrammi di Flusso logici per lo sviluppo software, l’attuale rappresentazione è per tenere contenute le dimensioni del diagramma e quindi renderlo più leggibile.

Dallo sketch per Processing, quindi osserviamo che abbiamo due tipologie di funzioni, una che segue le azioni in essa contenute una volta chiamata e l’altra che invece oltre ad eseguire le azioni, restituisce anche un valore che poi viene attribuito ad una variabile. Questa seconda tipologia di funzione in genere sono quelle di tipo matematico, dove si ha necessità di far eseguire calcoli più o meno complessi, magari richiamando anche altre funzioni, per poi resituire con “result” un valore. Nello sketch possiamo osservare che a questa tipologia di funzioni appartengono le due funzioni chiamate:

  • RotazioneGiuntoPos
  • RotazioneGiuntoNeg

Per queste due funzioni, chiamate con l’assegnazione anche di un parametro, si ha il calcolo dell’attuale rotazione richiesta ai servo.

Mentre per le funzioni:

  • DisegnaLegenda
  • IlluminaTasto
  • DisegnaBraccio
  • TastoReset

Queste una volta chiamate eseguono il loro compito e poi finisce lì, il tutto.

Con questo progetto termina la Lezione 5, ovviamente, come dico sempre, le mie proposte di progetto servono solo a solleticare la vostra curiosità e devono essere visti non come punti di arrivo, ma come punti di partenza, per cui se avete suggerimenti o se vi va di migliorare ciò che ho proposto, sentitevi liberi di farlo e se avete bisogno di aiuto contattatemi all’indirizzo info@bemaker.org.

Se hai trovato la lezione interessante fai una donazione mi aiuterai a realizzarne tante altre.