Measure distance travelled using rotary encoders

I am currently undertaking a project which requires me to measure the distance travelled by a robot car and use this as feedback to make the robot travel a specified distance. I am using an IR wheel encoder reader but not sure how to use this to measure the distance moved. Any help on how I should go about writing the code?

This is the image of the pins in the encoder reader

where is the rotary encoder? what's the IR interrupter for if you are using a rotary ?

Either way, you get an interrupt signal when some part (axle or wheel, or something) moves a certain distance. you work out how far, in bot movement that results in, under ideal circumstances, and that's about as good as it gets. It doesn't account for slippage or difference in rotation to achieve a turn, but it is a start. If possible, incorporate some kind of end of route leg indication (like an IR reflection, or ultra-sonic ping).

The rotary encoder supplies a fixed number of pulses per wheel/axis revolution. When you know the wheel diameter, you can compute the distance from the number of pulses received.

You have to supply an encoder wheel, usable with your module. When your module supplies a direction signal (what's D0 and A0?), you have to take into account pulses in forward and back direction.

Depending on the size of your wheels, etc, you will need to calibrate the code.
I suggest using the map() function. Run it for a foot, and log the encoder count. Then run it for 40 foot, and log the encoder count. put those 4 numbers into the map() function.

What is your target distance anyway?

Thanks for the replies but this is more like an IR sensor that detects rotary motion. It is suposed to detect the slots in the encoder disk and calculate the rotation based on this. but im not sure how to get it working. I have a code that uses a count and interrupt but it gives incorrect readings. The encoder disk has 20 slots, so theoretically it should give 20 counts for one rotation. but the readings are completely off. It goes to 100+ sometimes. Here is the code I'm talking about:

#define encoder0Pin               2

volatile int count, countold;

void setup()
{
  count = 0;
  countold = 0; 
  pinMode(encoder0Pin, INPUT_PULLUP); 
  attachInterrupt(0, sensor1, FALLING);

  Serial.begin(115200); // initialize serial communication:

}

void loop()
{
  if (countold != count)
  {
    Serial.println(count);
       countold = count;
  }
}

void sensor1()
{
  count++;
}

any help would be much appreciated

Post a clear picture of the encoder and disk arrangement.
Unless the sensor/disk is enclosed by a light-tight cover, you can expect interference from other light sources.

You have to protect access to your counter. When "volatile" is not enough, also disable the interrupts while reading count for output in loop(). Use noInterrupts() and interrupts() for that purpose, not attach/detachInterrupt.

The Reference example on "volatile" is confusing me. It suggests that "volatile" on a variable declaration is enough to prevent concurrent access to the variable. The observed behaviour suggests the contrary :frowning:

Post the link for where you got the sensor. We shouldn't have to ask you to do that.

If the encoder counts 20 times per revolution, how many revolutions a second does the motor run?
Is the encoder hooked to the motor, or after a set of reduction gears?

Why use map() ?!

"Why use map() ?!"
Well, if you have 31,423 revolutions then you want to know how many inches you moved. Not knowing off hand (tire size, reduction gears, etc.), how many inches you move for 31,423 revolutions, I suggest calibrating it using the map() function.

Have you ever used map() ? Do you not like it?

Thats what the sensor looks like and the encoder disk. I'm not very good with arduino coding and the code I posted is simply one I got off the internet from somewhere. Can anyone help with a proper coding that will atleast help with getting the readings of the count?

Link to where you got the encoder ?

Why not just use multiplication and/or division?

Anyway, you have to decide whether to use an int, long, or float. The map() function will not work on floats.

https://www.arduino.cc/en/Reference/Map

By the way, since you mention inches, I suppose you are American. And let's face it, Americans are not exactly known for their numeracy skills.

Americans are not exactly known for their numeracy skills.

Nonsense.

Americans have to convert inches (with bizarre fractions, like 3/64) to feet, yards to miles, pounds of weight to tons, pints to gallons, etc.

The rest of the world uses the metric system so they don't have to deal with such complex calculations.

ok what ive done now is make the robot move a certain known distance by manually pushing it and then taking not of the pulse count. Based on this i have estimated it to be 20 pulses per 10cm. but this varies on different speeds. so i calculate distance by pulse/20 to give the distance travelled in cm. but this doesnt seem very accurate. any better way of doing this?

Quote "Based on this i have estimated it to be 20 pulses per 10cm. but this varies on different speeds. so i calculate distance by pulse/20 to give the distance travelled in cm."

Shouldn't that calculation be "pulse/2" ?

I think that manually pushing it, may not feed back to the sensor.
You should:

  1. run the motor
  2. stop it at some distance
  3. measure the distance, and the number of pulses it took to get there.

I see now that my previous suggestion to use map() was not needed, so disregard the map() suggestion.

yes sorry, I meant pulse/2 actually.
but about your suggestion of moving it a certain distance and stopping it. how would i go about doing this? like for instance. this is the code that i used to test accuracy:

int encoder_pin = 3;  // The pin the encoder is connected           
unsigned int rpm;     // rpm reading
volatile byte pulses;  // number of pulses
unsigned long timeold; 
// The number of pulses per revolution
// depends on your index disc!!
unsigned int ppcm = 2;
float distance;


//motor pins
const int Motor1Pin1 = 8;
const int Motor1Pin2 = 9;
const int Motor2Pin1 = 10;
const int Motor2Pin2 = 11;

int flag;
int led=5;


void setup()
 {
   Serial.begin(9600);
     //Use statusPin to flash along with interrupts
   pinMode(encoder_pin, INPUT);
   
   //Interrupt 0 is digital pin 2, so that is where the IR detector is connected
   //Triggers on FALLING (change from HIGH to LOW)
   attachInterrupt(1, counter, FALLING);
   // Initialize
   pulses = 0;
   rpm = 0;
   timeold = 0;

   //define Motor Pins
    pinMode(Motor1Pin1, OUTPUT);   
  pinMode(Motor1Pin2, OUTPUT);   
  pinMode(Motor2Pin1, OUTPUT);   
  pinMode(Motor2Pin2, OUTPUT); 
  
  pinMode(led,OUTPUT);
 }

 void loop()
 {
   
   while (flag!=1)
   {
     GoForward();
   }
  
   //Write it out to serial port
   Serial.print("PULSES = ");
   Serial.println(pulses);
   distance = pulses/ppcm;
   Serial.print("DISTANCE = ");
   Serial.println(distance);


  }

 void counter()
 {
    //Update count
      pulses++; 
   distance = pulses/ppcm;
if (distance>=20)
{
 digitalWrite(led,HIGH) ;
Stop();
flag=1;


}

  else {
  GoForward();
 
  digitalWrite(led,LOW) ;
}
}

 
 //                MOTOR FUNCTIONS

void GoForward(){
  digitalWrite(Motor1Pin2, LOW);
  digitalWrite(Motor1Pin1, HIGH);
  digitalWrite(Motor2Pin2, LOW);
  digitalWrite(Motor2Pin1, HIGH);
}

void GoBackward(){
  digitalWrite(Motor1Pin1, LOW);
  digitalWrite(Motor1Pin2, HIGH);
  digitalWrite(Motor2Pin1, LOW);
  digitalWrite(Motor2Pin2, HIGH);
}

void GoLeft(){
  digitalWrite(Motor1Pin1, LOW);
  digitalWrite(Motor1Pin2, HIGH);
  digitalWrite(Motor2Pin2, LOW);
  digitalWrite(Motor2Pin1, HIGH);
}

void GoRight(){
  analogWrite(Motor1Pin2, 120);
  analogWrite(Motor1Pin1, 0);
  analogWrite(Motor2Pin1, 120);
  analogWrite(Motor2Pin2, 0);
}


void Stop(){
  digitalWrite(Motor1Pin2, LOW);
  digitalWrite(Motor1Pin1, LOW);
  digitalWrite(Motor2Pin1, LOW);
  digitalWrite(Motor2Pin2, LOW);
}

I thought about the method you mentioned. but what criteria would i add to the loop to make it stop at a specific distance in order to check the counts?

You don't need to stop at a specific distance. Just stop after a few seconds. Then measure both the distance, and the pulses.

Repeat that several times, to see if in measures consistently.

Code: "distance = pulses/ppcm;"
distance is a float, (not sure why you need a float, is 21.73cm better than 21cm). But the math is integer variables, and may not provide a float unless you have some indication in the calculation that it should calculate based on float.
If you really don't need float, I would go back to integer.

Try using the code at this site.