coBot robot collaborativo arduino

coBot robot collaborativo arduino è dedicato alla serie di articoli che hai letto come “robot arm 3d printed”

coBot robot collaborativo arduino

in cui passo-passo hai visto tutte le parti che lo compongono ed hai letto come è nato il progetto che nasconde nella parola “collaborativo” un doppio significato:

  • il progetto l’ho iniziato partendo dalla condivisione eseguita da un maker su thinghiverse del suo braccio robot a sua volta ripreso da un precedente progetto condiviso da un precedente maker, insomma una catena di collaborazioni come spesso accade nel mondo maker;
  • il secondo significato è nella capacità di questa tipologia di robot di collaborare con l’essere umano registrando una serie di movimenti, in modo molto intuitivo, e riproducendoli a comando.

Ho ampiamente parlato di questo progetto nel corso dell’ultimo Arduino Day 2020 in streaming, puoi riascoltarlo in questo video:

coBot robot collaborativo arduino

Oggi leggerai come realizzare lo sketch con cui registrare i movimenti del robot e farglieli riprodurre.

Ho scelto di non utilizzare il codice dei due precedenti progetti in quanto ho voluto provare io stesso la realizzazione dello sketch.

Mi sono voluto confrontare con questa tecnologia e mettere alla prova le mie capacità di programmazione.

Il risultato è quello che puoi vedere in questo video:

in cui ho eseguito una semplice demo dei movimenti che il robot nella fase 1 registra e nella fase 2 esegue in autonomia senza il mio intervento.

Articoli precedenti dedicati al robot

Se non ricordi quali articoli leggere per realizzare il tuo coBot robot collaborativo arduino ti riporti i link che puoi seguire:

in cui puoi seguire tutto il percorso dalla nascita del progetto fino a questo ultimo articolo dedicato al progetto.

Lo sketch del coBot robot collaborativo arduino

Il coBot robot collaborativo arduino si muove grazie al seguente sketch arduino:

#include <Servo.h>

#define potBase     A5
#define potGrip     A4
#define potShoulder A3
#define potWrist    A2
#define buttRec     A1
#define buttPlay    A0

#define ledGreen 10
#define ledRed   11

Servo servoBase;
Servo servoShoulder;
Servo servoWrist;
Servo servoGrip;

int coBase     = 0; 
int coGrip     = 0; // 25 - 80 
int coShoulder = 0; 
int coWrist    = 0; 
int coRec      = 0; 
int coPlay     = 0; 

boolean modeRec = true;
boolean modePlay = false;

int indexRec = 0;
int recordedPosBase[50]     = {0};
int recordedPosShoulder[50] = {0};
int recordedPosWrist[50]    = {0};
int recordedPosGrip[50]     = {0};

void setup() {
  Serial.begin( 115200 );
  
  pinMode( potBase ,   INPUT );
  pinMode( potGrip,    INPUT );
  pinMode( potShoulder,INPUT );
  pinMode( potWrist,   INPUT );
  pinMode( buttRec,    INPUT );
  pinMode( buttPlay,   INPUT );

  pinMode( ledGreen,OUTPUT );
  pinMode( ledRed,  OUTPUT );

  servoBase.attach(9);
  servoShoulder.attach(6);
  servoWrist.attach(5);
  servoGrip.attach(3);

  coBase     = 84;
  coShoulder = 78;
  coWrist    = 56;
  coGrip     = 25;

  moveServos(10);
  digitalWrite( ledGreen,1 );   
  digitalWrite( ledRed,1 );   
  delay( 1000 );
  digitalWrite( ledGreen,0 );
  digitalWrite( ledRed,0 );
}

void loop() {
  mapping();
  /*  
  Serial.print("Loop index: "); Serial.print( indexRec );
  Serial.print("\tRec: "); Serial.print( coRec );
  Serial.print("\tPlay: "); Serial.println( coPlay );
  */
  //writeSerial();
  moveServos(0);
  
  if ( coRec == 0 && coPlay == 1 ) { delay(300); modeRec = true; modePlay = false; }
  if ( coRec == 1 && coPlay == 0 ) { delay(300); modeRec = false; modePlay = true; }
  if ( coRec == 0 && coPlay == 0 ) { delay(300); modeRec = false; modePlay = false; deleteRecorded(); }

  if ( modeRec  ) { recordPosition(); modeRec = false; }
  if ( modePlay ) { playMovements(); modePlay = false; }
  delay( 50 );
}

void mapping() {
  coBase     = map(analogRead( potBase ),     0, 1023, 180, 0);
  coShoulder = map(analogRead( potShoulder ), 0, 1023, 0, 180);
  coWrist    = map(analogRead( potWrist ),    0, 1023, 0, 180);
  coGrip     = map(analogRead( potGrip ),     0, 1023, 0, 180);

  if (coGrip <=25) coGrip = 25;
  if (coGrip >=80) coGrip = 80;
  
  if (analogRead( buttRec )  == 0 )  coRec = 0; else coRec  = 1;
  if (analogRead( buttPlay ) == 0 ) coPlay = 0; else coPlay = 1;
}

void moveServos( byte pause ) {
  goPos(servoBase,     coBase,     pause); 
  goPos(servoShoulder, coShoulder, pause); 
  goPos(servoWrist,    coWrist,    pause);
  goPos(servoGrip,     coGrip,     pause); 
}

void goPos(Servo instance, byte moveToAngle, byte pause) {
   byte currPos = instance.read();
   
   if (currPos >= moveToAngle) {    
    for ( byte i = currPos; i > moveToAngle; i--) {
      instance.write(i);
      delay(pause);
    }
   }
   
   if (currPos <= moveToAngle) {
    for ( byte i = currPos; i < moveToAngle; i++) {
      instance.write(i);
      delay(pause);
    }
   }
}

void recordPosition() {
  Serial.println( "rec mode" );
  Serial.print("Indice Recording: ");   Serial.println( indexRec );
  Serial.print("Base Recording: ");     Serial.println( coBase );
  Serial.print("Shoulder Recording: "); Serial.println( coShoulder );
  Serial.print("Wrist Recording: ");    Serial.println( coWrist );
  Serial.print("Grip Recording: ");     Serial.println( coGrip );

  recordedPosBase[indexRec]     = coBase;
  recordedPosShoulder[indexRec] = coShoulder;
  recordedPosWrist[indexRec]    = coWrist;
  recordedPosGrip[indexRec]     = coGrip; 

  confirmRecord();
  Serial.print("Recorded:"); 
  Serial.print("\tindex: "); Serial.print( indexRec );
  Serial.print("\tbase: ");  Serial.print( recordedPosBase[indexRec] );
  Serial.print("\tshou: ");  Serial.print( recordedPosShoulder[indexRec] );
  Serial.print("\twris: ");  Serial.print( recordedPosWrist[indexRec] );
  Serial.print("\tgrip: ");  Serial.print( recordedPosGrip[indexRec] );
  Serial.print("\n");

  indexRec++;
  delay( 100 );
}

void playMovements() {
  Serial.println( "play mode" );

  playRecord();
  
  for ( int i=0; i<= indexRec; i++ ) {
    if ( recordedPosBase[i] != 0 && recordedPosShoulder[i] != 0 && recordedPosWrist[i] != 0 && recordedPosGrip[i] != 0 ) {
      coBase     = recordedPosBase[i];
      coShoulder = recordedPosShoulder[i];
      coWrist    = recordedPosWrist[i];
      coGrip     = recordedPosGrip[i];

      Serial.print("\tIndex: "); Serial.print( i );
      Serial.print("\tBaseStart: "); Serial.print( servoBase.read() );
      Serial.print("\tBaseEnd: "); Serial.print( coBase );

      if ( coRec == 0 && coPlay == 0 ) { delay(300); modeRec = false; modePlay = false; deleteRecorded(); }
      
      writeSerial();
      moveServos(15);
    }
  }
}

void deleteRecorded() {
  Serial.println( "delete recorded positions" );
  deleteRecord();

  for ( int i=0; i<= indexRec; i++ ) {
    recordedPosBase[i]     = 0; 
    recordedPosShoulder[i] = 0; 
    recordedPosWrist[i]    = 0; 
    recordedPosGrip[i]     = 0; 
  }
  indexRec=0;
  deleteRecord();
  
  coBase     = 84;
  coShoulder = 78;
  coWrist    = 56;
  coGrip     = 25;

  moveServos(10);
}

void confirmRecord() {
  for (uint8_t i=0; i<4; i++) {
    digitalWrite( ledRed,1 ); delay( 100 );
    digitalWrite( ledRed,0 ); delay( 100 ); 
  }
}

void playRecord() {
  for (uint8_t i=0; i<4; i++) {
    digitalWrite( ledRed,0 ); digitalWrite( ledGreen,1 ); delay( 200 );
    digitalWrite( ledRed,1 ); digitalWrite( ledGreen,0 ); delay( 200 ); 
  }
  digitalWrite( ledRed,0 );
  digitalWrite( ledGreen,0 ); 
}

void deleteRecord() {
  for (uint8_t i=0; i<2; i++) {
    digitalWrite( ledRed,1 ); delay( 100 );
    digitalWrite( ledRed,0 ); delay( 100 ); 
  }
}

void writeSerial() {
  Serial.print("\tB: "); Serial.print( coBase ); 
  Serial.print("\tG: "); Serial.print( coGrip ); 
  Serial.print("\tS: "); Serial.print( coShoulder ); 
  Serial.print("\tW: "); Serial.print( coWrist ); 
  Serial.print("\tR: "); Serial.print( coRec ); 
  Serial.print("\tP: "); Serial.print( coPlay );
  Serial.println();

  delay(1000);
}

in cui vedrai che molte delle linee presenti è la stessa dei precedenti sketch.

linee 001-011: definiscolo l’utilizzo della Servo.h e dei pin che sono utilizzati per la lettura dei potenziometri;

linee 013-016: imposta i 4 oggetti che userai per controllare i servo;

le linee 018-023: imposti le variabili nelle quali saranno memorizzati i valori angolari da inviare ai servo e lo stato dei pulsanti “rec” e “play”;

linee 025-026: servono a definire due variabili di tipo boolean che sono utilizzate per mantenere la modalità di funzionamento “Play” o “rec”;

linea 028: imposti un a variabile che userai come puntatore o indice di posizione negli array posizionali che vedrai alle linee cuccessive;

linee 029-032: per ciascun servo imposta un array da 50 posizioni che userai per memorizzare la posizione di tutti i servo all’indice definito.

In pratica alla posizione di registrazione 0 avrai per ogni array un valore corrispondente al servo a cui è associato.

Saprai in questo modo che posizione hanno tutti i servo all’indica X, in cui X è la posizione che stai registrando o riproducendo.

Avrai capito come funziona il sistema, ogni volta che premerai il pulsante di registrazione saranno salvate le posizioni, in gradi, di ogni servo nel rispettivo array per l’indice corrente e successivamente incrementato l’indice.

Le linee 034-063: definiscono la funzione setup che non differisce molto dalle versioni precedenti in quanto serve ad inizializzare i servo ed associarli ai rispettivi pin a cui sono connessi i servo. 

Alla linea 057: richiami la funzione moveServos() passandole come parametro 10 che sarà usato come ritardo nel movimento dei servo tra un grado ed il successivo fino al raggiungimento della posizione desiderata per ciascuno;

linea 066: richiami la funzione mapping() nella loop(), la funzione mapping ha lo scopo di rimappare i valori letti dai potenziometri sui limiti dei servo che potrai impostare nella funzione stessa;

linea 073: richiami di nuovo la funzione moveServos() passandole il valore 0 come pausa nel movimento;

linee 075-077: definisci in quale modalità stai operando in funzione della pressione dei pulsanti Play e Rec. Nota che alla linea 077, se entrambi i pulsanti sono premuti contemporaneamente viene richiamata la funzione di cancellazione della registrazione la funzione: deleteRecorded();

linea 079: se sei in modalità record richiami la funzione che registra le posizioni dei servo e dei potenziometri ad ogni passo;

linea 080: in modalità play richiami la funzione playMovements con il preciso scopo di seguire la matrice registrata e riprodurla sui servo;

linee 084-095: definisci la funzione mapping() che, come hai letto, trasforma tutti i valori letti sui potenziometri in valori da trasferire ai servo. Valuta anche il valore della pressione dei pulsanti;

linee 097-102: per ciascun servo da controllare richiama la funzione goPos() per mandare il servo in quella posizione usando come tempo di pausa tra un grado e l’altro il valore del terzo parametro passato;

linea 104: definisci la funzione goPos() a cui devono essere passati i valori relativi all’istanza Servo da controllare, il valore, in gradi, a cui il servo deve arrivare e la pausa da utilizzare nella rotazione;

linea 105: usando il metodo read per l’istanza corrente legge la posizione del servo in modo da poter valutare da quale angolo partire;

linee 107-112: se la posizione corrente è superiore all’angolo a cui devi arrivare hai bisogno di un ciclo for che partendo dall’angolo corrente arrivi a quello di destinazione decrementando il valore in gradi; 

linee 114-119: se la posizione corrente è inferiore all’angolo di arrivo il ciclo sarà da incrementare positivamente;

linea 122: definisci la funzione recordPosition()  il cui scopo è quello di registrare i valori angolari delle posizioni dei servo per uno stesso indice.

linee 123-128: scrivi sul monitor zeriale delle informazioni relative alla modalità di registrazione in corso, l’indice ed i valori angolari dei 4 servomotori.

linee 130-133: per ciasscun array predefinito alle linee 029-032, alla posizione indexRec corrente memorizza il valore assunto dall’angolo del servo;

linea 135: richiama la funzione  confirmRecord() che ti restituisce la segnalazione visiva di aver registrato i valori nei rispettivi array;

linee 136-142: scrivi sul monitor seriale i valori impostati negli array;

linea 144: incrementa l’indice di registrazione per la prossima memorizzazione;

line 148: definisci la funzione playMovements() con lo scopo di riprodurre i movimenti leggendoli dall’array registrato nei passi precedenti;

linee 149-151: scrivi sul monitor seriale il testo “play mode” per indicare che sei nella funzione di riproduzione e richiama la funzione playRecord() che ti da un feedback luminoso sui due led connessi ( rosso e verde );

linea 153: definisci un ciclo da 0 all0’indice raggiunto in modo da scorrere tutte le posizioni registrate;

linea 154: esegui un controllo in modo da scartare tutti i valori dei singoli array in cui la posizione memorizzata è nulla;

linee 155-158: leggi per siacun record memorizzato all’indice corrente il valore memorizzato e imposta la rispettiva variabile di posizione del servo corrispondente;

linea 164: verifica che siano stati premuti entrambi i pulsanti, in tal caso sei nella condizione di fermo per emergenza e cancellazione dei record. E’ una precauzione in caso di errore nell’eseguire i passi che potrebbero danneggiare il robot, in questo caso, ed in funzionamenti con robot industriali potrebbero causare danni a cose o persone;

linee 166-167: scrivi sul monitor seriale i valori che stai per attuare, mediante la funzione writeSerial() e richiama la funzione  moveServos() per spostare i servo in posizione mantenendo un delay 15 tra un passo ed il successivo;

linea 172: definisci la funzione deleteRecorded() il cui scopo è pulire l’array delle posizioni registrate;

linea 174: richiama la funzione deleteRecord() che segnala l’avvio della fase di cancellazione mediante i led;

linee 176-182: con un ciclo for partendo dall’indice 0 alla file dei record memorizzati imposta 0 come angolo per ciascun array di posizione, in modo che anche accidentalmente se venisse premuto il pulsante play la riga 154 eviterebbe qualsiasi movimento;

linea 183: richiama, nuovamente, la funzione delteRecord() per segnalare la fine delle operazioni di cancellazione;

linee 193-198: definisci la funzione confirmRecord() che esegue 4 lampeggi del led rosso, è il segnale che il record è stato memorizzato e viene richiamata allalinea 135;

linee 200-207: similmente alla precedente questa funzione fa lampeggiare 4 volte il led verde e poi spegne entrambi i led;

linea 209: definisci la funzione deleteRecord() richiamata alle linee 174 e 183 per segnalare che le funzioni di cancellazione sono state eseguite;

linee 210-212: esefue il coclo for di 2 lampeggi per il led rosso;

linee 216-226: la funzione writeSerial() scrive sul monitor seriale i valori relativi alle posizioni dei servo e dei pulsanti ogni volta che viene invocata.

Lo sketch di questo progetto non è semplice e le sue oltre 200 righe di codice servono ad eseguire correttamente sia la fase di registrazione sia quella di riproduzione dei movimenti.

Nel video puoi vedere chiaramente sia le procedure di movimento e registrazione degli stessi sia le sequenze di lampeggio dei led nelle differenti fasi.

Prima di inserire un commento, per favore, leggi il regolamento

Permanent link to this article: https://www.mauroalfieri.it/elettronica/cobot-robot-collaborativo-arduino.html

Lascia un commento

Your email address will not be published.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.