Homing open loop steppers on encoder index pulse
- CaliusOptimus
- Away
- New Member
-
Less
More
- Posts: 5
- Thank you received: 4
04 Dec 2025 03:45 - 04 Dec 2025 04:31 #339711
by CaliusOptimus
Homing open loop steppers on encoder index pulse was created by CaliusOptimus
Chatgpt and I have been working on this for quite a few hours now and I don't quite understand why this doesn't work with the out-of-the-box options... but I've learned a few things. If there is some easy way to fix this, please don't tell me. I don't want to know I've wasted all of this time.
JK, please do tell.
Here's what led me down this road:
1. Want precise homing.
2. Want to monitor for lost steps.
3. Full closed loop seems to be a no-go with the drive/motor/encoder combo I'm using. I may explore this more later.
Adding HOME_USE_INDEX = YES to the ini with stepgen it does a variety of very strange things. I can't give much detail on exactly what is happening because I've had some widely varying outcomes and it has been hard to track the exact cause and effect. It seems related to an issue with resetting the joint.x.motor-offset value at the wrong time. That's my best guess. I tried adjusting everything under the sun to get it working as expected and no luck.
SO. I think I have a solid solution. I'm typing it here to organize my thoughts before writing the hal and to share with anyone attempting the same thing.
The idea is to swap in the home switch input with the encoder's index-enable at the right moment. (When the index-enable is set high, it stays high until the next index pulse). For example with my X axis, the axis homes on the negative (left) side. The no-encoder homing procedure is to move left until switch trips, then move right until it releases and set 0 at that point. If I swap the home switch input over to the encoder's index-enable, see example below, after the first trip of the home switch the motor will continue to the right until the index pulse occurs and set 0 at that point.
From this: net min-home-x hm2_5i25.0.7i76.0.0.input-xx => joint.0.home-sw-in (normally low)
To this: net min-home-x hm2_5i25.0.encoder.00.index-enable => joint.0.home-sw-in (normally high)
The idea:
1. joint.0.home-sw-in needs to be connected to the home switch unless we're homing with the z pulse.
2. encoder.00.index-enable needs to be set (but not held) high at the moment the home switch goes high, and only while homing.
3. joint.0.home-sw-in must be swapped from the home switch over to encoder.00.index-enable.
4. after encoder.00.index-enable goes low, joint.0.home-sw-in must be swapped back to the home switch.
Logic layout (shorthand) tested and working:
home-switch > and2.in0
is-currently-homing > and2.in1
and2.out > toggle.0.in
toggle.out <=> encoder-index-enable
encoder-index-enable > or2.in0
home-switch > or2.in1
or2.out > joint.0.home-sw-in
This works although there are some issues when it comes to the feedback. Using encoder feedback (not with a closed loop PID) with joint.x.motor-pos-fb throughout the homing process doesn't work. Something about the timing causes a following error... if your following error is set reasonably tight anyway. SO. I found two options to solve this and they both have caveats.
Option 1: Use stepgen-pos-fb only while homing, and the encoder the rest of the time. Gives you following error protection unless the axis is currently homing. Caveat is that if you estop and the axis moves afterwards, more than your allowable following error, linuxcnc will pop the following error and it won't go away until you restart.
Option 2: Use stepgen-pos-fb unless the axis has been homed. If homed, use encoder feedback. Using this in conjunction with "VOLATILE_HOME = 1". This keeps linuxcnc from popping a following error after returning from estop, because the machine will be unhomed after an estop which puts it back into stepgen feedback. Caveat is that you won't have following error protection until the axis is homed. This also requires an extra hal component (tof) to deal with the timing issue that has plagued me with following errors.
Logic layout (shorthand) for swapping the feedback, both tested and working:
Option 1-
stepgen-pos-fb > mux2.in1
encoder-pos-fb > mux2.in0
mux2.out > joint.x.motor-pos-fb
joint.0.homing > mux2.sel
Option 2-
stepgen-pos-fb > mux2.in0
encoder-pos-fb > mux2.in1
mux2.out > joint.x.motor-pos-fb
joint.0.homed > tof.in
tof.out > mux2.sel
Here is the actual tested and working code:
INI
Hal
Custom Hal, Option 2 shown
For reference and context:
This particular machine has a single limit switch on either side of each axis. One side doubles as a home switch with "HOME_IGNORE_LIMITS = YES" in the ini. 3 axis router, axis GUI, LinuxCNC 2.9.6, Mesa 5i25, 7i76u on P3 and 7i89 on P2.
JK, please do tell.
Here's what led me down this road:
1. Want precise homing.
2. Want to monitor for lost steps.
3. Full closed loop seems to be a no-go with the drive/motor/encoder combo I'm using. I may explore this more later.
Adding HOME_USE_INDEX = YES to the ini with stepgen it does a variety of very strange things. I can't give much detail on exactly what is happening because I've had some widely varying outcomes and it has been hard to track the exact cause and effect. It seems related to an issue with resetting the joint.x.motor-offset value at the wrong time. That's my best guess. I tried adjusting everything under the sun to get it working as expected and no luck.
SO. I think I have a solid solution. I'm typing it here to organize my thoughts before writing the hal and to share with anyone attempting the same thing.
The idea is to swap in the home switch input with the encoder's index-enable at the right moment. (When the index-enable is set high, it stays high until the next index pulse). For example with my X axis, the axis homes on the negative (left) side. The no-encoder homing procedure is to move left until switch trips, then move right until it releases and set 0 at that point. If I swap the home switch input over to the encoder's index-enable, see example below, after the first trip of the home switch the motor will continue to the right until the index pulse occurs and set 0 at that point.
From this: net min-home-x hm2_5i25.0.7i76.0.0.input-xx => joint.0.home-sw-in (normally low)
To this: net min-home-x hm2_5i25.0.encoder.00.index-enable => joint.0.home-sw-in (normally high)
The idea:
1. joint.0.home-sw-in needs to be connected to the home switch unless we're homing with the z pulse.
2. encoder.00.index-enable needs to be set (but not held) high at the moment the home switch goes high, and only while homing.
3. joint.0.home-sw-in must be swapped from the home switch over to encoder.00.index-enable.
4. after encoder.00.index-enable goes low, joint.0.home-sw-in must be swapped back to the home switch.
Logic layout (shorthand) tested and working:
home-switch > and2.in0
is-currently-homing > and2.in1
and2.out > toggle.0.in
toggle.out <=> encoder-index-enable
encoder-index-enable > or2.in0
home-switch > or2.in1
or2.out > joint.0.home-sw-in
This works although there are some issues when it comes to the feedback. Using encoder feedback (not with a closed loop PID) with joint.x.motor-pos-fb throughout the homing process doesn't work. Something about the timing causes a following error... if your following error is set reasonably tight anyway. SO. I found two options to solve this and they both have caveats.
Option 1: Use stepgen-pos-fb only while homing, and the encoder the rest of the time. Gives you following error protection unless the axis is currently homing. Caveat is that if you estop and the axis moves afterwards, more than your allowable following error, linuxcnc will pop the following error and it won't go away until you restart.
Option 2: Use stepgen-pos-fb unless the axis has been homed. If homed, use encoder feedback. Using this in conjunction with "VOLATILE_HOME = 1". This keeps linuxcnc from popping a following error after returning from estop, because the machine will be unhomed after an estop which puts it back into stepgen feedback. Caveat is that you won't have following error protection until the axis is homed. This also requires an extra hal component (tof) to deal with the timing issue that has plagued me with following errors.
Logic layout (shorthand) for swapping the feedback, both tested and working:
Option 1-
stepgen-pos-fb > mux2.in1
encoder-pos-fb > mux2.in0
mux2.out > joint.x.motor-pos-fb
joint.0.homing > mux2.sel
Option 2-
stepgen-pos-fb > mux2.in0
encoder-pos-fb > mux2.in1
mux2.out > joint.x.motor-pos-fb
joint.0.homed > tof.in
tof.out > mux2.sel
Here is the actual tested and working code:
INI
Warning: Spoiler!
[AXIS_X]
MAX_VELOCITY = 166.66666666666666
MAX_ACCELERATION = 900.0
MIN_LIMIT = -0.01
MAX_LIMIT = 405.5
[JOINT_0]
VOLATILE_HOME = 1
TYPE = LINEAR
HOME = 0.0
FERROR = 1.5
MIN_FERROR = 1.5
MAX_VELOCITY = 166.66666666666666
MAX_ACCELERATION = 900.0
# The values below should be 25% larger than MAX_VELOCITY and MAX_ACCELERATION
# If using BACKLASH compensation STEPGEN_MAXACCEL should be 100% larger.
STEPGEN_MAXVEL = 208.33
STEPGEN_MAXACCEL = 1125.00
P = 2000.0
I = 0.0
D = 0.0
FF0 = 0.0
FF1 = 1.0
FF2 = 0.0
BIAS = 0.0
DEADBAND = 0.0
MAX_OUTPUT = 0.0
ENCODER_SCALE = -1600.0
# these are in nanoseconds
DIRSETUP = 10000
DIRHOLD = 10000
STEPLEN = 5000
STEPSPACE = 5000
STEP_SCALE = -200.0
MIN_LIMIT = -0.01
MAX_LIMIT = 405.5
HOME_OFFSET = 0.000000
HOME_SEARCH_VEL = -8.333333
HOME_LATCH_VEL = 0.5
HOME_FINAL_VEL = -1.0
HOME_IGNORE_LIMITS = YES
HOME_SEQUENCE = 1
MAX_VELOCITY = 166.66666666666666
MAX_ACCELERATION = 900.0
MIN_LIMIT = -0.01
MAX_LIMIT = 405.5
[JOINT_0]
VOLATILE_HOME = 1
TYPE = LINEAR
HOME = 0.0
FERROR = 1.5
MIN_FERROR = 1.5
MAX_VELOCITY = 166.66666666666666
MAX_ACCELERATION = 900.0
# The values below should be 25% larger than MAX_VELOCITY and MAX_ACCELERATION
# If using BACKLASH compensation STEPGEN_MAXACCEL should be 100% larger.
STEPGEN_MAXVEL = 208.33
STEPGEN_MAXACCEL = 1125.00
P = 2000.0
I = 0.0
D = 0.0
FF0 = 0.0
FF1 = 1.0
FF2 = 0.0
BIAS = 0.0
DEADBAND = 0.0
MAX_OUTPUT = 0.0
ENCODER_SCALE = -1600.0
# these are in nanoseconds
DIRSETUP = 10000
DIRHOLD = 10000
STEPLEN = 5000
STEPSPACE = 5000
STEP_SCALE = -200.0
MIN_LIMIT = -0.01
MAX_LIMIT = 405.5
HOME_OFFSET = 0.000000
HOME_SEARCH_VEL = -8.333333
HOME_LATCH_VEL = 0.5
HOME_FINAL_VEL = -1.0
HOME_IGNORE_LIMITS = YES
HOME_SEQUENCE = 1
Hal
Warning: Spoiler!
#*******************
# AXIS X JOINT 0
#*******************
setp pid.x.Pgain [JOINT_0]P
setp pid.x.Igain [JOINT_0]I
setp pid.x.Dgain [JOINT_0]D
setp pid.x.bias [JOINT_0]BIAS
setp pid.x.FF0 [JOINT_0]FF0
setp pid.x.FF1 [JOINT_0]FF1
setp pid.x.FF2 [JOINT_0]FF2
setp pid.x.deadband [JOINT_0]DEADBAND
setp pid.x.maxoutput [JOINT_0]MAX_OUTPUT
setp pid.x.error-previous-target true
# This setting is to limit bogus stepgen
# velocity corrections caused by position
# feedback sample time jitter.
setp pid.x.maxerror 0.012700
net x-index-enable => pid.x.index-enable
net x-enable => pid.x.enable
net x-pos-cmd => pid.x.command
net x-stepgen-pos-fb => pid.x.feedback
#net x-encoder-pos-fb => pid.x.feedback
net x-output <= pid.x.output
# Step Gen signals/setup
setp hm2_5i25.0.stepgen.00.dirsetup [JOINT_0]DIRSETUP
setp hm2_5i25.0.stepgen.00.dirhold [JOINT_0]DIRHOLD
setp hm2_5i25.0.stepgen.00.steplen [JOINT_0]STEPLEN
setp hm2_5i25.0.stepgen.00.stepspace [JOINT_0]STEPSPACE
setp hm2_5i25.0.stepgen.00.position-scale [JOINT_0]STEP_SCALE
setp hm2_5i25.0.stepgen.00.step_type 0
setp hm2_5i25.0.stepgen.00.control-type 1
setp hm2_5i25.0.stepgen.00.maxaccel [JOINT_0]STEPGEN_MAXACCEL
setp hm2_5i25.0.stepgen.00.maxvel [JOINT_0]STEPGEN_MAXVEL
# ---closedloop stepper signals---
net x-pos-cmd <= joint.0.motor-pos-cmd
net x-vel-cmd <= joint.0.vel-cmd
net x-output => hm2_5i25.0.stepgen.00.velocity-cmd
net x-stepgen-pos-fb <= hm2_5i25.0.stepgen.00.position-fb
net x-enable <= joint.0.amp-enable-out
net x-enable => hm2_5i25.0.stepgen.00.enable
#feedback source
#net x-encoder-pos-fb => joint.0.motor-pos-fb
#net x-stepgen-pos-fb => pid.x.feedback
#net x-stepgen-pos-fb => joint.0.motor-pos-fb pid.x.feedback
# ---Encoder feedback signals/setup---
setp hm2_5i25.0.encoder.00.counter-mode 0
setp hm2_5i25.0.encoder.00.filter 1
setp hm2_5i25.0.encoder.00.index-invert 1
setp hm2_5i25.0.encoder.00.index-mask 0
setp hm2_5i25.0.encoder.00.index-mask-invert 0
setp hm2_5i25.0.encoder.00.scale [JOINT_0]ENCODER_SCALE
net x-encoder-pos-fb <= hm2_5i25.0.encoder.00.position
net x-vel-fb <= hm2_5i25.0.encoder.00.velocity
#net x-index-enable joint.0.index-enable <=> hm2_5i25.0.encoder.00.index-enable
# ---setup home / limit switch signals---
#net min-home-x => joint.0.home-sw-in
net min-home-x => joint.0.neg-lim-sw-in
net max-x => joint.0.pos-lim-sw-in
# AXIS X JOINT 0
#*******************
setp pid.x.Pgain [JOINT_0]P
setp pid.x.Igain [JOINT_0]I
setp pid.x.Dgain [JOINT_0]D
setp pid.x.bias [JOINT_0]BIAS
setp pid.x.FF0 [JOINT_0]FF0
setp pid.x.FF1 [JOINT_0]FF1
setp pid.x.FF2 [JOINT_0]FF2
setp pid.x.deadband [JOINT_0]DEADBAND
setp pid.x.maxoutput [JOINT_0]MAX_OUTPUT
setp pid.x.error-previous-target true
# This setting is to limit bogus stepgen
# velocity corrections caused by position
# feedback sample time jitter.
setp pid.x.maxerror 0.012700
net x-index-enable => pid.x.index-enable
net x-enable => pid.x.enable
net x-pos-cmd => pid.x.command
net x-stepgen-pos-fb => pid.x.feedback
#net x-encoder-pos-fb => pid.x.feedback
net x-output <= pid.x.output
# Step Gen signals/setup
setp hm2_5i25.0.stepgen.00.dirsetup [JOINT_0]DIRSETUP
setp hm2_5i25.0.stepgen.00.dirhold [JOINT_0]DIRHOLD
setp hm2_5i25.0.stepgen.00.steplen [JOINT_0]STEPLEN
setp hm2_5i25.0.stepgen.00.stepspace [JOINT_0]STEPSPACE
setp hm2_5i25.0.stepgen.00.position-scale [JOINT_0]STEP_SCALE
setp hm2_5i25.0.stepgen.00.step_type 0
setp hm2_5i25.0.stepgen.00.control-type 1
setp hm2_5i25.0.stepgen.00.maxaccel [JOINT_0]STEPGEN_MAXACCEL
setp hm2_5i25.0.stepgen.00.maxvel [JOINT_0]STEPGEN_MAXVEL
# ---closedloop stepper signals---
net x-pos-cmd <= joint.0.motor-pos-cmd
net x-vel-cmd <= joint.0.vel-cmd
net x-output => hm2_5i25.0.stepgen.00.velocity-cmd
net x-stepgen-pos-fb <= hm2_5i25.0.stepgen.00.position-fb
net x-enable <= joint.0.amp-enable-out
net x-enable => hm2_5i25.0.stepgen.00.enable
#feedback source
#net x-encoder-pos-fb => joint.0.motor-pos-fb
#net x-stepgen-pos-fb => pid.x.feedback
#net x-stepgen-pos-fb => joint.0.motor-pos-fb pid.x.feedback
# ---Encoder feedback signals/setup---
setp hm2_5i25.0.encoder.00.counter-mode 0
setp hm2_5i25.0.encoder.00.filter 1
setp hm2_5i25.0.encoder.00.index-invert 1
setp hm2_5i25.0.encoder.00.index-mask 0
setp hm2_5i25.0.encoder.00.index-mask-invert 0
setp hm2_5i25.0.encoder.00.scale [JOINT_0]ENCODER_SCALE
net x-encoder-pos-fb <= hm2_5i25.0.encoder.00.position
net x-vel-fb <= hm2_5i25.0.encoder.00.velocity
#net x-index-enable joint.0.index-enable <=> hm2_5i25.0.encoder.00.index-enable
# ---setup home / limit switch signals---
#net min-home-x => joint.0.home-sw-in
net min-home-x => joint.0.neg-lim-sw-in
net max-x => joint.0.pos-lim-sw-in
Custom Hal, Option 2 shown
Warning: Spoiler!
loadrt and2 names="x-and2homesw"
loadrt toggle names="x-homeswtoggle"
loadrt or2 names="x-or2homesw"
loadrt mux2 names="x-fb-selector"
loadrt tof names="x-mux-tof"
addf x-and2homesw servo-thread
addf x-homeswtoggle servo-thread
addf x-or2homesw servo-thread
addf x-fb-selector servo-thread
addf x-mux-tof servo-thread
setp x-mux-tof.pt 0.1
#homing x, swapping limit sw for index pulse
#home switch > and2.in0
net min-home-x => x-and2homesw.in0
#is-currently-homing > and2.in1
net joint-0-homing joint.0.homing => x-and2homesw.in1
#and2.out > toggle.0.in
net x-and2homesw-to-homeswtoggle x-and2homesw.out => x-homeswtoggle.in
#toggle.0.out <=> encoder-index-enable
net x-toggle-to-indexenable x-homeswtoggle.out <=> hm2_5i25.0.encoder.00.index-enable
#encoder-index-enable > or2.0
net x-toggle-to-indexenable => x-or2homesw.in0
#home switch > or2.1
net min-home-x => x-or2homesw.in1
#or2.out > home-sw-in
net x-combined-switch-pulse x-or2homesw.out => joint.0.home-sw-in
#mux feedbacks
#stepgen-pos-fb > mux2.in0
net x-stepgen-pos-fb => x-fb-selector.in0
#encoder-pos-fb > mux2.in1
net x-encoder-pos-fb => x-fb-selector.in1
#mux2.out > joint.x.motor-pos-fb
net x-mux-out x-fb-selector.out => joint.0.motor-pos-fb
#joint.0.homed > tof.in
net joint-0-is-homed joint.0.homed => x-mux-tof.in
#tof.out > mux2.sel
net x-tof-to-mux-sel x-mux-tof.q => x-fb-selector.sel
loadrt toggle names="x-homeswtoggle"
loadrt or2 names="x-or2homesw"
loadrt mux2 names="x-fb-selector"
loadrt tof names="x-mux-tof"
addf x-and2homesw servo-thread
addf x-homeswtoggle servo-thread
addf x-or2homesw servo-thread
addf x-fb-selector servo-thread
addf x-mux-tof servo-thread
setp x-mux-tof.pt 0.1
#homing x, swapping limit sw for index pulse
#home switch > and2.in0
net min-home-x => x-and2homesw.in0
#is-currently-homing > and2.in1
net joint-0-homing joint.0.homing => x-and2homesw.in1
#and2.out > toggle.0.in
net x-and2homesw-to-homeswtoggle x-and2homesw.out => x-homeswtoggle.in
#toggle.0.out <=> encoder-index-enable
net x-toggle-to-indexenable x-homeswtoggle.out <=> hm2_5i25.0.encoder.00.index-enable
#encoder-index-enable > or2.0
net x-toggle-to-indexenable => x-or2homesw.in0
#home switch > or2.1
net min-home-x => x-or2homesw.in1
#or2.out > home-sw-in
net x-combined-switch-pulse x-or2homesw.out => joint.0.home-sw-in
#mux feedbacks
#stepgen-pos-fb > mux2.in0
net x-stepgen-pos-fb => x-fb-selector.in0
#encoder-pos-fb > mux2.in1
net x-encoder-pos-fb => x-fb-selector.in1
#mux2.out > joint.x.motor-pos-fb
net x-mux-out x-fb-selector.out => joint.0.motor-pos-fb
#joint.0.homed > tof.in
net joint-0-is-homed joint.0.homed => x-mux-tof.in
#tof.out > mux2.sel
net x-tof-to-mux-sel x-mux-tof.q => x-fb-selector.sel
For reference and context:
This particular machine has a single limit switch on either side of each axis. One side doubles as a home switch with "HOME_IGNORE_LIMITS = YES" in the ini. 3 axis router, axis GUI, LinuxCNC 2.9.6, Mesa 5i25, 7i76u on P3 and 7i89 on P2.
Last edit: 04 Dec 2025 04:31 by CaliusOptimus.
Please Log in or Create an account to join the conversation.
- PCW
-
- Away
- Moderator
-
Less
More
- Posts: 17464
- Thank you received: 5101
04 Dec 2025 05:04 #339712
by PCW
Replied by PCW on topic Homing open loop steppers on encoder index pulse
The easiest solution for Mesa hardware is to use firmware that
includes stepgen index, then the stepgen hardware mimics encoder
behaviour at index detection.
includes stepgen index, then the stepgen hardware mimics encoder
behaviour at index detection.
Please Log in or Create an account to join the conversation.
Time to create page: 0.053 seconds