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.
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.
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()etsetAcceleration()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.xsur 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.
- ProjetConstruire un robot de pick-and-place qui sait quand il rate
- ProjetConstruire un traceur à stylo : deux axes pas à pas qui arrivent
- Parcours d'apprentissageCommande des moteurs pour robots : un parcours d'apprentissage
- Parcours d'apprentissageMouvement de précision avec pas à pas : un parcours
- Parcours d'apprentissagePréhenseurs et effecteurs : attrapez-le vraiment
- Parcours d'apprentissageSuivi de trajectoire pour robots : un parcours d'apprentissage
- Parcours d'apprentissageTransmission holonome : aller de côté sans tourner
Pour aller plus loin