Encoder velocities and index signal reading

17 Sep 2019 10:27 #145382 by blazini36
I've been messing around with some hardware that's running Machinekit's mksocfpga port of hm2. The MK channels aren't the greatest place to discuss these things, so I figured I'd ask here.

For the most part the stuff is working pretty well, minus some Smart Serial issues, one other thing I'm having a minor issue with is encoder velocities and catching an index signal. Being that it's difficult to actually "see" the encoder's index trigger, I've set it up in hal to use the updown component to count every index that's caught. The problem is that hal is missing the index signal at anything above say 100rpms. Obviously the faster the motor spinning the encoder is running the shorter the index signal will be. I'm trying to figure out if there is any hm2 hack I can do to get this a bit better. I realize the FPGA itself probably has little issue catching the signal but the processor running at a 1ms servo thread just isn't catching it reliably. 1ms on the servo thread is probably even pushing it a bit (900mhz ARM cores). It seems that the only use in hal for the index pin is as if it were a normal GPIO input. Trying to figure out if there's a way to manipulate the index (or any gpio for that matter) to make it a more reliable read.

I'm also seeing velocities that are not as stable as I like. I believe counts and position are done in the FPGA firmware but I'm not sure about the velocity feedback. Looking at the man pages different versions of the encoder module's seem to support a dpll timer select pin that is not present on this firmware. The hm2_dpll timers are there, but the hm2_encoder.timer-number pin is not. I get the idea from the man that this helps to synchronize reads between the FGPA and hal, so I'm thinking it would be worth trying if it were available.

Hardware I wouldn't think to be an issue, I'm using a custom daughtercard that breaks out all stepgen and encoders into differential pairs. The encoder counts and position as seen by the FPGA are spot on at 4000rpms on a 1000ppr encoder in quadrature. Just for messing around I have that encoder coupled to a stepper motor that is able to run to at least 4000rpms (didn't even think that was possible). I need some better couplers before I try to run it any faster, point is just that the hardware/FPGA is capable, there's just something with hm2 reads I'd like to somehow improve.

Please Log in or Create an account to join the conversation.

17 Sep 2019 13:51 - 17 Sep 2019 13:52 #145391 by PCW
In LinuxCNC with hardware encoder counters, the index signal is not used directly
(as you found its not of much use since higher resolution encoders or medium speeds
will make the index pulse too short to detect reliably by software sampling)

The way index is used is that the hardware detects index and if the index function is enabled,
the hardware either clears the current encoder count or latches the current encoder count and
sets a "index detected" flag. This flag is sampled by the driver at the servo thread rate and if set,
the driver resets its position count hal pin and clears the index enable flag, thus the index enable
flag provides a 2 way handshake from hal to the index support software in the driver and to
the actual index hardware.

If you want to test the index function, you set the index enable flag, it should go low when index is
detected, This works up to the full hardware limit of the encoder counter (down to 100 ns or less on HostMot2)
Last edit: 17 Sep 2019 13:52 by PCW.

Please Log in or Create an account to join the conversation.

18 Sep 2019 00:31 #145472 by blazini36
I figured it was going to be something like that but I'm still not quite sure how I would be able to put the index to use. The man page I look ( http://linuxcnc.org/docs/html/man/man9/hostmot2.9.html ) at seems to be out of date or something because it's explanation of the index-enable pin is different then what I'm seeing.

The encoder counts/position don't reset, but the index-enable pin itself does. That makes the index pin useful if index-enable is set immediately before the index is triggered. Is there some possible way for the index-enable pin to remain true or something else I might be missing? Barring that the only thing I can think of is to get fancy in HAL and use an "OR" component or something to flip the index-enable pin back to true with the OR input being something based on the index event itself. Right now I'm just messing with this on my little test rig but I do have a machine that I was actually going to do something like this with. It currently uses an external sensor that is counted and on a specified number it triggers an event and the encoder.reset pin to start counting from 0 again. I was planning on configuring it to use the encoder's own index at some point.

Any thoughts on the velocity instabillity? I think my other machines are quite a bit more stable with velocity than this thing, but I'm not sure where velocity is calculated in HM2. I would expect that if it were done in the FPGA directly it would be quite a bit more stable, so I assume it's done on the PC side based on reads of counts? Is there any way to get this better on a slower CPU? I think it's quite a bit more stable at least at higher speeds on my x86/Mesa machines. hard to say but I think at 200RPMs it varies about 15% and 3000rpms it varies about 2%. I don't know that I could ever close a loop with it moving that much. The stepgen.velocity-fb is far more stable, but the encoder position and stepgen position match very closely once moves are completed so I know I'm not losing steps and the FPGA is catching pretty much all of the encoder counts.

Please Log in or Create an account to join the conversation.

18 Sep 2019 03:31 - 18 Sep 2019 03:31 #145481 by PCW
If your encoder position does not reset on hitting index, you have a driver, hardware, or firmware problem (this is how hm2 index enable/index has always worked, well for at least 12 years or so)

Velocity is calculated in a delta-count/delta-time basis where delta-time is the time between the first and last of delta-counts (not the sample time) so it should be fairly independent of servo thread jitter, however if the timestamp clock calculations are not correct because the FPGA clock is not reported right or something like that, this would cause errors in the velocity calculations
Last edit: 18 Sep 2019 03:31 by PCW.

Please Log in or Create an account to join the conversation.

18 Sep 2019 09:50 #145495 by blazini36
Actually I misspoke, the hostmot2 index-enable pin does reset counts/position in this source. Not sure what I was looking at.

Lol I don't suppose Mesa takes feature requests? An index-count pin in firmware would probably alleviate some headaches when I go to implement it on that machine.

Please Log in or Create an account to join the conversation.

18 Sep 2019 13:58 - 18 Sep 2019 14:43 #145507 by PCW
No real need to do it in firmware since is occurs at such a low frequency,
(a 1 KHz servo thread would work up to almost 60000 RPM)
it is something that could be done fairly easily in the driver.

Why do you need a count of index events? (also note that this number may be rather
random unless qualified by having a full turn of encoder counts between index events)

Edit, if you can live with the position being reset every turn you can do this:

loadrt not
loadrt tristate-bit
addf not.0 servo-thread
addf tristate-bit.0 servo-thread
setp tristate-bit.0.in 1
net toggle hm2_somecard.0.encoder.00.index-enable tristate-bit.0.out not.0.in
net reset not.0.out tristate-bit.0.enable

This will force index_enable high if it goes low so you can now count the reset
or toggle hal signals with the software encoder counter set into the counter-mode
They will both go low for one servo period at every index
Last edit: 18 Sep 2019 14:43 by PCW.

Please Log in or Create an account to join the conversation.

19 Sep 2019 05:18 #145551 by blazini36
Done inside the driver vs hal itself you mean right? I didn't realize the driver would catch something that hal would not.

I realize without some complicated way of detecting the direction of the encoder previous to the index it would just be adding to the count. If the encoder were always spinning in one direction would it still be random? I assume you mean that if the encoder was reversed for a short amount and passed the index it would just add another count, which for the use I'm considering wouldn't really matter since it's not really ever going to happen and if it did it wouldn't hurt anything.

There are several uses for that feature mine in particular is sort of hard to explain.

First use is just plain visibility. If maybe you assumed to have an encoder with a failing index or just wanted to test it, you either have to break out a scope, or start setting pins in hal to have HM2 catch it for you, Whereas just rotating your spindle or whatever by hand and watching for a count increase is much easier.

Second is if there is a reliable incremental change in an integer pin that change exists until the next change. It negates the need to set a condition to catch the short change of a bit pin, If the single increment of that pin matters you still have longer to realize it in hal and if the change would ever occur fast enough that multiple increments occurred prior to the next servo thread sample it is still easily picked up in hal. I can't think of the hal component at the moment but I'm pretty sure there is a standard one that just produces an output on the change of an integer input.

My case is probably hard to explain but I work with printing presses. A printing cylinder has a "repeat" that is the circumference of the cylinder, say 28" and that is the length of the image it prints on a web of paper. Say you wanted to synchronize something to the repeat of the printed image you would have to measure a position from a reference point. Since there are possibly interchangeable cylinders you wouldn't want to try to install an encoder on a cylinder and deal with that, instead you may install the encoder on the driveshaft that has a definitive circumference of say 8", For what I'm doing I don't want to trigger an event on every single repeat, every other is fine, so if the encoder is driven 1:1 with the driveshaft, I can count 7 index pulses, feed that into a comparator to reset the encoder position, the position runs into another comparator that will trigger the event at a specific position, and reset position and the index counter and start it all over. Doing it this way means I only have to count a position from 0-28", whereas otherwise I would have to count from 0 to the multiple of the repeat, plus the position. So if I wanted to trigger the event @ 14" on the second repeat I would have to count to 42" from the encoders original reset. That position number isn't directly relate-able to anything and it's easier for me to use the previous logic in hal to make everything happen.

It sounds convoluted I'm sure but I do a very similar thing right now using a contrast sensor that counts a printed mark that is repeated 1-2 times per cylinder repeat. I use the updown component to count the marks and feed those into the first comparator in place of the index. This is surprisingly, very accurate but it works because the pulse length from the contrast sensor is apparently much longer than an index pulse on the encoder I have on my desk, even at much higher speed. I can catch that mark all day with a 1ms servo period on an eth Mesa card.

If I could catch index pulses in hal It wouldn't matter, I'd just count them with the updown component. Since I can't I'd have to I guess flip the enable pin constantly and do something to count the false state of the index enable. I think the motion component has some built in logic to do whatever it has to with the index-enable pin for say a ridged tapping operation, or maybe something with an axis but that probably won't help me. If the driver can count the index for me and be reset-able. Wouldn't be able to handle an encoder-position reset every turn unless I added logic to disable it after the specified number of counts so it could measure a position several turns long.

This is my current hal logic file, you can see how convoluted it already is.
#HAL logic for handling camera triggers

loadrt updown         names=updown.index
loadrt conv_s32_float names=conv_s32_float.index
loadrt comp           names=comp.index,comp.encoder
loadrt edge           names=edge.main,edge.ind_comp,edge.UI_LED
loadrt and2           names=and2.trigger
loadrt mult2          count=1
loadrt or2            names=or2.encoder
loadrt mux2           names=mux2.encoder-hold
loadrt not            names=not.held

addf updown.index                servo-thread
addf and2.trigger                servo-thread
addf or2.encoder                 servo-thread
addf edge.main                   servo-thread
addf edge.ind_comp               servo-thread
addf edge.UI_LED                 servo-thread
addf mux2.encoder-hold           servo-thread
addf not.held                    servo-thread
addf conv_s32_float.index        servo-thread
addf comp.index                  servo-thread
addf comp.encoder                servo-thread
addf mult2.0                     servo-thread

####--Setup UPDOWN Signals to count index reference--######

####------select "idx_count" or "cue_count"---#####
#net idx_count                       => updown.index.countup       
net cue_count                        => updown.index.countup  
net updn_counts_s32                  <= updown.index.count
net updn_counts_s32                  => conv_s32_float.index.in
net updn_counts_float                <= conv_s32_float.index.out
net trigger_main                     => updown.index.reset

####--Setup COMP Signals, invert and edge to reset UPDOWN and trigger event1--######

net offset_out                       <= offset.index.out
net offset_out                       => comp.index.in0 
net updn_counts_float                => comp.index.in1
net comparator_index_out             <= comp.index.out
net trigger_init                     => edge.main.in
net trigger_main                     <= edge.main.out
setp offset.index.in [Machine]countspertrigger
setp offset.index.offset 0
setp edge.main.out-width-ns 1000000 
setp edge.main.in-edge false

####--Setup Vscale with COMP for encoder pos for trigger event2-----######

net ui_vscale_value                 => comp.encoder.in0
net enc_pos                         <= hm2_7i96.0.encoder.00.position              
net enc_pos_held                    => comp.encoder.in1
net comparator_encoder_out          <= comp.encoder.out
net comparator_index_out            => edge.ind_comp.in
net edge_ind_comp                   <= edge.ind_comp.out
net edge_ind_comp                   => or2.encoder.in0
net trigger_main                    => or2.encoder.in1
net comp_enc_resets                 <= or2.encoder.out
net comp_enc_resets                 => hm2_7i96.0.encoder.00.reset

setp edge.ind_comp.out-width-ns 1000000 
setp edge.ind_comp.in-edge false

####--Setup AND2, When both comparators true, trigger camera--######

net comparator_index_out             => and2.trigger.in0
net comparator_encoder_out           => and2.trigger.in1 
net trigger_init                     <= and2.trigger.out
net comparator_index_out             => not.held.in
net not_held                         <= not.held.out
net not_held                         => mux2.encoder-hold.sel 
net enc_pos                          => mux2.encoder-hold.in0
net enc_pos_held                     <= mux2.encoder-hold.out
#dirty-trick to make mux2 act like sample-hold
net enc_pos_held                     <= mux2.encoder-hold.out
net mux2_hold_trick                  => mux2.encoder-hold.in1

###-------Web Speed Scale velocity correction/UI signals---####
#setp mult2.0.in1 [Machine]webspeedcorrectionfactor
#net ui_speed_input           <= hm2_7i96.0.encoder.00.velocity 
#net ui_speed_input           => mult2.0.in0
#net ui_speed                 <= mult2.0.out

net cue_count                => edge.UI_LED.in
net ui_sync_LED              <= edge.UI_LED.out

setp hm2_7i96.0.encoder.00.counter-mode [WEB_1]COUNTER_MODE
setp hm2_7i96.0.encoder.00.scale [Machine]encoderpulseperrevolution

setp edge.UI_LED.out-width-ns 1000000 
setp edge.UI_LED.in-edge false

Please Log in or Create an account to join the conversation.

19 Sep 2019 16:12 #145594 by andypugh
I have a feeling that what you want to do might be better done with a simple HAL component that simply takes the modulo of the current float position.

Please Log in or Create an account to join the conversation.

19 Sep 2019 16:41 #145601 by blazini36

andypugh wrote: I have a feeling that what you want to do might be better done with a simple HAL component that simply takes the modulo of the current float position.

I think I see what you are getting at there. The modulus would be the number of repeats and the remainder would have to be specified as the intended position on the intended repeat. That might be something I’ll have to think about.

Problem is I don’t do any sort of programming languages so I’ll have to run that by my buddy see if he can help out.

I do actually have a component for doing this but it’s similar to what is done in the Hal file. It worked but I abandoned it when I had a new need to generate 2 separate events from the same component and it got too complicated for me to explain to someone else. Unfortunately that component wouldn’t help me in the situation that I used it on an encoder index and can’t reliably see it.

That’s good advice tho, i do still think a method of counting index pulses in the driver/firmware would be a good idea for the other reasons I listed.

Please Log in or Create an account to join the conversation.

19 Sep 2019 16:58 - 19 Sep 2019 17:01 #145602 by andypugh
It could be very simple.
component modulus;
pin in signed counts;
pin in signed divisor;
pin out signed result;
pin out signed remainder;
author "andypugh";
license "GPL 2+";
function _;


remainder = counts % divisor;
result = counts / divisor;

This is a simple version. Ideally you would want to keep a 64-bit buffer of counts in order to prevent rollover of the 32 bit counts value.

(64 bits is enough to hold counts for any common encoder for millions of years)
Last edit: 19 Sep 2019 17:01 by andypugh.

Please Log in or Create an account to join the conversation.

Time to create page: 0.103 seconds
Powered by Kunena Forum