Skip to content

Commit

Permalink
add_y_axis_support
Browse files Browse the repository at this point in the history
axis_twist_compensation: Implement Y-axis support

This commit implements support for the Y-axis in the axis_twist_compensation
module. This update enables the module to handle corrections for printers
with a twisted Y rail.

Signed-off-by: Jorge Apaza Merma <[email protected]>
  • Loading branch information
yochiwarez committed Jun 23, 2024
1 parent 11f04ba commit bdf6ea1
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 34 deletions.
1 change: 1 addition & 0 deletions docs/Axis_Twist_Compensation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
19 changes: 18 additions & 1 deletion docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down
148 changes: 115 additions & 33 deletions klippy/extras/axis_twist_compensation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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 "
Expand Down

0 comments on commit bdf6ea1

Please sign in to comment.