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!
Incremental Encoder
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
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
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
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.