Quadrature Encoder Counts

Actually , I have been since two weeks looking for convinced and final solution for my problem , actually I am completely lost , I am working on mobile robot (Rover 5) with 2 motors , 2 encoders . the controller that designed to the robot needs to know the odometery of mobile robot (X ,Y, Heading Angle ) , actually I am trying to function the encoders for this purpose , getting X ,Y, Heading Angle by measuring the traveled distance by each wheel , so to get the X ,Y, Heading Angle values , I should compute a accurate readings without missing any counts or ticks as could as possible .

The problem now is :

In the code in the attachment , while I am testing the encoders counts , I noticed that there is a difference between counts of encoders even when they spin in the same constant speed (PMW) , the difference increases as the two motors continue . so I thought that is the main cause of inaccurate odometery results .

In the output of the code (in the attachment also) the first two columns are right and left motors speed , the third & forth columns are right and left encoder counts , the fifth column is the difference between two encoders count , as you could see ,that even when the speed of two motors are approximately the same (each motor feed up with 100 PWM) there is a difference in the encoder counts and as you could see that the difference become big and big as the motors continuing spin .

One thing I thought that sending the same PWM value to two different motors will almost never produce the exact same speed , so I think that I should detect the absolute motion of the motors and adjust the power to get the speed/distance , but when I test the speed of motors after feed them with 100 PWM at same time , the two speeds were almost identical , but I noticed that there is a difference between counts of two encoders even when the motors spin in the same constant speed .

Actually , I don’t know where is the problem , Is it in the code ? Is it in the hardware ? or what ? I am completely lost , I need for patient someone to help
.
Thanks alot

Attachments contain Arduino Files .
Encoder_ino.ino (4.07 KB)
output.ino (28.7 KB)

What you are observing is as expected.

Unless your program commands the motors to move a certain number of counts, and monitors the number of counts moved, you should expect a difference between right and left motors.

Setting the velocities to be “the same” cannot be done exactly, and monitoring velocity is not the same as monitoring the actual change in position.

Your program does not monitor all the counts, because you have an interrupt attached to only one of each of the encoder outputs. That will introduce errors into the total number of counts.

Finally, the tracks will slip, so the exact number of counts actually does not matter very much.

thanks a lot Mr. Jim

So what do recomend me to do ?
Could you show me any code for Rover 5 encoders which doesn’t miss counts and able to give good results ?
Is there any modification on the origin code to reduce missed counts ?

I wouldn’t worry about small differences in encoder counts, but if the difference becomes large enough to be a problem, you can simply set position targets (counts) and stop the motors when those are reached.

This is usually done with a PID loop, comparing current position and target position for each motor, and setting the motor speed proportional to the difference between target position and current position. You need one PID loop for each motor.

To have the motors turning with constant velocity, at regular intervals just add a constant value to the target position.

I use pin change interrupts to monitor all the encoder outputs. Here is an example for one encoder, using Atmel Studio. This just prints the current encoder position on an LCD display.

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

//global variables: encoder position and direction of rotation
volatile unsigned int enc_pos;  // use unsigned long for large distances
volatile unsigned char enc_dir;

/*
PORTC Pin change interrupt service routine. Decodes the encoder.
For algorithm, see Scott Edwards article from Nuts&Volts V1 Oct. 1995
(righthand bit of old A,B) xor (lefthand bit of new A,B) => dir.
Increment or decrement encoder position accordingly
*/

ISR (PCINT1_vect) {
static unsigned char enc_last=0,enc_now;
enc_now = (PINC & (3<<PC4))>>4; //read the port pins and shift result to bottom bits
enc_dir = ( enc_last & 1 )^( (enc_now & 2)>>1 ); //determine direction of rotation
if (enc_dir == 0) enc_pos++; 
else enc_pos--; //update encoder position
enc_last = enc_now; //remember last state
}

#include "lcd.c"

int main(void)
{
unsigned char buf[8];

enc_pos=0; //Initialize encoder position

LCDInit(); //Initialize LCD display

DDRC  &= ~(3<<PC4); //Port C pins 4 and 5 as input
PCMSK1 |= (3<<PCINT12); //enable interrupt on pin change, bits 4&5 PORTC
PCICR |= 1<<PCIE1; //enable interrupt on pin change, PORTC
sei(); //enable global interrupts

while (1){

LCDSendCommand(LCD_Line1); //display current direction of rotation on line 1
itoa(enc_dir,buf,10);
LCDPrintString(buf);
LCDSendCommand(LCD_Line2); //display current encoder position on line 2
itoa(enc_pos,buf,10);
LCDPrintString(buf);
}
}

thanks a lot Jim

Actually I am working on Arduino Mega and I am not familiar with code lines below and I have no idea about it . Actually I am using DigitalRead & attachInterrupt to deal with encoders reading .

enc_now = (PINC & (3<<PC4))>>4; //read the port pins and shift result to bottom bits
DDRC  &= ~(3<<PC4); //Port C pins 4 and 5 as input
PCMSK1 |= (3<<PCINT12); //enable interrupt on pin change, bits 4&5 PORTC

My encoders pins :

// Right encoder
#define RightEncoderPinA 3
#define RightEncoderPinB 20

// Left encoder
#define LeftEncoderPinA 2
#define LeftEncoderPinB 21

So how to function these pins in the code you mentioned ?

thanks again .

You do not need to use those functions. They are very slow and very limited.

I (and you should) use direct port manipulation instead, which is available within the Arduino IDE. You also need to modify certain registers directly to enable the appropriate pin change interrupts. See section 13 of the ATmega328 data sheet for the details.

For a modified version of my code to work, both encoders should be connected to one port, using 4 adjacent pins of that port. I suggest to start with just one encoder and to connect the A and B outputs to PORTC bits 4 and 5 as I have done. Those pins are called A4 and A5 on the Arduino.

Get that to work before adding the second encoder, which I suggest be connected to PORTC bits 2 and 3 (A2 and A3). You will need to enable interrupts on those pins as well, and the interrupt code must determine which encoder caused the interrupt, by looking to see which pin changed.

Here is a method (written for Arduino) that uses pin change interrupts on different ports, so you don’t have to figure out which pin changed and caused the interrupt. forum.arduino.cc/index.php?topic=267426.0

thanks a lot Jim , I really benefited from the link you post ,thanks

Actually the code works good for one encoder and it counts about 167 counts per revolution but for other it doesn’t count . I know this may be silly to ask again and again but please be patient !

I am using Arduino mega , I have these configuration pins

PB0 ( SS/PCINT0 ) --> Digital pin 53 (SS)
PB1 ( SCK/PCINT1 ) --> Digital pin 52 (SCK)

PB2 ( MOSI/PCINT2 ) --> Digital pin 51 (MOSI)
PB3 ( MISO/PCINT3 ) --> Digital pin 50 (MISO)

I connected left Encoder on pins 52, 53 and right Encoder on pins 50 , 51 with activation interrupt pins as shown in the coder below

//Pin change interrupts on one pin of encoder pins A and B
//reads only half of quadrature states available

//PORTB pins; 
#define encoderPinA 53 //PB0
#define encoderPinB 52 //PB1

//PORTB pins; 
#define encoder2PinA 51 //PB2
#define encoder2PinB 50 //PB3

#define buttonPin  5 //reset button for encoder counts

//variables changed within interrupt make volatile
volatile long encoderValue;
volatile long encoder2Value;

long lastEncoderValue = 0; 
long lastEncoder2Value = 0;

void setup() { 

  Serial.begin (115200);
  Serial.println("Half of Quadrature Counts");

  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP);
  
  pinMode(encoder2PinA, INPUT_PULLUP); 
  pinMode(encoder2PinB, INPUT_PULLUP);
  



  //enable pin change interrupts and set pin masks; 
  //interrupt pin selection or wires from encoder can be switched for correct cw/ccw

PCICR |= (1<<PCIE0);//enable group interrupts on PORTB PCINT[7:0]
PCMSK0 |= (1<<PCINT0);//enable interrupt pin 53
//PCMSK0 |= (1<<PCINT1);//enable interrupt pin 9

PCMSK0 |= (1<<PCINT2);//enable interrupt pin 51



}

void loop() {

 //With fast counts, you may want to change this routine  to only print increments of n counts,  or counts per second, etc

  if(encoderValue != lastEncoderValue){
    Serial.print("Count:  ");
    Serial.print(encoderValue);
    Serial.println("\t");

    lastEncoderValue=encoderValue;
  }



  if(encoder2Value != lastEncoder2Value){
    Serial.print("Count2:  ");
    Serial.println(encoder2Value);
    

    lastEncoder2Value=encoder2Value;
  }
  


}

ISR (PCINT0_vect)//handle pin change interrupt for d8 to D13, triggered by 8
{ 
  if( bitRead(PINB,0)== bitRead(PINB,1)) //compare Pin 50 and 51
  {
    encoderValue++;
  } 
  else 
  {
    encoderValue--;
  } 
}


ISR (PCINT2_vect)// handle pin change interrupt for d8 to D13, triggered by 8
 { 
 if( bitRead(PINC,2)== bitRead(PINC,3)) //compare Pin 52 and 53
 {
 encoder2Value++;
 } 
 else 
 {
 encoder2Value--;
 } 
 }

You really need to pay better attention, and go over every post and every line of code with a magnifying glass. As clearly stated, the code you are using reads separate ports for each encoder. The following reads PORT C, pins 2 & 3 (pins 35 and 34 on a mega), but that is not where you connected an encoder.

As an aside, the Arduino developers created an unbelievable amount of confusion by relabeling all the microcontroller pins with meaningless names, and changing those names and/or functions between different versions of the Arduino.

ISR (PCINT2_vect)// handle pin change interrupt for d8 to D13, triggered by 8
{
if( bitRead(PINC,2)== bitRead(PINC,3)) //compare Pin 52 and 53
{
encoder2Value++;
}
else
{
encoder2Value--;
}
}

ooopppsss , I am so so sorry for that , I am really confused and my mind completely lost because pressures and hurry … your are right .

Actually , I just want to use port B for both encoder so I configured pins as follow :

PB0 ( SS/PCINT0 ) → Digital pin 53 (SS)
PB1 ( SCK/PCINT1 ) → Digital pin 52 (SCK)

PB2 ( MOSI/PCINT2 ) → Digital pin 51 (MOSI)
PB3 ( MISO/PCINT3 ) → Digital pin 50 (MISO)

I think the problem is in the code part below , need to be adjusted with pins above , I looked for datasheet of Arduino mega to see pins and registers configuration but I didn’t find thing could be useful , I just find this http://arduino.cc/en/Hacking/PinMapping2560 which doesn’t show "PCMSK " configuration

PCICR |= (1<<PCIE0);//enable group interrupts on PORTB PCINT[7:0]
PCMSK0 |= (1<<PCINT0);//enable interrupt pin 53
PCMSK0 |= (1<<PCINT2);//enable interrupt pin 51

The code you are using requires that you use separate ports for each encoder. If you want to use just one port, you will have to add extra code to figure out which encoder caused the interrupt.

Hello All,
I have a question related to using encoder counts and moving a motor a defined number of counts.
Basically, I would like to use a DC motor with an encoder to “step” like a stepper motor. I am using the PID Library
from Brett Beauregard to control a motor using a setpoint, with appropriate values for Kp, Ki, Kd, and I am using a pin change interrupt, complements of this web site and Jim Remington and “Cattledog” etc. This functions perfectly.

I would like to be able to send a signal to the motor, have it “rotate” a specific number of counts, and stop for a specified
length of time (like one second), and then “step” again. I have seen some code, not using the PID technique, which specifies a PWM value, a number of counts, and uses an analogWrite statement which drives the motor X counts. Perhaps that is the best way. Any suggestions? The motor I am testing theoretically has an 64 cpr encoder, 1 rev is 4192 counts, and the motor creeps (hysteresis?), but moves close to 1 turn plus a little, and the counts are around like 4200, 4203,4188, etc. I figure the motor is cheap (pololu 37D 131:1 gear ration with encoder), with spur gearing, and a small encoder. Any ideas on creating a step type action using the encoder counts?

thanks for a great resource.

ewholz

Hello, ewholz.

I am not familiar with the PWM method that you mentioned, but it seems like if you already have your motor moving a predefined number of counts on command that it should not be hard to get it to rotate by any number of counts that are divisible by the set number of counts in each step.

If you are trying to have it spin exactly one turn, you will probably want your code (PID loop) to slow down the motor before it reaches the stopping point to prevent overshoot. If overshoot does occur, your code should move backwards to achieve the desired motor position. Once you have that implemented, you should be able to use the same control scheme for arbitrary positions.

If you post what you have, I would be happy to take a look at it and make suggestions.

- Jeremy

Hello Jeremy,

Thanks for the reply. I am working on the code now, and will post a sample later on.
Currently, I am not using the encoder counts to set a specific distance. The encoder counts
are used in the PID formula to derive the input value for the PID. I have a formula based on the motor
gear ratio (131:1), the current value of the encoder count(s), like this:

void getMotorData()     // calculate speed, volts and Amps
   {                                         
     static long countAnt = 0;  // last count // 64 pulses X 131 gear ratio = 8384, try 32 > 4192 
     speed_act = ((encoderValue - countAnt)*(60*(1000/LOOPTIME)))/(32*131);  
     if (direction == CCW) {
       Input = -speed_act;       
     }else{
       Input = speed_act;
     }
     countAnt = encoderValue;   
   }

This value drives the motor to the setpoint. I have attached my current code, which just runs a speed set by a pot.
A_MilitaryMotor.ino (19.3 KB)

I briefly looked at your code, and it looks like a good starting point. To move a specific distance using the encoder counts, I recommend implementing something similar to what I suggested in my previous post. If you get stuck, please post your code so I can take a look at it.

- Jeremy

Hello Jeremy,
thanks for the update. I finally figured out how to do it. I found a link to a robotics site, which
mentioned about how he wanted to equate wheel movement and distance.

My brain was focused on “speed” or velocity, not distance. Remembering that there are X encoder counts
for a revolution, and when a gear ratio is involved the encoder counts need to be multiplied by the gear ratio.

I am using a pin change interrupt to detect the “high” for both the A and B channels, which results in a X2 decoding method. I set up an if statement in the code that gets called from the loop function that stops the motor after X counts,then added a delay() statement. Since it is being called by the loop function, the motor moves, then stops for the length of the delay function, and moves again. I would like to try to use some form of counts from timer2 to fire the motor, instead of the delay function. Then I can count time using timer2, say 2 secs worth, then fire the motor, instead of flashing an LED, etc.

Thanks again for the response to my forum post.

ewholz.

Hi, ewholz, could you share the link?
I kinda need to achieve the exact same requirement.
Thanks in advance.