How to make two motors have really equal speed?

Hi, I keep adjusting two motors' PWM ,and trying to make it go straight line but failed.

Give them equal PWM value still would drifted in real situation.

Should I need to add some module like compass and do PID control or any possible way?

Thanks.

Without some sort of "which way am I actually going" feedback, you can not make a robot move in a straight line (unless it is on a track that is straight).

Two nearly identical motors with two nearly identical wheels, driven at the same PWM value will cause the robot to steer, since the motor speeds will not be identical AND the wheel diameter is not identical AND the amount that each wheel slips is not identical (and they DO slip).

PaulS:
Without some sort of "which way am I actually going" feedback, you can not make a robot move in a straight line (unless it is on a track that is straight).

So, using wheel encoders would be the only option, if I'm not wrong?

Abhishek_G:
So, using wheel encoders would be the only option, if I'm not wrong?

Pretty much. You basically set your PWM, and monitor the pulses from the encoders, if one is slower than the other, increase the PWM to the slower motor. If you hit the max-limits of the PWM, then instead slow down the wheel that is moving faster (ie - lower it's PWM).

Alternatively (and maybe better), do the reverse: Slow down the motor that is moving faster, and if you hit the lower limit (0 PWM - or maybe a set percentage?) instead increase the PWM on the slow side.

I want it to follow a specific degree like a car with IR sensors follows a black line, is it possible ?
And I heard the digital compass would be interfered by metal,So I still cant really figure out a nice &simple solution.

How to use two encoders to make sure it goes straight? I have install one to count its moving displacement& turning 90 degrees .

thanks

New reply before I sent the reply , thanks cr0sh, let me read it for a while

Encodes will have the axles turning at the same speed. They can not help with differences in wheel diameter or with unequal slippage. While slippage may be a relatively small concern indoors, it is a huge problem outdoors.

My car is used for indoors mapping,like this http://www.robotc.net/blog/2011/08/08/robotc-advanced-training/

it counts a step by a grid, and arduino uno has only 2 external interrupt pins,so each pin can only connect one encoder to receive waveforms,its hard to tell which wheel is slower or faster that another one, right?

But it can count two wheels for same waveform times,I guess ,to let them go equal distance even one of them reach the setting value already but another one not until it reach the setting value ,too. and it goes to next step.

Hi,

First, what model arduino are you using?
How are you driving your motors?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Can you please post a copy of your sketch, using code tags?
They are made with the </> icon in the reply Menu.
See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Check which PWM pins you are using to control your motor speed, check the controller specs to see what the PWM frequencies are for those pins.
The pins are paired with the same frequency, but the each pair is a different frequency.
If you have two different PWM frequencies to your motors you will find they respond differently, including speed.

Tom... :slight_smile:

UNO

I use a L298 Dual H-Bridge Motor Driver board to drive my motors

Just a common circuit ,arduino board connects to L298 Dual H-Bridge Motor Driver and a encoder connects to two external interrupt pins.

PWM pin5,6 they are belongs to timer0.

volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;
const int x_size = 10;
const int y_size = 5;


int i =0;
int m[x_size][y_size] ={{0,0,2,0,0}, 
                        {0,1,0,1,0}, 
                        {99,1,1,1,0}, 
                        {0,0,0,0,0}, 
                        {0,0,0,0,0}, 
                        {0,0,0,0,0}, 
                        {0,0,0,0,0}, 
                        {0,0,0,0,0}, 
                        {0,0,0,0,0},
                        {0,0,0,0,0}};
 
#define MotorR_I1     8   
#define MotorR_I2     9   
#define MotorL_I3     10  
#define MotorL_I4     11  
#define MotorR_ENA    5   
#define MotorL_ENB    6   
#define initial_speedr 100
#define initial_speedl 100


void setup() {
  Serial.begin(9600);
  pinMode(MotorR_I1,OUTPUT);
  pinMode(MotorR_I2,OUTPUT);
  pinMode(MotorL_I3,OUTPUT);
  pinMode(MotorL_I4,OUTPUT);
  pinMode(MotorR_ENA,OUTPUT);
  pinMode(MotorL_ENB,OUTPUT);
  
attachInterrupt(0,rpm_fun,CHANGE);
attachInterrupt(1,rpm_fun2,CHANGE);
 rpmcount = 0;
  rpm = 0;
  timeold = 0;

}
void advance(int d)    
{   analogWrite(MotorR_ENA,initial_speedr);  
    analogWrite(MotorL_ENB,initial_speedl); 
    digitalWrite(MotorR_I1,LOW);  
    digitalWrite(MotorR_I2,HIGH);
    digitalWrite(MotorL_I3,LOW);   
    digitalWrite(MotorL_I4,HIGH);
    delay(d * 100);
}

void turnR(int d)    
{
    analogWrite(MotorR_ENA,initial_speedr);    
    analogWrite(MotorL_ENB,initial_speedl); 
    digitalWrite(MotorR_I1,HIGH);   
    digitalWrite(MotorR_I2,LOW);
    digitalWrite(MotorL_I3,LOW);    
    digitalWrite(MotorL_I4,HIGH); 
    delay(d * 100);
}

void turnL(int d)    
{   analogWrite(MotorR_ENA,initial_speedr);    
    analogWrite(MotorL_ENB,initial_speedl); 
    digitalWrite(MotorR_I1,LOW);    
    digitalWrite(MotorR_I2,HIGH);
    digitalWrite(MotorL_I3,HIGH);   
    digitalWrite(MotorL_I4,LOW);
    
    delay(d * 100);
}    

void stopRL(int d)  
{   
    digitalWrite(MotorR_I1,HIGH);   
    digitalWrite(MotorR_I2,HIGH);
    digitalWrite(MotorL_I3,HIGH);   
    digitalWrite(MotorL_I4,HIGH);
    delay(d * 100);
} 



void moveForward(void)
{if(rpmcount!=0)rpmcount=0;
  while(rpmcount<=48)
  advance(0);
  rpmcount=0;
  stopRL(5);
 
}


void turnLeft90(void)
{if(rpmcount!=0)rpmcount=0;
  while(rpmcount<=20)
  turnL(0);
  rpmcount=0;
  stopRL(5);
  
}
void turnRight90(void)
{if(rpmcount!=0)rpmcount=0;
 while(rpmcount<=20)
  turnR(0);
  rpmcount=0;
  stopRL(5);
}
void update_map(void) 
{
  for(int x=0;x<x_size;x++)
  {for(int y=0;y<y_size;y++)
    {if(m[x][y]!=1&&m[x][y]!=99&& m[x][y]!=2)
      m[x][y]=0;
     } 
  }
}
void WavefrontSearch(void)
{
  int goal_x, goal_y;
  bool foundWave = true;
  int currentWave = 2; //Looking for goal first
 
  while(foundWave == true)
  {
    foundWave = false;
    for(int y=0; y < y_size; y++)
    {
      for(int x=0; x < x_size; x++)
      {
        if(m[x][y] == currentWave)
        {
          foundWave = true;
          goal_x = x;
          goal_y = y;
 
          if(goal_x > 0) //This code checks the array bounds heading WEST
            if(m[goal_x-1][goal_y] == 0)  //This code checks the WEST direction
              m[goal_x-1][goal_y] = currentWave + 1;
 
          if(goal_x < (x_size - 1)) //This code checks the array bounds heading EAST
            if(m[goal_x+1][goal_y] == 0)//This code checks the EAST direction
              m[goal_x+1][goal_y] = currentWave + 1;
 
          if(goal_y > 0)//This code checks the array bounds heading SOUTH
            if(m[goal_x][goal_y-1] == 0) //This code checks the SOUTH direction
              m[goal_x][goal_y-1] = currentWave + 1;
 
          if(goal_y < (y_size - 1))//This code checks the array bounds heading NORTH
            if(m[goal_x][goal_y+1] == 0) //This code checks the NORTH direction
              m[goal_x][goal_y+1] = currentWave + 1;
        }
      }
    }
    currentWave++;

    delay(500);
  }
}
void NavigateToGoal()
{
  //Store our Robots Current Position
 int robot_x, robot_y;
 
  //First - Find Goal and Target Locations
  for(int x=0; x < x_size; x++)
  {
    for(int y=0; y < y_size; y++)
    {
      if(m[x][y] == 99)
      {
        robot_x = x;
        robot_y = y;
      }
    }
  }
 
  //Found Goal and Target, start deciding our next path
   int current_x = robot_x;
   int current_y = robot_y;
  int current_facing = 0;
  int next_Direction = 0;
  int current_low = 99;
  
     int Next_X = 0;
    int Next_Y = 0;
  while(current_low > 2)
  {
    current_low = 99; //Every time, reset to highest number (robot)
    
    next_Direction = current_facing;
 
 
    //Check Array Bounds West
    if(current_x > 0)
      if(m[current_x-1][current_y] < current_low && m[current_x-1][current_y] != 1) //Is current space occupied?
    {
      current_low = m[current_x-1][current_y];  //Set next number
      next_Direction = 3; //Set Next Direction as West
      Next_X = current_x-1;
      Next_Y = current_y;
    }
 
    //Check Array Bounds East
    if(current_x < (x_size -1))
      if(m[current_x+1][current_y] < current_low && m[current_x+1][current_y] != 1) //Is current space occupied?
    {
      current_low = m[current_x+1][current_y];  //Set next number
      next_Direction = 1; //Set Next Direction as East
      Next_X = current_x+1;
      Next_Y = current_y;
    }
 
    //Check Array Bounds South
    if(current_y > 0)
      if(m[current_x][current_y-1] < current_low && m[current_x][current_y-1] != 1)
    {
      current_low = m[current_x][current_y-1];  //Set next number
      next_Direction = 2; //Set Next Direction as South
      Next_X = current_x;
      Next_Y = current_y-1;
    }
 
    //Check Array Bounds North
    if(current_y < (y_size - 1))
      if(m[current_x][current_y+1] < current_low && m[current_x][current_y+1] != 1) //Is current space occupied?
    {
      current_low = m[current_x][current_y+1];  //Set next number
      next_Direction = 0; //Set Next Direction as North
      Next_X = current_x;
      Next_Y = current_y+1;
    }
 
    //Okay - We know the number we're heading for, the direction and the coordinates.
    current_x = Next_X;
    current_y = Next_Y;
    //m[current_x][current_y] = '*';
 
    //Track the robot's heading
    while(current_facing != next_Direction)
    {
      if (current_facing > next_Direction)
      {  if(current_facing==3&&next_Direction==0)
           {
            turnRight90();
            current_facing=0;
           }
           else{
        turnLeft90();
        current_facing--;}
      }
      else if(current_facing < next_Direction)
      { if(current_facing==0&&next_Direction==3)
        {
          turnLeft90();
          current_facing=3;
        }
        else{
        turnRight90();
        current_facing++;
        }
      }
    }
    moveForward();
    
    delay(500);
  }
  int s;
 s=m[robot_x][robot_y];
 m[robot_x][robot_y]=m[current_x][current_y];
 m[current_x][current_y]=s;
}
void loop() {
  
  while(i==0){Serial.println("start tracing:");
WavefrontSearch();    
  NavigateToGoal();
 stopRL(5);
 update_map();
 WavefrontSearch();    
  NavigateToGoal();
i=1;}
}
void rpm_fun()
{ //Serial.println( rpmcount);
  rpmcount++;
  
}
void rpm_fun2()
{ //Serial.println( rpmcount);
  rpmcount++;
  
}

yangkai:
Hi, I keep adjusting two motors' PWM ,and trying to make it go straight line but failed.

Give them equal PWM value still would drifted in real situation.

If wheel encoders are used to estimate the angular speed or even ground velocity, then the values coming out from each wheel could either be automatically adjusted - so that the left wheel tries to match the same velocity as the right wheel (or vice versa). So use one wheel as the reference, and make the other wheel try to lock onto the same velocity using a control algorithm.

Hi,
Did you have this problem of different motor speed as you developed your sketch.
Did you write a sketch just to run the motors forward, then develop it to turns.
That is did you program your sketch in stages, get each stage working correctly then program the next stage, and eventually join them?
OR did you try and write it ALL at once.

If you used the first method then you must have code that PURELY runs the motors.
Go back to that and see if your motor speed diff problem exists.

You need to program a basic sketch to check this problem, only controlling the motors.

Tom.... :slight_smile:

Southpark:
So use one wheel as the reference, and make the other wheel try to lock onto the same velocity using a control algorithm.

But that only takes care of the first part (the motor speed) of the three issues PaulS pointed out in #1:

PaulS:
since the motor speeds will not be identical AND the wheel diameter is not identical AND the amount that each wheel slips is not identical (and they DO slip).

It will still need .....

PaulS:
some sort of "which way am I actually going" feedback,

JimboZA:
But that only takes care of the first part (the motor speed) of the three issues PaulS pointed out in #1:

It will still need .....

Yeah..... not sure. If we want to control the speed of the robot, then we need should at least get both wheels (same diameter ones) going at the same speed. That's just to begin with. And if the robot goes over a bump etc, then some kind of system - like a compass etc, needs to get the robot back in line.

I agree. As close to equal speed as possible for two wheels/motors just needs encoders, and a control system to drive them to the same speed - preferably as quickly as possible.

If you use a compass stick it up the top of a mast so its well away from the motors and other iron+steel.
Indoors you'll always have some magnetic disturbance, but on average it should be OK unless you are
near an NMR lab or the LHC!

Sure, I will keep the compass away from the motors,thanks for your suggestion!