Control DC motor avec un potentiomètre

Il faut aussi modifier SetPoint et Input en fonction de ta plage de mesure de position.
Il faut afficher SetPoint, Input et Output.

Je te remercie, je teste sa demain et je tiendrais au courant le forum de l'avancement de mon projet car j'ai vue beaucoup de personne ayant le même problème que moi.

Merci

Actuellement j'ai ce code :

 Setpoint = (double)(sensorValue - 512)*0.35; //plage allant de setpoint +/- 180deg
  Input = (double)encoder0Pos*11.25; //360 deg = 32 pulses
  Input = constrain(Input, -512, 512);//Restreint la valeur de lencodeur a -512 +512
  compute(); // Applique les coeff PID si nécéssaire

Pour effectuer des réglages sur mon entrée Input et ma consigne Setpoint je ne sais pas trop comment m'y prendre.

La documentation du moteur dit que le nombre d'impulsions de l'encodeur pour 1 tour moteur est de 512, Mais comment faire pour associer cette valeur en degrès , car la ligne de code suivante :

Input = (double)encoder0Pos*11.25; //360 deg = 32 pulses

Je ne la comprend pas du tout.

Merci d'avance.

Si tu veux faire utiliser les degré il faut convertir en degré 360° -> 512 pas

Input=encoder0Pos*360.0/512;

I faut aussi convertir ton potentiometre en ° 360° -> 1024 pas

SetPoint=sensorValue*360.0/1024;

Merci mais juste une question, le code suivant

Input = constrain(Input, -512, 512);//Restreint la valeur de lencodeur a -512 +512

J'ai rajouter cela car mon encodeur ne s'arrete jamais au niveau du calcul des interruptions , cette ligne est elle ,écéssaire ou bien inutile du coup

si tu traites Input en degré, il ne va pas varier de -512 à + 512, mais de 0 à 360.
Normalement si l'asservissement est bon, ton moteur ne devrait jamais faire plus d'un tour.
Si tu veux contraindre entre 0 et 360 il faut contraindre encoder0Pos

encoder0Pos=constrain(encoder0Pos,0,512);

Sa marche presque !! :slight_smile: Seulement ce n'est pas très fluide et je pense devoir règler la sortie ouput

void compute()

{
/*Temps écoulé depuis le dernier calcul*/

  unsigned long now = millis();
  double dT = (double)(now - lastTime); //Temps d'échantillonnage
  /*Calcul des variables d'erreur*/
  double error = Setpoint - Input;
  errSum += (error * dT);
  double dErr = (error - lastErr) / dT;
  /*Calcul PID Output*/
  Output = kp * error + ki * errSum + kd * dErr;
  
  /*Max 200, Min -200*/
  if(Output>200){Output = 200;}
  else if(Output <-200){Output = -200;}
  else{}
  
  /*Reprise des variables précédentes pour la consigne suivante*/
  lastErr = error;
  lastTime = now;
}

Voila le calcul PID mais quand je tourne mon potentiomètre sa n'est pas fluide est sur mon Serial.println("Output") il me renvoie parfois "nan" Que signifie ce nan. Merci.

nan signifie "not a number"
ça peut arriver parce que le nombre est pas (ou mal) initialisé, mais ça arrive souvent en cas de division par 0.

Je ne sais pas d'ou viens ce problème de not a number dans mon code je n'effectue pas de division par 0, en attendant Input est quasiment égal à ma consigne (a 0.xx près) cela a donc le comportement que je souhaite, mais quand je tourne mon potentiomètre, le moteur tourne dans le même sens mais pas vraiment de la même position.

Exemple pour 30° ou plus de consigne potentiomètre, le moteur doit tourner d'environ 5 / 6 ° et la je ne comprend pas : Je pense que cela vien du input et de ses limites mais je ne sais pas trop quoi mettre

Je vous transfert mon code actuelle "PRESQUE" fonctionnel

/*Dans ce programme, nous utilisons des interruptions pour lire un codeur rotatif. C'est un travail parfait pour interruption parce que la
routine de service d'interruption (fonction doEncoder) peut être courte et rapide. Lorsque l'Arduino voit un changement d'état
sur le canal A (connecter à la broche 2), il saute immédiatement à la fonction "doEncoder" qui analyse
à la fois les état LOW à HIGH et le HIGH-à-LOW, en comptant par conséquent deux fois plus de transitions. Ainsi, le
résolution du codeur est augmenté deux fois. 
En fonction de la boucle, la position de référence (consigne) est obtenue à partir de la position du potentiomètre.
Ensuite, nous obtenons la position de la roue codeuse en fonction de "doEncoder". Après cela, "calcul(compute)" du PID de fonction
l'erreur, pour cela soustrait la valeur de consigne avec la valeur mesurée. La sortie du régulateur est calculée. 
Le résultat de la sortie (Output) est utilisé comme commande de moteur dont le signe +/- indique le sens de rotation.*/


int MotorIN_A = 9;// Sens anti-horaire 
int MotorIN_B = 10;// Sens horaire
int encoder0PinA = 3; //Pin encodeur 
int encoder0PinB = 4;//Pin encodeur
int sensorPin = A0; // Pin potentiomètre
int count = 0;

/*working variables*/
double Input, Output, Setpoint; //Variable interraction moteur
double kp, ki, kd; //Constante PID
volatile unsigned long lastTime; 
volatile double errSum, lastErr; //Variable calcul erreur
volatile int encoder0Pos = 0; //Variable pos encodeur
int sensorValue = 0;


void setup(){
  
  delay(1000);
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);
  attachInterrupt(0, doEncoder, CHANGE); //A chaque changement d'état sur la pin connecté, on entre dans la fonction do Encoder
  
  //Controller Parameter
  kp = 2;
  ki = 0;
  kd = 0;
  Serial.begin (9600); //Initialisation port série
  Serial.println("start");
}

void loop(){
  
  sensorValue = analogRead(sensorPin); //Lecture valeur potentiomètre
  //sensorValue = map(analogRead(sensorPin), 0, 1023, 0, 512);//Mapping de la valeur du potentiomètre pour 1 tour du moteur (512 tours codeuse)
  
  //Setpoint = (double)(sensorValue - 512)*0.35; //Plage allant de setpoint +/- 180deg
  Setpoint=sensorValue*360.0/1024; //Conversion de la valeur du potentiomètre 0--> 1024 en degrès 0 --> 360°
  Input = encoder0Pos*360.0/512; //Conversion d'un tour moteur de 512 pas en degrès 360° = 1 tour moteur
  Input = constrain(Input, 0, 512);//Restreint la valeur de lencodeur a -512 +512
  compute(); // Applique les coeff PID si nécéssaire
  // Serial.print("Output");
  //Serial.println(Output);
   
  //Envois des commande moteur CC
  if (Output > 0) {
    
    digitalWrite(MotorIN_A,0);
    analogWrite(MotorIN_B, Output);
  }
  else if (Output < -0) {
    
    digitalWrite(MotorIN_B,0);
    analogWrite(MotorIN_A, Output);
  }
  else {
    digitalWrite(MotorIN_A,0);
    digitalWrite(MotorIN_B,0);
  }
  
// Affichage de la consigne et des valeurs mesurés 

if(count>50) {

  Serial.print("Setpoint");
  Serial.println(Setpoint);
  Serial.print("Input");
  Serial.println(Input);
  Serial.print("Output");
  Serial.println(Output);
  count= 0;

}

else{count+=1;}

}

/*Fonction control du moteur
void CC(){
  
  digitalWrite(MotorIN_A,HIGH);//Le moteur tourne dans le sens horraire
  digitalWrite(MotorIN_B,LOW);//Le moteur ne tourne pas
}
void CCW(){
  
  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,HIGH);//Le moteur tourne dans le sens anti-horraire
}
void STOP(){
  
  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,LOW); //Le moteur ne tourne pas
}
*/

// Applique l'algoritmhe PID , compute est appelé à chaque fois mais n'execute rien si cela n'est pas utile.
void compute()
{
/*Temps écoulé depuis le dernier calcul*/

  unsigned long now = millis();
  double dT = (double)(now - lastTime); //Temps d'échantillonnage
  /*Calcul des variables d'erreur*/
  double error = Setpoint - Input;
  errSum += (error * dT);
  double dErr = (error - lastErr) / dT;
  /*Calcul PID Output*/
  Output = kp * error + ki * errSum + kd * dErr;
  
  /*Max 200, Min -200*/
  if(Output>200){Output = 200;}
  else if(Output <-200){Output = -200;}
  else{}
  
  /*Reprise des variables précédentes pour la consigne suivante*/
  lastErr = error;
  lastTime = now;
}

//doEncoder function
/*Si l'est deux pin de l'encodeur renvois état HAUT ou état BAS , le moteur tourne sens horraire
*Si leurs états sont différents , il tourne sens anti-horraire*/

void doEncoder(){
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
  encoder0Pos++;
}
  else {
  encoder0Pos--;
  
  }
  encoder0Pos=constrain(encoder0Pos,0,512);
}

J'ai commenter au maximum mon code pour plus de lisibilitée.

Merci

sojobo38420:
Je ne sais pas d'ou viens ce problème de not a number dans mon code je n'effectue pas de division par 0, en attendant Input est quasiment égal à ma consigne (a 0.xx près) cela a donc le comportement que je souhaite, mais quand je tourne mon potentiomètre, le moteur tourne dans le même sens mais pas vraiment de la même position.

Tu divises par dT. Si ta loop est trop rapide, tu as dT=0. Mets un delay(20); à la fin de ta loop.

Quand tu tournes le potentiomètre, est ce que le moteur s'arrête.

Dans ton code je ne vois pas d'affichage de SetPoint, Input, OutPut. Affiche les et mets le résultat ici.
Tu lances ton programme, tu tournes le potentiomètre de 20° environ et tu mets la trace obtenue.

Quand je tourne le potentiomètre pour mes premier test oui il s'arrête ensuite mon affichage ce fait ici :

// Affichage de la consigne et des valeurs mesurés

if(count>50) {

  Serial.print("Setpoint");
  Serial.println(Setpoint);
  Serial.print("Input");
  Serial.println(Input);
  Serial.print("Output");
  Serial.println(Output);
  count= 0;

}

else{count+=1;}

}

Je met un delay(); je test et je fait remonter la trace sur le forum.

Sépares tes affichages, c'est illisible

 Serial.print("Setpoint:");
 Serial.println(Setpoint);
 Serial.print("  Input:");
 Serial.println(Input);
 Serial.print("  Output:");
 Serial.println(Output);

Bon alors petit problème sa ne marche plus du tout mnt quand je tourne mon potar, il va dans un sens et accèlere et quand jarrive a la moitié du potard il tourne dans lautre sens et plus j'accelere plus il tourne ...

/*Dans ce programme, nous utilisons des interruptions pour lire un codeur rotatif. C'est un travail parfait pour interruption parce que la
routine de service d'interruption (fonction doEncoder) peut être courte et rapide. Lorsque l'Arduino voit un changement d'état
sur le canal A (connecter à la broche 2), il saute immédiatement à la fonction "doEncoder" qui analyse
à la fois les état LOW à HIGH et le HIGH-à-LOW, en comptant par conséquent deux fois plus de transitions. Ainsi, le
résolution du codeur est augmenté deux fois.
En fonction de la boucle, la position de référence (consigne) est obtenue à partir de la position du potentiomètre.
Ensuite, nous obtenons la position de la roue codeuse en fonction de "doEncoder". Après cela, "calcul(compute)" du PID de fonction
l'erreur, pour cela soustrait la valeur de consigne avec la valeur mesurée. La sortie du régulateur est calculée.
Le résultat de la sortie (Output) est utilisé comme commande de moteur dont le signe +/- indique le sens de rotation.*/


int MotorIN_A = 9;// Sens anti-horaire
int MotorIN_B = 10;// Sens horaire
int encoder0PinA = 3; //Pin encodeur
int encoder0PinB = 4;//Pin encodeur
int sensorPin = A0; // Pin potentiomètre
int count = 0;

/*working variables*/
double Input, Output, Setpoint; //Variable interraction moteur
double kp, ki, kd; //Constante PID
volatile unsigned long lastTime;
volatile double errSum, lastErr; //Variable calcul erreur
volatile int encoder0Pos = 0; //Variable pos encodeur
int sensorValue = 0;


void setup(){
 
  delay(1000);
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);
  attachInterrupt(0, doEncoder, CHANGE); //A chaque changement d'état sur la pin connecté, on entre dans la fonction do Encoder
 
  //Controller Parameter
  kp = 1;
  ki = 0;
  kd = 0;
  Serial.begin (9600); //Initialisation port série
  Serial.println("start");
}

void loop(){
 
  sensorValue = analogRead(sensorPin); //Lecture valeur potentiomètre
  //sensorValue = map(analogRead(sensorPin), 0, 1023, 0, 512);//Mapping de la valeur du potentiomètre pour 1 tour du moteur (512 tours codeuse)
 
  //Setpoint = (double)(sensorValue - 512)*0.35; //Plage allant de setpoint +/- 180deg
  Setpoint=sensorValue*360.0/1024; //Conversion de la valeur du potentiomètre 0--> 1024 en degrès 0 --> 360°
  encoder0Pos=constrain(encoder0Pos,0,512);
  Input = encoder0Pos*360.0/512; //Conversion d'un tour moteur de 512 pas en degrès 360° = 1 tour moteur
  //Input = constrain(Input, 0, 512);//Restreint la valeur de lencodeur a -512 +512
  compute(); // Applique les coeff PID si nécéssaire
  // Serial.print("Output");
  //Serial.println(Output);
   
  //Envois des commande moteur CC
  if (Output > 0) {
   
    digitalWrite(MotorIN_A,0);
    analogWrite(MotorIN_B, Output);
  }
  else if (Output < -0) {
   
    digitalWrite(MotorIN_B,0);
    analogWrite(MotorIN_A, Output);
  }
  else {
    digitalWrite(MotorIN_A,0);
    digitalWrite(MotorIN_B,0);
  }
 
// Affichage de la consigne et des valeurs mesurés

if(count>50) {

  Serial.print("Setpoint:");
  Serial.println(Setpoint);
  Serial.print("  Input:");
  Serial.println(Input);
  Serial.print("  Output:");
  Serial.println(Output);;
  count= 0;

}

else{count+=1;}
delay(20);
}

/*Fonction control du moteur
void CC(){
 
  digitalWrite(MotorIN_A,HIGH);//Le moteur tourne dans le sens horraire
  digitalWrite(MotorIN_B,LOW);//Le moteur ne tourne pas
}
void CCW(){
 
  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,HIGH);//Le moteur tourne dans le sens anti-horraire
}
void STOP(){
 
  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,LOW); //Le moteur ne tourne pas
}
*/

// Applique l'algoritmhe PID , compute est appelé à chaque fois mais n'execute rien si cela n'est pas utile.
void compute()
{
/*Temps écoulé depuis le dernier calcul*/

  unsigned long now = millis();
  double dT = (double)(now - lastTime); //Temps d'échantillonnage
  /*Calcul des variables d'erreur*/
  double error = Setpoint - Input;
  errSum += (error * dT);
  double dErr = (error - lastErr) / dT;
  /*Calcul PID Output*/
  Output = kp * error + ki * errSum + kd * dErr;
 
  /*Max 200, Min -200*/
  if(Output>200){Output = 200;}
  else if(Output <-200){Output = -200;}
  else{}
 
  /*Reprise des variables précédentes pour la consigne suivante*/
  lastErr = error;
  lastTime = now;
}

//doEncoder function
/*Si l'est deux pin de l'encodeur renvois état HAUT ou état BAS , le moteur tourne sens horraire
*Si leurs états sont différents , il tourne sens anti-horraire*/

void doEncoder(){
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
  encoder0Pos++;
}
  else {
  encoder0Pos--;
 
  }
  //encoder0Pos=constrain(encoder0Pos,0,512);
}

Voila le résultat que j'ai obtenue Pour une consigne potentiomètre de 20°

Concernant le comportement du moteur, Il tourne un premier coup je ne sais pourquoi de plus d'un angle inconnue, ensuite il s'arrête, quand jessaye de lui mettre une consigne de 20 ° il se déplace un peu ou alors il va trop loin et change de sens pour revenir mais de façons abusive il fait au moin deux ou trois tour moteur.

Voila j'espere que cela peut aider a interpréter mon problème.

Personne ?

ReBonjour,

Bon reprenons les choses calmement.

J'ai un peu modifié ton dernier fichier pour avoir un affichage exploitable.

/*Dans ce programme, nous utilisons des interruptions pour lire un codeur rotatif. C'est un travail parfait pour interruption parce que la
  routine de service d'interruption (fonction doEncoder) peut être courte et rapide. Lorsque l'Arduino voit un changement d'état
  sur le canal A (connecter à la broche 2), il saute immédiatement à la fonction "doEncoder" qui analyse
  à la fois les état LOW à HIGH et le HIGH-à-LOW, en comptant par conséquent deux fois plus de transitions. Ainsi, le
  résolution du codeur est augmenté deux fois.
  En fonction de la boucle, la position de référence (consigne) est obtenue à partir de la position du potentiomètre.
  Ensuite, nous obtenons la position de la roue codeuse en fonction de "doEncoder". Après cela, "calcul(compute)" du PID de fonction
  l'erreur, pour cela soustrait la valeur de consigne avec la valeur mesurée. La sortie du régulateur est calculée.
  Le résultat de la sortie (Output) est utilisé comme commande de moteur dont le signe +/- indique le sens de rotation.*/


int MotorIN_A = 9;// Sens anti-horaire
int MotorIN_B = 10;// Sens horaire
int encoder0PinA = 3; //Pin encodeur
int encoder0PinB = 4;//Pin encodeur
int sensorPin = A0; // Pin potentiomètre
int count = 0;

/*working variables*/
double Input, Output, Setpoint; //Variable interraction moteur
double kp, ki, kd; //Constante PID
volatile unsigned long lastTime;
volatile double errSum, lastErr; //Variable calcul erreur
volatile int encoder0Pos = 0; //Variable pos encodeur
int sensorValue = 0;


void setup() {

  delay(1000);
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);
  attachInterrupt(0, doEncoder, CHANGE); //A chaque changement d'état sur la pin connecté, on entre dans la fonction do Encoder

  //Controller Parameter
  kp = 1;
  ki = 0;
  kd = 0;
  Serial.begin (9600); //Initialisation port série
  Serial.println("start");
}

void loop() {

  sensorValue = analogRead(sensorPin); //Lecture valeur potentiomètre
  //sensorValue = map(analogRead(sensorPin), 0, 1023, 0, 512);//Mapping de la valeur du potentiomètre pour 1 tour du moteur (512 tours codeuse)

  //Setpoint = (double)(sensorValue - 512)*0.35; //Plage allant de setpoint +/- 180deg
  Setpoint = sensorValue * 360.0 / 1024; //Conversion de la valeur du potentiomètre 0--> 1024 en degrès 0 --> 360°
  encoder0Pos = constrain(encoder0Pos, 0, 512);
  Input = encoder0Pos * 360.0 / 512; //Conversion d'un tour moteur de 512 pas en degrès 360° = 1 tour moteur
  //Input = constrain(Input, 0, 512);//Restreint la valeur de lencodeur a -512 +512
  compute(); // Applique les coeff PID si nécéssaire
  // Serial.print("Output");
  //Serial.println(Output);

  //Envois des commande moteur CC
  if (Output > 0) {

    digitalWrite(MotorIN_A, 0);
    analogWrite(MotorIN_B, Output);
  }
  else if (Output < -0) {

    digitalWrite(MotorIN_B, 0);
    analogWrite(MotorIN_A, Output);
  }
  else {
    digitalWrite(MotorIN_A, 0);
    digitalWrite(MotorIN_B, 0);
  }

  // Affichage de la consigne et des valeurs mesurés
  Serial.print("Setpoint:");
  Serial.print(Setpoint);
  Serial.print("  Input:");
  Serial.print(Input);
  Serial.print("  Output:");
  Serial.println(Output);

  delay(20);
}

/*Fonction control du moteur
  void CC(){

  digitalWrite(MotorIN_A,HIGH);//Le moteur tourne dans le sens horraire
  digitalWrite(MotorIN_B,LOW);//Le moteur ne tourne pas
  }
  void CCW(){

  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,HIGH);//Le moteur tourne dans le sens anti-horraire
  }
  void STOP(){

  digitalWrite(MotorIN_A,LOW); //Le moteur ne tourne pas
  digitalWrite(MotorIN_B,LOW); //Le moteur ne tourne pas
  }
*/

// Applique l'algoritmhe PID , compute est appelé à chaque fois mais n'execute rien si cela n'est pas utile.
void compute()
{
  /*Temps écoulé depuis le dernier calcul*/

  unsigned long now = millis();
  double dT = (double)(now - lastTime); //Temps d'échantillonnage
  /*Calcul des variables d'erreur*/
  double error = Setpoint - Input;
  errSum += (error * dT);
  double dErr = (error - lastErr) / dT;
  /*Calcul PID Output*/
  Output = kp * error + ki * errSum + kd * dErr;

  /*Max 200, Min -200*/
  if (Output > 200) {
    Output = 200;
  }
  else if (Output < -200) {
    Output = -200;
  }
  else {}

  /*Reprise des variables précédentes pour la consigne suivante*/
  lastErr = error;
  lastTime = now;
}

//doEncoder function
/*Si l'est deux pin de l'encodeur renvois état HAUT ou état BAS , le moteur tourne sens horraire
  Si leurs états sont différents , il tourne sens anti-horraire*/

void doEncoder() {
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
    encoder0Pos++;
  }
  else {
    encoder0Pos--;

  }
  //encoder0Pos=constrain(encoder0Pos,0,512);
}

Mets ton potentiomètre à 0 et lance ton programme.
Attends d'avoir au moins une dizaine de lignes sur le moniteur série et tournes ton potentiomètre d'environ 20° sans revenir en arrière. Attends au moins 2 secondes.
Mets l'affichage sur le forum entre balises code
Ne fait pas de hardcopy d'écran ou on ne voit que quelques lignes. Sélectionne l'affichage dans le moniteur et copie le (sous windows c'est CTRL A CTRL C, je ne sais pas si c'est la même chose sous linux)