Mi è venuto in mente di costruirmi, così per diletto, un braccio robotico bidimensionale, per esempio per fare dei disegni attaccandoci un pennarello.
Sarebbe formato da due articolazioni (braccia), ognuna movimentata da un motore passo-passo.
Supponendo che le due braccia sia lunghe L, dovrei poter raggiungere un qualsiasi punto in tutta l'area circolare di raggio 2L con centro nel primo motore (o almeno un semicerchio di raggio 2L se il motore si muove con una rotazione -90/+90).
Ma ho due domande a cui la mia ormai troppo vecchia conoscenza di geometria non riesce a rispondere.
Come calcolare l'angolo di entrambi i motori per raggiungere un determinato punto di coordinate (x,y)? Molto probabilmente, per ogni punto, possono esistore diverse soluzioni. Per esempio, il punto (2L,0) è raggiungibile lasciano i due motori con un angolo di 0° (supponendo che l'angolo 0° corrisponda all'asse X). Mentre il punto (L,0) potrebbe essere raggiunto ruotando il primo motore di 60° (in senso antiorario) e il secondo motore di un angolo di -120° (in senso orario). Ma anche ruotando il primo di -60° e il secondo di +120°.
L'altra domanda, ancora più complicata. Se parto dal punto (x1,y1) e voglio raggiungere (x2,y2) spostandomi lunga il segmento che li congiunge (per esempio, per tracciare un linea con un pennarello) come devo spostare i due motori?
Immagino che siano problemi di robotica semplicissimi, considerando che stiamo ragiornando su un piano e non nel volume, quindi sicuramente ci saranno già delle risposte, ma per me rimangono complicate.
Didn't find your answer? Ask the community — no account required.
R
RoV
L'approccio piu' semplice e' calcolare la cinematica inversa del punto finale (quello iniziale lo sai, e' dove ti trovi), poi per ciascun asse costruire le tappe intermedie interpolando linearmente. Non ti assicura di eseguire il percorso piu' breve, ma funziona. Se la cinematica inversa ha piu' soluzioni, dovresti scegliere quella piu' vicina a quella attuale (minor distanza complessiva in coordinate assi). In alternativa puoi definire la traiettoria che vuoi tu nello spazio (x,y): rettilinea, curva per evitare collisioni con altri oggetti oppure per seguire una forma (obbligo se la penna sta scrivendo). Poi dividi la traiettoria in piccoli tratti (ossia interpoli lungo l'asse curvilineo) e ti calcoli la cinematica inversa per ogni punto da raggiungere. Anche in questo caso, se ci sono piu' soluzioni scegli ad ogni passo la piu' vicina in coordinate assi a quella corrente.
Ti segnalo un questo kit di Elektor:
formatting link
Ciao,
R
RoV
Ciao,
P
pozz
Molto interessante. L'esempio planare che fanno è con 3 variabili di giunto (come se fossero tre bracci), anche se parlano di posizione dell'end-effector (il manipolatore) che, nel mio caso, essendo una penna, non c'è.
Il trattamente matematico è molto rigoroso (si parla di matrici di rototraslazioni), ma alla fine le formule finali della cinematica diretta sono semplicissime equazioni che si potevano ottenere applicando la geometria trigonometrica della scuola superiore.
Con un po' di passaggi matematici al sistema delle equazioni della cinematica diretta riesce ad arrivare al sistema delle equazioni della cinemtica inversa, che poi è quella che mi serve per posizionare la penna dove voglio: l'angolo theta1 e theta2 che sono il pilotaggio dei due motori.
Interessante è capire come far fare ad un Arduino questi conti. Mi vengono in mente due cose:
- far fare i conti ad un PC collegato in seriale ad un Arduino
- utilizzare delle tabelle per il calcolo di atan2, sin e cos
Oppure, chiaramente, andare su piattaforme più potenti come RPi.
Nel documento si parla della lettura della posizione dei giunti tramite encoder. E da quella posizione calcola la successiva posizione considerando un piccolo movimento temporale deltaT.
A questo punto mi sorge il dubbio: mi servono dei giunti dei quali posso leggere la posizione? Utilizzando un servo, in teoria il feedback è interno. Se imposto un PWM che dice al servo di posizionarsi a 30°, in teoria dovrebbe fare il possibile per farlo e tenere la posizione, anche se ci sono dei disturbi. O sbaglio?
Quindi non dovrebbe servirmi leggere la posizione dei servo. Forse è necessario quando utilizzi motori passo-passo più grossi che non hanno un meccanismo di feedback integrato?
Interessante, anche se per divertimento vorrei costruirmelo. A proposito, che tipo di servomotore potrei prendere?
P
pcf ansiagorod
Che io sappia si fa proprio così, si chiamano 'lookup tables', per i valori intermedi si interpola. E' anche abbastanza facile capire se è fattibile per lo specifico hardware; si prende la risoluzione spaziale desiderata e il caso peggiore (massima distanza dal centro), si calcola il delta-teta o qualsivoglia nome per l'angolo considerando i bracci giacenti sulla stessa retta e con qualche conto si capisce quanti valori deve avere la tabella.
Una volta per non sfondare con la memoria ho usato un'interpolazione parabolica tra tre punti consecutivi i cui estremi contenevano il valore assegnato, ma se farlo dipende dal caso specifico e il mio era un problema del tutto diverso.
P
pozz
Il 28/04/2025 19:25, pcf ansiagorod ha scritto:
In realtà ho visto che le avr libc usato dalle board Arduino AVR implementano tranquillamente le funzioni trigonometriche standard C (atan2, sin, cos, ecc) nella versione float.
Ho dato una sbirciata al codice sorgente e sono tutte scritte in assembler. Ci sono chiaramente delle ottimizzazioni semplici (tipo atan(x)=pi/2-atan(1/x) se x>1), ma anche sviluppi in serie e lookup tables.
Dovrebbe tutto funzionare senza fare nulla.
Penso che il problema più grosso sia la parte meccanica.
A
alfio
"pozz" ha scritto nel messaggio news:vujjje$37u8e$ snipped-for-privacy@dont-email.me...
dovrebbe bastare l'applicazione della cinematica inversa descritta qui:
formatting link
prima usi la formula 10 per calcolare l'angolo del secondo motore (il trasportato). poi usi la formula 6 per l'angolo del primo motore (sull'origine degli assi)
essendo 2 circonferenze che si intersecano ci sono sempre 2 possibili soluzioni, tranne nel caso dei bracci in linea. nel documento precedente lo trovi nel calcolo dell'angolo del motore 2 che ha nella formula +/-sqrt(1-c2)
in un loop fatto di tanti passi quanto la differenza piu' grande tra le coordinate di partenza e quella di arrivo, calcoli il percorso lineare e per ogni punto x,y applicando la cinematica inversa (vista sopra) trovi gli angoli da dare ai motori. prendi questa funzione DDA come esempio:
formatting link
P
pcf ansiagorod
Fantastico, mi sa che sono ben poco aggiornato! :)
A
alfio
"alfio" ha scritto nel messaggio news:vuoqom$62l3$ snipped-for-privacy@dont-email.me...
e invece no, o perlomeno ci sono degli errori, la teoria e' quella giusta ma la hanno applicata male.
qui di seguito il codice corretto. provato sul Mega2560 ci mette meno di 1 msec per convertire da x,y ad angoli dei 2 motori.
poi provo a farti il generatore di traiettoria. ________________________________________________________________
calcola quando dura la funzione xyToJoint() facendo la media di 400 chiamate
*/ int i; unsigned long start = micros(); for( i=0 ; i<100 ; i++ ) { if( !sys.xyToJoint( 25,25 ) || !sys.xyToJoint( -5,35 ) || !sys.xyToJoint( 5,-55 ) || !sys.xyToJoint( -20,-50 ) ) break; } unsigned long end = micros(); if( i<100 ) Serial.println( "out of range" ); else Serial.println( "execution medium time usec:"+String( (end-start)/400.0 ) ); Serial.println(); }
#define RtD(x) (180.0*(x)/PI )
void loop() { Serial.println( "Send a character to start random test" ); while( !Serial.available() ) delay( 10 ); while( Serial.available() ) Serial.read(); randomSeed( micros() ); double x=50,y=30; x = random( 2*(sys.a1.len+sys.a2.len) )-(sys.a1.len+sys.a2.len); y = random( 2*(sys.a1.len+sys.a2.len) )-(sys.a1.len+sys.a2.len); Serial.println( "(x,y)="+String( x )+","+String( y ) ); unsigned long start = micros(); bool fail=!sys.xyToJoint( x,y ); unsigned long end = micros(); if( fail ) Serial.println( "Out of range:"+String( sqrt( x*x+y*y ) ) ); else { Serial.println( "execution time usec:"+String( end-start ) ); Serial.println( "(a1,a2)="+String( RtD(sys.a1.pos) )+","+String( RtD(sys.a2.pos) ) ); sys.JointToXY( x,y ); Serial.println( "(x,y)="+String( x )+","+String( y ) ); } Serial.println(); }
P
pcf ansiagorod
Urca quanto sei bravo, complimenti!!!!!!
A
alfio
"alfio" ha scritto nel messaggio news:vv2p4m$1dlpv$ snipped-for-privacy@dont-email.me...
eccolo, un po' diverso dalla formula DDA che ti avevo indicato, perche' ci ho messo il controllo di velocita' e accelerazione sulla risultante lineare. manca una gestione dei punti di singolarita', cioe' dove i bracci non possono andare. la classe Scara e' quello che servira' nel tuo applicativo, io ho creato un minimo di interfaccia su seriale per fare il debug, messi in setup() e loop(). il tempo di elaborazione di Scara.Loop() e' meno di 1.5 msec su atmega2560. Scara.Loop() fa i conti ad ogni scansione, cosi' puoi cambiare velocita' e accelerazione mentre il sistema si sta muovendo. puoi cambiare anche il punto di destinazione, ma avverrebbe senza rampe, in modo brusco, quindi non te lo consiglio.
se il copia incolla non produce un codice compilabile, dimmelo che lo pubblico da qualche parte sul web.
#define PRECISION 0.01 // tolleranza posizione raggiunta in mm #define R2D(x) (180.0*(x)/PI ) // da radianti a gradi
struct ArmDef { double len,lensq; // lunghezza del braccio in mm double pos; // angolo in radianti byte pin; // uscita controllo servo motor Servo srv; // Servo object
ArmDef(){} ArmDef( byte pn,double mm ) { len=mm; lensq=mm*mm; srv.attach( pin=pn ); SetPos( 0.0 ); } void SetPos ( double p ) // in range -90° to +90° { pos=p; srv.writeMicroseconds( 1500+1000*p/PI ); } };
struct Scara { ArmDef a1,a2; double rangeMin, rangeMax; // estensione minima e massima in mm double ax,ay; // coordinate x,y attuali in mm double dx,dy; // coordinate x,y di destinazione in mm unsigned long lastloop,interval; // tempo di scansione in usec double maxv,maxa; // velocita' e accelerazione massima in mm/scansione double av,aa; // velocita' e accelerazione attuali in mm/scansione bool inPosition; // punto di destinazione raggiunto bool deb; // output di debug attivo
*/ Scara sys ( ArmDef(2,50), // braccio sull'origine: pin controllo servo, lunghezza braccio in mm ArmDef(3,30), // braccio trasportato : pin controllo servo, lunghezza braccio in mm 20.0, // velocita' lineare in mm/s 300.0, // accelerazione lineare in mm/s/s 10000 // intervallo Loop di calcolo in usec );
void setup() { Serial.begin( 115200 );
Serial.println( "(L1,L2)="+String( sys.a1.len )+","+String( sys.a2.len ) ); Serial.println( "maxv,maxa="+String( sys.maxv )+","+String( sys.maxa ) ); Serial.println( "interval="+String( sys.interval ) ); Serial.println( "(ax,ay):"+String( sys.ax )+","+String( sys.ay )+" (av,aa):"+String( sys.av )+","+String( sys.aa ) ); Serial.println(); Serial.println( "Command: Px,y - Set destination point (P23.9,-45.8)" ); Serial.println( " Vmms - Set speed in mm/s (V20.5)" ); Serial.println( " Amms2- Set accel in mm/s*s (A100.0)" ); Serial.println( " T - Display Loop() max time" ); Serial.println( " D1/0 - Debug on/off" ); Serial.println(); }
Non ho ancora avuto modo di studiarmi il documento e il tuo codice. Sono curioso di sperimentare. Volevo provare a realizzare un braccio robotico fatto in casa facilmente (molto fai-da-te), ma ho paura che sia troppo inaffidabile.
Probabilmente sarebbe più utile partire da qualche kit di braccio robotico da comprare o per cui esistono i file di produzione con stampante 3D (ho visto che ce ne sono diversi).
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required
Report Content
You are reporting this content to the moderators. They will look at it
ASAP.