-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
z_thermal_adjust: Add Z thermal adjuster module #4157
Changes from 56 commits
48ffbde
dcfc683
1a6a49d
7e16f2b
66f2e23
6fa3841
b28d3a3
a4bb2d1
718055c
074b1bf
5a0c6e2
28f0e3e
45336e7
66e16d5
2d3afd7
f76555a
9d906a6
c5cdcdd
79818f5
810f2d7
5c6ae33
5a24e05
e7c30c7
f69100f
af9cb99
9e230eb
e3814ad
9f38d24
38f8caa
748f56f
1e8536e
9e584b0
4d7443a
3ea8e49
04f443d
974ab9c
bc3f358
ebc3b74
6cf5117
3b63ebb
8079da2
8f405d9
65dc088
97f6a9a
0ea7ae7
702bc2d
fb2d1f7
d341285
3530062
a6ccf0d
61cdfb8
2bdbea5
a14140a
79f0878
a1599d6
35affe2
d654d7e
c183076
cf82988
60c5e11
0f36462
161202b
62398a1
ec01c0b
40eb6c4
2233448
e6deb42
af928bb
d5e21e2
c78de87
2ace087
84dfa0b
e8b786d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1131,7 +1131,40 @@ the nature of skew correction these lengths are set via gcode. See | |
[skew_correction] | ||
``` | ||
|
||
## Customized homing | ||
### [z_thermal_adjust] | ||
|
||
Temperature-dependant toolhead Z position adjustment. Compensate for vertical | ||
toolhead movement caused by thermal expansion of the printer's frame in | ||
real-time using a temperature sensor (typically coupled to a vertical section | ||
of frame). | ||
|
||
See also: [extended g-code commands](G-Codes.md#z_thermal_adjust). | ||
|
||
``` | ||
[z_thermal_adjust] | ||
#temp_coeff: | ||
# The temperature coefficient of expansion, in mm/K. For example, a | ||
# temp_coeff of 0.01 mm/K will move the Z axis downwards by 0.01 mm for every | ||
# Kelvin/degree Celsius that the temperature sensor increases. Defaults to | ||
# 0.0, which applies no adjustment. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use only Celsius in the description. I find it very confusing to use different units for different settings, so the convention in Klipper is for distances to always be in mm, temperatures in Celsius, time in seconds, etc. I understand Kelvin and Celsius are interchangeable in this context, but for consistency I think the documentation should only use Celsius. |
||
temp_sensor: | ||
# Temperature sensor to use for Z adjustment. Use full config section name | ||
# without quoutes. E.g. temperature_sensor frame. Also compatible with | ||
# temperature_fan sensors. This parameter must be provided. | ||
#smooth_time: | ||
# Smoothing window applied to the temperature sensor, in seconds. Can reduce | ||
# motor noise from excessive small corrections in response to sensor noise. | ||
# The default is 2.0 seconds. | ||
#z_adjust_off_above: | ||
# Disables adjustments above this Z height [mm]. The last computed correction | ||
# will remain applied until the toolhead moves below the specified Z height | ||
# again. The default is 99999999.0 mm (always on). | ||
#max_z_adjustment: | ||
# Maximum absolute adjustment that can be applied to the Z axis [mm]. The | ||
# default is 99999999.0 mm (unlimited). | ||
``` | ||
|
||
# Customized homing | ||
|
||
### [safe_z_home] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1202,6 +1202,30 @@ print. | |
#### SDCARD_RESET_FILE | ||
`SDCARD_RESET_FILE`: Unload file and clear SD state. | ||
|
||
### [z_thermal_adjust] | ||
|
||
The following commands are available when the | ||
[z_thermal_adjust config section](Config_Reference.md#z_thermal_adjust) | ||
is enabled. | ||
|
||
#### QUERY_Z_THERMAL_ADJUST | ||
`QUERY_FRAME_COMP`: reports current state and key parameters of the Z Thermal | ||
Adjust module. | ||
|
||
#### SET_Z_THERMAL_ADJUST | ||
`SET_Z_THERMAL_ADJUST [ENABLE=<0:1>] [COEFF=<value>]`: Enable or disable the | ||
Z thermal adjustment with `ENABLE`. Disabling does not remove any adjustment | ||
already applied, but will freeze the current adjustment value - this prevents | ||
potentially unsafe downward Z movement. Re-enabling can potentially cause upward | ||
tool movement as the adjustment is updated and applied. `COEFF` allows run-time | ||
tuning of the adjustment temperature coefficient (i.e. the `TEMP_COEFF` config | ||
parameter). `COEFF` values are not saved to the config. | ||
|
||
#### SET_Z_THERMAL_ADJUST_REF | ||
`SET_Z_THERMAL_ADJUST_REF [TEMPERATURE=<value>]`: Set the thermal adjustment | ||
reference temperature to the current reading, or to the value supplied with | ||
`TEMPERATURE`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be preferable to use a single command for all three of the above - SET_Z_THERMAL_ADJUST with no parameters just reports the current state, SET_Z_THERMAL_ADJUST with a REFERENCE_TEMPERATURE=123.4 sets the reference temperature, SET_Z_THERMAL_ADJUST with COEFF=567.8 sets the coefficient, etc. |
||
|
||
### [z_tilt] | ||
|
||
The following commands are available when the | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
# Z Thermal Adjust | ||
# | ||
# Copyright (C) 2022 Robert Pazdzior <robertp@norbital.com> | ||
# | ||
# This file may be distributed under the terms of the GNU GPLv3 license. | ||
|
||
# Adjusts Z position in real-time using a thermal probe to e.g. compensate | ||
# for thermal expansion of the printer frame. | ||
|
||
import threading | ||
|
||
class ZThermalAdjuster: | ||
def __init__(self, config): | ||
self.printer = config.get_printer() | ||
self.gcode = self.printer.lookup_object('gcode') | ||
self.lock = threading.Lock() | ||
self.config = config | ||
|
||
# Get config parameters, convert to SI units where necessary | ||
self.temp_coeff = config.getfloat('temp_coeff', minval=-1, maxval=1, | ||
default=0) | ||
self.temp_sensor_name = config.get('temp_sensor') | ||
self.smooth_time = config.getfloat('smooth_time', 2., above=0.) | ||
self.inv_smooth_time = 1. / self.smooth_time | ||
|
||
self.off_above_z = config.getfloat('z_adjust_off_above', 99999999.) | ||
self.max_z_adjust_mm = config.getfloat('max_z_adjustment', 99999999.) | ||
|
||
# Register printer events | ||
self.printer.register_event_handler("klippy:connect", | ||
self.handle_connect) | ||
self.printer.register_event_handler("homing:home_rails_end", | ||
self.handle_homing_move_end) | ||
|
||
# Setup temperature sensor | ||
self.last_temp = self.smoothed_temp = 0. | ||
self.last_temp_time = 0. | ||
self.ref_temperature = 0. | ||
|
||
# Z transformation | ||
self.z_adjust_mm = 0. | ||
self.last_z_adjust_mm = 0. | ||
self.adjust_enable = True | ||
self.last_position = [0., 0., 0., 0.] | ||
self.next_transform = None | ||
|
||
# Register gcode commands | ||
self.gcode.register_command('SET_Z_THERMAL_ADJUST', | ||
self.cmd_SET_Z_THERMAL_ADJUST, | ||
desc=self.cmd_SET_Z_THERMAL_ADJUST_help) | ||
self.gcode.register_command('QUERY_Z_THERMAL_ADJUST', | ||
self.cmd_QUERY_Z_THERMAL_ADJUST, | ||
desc=self.cmd_QUERY_Z_THERMAL_ADJUST_help) | ||
self.gcode.register_command('SET_Z_THERMAL_ADJUST_REF', | ||
self.cmd_SET_Z_THERMAL_ADJUST_REF, | ||
desc=self.cmd_SET_Z_THERMAL_ADJUST_REF_help) | ||
|
||
def handle_connect(self): | ||
'Called after all printer objects are instantiated' | ||
self.toolhead = self.printer.lookup_object('toolhead') | ||
gcode_move = self.printer.lookup_object('gcode_move') | ||
|
||
# Temperature sensor config check and callback registration | ||
try: | ||
self.sensor = self.printer.lookup_object(self.temp_sensor_name) | ||
except Exception as e: | ||
msg = ''' | ||
%s\nUse the full config section name in the "temp_sensor" parameter, | ||
e.g. "temp_sensor: temperature_sensor frame". | ||
''' % e | ||
raise self.printer.config_error(msg) | ||
else: | ||
self.sensor.sensor.setup_callback(self.z_adj_temperature_callback) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not valid to "peak" into another class like this (see docs/Code_Overview.md ). I don't undersand why this is being done. Shouldn't the code instantiate a new temperature sensor like other temperature modules? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I legit forgot that I did this. It's a workaround; I originally implemented it as you described, but people complained they couldn't see it in their front end, use it with a temperature_fan, etc. I think |
||
|
||
# Register move transformation | ||
self.next_transform = gcode_move.set_move_transform(self, force=True) | ||
|
||
# Pull Z step distance for minimum adjustment increment | ||
kin = self.printer.lookup_object('toolhead').get_kinematics() | ||
steppers = [s.get_name() for s in kin.get_steppers()] | ||
z_stepper = kin.get_steppers()[steppers.index("stepper_z")] | ||
self.z_step_dist = z_stepper.get_step_dist() | ||
|
||
def get_status(self, eventtime): | ||
return { | ||
'temperature': self.smoothed_temp, | ||
'current_z_adjust': self.z_adjust_mm, | ||
'z_adjust_ref_temperature': self.ref_temperature, | ||
'enabled': self.adjust_enable | ||
} | ||
|
||
def handle_homing_move_end(self, homing_state, rails): | ||
'Set reference temperature after Z homing.' | ||
if 2 in homing_state.get_axes(): | ||
self.ref_temperature = self.smoothed_temp | ||
self.z_adjust_mm = 0. | ||
|
||
def calc_adjust(self, pos): | ||
'Z adjustment calculation' | ||
if pos[2] < self.off_above_z: | ||
delta_t = self.smoothed_temp - self.ref_temperature | ||
|
||
# Calculate Z adjustment | ||
adjust = -1 * self.temp_coeff * delta_t | ||
|
||
# compute sign (+1 or -1) for maximum offset setting | ||
sign = 1 - (adjust <= 0)*2 | ||
|
||
# Don't apply adjustments smaller than step distance | ||
if abs(adjust - self.z_adjust_mm) > self.z_step_dist: | ||
self.z_adjust_mm = min([self.max_z_adjust_mm*sign, | ||
adjust], key=abs) | ||
|
||
# Apply Z adjustment | ||
new_z = pos[2] + self.z_adjust_mm | ||
self.last_z_adjust_mm = self.z_adjust_mm | ||
return [pos[0], pos[1], new_z, pos[3]] | ||
|
||
def calc_unadjust(self, pos): | ||
'Remove Z adjustment' | ||
unadjusted_z = pos[2] - self.z_adjust_mm | ||
return [pos[0], pos[1], unadjusted_z, pos[3]] | ||
|
||
def get_position(self): | ||
return self.calc_unadjust(self.next_transform.get_position()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused why this doesn't set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Forgive my ignorance, is that just an expected property for a module like this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A gcode transform must be able to both translate to a new coordinate system ( It seems the code is tracking a -Kevin There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the explanation, Kevin! |
||
|
||
def move(self, newpos, speed): | ||
# don't apply to extrude only moves or when disabled | ||
if (newpos[0:2] == self.last_position[0:2]) or not self.adjust_enable: | ||
z = newpos[2] + self.last_z_adjust_mm | ||
adjusted_pos = [newpos[0], newpos[1], z, newpos[3]] | ||
self.next_transform.move(adjusted_pos, speed) | ||
else: | ||
adjusted_pos = self.calc_adjust(newpos) | ||
self.next_transform.move(adjusted_pos, speed) | ||
self.last_position[:] = newpos | ||
|
||
def z_adj_temperature_callback(self, read_time, temp): | ||
'Called everytime the thermistor is read, used for smoothing' | ||
self.sensor.temperature_callback(read_time, temp) | ||
with self.lock: | ||
time_diff = read_time - self.last_temp_time | ||
self.last_temp = temp | ||
self.last_temp_time = read_time | ||
temp_diff = temp - self.smoothed_temp | ||
adj_time = min(time_diff * self.inv_smooth_time, 1.) | ||
self.smoothed_temp += temp_diff * adj_time | ||
|
||
def cmd_SET_Z_THERMAL_ADJUST(self, gcmd): | ||
new_state = gcmd.get_int('ENABLE', 1, minval=0, maxval=1) | ||
self.temp_coeff = gcmd.get_float('COEFF', self.temp_coeff, | ||
minval=-1, maxval=1) | ||
state = 'ENABLED' if new_state else 'DISABLED' | ||
msg = ("state: %s\n" | ||
"temp_coeff: %f mm/K" | ||
% (state, self.temp_coeff) | ||
) | ||
gcmd.respond_info(msg) | ||
|
||
if new_state != self.adjust_enable: | ||
gcode_move = self.printer.lookup_object('gcode_move') | ||
gcode_move.reset_last_position() | ||
self.adjust_enable = new_state | ||
|
||
def cmd_QUERY_Z_THERMAL_ADJUST(self, gcmd): | ||
state = 'ENABLED' if self.adjust_enable else 'DISABLED' | ||
msg = ("state: %s\n" | ||
"current temperature: %.2f degC\n" | ||
"reference temperature: %.2f degC\n" | ||
"applied Z adjustment: %.4f mm" % (state, | ||
self.smoothed_temp, | ||
self.ref_temperature, | ||
self.z_adjust_mm) | ||
) | ||
gcmd.respond_info(msg) | ||
|
||
def cmd_SET_Z_THERMAL_ADJUST_REF(self, gcmd): | ||
ref_temp = gcmd.get_float('TEMPERATURE', -273.16, minval=-273.15) | ||
if ref_temp > -273.16: | ||
self.ref_temperature = ref_temp | ||
else: | ||
self.ref_temperature = self.smoothed_temp | ||
msg = ("Z thermal adjust reference temperature set" | ||
" manually to %.3fc" % self.ref_temperature) | ||
gcmd.respond_info(msg) | ||
|
||
cmd_SET_Z_THERMAL_ADJUST_help = 'Set Z Thermal Adjust parameters.' | ||
cmd_QUERY_Z_THERMAL_ADJUST_help = ('Report current Z Thermal Adjust' | ||
'parameters.') | ||
cmd_SET_Z_THERMAL_ADJUST_REF_help = ('Use current probe temperature as Z ' | ||
'Thermal Adjust reference ' | ||
'temperature') | ||
|
||
def load_config(config): | ||
return ZThermalAdjuster(config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this makes an unintended change to this section header.