NXT-G, leçon n° 14 : Le PID, un contrôleur intelligent

Pour expliquer certains fonctionnements, principes et fondements.
Avatar de l’utilisateur
roboleo
Level 8
Level 8
 
Messages: 955
Localisation: Hauts de Seine
expertnxt

Messagepar roboleo » Mer Fév 24, 2010 2:49 pm

Un contrôleur PID pour les robots Lego® Mindstorms® NXT


Nous allons aborder ici un aspect particulier du contrôle des robots, notamment la relation entre capteurs et moteurs, c'est-à-dire la manière dont ils interagissent les uns sur les autres.

PID désigne un dispositif, un organe de contrôle informatisé utilisé dans l'industrie pour une grande variété de machines incluant les véhicules, les robots et même les engins balistiques.
Donner ici une définition mathématique du PID est assez difficile pour ceux qui ne disposent pas de connaissances suffisantes et ne maîtrisent pas les calculs, mais une bonne compréhension du dispositif est nécessaire pour utiliser efficacement un PID.

Cette leçon décrit la façon de créer un PID destiné aux robots Lego Mindstorms NXT programmés en NXT-G version 1.1..
Elle est inspirée d'un article rédigé par James Sluka et publié le 29 septembre 2009 sur le blog NXT STEP
http://thenxtstep.blogspot.com/2009_09_01_archive.html

CHAPITRE 1

Le plus simple est de prendre un exemple, et dans notre cas, ce sera un Robot suiveur d'un tracé.
Une fois créé, le même PID peut-être employé moyennant quelques modifications mineures, pour n'importe quelle autre action, comme se diriger d'une manière rectiligne, ou grimper une rampe, ou encore se balancer en utilisant seulement 2 roues en contact avec le sol.

Pour éviter de se lancer dans des notions trop compliquées de calculs, et pour que cette approche soit à la portée de tous, je partirai d'un concept simple faisant appel aux mathématiques d'une manière très limitée.
Un PID est un outil assez simple, et sa description est facilement comprise par tous ceux qui calculent sans trop de difficultés.

Commençons par fixer les éléments de base d'un robot suiveur de tracé.

fig.1
Image

Ce robot schématiquement représenté, dispose de 2 roues arrières A et C entraînées chacune par un moteur.
à l'avant une roue folle. Ce dispositif permet une direction différentielle en jouant sur la vitesse des 2 moteurs. Egalement à l'avant, un capteur photosensible pointant vers le bas, de telle sorte qu'il ne "voit" que le support sur lequel il est posé. La flèche indique le sens de déplacement du robot.
L'objectif recherché est de contraindre le robot à suivre un tracé matérialisé sur le support par une large bande noire.
Ce robot, "suiveur de tracé" peut-être construit avec un capteur photosensible ou plusieurs si vous en disposez. Plus vous installerez de capteurs et meilleure sera cette conduite.
Dans notre cas nous nous limiterons à un seul capteur, car même équipé de la sorte, notre robot pourra suivre le tracé avec précision, quelle que soit sa forme, courbes comprises. La seule chose que vous perdrez, c'est la vitesse de déplacement.

La première astuce que nous utiliserons, et qui est sans rapport avec un PID, sera de suivre non pas le tracé, mais le bord du tracé. Pourquoi?
Parce que, si nous suivons le tracé lui-même (bande noire) alors que le robot s'écarte de la ligne et que le capteur "voit blanc", nous ne savons pas de quel côté du tracé nous nous trouvons. Sommes nous à droite ou à gauche? Mais si nous suivons le bord du tracé, alors nous pouvons savoir de quel côté nous nous trouvons quand le robot dévie. Si le capteur "voit blanc" alors nous savons qu'il s'agit du bord gauche. Et s'il "voit noir" nous savons qu'il se trouve à droite du bord. On appelle cela un "suiveur de tracé gauche" (left hand line follower).
Nous avons maintenant besoin de connaître les valeurs fournies par le capteur quand il "voit blanc" et quand il "voit noir". D'ordinaire, un Capteur Photosensible (non calibré) donne une valeur de 50 pour un "blanc" et 40 pour un "noir" (échelle de 0 à 100).
Si on représente ces valeurs sur une ligne graduée, on pourrait résumer les lectures de la manière suivante:

fig.2
Image

Bien, on a donc coupé la plage de valeurs en 2 tranches égales, et on peut dire que si la valeur relevée est inférieure à 45, le robot tournera à gauche, et si elle est supérieure à 45, il tournera à droite.
Pour l'instant la manière de virer ne sera pas abordée. Disons que sur une ligne plutôt rectiligne les virages seront mieux maîtrisés. Pour un tracé présentant de nombreuses courbes, il faudra tenir compte des virages plus serrés. Dans le cas d'une trajectoire rectiligne, on prévoira un niveau de puissance égal à 50% pour la roue rapide, et 20% pour la roue lente. Pour des virages plus serrés sur une trajectoire courbe, on choisira un niveau de 30% pour la roue rapide et un freinage pour la roue lente. Quels que soient les niveaux de puissance, il seront identiques pour les 2 virages (droite et gauche); il faudra seulement inverser les valeurs des 2 moteurs.
Cette manière de suivre un tracé manque d'élégance. Elle peut paraître suffisante pour des trajectoires à peu près rectilignes, mais quand il s'agit de trajectoires sinueuses, il faut demander au robot de "serpenter" le long du tracé puisqu'il ne sait faire que 2 choses: tourner à gauche ou à droite. De plus, ce dispositif ralenti le temps de parcours et offre un spectacle désolant.

Dans cette approche le robot ne suit jamais une trajectoire rectiligne, même s'il est parfaitement aligné sur le bord du tracé. Cela ne paraît pas très efficient, n'est-ce pas?

Procédons autrement. Au lieu de diviser la ligne graduée (fig.2) en 2 parties, divisons la en 3.
fig.3
Image

A présent, si le niveau lumineux est inférieur à 43, nous voulons que le robot tourne à gauche. Si le niveau est entre 44 et 47, le robot doit aller tout droit. Enfin si le niveau est supérieur à 47, le robot doit tourner à droite. Cette relation peut-être facilement établie en programmation NXT-G à l'aide de 2 commutateurs imbriqués (2 tests à faire et non 3).
Cette approche fonctionne mieux que la précédente. Nous savons maintenant que le robot peut avancer quelquefois en ligne droite.

Si le partage de la ligne des valeurs en 3 régions donne de meilleurs résultats qu' en 2 régions, qu'en est-il si on augmente ce nombre découpage?
C'est ici qu'on aborde le PID.

1 - Le "P" dans PID: P pour Proportionnel

Que se passe-t-il donc si on augmente le nombre de divisions dans la ligne de valeurs?
Il faut d'abord s'interroger sur le sens à donner au mot "Tourne" lorsque le nombre de régions dépasse 3. Dans la première approche, le robot ne pouvait faire que 2 choses; tourner à droite ou tourner à gauche. Les virages sont strictement identiques, mais de sens opposé. Dans la 2ème approche, on a ajouté "tout droit" aux deux précédents. Mais si nous dépassons 3 régions, il nous faut plus de "sortes" de 'Tourne', je dirai de Virages.
Fig.4
Image

Dans le cas suivant, nous sommes en présence de ce que nous appellerons une ligne Proportionnelle. C'est à quoi ressemblerait la ligne bleu en escalier, lorsque le nombre de régions (gradins) augmenterait indéfiniment. Dans ce cas, les virages se produisent avec douceur entre ces deux limites. Si la lecture du capteur dit que l'on est proche de la ligne, alors on exécute un petit virage. Si l'on se trouve à une plus grande distance de cette ligne, alors le virage sera plus important.
Fig.5
Image

Proportionnel signifie qu'il existe une relation linéaire entre 2 variables.

Pour comprendre ces "plus de sortes"de 'Tourne', nous allons utiliser une représentation graphique dans un système d'axes en X et Y. L'axe horizontal (axe des X) supporte les valeurs lues du niveau de luminosité tel qu'il est représenté. L'axe Vertical (Axe des Y) sera celui des Virages.
Pour simplifier, nous dirons que proportionnel signifie qu'il existe une relation entre ces 2 variables, dont la représentation graphique est une ligne droite.
Vous devez sans doute savoir que l'équation algébrique d'une ligne droite se présente sous la forme :
y = mx + b
Où y est la valeur verticale (positive ou négative) sur l'axe des Y, et x la valeur correspondante sur l'axe des X. La constante m est la pente de cette droite et b est la valeur de Y quand cette droite coupe l'axe des Y, c'est-à-dire quand x est égal à zéro.
La pente se définit comme une valeur résultant de la division de l'écart y (variation de y) par l'écart x (variation de x) correspondante, de 2 points rapprochés pris sur cette droite.
Si vous ne savez pas grand choses sur les droites (ou si vous avez oublié), je ferai donc un petit rappel en simplifiant le graphique et l'équation. D'abord, nous remettrons le centre de notre droite de valeurs lues (l'Axe des X) au zéro. C'est facile à faire. Aux valeurs extrêmes 40 et 50 du niveau lumineux, nous soustrairons juste 45 (qui est la moyenne de 40 et 50, soit (40+50)/2) de toutes les valeurs relevées. Nous appellerons ce résultat l'erreur. Ainsi, si la valeur est 47 nous soustrairons 45 et obtiendrons une erreur = 2.
L'erreur nous donne la valeur séparant notre position jusqu'au bord du tracé . Si le capteur est exactement sur la ligne notre erreur est égale à zéro puisque nous soustrayons 45 de toutes nos lectures. Si le capteur est entièrement dans le blanc notre erreur est égale à + 5, et entièrement dans le noir égale à - 5.
Fig.6
Image

Dans ce graphique, on a remplacé les valeurs de niveau lumineux de l'axe des X par une nouvelle échelle qui est celle des erreurs. Et comme cette droite coupe l'axe des Y à l'origine c'est-à-dire au point zéro; l'équation se simplifie et devient:
y = mx
Et si on adopte une expression plus générale, on peut écrire:

Virage = m * erreur

Mais, nous n'avons pas encore défini la signification de l'axe des virages. Aussi pour l'instant contentons-nous de dire que la plage des valeurs se situe entre -1 ( virage franc à droite) et +1 (virage franc à gauche); le zéro signifiant tout droit. La pente de cette droite peut-être calculée en utilisant les coordonnées des 2 points rouge situés aux extrémités de la droite:

pente = m = (écart des y) / (écart des x) = ( 1 - (-1)) / (-5 -(5) = - 2/10 = - 0,2 soit 0,20 en valeur absolue.

La pente est une contante proportionnelle qui est le facteur multiplicateur à appliquer à l'erreur (valeur de x) pour obtenir celle du virage (valeur de y).
Dans le vocabulaire adopté pour exprimer le PID, cette constante est appelée " K " (peut-être pour rappeler la notion de constante). On peut l'assimiler à un facteur de conversion qui, à partir d'un niveau de luminosité ou d'une erreur dans notre cas, produit une autre valeur comme celle d'un virage. Très simple et très puissant!

Donc, en utilisant ces nouvelles conventions, on peut écrire l'équation de cette droite ainsi:

Virage = K * (erreur)

La valeur Virage est considérée comme une sortie de notre Contrôleur P, et désignée par le terme P.

Vous avez remarqué que dans le dernier graphique, la ligne erreur est dans la plage -5 et +5. En dehors de cette plage, il n'est plus possible de déterminer l'éloignement du capteur par rapport au tracé. Tous les "blanc" sont identiques si le capteur n'est pas en mesure de détecter un "noir". Souvenez-vous que cette plage est arbitraire, car elle est déterminée par les caractéristiques du capteur photosensible, les couleurs des supports, etc…. Dès que le capteur s'éloigne trop du tracé, il relève des valeurs constantes ; cela signifie que les lectures ne sont plus proportionnelles à l'erreur.
La déviation par rapport au tracé ne peut être appréciée que si le capteur est assez proche de ce tracé. C'est à l'intérieur de cette plage que les valeurs lues sont proportionnelles à la distance. A l'extérieur de cette plage, il nous indique la bonne direction, mais l'ampleur est faussée. La lecture des valeurs, ou l'erreur, est plus petite qu'elle ne l'est en réalité, et ne fournit donc pas une réponse satisfaisante.

Dans la définition du PID, la plage qui nous intéresse et qui donne une réponse proportionnelle est appelée "Plage Proportionnelle". Ce concept est très important dans la notion du PID.
Pour revenir à notre suivi de tracé, la Plage Proportionnelle pour le Capteur Photosensible se situe entre 40 et 50, et pour l'erreur elle est comprise entre -5 et +5. On peut également noter que les servomoteurs ont eux aussi une plage proportionnelle, de -100 (pleine puissance arrière) à +100 (pleine puissance avant).

Deux remarques importantes sur la Plage Proportionnelle:
(1) Vous souhaitez une plage proportionnelle aussi grande que possible.
La plage proportionnelle du Capteur Photosensible est assez petite, cela signifie que le Capteur doit être assez près du bord du tracé pour obtenir des valeurs proportionnelles. Plus exactement, la grandeur de la plage dépend essentiellement de la hauteur du capteur par rapport au plan du tracé. Si le capteur est très proche du plan, disons 2 mm environ, il émet un très petit cercle lumineux. Un petit déplacement du capteur produira une variation de l'erreur de -5 à +5, correspondant à notre plage proportionnelle.
Vous pourriez dire que le capteur a "des vues étroites" et il peut seulement voir une très petite partie du plan. Le capteur doit être très près du bord du tracé pour obtenir une lecture qui n'est pas "blanc" ou "noir". Si le capteur est positionné plus haut, la tache lumineuse s'étale en un plus grand cercle sur le plan. À une hauteur de 12 mm environ, le capteur émet une tache lumineuse de même dimension. A cette hauteur, la plage proportionnelle est trop grande, le capteur devant se maintenir entre +/- 12 mm de part et d'autre du bord du tracé pour obtenir un résultat proportionnel satisfaisant.
Malheureusement, il y a deux inconvénient pour une position trop élevée du capteur. D'abord le capteur "voit" et réagit à l'éclairage ambiant beaucoup plus que s'il se trouve en position basse. Ensuite, il discerne moins bien les nuances entre le noir et le blanc. A une certaine distance, il donnera une même lecture au noir et au blanc.

(2) À l'extérieur de la plage proportionnelle le contrôleur provoquera un déplacement dans une direction correcte mais sous-évaluée. La réponse proportionnelle du contrôleur est limitée par la plage proportionnelle.

2 - De P à niveaux de puissance réelle du moteur.

Comment pouvons-nous mettre en oeuvre les virages ? Quel doit être le niveau de puissance des moteurs ?
Une façon d'envisager les virages consiste à définir "un Niveau de Puissance Cible", que nous appellerons "Tp". Tp est le niveau de puissance des deux moteurs quand on suppose que le robot se déplace tout droit, ce qu'il fait quand l'erreur = 0.
Quand l'erreur n'est pas nulle nous utilisons l'équation virage = K * (l'erreur) pour calculer le changement des niveaux de puissance des deux moteurs. Un moteur obtiendra un niveau de puissance égal Tp+Virage, l'autre moteur obtiendra un niveau de puissance égal à Tp-Virage.
A noter que notre erreur est comprise entre -5 à +5, ce qui signifie que le Virage peut être positif ou négatif c'est-à-dire, correspondant aux virages dans des sens opposés. Il se trouve que c'est exactement ce que nous recherchons puisqu'il mettra automatiquement un moteur en accélération et l'autre en ralentissement.
Un moteur (nous supposerons que c'est le moteur gauche du robot raccordé au port A) obtiendra toujours la valeur de Tp+Virage comme niveau de puissance. L'autre moteur (le droit raccordé au port C) obtiendra toujours la Tp-Virage comme c'est le niveau de puissance. Si l'erreur est positive, alors Virage est positif et Tp+Virage est plus grand que Tp; la vitesse du moteur gauche augmentera alors que celle du moteur droit diminuera.
Si l'erreur change de signe et devient négative (signifiant qu'on a franchi le bord du tracé et voit l'autre couleur), alors Tp+Virage est maintenant plus petit que Tp, la vitesse du moteur gauche augmente et celle du moteur droit diminue, puisque Tp-Virage est plus grand que Tp (rappelez-vous que le produit de 2 valeurs négatives donne une valeur positive). Simple, non?
Poursuivons.

3 - Pseudo Code pour un Contrôleur P

D'abord nous devons évaluer les valeurs lues par le capteur de lumière pour le blanc et le noir. De ces deux valeurs nous pouvons calculer la moyenne, c'est-à-dire quelle valeur soustraire à celle d'une lumière brute lue pour convertir l'ensemble en valeur d'erreur. La moyenne est la demi-somme des lectures "blanc" et "noir". Pour la simplicité on supposera que cette moyenne a déjà été mesurée et stockée dans une variable appelée moyenne. (Une démarche intéressante consisterait à faire calculer la moyenne par le robot à partir des mesures lues niveaux blanc et noir).

Nous aurons aussi besoin d'un emplacement de stockage pour la constante K, nous l'appellerons Kp (de constant pour le contrôleur proportionnel). Ainsi qu'une valeur de défaut pour Kp.
Il y a plusieurs façons pour l'obtenir, par tâtonnement puis corrections successives de l'erreur. Ou alors, vous pouvez essayer d'évaluer une valeur basée sur les caractéristiques du capteur et du robot.
Nous appliquerons cette dernière méthode. Nous utiliserons un Tp (niveau de puissance cible) de 50: quand l'erreur est égale à zéro les deux moteurs fonctionneront au niveau 50. La plage d'erreur se situe entre -5 à +5. Nous supposerons que la puissance variera de 50 à 0 quand l'erreur passera de 0 à -5. Cela signifie que Kp (la pente souvenez-vous, la variation de y divisée par la variation de x correspondante) est:

Kp = (0 - 50) / (-5 -0) = 10

Nous utiliserons la valeur 10 pour Kp, afin de convertir une valeur erreur en valeur Virage. En d'autres termes: une variation de 1 de l'erreur, se traduira par une augmentation de 10 de la puissance d'un moteur. L'autre moteur verra sa puissance diminuer de 10.

Aussi, en pseudo-code, c'est-à-dire, sous forme d'énumérations logiques d'actions, indépendamment du langage de programmation utilisé ( NXT-G ou autre), nous pourrions écrire:

Kp = 10                               ! Initialisation de trois variables
moyenne = 45
Tp = 50
Loop forever ! Début de Boucle pour toujours
   LightValue = valeurlueducapteur     ! Quelle est la valeur lue courante du capteur?
   erreur = LightValue - moyenne     ! calcul de l'erreur par soustraction de la moyenne.
   Virage = Kp * erreur ! le terme "P", de combien veut-on modifier la puissance du moteur
   powerA = Tp + Virage                 ! niveau de puissance pour le moteur A
   powerC = Tp - Virage                 ! niveau de puissance pour le moteur C
   MOTOR A direction=forward power=powerA ! transmet la commande avec la nouvelle valeur de puissance dans un bloc MOTEUR.
   MOTOR C direction=forward power=powerC ! Commande identique à l'autre moteur mais en utilisant l'autre valeur de puissance.
end loop forever                      ! fin de boucle, retour au début de boucle et recommencer à nouveau.

Bien, nous y voilà presque. Il y a cependant un problème subtil qui devrait être corrigé. Mais laissez de toute manière une chance à votre robot. S'il semble s'écarter du bord du tracé, au lieu de s'en rapprocher, la cause la plus probable est que vous avez inversé les sens des Virages. Corrigez Kp à -10 et vérifiez le résultat Si cela rectifie les sens de Virage alors revenez à Kp en conservant +10 et changent les signes dans les deux lignes de la manière suivante;

powerA = Tp - Virage
powerC = Tp + Virage

Il y a deux "paramètres variables" et une constante dans ce contrôleur P. La constante est la valeur moyenne (la moyenne des lectures du capteur photosensible pour le blanc et le noir). Vous devrez donc écrire un court programme pour évaluer les niveaux lumineux sur votre surface d'évolution de votre robot. Vous avez besoin de déterminer une valeur "noir" et une valeur "blanc". Calculez la moyenne et placez-la dans le programme du contrôleur P (dans la variable Moyenne).
La plupart des lignes qui vont suivre supposent que vous avez réalisé ce petit programme exécuté par le robot.

La valeur de Kp et la puissance cible Tp sont des paramètres variables. Un paramètre variable doit être choisi par tâtonnements et approximations successives. Kp contrôlera la vitesse du contrôleur dans ses mouvements de retour au bord du tracé quand il s'en est éloigné. Tp contrôlera la vitesse du robot dans son déplacement le long du tracé.
Si la ligne est pratiquement rectiligne, vous pouvez utiliser une grande valeur pour Tp afin de déplacer le robot à grande vitesse, et une petite valeur pour Kp pour rendre les virages plus doux.
Si le tracé présente des courbes serrées, La valeur maximum Tp sera la meilleure. Si Tp est supérieur à ce maximum, la valeur de Kp importe peu, et le robot ratera son virage car il se déplace trop rapidement. Si Tp est faible, la plupart des valeurs de Kp seront acceptables compte tenu du lent déplacement du robot. L'objectif est d'avoir un déplacement le plus rapide possible sans perdre la capacité de suivre le tracé.

Nous avions supposé une valeur de départ pour Kp de 10. Pour Tp vous pourriez choisir une valeur plus faible que celle suggérée ci-dessus, peut-être 15 (le robot se déplace assez lentement). Essayez et vérifiez le résultat. Si vous ratez le tracé parce que le robot semble virer trop lentement, alors augmentez Kp et essayez à nouveau. Si vous ratez le tracé parce que le robot semble trop réactif dans la recherche du tracé dans les deux sens diminuez alors Kp. Si le robot semble suivre le tracé correctement, augmentez Tp et assurez-vous de pouvoir suivre le tracé à une vitesse plus rapide. Pour chaque nouvelle valeur de Tp vous devrez déterminer une nouvelle valeur correspondante de Kp, sachant que Kp présente d'habitude peu de variation.

Suivre un tracé rectiligne est d'ordinaire assez facile. Suivre un tracé comportant des courbes douces est un peu plus délicat. Suivre enfin un tracé comportant des courbes serrées est nettement plus dur.
Si le robot se déplace assez lentement, presque n'importe quel tracé peut être suivi, même avec un contrôleur rudimentaire. Nous recherchons un bon suivi de tracé, avec une vitesse convenable et la capacité d'aborder avec succès les virages doux. (Les tracés avec virages serrés suivent généralement des trajectoires de tracés plus spécialisées).

Il est évident que le meilleur contrôleur P est spécifique à chaque type de tracé (largeur, rayons des courbes, éclairage, etc.) et à chaque robot. En d'autres termes, un contrôleur P (ou un contrôleur PID en la matière) est conçu pour une seule sorte de tracé et de robot et ne fonctionnera pas convenablement pour d'autres tracés ou robots. Il faudra donc adapter les paramètres Kp et Tp selon les circonstances.
Une idée intéressante consisterait à noter sous forme de tableau, toutes les tests avec les valeurs modifiées des différents paramètres. Chaque test pouvant être commenté en termes simples pour faire ressortir les valeurs qui conviennent le mieux.

Avant d'aller plus loin, Je vous propose deux exercices dont l'utilité se retrouvera par la suite.

Premier exercice:
Utiliser le tapis d'évolution (test pad) fourni dans le kit # 8527.
Ecrire un programme NXT-G capable de lire les valeurs fournies par le capteur photosensible pour le 'blanc' et le 'noir' à partir du tracé ovale du tapis d'évolution. Puis calculer la moyenne et la stocker dans une variable (cette moyenne permettra de calculer par la suite l'erreur).
Pour accomplir ce travail, placer le robot, le nez sur le tracé du tapis.
Au début du programme, le robot doit accomplir un balayage en pivotant sur lui-même. Pendant ce balayage (demi-rotation du moteur A), capter les valeurs et les stocker dans des variables. Arrêt du robot. Une fois ces valeurs captées, faire revenir le nez du robot à proximité du tracé. Arrêt du robot et affichage de la valeur moyenne sur le petit écran.

Deuxième exercice:
Transformer le programme précédent en un 'Mon bloc' en utilisant la palette 'Perso.' Ce 'Mon Bloc' est une routine qui sera utilisée pour l'écriture du programme futur du contrôleur P. Ce bloc est aussi très utile et peut-être réutilisé pour l'écriture d'autres programmes faisant appel aux valeurs lues du capteur.

Dans un prochain chapitre, nous verrons comment écrire un PID en NXT-G dans un cas simple utilisant le bloc DEPLACER. ;)



edit :
suite (1) de la leçon : post53474.html#p53474
suite (2) de la leçon : post56827.html#p56827
suite (3) de la leçon : post58022.html#p58022
suite (4) de la leçon : post58757.html#p58757
Dernière édition par roboleo le Lun Mar 01, 2010 1:59 pm, édité 1 fois au total.
A+
Roboleo
" Je ne cherche pas, je trouve…" P. Picasso

Avatar de l’utilisateur
Nico71
Level 14
Level 14
 
Messages: 9618
Âge: 35 ans
expertgbbexpertsoft3dexperttrialtruckmoceurtechnicexpertvainqueurconcours3

Messagepar Nico71 » Mer Fév 24, 2010 6:52 pm

Trés bien ! Un peu compliqué mais très synthétique ;)

J'ajouterais juste pour les nons initiés que le capteur de lumière n'est pas binaire (deux états : noir, blanc) mais possède plusieurs états.

S'il regarde une bande noire, il ne va pas forcément afficher 40, il peut très bien afficher 42 à cause du blanc à coté de la bande noire. Ce qu'il faut bien voir c'est que si le capteur n'est pas complètement contre l'objet, il sera influencé par les quantités de lumière arrivant des autres cotés.

Globalement, il fait un mix de tout ce qu'il voit (en face, à gauche , a droite etc) et renvoie une valeur moyenne de ce qu'il estime comme niveau de gris.

;)

NeuroMimetis
Level 10
Level 10
 
Messages: 1574
collectionneur

Messagepar NeuroMimetis » Jeu Fév 25, 2010 6:44 pm

Roboleo,

tu nous gates avec cette nouvelle leçon qui ne concerne plus seulement le NXT mais la robotique en général.

Je n'ai pas de NXT-G sous la main (sur le PC d'où j'écris) pour répondre à ton premier exercice, mais je poste un petit pseudo-code qui fera bien le travail. Volontairement, j'ai souhaité balayer le tracé de part et d'autre de 10° en 10° sur un arc de 120° :

!Robot : position de début = capteur centré sur le tracé

photomin=100
photomax=0

Robot : pivote à gauche (antihoraire) de 60°

Loop 12 fois

photo=lecture capteur photo
Si photo > photomax, alors photomax=photo ! on stocke la valeur maxi
Si photo < photomin, alors photomin=photo ! on stocke la valeur mini
Robot : pivote à droite (horaire) de +10° ! balaye au total 120° (60° de part et d’autre du tracé)

EndLoop

moyenne=(photomin+photomax)/2
robot : pivote à gauche de 60° ! revient en position initiale

Afficher moyenne



Dès que j'ai mon NXT-G sous la main, je vous le fais avec de jolis blocs !

Neuro

Avatar de l’utilisateur
roboleo
Level 8
Level 8
 
Messages: 955
Localisation: Hauts de Seine
expertnxt

Messagepar roboleo » Ven Fév 26, 2010 1:50 pm

NeuroMimetis a écrit:Roboleo,

Je n'ai pas de NXT-G sous la main (sur le PC d'où j'écris) pour répondre à ton premier exercice, mais je poste un petit pseudo-code qui fera bien le travail. Volontairement, j'ai souhaité balayer le tracé de part et d'autre de 10° en 10° sur un arc de 120° :

!Robot : position de début = capteur centré sur le tracé

photomin=100
photomax=0

Robot : pivote à gauche (antihoraire) de 60°

Loop 12 fois

photo=lecture capteur photo
Si photo > photomax, alors photomax=photo ! on stocke la valeur maxi
Si photo < photomin, alors photomin=photo ! on stocke la valeur mini
Robot : pivote à droite (horaire) de +10° ! balaye au total 120° (60° de part et d’autre du tracé)

EndLoop

moyenne=(photomin+photomax)/2
robot : pivote à gauche de 60° ! revient en position initiale

Afficher moyenne



Neuro


Pas mal du tout, tu y es presque… :D
Au lieu de répéter 12 fois la boucle, grâce au capteur intégré du moteur, il est possible de travailler sur une boucle continue contrôlée.
La sortie se fera lorsque le moteur A aura accompli 180°. Les relevés seront continus et comparés en permanence avec max et mini. Un fois la rotation du robot accomplie, petit arrêt pour permettre au NXT de faire les calculs. Puis rotation inverse du robot pour revenir sur le tracé et afficher le résultat. Fin du programme en appuyant sur le bouton rouge (il faut afficher en permanence, sinon impossible de lire).
Lorsque tu disposeras du programme NXT-G, tout cela te semblera 'lumineux'.

Le corrigé est bien entendu prévu… ;)
A+
Roboleo
" Je ne cherche pas, je trouve…" P. Picasso

Avatar de l’utilisateur
roboleo
Level 8
Level 8
 
Messages: 955
Localisation: Hauts de Seine
expertnxt

Messagepar roboleo » Lun Mar 01, 2010 3:32 pm

Un contrôleur PID pour les robots Mindstorms NXT (suite 1)

CHAPITRE 2

1 - Première solution d'un PID simple faisant appel au bloc 'DEPLACER'.

Pour écrire ce programme, nous avons besoin d'une routine ('dans la palette Mon Bloc) que nous appellerons:
PID_LF_Calib.
Cette routine permet de calibrer le capteur par lecture des valeurs lues mini et maxi. Elles s'obtiennent par balayage de la zone où se trouve le tracé. De ces valeurs, la routine en tire une moyenne appellée 'gray'.

Cette valeur est alors reprise par le programme que nous allons à présent développer.

La valeur 'gray' est transmise aux moteurs A et C par l'intermédiaire d'un bloc 'DEPLACER'. A noter que plus le niveau de puissance est élevé, plus le robot aura de difficultés à suivre le tracé.
Ce contrôleur est un suiveur gauche de tracé (left-hand edge follower), qui active le moteur A à gauche et le moteur C à droite. Le capteur photosensible est raccordé au port 2.

La constante proportionnelle Kp est multipliée par 100 pour tenir compte de la nature des nombres exprimés en valeurs entières (ce qui exclus la notion de virgule flottante).

La meilleure valeur pour Kp dépends de plusieurs facteurs: les roues du robot, la plage des valeurs et la hauteur du capteur par rapport à la surface d'évolution. Le programme fonctionnera correctement si le capteur se trouve entre 6 et 12 mm environ au dessus du plan, et si l'éclairage du local est uniformément réparti.

Mais, auparavant il est nécessaire de préciser un point particulier: celui de la virgule décimale.
Il faut rappeler ici que la version NXT-G 1.1 ne traite que des valeurs numériques entières , alors que la version 2.0 prend en compte les valeurs décimales. Cette différence se traduit par une petite complication.

Dans le processus de réglage du contrôleur P, la valeur de Kp ne cesse de varier entre des limites. La gamme attendue des valeurs que Kp dépend très exactement de l'action du contrôleur P. Quelle est la dimension de la plage d'entrée et celle de la plage de sortie ?
Pour notre contrôleur P (de suivi de tracé), la plage d'entrée est environ 5 unités et la plage de sortie de la puissance des moteurs est de 100 unités , donc il semble probable que Kp sera aux alentours de 100/5=20.
Dans certains cas Kp attendu ne sera pas aussi grand. Et qu'arrive-t-il si le Kp attendu est égal à 1 ?
Puisque les variables dans NXT-G sont limitées aux entiers, quand il s'agit de déterminer les valeurs de Kp tout ce qu'il est possible d'entrer c'est...-2,-1, 0, 1, 2, 3. Vous ne pouvez pas entrer 1.3 donc vous ne pouvez pas essayer Kp = 1.3. Vous ne pouvez pas utiliser un nombre avec une virgule (ou un point) décimal(e)!
Mais il y aura probablement une grande différence dans le comportement du robot quand Kp passera brutalement de 1 à 2. Avec Kp = 2 le robot tentera de corriger l'erreur deux fois plus durement comparée à Kp = 1. La puissance du moteur variera de plus du double pour un même changement de niveaux lumineux. Trop brutal! Ce que nous recherchons, c'est un mode de contrôle plus fin pour Kp.
Ce problème est facile à résoudre. Tout ce que nous ferons, c'est de multiplier Kp par 100 pour redéfinr la plage utilisable dans la limite des entiers. Si on s'attend à ce que Kp puisse être proche de 1 alors une valeur de 100 comme multiplicateur serait un bon pari. En effet, il est probablement plus judicieux de toujours utiliser 100*Kp comme nombre à entrer dans le programme. Une fois Kp multiplié par 100 nous pouvons maintenant entrer ce qui aurait été 1.3 comme 130. 130 n'a aucun point décimal donc NXT-G y trouve son compte.

Mais cela ne complique-t-il pas le calcul ? Oui, mais il est facile de corriger. Une fois que nous avons calculé le terme de P nous le diviserons par 100 pour faire disparaître notre multiplicateur. Rappelez-vous notre équation qui définit le contrôleur P:

Virage = Kp * (erreur)

Nous multiplierons Kp par 100, ce qui signifie que notre Virage calculé est 100 fois plus grand que cela devrait être. Aussi, avant d'utiliser Virage nous devons le diviser par 100 pour rectifier.
Ainsi, notre nouveau pseudo-code sera modifié de la manière suivante:

Kp = 1000                                 ! Souvenez-vous, on emploi Kp*100 aussi il est réellement égal à 10 !
offset = 45                               ! ! Initialisation de trois variables
moyenne = 45
Tp = 50
Loop forever ! Début de Boucle pour toujours
   LightValue = valeurlueducapteur        ! Quelle est la valeur lue courante du capteur?  
   erreur = LightValue - moyenne          ! calcul de l'erreur par soustraction de la moyenne.
   Virage = Kp * erreur                   ! le terme "P", de combien veut-on modifier la puissance du moteur
  Turn = Turn/100                        ! Souvenez-vous,pour annuler l'effet du facteur 100 dans Kp !
   powerA = Tp + Turn                     ! niveau de puissance pour le moteur A
   powerC = Tp - Turn                     ! niveau de puissance pour le moteur C
   MOTOR A direction=forward power=powerA ! transmet la commande avec la nouvelle valeur de puissance dans un bloc MOTEUR.
   MOTOR C direction=forward power=powerC ! Commande identique à l'autre moteur mais en utilisant l'autre valeur de puissance.
end loop forever                          ! fin de boucle, retour au début de boucle et recommencer à nouveau.


A - Analyse la routine 'Mon Bloc' PID_LF_Calib.

Voici à quoi ressemble le programme,

Image

et pour une meilleure lecture, ce 'Mon Bloc' sera découpé en 4 parties analysées successivement.


Fig.7 Partie 1

Image

Deux variables numériques sont crées pour stocker les valeurs Hautes (high) et Basses (low) des niveaux lumineux lu par le capteur lors de son balayage. On initialise ces variables en y inscrivant les valeurs suivantes:
-10000 dans High et +10000 dans Low.
Deux Blocs MOTEUR (A et C) avec déplacement opposé pour un balayage lent (fait pivoter le robot) sur lui-même.
Puis on ouvre une boucle dans laquelle on place un Bloc Capteur Photosensible.
Cette boucle est destinée a enregistrer les valeurs lues 'noir' et 'blanc' du test pad en une seule fois.

Pour cela on utilise une petite astuce en introduisant une nouvelle variable Number 1 qui recueille directement la valeur lue par le capteur au moment du balayage. Comme le capteur se déplace, Number 1 va recevoir plusieurs valeurs successives annulant à chaque fois la précédente. Il faut donc trouver le moyen de conserver la valeur la plus basse avant qu'elle ne soit remplacée par la valeur haute.
Pour cela, on compare (bloc commutateur) Number 1 d'abord avec HighLigth qui contient -10000. Si HighLigth est inférieur à Number 1 alors on remplace -10000 par Number 1. Puis on passe au 2ème bloc commutateur. en comparant Number 1 avec LowLight qui contient +10000. Si LowLigth est supérieur à Number 1 alors on remplace +10000 par Number 1. Cette boucle est contrôlée par le capteur de rotation du moteur A. Quand le moteur A termine ses 180° de rotation, on quitte la boucle.

Fig.8 Partie 2

Image

A la sortie de la boucle, le robot s'arrête, puis légère attente avant de procéder au calcul de la demi-somme qui est stockée dans une 4ème variable appelée MidLight. Ce travail réalisé, le robot reprend sa rotation en sens inverse pour revenir vers le tracé.

Fig.9 Partie 3

Image

Le capteur lumineux s'assure que le niveau est supérieur à la moyenne (MidLight) c'est à dire qu'il est toujours sur le 'blanc' (1ère boucle) et le robot poursuit sa rotation en passant sur le 'noir' (2éme boucle). Si le niveau est inférieur MidLight, on est alors sûr d'être sur le 'noir'. Dès que le capteur ne détecte plus le 'noir', on sort de la boucle, petite attente et le robot s'arrête.

Fig.10 Partie 4

Image

Remarques:
1 - La fin de course se produit sur le 'blanc', après avoir franchi le bord du tracé.
2 - Vous observerez un plot de fil de données un peu spécial ( il est "flottant") provenant d'une sortie de la variable MidLight; il porte le nom de Result et contient le signe # qui est celui d'une valeur numérique. Ce type de plot apparaît lorsque le programme devient un 'Mon Bloc' caractérisé par une bande verte. Donc, si vous souhaitez compléter ce programme par un affichage, il suffira de créer un nouveau programme conçu par exemple de cette manière:

Fig.11

Image


B - Analyse du programme principal.

Nous disposons maintenant de tous les éléments nécessaire pour l'écriture du contrôleur P. Le programme est intitulé Steer_LF_2.rbt.

Fig.12

Image

Comme vous pouvez le constater, grâce au 'Mon Bloc' PID_LF_Calib, le programme par lui-même est assez simple.
La variable 'Gray' reprend la valeur moyenne calculée à partir de ce 'Mon Bloc' . La boucle se contente de relever la valeur du capteur pendant le déplacement du robot; de cette valeur est déduite la valeur 'Gray' pour obtenir la valeur de l'Erreur. Ce résultat est ensuite multiplié par la constante Kp (multiplié par le facteur 100 pour résoudre le problème des valeurs décimales) pour obtenir la variation de puissance à appliquer aux moteurs. Le résultat est ensuite divisé par 100 pour retrouver les valeurs de la plage acceptée par les moteurs. Le fil de données est en final raccordé au plot 'Direction' du bloc DEPLACER. A ce sujet, vous noterez que la plage s'étend de - 100 à + 100. Quand le fil de données produit une valeur égale à zéro, le robot se déplace en ligne droite. Si la valeur est entre - 100 et zéro, le robot vire à gauche; il vire à droite si la valeur est entre zéro et + 100. Les virages sont plus ou moins serrés selon la valeur absolue transmise.

Remarque: lors du premier essai, vous serez probablement déçu du résultat. Cela signifie que des corrections sont nécessaires, puisque Kp et la puissance des moteurs sont variables. Nous verrons plus tard comment améliorer le suivi du tracé d'une manière plus fluide.
Comme il a été signalé plus haut, vous procéderez par correction de ces deux paramètres jusqu'à trouver les valeurs qui produisent le meilleur résultat.

N'oubliez pas de créer et d'installer dans 'Mon Bloc' le programme PID_LF_Calib.rbt avant de créer le programme Steer_LF_2.rbt.

Vous pouvez télécharger ces programmes ici:
PID_LF_Calib.rbt
http://files.me.com/roboleo/t4kr7s
Steer_LF_2.rbt
http://files.me.com/roboleo/0vgi8s

A suivre… ;)
A+
Roboleo
" Je ne cherche pas, je trouve…" P. Picasso

Avatar de l’utilisateur
Alban42800
Level 11
Level 11
 
Messages: 2473
Localisation: Loire (42)
Âge: 50 ans
expertgbbexpertnxtvainqueurconcours

Messagepar Alban42800 » Jeu Mar 04, 2010 12:11 am

Merci Roboléo de reprendre cet article et de le traduire (et de le populariser apparemment). Perso j'ai perdu toute compétence en anglais et j'ai abandonné la lecture vu la longueur de la page.
Donc j'attend impatiemment la suite :D pour ne plus être perdu avec I et D :lol:
[HS]
Je me suis pas mal battu avec PID, temps de réponses, asservissement de direction et de position ces derniers temps en vain. Je reprendrai le combat plus tard, et je pense aussi vous montrer les résultats si j'y arrive. Mes deux principaux problèmes était ma non maîtrise des PID et les temps de calculs de la position du robot trop longs (calculs trigonométriques de l'API Lejos).
Content aussi de te lire à nouveau sur le forum. ;)
[/HS]

Avatar de l’utilisateur
semtou
Level 7
Level 7
 
Messages: 525
Localisation: Toulouse
Âge: 55 ans

Messagepar semtou » Lun Mar 08, 2010 7:17 pm

Salut Roboléo
Enfin comprend le principe du PID même si c'est un peu complexe et que j'ai pas encore tout lu.
Effectivement cette méthode de calcul d'asservissement est pas mal utilisé dans l'industrie (je l'ai rencontré sur des presses chauffantes chez Airbus pour le pliage de tôles en titane).
j'avais jusqu'ici pas bien identifié qu'il s'agissait de calcul sur la base de 2 variables. j'espère avoir compris l'essentiel.
Je vais lire la suite, Merci
"Ce qui se conçoit bien s’énonce clairement et les mots pour le dire viennent aisément..." Nicolas Boileau-Despreaux
Mon blog = http://semtou.skyrock.com/
Ma galerie photos = http://www.brickshelf.com/cgi-bin/gallery.cgi?m=SEMTOU

Thibaud
Level 6
Level 6
 
Messages: 333
Localisation: Paris
expertnxt

Messagepar Thibaud » Lun Mar 08, 2010 7:42 pm

J'ai enfin imprimé ton tuto je vais le lire :) de ce que j'ai pu lire en diagonale je sens que je vais apprendre plein de truc :) Je te fais un retour des que j'ai tout lu. Beau travail!!!
Venez voir mes créations ;) NXTGEN
-----
La team BrickBot

Avatar de l’utilisateur
roboleo
Level 8
Level 8
 
Messages: 955
Localisation: Hauts de Seine
expertnxt

Messagepar roboleo » Sam Avr 24, 2010 2:44 pm

Un contrôleur PID pour les robots Mindstorms NXT (suite 2)

CHAPITRE 3

Ajouter "I" au contrôleur : le contrôleur PI

Pour améliorer les performances de notre contrôleur P nous ajouterons un nouveau terme à l'équation. Ce terme est appelé l'intégrale, le "I" dans PID. Les Intégrales sont une partie très importante des mathématiques supérieures, heureusement pour nous, celle dont nous avons besoin est assez simple.

L'intégrale est la somme courante des erreurs.

En effet, c'est aussi simple. Il y a toutefois quelques subtilités que nous laisserons sous silence pour l'instant.

Chaque fois que nous lirons une valeur fournie par le capteur lumineux et calculerons l'erreur qui en découle, nous ajouterons cette erreur à une variable nous appellerons intégrale (intelligent non ?).

Intégrale = intégrale + erreur

Cette équation paraît bizarre et elle l' est. Elle n'est pas écrite comme une équation mathématique courante, mais sous une forme communément utilisée en programmation pour additionner une série de valeurs. Mathématiquement cela n'a aucun sens.
Dans la programmation informatique le signe égal ( = ) a une signification quelque peu différente de celle des maths. C'est une expression de programmation et non pas une formule mathématique appropriée.
Le signe " = " signifie en mathématiques que l'expression située à droite du signe permet de calculer et de ranger le résultat dans une variable située à gauche.
Ce que nous demandons à l'ordinateur, c'est d'ajouter à l'ancienne valeur d'intégrale , celle de l'erreur et de ranger le résultat à la place de l'ancienne intégrale.

Ensuite, comme pour le terme proportionnel P, nous multiplierons l'intégrale par une constante, c'est un autre terme K. Puisque cette constante est associée avec le terme intégral nous l'appellerons Ki. Comme pour l'expression proportionnelle précédente, nous multiplions l'intégrale par la constante (Ki) pour obtenir une correction.
Ce qui donne à notre précédente formule du Virage un complément additionnel, et la modifie ainsi:

Virage = Kp * (erreur) + Ki * (Intégrale)

Nous retrouvons ici l'équation de base pour un contrôleur PI. Le Virage est la correction pour les moteurs. Le terme proportionnel est Kp * (l'erreur) et le terme intégrale est Ki * (intégrale).

A quoi sert exactement le terme intégrale? Si l'erreur garde le même signe pendant plusieurs boucles l'intégrale croît de plus en plus.
Par exemple, si nous vérifions le capteur lumineux et calculons que l'erreur est 1, et qu'un peu plus tard nous vérifions à nouveau le capteur et l'erreur est 2, et la fois suivante encore 2, alors l'intégrale sera 1 + 2 + 2 = 5. L'intégrale est égale à 5 alors que l'erreur à cette étape particulière est seulement de 2. On voit que l'intégrale peut être un grand facteur dans la correction mais il lui faut un certain temps avant qu'elle ne puisse commencer à contribuer.
Mais l'intégrale fait aussi autre chose; elle supprime les petites erreurs. Si dans notre suivi de tracé le capteur lumineux est assez proche du bord du tracé, mais pas exactement dessus, l'erreur sera sera petite et la correction de trajectoire sera faible. On serait alors tenté de modifier le Kp proportionnel mais cela mènera souvent le robot à serpenter (des oscillations dans les deux sens). Le terme intégrale est parfait pour rectifier les petites erreurs.
Alors que l'intégrale additionne les erreurs, plusieurs petites erreurs consécutives peuvent finalement lui donner assez d'importance pour faire une différence.
En d'autres termes, l'intégrale est une sorte de "mémoire" du contrôleur. Elle est l'histoire cumulative des erreurs et donne au contrôleur un moyen de les corriger quand elles persistent pendant une longue période de temps.

Quelques subtilités sur les intégrales

Bien, mais l'intégrale est un peu plus compliquée. Heureusement ce n'est pas trop douloureux.
J'ai ignoré un paramètre secondaire ( ce n'est pas vraiment secondaire, mais nous allons le considérer ainsi), le temps. L'intégrale est vraiment la somme de l'erreur * (l'écart de temps). L'écart de temps ou delta T(dT) est le temps écoulé entre la dernière lecture du capteur et le temps du contrôle le plus récent du capteur;

Intégrale = intégrale + erreur * (dT)

Ainsi, à chaque fois que nous ajoutons à intégrale un élément, nous devrions lui ajouter les temps d'erreur le dT.
Il est assez facile de faire mesurer le dT par le robot. Il suffit d'utiliser un minuteur à chaque relevé du capteur photosensible. Si nous soustrayons la dernière lecture du temps actualisé nous obtenons le temps écoulé depuis la dernière lecture: le dT. (Il existe une meilleure façon de le faire mais je l'ignorerai puisque cela n'est pas nécessaire). Mais ne serait-il pas plus simple s'il n'y avait pas la nécessité de mesurer le dT pour faire la multiplication ?
Bien, et si le dT avait toujours la même valeur? Chaque fois que intégrale croît, nous avons ce même terme dT. Donc nous pouvons faire abstraction de ce facteur dT de l'erreur * (dT) et se contenter d'appliquer une formule analogue du type:

intégrale = intégrale + erreur

Seule restriction, quand nous ferons un autre calcul avec intégrale nous devrons en réalité multiplier le résultat par dT.
Mais attendez il y a encore plus...

Nous pouvons faire mieux pour faire abstraction de l'expression du temps. Le terme intégrale dans l'équation du contrôleur de PI est Ki * (intégral) * dT. Mais Ki est un paramètre qui doit être réglé avec précision (tout comme Kp), alors pourquoi ne pas remplacer juste la partie Ki*dT par un nouveau Ki ? Ce nouveau Ki diffère du premier, mais puisque nous ne le connaissons pas, qu'importe celui que nous utilisons ou la manière de le désigner. Peu importe sa nomination ou ce qu'il représente nous devons toujours trouver la valeur correcte en grande partie par tâtonnement.

Nous avons donc supprimé l'élément temps pour le terme intégrale avec la seule restriction qu'à toutes les étapes, les dT sont constants (gardent la même valeur).

L'intégrale a une mémoire d'éléphant

Un dernier détail devrait être mentionné au sujet de l'intégrale. D'ordinaire, l'intégrale tend vers zéro, c'est-à-dire qu'elle ne contribue en rien au contrôleur, puisque des valeurs d'erreur cumulées qui sont de signes opposés pour la plupart d'entre elles, sont déjà rassemblés dans intégrale.
Par exemple, si après plusieurs cycles les erreurs respectives sont égales à 1,2,2,3,2,1 cela donne une intégrale de 11. Mais l'erreur au dernier cycle de données n'est seulement que 1, valeur qui est beaucoup plus petite que l'intégrale en ce point. La seule façon pour l'intégrale de tendre vers zéro, est d'obtenir une suite d'erreurs négatives pour contre balancer la suite précédente d'erreurs positives et atteindre cette limite. Par exemple, si les prochaines 'erreurs sont-2,-2,-3 alors l'intégrale baissera de 11 à 4 et nous aurions toujours besoin de plus d'erreurs négatives pour atteindre zéro . De plus, l'intégrale demande que l'erreur totale soit également répartie entre des erreurs positives et négatives.

Si un événement dévie notre robot vers la gauche du bord du tracé, le terme intégral cherche non seulement à le faire revenir sur ce bord, mais aussi le faire dépasser vers la droite de la même valeur que celle de la gauche. Ainsi l'intégrale a tendance à atteindre par le haut cette limite, si de grandes erreurs persistent quelque temps. Cela peut aussi causer quelques problèmes avec les contrôleurs qui incluent un terme intégral. Parfois cette tendance de dépassement du terme intégral dans sa tentative de correction d'erreur oblige le programmeur à intervenir pour neutraliser ces problèmes. Si la limite supérieure de l'intégrale constitue un problème deux solutions sont envisageables:
(1) remettre à zéro la variable intégrale, chaque fois que l'erreur est égale zéro ou à chaque changement du signe d'erreur.
(2) "Réduire" l'intégrale en multipliant sa valeur cumulée par un facteur inférieur à 1 quand un nouvelle valeur intégral est calculé. Par exemple:

intégrale = (2/3) * intégrale + erreur

Cela réduit la valeur intégrale précédente de 1/3 à chaque boucle. Si vous considérez le terme intégral comme la mémoire du contrôleur, alors cet diminution le force à "oublier" des événements qui se sont produits dans un passé lointain.

Pseudo code pour le contrôleur PI


Pour ajouter le terme intégral au contrôleur, nous avons besoin d'ajouter une nouvelle variable pour Ki et une pour l'intégrale proprement dite. Et ne pas oublier que nous multiplions Ks par 100 pour résoudre le problème des valeurs décimales.


Kp = 1000                                 ! Souvenez-vous, on emploi Kp*100 aussi il est réellement égal à 10 !
Ki = 100                                 ! Souvenez-vous, on emploi Ki*100 aussi il est réellement égal à 1 !
offset = 45                               ! ! Initialisation de trois variables
moyenne = 45
Tp = 50
integral = 0                          ! l'endroit où nous stockerons notre intégrale
Loop forever ! Début de Boucle pour toujours
   LightValue = valeurlueducapteur        ! Quelle est la valeur lue courante du capteur?  
   erreur = LightValue - moyenne          ! calcul de l'erreur par soustraction de la moyenne.
integral = integral + error        ! notre nouveau terme intégral
Virage = Kp * erreur + Ki * integral      ! le terme "P", et le terme "I"
  Virage = Virage/100                        ! Souvenez-vous,pour annuler l'effet du facteur 100 dans Kp !
   powerA = Tp + Virage                     ! niveau de puissance pour le moteur A
   powerC = Tp - Virage                     ! niveau de puissance pour le moteur C
   MOTOR A direction=forward power=powerA ! transmet la commande avec la nouvelle valeur de puissance dans un bloc MOTEUR.
   MOTOR C direction=forward power=powerC ! Commande identique à l'autre moteur mais en utilisant l'autre valeur de puissance.
end loop forever                          ! fin de boucle, retour au début de boucle et recommencer à nouveau.



Ajouter "D " au contrôleur : le contrôleur PID au complet
(" D ": que va-t-il se produire maintenant?

Notre contrôleur dispose maintenant d'un terme proportionnel (P) qui cherche à corriger l'erreur courante et un terme intégral (I) qui cherche à corriger les erreurs passées. A présent, existe-t-il un moyen qui permette au contrôleur de prévoir à l'avance et peut-être d'essayer de corriger les erreurs qui ne se sont pas encore produites?

La réponse est oui, et la solution est un autre concept des mathématiques supérieures appelé la dérivée. Ah, nous y voilà; c'est le fameux D du PID. Tout comme l'intégrale, la dérivée représente aussi une autre partie très importante des mathématiques supérieures, heureusement pour nous, ce dont nous avons besoin pour le PID est assez simple.
Nous pouvons prévoir le futur en supposant que la prochaine variation de l'erreur est identique à la dernière variation de l'erreur.

Cela signifie que la prochaine erreur est supposée être l'erreur courante plus ( + ) la variation de l'erreur entre deux précédentes lectures du capteur. La variation de l'erreur entre deux points consécutifs est appelée dérivée. La dérivée est équivalente à la pente d'une droite.

Cela pourrait paraître un calcul un peu complexe, mais ce n'est vraiment pas le cas ici. Un jeu de données type aidera à mieux comprendre le mécanisme. Supposons que l'erreur actuelle est 2 et l'erreur précédente 5. Quelle pourrait-être l'erreur suivante? Bien, la variation de l'erreur est la dérivée qui s'exprime ainsi:

( l'erreur actuelle ) - (l'erreur précédente )
ce qui donne pour nos valeurs: 2 - 5 = - 3. La dérivée actuelle est dans ce cas - 3. Pour utiliser la dérivée afin de prévoir l'erreur future, nous utiliserons la relation suivante:

( l'erreur future ) = ( l'erreur actuelle ) + (la dérivée actuelle )

qui dans notre cas donnera: 2 + (- 3) = - 1. Nous supposerons donc que l'erreur future sera égale à - 1.
En pratique nous n'allons pas systématiquement calculer l'erreur suivante. Au lieu de cela nous utiliserons la dérivée directement dans l'équation de contrôleur.

Le terme D, tout comme le terme I , devrait en réalité inclure un élément temps, et le terme 'officiel' de D serait:

Kd(dérivée) / (dT)

Tout comme les termes proportionnel et intégrale nous devons le multiplier par une constante. Puisque cette constante accompagne la dérivée, nous l'appellerons Kd. Vous remarquez que pour le terme dérivée nous le divisons par dT, tandis que le terme intégrale était multiplié par dT. Ne vous inquiétez pas trop de cela puisque nous allons adopter la même astuce pour nous débarrasser du dT du terme dérivé, tout comme nous l'avons fait pour le terme intégrale. La fraction Kd/dT est une constante si dT est le même pour chaque boucle. Donc nous pouvons remplacer Kd/dT par un autre Kd. Alors que ce K, comme le Ks précédent, est inconnu et doit être déterminé par tâtonnement, cela n'a pas d'importance si l'on adopte Kd au lieu de Kd/dT.

Nous pouvons maintenant écrire l'équation complète du contrôleur PID:

Virage = Kp * (erreur) + Ki * (Intégrale) + Kd * (dérivée)


Il est assez évident que "la prévision de l'avenir" serait une chose utile à réaliser, mais comment s'y prendre? Et comment déterminer la précision de cette prévision ?

Si l'erreur actuelle est plus grande que l'erreur précédente alors le terme D essayera de corriger cette erreur. S'il l'erreur actuelle est plus petite que l'erreur précédente alors le terme D essayera de stopper le contrôleur dans sa tentative de correction d'erreur.
C'est ce deuxième cas qui est particulièrement intéressant. Si l'erreur s'approche du zéro, alors nous tendons vers le point d'arrêt de la correction. Comme le système prend quelque temps pour répondre aux changements de la puissance des moteurs, nous voulons commencer à réduire la puissance du moteur avant que l'erreur ne tende en réalité vers zéro, sans quoi nous risquons le dépassement. Dans ces conditions, il semblerait que l'équation pour le terme D soit plus complexe, mais en vérité, elle ne l'est pas. La seule chose dont vous devez vous soucier est de procéder à la soustraction dans le bon ordre. L'ordre correct pour ce type d'opération est "actuel" moins "précédent". Ainsi pour calculer la dérivée nous prenons l'erreur actuelle et soustrayons l'erreur précédente.

Pseudo codez pour le contrôleur PID

Pour ajouter le terme dérivée au contrôleur, nous avons besoin d'ajouter une nouvelle variable pour Kd et une variable pour conserver la dernière erreur. Et ne pas oublier que nous multiplions Ks par 100 pour résoudre le problème des valeurs décimales.

Kp = 1000                                 ! Souvenez-vous, on emploi Kp*100 aussi il est réellement égal à 10 !
Ki = 100                                 ! Souvenez-vous, on emploi Ki*100 aussi il est réellement égal à 1 !
Kd = 10000 ! souvenez-vous, on emploi Kd*100 aussi il est réellement égal à 100
offset = 45                               ! ! Initialisation de trois variables
moyenne = 45
Tp = 50
integral = 0                          ! l'endroit où nous stockerons notre intégrale
lastError = 0 !l'endroit où nous stockerons la valeur de l'erreur précédente
derivative = 0 ! l'endroit où nous stockerons notre dérivée

Loop forever ! Début de Boucle pour toujours
   LightValue = valeurlueducapteur        ! Quelle est la valeur lue courante du capteur?  
   erreur = LightValue - moyenne          ! calcul de l'erreur par soustraction de la moyenne.
integral = integral + error        ! notre nouveau terme intégral
derivative = error - LastError ! calcule la valeur dérivée

Virage = Kp * erreur + Ki * integral + Kd*derivative      ! le terme "P", et le terme "I", ainsi que le terme "D"
  Virage = Virage/100                        ! Souvenez-vous,pour annuler l'effet du facteur 100 dans Kp !
   powerA = Tp + Virage                     ! niveau de puissance pour le moteur A
   powerC = Tp - Virage                     ! niveau de puissance pour le moteur C
   MOTOR A direction=forward power=powerA ! transmet la commande avec la nouvelle valeur de puissance dans un bloc MOTEUR.
   MOTOR C direction=forward power=powerC ! Commande identique à l'autre moteur mais en utilisant l'autre valeur de puissance.
lastError = Error ! enregistre la nouvelle valeur de l'erreur qui sera la lastError au prochain cycle.
end loop forever                          ! fin de boucle, retour au début de boucle et recommencer à nouveau.

Nous avons maintenant établi pour notre contrôleur PID, le pseudo-code complet du robot suiveur de tracé .
Reste à présent à aborder la partie la plus délicate du PID: le réglage. Il s'agit en fait de déterminer les valeurs optimales à attribuer aux constantes Kp, Ki et Kd.

A suivre… ;)
Dernière édition par roboleo le Lun Mai 03, 2010 12:51 pm, édité 1 fois au total.
A+
Roboleo
" Je ne cherche pas, je trouve…" P. Picasso

NeuroMimetis
Level 10
Level 10
 
Messages: 1574
collectionneur

Messagepar NeuroMimetis » Lun Mai 03, 2010 11:07 am

rien d'autre que :resp: pour la vulgarisation

Neuro

Thibaud
Level 6
Level 6
 
Messages: 333
Localisation: Paris
expertnxt

Messagepar Thibaud » Lun Mai 03, 2010 11:13 am

TU sais quoi roboleo pour faire le trajet en train pour fribot j'avais ton cours en français et l'original avec du code en C en anglais et d'autres articles sur le sujet et j'ai fini par tout comprendre :) . Je dois avouer que tu as fais un super travail!C'est un type d'algo super puissant .
Venez voir mes créations ;) NXTGEN
-----
La team BrickBot

Avatar de l’utilisateur
roboleo
Level 8
Level 8
 
Messages: 955
Localisation: Hauts de Seine
expertnxt

Messagepar roboleo » Lun Mai 03, 2010 1:15 pm

Content que cela vous plaise, et surtout que vous ayez compris. Je dois avouer qu'au départ, je n'étais pas très sûr de faire passer le message. J'ai moi-même mis du temps pour bien assimiler…

Alban m'a signalé une anomalie au chapitre 3 (ajouter "D" au contrôleur) que j'ai corrigée; cela concerne la dérivée.

Il faut lire:
( l'erreur future ) = ( l'erreur actuelle ) + (dérivée actuelle )
au lieu de:
( l'erreur future ) = ( l'erreur actuelle ) - (erreur précédente )

Pour la suite, je vous réserve quelques surprises, et bien entendu le(s) programme(s) en NXT-G. Mais laissez-moi le temps de rédiger. ;)
A+
Roboleo
" Je ne cherche pas, je trouve…" P. Picasso

Thibaud
Level 6
Level 6
 
Messages: 333
Localisation: Paris
expertnxt

Messagepar Thibaud » Lun Mai 03, 2010 2:20 pm

En NXT-G ce n'est pas un peu compliqué à écrire puis à relire? j'ai des doutes quant à l'utilisation de ce langage pour ce genre d'algo. SI tu faisais du C ou du Java se ne serait pas plus simple?
Venez voir mes créations ;) NXTGEN
-----
La team BrickBot

Avatar de l’utilisateur
Alban42800
Level 11
Level 11
 
Messages: 2473
Localisation: Loire (42)
Âge: 50 ans
expertgbbexpertnxtvainqueurconcours

Messagepar Alban42800 » Lun Mai 03, 2010 2:43 pm

C'est pourtant possible, nxtprograms.com l'a fait :
http://nxtprograms.com/NXT2/segway/index.html
Après tout ce ne sont que des additions, soustractions et multiplications.

Thibaud
Level 6
Level 6
 
Messages: 333
Localisation: Paris
expertnxt

Messagepar Thibaud » Lun Mai 03, 2010 2:48 pm

je n'ai pas dis que c'était impossible :) juste moins clair peut être.
Venez voir mes créations ;) NXTGEN
-----
La team BrickBot


Retourner vers De la théorie à la pratique

Qui est en ligne ?

Utilisateurs parcourant actuellement ce forum : Aucun utilisateur inscrit et 5 invités