From 29fc0acc34efaedf9ef3156a5a0abcf93910662d Mon Sep 17 00:00:00 2001 From: yochiwarez Date: Wed, 19 Jun 2024 20:32:15 +0100 Subject: [PATCH] add_y_axis_support --- docs/Axis_Twist_Compensation.md | 1 + docs/Config_Reference.md | 19 ++- klippy/extras/axis_twist_compensation.py | 148 ++++++++++++++++++----- 3 files changed, 134 insertions(+), 34 deletions(-) diff --git a/docs/Axis_Twist_Compensation.md b/docs/Axis_Twist_Compensation.md index 0017a2279de7..ee582aca4c26 100644 --- a/docs/Axis_Twist_Compensation.md +++ b/docs/Axis_Twist_Compensation.md @@ -30,6 +30,7 @@ perform `AXIS_TWIST_COMPENSATION_CALIBRATE` points along the bed * The calibration defaults to 3 points but you can use the option `SAMPLE_COUNT=` to use a different number. +* For Y-axis calibration, use `AXIS_TWIST_COMPENSATION_CALIBRATE AXIS=Y` instead. 2. [Adjust your Z offset](Probe_Calibrate.md#calibrating-probe-z-offset) 3. Perform automatic/probe-based bed tramming operations, such as [Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust), diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 6b42fe48da91..47bdadf7c461 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2035,7 +2035,7 @@ sensor_type: ldc1612 ### [axis_twist_compensation] -A tool to compensate for inaccurate probe readings due to twist in X gantry. See +A tool to compensate for inaccurate probe readings due to twist in X or Y gantry. See the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md) for more detailed information regarding symptoms, configuration and setup. @@ -2060,6 +2060,23 @@ calibrate_y: 112.5 # This should be the Y coordinate that positions the nozzle during the # calibration process. This parameter must be provided and is recommended to # be near the center of the bed + +# For Y-axis twist compensation, specify the following parameters: +calibrate_start_y: ... +# Defines the minimum Y coordinate of the calibration +# This should be the Y coordinate that positions the nozzle at the starting +# calibration position for the Y axis. This parameter must be provided if +# compensating for Y axis twist. +calibrate_end_y: ... +# Defines the maximum Y coordinate of the calibration +# This should be the Y coordinate that positions the nozzle at the ending +# calibration position for the Y axis. This parameter must be provided if +# compensating for Y axis twist. +calibrate_x: ... +# Defines the X coordinate of the calibration for Y axis twist compensation +# This should be the X coordinate that positions the nozzle during the +# calibration process for Y axis twist compensation. This parameter must be +# provided and is recommended to be near the center of the bed. ``` ## Additional stepper motors and extruders diff --git a/klippy/extras/axis_twist_compensation.py b/klippy/extras/axis_twist_compensation.py index e7aad52c047f..e0bbb7957260 100644 --- a/klippy/extras/axis_twist_compensation.py +++ b/klippy/extras/axis_twist_compensation.py @@ -26,13 +26,24 @@ def __init__(self, config): self.calibrate_start_x = config.getfloat('calibrate_start_x') self.calibrate_end_x = config.getfloat('calibrate_end_x') self.calibrate_y = config.getfloat('calibrate_y') - self.z_compensations = config.getlists('z_compensations', + self.zx_compensations = config.getlists('zx_compensations', default=[], parser=float) self.compensation_start_x = config.getfloat('compensation_start_x', default=None) - self.compensation_end_x = config.getfloat('compensation_start_y', + self.compensation_end_x = config.getfloat('compensation_end_x', default=None) + self.calibrate_start_y = config.getfloat('calibrate_start_y', + default=None) + self.calibrate_end_y = config.getfloat('calibrate_end_y', default=None) + self.calibrate_x = config.getfloat('calibrate_x', default=None) + self.compensation_start_y = config.getfloat('compensation_start_y', + default=None) + self.compensation_end_y = config.getfloat('compensation_end_y', + default=None) + self.zy_compensations = config.getlists('zy_compensations', + default=[], parser=float) + self.m = None self.b = None @@ -43,25 +54,41 @@ def __init__(self, config): self._update_z_compensation_value) def _update_z_compensation_value(self, pos): - if not self.z_compensations: - return + if self.zx_compensations: + pos[2] += self._get_interpolated_z_compensation( + pos[0], self.zx_compensations, + self.compensation_start_x, + self.compensation_end_x + ) + + if self.zy_compensations: + pos[2] += self._get_interpolated_z_compensation( + pos[1], self.zy_compensations, + self.compensation_start_y, + self.compensation_end_y + ) + + def _get_interpolated_z_compensation( + self, coord, z_compensations, + comp_start, + comp_end + ): - x_coord = pos[0] - z_compensations = self.z_compensations sample_count = len(z_compensations) - spacing = ((self.calibrate_end_x - self.calibrate_start_x) + spacing = ((comp_end - comp_start) / (sample_count - 1)) - interpolate_t = (x_coord - self.calibrate_start_x) / spacing + interpolate_t = (coord - comp_start) / spacing interpolate_i = int(math.floor(interpolate_t)) interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2) interpolate_t -= interpolate_i interpolated_z_compensation = bed_mesh.lerp( interpolate_t, z_compensations[interpolate_i], z_compensations[interpolate_i + 1]) - pos[2] += interpolated_z_compensation + return interpolated_z_compensation def clear_compensations(self): - self.z_compensations = [] + self.zx_compensations = [] + self.zy_compensations = [] self.m = None self.b = None @@ -80,10 +107,14 @@ def __init__(self, compensation, config): self._handle_connect) self.speed = compensation.speed self.horizontal_move_z = compensation.horizontal_move_z - self.start_point = (compensation.calibrate_start_x, + self.x_start_point = (compensation.calibrate_start_x, compensation.calibrate_y) - self.end_point = (compensation.calibrate_end_x, + self.x_end_point = (compensation.calibrate_end_x, compensation.calibrate_y) + self.y_start_point = (compensation.calibrate_start_y, + compensation.calibrate_x) + self.y_end_point = (compensation.calibrate_end_y, + compensation.calibrate_x) self.results = None self.current_point_index = None self.gcmd = None @@ -119,6 +150,7 @@ def _register_gcode_handlers(self): def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd): self.gcmd = gcmd sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT) + axis = gcmd.get('AXIS', 'X') # check for valid sample_count if sample_count is None or sample_count < 2: @@ -129,10 +161,51 @@ def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd): self.compensation.clear_compensations() # calculate some values - x_range = self.end_point[0] - self.start_point[0] - interval_dist = x_range / (sample_count - 1) - nozzle_points = self._calculate_nozzle_points(sample_count, - interval_dist) + if axis == 'X': + start_point = self.x_start_point + end_point = self.x_end_point + elif axis == 'Y': + + if not all([ + self.y_start_point[0], + self.y_end_point[0], + self.y_start_point[1] + ]): + raise self.gcmd.error( + """AXIS_TWIST_COMPENSATION for Y axis requires + calibrate_start_y, calibrate_end_y and calibrate_x + to be defined + """ + ) + + start_point = self.y_start_point + end_point = self.y_end_point + else: + raise self.gcmd.error( + "AXIS_TWIST_COMPENSATION_CALIBRATE: " + "Invalid axis.") + return + + axis_range = end_point[0] - start_point[0] + + interval_dist = axis_range / (sample_count - 1) + + # calculate the points to put the probe at, returned as a list of tuples + nozzle_points = [] + + if axis == 'X': + for i in range(sample_count): + x = start_point[0] + i * interval_dist + y = start_point[1] + nozzle_points.append((x, y)) + + elif axis == 'Y': + for i in range(sample_count): + x = start_point[1] + y = start_point[0] + i * interval_dist + nozzle_points.append((x, y)) + + probe_points = self._calculate_probe_points( nozzle_points, self.probe_x_offset, self.probe_y_offset) @@ -142,17 +215,9 @@ def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd): # begin calibration self.current_point_index = 0 self.results = [] + self.current_axis = axis self._calibration(probe_points, nozzle_points, interval_dist) - def _calculate_nozzle_points(self, sample_count, interval_dist): - # calculate the points to put the probe at, returned as a list of tuples - nozzle_points = [] - for i in range(sample_count): - x = self.start_point[0] + i * interval_dist - y = self.start_point[1] - nozzle_points.append((x, y)) - return nozzle_points - def _calculate_probe_points(self, nozzle_points, probe_x_offset, probe_y_offset): # calculate the points to put the nozzle at @@ -238,14 +303,31 @@ def _finalize_calibration(self): configfile = self.printer.lookup_object('configfile') values_as_str = ', '.join(["{:.6f}".format(x) for x in self.results]) - configfile.set(self.configname, 'z_compensations', values_as_str) - configfile.set(self.configname, 'compensation_start_x', - self.start_point[0]) - configfile.set(self.configname, 'compensation_end_x', - self.end_point[0]) - self.compensation.z_compensations = self.results - self.compensation.compensation_start_x = self.start_point[0] - self.compensation.compensation_end_x = self.end_point[0] + + if(self.current_axis == 'X'): + + configfile.set(self.configname, 'zx_compensations', values_as_str) + configfile.set(self.configname, 'compensation_start_x', + self.x_start_point[0]) + configfile.set(self.configname, 'compensation_end_x', + self.x_end_point[0]) + + self.compensation.zx_compensations = self.results + self.compensation.compensation_start_x = self.x_start_point[0] + self.compensation.compensation_end_x = self.x_end_point[0] + + elif(self.current_axis == 'Y'): + + configfile.set(self.configname, 'zy_compensations', values_as_str) + configfile.set(self.configname, 'compensation_start_y', + self.y_start_point[0]) + configfile.set(self.configname, 'compensation_end_y', + self.y_end_point[0]) + + self.compensation.zy_compensations = self.results + self.compensation.compensation_start_y = self.y_start_point[0] + self.compensation.compensation_end_y = self.y_end_point[0] + self.gcode.respond_info( "AXIS_TWIST_COMPENSATION state has been saved " "for the current session. The SAVE_CONFIG command will "