Help! I have tried everything I can think of but am stuck and totally confused about the output from my optical rotary encoder. In the graph pic you will see a “sawtooth” pattern, that’s the problem, what I want to see is a nice smooth curved line.
In an effort to find the reason for this issue I has stripped out all unnecessary code from my project, back to bare bones. The graph is output data from a simple gravity drop test. The encoder has a pendulum attached and I simply dropped it from a horizontal starting position and record the microseconds to each pulse and then find the intervals between pulses. I am using gravity to eliminate as many environmental variables as possible.
The graph seems to indicate that the time recorded by each pulse (4 pulses per cycle) is slightly and consistently out, resulting in the ‘sawtooth’ pattern. From the basics I know about optical encoders each of the four quarters of a 360 degree cycle are, just that, quarters, 90 Degrees. But my data appears to contain a 4 part repeating pattern that suggests that the encoder pulses are not firing at exactly 90 degrees.
I originally though it was because of a broken Sparkfun encoder, so I purchases a S5 Encoder from US Digital but the issue remained. I am using an Arduino UNO and each channel wire is connected to each of the two interrupt pins.
No amount of Google-ing has turned up an answer. I must be missing some fundamental I just don’t know what it is.
Hopefully you can tell me what this issue is and/or how to fix it?!
volatile int counter = 0;
volatile int countMax = 150;
volatile long array[150];
void setup() {
attachInterrupt(0, encoderInterupt, CHANGE);
attachInterrupt(1, encoderInterupt, CHANGE);
Serial.begin(9600);
}
void loop()
{
if( counter == countMax )
{
for (int i = 0; i < countMax; i++)
{
float val = array[i] - array[0];
Serial.println(val);
delay(10);
}
}
}
void encoderInterupt()
{
if(counter < countMax)
{
array[counter] = micros();
counter++;
}
}
As the pendulum swings back and forth, the direction changes ... so without determining direction, how do you know which data represents 90° or 270°? 0° or 180°?
I understand that you are simply recording the times of the first 150 edges you see on the two channels without regard for direction. Here's some suggestions.
array[150]should be typed as volatile unsigned long since it contains micros().
val should be typed as unsigned long. Sometimes float math can be unpredictable and you are merely subtracting two big numbers.
I don't understand how the graph is related to the data you record
for (int i = 0; i < countMax; i++)
{
float val = array[i] - array[0];
Serial.println(val);
delay(10);
This should be monotonically increasing as the starting value is subtracted from each subsequent value.
One way to help trouble shoot the encoder would be to run only one interrupt at a time, or to change the interrupt to RISING or FALLING and determine if you see the sawtooth.
cattledog:
I understand that you are simply recording the times of the first 150 edges you see on the two channels without regard for direction.
correct.
array[150]should be typed as volatile unsigned long since it contains micros(). val should be typed as unsigned long.
I added this to the code, but the sawtooth remains, unfortunately.
I don't understand how the graph is related to the data you record ... This should be monotonically increasing as the starting value is subtracted from each subsequent value.
You are right, I should have been clearer here. the graph attached is not directly the data as suggested by the code, it is the interval time between each data point. I use this inteval graph to better illustrate the issue.
One way to help trouble shoot the encoder would be to run only one interrupt at a time, or to change the interrupt to RISING or FALLING and determine if you see the sawtooth.
One at a time they work just fine, I need 4 because i am measuring rotary acceleration to calculate torque and so I need as high resolution as possible.
Thank for your reply, any other thoughts? The cable is 1 metre long (3 feet) could this be an issue?
Can you take the measurements to see the length of pulse A and the length of pulse B as well as the offsets between them to see if there is an explanation for the sawtooth.
I still highly recommend that direction of rotation is determined. This will involve tracking (recording) both rising and falling edges for both signal channels.
Note that the phase relationship signal A to B changes for each direction. Unfortunately, other characteristics change too ... symmetry ±30°, quadrature ±30°, index pulse width ±30°,
Ch. I Rise After Ch. B or Ch. A Fall = 100 ns typical
Ch. I Fall After Ch. B or Ch. A Rise = 15 ns typical
One additional thought, which could explain why you see the same problem on two different encoder. Perhaps the two different interrupt pins have different thresholds for high and low values. That would affect the timing of the pulses. To explore this, you could switch the A and B channel pins and see if the sawtooth pattern is different.
Another idea would be to switch to pin change interrupts on two other pins like 8 and 9.
Note that the phase relationship signal A to B changes for each direction. Unfortunately, other characteristics change too ... symmetry ±30°, quadrature ±30°, index pulse width ±30°,
Hi Dlloyd, hmmm that could explain it. Do you know if these tolerance would be somewhat constant or would it shift around with heat, age, etc? I am wondering if I might be able to 'tune' the figures if that is the case?
catledog, thanks for doing that! I'll go test it out and compare,
Hi Dlloyd, hmmm that could explain it. Do you know if these tolerance would be somewhat constant or would it shift around with heat, age, etc? I am wondering if I might be able to 'tune' the figures if that is the case?
Could help better if we had the full part number of your encoder ... with your code counting to 150 max, this might imply that your using a linear strip encoder module with 127 lines per inch (EM1-0-127-N or EM1-0-127-I) ... or is it disk type?
I'm sure we could all offer better help with full part numbers and an image of your setup.
I used he pin change code and compared it to a single external interupt with CHANGE parameter. The
resulting data was estentially identical. I then tried with a second arduino and got the same result. This obviously pointed pretty strongly to the encoder as the source of the trouble. This went against what I was assuming that "One at a time they work just fine" so i re-graphed the interval time of a single external interrupt channel with with RISING and, contrary to what I believed, that also has a repeating pattern.
So yeah it is pretty clear to me now that is the encoder as you suggested dlloyd.
The encder model is S5-5000-236-IE-S-B
(Shaft Encoder, 5000 CPR, 6mm Dia Shaft, Index, Single-Ended, Ball Bearing)
The pics show the setup of the project. (for the gravity drop I simple clamp the removable top half to my bench) The project is a kind-of "human dyno" called PunchBot that will measure the Watts power of a punch.
I need super accuracy is to determine when deceleration occurs. So I guess I now need either a way to dial out the repeating pattern or some kind of data smoothing?
Alos, thanks you very much for the your time and help
This went against what I was assuming that "One at a time they work just fine" so i re-graphed the interval time of a single external interrupt channel with with RISING and, contrary to what I believed, that also has a repeating pattern.
Do both channel A and B show the saw tooth when they are run one at a time with only the trigger for RISING?
Have you tested the pulse lengths of A and B or the timing between the channels?
You are going to have four different intervals which you are timing. Having a quadrature pattern chart to look at will help. For example, start with an A rising pulse, then for one of the directions the intervals will be that first A rising to the next B falling, then that B falling to the next A falling, then that A falling to the next B rising, and finally, that B rising to the next A Rising. If you identify the transitions you should be able to figure out which are causing the sawtooth and correct for them.
I Agree with cattledog ... I was preparing a post similar in nature, but rather than delete it, here it is...
I need super accuracy is to determine when deceleration occurs. So I guess I now need either a way to dial out the repeating pattern or some kind of data smoothing?
To get ultimate accuracy, you'll need to "calibrate" out the error. My thoughts are that there is a different error for each direction of travel, however I believe the error for each direction is consistent and repeatable.
Firstly, I'm suspicious of the use of "long" and "float". See if this gives any improvement...
volatile int counter = 0;
volatile int countMax = 150;
volatile unsigned long array[150];
void setup() {
for (int i = 0; i < countMax; i++)
{
array[i] = 0;
}
attachInterrupt(0, encoderInterupt, CHANGE);
attachInterrupt(1, encoderInterupt, CHANGE);
Serial.begin(9600);
}
void loop()
{
if ( counter == countMax )
{
for (int i = 0; i < countMax; i++)
{
unsigned long val = array[i] - array[0];
Serial.println(val);
delay(10);
}
}
}
void encoderInterupt()
{
if (counter < countMax)
{
array[counter] = micros();
counter++;
}
}
This will also show signal status for each data point. Need to calculate timing errors for calibration. 0% error is when the rising or falling edge of B is 1/2 way between the edges of A.
volatile int counter = 0;
volatile int countMax = 150;
volatile unsigned long array[150];
volatile byte signalA[150];
volatile byte signalB[150];
int encoderPinA = 2;
int encoderPinB = 3;
void setup() {
for (int i = 0; i < countMax; i++)
{
array[i] = 0;
signalA[i] = 0;
signalB[i] = 0;
}
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
attachInterrupt(0, encoderInterupt, CHANGE);
attachInterrupt(1, encoderInterupt, CHANGE);
Serial.begin(9600);
}
void loop()
{
if ( counter == countMax )
{
for (int i = 0; i < countMax; i++)
{
unsigned long val = array[i] - array[0];
Serial.print("val ");
Serial.print(" A ");
Serial.print(signalA[i]);
Serial.print(" B ");
Serial.println(signalB[i]);
delay(10);
}
}
}
void encoderInterupt()
{
if (counter < countMax)
{
array[counter] = micros();
signalA[counter] = digitalRead(encoderPinA);
signalB[counter] = digitalRead(encoderPinB);
counter++;
}
}
Hi gentlemen, attached is an excel file with the results of dlloyds code (thanks for that) with a few tweeks to output CSV
I have created an excel with the data but I cant upload excel. If you PM me I will email it to you. There are two sheets to the document: the first using micros() and unsigned long as requsted but the arduino Uno can only resolve to +-4ms accuracy. The second sheet used a timer library that can resolve to +-0.5ms (this is what I was originally using by took it out for the drop tests to reduce unknowns)
I have colour coded the graph so you can see the 4 different types. Hopefully you can pull some info from that data.
I am beginning to wonder if that sawtooth pattern might be very minor vibration. I was thinking perhaps I could rig the encoder up to my turntable and take some values from that? I suspect it would eliminate some environment variables as the turntable is a reasonably high quality unit and is designed for consistent rotational speed.
Do both channel A and B show the saw tooth when they are run one at a time with only the trigger for RISING?
Have you tested the pulse lengths of A and B or the timing between the channels?
cattledog, hopefully the excel file will answer these questions
I dlloyd and cattledog I welcome any other comments you might have but I think I have worked out the issue. See the attached pic. I changed the Increment column from 'the difference between one value to the next' to 'the difference between the value of one channels type (ie: A low) to the next value of the same type'.
In the pic you can see the result of this produced data that is much more sensible leaving what I can imagine is just harmonic/vibration.
Thanks again for all your time! You questions, suggestions and code we super helpful.
Nice work lownlazy. You're graphing and skills and tools are much better than mine, and once you found the right format to present the transition data, all is clear.
Will you need to curve fit to the data in order to measure your punch? I thinking the swing following a punch would be much faster and the pivot joint friction/stiction might not be a big factor compared to the free drop. If you are measuring the difference in acceleration between the free swing and the punched swing maybe you can just subtract the theoretical gravitational component rather than subtracting the baseline curve. Or, since gravity is constant maybe everyone can just look a little stronger