🤖 Cinématique robotique · sur automate (PLC)

Chapitre 06
Le Jacobien

Objectifs du chapitre

1. Le Jacobien : la dérivée de la cinématique directe

La cinématique directe (chapitre 5) est une fonction non linéaire qui associe à la configuration articulaire q ∈ ℝn la pose de l'effecteur 0Tn(q) ∈ SE(3). Le Jacobien est sa dérivée : il décrit comment un petit mouvement des articulations engendre un petit mouvement de l'outil. Plus précisément, il relie les vitesses articulaires aux vitesses cartésiennes de l'effecteur :

[ v ; ω ] = J(q) · q̇

v ∈ ℝ³ est la vitesse linéaire (3×1) de l'origine de l'effecteur et ω ∈ ℝ³ sa vitesse angulaire (3×1). Le vecteur empilé [v ; ω] est le torseur cinématique (twist) de l'outil. Comme il a 6 composantes et qu'il y a n articulations, J(q) est une matrice 6×n.

J(q) ∈ ℝ6×n,   q̇ ∈ ℝn,   [v ; ω] ∈ ℝ6

Les trois premières lignes de J forment le Jacobien de translation Jv (3×n), les trois dernières le Jacobien de rotation Jω (3×n). Le Jacobien dépend de la configuration q : ce n'est pas une constante, il se recalcule à chaque point de la trajectoire.

Une intuition de descente. Puisque [v ; ω] = J q̇, en multipliant par un petit intervalle de temps on obtient Δx ≈ J · Δq : le Jacobien est la linéarisation locale de la cinématique directe. C'est exactement cette relation qu'on inversera pour la commande cartésienne et la cinématique inverse numérique.

2. Le Jacobien géométrique, colonne par colonne

Il existe une manière remarquablement pratique de construire J sans dériver aucune expression trigonométrique : la méthode géométrique. Chaque colonne i de J exprime la contribution de la seule articulation i au mouvement de l'effecteur, quand toutes les autres sont figées. Sa forme dépend du type de la liaison.

2.1 Éléments géométriques nécessaires

Tout se lit dans les repères cumulés 0Ti que la cinématique directe du chapitre 5 a déjà produits (le tableau T0i). Pour la liaison i on a besoin de deux vecteurs, tous deux exprimés dans le repère de base :

ainsi que pn, l'origine de l'effecteur (translation de 0Tn).

2.2 Colonne pour une liaison rotoïde

Une articulation rotoïde tourne autour de son axe zi-1. Elle engendre à la fois une vitesse angulaire (l'outil tourne) et une vitesse linéaire (l'outil décrit un cercle autour de l'axe). La contribution est :

colonnei = [ zi-1 × (pn − pi-1) ; zi-1 ]

La partie haute zi-1 × (pn − pi-1) est la vitesse linéaire (un rayon pn − pi-1 mis en rotation autour de l'axe, d'où le produit vectoriel), la partie basse zi-1 est la vitesse angulaire (l'outil tourne autour de l'axe de la liaison).

2.3 Colonne pour une liaison prismatique

Une articulation prismatique translate le long de son axe zi-1. Elle ne crée aucune rotation de l'effecteur, seulement une translation dans la direction de l'axe :

colonnei = [ zi-1 ; 0 ]

La partie basse est nulle (pas de vitesse angulaire), la partie haute est simplement l'axe de glissement.

Attention à l'indexation 0-based en ST. La liaison i (comptée de 1 à n dans les manuels) utilise l'axe et l'origine du repère précédent i-1. Dans notre tableau T0i : ARRAY[0..N], le repère 0 est la base et le repère N l'effecteur. La colonne j du Jacobien (avec j := 0..N-1) correspond à la liaison mathématique j+1 et se construit à partir de T0i[j] (le repère i-1) et de T0i[N] (l'effecteur). Décaler d'un cran par erreur donne un Jacobien faux mais d'allure plausible — bug redoutable à débusquer.

3. Jacobien géométrique vs Jacobien analytique

Le Jacobien construit ci-dessus est dit géométrique : sa partie basse fournit la vitesse angulaire physique ω de l'effecteur (un vecteur bien défini, mesurable par un gyromètre). Mais on représente souvent l'orientation par un jeu de trois angles — par exemple les angles de roulis-tangage-lacet (RPY, φ = (φx, φy, φz)). On peut alors vouloir relier aux dérivées de ces angles φ̇ : c'est le rôle du Jacobien analytique.

Or ω et φ̇ ne sont pas égaux : la vitesse angulaire n'est pas la simple dérivée des trois angles, mais une combinaison qui dépend de la convention choisie. Une matrice de transformation E(φ) les relie (ω = E(φ) · φ̇), d'où :

Janalytique = [[ I , 0 ] ; [ 0 , E(φ)⁻¹ ]] · Jgéométrique

La partie translation (bloc identité I) est inchangée ; seule la partie orientation est « re-projetée » sur la base des angles via E(φ)⁻¹, qui dépend de la convention d'angles retenue.

Jacobien géométriqueJacobien analytique
Sortie orientationω (vitesse angulaire physique)φ̇ (dérivée des angles choisis)
Dépend d'une conventionnonoui (RPY, Euler…)
Singularités de représentationaucuneoui (« gimbal lock » de E)
Usage privilégiécommande, statique, IKsuivi d'une consigne en angles

Privilégiez le géométrique pour la commande. Il ne dépend d'aucune convention et n'introduit aucune singularité de représentation (le « gimbal lock » des angles d'Euler, où E(φ) devient singulière indépendamment de la géométrie du bras). On n'emploie l'analytique que lorsque la tâche est explicitement exprimée en dérivées d'angles. Dans tout le reste de ce chapitre, J désigne le Jacobien géométrique.

4. À quoi sert le Jacobien ?

4.1 Commande cartésienne en vitesse

C'est l'usage le plus direct. On veut imposer à l'outil un torseur de vitesse [v ; ω] (déplacer la pince à 5 cm/s vers l'avant, par exemple). Il faut pour cela les vitesses articulaires correspondantes — on inverse la relation. Pour un robot 6 axes non singulier, J est carrée (6×6) et inversible :

q̇ = J⁻¹ · [ v ; ω ]

Pour un robot redondant (n > 6) ou non carré, on remplace l'inverse par la pseudo-inverse de Moore-Penrose J+, qui fournit la solution de norme minimale :

J+ = JT (J JT)⁻¹,   q̇ = J+ · [ v ; ω ]

4.2 Dualité statique : force et couple

Le Jacobien relie aussi les efforts, par une élégante dualité. Si l'effecteur exerce (ou subit) un torseur d'effort F = [f ; m] (force 3×1 + moment 3×1), les couples articulaires τ nécessaires pour l'équilibrer sont donnés par la transposée du Jacobien :

τ = JT · F

La même matrice qui transporte les vitesses de l'articulaire vers le cartésien transporte les efforts du cartésien vers l'articulaire. C'est la base de la commande en effort, de la compensation de gravité et de l'estimation de la charge portée par l'outil. Remarquez que J est 6×n, donc JT est n×6 : elle prend bien un effort 6×1 et rend n couples articulaires.

4.3 Cinématique inverse numérique

Enfin, le Jacobien est la porte d'entrée de la cinématique inverse numérique. Pour atteindre une pose cible, on itère : à partir de la configuration courante, on calcule l'écart de pose Δx, on résout le pas articulaire, on met à jour, on recommence (méthode de Newton, ou resolved-rate) :

Δq = J⁻¹ · Δx,   q ← q + Δq

Lien avec le chapitre Rust 12. C'est exactement la boucle d'IK numérique développée dans le cours Rust (chapitre 12) : linéarisation Δx ≈ J·Δq, résolution du pas, itération, avec amortissement près des singularités. Le présent chapitre en fournit la brique manquante — comment obtenir J pour un bras 3D quelconque — et la version temps réel sur automate.

5. Singularités et manipulabilité

Une singularité est une configuration où J perd son rang : pour un robot 6×6, cela se traduit par det J = 0. Physiquement, l'effecteur perd instantanément un degré de liberté cartésien : il existe une direction de l'espace dans laquelle il ne peut plus se déplacer, quelles que soient les vitesses articulaires.

Le danger, pour la commande, est numérique. Près d'une singularité, l'inverse J⁻¹ explose : un déplacement cartésien minuscule exige des vitesses articulaires énormes (q̇ = J⁻¹[v;ω] diverge). Sur un robot réel, cela se traduit par des à-coups violents, des pics de couple, voire un arrêt de sécurité — au mieux. Types classiques pour un bras 6 axes :

Pour quantifier la proximité d'une singularité, on utilise la manipulabilité de Yoshikawa :

w = √( det( J · JT ) )

w est grand loin des singularités (le robot se meut aisément dans toutes les directions) et w → 0 à la singularité. Pour un robot carré, w = |det J|. C'est un indicateur scalaire précieux à surveiller en continu.

Ne jamais inverser aveuglément J près d'une singularité. Une consigne cartésienne parfaitement raisonnable peut produire une consigne articulaire à des milliers de degrés par seconde si det J ≈ 0. Le symptôme : le robot « fouette » brutalement en approchant du bras tendu ou d'un alignement de poignet. Testez toujours la manipulabilité w (ou le conditionnement de J) avant d'appliquer .

La parade standard est la méthode des moindres carrés amortis (Damped Least Squares, amortissement de Levenberg) : on ajoute un terme de régularisation λ² qui borne l'amplitude de la solution même quand J devient singulière :

q̇ = JT ( J JT + λ² I )⁻¹ · [ v ; ω ]

Amortir plutôt qu'inverser. La matrice (J JT + λ² I) est toujours inversible (elle ne devient jamais singulière), ce qui borne par construction. Loin des singularités et pour λ → 0 on retrouve la pseudo-inverse (précision maximale) ; près d'une singularité, λ² I lisse la commande au prix d'une petite erreur résiduelle. On choisit λ petit et fixe, ou adaptatif selon w. Combinez avec une planification qui évite d'approcher les singularités connues : la robustesse en ligne ne remplace pas une trajectoire propre.

6. Implémentation en Structured Text

On réutilise les types du chapitre 1 (T_VEC3, T_MAT4, T_DH) et le tableau de repères cumulés T0i produit par la cinématique directe (chapitre 5). On ajoute un type pour le Jacobien, une fonction de produit vectoriel, et le bloc fonctionnel qui remplit J.

6.1 Type du Jacobien et constante de dimension

// Dimension maximale (nb. d'axes) allouee statiquement. 8 couvre la
// plupart des bras (6 axes) et laisse de la marge pour les robots redondants.
VAR_GLOBAL CONSTANT
    N_MAX : INT := 8;
END_VAR

// Jacobien geometrique 6 x n : 6 lignes (v puis omega), N_MAX colonnes.
// Lignes 0..2 = Jv (translation), lignes 3..5 = Jomega (rotation).
TYPE T_JAC : ARRAY[0..5, 0..N_MAX-1] OF LREAL; END_TYPE

6.2 Produit vectoriel

Le cœur de la colonne rotoïde est un produit vectoriel a × b. On le code une fois, en passant le résultat par VAR_IN_OUT (aucune copie, réflexe temps réel).

// Produit vectoriel r := a x b  (vecteurs 3x1).
// r doit etre DISTINCT de a et de b (aliasing interdit, cf. chapitre 1).
FUNCTION Cross : BOOL
VAR_INPUT
    a : T_VEC3;
    b : T_VEC3;
END_VAR
VAR_IN_OUT
    r : T_VEC3;   // resultat
END_VAR
    r[0] := a[1] * b[2] - a[2] * b[1];
    r[1] := a[2] * b[0] - a[0] * b[2];
    r[2] := a[0] * b[1] - a[1] * b[0];
    Cross := TRUE;
END_FUNCTION

6.3 Le bloc fonctionnel FB_GeometricJacobian

Il prend le tableau des repères cumulés T0i (calculés par la FK), la table DH (pour connaître le type de chaque liaison via isPrismatic) et le nombre d'axes n, puis remplit J. La boucle parcourt les liaisons : pour chacune on extrait zi-1 (3ᵉ colonne de la rotation de T0i[j]) et pi-1 (translation de T0i[j]), puis on écrit la colonne selon le type de liaison.

// Construit le Jacobien geometrique 6 x n dans J.
//
// ENTREES :
//   T0i : repere cumule 0->i pour i = 0..n, produit par la FK (chapitre 5).
//         T0i[0] = base (identite), T0i[n] = effecteur.
//   dh  : table Denavit-Hartenberg (n liaisons) ; seul isPrismatic sert ici.
//   n   : nombre d'axes (1..N_MAX).
// SORTIE :
//   J   : Jacobien 6 x n. Colonne j (j=0..n-1) = contribution de la liaison j+1.
//         Lignes 0..2 = vitesse lineaire, lignes 3..5 = vitesse angulaire.
FUNCTION_BLOCK FB_GeometricJacobian
VAR_IN_OUT
    T0i : ARRAY[0..N_MAX] OF T_MAT4;   // reperes cumules (0..n utilises)
    dh  : ARRAY[0..N_MAX-1] OF T_DH;   // parametres DH
    J   : T_JAC;                       // resultat 6 x n
END_VAR
VAR_INPUT
    n : INT;                           // nombre d'axes
END_VAR
VAR
    j        : INT;                    // colonne / liaison (0-based)
    z        : T_VEC3;                 // axe z_(i-1), 3e colonne de R
    p_prev   : T_VEC3;                 // origine p_(i-1)
    p_n      : T_VEC3;                 // origine effecteur p_n
    diff     : T_VEC3;                 // p_n - p_(i-1)
    vlin     : T_VEC3;                 // z x diff (partie translation rotoide)
    dummy    : BOOL;
END_VAR

// --- Origine de l'effecteur p_n = translation de T0i[n] (colonne 3) ---
p_n[0] := T0i[n][0, 3];
p_n[1] := T0i[n][1, 3];
p_n[2] := T0i[n][2, 3];

// --- Boucle sur les n liaisons ---
FOR j := 0 TO n - 1 DO
    // Axe z_(i-1) = 3e colonne de la rotation de T0i[j] (indices [.,2]).
    z[0] := T0i[j][0, 2];
    z[1] := T0i[j][1, 2];
    z[2] := T0i[j][2, 2];

    // Origine p_(i-1) = translation de T0i[j] (colonne 3).
    p_prev[0] := T0i[j][0, 3];
    p_prev[1] := T0i[j][1, 3];
    p_prev[2] := T0i[j][2, 3];

    IF dh[j].isPrismatic THEN
        // Liaison PRISMATIQUE : colonne = [ z ; 0 ].
        J[0, j] := z[0];   J[1, j] := z[1];   J[2, j] := z[2];   // Jv = z
        J[3, j] := 0.0;    J[4, j] := 0.0;    J[5, j] := 0.0;    // Jomega = 0
    ELSE
        // Liaison ROTOIDE : colonne = [ z x (p_n - p_prev) ; z ].
        diff[0] := p_n[0] - p_prev[0];
        diff[1] := p_n[1] - p_prev[1];
        diff[2] := p_n[2] - p_prev[2];

        dummy := Cross(a := z, b := diff, r := vlin);   // vlin := z x diff

        J[0, j] := vlin[0];   J[1, j] := vlin[1];   J[2, j] := vlin[2];  // Jv
        J[3, j] := z[0];      J[4, j] := z[1];      J[5, j] := z[2];     // Jomega = z
    END_IF
END_FOR

Tableaux fixes, pas de recalcul trigonométrique. T_JAC est dimensionné à N_MAX colonnes sur la mémoire statique : aucune allocation. Surtout, FB_GeometricJacobian ne recalcule aucun sinus ni cosinus : toute la trigonométrie a déjà été payée par la cinématique directe, condensée dans T0i. Le Jacobien n'est plus qu'une extraction de colonnes et n produits vectoriels.

7. Aspects temps réel

Le Jacobien géométrique est quasi gratuit quand il suit une cinématique directe : il réutilise les repères déjà calculés (T0i) et ne fait que lire des colonnes et calculer n produits vectoriels. Aucun SIN/COS supplémentaire. Le coût est de l'ordre de quelques microsecondes, parfaitement borné et déterministe — négligeable devant la FK elle-même.

Ordre de grandeur. Un produit vectoriel = 6 multiplications + 3 soustractions ≈ quelques dizaines de nanosecondes. Pour un bras 6 axes : 6 produits vectoriels + extractions ≈ 1 µs, une fois la FK faite. La construction du Jacobien n'est donc jamais le goulot d'étranglement. C'est son inversion (pour la commande) qui coûte davantage : une résolution 6×6 par élimination de Gauss, c'est de l'ordre de 6³/3 ≈ 72 opérations flottantes — quelques µs de plus, mais toujours borné et faisable dans un cycle de 250 µs à 1 ms.

Surveillez le conditionnement. L'élimination de Gauss 6×6 tient dans le cycle, mais elle diverge si J est mal conditionnée (proche d'une singularité) : les pivots deviennent minuscules et les erreurs explosent. En pratique, sur automate : (1) calculez la manipulabilité w à chaque cycle et déclenchez une alarme sous un seuil ; (2) basculez sur les moindres carrés amortis (§5) dès que w chute — l'ajout de λ² I garantit un pivot non nul et une commande bornée, préservant le déterminisme du cycle.

Exercices

Exercice 1 — Jacobien analytique du bras 2R et sa singularité

Pour le bras planaire 2R (segments L1, L2, angles θ1, θ2) dont la FK donne la position (x, y), dérivez à la main le Jacobien analytique J = ∂(x,y)/∂(θ12), montrez que det J = L1 L2 sin θ2, et identifiez les singularités. Codez ensuite J en ST.

Voir la solution

Avec x = L1c1 + L2c12 et y = L1s1 + L2s12 (notations c12 = cos(θ12)), on dérive :

J = [[ −L1s1 − L2s12 , −L2s12 ] ; [ L1c1 + L2c12 , L2c12 ]]

Le déterminant se simplifie (en développant et via s1c12 − c1s12 = −sin θ2) :

det J = L1 L2 sin θ2

Il s'annule pour θ2 = 0 (bras tendu) ou θ2 = π (bras replié) : ce sont les singularités du 2R, où l'effecteur ne peut plus se déplacer radialement.

// Jacobien analytique 2x2 du bras 2R. J range en ARRAY[0..1, 0..1].
FUNCTION Jacobian2R : BOOL
VAR_INPUT
    t1, t2 : LREAL;   // angles (rad)
    L1, L2 : LREAL;   // longueurs de segment (m)
END_VAR
VAR_IN_OUT
    J : ARRAY[0..1, 0..1] OF LREAL;
END_VAR
VAR
    s1, c1, s12, c12 : LREAL;
END_VAR
    s1  := SIN(t1);        c1  := COS(t1);
    s12 := SIN(t1 + t2);   c12 := COS(t1 + t2);

    J[0, 0] := -L1 * s1 - L2 * s12;   J[0, 1] := -L2 * s12;
    J[1, 0] :=  L1 * c1 + L2 * c12;   J[1, 1] :=  L2 * c12;
    Jacobian2R := TRUE;
END_FUNCTION

Exercice 2 — Coder et tester le produit vectoriel

Implémentez Cross(a, b, r) et vérifiez sur la base canonique que ex × ey = ez (aux erreurs d'arrondi près). Vérifiez aussi que a × a = 0.

Voir la solution
PROGRAM P_TestCross
VAR
    ex : T_VEC3 := [1.0, 0.0, 0.0];
    ey : T_VEC3 := [0.0, 1.0, 0.0];
    r  : T_VEC3;
    ok : BOOL := TRUE;
    d  : BOOL;
END_VAR

// ex x ey doit valoir ez = (0, 0, 1)
d := Cross(a := ex, b := ey, r := r);
IF ABS(r[0] - 0.0) > 1E-12 THEN ok := FALSE; END_IF
IF ABS(r[1] - 0.0) > 1E-12 THEN ok := FALSE; END_IF
IF ABS(r[2] - 1.0) > 1E-12 THEN ok := FALSE; END_IF

// a x a doit valoir 0
d := Cross(a := ex, b := ex, r := r);
IF ABS(r[0]) + ABS(r[1]) + ABS(r[2]) > 1E-12 THEN ok := FALSE; END_IF
// ok = TRUE si les deux proprietes sont verifiees

On compare toujours avec une tolérance (1E-12), jamais avec = strict — même réflexe qu'au chapitre 1. Le produit vectoriel étant antisymétrique, a × a = 0 est un test d'autocohérence gratuit.

Exercice 3 — Extraire z et p d'une T_MAT4

Écrivez deux fonctions qui, à partir d'une transformation homogène T ∈ T_MAT4, extraient l'axe z (3ᵉ colonne de la rotation) et l'origine p (translation). Ce sont les deux briques que FB_GeometricJacobian utilise pour chaque liaison.

Voir la solution
// Axe z du repere : 3e colonne de la rotation = colonne d'indice 2, lignes 0..2.
FUNCTION GetAxisZ : BOOL
VAR_IN_OUT
    T : T_MAT4;
    z : T_VEC3;
END_VAR
    z[0] := T[0, 2];
    z[1] := T[1, 2];
    z[2] := T[2, 2];
    GetAxisZ := TRUE;
END_FUNCTION

// Origine du repere : partie translation = colonne d'indice 3, lignes 0..2.
FUNCTION GetOrigin : BOOL
VAR_IN_OUT
    T : T_MAT4;
    p : T_VEC3;
END_VAR
    p[0] := T[0, 3];
    p[1] := T[1, 3];
    p[2] := T[2, 3];
    GetOrigin := TRUE;
END_FUNCTION

Rappel de la structure d'une T_MAT4 : le bloc 3×3 en haut à gauche est la rotation (ses colonnes 0, 1, 2 sont les axes x, y, z du repère exprimés dans la base), la colonne 3 (lignes 0..2) est la translation. Extraire l'axe z, c'est donc lire la colonne d'indice 2 ; l'origine, la colonne d'indice 3.

Exercice 4 — Calculer la manipulabilité d'un 2R

À partir du Jacobien 2×2 de l'exercice 1, calculez la manipulabilité w = √(det(J·JT)). Montrez que pour un robot carré cela se réduit à w = |det J|, et vérifiez que w → 0 au bras tendu (θ2 = 0).

Voir la solution

Pour J carrée, det(J·JT) = det(J)·det(JT) = (det J)², donc w = √((det J)²) = |det J|. Pour le 2R, w = |L1 L2 sin θ2|, qui tend bien vers 0 quand θ2 → 0 ou π.

// Manipulabilite d'un 2R : w = |det J| = |L1 * L2 * sin(t2)|.
FUNCTION Manipulability2R : LREAL
VAR_INPUT
    t2     : LREAL;   // angle du coude (rad)
    L1, L2 : LREAL;
END_VAR
    Manipulability2R := ABS(L1 * L2 * SIN(t2));
END_FUNCTION

// --- Verification (pseudo-programme) ---
// w(t2 = PI/2) = L1*L2         -> manipulabilite maximale
// w(t2 = 0)    = 0             -> singularite (bras tendu)
// w(t2 = PI)   = 0             -> singularite (bras replie)

En pratique, surveillez w à chaque cycle et déclenchez le passage aux moindres carrés amortis (ou une alarme) dès qu'il descend sous un seuil calibré sur le robot. C'est le garde-fou temps réel contre l'explosion de près des singularités.

Récapitulatif