Incremental Encoder

Topics about the Software of Revolution Pi
Post Reply
Adineh
Posts: 3
Joined: 22 Jan 2024, 06:12

Incremental Encoder

Post by Adineh »

Help Needed with Encoder Pulse Counting on Revolution Pi Similar to pigpio rasperrypi

Hi everyone,

I'm working on a project involving reading encoder pulses using revpimodeio2 Revolution Pi. For the first attempt, I've written a code snippet to increment the count on both rising and falling edges independently of the direction. However, I have a few questions regarding some specific parameters.

I would appreciate it if someone could shed light on the following parameters and their units:

1. `.core.iocycle`: What does this parameter represent, and what are the units (microseconds)?

2. `.io.InputDebounce`: How does this parameter relate to debouncing, and what units are used for the debounce time (microseconds)?

3. `.core.frequency`: What is the significance of this parameter, and what are its units (Hz)?

I've set up an input debounce of 25 us in PiCtory, but when checking the value using the code, it shows `inputdebounce = 1` (attached image).
My encoder has a high resolution of 2024 PPR, but I get only around 50 counts for one revolution, which is far less than expected (2048). I suspect I might have made a mistake, and I've read that Revolution Pi might not be suitable for such high frequencies.

Any assistance in understanding these parameters and resolving the issue would be greatly appreciated. Also, if anyone from the Revolution Pi community could share a high-level Python code for reading encoders, that would be fantastic.

Thank you in advance!
Attachments
valueEditor.PNG
output.PNG
output.PNG (8.04 KiB) Viewed 3440 times
code.PNG
User avatar
stefanh
Posts: 33
Joined: 05 Apr 2023, 10:53

Re: Incremental Encoder

Post by stefanh »

Hello Adineh,

you can find documentation for REVPIMODIO2 objects here: .core https://revpimodio.org/en/doc2/core/ , .device https://revpimodio.org/en/doc2/device/ and .io https://revpimodio.org/en/doc2/io/

.core.iocycle  Returns the cycletime of the PiBridge, the PiBridge is a backplane bus system for the Revolution Pi platform, enabling the coupling of up to 10 modules plus a RevPi Core. It uses a 20-pin connector for module coupling, supports high-speed RS485 and Ethernet communication, and facilitates automatic module and position detection.

.io.InputDebounce This is where a time for the debounce filter is specified for all inputs. This time indicates when a changing state at the input is considered stable. Possible values are Off, 25 µs, 750 µs and 3 ms. More: https://revolutionpi.com/tutorials/over ... io-modules

.core.frequency  Returns the CPU clockfrequency of the RevPi

What are your exact time requirements to realize your application?

Regards
Stefan

Support | KUNBUS
Adineh
Posts: 3
Joined: 22 Jan 2024, 06:12

Re: Incremental Encoder

Post by Adineh »

Hi Stefan,
Thanks for your response!
I have convinced my manager to use Rev Pi instead of Compact Rio NI for our industrial application, but it seems it is not fast enough!
The application is a controller of an electrical-driven drum with a maximum velocity of 3000 RPM. We use the incremental encoder(HTL) with 2048 PPR. I developed the code in Python by using the Pigpio library and tested it, which worked properly (I will share the encoder part of my code below). However, to achieve a robust IO interface, we bought DIO, AIO, and RevPi Connect 4! We scheduled to test the new setup (RevPi, not Rassperypi) ASAP with a deadline of 8/2/2024.
A couple of more questions:
1- Do you think it is possible not to miss the encoder count with 2048 PPR when the drum rotates around 100 RPM (the encoder is installed after the gearbox: 3000 Drum RPM=100 Encoder RPM)?
2-How can I pass argument to .reg_event(…, as_thread=True)
3- why do I get 1 for input debounce?
4-Can we implement interrupt like pigpio?

class IncrementalEncoder:
def __init__(self, layer_from_plc, wrap_from_plc):

self.WRAPS_PER_LAYER = WRAPS_PER_LAYER
self.COUNT_PER_REV = ENCODER_INC_COUNT_PER_REV
self.DRUM_DIAMETER = DRUM_DIAMETER
self.WIRE_DIAMETER = WIRE_DIAMETER
self.DIAM_INC_PER_LAYER = DIAM_INC_PER_LAYER
self.GEAR_RATIO = GEAR_RATIO
self.layer = layer_from_plc
self.wrap = wrap_from_plc
self.current_drum_diameter = None
self.rope_out = None
self.turns = None
self.error = False

self.cbB = None
self.cbA = None
self.count = deque(maxlen=2)
self.count.append(self.encoder_initialisation(layer_from_plc, wrap_from_plc))
self.period = deque(maxlen=2)
self.period.append(time.time())
self.pi = pigpio.pi()
self.gpioA = ENCODER_A
self.gpioB = ENCODER_B

self.levA = 0
self.levB = 0

self.lastGpio = None

# self.pi.set_mode(self.gpioA, pigpio.INPUT)
# self.pi.set_mode(self.gpioB, pigpio.INPUT)
#
# self.pi.set_pull_up_down(self.gpioA, pigpio.PUD_UP)
# self.pi.set_pull_up_down(self.gpioB, pigpio.PUD_UP)

def start(self):
self.cbA = self.pi.callback(self.gpioA, pigpio.EITHER_EDGE, self._pulse)
self.cbB = self.pi.callback(self.gpioB, pigpio.EITHER_EDGE, self._pulse)

def _pulse(self, gpio, level, tick):
if gpio == self.gpioA:
self.levA = level
else:
self.levB = level

if gpio != self.lastGpio: # debounce
self.lastGpio = gpio

if gpio == self.gpioA and level == 1:
if self.levB == 1:
self.callback(1)
elif gpio == self.gpioB and level == 1:
if self.levA == 1:
self.callback(-1)

def stop(self):
self.cbA.cancel()
self.cbB.cancel()
self.pi.stop()

def callback(self, way):
"""
:param way: encoder tick
:return: update encoder count

todo: It is required to clockwise and anti clockwise to be checked!
way sign convention: + ACW, - CW : Drum anti clock wise means pay-in so warp increase
Anti clock wise and clock wise from motor side not encoders side
"""
self.count.append(self.count[-1] + way)
self.period.append(time.time())

def get_angular_velocity(self):
# todo this function is not reliable and MUST not be used
return (self.count[1] - self.count[0]) / (self.period[1] - self.period[0])

def encoder_initialisation(self, layer_from_plc, wrap_from_plc):
encoder_turns = (layer_from_plc - 1) * self.WRAPS_PER_LAYER + wrap_from_plc
encoder_count = encoder_turns * self.COUNT_PER_REV
return encoder_count

def get_live_drum_diameter(self, layer):
self.current_drum_diameter = self.DRUM_DIAMETER + self.WIRE_DIAMETER + (layer - 1) * self.DIAM_INC_PER_LAYER
return self.current_drum_diameter

def get_warp_layer(self):
encoder_turns = self.count[-1] / self.COUNT_PER_REV
layer = int(encoder_turns / self.WRAPS_PER_LAYER) + 1
wrap = (self.count[-1] - (layer - 1) * self.WRAPS_PER_LAYER * self.COUNT_PER_REV) / self.COUNT_PER_REV
return wrap, layer

def get_rope_out(self):
self.layer, self.wrap = self.get_warp_layer()
selected_layer = self.layer
self.rope_out = 0
while selected_layer >= 1:
diameter_selected_layer = self.get_live_drum_diameter(selected_layer)
if diameter_selected_layer == self.layer:
# Calculate the outermost rope length wrapped around drum
self.rope_out -= diameter_selected_layer * pi * self.wrap
else:
# Calculate the rope length wrapped around drum in one layer
self.rope_out -= diameter_selected_layer * pi * self.WRAPS_PER_LAYER
# go to the next layer
selected_layer = selected_layer - 1
return self.rope_out
Last edited by Adineh on 29 Jan 2024, 01:46, edited 1 time in total.
Post Reply