Pololu wheel encoders

Hi all!

This is my first post in this Forum.

I’ve bought a Pololu wheel encoder set from Sparkfun (sparkfun.com/commerce/produc … ts_id=9209) and I’m mounting them on Sparfun’s Ardubot PCB. The brain will be an Arduino Pro board.

  • Encoder 0 is tied to Arduino’s pins 2 (phase A) and 4 (phase B),
  • Encoder 1 is tied to Arduino’s pins 7 (phase A) and 8 (phase B).

I’m planning to use AVR’s PCINT feature, so I can use “interrupt on change” linked to some microcontroller pins. I’ve tested your PololuWheelEncoder class and I’ve also written my own encoder class:

class Encoder {
  // private methods and attributes
private:
  static char global_last_m1a_val, global_last_m1b_val, countA, countB;
  static uint8_t _pA, _pB;
  static int32_t _position;
  static void doEncoderA(void);
  static void doEncoderB(void);
   // public methods
public:
  Encoder(uint8_t, uint8_t);
  void start();
  void stop();
  void write(int32_t);
  int32_t read(void);
};

char Encoder::global_last_m1a_val = 0;
char Encoder::global_last_m1b_val = 0;
char Encoder::countA = 0;
char Encoder::countB = 0;
uint8_t Encoder::_pA = 0;
uint8_t Encoder::_pB = 0;
int32_t Encoder::_position = 0;

//
// Constructor
//
Encoder::Encoder(uint8_t pA, uint8_t pB) {
  // initialisation of class attributes
  _pA = pA;
  _pB = pB;
  _position = 0;

  // initialisation of I/O
  pinMode( _pA, INPUT );
  pinMode( _pB, INPUT );
  digitalWrite( _pA, HIGH );  // pull-up
  digitalWrite( _pB, HIGH );  // pull-up
}

void Encoder::start() {
  PCattachInterrupt( _pA, doEncoderA, CHANGE );
  PCattachInterrupt( _pB, doEncoderB, CHANGE );

  global_last_m1a_val = get_val( _pA );
  global_last_m1b_val = get_val( _pB );
}

void Encoder::stop() {
  PCdetachInterrupt(_pA);
  PCdetachInterrupt(_pB);
}

void Encoder::write(int32_t position) {
  _position = position;
}

int32_t Encoder::read(void) {
  return _position; 
}

// Interrupt on A changing state
void Encoder::doEncoderA(){
  countA ++;

  unsigned char m1a = get_val( _pA );
  char plus_m1 = m1a ^ global_last_m1b_val;

  if (plus_m1) _position++;

  global_last_m1a_val = m1a;
}

// Interrupt on B changing state
void Encoder::doEncoderB(){
  countB ++;

  unsigned char m1b = get_val( _pB );
  char minus_m1 = m1b ^ global_last_m1a_val;

  if (minus_m1) _position--;

  global_last_m1b_val = m1b;
}

The code uses the standard code for PCINT as shown at arduino.cc/playground/Main/PcInt. When turning the wheel clock-wise, the number of pulses counted is not equal to the number of counts counter clock-wise. What’s wrong? Anyway countA and countB get the same values.

When debugging with my serial terminal, I get something like this:

10 1 0 1
00 2 0 1
01 2 1 0
00 2 2 0
10 3 2 1
00 4 2 1
01 4 3 0
00 4 4 0
10 5 4 1
00 6 4 1
01 6 5 0
11 7 5 0
10 7 6 -1
00 8 6 -1
01 8 7 -2
11 9 7 -2
10 9 8 -3
00 10 8 -3
01 10 9 -4
11 11 9 -4
10 11 10 -5
00 12 10 -5
  • first column are phase A and B inputs;
  • second column is phase A interrupt counter (called countA);
  • third column is phase B interrupt counter (called countB);
  • last column is the position counter.

Look at this:

  • when countB is between 0 and 6, there is no increment in the position counter and both pins are never 1 at the same time! This was a quater turn CW very slowly.
  • when countB is between 7 and 12, the position counter gets 5 pulses and both pins get all possible transitions.

I’ve been working with industrial encoders for years and this is not a good behaviour for an encoder. Maybe there is something wrong in the board or I’ve mounted it wrong, but all has been done as in your photos.

Best Regards,

suby

Hello.

It doesn’t make sense that you would see different behavior for different rotation directions. The readings you’re getting from the sensors at any given moment depends on the instantaneous positions of the encoder teeth; it’s not really relevant whether they’re moving CW or CCW. Do you observe the same behavior from both encoders? What happens if you use the encoder class from the Pololu library? Are you running the encoders at 5V? Have you turned any of the calibration trimpots? What happens if you don’t enable the pull-ups on your encoder pins (you don’t need these).

- Ben

Paul,

Let me comment your message:

Of course! The encoder should behave the same way.

Yes, I know, but anytime you have a problem in an encoder you see things like these.

Tomorrow I’ll try this with the other encoder.

The only difference may be the pull-ups. Nothing else.

Yes.

No.

I’ll try also this.

Thanks!

suby

Hello, Suby,

If you continue having problems, can you post a picture of your setup? Is it possible that you’re getting a lot of ambient light on the sensors? Or do you have some other IR sensors around the encoders that could be interfering with their operation? One other thing to check is that the tire is seated well on the wheel since the black tire is what provides the contrast with the white teeth.

- Jan

I’m Ben :slight_smile:

Have you actually tried it, or are you just assuming that you duplicated it exactly? Since you have two encoders and two encoder libraries, it seems like it will be easy to track down if the problem is in hardware or software. Another thing I’d be interested to see is what happens if you just write a simple, interrupt-free program that just constantly streams the values of the two encoder outputs over serial. You should see, with multiple consecutive identical entries condensed into a single instance:

10
11
01
00
10

when turning very slowly in one direction, and

10
00
01
11
10

when turning very slowly in the other direction.

If you don’t, it’s a good indication that there’s something wrong with the hardware side of things.

- Ben

Ben,

Sorry! I’ve written my code with the PololuWheelEncoder class and it works perfectly! When I wrote my Encoder class, I assumed it behave like yours. :cry:
Look at the attached Arduino code:

Motor LeftMotor(5,3);
Motor RightMotor(9,6);
// Encoder RightEncoder(2,3);
// Encoder LeftEncoder(7,8);
PololuWheelEncoders encoder;

// Metro tim1(100,1);

char val1, val2;
char s;

void setup() {

  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);

//  RightEncoder.start();
//  LeftEncoder.start();

  //  encoderInit();
  
  encoder.init(7,8,2,4);

  Serial.begin(38400);
  delay(1000);
  Serial.print("test ardubot...");
  Serial.println();
}

long t1,t2;

void loop() {
  /*  if (tim1.check()) {
   digitalWrite( 13, LOW);
   Serial.print( digitalRead( 7 ));
   Serial.print( digitalRead( 8 ));
   Serial.print(" ");
   Serial.print( LeftEncoder.read(), DEC );
   //    Serial.print( position , DEC);
   Serial.println();
   }*/

  LeftMotor.forward(100);
  RightMotor.forward(100);
  delay(1000);
  
  LeftMotor.stop();
  RightMotor.stop();
  llegirPos();
  delay(1000);  

  LeftMotor.backward(100);
  RightMotor.backward(100);
  delay(1000);
  LeftMotor.stop();
  RightMotor.stop();
  llegirPos();
  delay(1000);

}

void llegirPos() {
//  Serial.print( RightEncoder.read(), DEC );
  Serial.print( encoder.getCountsAndResetM1(), DEC );
  Serial.print(", ");
  Serial.print( encoder.getCountsAndResetM2(), DEC );
  Serial.println();
}[/code]
After turning both motors on each way for 1 s, their position is sent to serial port and reset, so each time there must be the same count. Here are the results:
[code]43, 48

-48, -46

47, 48

-47, -48

45, 48

-46, -47

46, 48

-46, -47

45, 47

-47, -47

47, 48

-47, -46

46, 46

-47, -46

45, 48

-46, -47

46, 47

-46, -46

45, 47

-47, -47

47, 47

-47, -46

46, 47

-47, -46

46, 46

-46, -46

Thanks for your patience!!!

suby