Asservissement position moteur arduino PID

Bonjour tout le monde!
J'ai un soucis avec un programme arduino conçu pour asservir un moteur cc avec encodeur en position et ce problème me prends la tête depuis 2 bonne semaines maintenant... C'est assez important, c'est pour mon TIPE (projet de prépa équivalent aux TPE de 1re) et on est assez en retard comme ça avec mon collègue...

Contexte: le motoréducteur sert à faire déplacer un chariot sur un axe linéaire. Un système de poulie-courroie transforme la rotation du moteur en translation du chariot.
Cependant, quand j'entre une consigne de 10cm, il ne se déplace que de 3cm environ.
De plus, le moniteur série n'affiche que des valeurs entières de l'angle mesuré en sortie du réducteur (nommé 'angle_tour dans le programme), alors qu'il devrait y avoir des décimales puisque 'angle_tour' est défini comme un float...

La poulie a un rayon de Rpoul=0.5cm
Mon moteur a un rapport de réduction de r=1/20
Le capteur à effet Hall situé à l'extrémité du moteur a une résolution de PPR= 11 impulsions par tour

Le code est normalement joint en fichier.

Si même l'un d'entre vous serait d'accord pour se contacter très rapidement par vidéo conférence ce serait topissime!
Merci d'avance...

Asservissement.txt (4.88 KB)

I've asked the moderator to move this to the French forum.

Ne postez pas en français dans le forum anglophone SVP... Lisez les règles du forum et Merci de poster aussi le code directement dans votre post, avec les balises de code.

De plus, le moniteur série n'affiche que des valeurs entières de l'angle mesuré en sortie du réducteur (nommé 'angle_tour dans le programme), alors qu'il devrait y avoir des décimales puisque 'angle_tour' est défini comme un float...

angle_tour est un float mais calculé en entier car vous n'avez que des entiers dans les opérations   float angle_tour = encoder0Pos / PPR / r;
il faudrait passer le premier terme en float par exemple, comme ça le calcul sera effectué en nombres réels  float angle_tour = ((float) encoder0Pos) / PPR / r;

il faudrait que vos éléments utilisés dans les interruptions soient volatiles (encoder0Pos) et en section protégée dans la boucle principale (ou travailler sur une copie). le plus simple serait de prendre plutôt une bibliothèque encoder. Attention au nombre d’impulsions, il peut y en avoir deux ou 4 par pas.

Comptez bien le nombre de crans de votre poulie, c’est ce qui compte plus que juste son diamètre

Vérifiez partout où vous voulez utiliser des float que le calcul n’est pas fait juste avec des entiers

Programme un peu long, difficile à tester sans le matériel, toutefois je vois quelques problèmes de fond. D'abord la manière dont vous traitez les signaux du codeur incrémental.

// Interruption de l'encodeur A en sortie 0 (pin 2)
 attachInterrupt(0, doEncoderA, CHANGE);
 // Interruption de l'encodeur A en sortie 1 (pin 3)
 attachInterrupt(1, doEncoderB, CHANGE);

//---- Interruption appelée à tous les changements d'état de A
void doEncoderA()
{
 A_set = (digitalRead(encoderPinA) == HIGH);
 encoder0Pos += (A_set != B_set) ? -1 : 1 ; //modifie le compteur selon les deux états des encodeurs ( correspond au front montant de A, on compare à B pour déduire le sens de rotation et on en déduit si on incrémente +1 ou -1)
}

Si je lis bien les docs, vous passez dans vos ISR à chaque changement d'état, donc aussi bien sur front montant que sur front descendant. Votre manière de détecter le sens de rotation et de compter ne fonctionne donc pas puisque vous ne tenez pas compte du sens du changement d'état qui a provoqué l'interruption. Je vous recommande, dans chacune des deux ISR, de lire les DEUX bits A et B, avec le moins de temps de latence possible entre les deux. Cela parce que les codeurs suivent les vibrations mécaniques de l'arbre sur lequel ils sont fixés et, dans certains cas, vous avez une rafale de changements très rapides sur un des deux bits, et si vous n'en lisez qu'un par ISR vous risquez d'avoir des situations de "décalage de position" petit à petit.

void asservissement ()
{
 time += 20; // pratique pour graphes excel après affichage sur le moniteur
 erreur = target_ticks - encoder0Pos;
 somme_erreur += erreur;
 
 // Calcul de la vitesse courante du moteur
 int vitMoteur = kp * erreur + kd * (erreur - erreurPrecedente) + ki * (somme_erreur);
 erreurPrecedente = erreur; // Ecrase l'erreur précedente par la nouvelle erreur

Le commentaire "calcul de la vitesse courante du moteur" est trompeur. Il s'agit du calcul de la sortie à appliquer en consigne de vitesse à un variateur de vitesse. Faites attention au fait qu'avec cette manière de calculer votre coefficient Kd doit être négatif si vous voulez stabiliser votre asservissement. Je vous conseille aussi de limiter la valeur de la variable somme_erreur, pour éviter qu'un retard de poursuite dû à une accélération trop forte, un problème mécanique, etc. ne vous conduise à des situations à problème.

Votre sortie vitmoteur est codée sur 8 bits, voire 8 bits plus signe. Si vous utilisez un variateur de vitesse derrière pour commander le moteur, avec une dynamo tachy, cette résolution est trop faible, vous allez ressentir la numérisation de votre signal de sortie sur la mécanique en injectant des fréquences vibratoires. Dans ces cas là, 12 bits de résolution est un mimimum. Si vous attaquez directement un petit moteur en tension, ça peut le faire.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.