Contrôle moteur/encodeur Arduino + L298N

Bonjour à tous !
Ce topic fait suite à un autre que j'avais ouvert il y a quelque temps sur un projet de contrôle de moteur CC avec arduino ([Arduino Shield] Connexion encodeur avec arduino shield - Français - Arduino Forum). Beaucoup de réponses ont été apportées mais je rencontre encore quelques difficultés. Les questions que j'ai étant très éloignées de mon premier topic, je préfère en créer un nouveau.

Pour résumer :

-J'utilise une arduino UNO avec un module L298N (j'ai suivi ce tutoriel pour réaliser mon circuit Tutorial – L298N Dual Motor Controller Modules and Arduino | tronixstuff.com)

  • Mon circuit est le suivant

Le problème que je rencontre est avec l'encodeur. Celui-ci est censé délivrer 810 impulsions par tour. J'ai créé un programme basique pour calculer ça et le résultat est surprenant (plus de 11 000 impulsions, comptées avec tick_codeur dans le programme). J'aimerai donc savoir si mon erreur vient de mon code, de ma mauvaise gestion de l'encodeur ou bien d'autre chose. Le reste fonctionne (mon moteur est alimenté correctement, change de sens et de vitesse et les impulsions sont bien décomptées si mon moteur change de sens, j'ai testé ça avec un autre programme que je peux vous donner aussi. Le problème vient du nombre d'impulsions reçues)

//INCLUDE
#include <digitalWriteFast.h>
 
//MOTEUR (avec L298N)
int motorMoins = 8;
int motorPlus = 9;
int enablePin = 5;

// ENCODEUR 
#define codeurInterruptionA 0                   //Pin 0 = interruptA
#define codeurInterruptionB 1                   //Pin 1 = interruptB
#define codeurPinA 2                            //Pin 2 = codeur channel A associé à l'interrupteur 0
#define codeurPinB 3                            //Pin 3 = codeur channel B associé à l'interrupteur 1
volatile long tick_codeur = 0;                  //nb tick codeur

//VARIABLES
volatile double omega;                         //vitesse rotation moteur en rad.s-1
volatile double theta_precedent = 0;           //pour calcul intégral
volatile double theta;                         //angle moteur en rad
volatile double commande = 0.;                 //commande moteur


void setup() {
    // Configuration des ports en mode "sortie"
    pinMode(motorMoins, OUTPUT);
    pinMode(motorPlus, OUTPUT);
    pinMode(enablePin, OUTPUT);

    // ENCODEUR
    pinMode(codeurPinA, INPUT); 
    pinMode(codeurPinB, INPUT);
    digitalWrite(codeurPinA, HIGH);                 //turn pullup resistor on
    digitalWrite(codeurPinB, HIGH);                 //turn pullup resistor on
    attachInterrupt(0, updateEncoderA, CHANGE);     //Pin 0 et 1 interrupteur à chaque changement d'état
    attachInterrupt(1, updateEncoderB, CHANGE);     //renvoi vers updateEncoder
    
    Serial.begin(9600);
    Serial.flush();

    tick_codeur = 0;    
}


void loop() {
  Serial.println(tick_codeur);
  delay(200);
  commande = 255.;
  commandeMoteur(commande);
}

void commandeMoteur(double commande){
    digitalWrite(motorPlus, HIGH); 
    digitalWrite(motorMoins, LOW);   
    analogWrite(enablePin, commande);
}

void updateEncoderA()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur--;
  }
  else {
    tick_codeur++;
  }
}

void updateEncoderB()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur++;
  }
  else {
    tick_codeur--;
  }
}

Merci à tous pour votre aide sur mon précédent topic, vous êtes vraiment une communauté en or !

Peut-être que ceci peut t'aider...

Salut ! Je me suis déjà servi de ce tutoriel pour créer mon programme ^^

Je m'en doutais, mais tu n'as pas tout pris, notamment la possibilité de gérer le timing des interruptions.

J'ai réalisé un code permettant de gérer les interruptions avec un timer :

//INCLUDE
#include <FlexiTimer2.h>
#include <digitalWriteFast.h>
 
//MOTEUR (avec L298N)
int motorMoins = 8;
int motorPlus = 9;
int enablePin = 5;
 
//TIMER
#define CADENCE_MS 10
volatile double dt = CADENCE_MS/1000.;
volatile double temps = -CADENCE_MS/1000.;

// ENCODEUR 
#define codeurInterruptionA 0                   //Pin 0 = interruptA
#define codeurInterruptionB 1                   //Pin 1 = interruptB
#define codeurPinA 2                            //Pin 2 = codeur channel A associé à l'interrupteur 0
#define codeurPinB 3                            //Pin 3 = codeur channel B associé à l'interrupteur 1
volatile long tick_codeur = 0;                  //nb tick codeur

//VARIABLES
volatile double omega;                         //vitesse rotation moteur en rad.s-1
volatile double theta_precedent = 0;           //pour calcul intégral
volatile double theta;                         //angle moteur en rad
volatile double commande = 0.;                 //commande moteur


void setup() {
    // Configuration des ports en mode "sortie"
    pinMode(motorMoins, OUTPUT);
    pinMode(motorPlus, OUTPUT);
    pinMode(enablePin, OUTPUT);

    // ENCODEUR
    pinMode(codeurPinA, INPUT); 
    pinMode(codeurPinB, INPUT);
    digitalWrite(codeurPinA, HIGH);                 //turn pullup resistor on
    digitalWrite(codeurPinB, HIGH);                 //turn pullup resistor on
    attachInterrupt(0, updateEncoderA, CHANGE);     //Pin 0 et 1 interrupteur à chaque changement d'état
    attachInterrupt(1, updateEncoderB, CHANGE);     //renvoi vers updateEncoder
    
    Serial.begin(9600);
    Serial.flush();

    tick_codeur = 0;

    FlexiTimer2::set(CADENCE_MS, 1/1000., calculs); // résolution timer = 1 ms
    FlexiTimer2::start();    
}


void loop() {
  commande = 255.;
  commandeMoteur(commande);
  delay(100);
}

void commandeMoteur(double commande){
    digitalWrite(motorPlus, HIGH); 
    digitalWrite(motorMoins, LOW);   
    analogWrite(enablePin, commande);
}

//CALCULS                                                     // Programme d'interruption
  
void calculs(){                                             // SERT AUSSI POUR IMPLEMENTATION PID
  int codeurDeltaPos;   
  codeurDeltaPos = tick_codeur;                               // Nombre de ticks codeur depuis la dernière fois
  tick_codeur = 0;
  
  omega = ((2.*3.141592*((double)codeurDeltaPos))/810.)/dt;   // Calcul de la vitesse de rotation
  theta = theta_precedent + omega*dt;                         // Calcul position
  theta_precedent = theta;

  temps += dt;
}


void updateEncoderA()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur--;
  }
  else {
    tick_codeur++;
  }
}

void updateEncoderB()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur++;
  }
  else {
    tick_codeur--;
  }
}

Cependant le problème reste le même (le nombre d'impulsions, soit tick_codeur), le programme me renvoie une vitesse constante de 14 rad.s-1 ce qui est gigantesque par rapport à la vitesse réelle de 16 rpm. Il semble cependant que le timer des calculs fonctionne bien (j'avais testé en affichant les variables "temps" et omega).

Salut,

La vitesse de 14 rad/s est au niveau du moteur, celle de 16 rpm est celle en sortie du motoréducteur ?

14 rad/s cela fait 134tr/min, ça fait pas beaucoup comme vitesse de rotation moteur, il doit y a voir un soucis dans le calcul de cette vitesse, car avec le reducteur 1/270 , cela ferai un trés petite vitesse sortie réducteur ??.

Pour le moment a tu un moyen de mesurer simplement la vitesse sortie réducteur ?

Salut !
Mon calcul d'omega est fait pour calculer la vitesse en sortie du réducteur ( /810.). Je pense que l'erreur vient de CodeurDeltaPos soit tick_codeur, qui renvoie un chiffre beaucoup trop élevé (11 000 au lieu de 810 pour environ un tour de l'arbre de sortie)

Pour plus de précision sur le calcul voici la datasheet :
"This encoder provides 3 counts per revolution of the rear shaft."
"810 counts per main shaft revolution for 1:270 geared motor" (soit 3270)
Si N impulsions sont comptées en dt secondes :
omega = 2
piN/(dt810)
Sauf erreur de ma part ^^

EDIT : Mon moteur est censé être très peu rapide (16 rpm me semble juste à vue d'oeil), je pense que l'erreur vient du nombre d'impulsions reçues par la carte arduino et donc de la gestion d'interruption. C'est la première fois que j'utilise un encodeur et les interruptions mais le résultat de 11000 impulsions au lieu de 810 pour un tour de moteur à vue d'oeil me semble problématique. La partie timer me semble juste car la vitesse était à peu près constante (entre 13.5 et 14 rad/s ce qui n'est par contre pas proche de la réalité). Je ne dispose malheureusement que de mon oeil pour calculer la vitesse de mon moteur

Tu as 810 impulsions pour un tour réducteur avec un rapport 1/270, donc ce qui fait 810/270 = 3 , donc bien 3 impulsions par tour moteur, comme indiqué dans le doc moteur.

La vitesse de rotation du motoreducteur est combien sous 5V ??, car sur image moteur doc il y a indiqué 185 rpm .

Oui, c'est pour ça que je pense que mon calcul d'omega est juste. Cependant je ne retrouve pas cette valeur de 810 impulsions lorsque je print "tick_codeur" (censé compter le nombre d'impulsions) et que je laisse mon moteur tourner un tour à vue d'oeil.

Je n'ai pas réussi à faire fonctionner mon moteur sous 5V, il fonctionne sous 12V et je viens de vérifier l'arbre moteur tourne bien à 16 rpm pour une commande analogWrite(enablePin, 255).

Je ne vois pas l'indication de 185 rpm par contre.

Si la vitesse en sortie est de 16 rpm , cela fait une vitesse moteur de 16*270 / 60 = 72 tours/s, donc une fréquence de 216 impulsions /s

Et 216 impulsions / s ferait donc 21.6 impulsions à chaque adresse de calculs() ce qui me donnerait une vitesse omega de 1.65 rad/s soit 16 rpm ce qui serait juste mais là omega est de 14 rad/s (ce qui est faux, omega devrait être de 1.65 et non 14, et l'erreur ne semble pas provenir de ma fonction calculs() ).
Je ne vois donc vraiment où se trouve l'erreur si ce n'est dans mes updateEncoderA et B

Alors 72 tours/s correspond à 452.4 rad/s , donc soucis si tu trouves 14 rad/s

Si tu trouves 14 rad/s soit 134 tr/min au niveau du moteur (codeur) cela fait 134/270 = 0.5 tr/min au niveau sortie reducteur ?

Tu fais des mesures sur codeur toute les 1 ms c'est bien ça ??, sauf que tu as 216 impulsions par seconde , donc sur 1 ms, tu auras 216/1000 = 0.216 impulsion !!, le soucis provient surrement de la ?.

Je sais qu'il y a un soucis avec 14 rad/s. Je devrais trouver 1.65 rad/s, ce qui correspond à 810 impulsions par tour ou 21.6 impulsions pour 10 ms (je réalise la fonction calculs() toutes les 10ms mais le nombre d'impulsions est compté par interruption).

Je ne pense pas que l'erreur vient de la fonction calculs() sur laquelle on se concentre mais bien d'updateEncoder(). En effet, la fonction calculs() renvois des résultats cohérent bien que faux : 14 rad/s correspond bien au 11 000 impulsions reçues mais ne correspond pas à la réalité. Je pense donc que le problème vient de ma gestion des interruptions et non pas de mon calcul d'omega ni de mon timer donc de la fonction updateEncoder(). En effet le résultat de 11 000 impulsions pour 1 tour est faux (cela devrait être proche de 810) et cela vient uniquement de la gestion d'interruption. Le résultat de 14 rad/s vient du fait que tick_codeur (le nombre d'impulsions) est trop élevé.

Je cherche donc à savoir ce qui est faux dans ma gestion d'interruption :

void updateEncoderA()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur--;
  }
  else {
    tick_codeur++;
  }
}

void updateEncoderB()
{
  if (digitalReadFast2(codeurPinA) == digitalReadFast2(codeurPinB)) {
    tick_codeur++;
  }
  else {
    tick_codeur--;
  }
}

hello
je n'ai pas regardé en détail ton code, mais le fait que tu incrementes la même variable pour le signal A et pour le signal B ...me laisse perplexe.

en principe, un signal donne les tours et l'autre le sens

edit: c'est ok, je suis allé voir sur le lien donné par le sept
c'est bien la même variable qui est soit incrémentée soit décrémentée selon le sens de rotation

Je viens de tester une autre méthode et le résultat est identique (environ 11 000 impulsions par tour de l'arbre de sortie pour encoderValue)

void updateEncoder(){
  int MSB = digitalRead(codeurPinA); //MSB = most significant bit
  int LSB = digitalRead(codeurPinB); //LSB = least significant bit
 
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
 
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
 
  lastEncoded = encoded; //store this value for next time
}

EDIT : Je viens encore de tester une nouvelle méthode et toujours le même résultat tick_codeur s'envole vers les 10 000 très rapidement.

#include <Encoder.h>
Encoder myEncoder(2,3);


void updateEncoder(){
    tick_codeur = myEncoder.read();
}

petite question de debutant :

A quoi servent les 2 resistances svp ?
Ca evite de passer par du code Pull up d'arduino ?