Skip to content

Commit

Permalink
add_auto_compensation_support
Browse files Browse the repository at this point in the history
axis_twist_compensation: new command implementation AXIS_TWIST_COMPENSATION_AUTOCALIBRATE

This commit adds automatic calculation support for compensating X and Y axis twist in the axis_twist_compensation module.

Signed-off-by: Jorge Apaza Merma <[email protected]>
  • Loading branch information
yochiwarez committed Jun 29, 2024
1 parent bdf6ea1 commit 4585e73
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 1 deletion.
8 changes: 8 additions & 0 deletions docs/Axis_Twist_Compensation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ points along the bed
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
> an influence to the calibration process.
## New Command: AXIS_TWIST_COMPENSATION_AUTOCALIBRATE

The ``AXIS_TWIST_COMPENSATION_AUTOCALIBRATE`` command performs automatic calibration to calculate the twist of the X and Y axes without manual measurement.

* Recommendation: Ensure the bed is completely flat and aligned (without tilt) as much as possible before performing the autocalibration. The autocalibration will take probes and automatically calculate the twist of the X and Y axes.

User Recommendation: It is recommended to use a glass bed for calibration.

## [axis_twist_compensation] setup and commands

Configuration options for [axis_twist_compensation] can be found in the
Expand Down
5 changes: 4 additions & 1 deletion docs/G-Codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,12 @@ The following commands are available when the
section](Config_Reference.md#axis_twist_compensation) is enabled.

#### AXIS_TWIST_COMPENSATION_CALIBRATE
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>]`: Initiates the X
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>] [AXIS=<X or Y, default X>]`: Initiates the X or Y
twist calibration wizard. `SAMPLE_COUNT` specifies the number of points along
the X axis to calibrate at and defaults to 3.
`axis` can either be X or Y

`AXIS_TWIST_COMPENSATION_AUTOCALIBRATE` performs automatic calibration to calculate the twist of the X and Y axes without manual measurement.

### [bed_mesh]

Expand Down
155 changes: 155 additions & 0 deletions klippy/extras/axis_twist_compensation.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ def _register_gcode_handlers(self):
'AXIS_TWIST_COMPENSATION_CALIBRATE',
self.cmd_AXIS_TWIST_COMPENSATION_CALIBRATE,
desc=self.cmd_AXIS_TWIST_COMPENSATION_CALIBRATE_help)
self.gcode.register_command(
'AXIS_TWIST_COMPENSATION_AUTOCALIBRATE',
self.cmd_AXIS_TWIST_COMPENSATION_AUTOCALIBRATE,
desc=self.cmd_AXIS_TWIST_COMPENSATION_CALIBRATE_help)

cmd_AXIS_TWIST_COMPENSATION_CALIBRATE_help = """
Performs the x twist calibration wizard
Expand Down Expand Up @@ -218,6 +222,157 @@ def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
self.current_axis = axis
self._calibration(probe_points, nozzle_points, interval_dist)

def _calculate_corrections(self, coordinates):
# Extracting x, y, and z values from coordinates
x_coords = [coord[0] for coord in coordinates]
y_coords = [coord[1] for coord in coordinates]
z_coords = [coord[2] for coord in coordinates]

# Calculate the desired point (average of all corner points in z)
# For a general case, we should extract the unique
# combinations of corner points
z_corners = [z_coords[i] for i, coord in enumerate(coordinates)
if (coord[0] in [x_coords[0], x_coords[-1]])
and (coord[1] in [y_coords[0], y_coords[-1]])]
z_desired = sum(z_corners) / len(z_corners)


# Calculate average deformation per axis
unique_x_coords = sorted(set(x_coords))
unique_y_coords = sorted(set(y_coords))

avg_z_x = []
for x in unique_x_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[0] == x]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_x.append(avg_z)

avg_z_y = []
for y in unique_y_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[1] == y]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_y.append(avg_z)

# Calculate corrections to reach the desired point
x_corrections = [z_desired - avg for avg in avg_z_x]
y_corrections = [z_desired - avg for avg in avg_z_y]

return x_corrections, y_corrections

def cmd_AXIS_TWIST_COMPENSATION_AUTOCALIBRATE(self, gcmd):
self.gcmd = gcmd
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)


if not all([
self.x_start_point[0],
self.x_end_point[0],
self.y_start_point[0],
self.y_end_point[0]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION_AUTOCALIBRATE requires
calibrate_start_x, calibrate_end_x, calibrate_start_y
and calibrate_end_y to be defined
"""
)

# check for valid sample_count
if sample_count is None or sample_count < 2:
raise self.gcmd.error(
"SAMPLE_COUNT to probe must be at least 2")

# clear the current config
self.compensation.clear_compensations()

# calculate the points to put the probe at, returned as a list of tuples
nozzle_points = []

min_x = self.x_start_point[0]
max_x = self.x_end_point[0]
min_y = self.y_start_point[0]
max_y = self.y_end_point[0]

# calculate x positions
spcx = (max_x - min_x) / (sample_count - 1)
xps = [min_x + spcx * i for i in range(sample_count)]

# Calculate points array
spcy = (max_y - min_y) / (sample_count - 1)
flip = False

points = []
for i in range(sample_count):
for j in range(sample_count):
if(not flip):
idx = j
else:
idx = sample_count -1 - j
points.append([xps[i], min_y + spcy * idx ])
flip = not flip

# verify no other manual probe is in progress
manual_probe.verify_no_manual_probe(self.printer)


# calculate the points to put the nozzle at, and probe
probe_points = []

for i in range(len(points)):
x = points[i][0] - self.probe_x_offset
y = points[i][1] - self.probe_y_offset
probe_points.append([x, y, self._auto_calibration((x,y))[2]])

# calculate corrections
x_corr, y_corr = self._calculate_corrections(probe_points)

x_corr_str = ', '.join(["{:.6f}".format(x)
for x in x_corr])

y_corr_str = ', '.join(["{:.6f}".format(x)
for x in y_corr])

# finalize
configfile = self.printer.lookup_object('configfile')
configfile.set(self.configname, 'zx_compensations', x_corr_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])


configfile.set(self.configname, 'zy_compensations', y_corr_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.gcmd.respond_info(
"AXIS_TWIST_COMPENSATION_AUTOCALIBRATE: Calibration complete: ")
self.gcmd.respond_info("\n".join(map(str, [x_corr, y_corr])), log=False)

def _auto_calibration(self, probe_point):

# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))

# move to point to probe
self._move_helper((probe_point[0],
probe_point[1], None))

# probe the point
pos = probe.run_single_probe(self.probe, self.gcmd)
#self.current_measured_z = pos[2]

# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))

return pos


def _calculate_probe_points(self, nozzle_points,
probe_x_offset, probe_y_offset):
# calculate the points to put the nozzle at
Expand Down

0 comments on commit 4585e73

Please sign in to comment.