From ddbfad6047f235c40f5496236a132a4edff9e1fa Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sun, 18 Jun 2023 14:28:36 +0300 Subject: [PATCH 1/3] stepper: Fix typo Signed-off-by: Dennis Marttinen --- klippy/stepper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/stepper.py b/klippy/stepper.py index 05e56cca4327..fd44effb6ffa 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -403,7 +403,7 @@ def add_extra_stepper(self, config): changed_invert = pin_params['invert'] != endstop['invert'] changed_pullup = pin_params['pullup'] != endstop['pullup'] if changed_invert or changed_pullup: - raise error("Pinter rail %s shared endstop pin %s " + raise error("Printer rail %s shared endstop pin %s " "must specify the same pullup/invert settings" % ( self.get_name(), pin_name)) mcu_endstop.add_stepper(stepper) From 7e06122e81c5d73be4a8a508aa36ad25b5512785 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Mon, 28 Aug 2023 15:58:32 +0200 Subject: [PATCH 2/3] kinematics: implement `clear_homing_state` Signed-off-by: Dennis Marttinen --- docs/Code_Overview.md | 8 ++++---- klippy/extras/safe_z_home.py | 4 ++-- klippy/kinematics/cartesian.py | 9 +++++---- klippy/kinematics/corexy.py | 9 +++++---- klippy/kinematics/corexz.py | 9 +++++---- klippy/kinematics/delta.py | 8 ++++++-- klippy/kinematics/deltesian.py | 4 ++++ klippy/kinematics/hybrid_corexy.py | 9 +++++---- klippy/kinematics/hybrid_corexz.py | 9 +++++---- klippy/kinematics/none.py | 2 ++ klippy/kinematics/polar.py | 12 +++++++----- klippy/kinematics/rotary_delta.py | 8 ++++++-- klippy/kinematics/winch.py | 3 +++ 13 files changed, 59 insertions(+), 35 deletions(-) diff --git a/docs/Code_Overview.md b/docs/Code_Overview.md index 0e4836acf5ec..9ccaa60be173 100644 --- a/docs/Code_Overview.md +++ b/docs/Code_Overview.md @@ -359,10 +359,10 @@ Useful steps: be efficient as it is typically only called during homing and probing operations. 5. Other methods. Implement the `check_move()`, `get_status()`, - `get_steppers()`, `home()`, and `set_position()` methods. These - functions are typically used to provide kinematic specific checks. - However, at the start of development one can use boiler-plate code - here. + `get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()` + methods. These functions are typically used to provide kinematic + specific checks. However, at the start of development one can use + boiler-plate code here. 6. Implement test cases. Create a g-code file with a series of moves that can test important cases for the given kinematics. Follow the [debugging documentation](Debugging.md) to convert this g-code file diff --git a/klippy/extras/safe_z_home.py b/klippy/extras/safe_z_home.py index ca9b8eb59ea3..b5b5244611c0 100644 --- a/klippy/extras/safe_z_home.py +++ b/klippy/extras/safe_z_home.py @@ -40,8 +40,8 @@ def cmd_G28(self, gcmd): toolhead.set_position(pos, homing_axes=[2]) toolhead.manual_move([None, None, self.z_hop], self.z_hop_speed) - if hasattr(toolhead.get_kinematics(), "note_z_not_homed"): - toolhead.get_kinematics().note_z_not_homed() + if hasattr(toolhead.get_kinematics(), "clear_homing_state"): + toolhead.get_kinematics().clear_homing_state((2,)) elif pos[2] < self.z_hop: # If the Z axis is homed, and below z_hop, lift it to z_hop toolhead.manual_move([None, None, self.z_hop], diff --git a/klippy/kinematics/cartesian.py b/klippy/kinematics/cartesian.py index 0c4bb9255853..2715d1720d68 100644 --- a/klippy/kinematics/cartesian.py +++ b/klippy/kinematics/cartesian.py @@ -73,9 +73,10 @@ def set_position(self, newpos, homing_axes): else: rail = self.rails[axis] self.limits[axis] = rail.get_range() - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limits[2] = (1.0, -1.0) + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.limits[i] = (1.0, -1.0) def home_axis(self, homing_state, axis, rail): # Determine movement position_min, position_max = rail.get_range() @@ -97,7 +98,7 @@ def home(self, homing_state): else: self.home_axis(homing_state, axis, self.rails[axis]) def _motor_off(self, print_time): - self.limits = [(1.0, -1.0)] * 3 + self.clear_homing_state((0, 1, 2)) def _check_endstops(self, move): end_pos = move.end_pos for i in (0, 1, 2): diff --git a/klippy/kinematics/corexy.py b/klippy/kinematics/corexy.py index d3b755001916..dc80d1eeca0b 100644 --- a/klippy/kinematics/corexy.py +++ b/klippy/kinematics/corexy.py @@ -43,9 +43,10 @@ def set_position(self, newpos, homing_axes): rail.set_position(newpos) if i in homing_axes: self.limits[i] = rail.get_range() - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limits[2] = (1.0, -1.0) + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.limits[i] = (1.0, -1.0) def home(self, homing_state): # Each axis is homed independently and in order for axis in homing_state.get_axes(): @@ -63,7 +64,7 @@ def home(self, homing_state): # Perform homing homing_state.home_rails([rail], forcepos, homepos) def _motor_off(self, print_time): - self.limits = [(1.0, -1.0)] * 3 + self.clear_homing_state((0, 1, 2)) def _check_endstops(self, move): end_pos = move.end_pos for i in (0, 1, 2): diff --git a/klippy/kinematics/corexz.py b/klippy/kinematics/corexz.py index 72134c32741b..b1c46146b937 100644 --- a/klippy/kinematics/corexz.py +++ b/klippy/kinematics/corexz.py @@ -43,9 +43,10 @@ def set_position(self, newpos, homing_axes): rail.set_position(newpos) if i in homing_axes: self.limits[i] = rail.get_range() - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limits[2] = (1.0, -1.0) + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.limits[i] = (1.0, -1.0) def home(self, homing_state): # Each axis is homed independently and in order for axis in homing_state.get_axes(): @@ -63,7 +64,7 @@ def home(self, homing_state): # Perform homing homing_state.home_rails([rail], forcepos, homepos) def _motor_off(self, print_time): - self.limits = [(1.0, -1.0)] * 3 + self.clear_homing_state((0, 1, 2)) def _check_endstops(self, move): end_pos = move.end_pos for i in (0, 1, 2): diff --git a/klippy/kinematics/delta.py b/klippy/kinematics/delta.py index bb81ab18abeb..04a75547b3cd 100644 --- a/klippy/kinematics/delta.py +++ b/klippy/kinematics/delta.py @@ -105,6 +105,11 @@ def set_position(self, newpos, homing_axes): self.limit_xy2 = -1. if tuple(homing_axes) == (0, 1, 2): self.need_home = False + def clear_homing_state(self, axes): + # Clearing homing state for each axis individually is not implemented + if 0 in axes or 1 in axes or 2 in axes: + self.limit_xy2 = -1 + self.need_home = True def home(self, homing_state): # All axes are homed simultaneously homing_state.set_axes([0, 1, 2]) @@ -112,8 +117,7 @@ def home(self, homing_state): forcepos[2] = -1.5 * math.sqrt(max(self.arm2)-self.max_xy2) homing_state.home_rails(self.rails, forcepos, self.home_position) def _motor_off(self, print_time): - self.limit_xy2 = -1. - self.need_home = True + self.clear_homing_state((0, 1, 2)) def check_move(self, move): end_pos = move.end_pos end_xy2 = end_pos[0]**2 + end_pos[1]**2 diff --git a/klippy/kinematics/deltesian.py b/klippy/kinematics/deltesian.py index bb23137084a9..e48949af2c79 100644 --- a/klippy/kinematics/deltesian.py +++ b/klippy/kinematics/deltesian.py @@ -117,6 +117,10 @@ def set_position(self, newpos, homing_axes): rail.set_position(newpos) for n in homing_axes: self.homed_axis[n] = True + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.homed_axis[i] = False def home(self, homing_state): homing_axes = homing_state.get_axes() home_xz = 0 in homing_axes or 2 in homing_axes diff --git a/klippy/kinematics/hybrid_corexy.py b/klippy/kinematics/hybrid_corexy.py index 265a0e6da33d..85f20bcc6040 100644 --- a/klippy/kinematics/hybrid_corexy.py +++ b/klippy/kinematics/hybrid_corexy.py @@ -75,9 +75,10 @@ def set_position(self, newpos, homing_axes): else: rail = self.rails[axis] self.limits[axis] = rail.get_range() - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limits[2] = (1.0, -1.0) + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.limits[i] = (1.0, -1.0) def home_axis(self, homing_state, axis, rail): position_min, position_max = rail.get_range() hi = rail.get_homing_info() @@ -97,7 +98,7 @@ def home(self, homing_state): else: self.home_axis(homing_state, axis, self.rails[axis]) def _motor_off(self, print_time): - self.limits = [(1.0, -1.0)] * 3 + self.clear_homing_state((0, 1, 2)) def _check_endstops(self, move): end_pos = move.end_pos for i in (0, 1, 2): diff --git a/klippy/kinematics/hybrid_corexz.py b/klippy/kinematics/hybrid_corexz.py index 2d89e3f7bd03..8e5c3d92e74a 100644 --- a/klippy/kinematics/hybrid_corexz.py +++ b/klippy/kinematics/hybrid_corexz.py @@ -75,9 +75,10 @@ def set_position(self, newpos, homing_axes): else: rail = self.rails[axis] self.limits[axis] = rail.get_range() - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limits[2] = (1.0, -1.0) + def clear_homing_state(self, axes): + for i, _ in enumerate(self.limits): + if i in axes: + self.limits[i] = (1.0, -1.0) def home_axis(self, homing_state, axis, rail): position_min, position_max = rail.get_range() hi = rail.get_homing_info() @@ -97,7 +98,7 @@ def home(self, homing_state): else: self.home_axis(homing_state, axis, self.rails[axis]) def _motor_off(self, print_time): - self.limits = [(1.0, -1.0)] * 3 + self.clear_homing_state((0, 1, 2)) def _check_endstops(self, move): end_pos = move.end_pos for i in (0, 1, 2): diff --git a/klippy/kinematics/none.py b/klippy/kinematics/none.py index ff3c57a9a2e5..fda1f073a38c 100644 --- a/klippy/kinematics/none.py +++ b/klippy/kinematics/none.py @@ -13,6 +13,8 @@ def calc_position(self, stepper_positions): return [0, 0, 0] def set_position(self, newpos, homing_axes): pass + def clear_homing_state(self, axes): + pass def home(self, homing_state): pass def check_move(self, move): diff --git a/klippy/kinematics/polar.py b/klippy/kinematics/polar.py index deed26d06009..fdd428a60edb 100644 --- a/klippy/kinematics/polar.py +++ b/klippy/kinematics/polar.py @@ -51,9 +51,12 @@ def set_position(self, newpos, homing_axes): self.limit_z = self.rails[1].get_range() if 0 in homing_axes and 1 in homing_axes: self.limit_xy2 = self.rails[0].get_range()[1]**2 - def note_z_not_homed(self): - # Helper for Safe Z Home - self.limit_z = (1.0, -1.0) + def clear_homing_state(self, axes): + if 0 in axes or 1 in axes: + # X and Y cannot be cleared separately + self.limit_xy2 = -1. + if 2 in axes: + self.limit_z = (1.0, -1.0) def _home_axis(self, homing_state, axis, rail): # Determine movement position_min, position_max = rail.get_range() @@ -86,8 +89,7 @@ def home(self, homing_state): if home_z: self._home_axis(homing_state, 2, self.rails[1]) def _motor_off(self, print_time): - self.limit_z = (1.0, -1.0) - self.limit_xy2 = -1. + self.clear_homing_state((0, 1, 2)) def check_move(self, move): end_pos = move.end_pos xy2 = end_pos[0]**2 + end_pos[1]**2 diff --git a/klippy/kinematics/rotary_delta.py b/klippy/kinematics/rotary_delta.py index 1eb050baa57d..46a63b822e5d 100644 --- a/klippy/kinematics/rotary_delta.py +++ b/klippy/kinematics/rotary_delta.py @@ -88,6 +88,11 @@ def set_position(self, newpos, homing_axes): self.limit_xy2 = -1. if tuple(homing_axes) == (0, 1, 2): self.need_home = False + def clear_homing_state(self, axes): + # Clearing homing state for each axis individually is not implemented + if 0 in axes or 1 in axes or 2 in axes: + self.limit_xy2 = -1 + self.need_home = True def home(self, homing_state): # All axes are homed simultaneously homing_state.set_axes([0, 1, 2]) @@ -97,8 +102,7 @@ def home(self, homing_state): forcepos[2] = -1. homing_state.home_rails(self.rails, forcepos, self.home_position) def _motor_off(self, print_time): - self.limit_xy2 = -1. - self.need_home = True + self.clear_homing_state((0, 1, 2)) def check_move(self, move): end_pos = move.end_pos end_xy2 = end_pos[0]**2 + end_pos[1]**2 diff --git a/klippy/kinematics/winch.py b/klippy/kinematics/winch.py index 11475d24cdd5..c69d1b6dcc54 100644 --- a/klippy/kinematics/winch.py +++ b/klippy/kinematics/winch.py @@ -36,6 +36,9 @@ def calc_position(self, stepper_positions): def set_position(self, newpos, homing_axes): for s in self.steppers: s.set_position(newpos) + def clear_homing_state(self, axes): + # XXX - homing not implemented + pass def home(self, homing_state): # XXX - homing not implemented homing_state.set_axes([0, 1, 2]) From 98efefedb735dec1697c4e610aa09c4d9ac81ec4 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 12 Sep 2023 13:39:54 +0200 Subject: [PATCH 3/3] force_move: implement CLEAR for SET_KINEMATIC_POSITION `CLEAR` clears the homing status (resets the axis limits) without turning off the motors. This is particularly useful when implementing safe Z homing in `[homing_override]` on printers with multiple independent Z steppers (where `FORCE_MOVE` can't be used). Signed-off-by: Dennis Marttinen --- docs/G-Codes.md | 19 ++++++++++--------- klippy/extras/force_move.py | 12 ++++++++++-- klippy/toolhead.py | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 94a6e9a9878d..4ef8818e34f7 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -585,15 +585,16 @@ state; issue a G28 afterwards to reset the kinematics. This command is intended for low-level diagnostics and debugging. #### SET_KINEMATIC_POSITION -`SET_KINEMATIC_POSITION [X=] [Y=] [Z=]`: Force -the low-level kinematic code to believe the toolhead is at the given -cartesian position. This is a diagnostic and debugging command; use -SET_GCODE_OFFSET and/or G92 for regular axis transformations. If an -axis is not specified then it will default to the position that the -head was last commanded to. Setting an incorrect or invalid position -may lead to internal software errors. This command may invalidate -future boundary checks; issue a G28 afterwards to reset the -kinematics. +`SET_KINEMATIC_POSITION [X=] [Y=] [Z=] +[CLEAR=<[X][Y][Z]>]`: Force the low-level kinematic code to believe the +toolhead is at the given cartesian position. This is a diagnostic and +debugging command; use SET_GCODE_OFFSET and/or G92 for regular axis +transformations. If an axis is not specified then it will default to the +position that the head was last commanded to. Setting an incorrect or +invalid position may lead to internal software errors. Use the CLEAR +parameter to forget the homing state for the given axes. This command +may invalidate future boundary checks; issue a G28 afterwards to reset +the kinematics. ### [gcode] diff --git a/klippy/extras/force_move.py b/klippy/extras/force_move.py index 50b801412dbd..fefedc3df5a4 100644 --- a/klippy/extras/force_move.py +++ b/klippy/extras/force_move.py @@ -131,8 +131,16 @@ def cmd_SET_KINEMATIC_POSITION(self, gcmd): x = gcmd.get_float('X', curpos[0]) y = gcmd.get_float('Y', curpos[1]) z = gcmd.get_float('Z', curpos[2]) - logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f", x, y, z) - toolhead.set_position([x, y, z, curpos[3]], homing_axes=(0, 1, 2)) + clear = gcmd.get('CLEAR', '').upper() + axes = ['X', 'Y', 'Z'] + clear_axes = [axes.index(a) for a in axes if a in clear] + logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f clear=%s", + x, y, z, ','.join((axes[i] for i in clear_axes))) + toolhead.set_position( + [x, y, z, curpos[3]], + homing_axes=(0, 1, 2), + clear_axes=clear_axes + ) def load_config(config): return ForceMove(config) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index 256915daabf9..ed6a0e115972 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -457,13 +457,14 @@ def _flush_handler(self, eventtime): # Movement commands def get_position(self): return list(self.commanded_pos) - def set_position(self, newpos, homing_axes=()): + def set_position(self, newpos, homing_axes=(), clear_axes=()): self.flush_step_generation() ffi_main, ffi_lib = chelper.get_ffi() ffi_lib.trapq_set_position(self.trapq, self.print_time, newpos[0], newpos[1], newpos[2]) self.commanded_pos[:] = newpos self.kin.set_position(newpos, homing_axes) + self.kin.clear_homing_state(clear_axes) self.printer.send_event("toolhead:set_position") def limit_next_junction_speed(self, speed): last_move = self.lookahead.get_last()