Skip to content

Commit

Permalink
eamars/sensorless_homing_helper
Browse files Browse the repository at this point in the history
  • Loading branch information
rogerlz committed Sep 19, 2023
1 parent 8759a30 commit 769ed8c
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Features merged into the master branch:

- [probe: z_calibration](https://github.com/DangerKlippers/danger-klipper/pull/31) ([klipper#4614](https://github.com/Klipper3d/klipper/pull/4614) / [protoloft/z_calibration](https://github.com/protoloft/klipper_z_calibration))

- [stepper: Sensorless Homing Helper](https://github.com/DangerKlippers/danger-klipper/pull/61) ([eamars/sensorless_homing_helper](https://github.com/eamars/sensorless_homing_helper))

"Dangerous Klipper for dangerous users"

Klipper is a 3d-Printer firmware. It combines the power of a general
Expand Down
52 changes: 50 additions & 2 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1810,7 +1810,7 @@ pins:

## Bed probing hardware

### [probe]
### ⚠️ [probe]

Z height probe. One may define this section to enable Z height probing
hardware. When this section is enabled, PROBE and QUERY_PROBE extended
Expand Down Expand Up @@ -1943,7 +1943,7 @@ control_pin:
# See the "probe" section for information on these parameters.
```

### [dockable_probe]
### ⚠️ [dockable_probe]

Certain probes are magnetically coupled to the toolhead and stowed
in a dock when not in use. One should define this section instead
Expand Down Expand Up @@ -2187,6 +2187,54 @@ end_gcode:
# detach the probe afterwards.
```

### ⚠️ [sensorless_homing_helper]

Sensorless Homing Helper with additional retraction along XY axis when
- X or Y is not homed or
- The last known location of the toolhead is too closed to the gantry.

The sensorless homing requires certain speed while hitting the gantry to
get repeatable XY coordinate. The additional XY retraction allows the carriage to
accelerate to the calibrated speed during the calibration.

```
[sensorless_homing_helper]
tmc_stepper_y_name: tmc2209 stepper_y
# The TMC stepper section name for Y.
# This parameter is required.
tmc_stepper_x_name: tmc2209 stepper_x
# The TMC stepper section name for X.
# This parameter is required.
home_current: 0.5
# The current while running the sensorless homing.
# This parameter is required.
minimum_homing_distance: 10
# The minimum distance (in mm) to achieve the repeatible sensorless homing.
# The default is 10
retract_distance: 10
# The retract distance (in mm) after the axis is homed. The default is 10
retract_speed: 20
# The speed (in mm/s) while running the retraction (both before and after homing).
# The default is 20
stallguard_reset_time: 1
# The time (in secs) for stallguard to reset before the next homing move.
# The default is 1
```

Additional Macros might be needed for 3rd-party plugins (e.g. Klicky) to use the sensorless routine.

```
[gcode_macro _HOME_X]
rename_existing: __HOME_X
gcode:
__HOME_X
[gcode_macro _HOME_Y]
rename_existing: __HOME_Y
gcode:
__HOME_Y
```

## Additional stepper motors and extruders

### [stepper_z1]
Expand Down
203 changes: 203 additions & 0 deletions klippy/extras/sensorless_homing_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
from contextlib import contextmanager


class SensorLessHomingHelper(object):
def __init__(self, config):
self.config = config
self.printer = config.get_printer()
self.toolhead = None
self.gcode = self.printer.lookup_object("gcode")

# Run current
self.tmc_stepper_y_name = config.get("tmc_stepper_y_name")
self.tmc_stepper_x_name = config.get("tmc_stepper_x_name")

self.pconfig = self.printer.lookup_object("configfile")

# Read config
self.home_current = config.getfloat("home_current")
self.minimum_homing_distance = config.getfloat(
"minimum_homing_distance", 10
)
self.retract_distance = config.getfloat("retract_distance", 10)
self.retract_speed = config.getfloat("retract_speed", 20)
self.stallguard_reset_time = config.getfloat("stallguard_reset_time", 1)
self.use_homing_status = config.getboolean("use_homing_status", True)

self.gcode.register_command(
"_HOME_X", self.cmd_HOME_X, "Sensorless homing X axis"
)
self.gcode.register_command(
"_HOME_Y", self.cmd_HOME_Y, "Sensorless homing X axis"
)

self.printer.register_event_handler(
"klippy:connect", self.handle_connect
)

def handle_connect(self):
self.toolhead = self.printer.lookup_object("toolhead")

@contextmanager
def set_xy_motor_current(self, homing_current):
x_stepper_name = self.tmc_stepper_x_name.split()[1]
self.gcode.run_script_from_command(
"SET_TMC_CURRENT STEPPER={} CURRENT={}".format(
x_stepper_name, homing_current
)
)

y_stepper_name = self.tmc_stepper_y_name.split()[1]
self.gcode.run_script_from_command(
"SET_TMC_CURRENT STEPPER={} CURRENT={}".format(
y_stepper_name, homing_current
)
)

try:
yield
finally:
curtime = self.printer.get_reactor().monotonic()
settings = self.pconfig.get_status(curtime)["settings"]
self.gcode.run_script_from_command(
"SET_TMC_CURRENT STEPPER={} CURRENT={}".format(
x_stepper_name,
settings[self.tmc_stepper_x_name]["run_current"],
)
)
self.gcode.run_script_from_command(
"SET_TMC_CURRENT STEPPER={} CURRENT={}".format(
y_stepper_name,
settings[self.tmc_stepper_y_name]["run_current"],
)
)

def cmd_HOME_X(self, gcmd):
# Check if X axis is homed and its last known position
curtime = self.printer.get_reactor().monotonic()
kin_status = self.toolhead.get_status(curtime)

pos = self.toolhead.get_position()

if self.use_homing_status and "x" not in kin_status["homed_axes"]:
gcmd.respond_info(
"X is not homed {}. Will perform the retract before home.".format(
kin_status["homed_axes"]
)
)
# Run the sensorless homing to the opposite direction
with self.set_xy_motor_current(self.home_current):
move_pos = pos[:]
move_pos[0] = 0
current_pos = pos[:]
current_pos[0] = self.minimum_homing_distance
endstops = (
self.toolhead.get_kinematics().rails[0].get_endstops()
)

# Do a manual homing
phoming = self.printer.lookup_object("homing")
self.toolhead.set_position(current_pos, homing_axes=[0])
phoming.manual_home(
toolhead=self.toolhead,
endstops=endstops,
pos=move_pos,
speed=self.retract_speed,
triggered=True,
check_triggered=False,
)

elif (
kin_status["axis_maximum"][0] - pos[0]
< self.minimum_homing_distance
):
gcmd.respond_info(
"X is homed but too closed to the maximum range {}. Will perform the retract before home.".format(
pos[0]
)
)
pos[0] -= self.minimum_homing_distance
self.toolhead.manual_move(pos, self.retract_speed)
else:
gcmd.respond_info(
"X is homed {} and away from maximum range {}.".format(
kin_status["homed_axes"], pos[0]
)
)

with self.set_xy_motor_current(self.home_current):
self.gcode.run_script_from_command("G28 X")
self.toolhead.wait_moves()

# Retract
pos = self.toolhead.get_position()
pos[0] -= self.retract_distance
self.toolhead.move(pos, self.retract_speed)
self.toolhead.dwell(self.stallguard_reset_time)

def cmd_HOME_Y(self, gcmd):
# Check if Y axis is homed and its last known position
curtime = self.printer.get_reactor().monotonic()
kin_status = self.toolhead.get_status(curtime)

pos = self.toolhead.get_position()

if self.use_homing_status and "y" not in kin_status["homed_axes"]:
gcmd.respond_info(
"Y is not homed {}. Will perform the retract before home.".format(
kin_status["homed_axes"]
)
)

# Run the sensorless homing to the opposite direction
with self.set_xy_motor_current(self.home_current):
move_pos = pos[:]
move_pos[1] = 0
current_pos = pos[:]
current_pos[1] = self.minimum_homing_distance
endstops = (
self.toolhead.get_kinematics().rails[1].get_endstops()
)

# Do a manual homing
phoming = self.printer.lookup_object("homing")
self.toolhead.set_position(current_pos, homing_axes=[1])
phoming.manual_home(
toolhead=self.toolhead,
endstops=endstops,
pos=move_pos,
speed=self.retract_speed,
triggered=True,
check_triggered=False,
)
elif (
kin_status["axis_maximum"][1] - pos[1]
< self.minimum_homing_distance
):
gcmd.respond_info(
"Y is homed but too closed to the maximum range {}. Will perform the retract before home.".format(
pos[1]
)
)
pos[1] -= self.minimum_homing_distance
self.toolhead.manual_move(pos, self.retract_speed)
else:
gcmd.respond_info(
"Y is homed {} and away from maximum range {}.".format(
kin_status["homed_axes"], pos[1]
)
)

with self.set_xy_motor_current(self.home_current):
self.gcode.run_script_from_command("G28 Y")
self.toolhead.wait_moves()

# Retract
pos = self.toolhead.get_position()
pos[1] -= self.retract_distance
self.toolhead.move(pos, self.retract_speed)
self.toolhead.dwell(self.stallguard_reset_time)


def load_config(config):
return SensorLessHomingHelper(config)
Loading

0 comments on commit 769ed8c

Please sign in to comment.