Braccio Meccanico
Un braccio meccanico è costituito da “links” (membri) e da giunti (servocomandi). Esiste una relazione analitica tra posizione angolare dei giunti e la “postura” del bracio.

Tramite semplici considerazioni geometriche è piuttosto semplice trovare la posizione (nello spazio) di un qualsiasi punto del braccio note le posizioni angolari dei singoli attuatori e le dimensioni dei singoli membri.
Meno semplice è la determinazione della posizione angolare dei giunti assegnato un obiettivo (es. posizione). Si tratta, in questo caso, della “cinematica inversa”.
LEGGE DEL COSENO
Uno dei metodi usati è quello basato sulla “Teorema del Coseno“, che mette in correlazione analitica la lunghezza dei membri e l’angolo tra due di essi:


Dall grafico precedente, partendo dalle coordinate x e y (obiettivo) si risale alle posizioni angolari come segue (codice in processing):
void calcolo_angoli(){
//TRIGONOMETRIA
I = sqrt(pow(X,2)+pow(Y,2)); //lunghezza ipotenusa
theta_t = atan(Y/X); //angolo tra ipotenusa e asse X
//LEGGE DEL COSENO
angolo[1] = theta_t +acos((pow(L1,2)+pow(I,2)-pow(L2,2))/(2*L1*I));
// NOTA: la posizione angolare viene riferita all'asse X
angolo[2] = acos((pow(L1,2)-pow(I,2)+pow(L2,2))/(2*L1*L2));
}
Appare evidente che non sempre esistono soluzioni: se ad esempio le coordinate fossero tali da avere “ipotenusa” più lunga della massima estenzione del braccio, non si avrebbero soluzioni (fisicamente non può arrivarci!).
Lo spazio W dei punti p(x,y) ammissibili (raggiungibili) è il seguente:
Notare, inoltre, che la solusione può non essere univoca.
A seguire un’animazione in Processing (vedasi come fare animazioni in Processing):
float theta_t;
float[] angolo = new float[2];
float L1 = 120,L2 = 120;
float X,Y;
float X0,Y0=0;
float I;
float t = 0,passo = 0.1;
float R = 40,r = 20;
void setup(){
size(250,270,P3D);
X0 = 1.2*(L1+L2)/2;
}
void draw(){
background(155);
translate(width/10,height/2);
//equazione parametrica circonferenza
X =X0+ R*cos(t);
Y =Y0+ R*sin(t);
t = t + passo; //incremento tempo
noFill();
strokeWeight(2);
ellipse(X0,Y0,2*R,2*R);
//ellipse(X0,Y0,2*R-r,2*R-r);
//ellipse(X0,Y0,2*R+r,2*R+r);
calcolo_angoli();//funzione calcolo angoli (legge del coseno)
fill(0,255,0);
strokeWeight(4);
//costruzione "postura"
rotateZ(-angolo[0]);
line(0,0,L1,0);
ellipse(0,0,r,r);
translate(L1,0);
rotateZ(PI-angolo[1]);
line(0,0,L2,0);
ellipse(0,0,r,r);
fill(255,0,0);
ellipse(L2,0,r,r);
}
void calcolo_angoli(){
//TRIGONOMETRIA
I = sqrt(pow(X,2)+pow(Y,2));
theta_t = atan(Y/X);
//LEGGE DEL COSENO
angolo[0] = theta_t +acos((pow(L1,2)+pow(I,2)-pow(L2,2))/(2*L1*I));
angolo[1] = acos((pow(L1,2)-pow(I,2)+pow(L2,2))/(2*L1*L2));
}

Le cose si complicano se il numero di membri è maggiore di due.
FABRIK
FABRIK è l’acronimo (Forward And Backward Reaching Inverse Kinematics”).

In figura viene mostrato il principio che sta alla base del FABRIK, per il cui utilizzo è necessario conoscere lo stato iniziale:
Si vuole spostare il punto V1 nel punto t (target): il tratto v1-v2 viene spostato in modo tale da cadere sulla direzione della congiungente tra t e v2. Ovviemente per garantire la continuità meccanica è necessario che anche il secondo tratto venga traslato e ruotato in modo da farlo giacere sulla direzione della congiungente v2-v3. Il procedimento si ripete per tutti i membri.
A seguire un esempio in Processing:
float L = 10; //lunghezze
int n = 50; //numero elementi del braccio
PVector a,b,dir;//vettori di servizio
float Xt,Yt;//coordinate "target" t
//MATRICE DELLE COORDINATE DELLE ARTICOLAZIONI
float[][] coordinate = new float[2][n+1];
float t=0;
void setup(){
size(500,500);
//NECESSARIO IMPOSTARE UNO STATO INIZIALE:
for (int i = 0; i <n; i = i+1) {
coordinate[1][i] = 0;
coordinate[0][i] = (i+1)*L;
}
}
void draw(){
background(155);
Xt = width/2 +width/3*cos(t);
Yt = height/2 +width/3*sin(t);
if(t>2*PI){
t = 0;//periodico 2*PI
}
t = t+0.1;
//Xt = mouseX;
//Yt = mouseY;
delay(100);
coordinate[0][0] = Xt;
coordinate[1][0] = Yt;
for (int i = 0; i <n; i = i+1) {
//per ogni segmento se ne prendono le coordinate degli estremi
a = new PVector(coordinate[0][i],coordinate[1][i]);
b = new PVector(coordinate[0][i+1],coordinate[1][i+1]);
dir = PVector.sub(a,b);//vettore nella direzione a-b
dir.setMag(L);//settaggio lunghezza del suddetto vettore
dir.mult(-1);//rotazione
b = PVector.add(a,dir);//aggiunta ad "a"
//aggiornamento matrice delle coordinate:
coordinate[0][i+1] = b.x;
coordinate[1][i+1] = b.y;
}
//tracciamento
for (int i = 0; i <n; i = i+1) {
strokeWeight(5);
line(coordinate[0][i] ,coordinate[1][i],coordinate[0][i+1] ,coordinate[1][i+1]);
}
}
è possibile cambiare sia la lunghezza L dei singoli elementi che il numero totale n.


Matteo Gentileschi