Braccio meccanico: vi presento il mio progetto (è già fatto, tranquilli)

Ciao a tutti,
dato che quando al tempo mi sono approcciato ad Arduino ho trovato su questo forum molte persone disponibili e informazioni utili, vorrei rendervi partecipi e sollecitare vostri consigli, apprezzamenti, domande o aspre critiche riguardo al progetto da me realizzato. :slight_smile:

Si tratta di un braccio meccanico a 4 gradi di libertà, in grado di afferrare e spostare piccoli oggetti.
Ho realizzato sia un controllo manuale tramite joystick della Wii, sia un'interfaccia software per il controllo "remoto".

Seguono i link ai video!

Ciao,
A.

Carino, molto bello!
Se metti anche schemi e codice questo progetto può andare in Megatopic senza meno, così altri che vorranno replicare il tuo lavoro avranno già un bel po' di cose pronte da usare :smiley:

Grazie, appena ho un attimo di tempo in questi giorni metto volentieri a disposizione il tutto :slight_smile:

Bravo, è un bel lavoro.

Ho notato però che il movimento, soprattutto della leva del braccio principale(quella attaccata al basamento per intenderci) è molto brusco.
Dovresti provare a gestire una sorta di accelerazione per rendere tutto più fluido.
A tal proposito non sò se sai che i servi, variano la velocità del motore interno in base alla distanza che devono percorrere, quanto maggiore è la distanza , quanto è maggiore la velocità di rotazione del motore.
Potresti pensare di gestire una sorta di rampa incrementando la posizione del servo stesso a passi sempre crescenti fino ad arrivare al punto di destinazione.
Poi dai video non si capisce, ma pare che il movimento non sia interpolato, o meglio, che si riesca a gestire un grado di movimento unico per volta, è così?

Ho notato però che il movimento, soprattutto della leva del braccio principale(quella attaccata al basamento per intenderci) è molto brusco.
Dovresti provare a gestire una sorta di accelerazione per rendere tutto più fluido.
A tal proposito non sò se sai che i servi, variano la velocità del motore interno in base alla distanza che devono percorrere, quanto maggiore è la distanza , quanto è maggiore la velocità di rotazione del motore.
Potresti pensare di gestire una sorta di rampa incrementando la posizione del servo stesso a passi sempre crescenti fino ad arrivare al punto di destinazione.

Ciao, prima di tutto grazie.
Il movimento della "spalla" (per capirci) in effetti è un po' brutale... In effetti non sapevo che il servo regolasse la velocità in base alla distanza da percorrere, tuttavia avevo già tentato alcune soluzioni del tipo da te descritto per controllarne il movimento a livello software ma i risultati erano stati un po' deludenti :cold_sweat:

Poi dai video non si capisce, ma pare che il movimento non sia interpolato, o meglio, che si riesca a gestire un grado di movimento unico per volta, è così?

Se ho ben capito la domanda la risposta è sì, si può muovere solo un'articolazione alla volta (ogni ciclo di Arduino). E' stata una mia scelta di programmazione per controllarne un po' maggiormente il movimento!

Ciao,
A.

Ciao, magari questo articolo che ho fatto può esserti utile per rendere più fluidi i movimenti, i servi hanno i loro limiti in queste applicazioni (dovuti al loro sistema di controllo) ma diciamo che è un inizio: delucagiovanni forum • View topic - Traiettorie punto-punto, codice ed esperimenti

Ciao

flz47655:
i servi hanno i loro limiti ma diciamo che è un inizio:

Più che altro i servo RC hanno il loro modo di funzionare, come prima cosa tocca pensare allo scopo per cui sono stati progettati, ovvero muovere parti mobili su un modello radiocomandato seguendo fedelmente i comandi impartiti da un pilota umano, cosa che fanno benissimo.
Altro particolare importante è la qualità del servo, quelli low cost sono pietosi come comportamento, si muovono a scatti ed hanno una elevata dead band (punto morto), quelli di qualità superiore sono perfetti nei loro movimenti e consentono di ottenere movimentazioni molto fluide e precise.
Semmai il vero limite è nel controller per automatizzare i movimenti dei servo, Arduino offre la classe servo però questa è limitata alla generazione di un impulso PPM della durata desiderata, non prevede nessun tipo di controllo del profilo di movimento.
Se rimani nel "limitato" mondo di Arduino, con tutti i vari paletti imposti, difficilmente riesci a realizzare un controller servo decente, se lavori in C e utilizzi l'ATmega 328 in modo nativo allora riesci a realizzare un buon controllo servo.
Un ottimo esempio è la SSC32, progetto open source, basato su un ATmega 168 che consente un ottimo controllo del servo.

Ciao Astro,
Se hai dato un'occhiata all'articolo avevo imposto la posizione tramite il classico comando di arduino servo.write() per far seguire al servo un profilo di velocità e accelerazione dolce, ho pubblicato anche il codice e i grafici che ho ottenuto coi mezzi che avevo a disposizione.

Come potrei migliorare la situazione lavorando in modo nativo?

flz47655:
Come potrei migliorare la situazione lavorando in modo nativo?

Tutto il tuo ragionamento non è applicabile ad un servo RC, ha già una sua logica interna e un suo profilo di movimento, non puoi modificarli, i servo RC sono progettati per seguire fedelmente, in proporzione, i movimenti dello stick sul radiocomando, se vuoi spostarli in modo fluido devi emulare questi movimenti.
Altro dettaglio, se usi la servo.write hai solo 255 step su 180°, già questo introduce una forte granularità nel movimento che si traduce in un "tremolio" durante l'avanzamento anche se utilizzi servo di fascia alta, anzi su questi si nota ancora di più, devi usare la " servo.writeMicroseconds();" che ti consente di ottenere una risoluzione di 1000 step su 90° e ottenere un movimento fluido.

Qui http://www.lynxmotion.com/driver.aspx?Topic=oldvideos#arms trovi una serie di video dimostrativi dei bracci robot di Lynxmotion, usano tutti la SSC32 come servo controller, così ti rendi conto cosa intendo con movimenti fluidi e precisi, e si può fare di meglio :slight_smile:
In diversi video viene utilizzata la cinematica inversa per realizzare movimenti composti coordinati, però questa è solo una questione di analisi matematica.

Si, l'articolo principalmente era per mostrare le traiettorie punto-punto, teoricamente e con un esempio di codice. Non avendo motori DC con encoder adatti.. ho fatto prove con quello che avevo in casa, dei servo low-cost.

Avevo puntato sul fatto che i servo hanno una loro inerzia e dando nuove posizioni da raggiungere prima del raggiungimento della posizione precedente li mantenevo in movimento "forzando" il profilo di movimento originale. Come si vede anche dai grafici, per forza di cose il movimento è comunque scattoso ma ci sono comunque dei miglioramenti come descritto nell'articolo.
Non conoscevo la servo.writeMicroseconds() provvederò ad utilizzarla visto che non comporta grandi cambiamenti di codice, unica cosa ho visto che ha una risoluzione di 1000 step su 180° e non su 90°, quindi una risoluzione di 0.18°, un bel miglioramento comunque rispetto ai 0.7° del servo.write() :smiley:

Ho visto il video, sicuramente non sono come il mio servo low-cost, ho dato un'occhiata anche al sorgente di SSC32 ma è completamente senza documentazione.. :astonished: alcune parti sono scritte in Assembly e non è immediato studiarlo purtroppo.

Ciao

flz47655:
SNon conoscevo la servo.writeMicroseconds() provvederò ad utilizzarla visto che non comporta grandi cambiamenti di codice, unica cosa ho visto che ha una risoluzione di 1000 step su 180° e non su 90°, quindi una risoluzione di 0.18°, un bel miglioramento comunque rispetto ai 0.7° del servo.write() :smiley:

La risoluzione è di 1000 step su 90° perché il comando standard dei servo prevede una variazione compresa tra 1000us e 2000us per 90°, per ottenere 180° l'impulso cambia tra 500us e 2500us.
Non tutti i servo rispettano questo standard, a seconda del produttore e del modello è possibile sia che il punto neutro non si trovi a 1500us e che gli estremi (+/-45°) si trovano a valori minori di 1000 e 2000 us.

Ok, dalla documentazione di Arduino non era chiaro

Writes a value in microseconds (uS) to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft. On standard servos a parameter value of 1000 is fully counter-clockwise, 2000 is fully clockwise, and 1500 is in the middle.

Uwe in un intervento diceva Arduino Forum

Con myservo.writeMicrosecond() dai come valore il tempo in µsecondi del impulso dove 1000 corrisponde a 0 di myservo.write() e 2000 a 180

Con queste informazioni avevo dedotto che la risoluzione era 180/(2000-1000) = 0.18°

flz47655:
Ok, dalla documentazione di Arduino non era chiaro

Abbiamo già accertato in più di una occasione che la documentazione di Arduino è errata :slight_smile:

Uwe in un intervento diceva Arduino Forum

Con myservo.writeMicrosecond() dai come valore il tempo in µsecondi del impulso dove 1000 corrisponde a 0 di myservo.write() e 2000 a 180

Non è corretto, del resto basta guardare il datasheet di un qualunque servo per rendersi conto che l'impulso compreso tra 1000us e 2000us è quello per una variazione compresa tra +/- 45° rispetto alla posizione centrale, sono possibili leggeri scostamenti da questi valori a seconda del produttore e del modello.

Ok, grazie

Che tipo di pinza monta il braccio, è di plastica verniciata o alluminio?
Ne sto progettando una simile a 3 dita per tagliarmela poi con la cnc ma se non costa parecchio credo rimando molto volentieri l'autocostruzione :slight_smile:

Dimenticavo: bel progetto, complimenti!

Riguardo la discussione sei servomotori, confesso la mia ignoranza dei dettagli in materia, provvederò a documentarmi meglio. Prossimamente mi piacerebbe tentare di lavorare con l'ATmega "nudo", per tentare un controllo più sofisticato.

La pinza è di alluminio, è questa:

Io ne ho inizialmente costruita una simile, ma il risultato poco soddisfacente mi ha fatto propendere per questo acquisto! Anche perchè è a buon mercato, con meno di 15€ te la porti a casa (senza servo, ovvio).
Unico problema: lì sopra ci puoi montare agevolmente solo uno dei servomotori che ti propongono loro secondo me, io ad esempio ho utilizzato un micro-servo hitec e non si incastrava bene, anche se un po' di colla ha risolto la questione!

Ciao,
A.

ndrplz:
La pinza è di alluminio, è questa:

" Peso: 70 grammi."

Mi sembrano veramente tanti per un oggetto che dovrebbe essere realizzato in lega di alluminio leggera, mi sa tanto che è fatto con un qualche metallo tenero ma pesante.
Tieni presente che quei 70 grammi gravano tutti su i servo moltiplicati per la distanza dal fulcro, già i bracci robot realizzati con servo low cost riescono a sollevare poco se poi vengono gravati da pesi eccessivi delle parti allora non sollevano più nulla :slight_smile:
Un consiglio per chi vuole realizzare un ottimo braccio spendendo il giusto, usate i servo AX12A di Dynamixel, oltre ad essere di tipo digitale ed avere un'elevata coppia (15kg*cm) si controllano tramite una connessione serial onewire multidrop a 1 Mbps (si ottiene facilmente tramite la UART di un 328), non serve il PPM, permettono di impostare tutti i parametri operativi quali velocità e coppia massima, profilo di movimento, comportamento elastico etc, sopratutto permettono di impostare sia lo spazio da percorrere che la velocità di movimento, il che li rende perfetti per la robotica.
Gli AX12A costano circa 50 Euro l'uno, ovvero meno di un buon servo digitale RC e offrono caratteristiche superiori visto che sono progettati specificatamente per la robotica, anche il sistema di fissaggio multiplo e la possibilità di vincolare la squadretta alla carcassa posteriore (carico ripartito in modo omogeneo) sono caratteristiche uniche presenti solo in questi servo.

Complimenti per il progetto come hai interfacciato il nunchuck?

spero che non hai maltrattato altre paperelle di gomma

Iz2rpn:
Complimenti per il progetto come hai interfacciato il nunchuck?

Il Nunchuck si interfaccia ad Arduino tramite il bus I2C, è abbastanza semplice gestirlo, ti allego uno sketch sicuramente funzionante con i Nunchuk originali, legge tutti i dati forniti mettendoli in apposite variabili dedicate e le stampa a video sul monitor seriale.
In testa allo sketch trovi anche come collegare i pin del connettore, oppure lo tagli e fai riferimento ai colori del cavo, non servono pull up per la I2C perché sono presenti internamente al Nunchuck, attenzione che lo sketch è stato scritto con l'IDE 0022, se lo usi con la 1.x tocca aggiornare i nomi delle funzioni per l'I2C.

/*
_________
| 1 2 3 |
|       |
| 6 5 4 |
|_-----_|

•pin 1: verde - data (Arduino analog pin 4) 
•pin 2: (not connected) 
•pin 3: rosso - 3.3V 
•pin 4: giallo - clock (Arduino analog pin 5) 
•pin 5: (not connected) 
•pin 6: bianco - GND 
*/

#include <Wire.h>

void setup()
{
Serial.begin(115200);
nunchuck_init(); // inizializza il nunchuck
}

void loop()
{
nunchuck_get_data();
nunchuck_print_data();
delay(100);
}

//
// Nunchuck functions
//

static uint8_t nunchuck_buf[6];   //array utilizzato per immagazzinare i dati in arrivo dal nunchuck,


void nunchuck_init()
{ 
Wire.begin();                    
Wire.beginTransmission(0x52);    // trasmettiamo l'indirizzo della periferica 0x52
Wire.send(0x40);        // trasmettiamo l'indirizzo della memoria
Wire.send(0x00);        // trasmettiamo uno 0 perchè vogliamo leggere dati
Wire.endTransmission();    // smettiamo di trasmettere
}

void nunchuck_send_request()
{
Wire.beginTransmission(0x52);    //trasmettiamo l'indirizzo del nunchuck 
Wire.send(0x00);        // trasmettiamo un byte
Wire.endTransmission();    // smettiamo di trasmettere
}

// Ricevere dati e immagazzinarli in un buffer
int nunchuck_get_data()
{
int cnt=0;
Wire.requestFrom (0x52, 6);   
while (Wire.available ()) {
// decodifichiamo i byte che ci arrivano e li traformiamo in un intero
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive());
cnt++;
}
nunchuck_send_request();  

if (cnt >= 5) {
return 1;  //restituisce 1 se fallisce
}
return 0; //restituisce 0 se i dati sono stati ricevuti in maniera corretta
}

// Stampare i dati arrivati
// i dati di accelerazione sono lunghi 10 bit
// quindi ne leggiamo 8 poi aggiungiamo
// gli ultimi 2 bit.
void nunchuck_print_data()
{ 
static int i=0;
int joy_x_axis = nunchuck_buf[0];
int joy_y_axis = nunchuck_buf[1];
int accel_x_axis = nunchuck_buf[2] << 2; 
int accel_y_axis = nunchuck_buf[3] << 2;
int accel_z_axis = nunchuck_buf[4] << 2;

int z_button = 0;
int c_button = 0;

// byte nunchuck_buf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if ((nunchuck_buf[5] >> 0) & 1) z_button = 1;
if ((nunchuck_buf[5] >> 1) & 1) c_button = 1;

if ((nunchuck_buf[5] >> 2) & 1) accel_x_axis += 1;
if ((nunchuck_buf[5] >> 3) & 1) accel_x_axis += 2;

if ((nunchuck_buf[5] >> 4) & 1) accel_y_axis += 1;
if ((nunchuck_buf[5] >> 5) & 1) accel_y_axis += 2;

if ((nunchuck_buf[5] >> 6) & 1) accel_z_axis += 1;
if ((nunchuck_buf[5] >> 7) & 1) accel_z_axis += 2;

Serial.print(i,DEC);
Serial.print(",");

Serial.print(accel_x_axis, DEC);
Serial.print(",");
Serial.print(accel_y_axis, DEC);
Serial.print(",");
Serial.print(accel_z_axis, DEC);
Serial.print(",");
Serial.print(joy_x_axis,DEC);
Serial.print(",");
Serial.print(joy_y_axis, DEC);
Serial.print(",");
Serial.print(z_button, DEC);
Serial.print(",");
Serial.print(c_button, DEC);
Serial.print(",");
Serial.print(nunchuck_buf[5], BIN);

Serial.print("\r\n");  // newline
i++;
}

// metodi di codifica dei nunchuck originali
char nunchuk_decode_byte (char x)
{
 x = (x ^ 0x17) + 0x17;
 return x;
}