How can I make robot car move straight perfectly and turn 90 degrees?

Hi all,
I am a beginner in coding and I have to create a project for my university. I have to build a robot car and its control system. In this project, I am using 2 DC motors with encoders.
Control system design: the robot must move forward and backward 1 meter and then turn 90 degrees and move further a half meter.

So far, the robot can move 1m forward and backward, but one side of the motors is always off, which means that does not move straight perfectly ( always off to the right side).
So, the question is how can I make it move straight or how can I control the differences between both encoders' pulses? For instance, which functions I should use or which equation or formula I should configure in my code.

Subsequently, due to new in coding, I have less idea of making the robot turns 90 degrees. Therefore, I do need you guys a favor and advice of turning the robot. So, if you guys have some example codes that I can use as a guide, or at least please, point it out for me a little bit of doing that, I would be really appreciated.

This is the code that I have been made so far.

// Motor A //
const int ENCA1 = 2; 
const int ENA = 5;
const int In1 = 7;
const int In2 = 8;
const int ENCA2 = 11;

// Motor B //
const int ENCB1 = 3;
const int ENB = 6;
const int In3 = 9;
const int In4 = 10;
const int ENCB2 = 12;

//Parameter //
const float HallRatio = 341.2 / 34.02;              // Hall x Ratio 34.02 = 341.2PPR 
const float pulse_per_fullrev = 34 * HallRatio;    // (34:1 gearbox) * (HallRatio (10.3))   Hall x Ratio 34.02 = 341.2PPR 
const int Diawheel = 44;                           // Diameter of the wheel (mm) 

//the globle variable
volatile float A_pulse = 0;
volatile float B_pulse = 0;

// Function to convert from millimetre to steps
int MMtoPulses(float mm){
  
  int result;                                                // Final calculation result
  const float Cirwheel = PI*Diawheel;                   // Calculate wheel circumference in mm
  float mm_step = Cirwheel / pulse_per_fullrev;         // MM per Step
  
  float f_result = mm / mm_step;                             // Calculate result as a float
  result = (int) f_result;                                  // Convert to an integer (note this is NOT rounded)
  
  return result;                                             // End and return result
}

void MoveForward(int distance, int mspeed){

  A_pulse = 0;
  B_pulse = 0;

   // Set Motor A forward
   digitalWrite(In1, HIGH);
   digitalWrite(In2, LOW);
 
   // Set Motor B forward
   digitalWrite(In3, HIGH);
   digitalWrite(In4, LOW);

    // Go forward until step value is reached
   while (distance > abs(A_pulse) && distance > abs(B_pulse)) {
   Serial.print("Motor A is ");
   Serial.print(A_pulse);
   Serial.print(" pulses and Motor B ");
   Serial.print(B_pulse);
   Serial.println(" pulses");
   
    if (distance > abs(A_pulse)) {
    analogWrite(ENA, mspeed);
    } 
    else {
    analogWrite(ENA, 0);
    }
    if (distance > abs(B_pulse)) {
    analogWrite(ENB, mspeed);
    } else 
    {
    analogWrite(ENB, 0);
    }
   }
//Stop/// 
 analogWrite(ENA, 0);
 analogWrite(ENB, 0);

}

void MoveBackward(int distance, int mspeed){

  A_pulse = 0;
  B_pulse = 0;

   // Set Motor A Backward
   digitalWrite(In1, LOW);
   digitalWrite(In2, HIGH);
 
   // Set Motor B Backward
   digitalWrite(In3, LOW);
   digitalWrite(In4, HIGH);

    // Go Backward until step value is reached
   while (distance > abs(A_pulse) && distance > abs(B_pulse)) {
   Serial.print("Motor A is ");
   Serial.print(A_pulse);
   Serial.print(" pulses and Motor B ");
   Serial.print(B_pulse);
   Serial.println(" pulses");
    
    if (distance > abs(A_pulse)) {
      //abs()
    analogWrite(ENA, mspeed);
    } 
    else {
    analogWrite(ENA, 0);
    }
    if (distance > abs(B_pulse)) {
    analogWrite(ENB, mspeed);
    } else 
    {
    analogWrite(ENB, 0);
    }
   }
//Stop/// 
 analogWrite(ENA, 0);
 analogWrite(ENB, 0);
}

void readA(){
  int A2 = digitalRead(ENCA2);
  if(A2 > 0){
    A_pulse--;
  }
  else{
    A_pulse++;
  }
}

void readB(){
  int B2 = digitalRead(ENCB2);
  if(B2 > 0){
    B_pulse++;
  }
  else{
    B_pulse--;
  }
}

  void setup(){
  Serial.begin(9600);
  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(In1, OUTPUT);
  pinMode(In2, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(In3, OUTPUT);
  pinMode(In4, OUTPUT);
  
  pinMode(ENCA1, INPUT);
  pinMode(ENCB1, INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA1), readA, RISING);
  attachInterrupt(digitalPinToInterrupt(ENCB1), readB, RISING);  

  delay(3000);
  MoveForward(MMtoPulses(1000), 100);  // Forward 1 metre at 100 speed
  delay(2000);  // Wait two second
  MoveBackward(MMtoPulses(1000), 100);  // Backward 1 metre at 100 speed

} 
  
void loop(){
  // put your main code here, to run repeatedly:

   Serial.print("Motor A is ");
   Serial.print(A_pulse);
   Serial.print(" pulses and Motor B ");
   Serial.print(B_pulse);
   Serial.println(" pulses");
//20/05/22
}

If there are encoders on each wheel then you can compare the count of pulses that they output and correct the speed of one or both motors to keep the car running straight. You may have to apply a correction factor if the wheels are not exactly the same diameter. If you know the diameter of the wheels you can calculate the distance moved by counting the pulses

You can employ a similar strategy to turn the car 90 degrees by counting pulses as the wheels move in opposite directions

Note that this assumes that the wheels do no slip on the surface that they are running on

1 Like

There are two parts to it. First, find out by experiment what the speed value is for each motor that gets the robot going as straight as you can manage. They are unlikely to be the same.

During movement, compare the encoder counts. If motor B gets ahead, reduce it's PWM a bit. If motor A gets ahead increase PWM on motor B. Only varying motor B ensures that the speed will stay around 100.

1 Like

This is called twice in setup():

  pinMode(ENB, OUTPUT);
1 Like

A straightforward approach to driving in a straight line with encoders is to use PID speed control of one motor, with the PID input being the difference in encoder counts.

In the PID loop, simplest form (P only):

motor_L_speed = base_speed + K*(encoder_R_count-encoder_L_count);  //adjust K for smooth response
set_motor_speeds(motor_L_speed, base_speed); //L, R motor speeds

As you can see, if encoder_R_count exceeds encoder_L_count, the left motor speeds up, and vice versa.

This assumes your robot is two wheel differential drive. With 4WD, wheel slip may be a serious problem.

1 Like

could you please tell me a little bit about where I should add them in my code?

Thank you

how do I know the distance of 90 degrees?

Thank you in advance.

Experimentation will tell you how many encoder counts are required to move 90 degrees

So, don't we have some equation, formula or theories to support and calculate it?

What percentage of a full rotation do the wheels have to make to turn the car 90 degrees ?

How many encoder clicks there are per wheel rotation ?

How many encoder clicks are needed to turn the car 90 degrees ?

Sorry, I'm so confused about those question that you have made.

So....
34 : 1 this my gearbox ratio
10.3 : 1 this for encoder per REV.
350.2 pulses is per 1 REV motor

PI * Wheel Diameter (44 mm)
one full rotation will travel about 138.23 mm

for 1 pulse will move around 0.39 mm

and yea, similar question that I have made, how do I know or calculate pulses which are needed to turn car 90 degrees?

Do we have a equation of formula to calculate them?

Thank you in advance.

In order to turn a 2 wheel vehicle through 90 degrees each wheel has to travel 1/4 of the diameter of the circle that the wheels move through. The diameter of that circle will be the distance between the wheels

Does that help you write the formula ?

The formula should not be difficult to derive if you want to, but it will likely generate the wrong number for a precise turn because of slop in the gears or wheel slip or other annoying real world considerations. In the end, you will need to tweak the required steps to compensate, so in my view, experimentation will serve you better than theory,

For the small distances you're moving your dead reckoning approach may get you adequate results, but it will illustrate the need for other sensing options to do navigation well.

not much at the moment, but I'll give it a goal.
Thank you so much

Wherever it makes sense to do so.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.