Control DC motor avec un potentiomètre

Bonjour,

Je suis un étudiant en licence pro système embarqué et je suis actuellement en stage. Pendant ce stage, on me demande de réaliser l'asservissement en position d'un moteur a courant continue à l'aide d'un potentiomètre.

Je me suis basé sur la librairie PID_V1 de arduino ainsi que l'exemple du site :

J'aimerais exactement reproduire la même chose que sur sa vidéo mais je rencontre quelques problème, quand je tourne mon potentiomètre, le moteur tourne mais ne s'arrete pas en fonction de la position du potentiomètre :

Objectif : Potentiomètre déplacer de 30° par exemple alors le moteur ce déplace de 30° et attend la nouvelle consigne du potentiomètre.

Je vous transmet le code que j'ai réaliser jusqu'à présent , je ne pense plus être loin du but mais tout aide est bonne à prendre

Pardon s'il y a des fautes
MERCI

/********************************************************
 * PID Basic Example
 * Reading analog input 0 to control analog PWM output 3
 ********************************************************/

#include <PID_v1.h>

//Define Variables we'll be connecting to double Setpoint, Input, Output;
double Setpoint, Input, Output;

//Setpoint = consigne
//Input = Position
//Output = Vitesse en sortie réguler par PID

int encoder0PinA = 3;//broche 3
int encoder0PinB = 4;//broche 4
int encoder0Pos = 0;//initialisation pos codeur 0
int encoder0PinALast = LOW;
int n = LOW;

// Constructeur. Relie le PID à l'entrée, de sortie, et la consigne
PID myPID(&Input, &Output, &Setpoint,1,5,1, DIRECT);//DIRECT = sens moteur (INVERSE est possible)

void setup()
{
  Serial.begin(9600);
  
  //initialize the variables we're linked to
  Input = analogRead(0); //Initialisation entrée est la valeur du potard (en position)
  myPID.SetOutputLimits(0, 255);
  Setpoint = 100; //Consigne
  pinMode(encoder0PinA, INPUT);
  pinMode(encoder0PinB, INPUT);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
}

void loop()
{
  //Setpoint = abs(Setpoint);
  Encoder(); // Appel fonction détermine pos encoder0Pos
  //Setpoint = encoder0Pos; // La consigne est la position de l'encoder
  Input = analogRead(0); //Initialisation entrée est la valeur du potard (en position)
  Input = map(Input, 0, 1023, 0, 512);// l'entrée est la valeur du potard (en position) mapper de 0 a 512 pour un tour de moteur
  Setpoint = encoder0Pos;
  myPID.Compute(); // Applique l'algoritmhe PID , compute appelé a chaque fois mais n'execute rien si cela n'est pas utile.
  analogWrite(9,Output);// applique au moteur la sortie régulé
}


/*Fonction permettant de connaitre la position de l'encoder (du moteur aussi)*/
void Encoder(){
 
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }
     Serial.println (encoder0Pos);//affiche pos encoder
     Serial.print ("/");
   }
   encoder0PinALast = n;
}

Bonjour,

Tu as inversé la consigne et l'entrée.

Est tu sûr que c'est seulement sa qui m'apporterais une solution ?

Non, je ne suis pas sur qu'il n'y ait que ce problème.

Bonjour,

Tu écris:

Setpoint = encoder0Pos;

Il y a au moins ça qui ne va pas: tu ne peux pas remplacer ta consigne par la position de l'encodeur. La consigne doit rester fixe et la position de l'encodeur doit venir rejoindre cette consigne.

Certe, mais ce que j'aimerais c'est que la consigne soit la position du potentiomètre pour que quand je le déplace, le moteur se déplace du même angle et s'arrête a la position souhaiter, et si je tourne de nouveau le potentiomètre, le moteur prennent le nouvel angle donner par le potentiomètre.

Si vous avez des idées je suis preneurs car j'ai bien compris le fonctionnement du PID mais j'ai du mal a comprendre comment associer mon potentiomètre a mon moteur.

Si tu veux que la consigne soit la valeur du potentiomètre il faut mettre la valeur du potentiomètre dans Setpoint, éventuellement redimensionné pour être dans la plage de valeur de la position du moteur.

Voici la datasheet du moteur, 1tour de moteur = 512 tour de roue codeuse.

qu'entend tu par redimensionner la plage de valeur :

Input = map(Input, 0, 1023, 0, 512); ??

Il faut faire les choses par étape: commence par faire un asservissement avec une consigne fixe et ensuite tu pourras ajouter la gestion du potentiomètre.

Input = map(Input, 0, 1023, 0, 512);

Oui, c'est ça si ta position va de 0 à 512, il faut que la consigne aille de 0 à 512.
Mais comme je te l'ai dis la consigne c'est Setpoint

Setpoint = map(analogRead(A0), 0, 1023, 0, 512);

Merci de ta réponse kamill mais j'ai encore une petite question :

Voici la librairie de la fonction compute :

erreur = ma consigne - l'entrée , si ma consigne est la valeur de mon potentiomètre alors que dois-je mettre pour l'input ?

bool PID::Compute()
{
   if(!inAuto) return false;
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
     
      /*Compute all the working error variables*/
      double input = *myInput;
      double error = *mySetpoint - input;
      ITerm+= (ki * error);
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput;
      Serial.print("INPUT : "); //Affiche INPUT
      Serial.println(input); //val INPUT
      Serial.print("OUTPUT librairie : ");
      Serial.println(output); //Supérieur a 255

  if(output > outMax) output = outMax; //
      else if(output < outMin) output = outMin;
  *myOutput = output;
      Serial.print("OUTPUT Arduino : ");
      Serial.println(output); //Supérieur a 255
  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
  return true;
   }
   else return false;
}

Input c'est la valeur que tu mesures, dans ton cas c'est la position du moteur.

Merci

Donc si je résume bien ma consigne c'est la valeur du potentiomètre avec mapping et mon entrée c'est la position de mon encodeur qui me renvoie la position du moteur

oui

Malgrès cela, quand je positionne mon potentiomètre au lancement sur 0, rien ne se passe meme si je le bouge par la suite

Alors que quand je le positionne sur le 1023 le moteur tourne en continue

J'ai actuellement ce code :

/********************************************************
 * PID Basic Example
 * Reading analog input 0 to control analog PWM output 3
 ********************************************************/

#include <PID_v1.h>

//Define Variables we'll be connecting to double Setpoint, Input, Output;
double Setpoint, Input, Output;

//Setpoint = consigne
//Input = Position moteur
//Output = Vitesse en sortie réguler par PID

int encoder0PinA = 3;//broche 3
int encoder0PinB = 4;//broche 4
int encoder0Pos = 0;//initialisation pos codeur 0
int encoder0PinALast = LOW;
int n = LOW;

// Constructeur. Relie le PID à l'entrée, de sortie, et la consigne
PID myPID(&Input, &Output, &Setpoint,1,5,1, DIRECT);//DIRECT = sens moteur (INVERSE est possible)

void setup()
{
  Serial.begin(9600);
  
  //initialize the variables we're linked to
  Input = 0; //Initialisation entrée 0
  Setpoint = 0; //Consigne initialisé a 0
  encoder0Pos = 0;
  //myPID.SetOutputLimits(0, 255);
  pinMode(encoder0PinA, INPUT);
  pinMode(encoder0PinB, INPUT);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
}

void loop()
{
  //Setpoint = abs(Setpoint);
  Encoder(); // Appel fonction détermine pos encoder0Pos
  Setpoint = map(analogRead(A0), 0, 1023, 0, 512); //La consigne est la position du potentiomètre on mappe sa pour un tour moteur 0 --> 512
  Input = encoder0Pos; //Initialisation entrée (en position)
  myPID.Compute(); // Applique l'algoritmhe PID , compute appelé a chaque fois mais n'execute rien si cela n'est pas utile.
  analogWrite(9,Output);// applique au moteur la sortie régulé
}


/*Fonction permettant de connaitre la position de l'encoder (du moteur aussi)*/
void Encoder(){
 
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }
     Serial.println (encoder0Pos);//affiche pos encoder
     Serial.print ("/");
   }
   encoder0PinALast = n;
}

Il faut que tu affiches Input et Output pour essayer de comprendre ce qui se passe.

C'est ce que je fesais déjà ... Mais du coup je ne comprend pas :

Mon encoder pos s'incrémente jusqu'à énormément sa ok et output reste toujours a 0 ...

Comment ça se fait que ton moteur tourne alors que la sortie est à 0? Il est commandé comment?

C'est justement cela que je ne comprend pas :

La totalité de mon code a été transmit, je lance ma fonction Encoder(); pour obtenir la position du moteur donc 0 la 1ère fois logiquement.

Ensuite je dit que ma ocnsigne est le pot A0

Je dit que l'entrée input c'est la pos de encoder0Pos et donc du moteur et ensuite j'appel myPID.Compute();

/* Compute() **********************************************************************
 *     This, as they say, is where the magic happens.  this function should be called
 *   every time "void loop()" executes.  the function will decide for itself whether a new
 *   pid Output needs to be computed.  returns true when the output is computed,
 *   false when nothing has been done.
 **********************************************************************************/ 
bool PID::Compute()
{
   if(!inAuto) return false;
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
    /*Serial.print("timeChange");
    Serial.println(timeChange);
    Serial.print("SampleTime");
    Serial.println(SampleTime);*/
   if(timeChange>=SampleTime)
   {
     
      /*Compute all the working error variables*/
      double input = *myInput; //entrée position moteur
      double error = *mySetpoint - input; //erreur egal position désiré - pos actu
      ITerm+= (ki * error); //somme erreurs
      if(ITerm > outMax) ITerm= outMax; //prend pour valeur 255
      else if(ITerm < outMin) ITerm= outMin; // "" 0
      double dInput = (input - lastInput); // position actuel - derniere position
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput; //Applique les coeff PID
      Serial.print("INPUT : "); //Affiche INPUT
      Serial.println(input); //val INPUT
      //Serial.print("OUTPUT librairie : ");
      //Serial.println(output); //Supérieur a 255

	  if(output > outMax) output = outMax; //
      else if(output < outMin) output = outMin;
	  *myOutput = output;
      Serial.print("OUTPUT Arduino : ");
      Serial.println(output); //Supérieur a 255 = out of limit
	  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
	  return true;
   }
   else return false;
}

Cela doit venir de outmin et outmax mais je ne comprend pas pourquoi quand il est a 0 le moteur tourne.

Quand tu fais
analogWrite(9,Output);// applique au moteur la sortie régulé
avec Output=0, le moteur ne doit pas tourner. Là il y a quelque-chose qui ne va pas!