Using PID algorithm to control 5 motors. How to lighten Void loop

PieterP:
I'm interested to know what makes you think that control theory is pseudoscience?

You must be a lot more careful with your use of language. I did not say that "control theory" is pseudoscience.

...R

Could you explain what you did mean?

Robin2:
You must be a lot more careful with your use of language. I did not say that "control theory" is pseudoscience.

...R

PID is a part of control theory and neither PID nor control theory in general are pseudosciences - if they were, then my classical controls professor wouldn't have been able to teach us controls classes in college :smiley:

Trust me, I've taken several systems/controls classes in college: it is an art, but there is a helluva bit of hard science and math involved. But that's just it - if you don't have the foundational background, it makes understanding the high level explanations of PID extremely difficult.

To get back on track with OP's question, I tried to understand what's going on, but the sketch is rather unreadable for me - could use better use of whitespace, better naming conventions (instead of just appending 'a' to every variable, etc), and could add more comments to explain what is going on. I also do not see "val_ouput" anywhere in your sketch.

PieterP:
Could you explain what you did mean?

I can't think of anything other than to repeat what I said in Reply #14
"As far as I can see most of what is written about PID is just pseudo-science"

And the evidence I have in favour of that is the good performance from my simple motor control code which uses the same calculations as the PID library but with a huge range of sampling interval.

If this business is as scientific as people make out then they would be able to produce a pair of experiments to demonstrate how it works in one case and not in another despite the best efforts to get the other case to work. We can all demonstrate the effect of gravity, or the splitting of light with prism.

...R

Robin2:
If this business is as scientific as people make out then they would be able to produce a pair of experiments to demonstrate how it works in one case and not in another despite the best efforts to get the other case to work. We can all demonstrate the effect of gravity, or the splitting of light with prism.

...R

You should take a read here. I've started to read it and I think you should too. It explains how the theory on controls (and, by extension, PID) actually works in the discrete-time domain. This is all of the science you call "pseudo-science" :wink:

The pseudo-science is the things written about it. Like astrology is written about planets and constellations.

Statements like "just set d to zero and turn it up until it is unstable" are not very scientific.

Power_Broker:
You should take a read here.

I'm afraid to. If I were to understand what is theoretically wrong with my program it might stop working :slight_smile:

...R

Robin2:
I'm afraid to. If I were to understand what is theoretically wrong with my program it might stop working :slight_smile:

...R

:smiley: I know that feeling myself all too well, lol

That would be a Schroedingbug. As soon as you look inside the box, it stops working.

Hi,

I would like two know if I can improve my program to have perfectly the same speed on two motors for a long time.(one hour at least)

Because, I have two same motor+encoder but after a couple of minutes, one of them have one half round in more.

Maybe i have got problem declaring constant in the setup with the attachinterrupt. Should i replace const byte by volatile byte or something else?

My program with the monitor detect the two speed are both same.

//The sample code for driving one way motor encoder
#include <PID_v1.h>

//MOTOR GAUCHE pin D 3,7,9,8,5
 
const byte encoder0pinA = 3;//A pin -> the interrupt pin 1
const byte encoder0pinB = 7;//B pin -> the digital pin 4

byte encoder0PinALast;  //  volatile byte encoder0PinALast 
double  latestDurationCount,duration,abs_duration;//the number of the pulses
boolean Direction;//the rotation direction 
boolean result;

double val_output;//Power supplied to the motor PWM value.
double Setpoint;
double Kp=1, Ki=10, Kd=0.01; // 0.6 ; 5 ; 0
PID myPID(&abs_duration, &val_output, &Setpoint, Kp, Ki, Kd, DIRECT); 


//**************************************************
//MOTOR DROIT pin C 2,4,11,10,6

const byte encoder0pinAa = 2;//A pin -> the interrupt pin 1
const byte encoder0pinBa = 4;//B pin -> the digital pin 7

byte encoder0PinALasta;
double  latestDurationCounta,durationa,abs_durationa;//the number of the pulses
boolean Directiona;//the rotation direction 
boolean resulta;

double val_outputa;//Power supplied to the motor PWM value.
double Setpointa;
//double Kp=0.6, Ki=5, Kd=0; // 0.6 ; 5 ; 0
PID myPIDa(&abs_durationa, &val_outputa, &Setpointa, Kp, Ki, Kd, DIRECT); 

//***************************************************

// PORT POUR KIT L298N 2nd (Bleu clair) supllied with 6 V
//  MOTEUR GAUCHE  avec myPID

//Ports de commande du moteur D supplied with 6 volt.(8,9,5,3,7,

int motorPinD1 = 9; //13 pin to set H BRIDGE of L298N  
int motorPinD2 = 8; //12 pin to set H BRIDGE of L298N  
const int enablePinD= 5; //11 //  Commande de vitesse moteur, to Output ENA pour L298 the second

//  MOTEUR C Droit  avec myPIDa 10,11,6,2,4
  
//Ports de commande du moteur C supplied with 6 volt.

int motorPinC1 = 10; // pin to set H BRIDGE of L298N
int motorPinC2 = 11; //12 pin to set H BRIDGE of L298N
const int enablePinC= 6; //11 //  Commande de vitesse moteur, to Output ENA pour L298 the second


void setup()

{  
  Serial.begin(19200);//Initialize the serial port
  
 // Configuration des ports en mode "sortie" C
   pinMode(motorPinC1, OUTPUT);   //L298N Control port settings direction of motor C (originaly L298P)
   pinMode(motorPinC2, OUTPUT);  //L298N Control port settings direction of motor C
   pinMode(enablePinC, OUTPUT);  // powerRate to control speed of motor C

   // Configuration des ports en mode "sortie" D  moteur GAUCHE
  pinMode(motorPinD1, OUTPUT);
  pinMode(motorPinD2, OUTPUT);
  pinMode(enablePinD, OUTPUT);
   
   //Originaly to Set the output value of the PID // (read between 42 and 43 pulses when setpoint=30 val outpout=47)
   Setpoint; // MOTEUR GAUCHE
   
   myPID.SetMode(AUTOMATIC);//PID is set to automatic mode
   myPID.SetSampleTime(5);//Set PID sampling frequency is 100ms
  EncoderInit();//Initialize the module
  advance();

   //REGLAGE PID  MOTEUR DROIT (pinc)
   
   Setpointa; 
   
   myPIDa.SetMode(AUTOMATIC);//PID is set to automatic mode
   myPIDa.SetSampleTime(5);//Set PID sampling frequency is 100ms
  EncoderInita();//Initialize the module
  advancea();

  
  
}
 
void loop()
{  

  
  //MOTOR GAUCHE less fast when latestDurationCount = duration; or myPID at top of void loop?
 
      noInterrupts();
      latestDurationCount = duration;
      interrupts();
      abs_duration = abs(latestDurationCount);

        
        Setpoint= 40;

      result=myPID.Compute();//PID conversion is complete and returns 1
       advance();
      if(result)
      {
        Serial.print("LD"); Serial.print(latestDurationCount); 
        Serial.print("D  : ");  Serial.print(duration); Serial.print(duration); Serial.print(duration);
        duration = 0; //Count clear, wait for the next count
        //Serial.print("Setpoint"); Serial.println(Setpoint);
        Serial.print("V"); Serial.println(val_output);// volt to MOTOR= real speed
      }

        // *******************************************

         // SETTING  MOTEUR DROIT PIDa avec PIN C
   
      noInterrupts();
      latestDurationCounta = durationa;
      interrupts();
      abs_durationa = abs(latestDurationCounta);

       Setpointa=40; //

     
      resulta=myPIDa.Compute();//PID conversion is complete and returns 1
      advancea();
      
      if(resulta)
      {
      //val_outputa124.95 latestDurationCounta100.00Plusea: 194.00

      //with 19200 bauds Setpointa20.00 val_outputa73.10 latestDurationCounta20.00Plusea: 39.00

        Serial.print("LDa"); Serial.print(latestDurationCounta);  
        Serial.print("Da: ");  Serial.print(durationa);Serial.print(durationa);Serial.print(durationa); 
        durationa = 0; //Count clear, wait for the next count
        //Serial.print("Setpointa"); Serial.println(Setpointa);
        Serial.print("Va"); Serial.println(val_outputa);// volt to MOTOR= real speed
      }
     
}
 
void EncoderInit()
{
  Direction = true;//default -> Forward  
  pinMode(encoder0pinB,INPUT);  
  attachInterrupt(1, wheelSpeed, CHANGE);
}
 
void wheelSpeed()
{
  byte Lstate = digitalRead(encoder0pinA);
  if((encoder0PinALast == LOW) && Lstate==HIGH)
  {
    byte val = digitalRead(encoder0pinB);
    if(val == LOW && Direction)
    {
      Direction = false; //Reverse
    }
    else if(val == HIGH && !Direction)
    {
      Direction = true;  //Forward
    }
  }
  encoder0PinALast = Lstate; // volatile byte encoder0PinALast ??
  
 
  if(!Direction)  duration++;
  else  duration--;

}
void advance()//Motor Forward
{
     digitalWrite(motorPinD1,HIGH);
     digitalWrite(motorPinD2,LOW);
     
     analogWrite(enablePinD,val_output);
}
void back()//Motor reverse
{
      digitalWrite(motorPinD1,LOW);
     digitalWrite(motorPinD2,HIGH);
     
     analogWrite(enablePinD,val_output);
}

void Stop()//Motor stops
{
     digitalWrite(enablePinD, LOW); 
}

void EncoderInita()
{
  Directiona = true;//default -> Forward  
  pinMode(encoder0pinBa,INPUT);  
  attachInterrupt(0, wheelSpeeda, CHANGE);
}
 
void wheelSpeeda()
{
  byte Lstatea = digitalRead(encoder0pinAa);
  if((encoder0PinALasta == LOW) && Lstatea==HIGH)
  {
    byte vala = digitalRead(encoder0pinBa);
    if(vala == LOW && Directiona)
    {
      Directiona = false; //Reverse
    }
    else if(vala == HIGH && !Directiona)
    {
      Directiona = true;  //Forward
    }
  }
  encoder0PinALasta = Lstatea;
 
  if(!Directiona)  durationa++;
  else  durationa--;

}
void advancea()//Motor Forward
{
     digitalWrite(motorPinC1,HIGH);
     digitalWrite(motorPinC2,LOW);
     
     analogWrite(enablePinC,val_outputa);
}
void backa()//Motor reverse
{
      digitalWrite(motorPinC1,LOW);
     digitalWrite(motorPinC2,HIGH);
     
     analogWrite(enablePinC,val_outputa);
}

void Stopa()//Motor stops
{
     digitalWrite(enablePinC, LOW); 
}

Huh, sounds a lot like your post here :roll_eyes:

double  latestDurationCount,duration,abs_duration;//the number of the pulses

I can't imagine the relationship between variables with duration in the name and number of pulses.

  Setpoint; // MOTEUR GAUCHE

If that comment translates to "stupid code", I agree. If not, explain just what you think this useless statement is accomplishing.

  Setpointa;

More uselessness.

Threads merged.

PaulS:

double  latestDurationCount,duration,abs_duration;//the number of the pulses

I can't imagine the relationship between variables with duration in the name and number of pulses.

  Setpoint; // MOTEUR GAUCHE

If that comment translates to "stupid code", I agree. If not, explain just what you think this useless statement is accomplishing.

  Setpointa;

More uselessness.

duration is to know the number of pulse which should be the same as Setpoint, but actually it is not a good measurement method, that's why someone tall me to add this to have the latest duration

noInterrupts();
      latestDurationCount = duration;
      interrupts();
      abs_duration = abs(latestDurationCount);

I added Setpoint and Setpointa in the setup just to make understand (for me) which Setpoint is on LEFT or RIGHT MOTOR. It's standard label in PID, I should call it Setspeed, maybe.

No, my threads are not merged. But I have seen someone who program PID by "masking the other input in order they don't affect the Arduino"

Like that

void counter()
{
  byte state=PIND;
  
  state|=B11101011;  // mask to look only changing on 2 et 4 

  // Modifier le MASK  B01111011 par BXXXXXXXX mettre des 0 là où sont les pins utilisés par l'encodeur
  if( state!=laststate)
  {
    (((state&(1<<ENCODEURA))>>ENCODEURA)^((state&(1<<ENCODEURB))>>ENCODEURB))?count--:count++;
    laststate=state;
  }
}

Is it useful?

bvking:
I would like two know if I can improve my program to have perfectly the same speed on two motors for a long time.(one hour at least)

For a system like that I would control the speed of one motor directly and make the other motor match the first motor, rather than trying to control it separately at the same speed.

If you want very close co-ordination you may need to account for the accumulated difference between the motors and adjust to bring that back to zero also.

Or just make a physical connection between the motors?

Using a pair of stepper motors would be much easier if the requirement is for a slow speed within the scope of a stepper motor.

...R