py-gaugette
py-gaugette copied to clipboard
How to get consistent results?
I'm trying to read out a rotary encoder using a Raspberry Pi, but I'm having trouble getting good results.
I first tried the code in the rotary_interrupt_test.py file, but as soon as the motor/encoder moves, the program dies without any error. It just ends as if it's finished. No output whatsoever.
So then I tried the code in the rotary_worker_test.py file, but whichever way I turn it, it only gives the following output (I removed the switch print):
rotate -1
rotate 1
rotate -1
rotate 1
rotate -1
etc.
So then I tried the code in the rotary_test.py file and adjusted it to the code below so that I can test if it always gets positive deltas when I expect only positive ones.
#!/usr/bin/env python
import math
import gaugette.gpio
import gaugette.rotary_encoder
import gaugette
A_PIN = 7
B_PIN = 9
gpio = gaugette.gpio.GPIO()
encoder = gaugette.rotary_encoder.RotaryEncoder(gpio, A_PIN, B_PIN)
last_state = None
last_delta = 0
last_sequence = encoder.rotation_sequence()
last_heading = 0
# for coarser granularity reading
steps_per_cycle = 4 # arbitrary, usually equal to steps per detent
while True:
state = encoder.rotation_state()
if state != last_state:
last_state = state
last_heading += 1
a_state = state & 0x01
b_state = (state & 0x02) >> 1
# compute sequence number:
# This is the same as the value returned by encoder.rotation_sequence()
sequence = (a_state ^ b_state) | b_state << 1
# compute delta:
# This is the same as the value returned by encoder.get_delta()
delta = (sequence - last_sequence) % 4
if delta == 3:
delta = -1
elif delta == 2:
# this is an attempt to make sense out of a missed step:
# assume that we have moved two steps in the same direction that we were previously moving.
delta = int(math.copysign(delta, last_delta))
last_delta = delta
last_sequence = sequence
if delta < 0:
print 'got a negative delta when I expect only positive ones:', delta
When I turn the motor manually I get the results that I expect (positive deltas), but when I turn the motor on at full speed I'm sometimes getting negative deltas. That sometimes is also not very consistent. Sometimes nothing for a minute, and then I get hundreds of wrong results within seconds:
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -2
got a negative delta when I expect only positive ones: -2
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -2
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -1
got a negative delta when I expect only positive ones: -2
So at this point I don't really know what to do. Of the three examples;
- one simply dies
- one gives a
1and a-1interchangeably whichever way I turn the motor - and one gives inconsistent wrong results
So at this point I really have no idea how to proceed.
Does anybody have any tips on what I can do to get good results from my encoder. Any help would be really welcome! :-)
Your test indicating -1, 1, -1 points to a problem with inputs. That would be consistent with 1 pin value not changing, so double-check the PIN definitions at the top of the code. Looks from your code above like you are using pins 7 and 9 rather than the default config of 7 and 8.
You can get much better diagnostics from rotary_test.py as explained here: http://guy.carpenter.id.au/gaugette/2013/06/04/improved-test-code-for-rotary-encoders/
Your subsequent results suggest your inputs are fine, but you are exceeding the sampling limit of your setup. If you have an encoder with 24 pulses per revolution, you need to be able to sample at (24 * 4 * max RPS) samples per second to avoid missing a step. Using interrupts might help, but you should probably do a sanity check to compare the actual sampling rate with your max sampling requirements.
One more consideration; if your motor only turns one direction, you can modify the sampling logic to reliably accommodate 1 or 2 missed states by removing all of the delta adjustments. This reduces your minimum sampling frequency by a factor of 3, but leaves you with no easy way to detect when you exceed your max sampling rate.
Hi @guyc - Thanks for the response. Today I got myself back to this project and I found that indeed I was using the wrong pin numbers. I first tried this example from the readme, which uses pin numbers 7 and 9 for the rotary encoder.
So after fixing the pin numbers I tried the code in the rotary_worker_test.py file again. It now indeed gives better results. When I manually turn the motor I get seemingly correct results. But if I turn the motor on I get the same wrong results as I had with the script I posted above. So sometimes I get correct results for a while, and other times I get hundreds of wrong results within seconds, all that while the motor is continuously running in one direction.
You also suggest that I might be "exceeding the sampling limit of your setup". Do you mean that the motor is turning too fast for the rotary encoder (so I need a better rotary encoder) or do you mean that my raspberry pi can't handle the rotations being sent in by the rotary encoder (in which case I would have no idea what to do)?
Finally; unfortunately the motor needs to turn both ways, so your last tip is (although well appreciated) unfortunately not useful to me. Your thoughts on the limits of my setup (or other tips) are very welcome thought! :-)
The worker example is polling to get events, and so it has a definite upper limit for speed. In fact with polling there is a tradeoff of resource waste vs upper limit. You'll notice https://github.com/guyc/py-gaugette/blob/master/samples/rotary_worker_test.py#L25 there is a delay here which is intended to prevent the worker from consuming all available CPU, but sets an upper sampling limit.
With a sleep of 0.05 seconds the max sampling rate will be less than 20 samples per second. That means it can detect at most 20 state changes per second, and a full quadrature cycle is 4 state changes, so it will be capped at 5 major steps per second. That's actually a really low limit!
So you have some options:
- decrease or remove that sleep. If you know what your speed range is, you can use that to compute the correct delay. Note that this will consume more cpu checking the state more frequently.
- use interrupts. There is an interrupt example in the code now. Some people have reported immediate crashes when using interrupts. It seems to be a system setup issue but I haven't been able to reproduce it yet.
@guyc - Thanks for your response.
In the end I bought another rotary encoder and put it on the exit shaft of the motor (which is behind a gearbox). That way the max rotation speed is a lot lower and I can get it working with the polling example. I noticed that the polling example does consume 100% CPU of one thread (or core, I'm not sure which one). That consumes more power and leaves less computational resources for my other tasks, but it works for now and I'm only in a prototyping stage so for now it's fine.
Thanks a million for creating this repo! You made my life just that little bit better :-)