HELP: Need help to make my odometer works

Hi all,

I need help in order to make my robot odometer works. I'm building my robot in order to make some study around SLAM systems so the odometer is really important for my project.

My robot is based on this chassis: http://www.homotix.it/catalogo/robotica-e-accessori-438/magician-chassis
I work to add odometer to it using an encoder wheel with 6 black band and a IR sensor (SparkFun Line Sensor Breakout - QRE1113 (Analog) - ROB-09453 - SparkFun Electronics) to detect the wheel rotation. This sensor give as output a tension that change when the black or white band pass ahed the sensor.


(package will be improved as soon it will works!)

In order to transform the signal wave to a square wave that can be used with arduino interrupt I added a Schmitt trigger: using this circuit schema the signal became HIGH when the output rise above a threshold and LOW when the output drop down a second threshold lower than the previous one.
I used an OpAmp to implement this schema (Schmitt Trigger Applications).

I attached the output to my Arduino DUE digital pin and attach interrupt to them. On the interrupt void a volatile int counter is incremented by one for every wheel.
Unfortunately it is not working as expected. When the robot is moving my interrupts are called a lot of time for a single passage from black to white band, so, instead to have:
0,1, 2, 3, 4....
I have
0,12,34,78,134....

I added the trigger just to avoid this.... argh :0

By now I solve it by software: when the interrupt void is invoked I check invocation time and make difference between last invocation: if the difference is less that a threshold I don't update the counter. In that it works better but really prefer to make it work in the right way.

So, what's the right way to connect the IR to arduino? Any advice on how to fix my problem?

Thanks all for support
Ale

Any advice on how to fix my problem?

Don't suppose that seeing your code is a possibility, is it?

PaulS:

Any advice on how to fix my problem?

Don't suppose that seeing your code is a possibility, is it?

To be honest no... it's an hardware problem!

Btw, this is the sketch I'm using now for testing:

#include <TFT.h> 
#include <SPI.h>
#include "TB6612FNG.h"
#include "GeduinoPINS.h"

TB6612FNG tb6612nfg;
TFT TFTscreen = TFT(CONTROL_CSLD, CONTROL_DCLD, CONTROL_RESET);

unsigned volatile int guidoEncACount = 0;
unsigned volatile int guidoEncBCount = 0;

long lastReadA = 0;
long lastReadB = 0;

void setup() {

   Serial.begin(9600);
  
  // Attach tb6612nfg
  tb6612nfg.begin();
 
  attachInterrupt(GUIDO_ENCA, guidoEncA, RISING);
  attachInterrupt(GUIDO_ENCB, guidoEncB, RISING); 

  pinMode(CONTROL_JUP, INPUT);
  pinMode(CONTROL_JDW, INPUT);
  pinMode(CONTROL_JLX, INPUT);
  pinMode(CONTROL_JRX, INPUT);
  
  pinMode(CONTROL_JCN, INPUT);
  pinMode(CONTROL_ENCA, INPUT);
  pinMode(CONTROL_ENCB, INPUT);

  TFTscreen.begin();
  TFTscreen.background(0, 0, 0);
  TFTscreen.stroke(0,0,0);
  TFTscreen.setTextSize(2);
}

void guidoEncA() {
 // long now = millis();
  
 // if (now - lastReadA > 20) {
    guidoEncACount = guidoEncACount + 1;
 // }
 // lastReadA = now;
}

void guidoEncB() {
 // long now = millis();
 // if (now - lastReadB > 20) {
  guidoEncBCount = guidoEncBCount + 1;
 // }
 // lastReadB = now;
}

void loop() {

  int up = digitalRead(CONTROL_JUP);
  int down = digitalRead(CONTROL_JDW);
  int left = digitalRead(CONTROL_JLX);
  int right = digitalRead(CONTROL_JRX);
   int confirm = digitalRead(CONTROL_JCN);
  int enca = digitalRead(CONTROL_ENCA);
  int encb = digitalRead(CONTROL_ENCB);

  Serial.print(up);
  Serial.print(down);
  Serial.print(left);
  Serial.print(right);
  Serial.print(confirm);
  Serial.print(enca);
  Serial.print(encb);
  Serial.print(" ");
  Serial.print(guidoEncACount);
Serial.print(" ");
Serial.print(guidoEncBCount);
Serial.print(" ");
Serial.println(lastReadA);

char printA[4];
char printB[4];
String(guidoEncACount).toCharArray(printA, 4);
String(guidoEncBCount).toCharArray(printB, 4);

TFTscreen.background(0, 0, 0);
TFTscreen.stroke(255,255,255);
TFTscreen.text(printA, 0, 20);
TFTscreen.text(printB, 0, 50);

  if (up == HIGH) {

    // Set spped
    tb6612nfg.setSpeed(32,32);

    // Start motors
    tb6612nfg.start();

    delay(1000);

    tb6612nfg.stop();
  } else if (down == HIGH) {
    // Set spped
    tb6612nfg.setSpeed(-128, -128);

    // Start motors
    tb6612nfg.start();

    delay(1000);

    tb6612nfg.stop();
  } else if (left == HIGH) {
    
    // Set spped
    tb6612nfg.setSpeed(64,-64);

    // Start motors
    tb6612nfg.start();

    delay(250);

    tb6612nfg.stop();
    
  } else if (right == HIGH) {
    
    // Set spped
    tb6612nfg.setSpeed(-64,64);

    // Start motors
    tb6612nfg.start();

    delay(250);

    tb6612nfg.stop();
    
  }

  delay(100);
}

On this code the filter system I used is commented.

Thanks
Ale

So are you getting multiple interrupts from a single transition of your wheel ?

Or is your program running so slowly that you only get to check, after the wheel has turned many times ?

Or are you getting spurious triggering from your wheel ? Most implementation seem to be trying to shine some kind
of light through a hole or slot in the wheel, not trying to detect the black and white colour.

Those long delays in your program look unhelpful.

michinyon:
So are you getting multiple interrupts from a single transition of your wheel ?

Yes, exactly! Also moving the wheel really slow with my hand the value is increased by 10, 20 sometimes also more.

michinyon:
Or is your program running so slowly that you only get to check, after the wheel has turned many times ?

Of course the display it's not real time refreshed: but if my wheel moved only from a black area to the next white one
the increment should be one.

michinyon:
Or are you getting spurious triggering from your wheel ? Most implementation seem to be trying to shine some kind
of light through a hole or slot in the wheel, not trying to detect the black and white colour.

This is what I think is happening! My system works really similar: instead to have an hole I have white/black area. The sensor has an emitter and a receiver and the black/white area absorb/reflect the emitted signal. Do you think this should be the cause of my problem?

This is the Eagle schema of the system:

On the left side there is the sensor schema from Sparkfun. On the right side the OpAmp used to transform the incoming signal to a square wave.

O nice this is a similar problem to the button bounce. For this reason I try to add and RC filter before the trigger without result.
I think I need an oscilloscope in order to "debug" my hardware!

My thoughts on your situation:

First - clean up your code and narrow the function of the code down to -just- showing a count based on the sensor input. Turn the wheel by hand or via a battery. Basically doing this will rule out anything else that might be causing issues.

You have what appears to be a simple encoder wheel printed on paper and either taped or glued to the encoder wheel supplied with the kit and motor; it doesn't appear like you have mounted that paper wheel perfectly flat on the supplied encoder wheel; it needs to be kept as perpendicular to your sensor as possible. In fact, printing it on heavier stock may be in order. Also, increase the darkness level of your printer as much as possible, and make sure you are printing with black ink only (not some fake combo like some printers use) - ideally, you should be using a black and white laser printer and toner, on the darkest and densest print setting. Even better would be to use flat (non-gloss) black and white paint (instead of printing).

Your sensor should be covered as much as possible, to keep out as much ambient light as possible (lots of IR in ambient light). Also, you might take a clue from IR remote controls, which PWM the LED (usually at around 38 KHz), and have a means to sense only 38 KHz modulated signals - also in an effort to cut out ambient IR from the environment.

Finally, IIRC, that kit has a slot on the chassis for the encoder wheel to pass thru (?) - and you are supposed to mount a slotted IR emitter-detector pair on the other side of it; something like this:

...although the gap on that sensor seems a bit wide for your application (you would need to replicate something like the circuit you provided a schematic for earlier; that particular sensor I linked to only has the LED and photo-transistor; none of the other parts are included and would be needed to make the sensor work properly with the Arduino).

That wheel is slotted to act as an encoder; no need to print anything out, etc - though you still might need to enclose the sensor more (and/or modulate it) to help reject ambient IR.

I'm following suggestions by cr0sh:

First - clean up your code and narrow the function of the code down to -just- showing a count based on the sensor input. Turn the wheel by hand or via a battery. Basically doing this will rule out anything else that might be causing issues.

I remove all other code from my sketch:

#include "GeduinoPINS.h"

unsigned volatile int guidoEncACount = 0;
unsigned volatile int guidoEncBCount = 0;


void setup() {

   Serial.begin(9600);
 
  attachInterrupt(GUIDO_ENCA, guidoEncA, RISING);
  attachInterrupt(GUIDO_ENCB, guidoEncB, RISING); 

}

void guidoEncA() {
    guidoEncACount = guidoEncACount + 1;
}

void guidoEncB() {
  guidoEncBCount = guidoEncBCount + 1;
}

void loop() {

  Serial.print(guidoEncACount);
Serial.print(" ");
Serial.println(guidoEncBCount);

 }

I'm surprised by results: it's not working yet as expected but is really more stable. For a complete wheel round I should have 6 'clicks' by the encoder. Using this code counters are increased sometimes by 1 or 2 per clicks (before sometimes it was incremented also by 100!). I moved the wheel by hands and it seems the behavior is not affected by the wheel speed.

I try also this code:

#include "GeduinoPINS.h"

unsigned int guidoEncACount = 0;
unsigned int guidoEncBCount = 0;
int guidoEncAPreviousState = LOW;
int guidoEncBPreviousState = LOW;
int count = 0;

void setup() {

Serial.begin(9600);

pinMode(GUIDO_ENCA, INPUT);
pinMode(GUIDO_ENCB, INPUT);

}

void loop() {

const int guidoEncAState = digitalRead(GUIDO_ENCA);
const int guidoEncBState = digitalRead(GUIDO_ENCB);

if (guidoEncAState == HIGH && guidoEncAPreviousState == LOW) {
guidoEncACount++;
}

if (guidoEncBState == HIGH && guidoEncBPreviousState == LOW) {
guidoEncBCount++;
}

guidoEncAPreviousState = guidoEncAState;
guidoEncBPreviousState = guidoEncBState;

if (count++ == 1000) {
Serial.print(guidoEncACount);
Serial.print(" ");
Serial.println(guidoEncBCount);
count = 0;
}

}

More surprised again: it works perfectly!
I add the if statement on the serial output in order to avoid a fix delay after every read.

You have what appears to be a simple encoder wheel printed on paper and either taped or glued to the encoder wheel supplied with the kit and motor; it doesn't appear like you have mounted that paper wheel perfectly flat on the supplied encoder wheel; it needs to be kept as perpendicular to your sensor as possible. In fact, printing it on heavier stock may be in order. Also, increase the darkness level of your printer as much as possible, and make sure you are printing with black ink only (not some fake combo like some printers use) - ideally, you should be using a black and white laser printer and toner, on the darkest and densest print setting. Even better would be to use flat (non-gloss) black and white paint (instead of printing).

I know this is not well build! BTW I use laser printer and I also tested the result with sensor before mounting and the output change from 0.3 to 3.2 passing from black to white, so I think they are ok. In future I want to improve it using white sticky paper for white bands black velvety sticky paper for the black ones (this should be best to avoid reflection at all). I will also build a 'case' to isolate encoders between them and the environment.

During design I first think about the sparkfun sensor you link but it was too wide and not so easy to integrate on my project. Of course, if I will fail with actual system, I will try it.

Thanks for all suggestion but I think I have some general problem with interrupts. I absolutely need to use it for my encoders but I really cannot understand the behavior of my code. I have a lot on experience on computer programming but not on microcontrollers and I'm learning is something complete differently.

Can anyone explain what happens?

Thanks for support
Ale

Another update: I tried to merge the two last sketch and change the guidoEncA function:

void guidoEncA() {

   const int guidoEncAState = digitalRead(GUIDO_ENCA);
   if (guidoEncAState == HIGH && guidoEncAPreviousState == LOW) {
     guidoEncACount = guidoEncACount + 1;
   }
   guidoEncAPreviousState = guidoEncAState;
 
}

In this way it works but... why?

RISING interrupt should not be called when the pin pass from LOW to HIGH?

I'm starting to be really confused about interrupts....

Thank to everyone can explain me how they are working!

Bye
Ale

I followed suggestion from Cr0sh and improve the packaging of the odometer.

Furthermore I make fine tuning of trigger treshold and now it works!

Thanks all
Ale

   const int guidoEncAState = digitalRead(GUIDO_ENCA);

Why is a local variable that is going to go out of scope a few nanoseconds later const?