Tutoriel · Intermédiaire · 40 min de lecture

Profils de mouvement : trapézoïdal et courbe en S

Pourquoi une position nue fait patiner les moteurs, comment un profil trapézoïdal corrige, et ce que coûte en temps une courbe en S limitée en à-coups.

Introduction

Dites à un moteur d’aller à une position et il essaiera d’y arriver immédiatement. Pas vite — immédiatement. La commande ne contient aucune information sur la vitesse, donc la seule interprétation disponible est « aussi vite que possible », et ce qui arrive ensuite est décidé par ce qui s’épuise en premier : couple, limite de courant, traction, ou la capacité du pas à pas à suivre.

Un profil de mouvement est l’information manquante. Il transforme « va à 100 mm » en horaire — où l’axe devrait être à chaque instant entre maintenant et l’arrivée — et remet au contrôleur une cible qui bouge à une vitesse que la machine peut vraiment délivrer.

C’est la différence entre un bras robotique qui arrive et un qui arrive et cesse de trembler.

Ce qu’une commande de position nue demande vraiment

Considérez un échelon : à t = 0 la cible saute de 0 mm à 100 mm.

Pour le suivre exactement, l’axe aurait besoin d’une vitesse infinie, ce qui exige une accélération infinie, ce qui exige un effort infini. Il ne peut pas, donc quelque chose cède :

  • Un pas à pas est commandé à un taux de pas que son couple ne peut produire à cette vitesse, rate des pas, et — tournant en boucle ouverte — n’en sait rien. La position est désormais fausse en permanence. Pourquoi cela arrive, et pourquoi c’est toujours quatre pas à la fois.
  • Un moteur CC avec une boucle PID voit une erreur instantanée énorme, sature sa sortie, emballe son terme intégral, et dépasse.
  • Un servo fonce à son taux maximal et arrive avec tout le mécanisme encore en mouvement.

Aucune n’est un problème de réglage. Ce sont toutes le même problème : la commande était physiquement impossible.

Le profil trapézoïdal

Le correctif est de commander la vitesse, et de changer la vitesse à un taux que le moteur peut soutenir. Accélérez à taux constant jusqu’à une vitesse de croisière, tenez-la, puis décélérez au même taux. Tracez la vitesse contre le temps et c’est un trapèze, d’où le nom.

Trois nombres le définissent : la distance d, une vitesse de pointe v_max, et une accélération a.

t_accel = v_max / a                     time spent ramping
d_accel = v_max² / (2a)                 distance used by one ramp
t_total = v_max / a  +  d / v_max       whole move, if it reaches v_max

Pour un déplacement de 100 mm à 100 mm/s avec a = 500 mm/s² : chaque rampe prend 0,2 s et couvre 10 mm, laissant 80 mm de croisière, pour un total de 1,2 s.

Voici le tout en fonction du temps — la position que votre boucle de commande devrait poursuivre à l’instant t :

// Target position t seconds into a trapezoidal move of `d` mm.
float profilePosition(float t, float d, float vMax, float a) {
  float tAcc = vMax / a;
  float dAcc = vMax * vMax / (2 * a);

  if (2 * dAcc > d) {                      // too short to reach vMax
    float tPeak = sqrtf(d / a);            // triangular profile
    if (t >= 2 * tPeak) return d;
    if (t < tPeak) return 0.5f * a * t * t;
    float left = 2 * tPeak - t;
    return d - 0.5f * a * left * left;
  }

  float tCruise = (d - 2 * dAcc) / vMax;
  float tTotal  = 2 * tAcc + tCruise;
  if (t >= tTotal) return d;
  if (t < tAcc)           return 0.5f * a * t * t;
  if (t < tAcc + tCruise) return dAcc + vMax * (t - tAcc);

  float left = tTotal - t;
  return d - 0.5f * a * left * left;
}

Notez ce que cette fonction n’est pas : ce n’est pas un contrôleur. Elle génère une consigne. Ce que vous aviez — un ordonnanceur de pas pas à pas, une boucle PID, une écriture servo — fait toujours le suivi. Le profil change seulement ce qui est suivi.

Le déplacement demandé n’est pas toujours le déplacement obtenu

Cette branche 2 * dAcc > d est le cas rencontré en premier et mal diagnostiqué.

Si le déplacement est plus court que la distance nécessaire pour accélérer puis redécélérer, l’axe n’atteint jamais la vitesse configurée. Il monte en rampe, atteint le point médian, et redescend aussitôt — un triangle, pas un trapèze.

Le seuil est d < v_max² / a. Pour les nombres ci-dessus c’est 20 mm, donc chaque déplacement sous 20 mm plafonne sous 100 mm/s quoi que vous régliez.

Un graphe de la vitesse en fonction du temps pour trois déplacements sous les mêmes réglages de profil de 500 mm/s carrés et une vitesse de pointe de 100 mm/s. Un déplacement de 8 mm forme un triangle culminant à 63 mm/s après 0,25 seconde, sans jamais toucher la vitesse de pointe configurée montrée en pointillés. Un déplacement de 20 mm atteint juste 100 mm/s à son sommet. Un déplacement de 60 mm forme un trapèze avec une phase de croisière nette, durant 0,80 seconde.
Sous 20 mm, cet axe manque de distance avant de manquer de rampe. Monter la limite de vitesse ne fait rien à ces déplacements — seule monter l'accélération le fait. Télécharger le SVG

C’est pourquoi monter v_max sur une machine qui fait beaucoup de petits déplacements ne change rien de mesurable. Les déplacements n’étaient jamais limités en vitesse ; ils étaient limités en accélération. Une imprimante 3D faisant du remplissage dense est l’exemple quotidien.

Les coins restent discontinus

Le profil trapézoïdal a corrigé la vitesse. Regardez l’accélération et le problème a simplement déménagé.

À t = 0 l’accélération saute de 0 à 500 mm/s² instantanément. En haut de la rampe elle retombe à 0 instantanément. Chacun est un échelon d’accélération — et le taux de changement de l’accélération est l’à-coup, donc un échelon d’accélération est une impulsion d’à-coup infini.

L’infini n’est pas un nombre qu’une machine peut produire non plus. Ce qu’elle produit à la place :

  • Un cognement audible au début et à la fin de chaque rampe.
  • Une sonnerie — les courroies s’étirent, les accouplements s’enroulent, et la structure a de la souplesse, donc un changement brutal d’effort l’excite et elle oscille après. Sur une imprimante c’est le fantôme vu à côté des détails nets.
  • Des pics de couple qui font disjoncter des limites de courant que le chiffre de couple moyen disait correctes.
Quatre graphes empilés en fonction du temps comparant un profil trapézoïdal et un profil en courbe en S pour le même déplacement de 100 mm. Position : les deux sont des S lisses atteignant 100 mm, la courbe en S légèrement derrière. Vitesse : le trapèze a des rampes droites et des coins vifs, la courbe en S a des coins arrondis et finit 0,1 seconde plus tard. Accélération : le trapèze est un signal carré commutant instantanément entre plus et moins 500, la courbe en S est un trapèze montant entre eux. À-coup : la courbe en S est un signal carré borné, tandis que le profil trapézoïdal est dessiné comme quatre flèches d'impulsion non bornées aux coins.
Le même déplacement sous les deux profils. Chaque ligne est la dérivée de celle du dessus, et la discontinuité ne disparaît pas en dérivant — elle descend juste dans la pile jusqu'à devenir une impulsion. Télécharger le SVG

La courbe en S

Un profil en courbe en S limite l’à-coup autant que l’accélération. Au lieu de sauter à la pleine accélération, il y monte en rampe sur un temps d’à-coup t_j = a_max / j_max, tient, puis redescend en rampe. La courbe de vitesse gagne des coins arrondis — d’où le nom — et l’accélération devient un trapèze à son tour.

Un déplacement en S complet a sept phases : à-coup montant, tenue d’accélération, à-coup descendant, croisière, et l’image miroir pour s’arrêter.

Ce que cela coûte : exactement un t_j

C’est la partie à graver, parce qu’elle rend le compromis concret.

Quand les deux profils atteignent le même v_max et le même a_max, le déplacement en S prend plus longtemps d’exactement t_j — un temps d’à-coup, pas deux, et quelle que soit la distance du déplacement :

trapezoidal   T = v_max/a + d/v_max
S-curve       T = v_max/a + d/v_max + t_j          where t_j = a_max / j_max

Pour le déplacement calculé : 1,2 s deviennent 1,3 s. Une pénalité de 8 % sur ce déplacement, et un pourcentage moindre sur les plus longs, en échange d’un à-coup borné.

La raison pour laquelle c’est un et pas deux : le segment d’accélération de la courbe en S est plus long, donc il couvre plus de distance avant la croisière, donc la phase de croisière rétrécit et rend l’essentiel du temps. Seul le temps d’à-coup lui-même est irrécupérable.

Il y a une contrainte cachée là-dedans. L’accélération n’atteint a_max que si v_max ≥ a_max² / j_max. En dessous, le déplacement n’atteint jamais la pleine accélération non plus, et vous obtenez un profil d’accélération triangulaire — la même histoire de « manque de distance », une dérivée plus haut.

L’« à-coup » du micrologiciel d’imprimante n’est pas l’à-coup

Bon à savoir avant de régler quoi que ce soit, parce que le vocabulaire induit activement en erreur.

Le DEFAULT_XJERK du Marlin classique n’est pas la dérivée de l’accélération. C’est le changement instantané de vitesse maximal permis à une jonction entre deux déplacements, en mm/s — une tolérance de virage qui laisse le planificateur prendre un coin sans décélérer à zéro. Utile, mais une autre grandeur avec le mauvais nom et les mauvaises unités.

Le Marlin récent utilise plutôt la déviation de jonction, qui modélise le coin comme un arc de cercle d’une déviation donnée en mm. Klipper utilise square_corner_velocity — la vitesse à laquelle un coin à 90° est pris. Les trois sont des réponses à « comment ne pas s’arrêter à chaque coin ? », et aucun n’est une limite d’à-coup au sens physique.

Donc si un message de forum dit de baisser votre à-coup pour arrêter la sonnerie, vérifiez quelle grandeur est visée. Baisser la vitesse de jonction réduit le choc de virage ; cela ne borne pas da/dt dans un déplacement unique.

Votre PID va probablement bien

Si vous avez un axe en boucle fermée qui dépasse et oscille à chaque déplacement, l’instinct est de le désaccorder — baisser Kp, ajouter de la dérivée, combattre le dépassement.

Essayez d’abord de profiler la consigne.

Un échelon demande à un contrôleur PID une sortie infinie. Son terme d’erreur part de toute la distance du déplacement, le terme dérivé voit un saut instantané, et le terme intégral accumule pendant que la sortie est saturée et que l’axe ne peut répondre. Désaccorder rend le contrôleur mou partout pour survivre à un instant impossible.

Avec un profil, l’erreur reste petite pendant tout le déplacement, parce que la cible est quelque part où l’axe peut vraiment être. Les mêmes gains inutilisables contre un échelon sont souvent sages contre une rampe — et le contrôleur ne corrige plus que charge et frottement, ce pourquoi il est fait. Vous pouvez voir la différence dans le simulateur PID en comparant une consigne en échelon à une progressive.

Où vous rencontrez cela sur un vrai robot

  • Pas à pas. setMaxSpeed() et setAcceleration() d’AccelStepper donnent un profil trapézoïdal et rien d’autre. Pas d’option en courbe en S ; pour cela vous générez la consigne vous-même.
  • Servos amateurs. Un SG90 a un contrôleur interne invisible et non réglable, donc profiler signifie échelonner l’angle commandé progressivement au lieu d’écrire la cible en un appel — la même idée appliquée de l’extérieur.
  • Moteurs CC avec encodeurs. Profilez la consigne de position et laissez le PID de l’encodeur la suivre. C’est l’arrangement où profiler paie le mieux.
  • Robots mobiles. La même limite s’applique à linear.x sur un pont cmd_vel : commander un échelon de vitesse fait patiner les roues, ce qui corrompt l’odométrie. Limitez le taux du twist.
  • Imprimantes et CNC. Marlin, Klipper et GRBL planifient tous des déplacements trapézoïdaux avec une règle de virage entre eux.

Quand ça tourne mal

Symptôme Généralement
Le pas à pas perd sa position en charge, silencieusement Pas de profil — taux commandé au-delà du couple d’accrochage
La machine cogne au début et à la fin des déplacements Impulsion d’à-coup trapézoïdale ; il faut courbe en S ou accélération moindre
Sonnerie à côté des détails nets Résonance structurelle excitée par les échelons d’accélération
Les petits déplacements ignorent le réglage de vitesse Profil triangulaire — limité en accélération, pas en vitesse
Dépassement et oscillation à chaque déplacement Consigne en échelon dans une boucle fermée ; profilez avant de désaccorder
Monter l’accélération aide, puis soudain non Désormais limité en couple plutôt qu’en profil
L’odométrie dérive fort aux démarrages Patinage des roues dû à un échelon de vitesse ; limitez le taux de la commande

Profiler est la grosse amélioration la moins chère disponible pour la plupart des systèmes de mouvement amateurs, parce que cela coûte une fonction et aucun matériel. Le simulateur de bras robotique est un bon endroit pour voir pourquoi cela compte au bout d’un long segment, où un petit échelon d’accélération à la base devient un flottement visible au préhenseur. Sur un robot mobile, le même profil permet à un suiveur de trajectoire de ralentir pour un virage plus serré que son rayon de braquage, au lieu de ne pas le suivre.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références