Skip to content

Commit

Permalink
good sensorless (#65)
Browse files Browse the repository at this point in the history
* real sensorless v1

* flake

* tests and docs

* refactor current again to not wipe overrides

* add min homing dist behavior

* Remove unecessary logs

* use stepper name instead

* readme

---------

Co-authored-by: Rogerio Goncalves <[email protected]>
  • Loading branch information
bwnance and rogerlz authored Oct 12, 2023
1 parent 2a37eb5 commit 72b9b99
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ klippy/.version
.DS_Store
ci_build/
ci_cache/
_test_.*
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ Features merged into the master branch:

- [core: danger_options](https://github.com/DangerKlippers/danger-klipper/pull/67)

- [stepper: home_current](https://github.com/DangerKlippers/danger-klipper/pull/65)

- [homing: post-home retract](https://github.com/DangerKlippers/danger-klipper/pull/65)

- [homing: sensorless minimum home distance](https://github.com/DangerKlippers/danger-klipper/pull/65)

"Dangerous Klipper for dangerous users"

Klipper is a 3d-Printer firmware. It combines the power of a general
Expand Down
23 changes: 22 additions & 1 deletion docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ position_max:
# is 5mm/s.
#homing_retract_dist: 5.0
# Distance to backoff (in mm) before homing a second time during
# homing. Set this to zero to disable the second home. The default
# homing. If `use_sensorless_homing` is false, this setting can be set
# to zero to disable the second home. If `use_sensorless_homing` is
# true, this setting can be > 0 to backoff after homing. The default
# is 5mm.
#homing_retract_speed:
# Speed to use on the retract move after homing in case this should
Expand All @@ -219,6 +221,9 @@ position_max:
# better to use the default than to specify this parameter. The
# default is true if position_endstop is near position_max and false
# if near position_min.
#use_sensorless_homing:
# If true, disables the second home action if homing_retract_dist > 0.
# The default is true if endstop_pin is configured to use virtual_endstop
```

### Cartesian Kinematics
Expand Down Expand Up @@ -3399,6 +3404,9 @@ run_current:
# when the stepper is not moving. Setting a hold_current is not
# recommended (see TMC_Drivers.md for details). The default is to
# not reduce the current.
#home_current:
# The amount of current (in amps RMS) to configure the driver to use
# during homing procedures. The default is to not reduce the current.
#sense_resistor: 0.110
# The resistance (in ohms) of the motor sense resistor. The default
# is 0.110 ohms.
Expand Down Expand Up @@ -3492,6 +3500,9 @@ run_current:
# when the stepper is not moving. Setting a hold_current is not
# recommended (see TMC_Drivers.md for details). The default is to
# not reduce the current.
#home_current:
# The amount of current (in amps RMS) to configure the driver to use
# during homing procedures. The default is to not reduce the current.
#sense_resistor: 0.110
# The resistance (in ohms) of the motor sense resistor. The default
# is 0.110 ohms.
Expand Down Expand Up @@ -3535,6 +3546,7 @@ uart_pin:
#interpolate: True
run_current:
#hold_current:
#home_current:
#sense_resistor: 0.110
#stealthchop_threshold: 0
# See the "tmc2208" section for the definition of these parameters.
Expand Down Expand Up @@ -3603,6 +3615,9 @@ cs_pin:
run_current:
# The amount of current (in amps RMS) used by the driver during
# stepper movement. This parameter must be provided.
#home_current:
# The amount of current (in amps RMS) to configure the driver to use
# during homing procedures. The default is to not reduce the current.
#sense_resistor:
# The resistance (in ohms) of the motor sense resistor. This
# parameter must be provided.
Expand Down Expand Up @@ -3680,6 +3695,9 @@ run_current:
# when the stepper is not moving. Setting a hold_current is not
# recommended (see TMC_Drivers.md for details). The default is to
# not reduce the current.
#home_current:
# The amount of current (in amps RMS) to configure the driver to use
# during homing procedures. The default is to not reduce the current.
#rref: 12000
# The resistance (in ohms) of the resistor between IREF and GND. The
# default is 12000.
Expand Down Expand Up @@ -3801,6 +3819,9 @@ run_current:
# when the stepper is not moving. Setting a hold_current is not
# recommended (see TMC_Drivers.md for details). The default is to
# not reduce the current.
#home_current:
# The amount of current (in amps RMS) to configure the driver to use
# during homing procedures. The default is to not reduce the current.
#sense_resistor: 0.075
# The resistance (in ohms) of the motor sense resistor. The default
# is 0.075 ohms.
Expand Down
102 changes: 89 additions & 13 deletions klippy/extras/homing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import math
import logging

HOMING_START_DELAY = 0.001
ENDSTOP_SAMPLE_TIME = 0.000015
ENDSTOP_SAMPLE_COUNT = 4


# Return a completion that completes when all completions in a list complete
def multi_complete(printer, completions):
if len(completions) == 1:
Expand Down Expand Up @@ -47,6 +49,7 @@ def __init__(self, printer, endstops, toolhead=None):
toolhead = printer.lookup_object("toolhead")
self.toolhead = toolhead
self.stepper_positions = []
self.distance_elapsed = []

def get_mcu_endstops(self):
return [es for es, name in self.endstops]
Expand Down Expand Up @@ -116,6 +119,7 @@ def homing_move(
)
endstop_triggers.append(wait)
all_endstop_trigger = multi_complete(self.printer, endstop_triggers)

self.toolhead.dwell(HOMING_START_DELAY)
# Issue move
error = None
Expand Down Expand Up @@ -157,6 +161,16 @@ def homing_move(
sp.stepper_name: sp.halt_pos - sp.trig_pos
for sp in self.stepper_positions
}
steps_moved = {
sp.stepper_name: (sp.halt_pos - sp.start_pos)
* sp.stepper.get_step_dist()
for sp in self.stepper_positions
}
filled_steps_moved = {
sname: steps_moved.get(sname, 0)
for sname in [s.get_name() for s in kin.get_steppers()]
}
self.distance_elapsed = kin.calc_position(filled_steps_moved)
if any(over_steps.values()):
self.toolhead.set_position(movepos)
halt_kin_spos = {
Expand Down Expand Up @@ -216,10 +230,38 @@ def _fill_coord(self, coord):
def set_homed_position(self, pos):
self.toolhead.set_position(self._fill_coord(pos))

def _set_current_homing(self, homing_axes):
print_time = self.toolhead.get_last_move_time()
affected_rails = set()
for axis in homing_axes:
axis_name = "xyz"[axis] # only works for cartesian
partial_rails = self.toolhead.get_active_rails_for_axis(axis_name)
affected_rails = affected_rails | set(partial_rails)

for rail in affected_rails:
ch = rail.get_tmc_current_helper()
if ch is not None:
ch.set_current_for_homing(print_time)
self.toolhead.dwell(0.5)

def _set_current_post_homing(self, homing_axes):
print_time = self.toolhead.get_last_move_time()
affected_rails = set()
for axis in homing_axes:
axis_name = "xyz"[axis] # only works for cartesian
partial_rails = self.toolhead.get_active_rails_for_axis(axis_name)
affected_rails = affected_rails | set(partial_rails)

for rail in affected_rails:
ch = rail.get_tmc_current_helper()
if ch is not None:
ch.set_current_for_normal(print_time)
self.toolhead.dwell(0.5)

def home_rails(self, rails, forcepos, movepos):
# Notify of upcoming homing operation
self.printer.send_event("homing:home_rails_begin", self, rails)
# Alter kinematics class to think printer is at forcepos
# Alter kinematics class to think printer is at forcepo
homing_axes = [axis for axis in range(3) if forcepos[axis] is not None]
startpos = self._fill_coord(forcepos)
homepos = self._fill_coord(movepos)
Expand All @@ -228,9 +270,24 @@ def home_rails(self, rails, forcepos, movepos):
endstops = [es for rail in rails for es in rail.get_endstops()]
hi = rails[0].get_homing_info()
hmove = HomingMove(self.printer, endstops)

self._set_current_homing(homing_axes)

hmove.homing_move(homepos, hi.speed)

homing_axis_distances = [
dist
for i, dist in enumerate(hmove.distance_elapsed)
if i in homing_axes
]

# Perform second home
if hi.retract_dist:
needs_rehome = False
if any([dist < hi.retract_dist for dist in homing_axis_distances]):
needs_rehome = True

logging.info("needs rehome: %s", needs_rehome)
# Retract
startpos = self._fill_coord(forcepos)
homepos = self._fill_coord(movepos)
Expand All @@ -241,18 +298,37 @@ def home_rails(self, rails, forcepos, movepos):
hp - ad * retract_r for hp, ad in zip(homepos, axes_d)
]
self.toolhead.move(retractpos, hi.retract_speed)
# Home again
startpos = [
rp - ad * retract_r for rp, ad in zip(retractpos, axes_d)
]
self.toolhead.set_position(startpos)
hmove = HomingMove(self.printer, endstops)
hmove.homing_move(homepos, hi.second_homing_speed)
if hmove.check_no_movement() is not None:
raise self.printer.command_error(
"Endstop %s still triggered after retract"
% (hmove.check_no_movement(),)
)
if not hi.use_sensorless_homing or needs_rehome:
self.toolhead.dwell(0.5)
# Home again
startpos = [
rp - ad * retract_r for rp, ad in zip(retractpos, axes_d)
]
self.toolhead.set_position(startpos)
print_time = self.toolhead.get_last_move_time()
for endstop in endstops:
# re-querying a tmc endstop seems to reset the state
# otherwise it triggers almost immediately upon second home
endstop[0].query_endstop(print_time)
hmove = HomingMove(self.printer, endstops)
hmove.homing_move(homepos, hi.second_homing_speed)
if hmove.check_no_movement() is not None:
raise self.printer.command_error(
"Endstop %s still triggered after retract"
% (hmove.check_no_movement(),)
)

# Retract (again)
startpos = self._fill_coord(forcepos)
homepos = self._fill_coord(movepos)
axes_d = [hp - sp for hp, sp in zip(homepos, startpos)]
move_d = math.sqrt(sum([d * d for d in axes_d[:3]]))
retract_r = min(1.0, hi.retract_dist / move_d)
retractpos = [
hp - ad * retract_r for hp, ad in zip(homepos, axes_d)
]
self.toolhead.move(retractpos, hi.retract_speed)
self._set_current_post_homing(homing_axes)
# Signal home operation complete
self.toolhead.flush_step_generation()
self.trigger_mcu_pos = {
Expand Down
41 changes: 35 additions & 6 deletions klippy/extras/tmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Field helpers
######################################################################


# Return the position of the first bit set in a mask
def ffs(mask):
return (mask & -mask).bit_length() - 1
Expand Down Expand Up @@ -345,29 +346,53 @@ def cmd_SET_TMC_FIELD(self, gcmd):

def cmd_SET_TMC_CURRENT(self, gcmd):
ch = self.current_helper
prev_cur, prev_hold_cur, req_hold_cur, max_cur = ch.get_current()
(
prev_cur,
prev_hold_cur,
req_hold_cur,
max_cur,
prev_home_cur,
) = ch.get_current()
run_current = gcmd.get_float(
"CURRENT", None, minval=0.0, maxval=max_cur
)
hold_current = gcmd.get_float(
"HOLDCURRENT", None, above=0.0, maxval=max_cur
)
if run_current is not None or hold_current is not None:
home_current = gcmd.get_float(
"HOMECURRENT", None, above=0.0, maxval=max_cur
)
if (
run_current is not None
or hold_current is not None
or home_current is not None
):
if run_current is None:
run_current = prev_cur
if hold_current is None:
hold_current = req_hold_cur
if home_current is not None:
ch.set_home_current(home_current)
toolhead = self.printer.lookup_object("toolhead")
print_time = toolhead.get_last_move_time()
ch.set_current(run_current, hold_current, print_time)
prev_cur, prev_hold_cur, req_hold_cur, max_cur = ch.get_current()
(
prev_cur,
prev_hold_cur,
req_hold_cur,
max_cur,
prev_home_cur,
) = ch.get_current()
# Report values
if prev_hold_cur is None:
gcmd.respond_info("Run Current: %0.2fA" % (prev_cur,))
gcmd.respond_info(
"Run Current: %0.2fA Home Current: %0.2fA"
% (prev_cur, prev_home_cur)
)
else:
gcmd.respond_info(
"Run Current: %0.2fA Hold Current: %0.2fA"
% (prev_cur, prev_hold_cur)
"Run Current: %0.2fA Hold Current: %0.2fA Home Current: %0.2fA"
% (prev_cur, prev_hold_cur, prev_home_cur)
)

# Stepper phase tracking
Expand Down Expand Up @@ -451,6 +476,8 @@ def _handle_mcu_identify(self):
# Lookup stepper object
force_move = self.printer.lookup_object("force_move")
self.stepper = force_move.lookup_stepper(self.stepper_name)
self.stepper.set_tmc_current_helper(self.current_helper)

# Note pulse duration and step_both_edge optimizations available
self.stepper.setup_default_pulse_duration(0.000000100, True)

Expand Down Expand Up @@ -551,6 +578,7 @@ def cmd_DUMP_TMC(self, gcmd):
# TMC virtual pins
######################################################################


# Helper class for "sensorless homing"
class TMCVirtualPinHelper:
def __init__(self, config, mcu_tmc):
Expand Down Expand Up @@ -636,6 +664,7 @@ def handle_homing_move_end(self, hmove):
# Config reading helpers
######################################################################


# Helper to initialize the wave table from config or defaults
def TMCWaveTableHelper(config, mcu_tmc):
set_config_field = mcu_tmc.get_fields().set_config_field
Expand Down
Loading

0 comments on commit 72b9b99

Please sign in to comment.