Adjusting the DC motors speed

hi ,

I want to adjust the two DC motors speed dynamically to correct for the differences , I use the difference between left ticks and right ticks to tell in what way the bot is moving ,so to go straight I want there to be no difference between left and right tick counts. the code I wrote increases the left motor and decreases the right motor if the error is negative, and vice verse if positive.
I just multiply the error by some factor to give us a better adjustment value. This factor P represents the P (for Proportional) in PID .
But when run the code , at first the difference is small enough but suddenly increases dramatically , I guess there is may be wrong in data types matching or wrong in choice the right value for adjustment , need some help/advice to how choose it correctly ?

#include "Arduino.h"

#include <digitalWriteFast.h> 
                           

int ENA=8;    // SpeedPinA
int ENB=9;    // SpeedPinB 

int IN1=48;    // RightMotorWire1 connected to Arduino's port 48
int IN2=49;    // RightMotorWire2 connected to Arduino's port 49
 
int IN3=50;    // LeftMotorWire1 connected to Arduino's port 50
int IN4=51;    // LeftMotorWire2 connected to Arduino's port 51


// Quadrature encoders
// Left encoder
#define c_LeftEncoderInterrupt 0
#define c_LeftEncoderPinA 2
#define c_LeftEncoderPinB 4
#define LeftEncoderIsReversed
volatile bool _LeftEncoderBSet;
volatile long _LeftEncoderTicks = 0;

// Right encoder
#define c_RightEncoderInterrupt 1
#define c_RightEncoderPinA 3
#define c_RightEncoderPinB 5
volatile bool _RightEncoderBSet;
volatile long _RightEncoderTicks = 0;


void setup()
{
  
  
 Serial.begin(115200);
 pinMode(ENA,OUTPUT);
 pinMode(ENB,OUTPUT);
 pinMode(IN1,OUTPUT);
 pinMode(IN2,OUTPUT);
 pinMode(IN3,OUTPUT);
 pinMode(IN4,OUTPUT);
 digitalWrite(ENA,HIGH);    //enable motorA
 digitalWrite(ENB,HIGH);    //enable motorB
 SetupEncoders();

}
int WL=50;                      
                     
int WR=50;        




void SetupEncoders()
{
  // Quadrature encoders
  // Left encoder
  pinMode(c_LeftEncoderPinA, INPUT);      // sets pin A as input
  digitalWrite(c_LeftEncoderPinA, LOW);  // turn on pullup resistors
  pinMode(c_LeftEncoderPinB, INPUT);      // sets pin B as input
  digitalWrite(c_LeftEncoderPinB, LOW);  // turn on pullup resistors
  attachInterrupt(c_LeftEncoderInterrupt, HandleLeftMotorInterruptA, RISING);
  
  // Right encoder
  pinMode(c_RightEncoderPinA, INPUT);      // sets pin A as input
  digitalWrite(c_RightEncoderPinA, LOW);  // turn on pullup resistors
  pinMode(c_RightEncoderPinB, INPUT);      // sets pin B as input
  digitalWrite(c_RightEncoderPinB, LOW);  // turn on pullup resistors
  attachInterrupt(c_RightEncoderInterrupt, HandleRightMotorInterruptA, RISING); 
}

void loop()
{


  //  PID controller to adjust the motor speeds dynamically to correct for the differences
  
float P= 0.2;
long difference =long (_LeftEncoderTicks - _RightEncoderTicks);
if (difference > 0)
{WL -= int(P * difference);
 WR += int(P * difference);}
else
{WL += int(P * difference);
 WR -= int(P * difference);}


int rightPWM, leftPWM;

// Right Motor
  if (WR > 0) {
    //forward
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,HIGH);
    
  }  else if (WR < 0){
    //reverse
  digitalWrite(IN1,HIGH);
  digitalWrite(IN2,LOW);
  }
  
  if (WR == 0) {
   rightPWM = 0;
   analogWrite(ENA, rightPWM);
  } else {
    rightPWM = map(abs(WR), 1, 100, 1, 255);
    analogWrite(ENA, rightPWM);
  }
  
 
  
  // Left Motor
  
  if (WL > 0) {
     //forward
  digitalWrite(IN3,LOW);
  digitalWrite(IN4,HIGH);
  }  else if (WL < 0) {
     //reverse
  digitalWrite(IN3,HIGH);
  digitalWrite(IN4,LOW);}
  
  if (WL == 0) {
    leftPWM = 0;
    analogWrite(ENB, leftPWM);
  } else {
    leftPWM = map(abs(WL), 1, 100, 1, 255);
    analogWrite(ENB, leftPWM);
  }
  
  
  
Serial.print(_LeftEncoderTicks);
Serial.print("\t");
Serial.print(_RightEncoderTicks);
Serial.print("\t");
Serial.print(_LeftEncoderTicks - _RightEncoderTicks);
Serial.println("\t");


}



// Interrupt service routines for the left motor's quadrature encoder
void HandleLeftMotorInterruptA()
{
  // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
  _LeftEncoderBSet = digitalReadFast(c_LeftEncoderPinB);   // read the input pin

  // and adjust counter + if A leads B
  #ifdef LeftEncoderIsReversed
    _LeftEncoderTicks -= _LeftEncoderBSet ? -1 : +1;
  #else
    _LeftEncoderTicks += _LeftEncoderBSet ? -1 : +1;
  #endif
}

// Interrupt service routines for the right motor's quadrature encoder
void HandleRightMotorInterruptA()
{
  // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
  _RightEncoderBSet = digitalReadFast(c_RightEncoderPinB);   // read the input pin

  // and adjust counter + if A leads B
  #ifdef RightEncoderIsReversed
    _RightEncoderTicks -= _RightEncoderBSet ? -1 : +1;
  #else
    _RightEncoderTicks += _RightEncoderBSet ? -1 : +1;
  #endif
}

But when run the code , at first the difference is small enough but suddenly increases dramatically

That sounds like you have got the logic reversed somewhere and are therefore have positive feedback which leads to the correction factor increasing instead of decreasing. Your use of #ifdef LeftEncoderIsReversedand similar checks makes the logic of your program more difficult to follow. Do you really need them in your code, at least at this stage ? Your hardware is fixed so why not make the software match it ?

In another of your threads I suggested trying a fixed adjustment factor before refining the technique to use a PID. Did you try that ? It would at least ensure that the logic is correct without the complication of having to compute the correction factor in the first instance.

As for the PID, have you looked at the PID library ?

UKHeliBob:

But when run the code , at first the difference is small enough but suddenly increases dramatically

That sounds like you have got the logic reversed somewhere and are therefore have positive feedback which leads to the correction factor increasing instead of decreasing. Your use of #ifdef LeftEncoderIsReversedand similar checks makes the logic of your program more difficult to follow. Do you really need them in your code, at least at this stage ? Your hardware is fixed so why not make the software match it ?

In another of your threads I suggested trying a fixed adjustment factor before refining the technique to use a PID. Did you try that ? It would at least ensure that the logic is correct without the complication of having to compute the correction factor in the first instance.

As for the PID, have you looked at the PID library ?

thanks UKHeliBob

Do you really need them in your code, at least at this stage ?

No , I don't , I remove it but that was not be useful .

Your hardware is fixed so why not make the software match it ?

this point is not clear to me , more info. ?

In another of your threads I suggested trying a fixed adjustment factor before refining the technique to use a PID. Did you try that ? It would at least ensure that the logic is correct without the complication of having to compute the correction factor in the first instance.

I was wishing to do that but the difference (error) between two speeds is not stable and increases constantly .

Your hardware is fixed so why not make the software match it ?

this point is not clear to me , more info. ?

What I meant was that as you know which direction the encoders are working so you don't need the checks on the value of LeftEncoderIsReversed etc

In another of your threads I suggested trying a fixed adjustment factor before refining the technique to use a PID. Did you try that ? It would at least ensure that the logic is correct without the complication of having to compute the correction factor in the first instance.

I was wishing to do that but the difference (error) between two speeds is not stable and increases constantly .

The point of adding a fixed adjustment factor is precisely that it is fixed instead of being calculated an varying. If you use a fixed adjustment factor it is easy to change its value and/or sign (positive/negative), recompile and try again until you know what range of values is sensible for the adjustment. Once you know that you can move on to calculating the adjustment and reducing it to a minimum as the robot moves nearly in a straight line.

With a fixed adjustment factor and, say an adjustment to the right being needed then eventually too much adjustment will be applied and a left adjustment will be required and so on but at least you will have something to work on.

UKHeliBob:

Your hardware is fixed so why not make the software match it ?

this point is not clear to me , more info. ?

What I meant was that as you know which direction the encoders are working so you don't need the checks on the value of LeftEncoderIsReversed etc

In another of your threads I suggested trying a fixed adjustment factor before refining the technique to use a PID. Did you try that ? It would at least ensure that the logic is correct without the complication of having to compute the correction factor in the first instance.

I was wishing to do that but the difference (error) between two speeds is not stable and increases constantly .

The point of adding a fixed adjustment factor is precisely that it is fixed instead of being calculated an varying. If you use a fixed adjustment factor it is easy to change its value and/or sign (positive/negative), recompile and try again until you know what range of values is sensible for the adjustment. Once you know that you can move on to calculating the adjustment and reducing it to a minimum as the robot moves nearly in a straight line.

With a fixed adjustment factor and, say an adjustment to the right being needed then eventually too much adjustment will be applied and a left adjustment will be required and so on but at least you will have something to work on.

thanks a lot , sorry to say that , is there any example to make things be clear to me . I really need help to solve this problem, I completely lost and tired , I have been more than month looking for a solution to the problem of encoders and motors .

Below you will find an example. It uses a library to control the motors but you should see the principle used in the adjustTrack() function. It is very crude because it only adjust the speed of the right motor and the left motor speed stay constant. I installed LEDs on the robot so that I could get visual feedback on the operation of the code. As written the track is adjusted every 200 milliseconds by a factor of 10 but these can, of course, be changed.

I cannot remember how well the program worked so please don't expect too much accuracy from it.

//2 wheel vehicle with straight line correction by reading rotary encoders

#include <HUBeeBMDWheel.h>

HUBeeBMDWheel leftWheel;
HUBeeBMDWheel rightWheel;

const byte rightFasterLED = A0;    //diagnostic LEDs
const byte leftFasterLED = A4;
const byte equalSpeedLED = A2;

const byte forward = 1;
const byte reverse = 0;

const byte hard = 1;    //type of braking
const byte soft = 0;

int leftSpeed = 150;    //use int not byte because speeds can be negative
int rightSpeed = 150;

volatile int leftCount =  0;    //used in ISR so need to be volatile
volatile int rightCount =  0;
const int trackAdjustFactor = 10;

long trackStartTime = 0;
long trackInterval = 200;

void setup() 
{
  attachInterrupt(1, updateLeftCount, CHANGE);
  attachInterrupt(0, updateRightCount, CHANGE);

  pinMode(rightFasterLED, OUTPUT);
  pinMode(leftFasterLED, OUTPUT);
  pinMode(equalSpeedLED, OUTPUT);

  leftWheel.setupPins(13, 12, 10);  
  rightWheel.setupPins(8, 11, 9);

  leftWheel.setBrakeMode(hard);
  rightWheel.setBrakeMode(hard);

  leftWheel.setDirectionMode(reverse);
  rightWheel.setDirectionMode(reverse);
}

void loop() 
{
  leftWheel.setMotorPower(leftSpeed);      //set motor speeds to current values
  rightWheel.setMotorPower(rightSpeed);

  if ( (millis() - trackStartTime) > trackInterval)
  {
    adjustTrack();
    trackStartTime = millis(); 
  }
}

void adjustTrack()
{
  noInterrupts();      //update of ints is not atomic so need to avoid interrupts
  if (leftCount > rightCount)
  {
    rightSpeed += trackAdjustFactor;
    digitalWrite(leftFasterLED, HIGH);
    digitalWrite(rightFasterLED, LOW);
    digitalWrite(equalSpeedLED, LOW);
  } 
  else if (rightCount > leftCount)
  {
    rightSpeed -= trackAdjustFactor;
    digitalWrite(rightFasterLED, HIGH);
    digitalWrite(leftFasterLED, LOW);
    digitalWrite(equalSpeedLED, LOW);
  }
  else
  {
    digitalWrite(leftFasterLED, LOW);
    digitalWrite(rightFasterLED, LOW);
    digitalWrite(equalSpeedLED, HIGH);
  }
  leftCount = 0;
  rightCount = 0;
  interrupts();
}

void updateLeftCount()
{
  leftCount++; 
}

void updateRightCount()
{
  rightCount++; 
}

UKHeliBob:
Below you will find an example. It uses a library to control the motors but you should see the principle used in the adjustTrack() function. It is very crude because it only adjust the speed of the right motor and the left motor speed stay constant. I installed LEDs on the robot so that I could get visual feedback on the operation of the code. As written the track is adjusted every 200 milliseconds by a factor of 10 but these can, of course, be changed.

I cannot remember how well the program worked so please don't expect too much accuracy from it.

//2 wheel vehicle with straight line correction by reading rotary encoders

#include <HUBeeBMDWheel.h>

HUBeeBMDWheel leftWheel;
HUBeeBMDWheel rightWheel;

const byte rightFasterLED = A0;    //diagnostic LEDs
const byte leftFasterLED = A4;
const byte equalSpeedLED = A2;

const byte forward = 1;
const byte reverse = 0;

const byte hard = 1;    //type of braking
const byte soft = 0;

int leftSpeed = 150;    //use int not byte because speeds can be negative
int rightSpeed = 150;

volatile int leftCount =  0;    //used in ISR so need to be volatile
volatile int rightCount =  0;
const int trackAdjustFactor = 10;

long trackStartTime = 0;
long trackInterval = 200;

void setup()
{
  attachInterrupt(1, updateLeftCount, CHANGE);
  attachInterrupt(0, updateRightCount, CHANGE);

pinMode(rightFasterLED, OUTPUT);
  pinMode(leftFasterLED, OUTPUT);
  pinMode(equalSpeedLED, OUTPUT);

leftWheel.setupPins(13, 12, 10); 
  rightWheel.setupPins(8, 11, 9);

leftWheel.setBrakeMode(hard);
  rightWheel.setBrakeMode(hard);

leftWheel.setDirectionMode(reverse);
  rightWheel.setDirectionMode(reverse);
}

void loop()
{
  leftWheel.setMotorPower(leftSpeed);      //set motor speeds to current values
  rightWheel.setMotorPower(rightSpeed);

if ( (millis() - trackStartTime) > trackInterval)
  {
    adjustTrack();
    trackStartTime = millis();
  }
}

void adjustTrack()
{
  noInterrupts();      //update of ints is not atomic so need to avoid interrupts
  if (leftCount > rightCount)
  {
    rightSpeed += trackAdjustFactor;
    digitalWrite(leftFasterLED, HIGH);
    digitalWrite(rightFasterLED, LOW);
    digitalWrite(equalSpeedLED, LOW);
  }
  else if (rightCount > leftCount)
  {
    rightSpeed -= trackAdjustFactor;
    digitalWrite(rightFasterLED, HIGH);
    digitalWrite(leftFasterLED, LOW);
    digitalWrite(equalSpeedLED, LOW);
  }
  else
  {
    digitalWrite(leftFasterLED, LOW);
    digitalWrite(rightFasterLED, LOW);
    digitalWrite(equalSpeedLED, HIGH);
  }
  leftCount = 0;
  rightCount = 0;
  interrupts();
}

void updateLeftCount()
{
  leftCount++;
}

void updateRightCount()
{
  rightCount++;
}

thanks , but I think there is problem in calling 'HUBeeBMDWheel' library .

sketch_sep23a:5: error: 'HUBeeBMDWheel' does not name a type
sketch_sep23a:6: error: 'HUBeeBMDWheel' does not name a type
sketch_sep23a.ino: In function 'void setup()':
sketch_sep23a:37: error: 'leftWheel' was not declared in this scope
sketch_sep23a:38: error: 'rightWheel' was not declared in this scope
sketch_sep23a.ino: In function 'void loop()':
sketch_sep23a:49: error: 'leftWheel' was not declared in this scope
sketch_sep23a:50: error: 'rightWheel' was not declared in this scope

thanks , but I think there is problem in calling 'HUBeeBMDWheel' library .

Well, I did say that it used a library but I really posted the program so that you could see the principle of adjusting the speed of one of the wheels by a fixed amount depending on the relative counts of the two encoders over a period of time. It was not meant to be a working example unless you have the library, which you can get from http://www.creative-robotics.com/?q=hubee-resources, but even then your use of Arduino pins would need to match mine and we will have opened a new can of worms.

Forget trying to compile and run the program. Read through it and see how it works. Basically it counts interrupts from each of 2 wheel encoders for 200 milliseconds then calls the adjustTrack() function to change the speed of one of the wheels to compensate for the different counts. Then it resets the counts and the process starts over again.

UKHeliBob:

thanks , but I think there is problem in calling 'HUBeeBMDWheel' library .

Well, I did say that it used a library but I really posted the program so that you could see the principle of adjusting the speed of one of the wheels by a fixed amount depending on the relative counts of the two encoders over a period of time. It was not meant to be a working example unless you have the library, which you can get from http://www.creative-robotics.com/?q=hubee-resources, but even then your use of Arduino pins would need to match mine and we will have opened a new can of worms.

Forget trying to compile and run the program. Read through it and see how it works. Basically it counts interrupts from each of 2 wheel encoders for 200 milliseconds then calls the adjustTrack() function to change the speed of one of the wheels to compensate for the different counts. Then it resets the counts and the process starts over again.

OK , thanks :smiley: I 'll try that

UKHeliBob:
Below you will find an example. It uses a library to control the motors but you should see the principle used in the adjustTrack() function. It is very crude because it only adjust the speed of the right motor and the left motor speed stay constant. I installed LEDs on the robot so that I could get visual feedback on the operation of the code. As written the track is adjusted every 200 milliseconds by a factor of 10 but these can, of course, be changed.

I cannot remember how well the program worked so please don't expect too much accuracy from it.

//2 wheel vehicle with straight line correction by reading rotary encoders

#include <HUBeeBMDWheel.h>

HUBeeBMDWheel leftWheel;
HUBeeBMDWheel rightWheel;

const byte rightFasterLED = A0;    //diagnostic LEDs
const byte leftFasterLED = A4;
const byte equalSpeedLED = A2;

const byte forward = 1;
const byte reverse = 0;

const byte hard = 1;    //type of braking
const byte soft = 0;

int leftSpeed = 150;    //use int not byte because speeds can be negative
int rightSpeed = 150;

volatile int leftCount =  0;    //used in ISR so need to be volatile
volatile int rightCount =  0;
const int trackAdjustFactor = 10;

long trackStartTime = 0;
long trackInterval = 200;

void setup()
{
 attachInterrupt(1, updateLeftCount, CHANGE);
 attachInterrupt(0, updateRightCount, CHANGE);

pinMode(rightFasterLED, OUTPUT);
 pinMode(leftFasterLED, OUTPUT);
 pinMode(equalSpeedLED, OUTPUT);

leftWheel.setupPins(13, 12, 10);  
 rightWheel.setupPins(8, 11, 9);

leftWheel.setBrakeMode(hard);
 rightWheel.setBrakeMode(hard);

leftWheel.setDirectionMode(reverse);
 rightWheel.setDirectionMode(reverse);
}

void loop()
{
 leftWheel.setMotorPower(leftSpeed);      //set motor speeds to current values
 rightWheel.setMotorPower(rightSpeed);

if ( (millis() - trackStartTime) > trackInterval)
 {
   adjustTrack();
   trackStartTime = millis();
 }
}

void adjustTrack()
{
 noInterrupts();      //update of ints is not atomic so need to avoid interrupts
 if (leftCount > rightCount)
 {
   rightSpeed += trackAdjustFactor;
   digitalWrite(leftFasterLED, HIGH);
   digitalWrite(rightFasterLED, LOW);
   digitalWrite(equalSpeedLED, LOW);
 }
 else if (rightCount > leftCount)
 {
   rightSpeed -= trackAdjustFactor;
   digitalWrite(rightFasterLED, HIGH);
   digitalWrite(leftFasterLED, LOW);
   digitalWrite(equalSpeedLED, LOW);
 }
 else
 {
   digitalWrite(leftFasterLED, LOW);
   digitalWrite(rightFasterLED, LOW);
   digitalWrite(equalSpeedLED, HIGH);
 }
 leftCount = 0;
 rightCount = 0;
 interrupts();
}

void updateLeftCount()
{
 leftCount++;
}

void updateRightCount()
{
 rightCount++;
}

Hello again ,

I applied the general idea in the code above and almost it works good with 100 PWM on each motor and the track is adjusted every 200 milliseconds (i.e trackInterval = 200) , but there are some issues need to some explanation

1- first I notice that as I increases the "trackInterval" , the difference (error) increases .

2- As known , when apply the same PWM on each motor , one motor will run faster then the other motor ,
for example when apply 100 PWM on each motor , one motor will runs at about 100 and other at 70 (just guessed values ) and this problem could be solved by many ways one of them the code above that adjust the speed of two motors and make robot run in straight line .

but what about when apply the desired PWM on each motor with difference PWM values for example 100 on the first and 50 on the second , so as always that there is difference and one motor runs faster then other so that's mean the motors will not run at the desired PWM , so how could solve this matter . I asked this question because actually I had fuzzy logic controller that control path tracking of robot , the controller inputs are generated by an algorithm that planed the desired path and the outputs of the controller are angular velocity for the left and right motors (i.e. PWM) , so how could make motors run at the desired PWMs generated by fuzzy controller without errors (difference ) so the robot could move right , left and in straight line as the fuzzy controller tells him .

sorry for bad explanation of my problem .

1- first I notice that as I increases the "trackInterval" , the difference (error) increases .

Yes that is because there is longer between each correction so it drifts further before correction.

but what about when apply the desired PWM on each motor with difference PWM values for example 100 on the first and 50 on the second , so as always that there is difference and one motor runs faster then other so that's mean the motors will not run at the desired PWM , so how could solve this matter .

Here you want the number of pulses from each motor to be a different value each "trackInterval" time. So you must work out what values you want from each motor and adjust the PWM for each motor up or down to correct for any drift. That is the difference between the number of pulses you expect and what you get.

1- first I notice that as I increases the "trackInterval" , the difference (error) increases .

No surprise there. If the difference between the counts is ay 10 in a period of 200 milliseconds than what would you expect it to be over a period of say 400 milliseconds ?

this problem could be solved by many ways one of them the code above that adjust the speed of two motors and make robot run in straight line .

Who cares what PWM values make the robot run straight as long as it does. You can improve the simple straight running program that I suggested by slowing the fast motor and speeding up the slow motor at the end of every measurement period and by making the measurement period shorter so that the adjustment happens more often.

The best solution is to use a PID which takes an error (the difference between the two counts) and applies a correction (change of motor speed) proportional to the size of the difference. This makes the adjustment smoother and you can adjust the frequency of the adjustment and also how aggressively the correction is applied.

Here is a program that uses a PID derived from my previous one . As before it uses a library to control the motor and will need to be adapted to suit your hardware.

#include <PID_v1.h>
#include <HUBeeBMDWheel.h>

HUBeeBMDWheel leftWheel;
HUBeeBMDWheel rightWheel;

//PID variables.  All doubles
double trackAdjustValue;
double trackSetpoint;
double trackError;

double Kp = 3;  //Determines how aggressively the PID reacts to the current amount of error (Proportional)
double Ki = 2;  //Determines how aggressively the PID reacts to error over time (Integral)
double Kd = 1;  //Determines how aggressively the PID reacts to the change in error (Derivative)

PID trackPID(&trackError, &trackAdjustValue, &trackSetpoint, Kp, Ki, Kd, DIRECT);

const byte rightFasterLED = A0;
const byte leftFasterLED = A4;
const byte equalSpeedLED = A2;

const byte forward = 1;
const byte reverse = 0;

const byte hard = 1;    //type of braking
const byte soft = 0;

double leftSpeed = 100;      //intital speeds.  Using doubles for use with PID
double rightSpeed = 100;

volatile double leftCount =  0;
volatile double rightCount =  0;

void setup() 
{
  Serial.begin(115200);

  attachInterrupt(1, updateLeftCount, CHANGE);
  attachInterrupt(0, updateRightCount, CHANGE);

  pinMode(rightFasterLED, OUTPUT);
  pinMode(leftFasterLED, OUTPUT);
  pinMode(equalSpeedLED, OUTPUT);

  leftWheel.setupPins(13, 12, 10);  
  rightWheel.setupPins(8, 11, 9);

  leftWheel.setBrakeMode(hard);
  rightWheel.setBrakeMode(hard);

  leftWheel.setDirectionMode(forward);
  rightWheel.setDirectionMode(forward);

  trackAdjustValue = 0; 
  trackSetpoint = 0;
  trackError = 0;

  trackPID.SetMode(AUTOMATIC);
  trackPID.SetSampleTime(200);
  trackPID.SetOutputLimits(-20,20);
}

void loop() 
{
  leftWheel.setMotorPower(leftSpeed);      //set motor speeds to current values
  rightWheel.setMotorPower(rightSpeed);

  trackError = rightCount - leftCount;

  if (trackPID.Compute()) //true if PID has triggered
  {
    rightSpeed += trackAdjustValue;
    leftCount = 0;
    rightCount = 0;
    
    if (trackError > 0)
    {
      digitalWrite(rightFasterLED, HIGH);
      digitalWrite(leftFasterLED, LOW);
      digitalWrite(equalSpeedLED, LOW);
    }
    else if (trackError < 0)
    {
      digitalWrite(rightFasterLED, LOW);
      digitalWrite(leftFasterLED, HIGH);
      digitalWrite(equalSpeedLED, LOW);
    }
    else
    {
      digitalWrite(rightFasterLED, LOW);
      digitalWrite(leftFasterLED, LOW);
      digitalWrite(equalSpeedLED, HIGH);
    }
  }

  Serial.println(trackAdjustValue);
}

void updateLeftCount()
{
  leftCount = leftCount + 1;  //++ not valid for doubles
}

void updateRightCount()
{
  rightCount = rightCount + 1;  //++ not valid for doubles
}

UKHeliBob:

1- first I notice that as I increases the "trackInterval" , the difference (error) increases .

No surprise there. If the difference between the counts is ay 10 in a period of 200 milliseconds than what would you expect it to be over a period of say 400 milliseconds ?

this problem could be solved by many ways one of them the code above that adjust the speed of two motors and make robot run in straight line .

Who cares what PWM values make the robot run straight as long as it does. You can improve the simple straight running program that I suggested by slowing the fast motor and speeding up the slow motor at the end of every measurement period and by making the measurement period shorter so that the adjustment happens more often.

The best solution is to use a PID which takes an error (the difference between the two counts) and applies a correction (change of motor speed) proportional to the size of the difference. This makes the adjustment smoother and you can adjust the frequency of the adjustment and also how aggressively the correction is applied.

Here is a program that uses a PID derived from my previous one . As before it uses a library to control the motor and will need to be adapted to suit your hardware.

#include <PID_v1.h>

#include <HUBeeBMDWheel.h>

HUBeeBMDWheel leftWheel;
HUBeeBMDWheel rightWheel;

//PID variables.  All doubles
double trackAdjustValue;
double trackSetpoint;
double trackError;

double Kp = 3;  //Determines how aggressively the PID reacts to the current amount of error (Proportional)
double Ki = 2;  //Determines how aggressively the PID reacts to error over time (Integral)
double Kd = 1;  //Determines how aggressively the PID reacts to the change in error (Derivative)

PID trackPID(&trackError, &trackAdjustValue, &trackSetpoint, Kp, Ki, Kd, DIRECT);

const byte rightFasterLED = A0;
const byte leftFasterLED = A4;
const byte equalSpeedLED = A2;

const byte forward = 1;
const byte reverse = 0;

const byte hard = 1;    //type of braking
const byte soft = 0;

double leftSpeed = 100;      //intital speeds.  Using doubles for use with PID
double rightSpeed = 100;

volatile double leftCount =  0;
volatile double rightCount =  0;

void setup()
{
 Serial.begin(115200);

attachInterrupt(1, updateLeftCount, CHANGE);
 attachInterrupt(0, updateRightCount, CHANGE);

pinMode(rightFasterLED, OUTPUT);
 pinMode(leftFasterLED, OUTPUT);
 pinMode(equalSpeedLED, OUTPUT);

leftWheel.setupPins(13, 12, 10);  
 rightWheel.setupPins(8, 11, 9);

leftWheel.setBrakeMode(hard);
 rightWheel.setBrakeMode(hard);

leftWheel.setDirectionMode(forward);
 rightWheel.setDirectionMode(forward);

trackAdjustValue = 0;
 trackSetpoint = 0;
 trackError = 0;

trackPID.SetMode(AUTOMATIC);
 trackPID.SetSampleTime(200);
 trackPID.SetOutputLimits(-20,20);
}

void loop()
{
 leftWheel.setMotorPower(leftSpeed);      //set motor speeds to current values
 rightWheel.setMotorPower(rightSpeed);

trackError = rightCount - leftCount;

if (trackPID.Compute()) //true if PID has triggered
 {
   rightSpeed += trackAdjustValue;
   leftCount = 0;
   rightCount = 0;
   
   if (trackError > 0)
   {
     digitalWrite(rightFasterLED, HIGH);
     digitalWrite(leftFasterLED, LOW);
     digitalWrite(equalSpeedLED, LOW);
   }
   else if (trackError < 0)
   {
     digitalWrite(rightFasterLED, LOW);
     digitalWrite(leftFasterLED, HIGH);
     digitalWrite(equalSpeedLED, LOW);
   }
   else
   {
     digitalWrite(rightFasterLED, LOW);
     digitalWrite(leftFasterLED, LOW);
     digitalWrite(equalSpeedLED, HIGH);
   }
 }

Serial.println(trackAdjustValue);
}

void updateLeftCount()
{
 leftCount = leftCount + 1;  //++ not valid for doubles
}

void updateRightCount()
{
 rightCount = rightCount + 1;  //++ not valid for doubles
}

Thanks a lot UKHeliBob

So sorry for being too late , I was too busy .

Actually , as I said later that the problem that I still face is that motors didn't spin in the desired PWM value . Previously , I solved the problem for straight line movement (thanks UKHeliBob for help ) , but when applying different PWM values on both motors (for example to turn left or right ) , I think what is called "drifting" happens and the motors didn't spin with desired PWM .

Could I solve this problem by using concept in the last post ?

I think what is called "drifting" happens and the motors didn't spin with desired PWM .

Hi,

What kind of motors are yu using?. Are them those small DC hobby motors?.

Regards

Could I solve this problem by using concept in the late post ?

The whole point of measuring the wheel rotations, comparing them and adjusting the speed of one or both wheels then doing the measurement/adjustment over and over again is to keep the robot running as straight as possible.

My first program uses a crude method to adjust the speed of one motor because the adjustment factor is fixed. My second program uses a PID which does the correction by applying a correction that is proportional to the difference between the 2 encoder readings. That way a bigger difference applies a bigger correction and when tuned correctly the difference reduces over time and smaller and smaller corrections are applied. The PID parameters can be adjusted to make the correction more or less aggressive.

Now, when it comes to turning the robot you have a different set of problems because the 2 motors will deliberately have a different PWM value applied to them so there is bound to be a difference in encoders readings and you do not want the program to adjust the PWM value(s) to make the robot run straight. So, what can you do ? Well, one approach is to make one motor run at a fixed speed and the other to run at a speed that is a known speed faster, say twice as fast. Then when you read the encoders you would expect one value to be twice the other and you can apply a correction based on the ratio of the 2 wheel speeds by dividing the reading of the faster wheel in the turn by the ratio of the 2 wheel speeds. If the robot is working perfectly the difference will be zero, if not then use the difference to calculate the adjustment to be applied, preferably using a PID.

My second program will only work to keep the robot running straight but you can apply the principle outlined above, perhaps by having a variable to hold the current ratio of wheel speeds, normally set to 1 and multiplying the difference in encoder values by that value. When you turn set the variable to the ratio of wheel speeds (PWM values).

Note that neither method can take account of slippage of one or both wheels on the surface they are running on. The problem is likely to be greater when turning as the wheels will naturally need to skid sideways a little which will be different at different wheel speeds unless you spin on the spot with both wheels at the same speed in which case the skidding should cancel out.