Boucles de contrôle¶
Les boucles de contrôle sont des logiciels utilisés pour faire fonctionner des systèmes de transmission de puissance (tels qu’un châssis ou un chariot linéaire) de manière rapide et contrôlée. Non seulement les boucles de contrôle vous permettent de faire fonctionner des mécanismes rapidement sans craindre de perdre le contrôle, mais dans de nombreux cas, elles contribuent à préserver la longévité des mécanismes en réduisant les changements rapides de la tension appliquée au moteur.
Qu’est-ce qu’une erreur ?¶
La première chose à définir lorsque l’on parle de boucles de contrôle est le concept d’erreur.
L’erreur est définie comme la différence entre l’endroit où vous êtes et l’endroit où vous voulez être. Par exemple, supposons que vous demandiez à votre châssis de rouler à une vitesse de 30 pouces par seconde, mais qu’en réalité, à un moment donné, le châssis roule à une vitesse de 28 pouces par seconde. Puisque \(30-28=2\), l’erreur de vitesse de la transmission à ce moment \(T\) est de 2 pouces par seconde. En d’autres termes, à un moment \(t=T\), \(e(t)=2\).
PID¶
Un contrôleur PID (ou contrôleur proportionnel intégral dérivé) est une boucle de contrôle qui utilise uniquement l’erreur pour contrôler le système. Le PID est une forme de boucle de contrôle à rétroaction, ou commande en boucle fermée. Cela signifie que des données sur la variable que vous contrôlez sont nécessaires pour que la boucle contrôle cette variable. Dans ce cas, des informations sur l”erreur du système sont nécessaires pour contrôler le système à l’aide d’un contrôleur PID.
Le calcul optionnel¶
L’équation suivante représente la définition mathématique rigoureuse de la sortie d’un contrôleur PID \(f\) à un moment donné \(t\) :
où \(K_p\), \(K_i\), et \(K_d\) sont des constantes et \(e(t)\), comme indiqué précédemment, est l’erreur dans le système.
Si vous n’avez aucune expérience en calcul, ne vous inquiétez pas ; bien que le PID soit fondamentalement ancré dans le calcul, vous n’avez besoin d’aucune expérience en calcul pour pouvoir le comprendre, mais seulement d’une connaissance de base de l’algèbre. Cependant, nous vous conseillons vivement de lire le reste de la section, même si vous n’avez pas d’expérience en calcul, car la formule seule ne vous explique pas pourquoi elle fonctionne.
Simplification de la formule PID¶
Voici une version simplifiée de la formule PID : \(f(t)=K_p P(t)+K_i I(t)+K_d D(t)\)
Nous avons simplement pris la formule complète et remplacé une partie des termes par des fonctions : \(P(t)\), \(I(t)\), et \(D(t)\).
Le terme proportionnel¶
La première composante de la fonction, \(K_p P(t)\), est de loin la plus simple et la plus facile à comprendre, car \(P(t) = e(t)\). Pour les besoins de l’exemple, supposons que \(K_i=0\) et \(K_d=0\) (un contrôleur PID avec seulement une constante proportionnelle est connu sous le nom de contrôleur P). Comment le système va-t-il se comporter ? Si l’erreur est importante, la sortie sera importante. De même, si l’erreur est faible, la sortie sera faible. Idéalement, avec suffisamment de temps, le système se rapproche toujours de sa destination, en supposant que \(K_p\) soit du bon signe.
Imaginons que nous appliquions cela à un châssis. Vous voulez parcourir une distance \(D\), et vous décidez de régler la puissance de vos moteurs à l’aide d’un contrôleur P. Dans ce cas, votre erreur est la distance qui sépare le robot de l’endroit désiré. Dans ce cas, votre erreur est la distance qui sépare le robot de l’endroit désiré. Lorsque vous commencez à avancer, votre erreur est importante et vous avancez rapidement, ce qui est souhaitable. Après tout, vous ne craignez pas de dépasser la cible si vous en êtes loin.
Mais au fur et à mesure que la distance entre le robot et la cible se rapproche de 0, vous commencez à ralentir, ce qui vous permet de mieux contrôler le robot. Lorsque l’erreur est nulle, idéalement, le robot s’arrête et vous avez atteint votre destination. Si vous dépassez la cible, l’erreur devient négative et le robot revient sur ses pas, répétant le processus.
Le terme dérivé¶
Ce terme, \(K_d D(t)\), est destiné à amortir le taux de variation de l’erreur. En d’autres termes, il tente de maintenir l’erreur constante. Comment cela se passe-t-il ?
Eh bien, pour ceux d’entre vous qui savent calculer, \(D(t)=\frac{de(t)}{dt}\). Pour ceux qui n’ont pas d’expérience en calcul, cela représente la vitesse à laquelle l’erreur change. Graphiquement, \(D(t)\) est simplement la pente de l’erreur à un moment donné \(t\).
Cette pente peut être calculée en suivant l’erreur au cours des itérations successives de la boucle de contrôle. Une itération a lieu au temps \(t_n\) avec une erreur de \(e(t_n)\). À l’itération suivante, le temps est \(t_{n+1}\) avec une erreur de \(e(t_{n+1})\). Ainsi, pour trouver \(D(t)\), il suffit de trouver la pente de \(e(t)\) à partir de ces deux points.
Le terme intégral¶
Il faut admettre que le terme intégral est le moins important pour les boucles de contrôle PID de FTC®. Avec un \(K_p\) et un \(K_d\) correctement réglés, vous pouvez souvent mettre \(K_i\) à 0 et en rester là.
Cependant, il peut encore être utile dans certains cas. Tout comme le terme dérivé, le terme intégral vise à corriger les dépassements. Si le système pense avoir atteint sa destination, il s’arrêtera, même si, en fait, l’erreur n’est pas encore 0. Peut-être le moteur ne reçoit-il plus assez de puissance pour se déplacer. Si l’on dispose de suffisamment de temps, le terme intégral augmentera la sortie (dans ce cas, la puissance du moteur), ce qui entraînera un mouvement vers la destination.
Pour l’expliquer sans calcul, le terme intégral additionne essentiellement l’erreur sur un intervalle de temps spécifique. Pour ce faire, l’erreur de chaque itération de la boucle est ajoutée à une variable (dans ce cas, \(I(t)\)).
Cependant, l’addition de l’erreur de cette façon a un effet secondaire malheureux : plus la boucle prend de temps pour compléter une itération, plus cette somme augmente lentement, ce qui n’est évidemment pas souhaitable, car nous ne voulons pas que le décalage affecte la façon dont le robot se déplace. Pour compenser cela, avant que l’erreur ne soit ajoutée à \(I(t)\), elle est multipliée par le temps que la boucle précédente a pris pour se terminer, ou \(t_{n+1}-t_n\), empêchant ainsi le décalage de rendre la somme du système plus lente.
Supposons que le robot s’arrête avant la cible. La combinaison P et D n’est pas assez forte pour le faire avancer jusqu’à sa destination. Vous pouvez soit ajuster \(K_p\) et \(K_d\) pour compenser (cela est recommandé), soit ajouter le terme intégral pour augmenter le rendement (cela fonctionne aussi, mais demande plus d’attention et d’ajustements pour obtenir le même résultat).
Pseudocode PID¶
while True:
current_time = get_current_time()
current_error = desire_position-current_position
p = k_p * current_error
i += k_i * (current_error * (current_time - previous_time))
if i > max_i:
i = max_i
elif i < -max_i:
i = -max_i
D = k_d * (current_error - previous_error) / (current_time - previous_time)
output = p + i + d
previous_error = current_error
previous_time = current_time
Réglage d’une Boucle PID¶
La chose la plus importante à savoir lors du réglage d’une boucle PID est la manière dont chacun des termes affecte la sortie. Cela vous permet de voir quels gains doivent être ajustés.
Par exemple, si la cible n’est pas atteinte mais que le point de consigne commence à osciller autour de la cible, cela signifie que le gain D est insuffisant. Si la cible est finalement atteinte, bien que très lentement, cela signifie que le gain P est insuffisant ou que le gain D est trop élevé.
En résumé, la variable P fait tendre l’erreur vers zéro, la variable I corrige l’erreur en régime permanent et la variable D amortit les effets de la variable P, d’autant plus que l’erreur se rapproche de zéro, ce qui empêche le dépassement.
La méthode la plus courante pour régler un contrôleur PID est la suivante :
Régler les gains I et D à zéro
Augmenter le gain P jusqu’à ce qu’il y ait des oscillations autour de la cible.
Augmenter le gain D jusqu’à ce qu’il n’y ait plus de dépassement.
En cas d’erreur en régime permanent, augmenter le gain I jusqu’à ce que l’erreur soit corrigée.
Il est important de noter que la plupart des systèmes n’ont pas besoin d’un contrôle I et d’un contrôle D. En général, les systèmes sans beaucoup de friction n’ont pas besoin d’un terme I, mais auront besoin d’un contrôle D plus important. Les systèmes avec beaucoup de friction, en revanche, n’ont généralement pas besoin d’un contrôle D parce que la friction facilite la décélération, mais ont besoin d’un contrôle I parce que la friction empêche le système d’atteindre la cible autrement.
Pour une explication plus approfondie, cliquez ici
Contrôleur PID intégré¶
Pour les situations où il est nécessaire de contrôler la vitesse ou la position d’un seul moteur, le contrôleur PID intégré peut être utilisé. Le PID peut être activé en changeant le mode d’exécution en RUN_USING_ENCODER.
Indication
Beaucoup se méprennent sur l’utilisation de RUN_USING_ENCODER, beaucoup pensent qu’il est nécessaire d’utiliser ce mode pour que les encodeurs fonctionnent, mais ce n’est pas vrai. Au lieu de cela, RUN_USING_ENCODER active le retour de vitesse en utilisant l’encodeur. Si vous utilisez un contrôleur PID externe tel que celui que vous implantez, il est généralement recommandé d’utiliser RUN_WITHOUT_ENCODER.
Pour la documentation officielle sur le contrôleur PID intégré, voyez ici
Débogage du Contrôleur PID Intégré¶
Problème |
Solution |
|---|---|
Le moteur passe à la vitesse maximale indépendamment du point de consigne de la vitesse. |
La plupart du temps, cela se produit lorsque l’une des deux choses suivantes se produit : #1 : Votre encodeur n’est pas correctement connecté.
#2 : Votre moteur va dans la mauvaise direction.
|
Le moteur n’atteint pas sa vitesse maximale avec |
Utilisez la méthode |
Taux d’échantillonnage du contrôleur PID¶
Pour les équipes qui souhaitent obtenir les meilleures performances de leur contrôleur PID, il est essentiel de prendre en compte le taux d’échantillonnage du contrôleur. Le taux d’échantillonnage est le moment où le contrôleur met à jour sa sortie en fonction des nouvelles données du capteur. Des taux d’échantillonnage plus élevés permettent un contrôle plus stable et l’utilisation de coefficients PID plus importants pour réduire le temps de stabilisation. Voir cette vidéo pour voir comment le taux d’échantillonnage affecte la stabilité dans un exemple pratique de contrôle de moteur. Le contrôleur PID intégré est verrouillé à un taux de rafraîchissement de 20 Hz (taux d’échantillonnage de 50 ms). De nombreuses équipes FTC optimisent les boucles de leurs robots pour qu’elles fonctionnent jusqu’à 80 Hz, ce qui permet d’obtenir un contrôle beaucoup plus stable avec un PID externe.
Contrôle par Anticipation¶
Le contrôle par anticipation est une méthode de contrôle dite « en boucle ouverte ». C’est l’opposé du contrôle en boucle fermée et la principale différence est que le contrôle par anticipation n’utilise pas activement les capteurs pour contrôler le système. Au lieu de cela, il « prédit » l’entrée souhaitée sur la base d’un modèle.
En règle générale, l’anticipation est utilisée pour contrôler les taux de changement ou pour lutter contre les perturbations connues de votre système.
L’anticipation est très puissante parce qu’elle est immunisée contre le bruit ou d’autres erreurs de capteur. En effet, elle ne mesure pas activement le système, mais prédit l’entrée souhaitée. Toutefois, cela signifie également qu’elle n’est pas très efficace pour corriger les erreurs. C’est pourquoi il est souvent utilisé en conjonction avec un contrôleur en boucle fermée tel que le PID.
Modèle d’anticipation Kv Ka¶
Le modèle d’anticipation le plus courant et celui utilisé par des bibliothèques telles que road-runner est le modèle d’anticipation Kv-Ka :
Où \(K_v\) est le gain de vitesse, \(K_a\) est le gain d’accélération, et \(f(t)\) est la sortie feedforward envoyée à vos moteurs.
Ces gains peuvent être estimés en donnant au contrôleur une série de rampes d’entrée (telles que celles calculées avec un profil de mouvement), en mesurant la sortie, puis en modifiant ces gains jusqu’à ce que le robot atteigne le mouvement souhaité.
Note
Les gains varieront en fonction de la masse du robot, de la friction et d’autres facteurs. Il est recommandé de réestimer ces gains chaque fois que vous apportez une modification importante à votre robot.
Pseudocode anticipation Kv Ka¶
while True:
targetVelocity = getTargetVelocity(time)
targetAcceleration = getTargetAcceleration(time)
output = targetVelocity * Kv + targetAcceleration * Ka;
Friction statique d’anticipation¶
Dans tout système, il y a forcément une certaine quantité de friction statique. Cela signifie que le mécanisme du robot ne bougera pas tant qu’une certaine puissance n’est pas appliquée. Ce phénomène peut être modélisé par l’ajout d’un terme d’anticipation constant dans la direction où vous souhaitez vous déplacer.
while True:
error = desire_position - current_position;
sign = signum(error) # sign of error, will be -1, 0, or 1
output = sign * staticFriction + PID(error); # PID Controller + Friction Feedforward
Gravity Compensated Feedforward¶
In Gravity Compensation we derive the effect of gravity upon an arm as \(F_g = g\cos{\theta}\). Here we can use that with the following logic.
while True:
error = desire_position - current_position; # no effect on gravity compensation
current_angle = (TICKS_AT_ZERO - current_tick) * DEGREE_PER_TICK
output = PID(error) + cos(radians(current_angle)) * kF
This code uses a kF constant to approximate how much power the arm needs to counteract gravity. In theory, this is something related to gravity multiplied by the rotational moment of inertia of your arm. Calculating this is impractical and kF is instead found empirically. This can be done by setting the gains on the PID to 0, and increasing kF until the arm can hold itself up at any position. If your arm is still falling, increase kF, and if your arm is moving upwards, decrease kF. FTC team 16379 Kookybotz has an excellent video on Arm programming, where they demonstrate how to increase kF.
Profils de mouvement¶
Astuce
Les profils de mouvement ne sont pas un type spécifique de boucle de contrôle, mais plutôt une technique qui fonctionne bien en combinaison avec d’autres boucles de contrôle telles que PID et feedforward.
Le profilage de mouvement est une technique popularisée par FRC® et qui commence à faire son chemin vers FTC. Un profil de mouvement est une fonction utilisée pour modifier la vitesse d’un système de transmission de puissance de manière contrôlée et cohérente en modifiant la vitesse souhaitée progressivement plutôt qu’instantanément.
Illustrons cela par un exemple : supposons que vous souhaitiez que votre châssis, qui est initialement immobile, avance à pleine vitesse. D’ordinaire, vous devriez mettre tous les moteurs du châssis à pleine puissance dans le code. Cependant, cela peut poser des problèmes, car même si vous demandez aux moteurs d’avancer à pleine vitesse instantanément, le châssis met du temps à atteindre sa vitesse maximale. Cela peut entraîner des mouvements incontrôlés qui risquent de rendre l’autonomie moins cohérente et, peut-être plus important encore, d’endommager les mécanismes.
Le profilage des mouvements tente de résoudre ce problème.
Avantages¶
Des mouvements plus contrôlés et plus prévisibles
Réduit les variations rapides de la tension appliquée au moteur
Inconvénients¶
Peut être plus lent
Il existe deux principaux types de profils de mouvement : les profils trapézoïdaux et les profils courbes en S. Les profils trapézoïdaux accélèrent le système à un rythme constant, tandis que les profils en courbe S supposent que le jerk (la vitesse à laquelle l’accélération change) est constant. Étant donné que les profils en courbe S ne sont pas optimaux pour le contrôle des trajectoires 2D (comme la conduite) et qu’ils existent pour réduire le glissement (qui ne se produit généralement que lors de la conduite en FTC), les profils trapézoïdaux sont recommandés pour la plupart des applications de FTC.
Les profils trapézoïdaux tirent leur nom de la forme du graphique de la vitesse en fonction du temps :
Il s’agit des « fonctions magiques » pour la vitesse et l’accélération dans le temps auxquelles il a été fait allusion dans la section sur l’anticipation.¶
Voici un pseudo-code pour un profil trapézoïdal :
while True:
current_velocity = get_current_velocity()
current_time = get_current_time()
direction_multiplier = 1
if position_error < 0:
direction_multiplier = -1
# if maximum speed has not been reached
if MAXIMUM_SPEED > abs(current_velocity):
output_velocity = current_velocity + direction_multiplier * MAX_ACCELERATION * (current_time - previous_time)
output_acceleration = MAX_ACCELERATION
#if maximum speed has been reached, stay there for now
else:
outputVelocity = MAXIMUM_SPEED
outputAcceleration = 0
#if we are close enough to the object to begin slowing down
if position_error <= (output_velocity * output_velocity) / (2 * MAX_ACCELERATION)):
output_velocity = current_velocity - direction_multiplier * MAX_ACCELERATION * (current_time - previous_time)
output_acceleration = -MAX_ACCELERATION
previous_time = current_time
Les résultats du pseudocode ci-dessus sont ensuite utilisés dans une boucle d’anticipation et/ou une boucle PID pour contrôler la position du système de manière régulière et prévisible.
Un exemple plus avancé des mathématiques pour la génération du profil de mouvement tel qu’il est utilisé dans la bibliothèque Road Runner peut être trouvé dans ce Jupyter Notebook.