Optical encoder and fake pulses

Hello!
I've got a problem with measuring distance in my mobile robot with differential drive.
I'm using an optical encoder (TCST1103) with a disc mounted on the motor shaft.
The output of the encoder is connected to Arduino Leonardo pin with interrupt attached (falling edge).
The problem is that the encoder SOMETIMES counts a lot of fake pulses while the robot is not moving. It doesn't happen when the robot moves around (or I didn't notice it). I can set the shaft by hand to force this situation. It doesn't occur only in 1 position so it isn't probably because of the disc shape.
I've tried using Debouncer library as well as connecting capacitor between GND and OUT od the encoder. Debouncer worked well unless I pasted code to my program which is way more complexed than the one with just doubouncer library handling.
Capacitance of 330nF did nothing and 750nF affected not counting any pulses.
I would appreciate any solution,
Greetings

An optical solution shouldn't need debouncing. Please post the schematics of your setup. From your description my guess is a floating input line.

This is the datasheet od the device http://www.vishay.com/docs/83764/tcst1103.pdf
In the attachment I posted a photo.
Hope you don’t mind the paper version instead of electronically generated one, I wanted to do it fast :slight_smile:

Untitled-122.jpg

Hand drawn schematics are MUCH preferred over Frizting diagrams.

I suggest to reduce the size of the phototransistor emitter resistor from 10K to 1K or less. Note that the data sheet suggests 100 Ohms. That way it will be less sensitive to stray light.

@jremington

I've just tried your suggestion but things got much worse. Now, the avalanche of pulses at the Arduino's pin occurs at much wider range of revolution of the disc. I'm gonna try a 100K resistor instead.

Something else is wrong.

I've also tried messing with emitter side resistor afterwards. It affects pulses too. Tried 60 Ohm with 110 Ohm at the detector side, but it worked inconsistent (sometimes no detection and many pulses just in a second). Have you got any hints how to match resistors on each side?
Those values I tried are suggested in datasheet. The encoder isn't broken (I think) because detects pulses, yet works weird. The same happens on another one.

In reply #3, I suggested 1K or less at the detector side, (not the LED side), i.e. the phototransistor emitter load.

May we ask to post a photo of the installed sensor? Maybe it’s a problem with the rotating mask. What voltage has Vcc? 5V?

@jremington
Of course, I understood what you’ve said. Tried it but encoder started to detect even more “unnecessary” pulses.

@pylon
Sure, I attached the photo to the post. And yes, Vcc is taken from Arduino’s 5V pin.

Do you have the possibility to have a look at these pulses with a scope?

Try boosting the IRLED current to 25mA and inverting the output so that blocked is HIGH, open slot is LOW. Also try 100nF (0.1µF) capacitor.

Here's the circuit:

That sounds very likely to be interference, many times caused by the PWM. Also, being an optical sensor I would try to protect it from any external light sources, specially daylight.

What's the maximum frequency of your encoder? A debounce circuit would help to filter-out any high-frequency interference, which can be caused by any of the items mentioned before. Something like that should help:


Just replace the switch by the encoder... if your circuit uses a normally LOW, you need to invert a bit the circuit (just google it). Different capacitors and resistors will provide different rising and falling times, so you need to adjust according to your signal and filter out the high-frequency (low pass filter?!).

If you never used this circuit, imagine the capacitor as a buffer or reservoir. If the switch is "opened" and it "close" and "open" very quickly, there's no enough time for the capacitor to discharge and the MCU PIN still "thinks" nothing happen. Similar to when the switch is already "closed" and "open" and "close" again very quickly, there's no enough time for the capacitor to "fill" and the MCU pin things nothing happen.

Those sort of problem normally require a scope to keep eye on the signal, but you might be able to "print" the pulse duration on the Serial by setting the interrupt as "CHANGE" and calculate the "end micros()" minus "start micros()" between two interrupts. Just don't print every single interrupt, maybe print the smallest duration one every 1 second so you'll have an idea how much last a normal encoder pulse and how long the "fake" pulse is. This will help understanding the problem...

Thaks for all the solutions. I've solved the problem not changing anything in hardware.

I connected the sensor to analog input. Enabled Free Running mode of ADC and in ISR I attached branch control structures to check whether last state was "high" or "low". I had to mess a lot with values, but now it works perfect. It wasn't the interference issue I think, it's just how that sensor works.

Looking at the voltage values while rotating the wheel it seems like it's about linear dependency (at least in my sensor) of voltage and aperture. When light gets across the center of aperture the voltage is highest and decreases as less and less light gets through to detector of sensor. Every noise when disc covers over the aperture led to many fake pulses.

I highly recommend the above solution and Nick Gammon's tutorial about interrupts. That was a first time I've been messing with register values and took some time to get everything working. Hope this post will save a lot of somebody's time. :slight_smile:
Thank you!

Hello Ravburn,

Great it's working... but I didn't understand how your final solution looked like? Are you still using digital interrupts or reading the analog values from the sensor to count "pulses"? Are you using comparators?

I would assume the GPIO hysteresis should take care of the gradual changes in voltage form the IR optical encoder. It's understandable that the output voltage will be inversely proportional to the amount of light is passing through the encoder well.

I'm not sure if that would be a perfect linear relation but you'll end-up with a kind of triangle/sineish wave, considering all your setup is 5V, the wave would be from 0V to 5V and 5V to 0V.

Now, the MCU would read "LOW" at around 0.8V and "HIGH" at 2.7V... and it would have lots of "noise" when the voltage is close to those values if those where absolute numbers, but you need to count with the hysteresis, which is around 0.55V. Below is how the hysteresis works, basically... for the MCU if the voltage goes over 2.7V it reads HIGH until the voltage drops below 2.15V (2.7V - 0.55V). So this would take care of everything in a non-interference/acceptable noise environment.

Below, A is an output without hysteresis and B is with hysteresis.


I'm asking that because I'm about the replace some hall-effect rotary encoders with a optical encoders and perforated encoder wheels so I wasn't expecting much difference.

I connected the sensor to analog input. Enabled Free Running mode of ADC and in ISR I attached branch control structures to check whether last state was "high" or "low". I had to mess a lot with values, but now it works perfect. It wasn't the interference issue I think, it's just how that sensor works.

If you're using ADC to triger an interrupt at a certain value, then its uderstandable that you had to mess a lot with values to get it working. The problem is that the sensor is designed to operate as an optical switch. If the signal levels aren't quite reaching digital input logic levels, it just means that your circuit interface (signal conditioning) needs adjustment in hardware.

I think what you've managed to do is get it working with the existing deficient signal span. Over some unknown time, the IRLED output and the CTR will degrade a little and the sensor's signal span will drift. Then your code will need re-calibrating, so this is not a long term solution.

Well, from this information ...

Debouncer worked well unless I pasted code to my program which is way more complexed than the one with just doubouncer library handling.

it appears that the signal does cross over digital trigger levels for HIGH and LOW. However, there's probably multiple transistions (fake pulses) during both the signal rise and additional fake pulses during the signal fall. The problem with most debouncers is that they use only one ignore period for debouncing. This does not ensure debouncing on the opposite state change of the signal.

Here's a debouncing method that only looks at when the signal is stable. So as long as you have a signal that eventually settles HIGH, then changes state and eventually settles LOW, all you need to specify is the stable period for it to use. I've set it to 100µs which should work well with signals up to 5kHz. All state changes are fully debounced. No library needed ... please give it a try.

const unsigned long stableTime = 100; //µs
const word printTime = 250; //ms
const byte sensorPin = 2;

unsigned long previousPrint;
volatile unsigned long previousTime, count;

void setup()
{
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(sensorPin), sensor_isr, CHANGE);
}

void loop()
{
  if ((millis() - previousPrint) >= printTime) {
    previousPrint = millis();
    noInterrupts();
    unsigned long count_copy = count;
    interrupts();
    Serial.println(count_copy);
  }
}

void sensor_isr()
{
  unsigned long currentTime = micros();
  if (currentTime - previousTime >= stableTime) count++;
  previousTime = currentTime;
}

@ musskopf
What you've explained is exactly what I did, but I'm using ADC Interrupts. Comparators didn't work well, they also caught many fake pulses.
As I said, this idea works perfect when there is one power source and not too much devices connected to board. But now I've got another problem. In my mobile robot there are 3 power sources. GND of each of them are connected. The problem is, when I start the program (a ride of the robot) min ADC values are about 50, max about 400. 5 minutes later min are 150 and max about 350. Tried to adjust tresholds or histeresis, but it doesn't work well. I've also tried to set reference to internal 2,56V, but it works the same, just ADC returns different values.
What this "drift" comes from? Linear stabiliser?
If I set the thresholds too close to each other, I will still get fake pulses.
Have you got any solutions?

EDIT:
@ dlloyd
Sorry, I missed your post. I'll give it a try in a minute. Thank you!

I personally would use a digital interrupt with a debounce circuit like described before.

If the voltage levels from the IR encoder are too low, I would include a LM393 or similar comparator to get the voltage levels right. Any noise caused by the LM393 would be removed by the debounce circuit as well.

Note that CHANGE mode would count each state change - that is 1 count for when the slot gets blocked and 1 count for when the slot opens. You may need to divide the result by 2, but the counting should be consistent.

A way to increase signal level span would be to replace the 220Ω resistor with 150Ω. An additional increase could be made by changing the 10K resistor with 15K-22K.

pylon:
An optical solution shouldn’t need debouncing. Please post the schematics of your setup. From your description my guess is a floating input line.

An analog optical encoder can generate a lot of transitions due to noise, enough for proper
signal conditioning to be vital if you are going to feed it to an interrupt pin. Using a 74HC14
schmitt trigger to clean up the signal would be the right thing to do if using interrupts.

If polling, the transitions will be accounted for if the code is correct, and performance isn’t affected,
but still feeding an analog slow waveform into a digital input isn’t great practice.