manual_probe: Report final probe results in new ProbeResult named tuple

Return the manual probe results in a named tuple containing (bed_x,
bed_y, bed_z, test_x, test_y, and test_z) components.  For a manual
probe the test_xyz will always be equal to bed_xyz, but these
components may differ when using automated z probes.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2025-12-20 16:41:44 -05:00
parent 3c56eb7f6f
commit 9ccb4d96e9
5 changed files with 39 additions and 27 deletions

View file

@ -286,14 +286,14 @@ class Calibrater:
# returns a callback function for the manual probe
is_end = self.current_point_index == len(probe_points) - 1
def callback(kin_pos):
if kin_pos is None:
def callback(mpresult):
if mpresult is None:
# probe was cancelled
self.gcmd.respond_info(
"AXIS_TWIST_COMPENSATION_CALIBRATE: Probe cancelled, "
"calibration aborted")
return
z_offset = self.current_measured_z - kin_pos[2]
z_offset = self.current_measured_z - mpresult.bed_z
self.results.append(z_offset)
if is_end:
# end of calibration

View file

@ -1,9 +1,18 @@
# Helper script for manual z height probing
#
# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2019-2025 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, bisect
import logging, bisect, collections
# Main probe results tuple. The probe estimates that if the toollhead
# is commanded to xy position (bed_x, bed_y) and then descends, the
# nozzle will contact the bed at a toolhead z position of bed_z. The
# probe test itself was performed while the toolhead was at xyz
# position (test_x, test_y, test_z). All coordinates are relative to
# the frame (the coordinate system used in the config file).
ProbeResult = collections.namedtuple('probe_result', [
'bed_x', 'bed_y', 'bed_z', 'test_x', 'test_y', 'test_z'])
# Helper to lookup the Z stepper config section
def lookup_z_endstop_config(config):
@ -62,9 +71,9 @@ class ManualProbe:
self.cmd_Z_OFFSET_APPLY_DELTA_ENDSTOPS,
desc=self.cmd_Z_OFFSET_APPLY_ENDSTOP_help)
self.reset_status()
def manual_probe_finalize(self, kin_pos):
if kin_pos is not None:
self.gcode.respond_info("Z position is %.3f" % (kin_pos[2],))
def manual_probe_finalize(self, mpresult):
if mpresult is not None:
self.gcode.respond_info("Z position is %.3f" % (mpresult.bed_z,))
def reset_status(self):
self.status = {
'is_active': False,
@ -77,10 +86,10 @@ class ManualProbe:
cmd_MANUAL_PROBE_help = "Start manual probe helper script"
def cmd_MANUAL_PROBE(self, gcmd):
ManualProbeHelper(self.printer, gcmd, self.manual_probe_finalize)
def z_endstop_finalize(self, kin_pos):
if kin_pos is None:
def z_endstop_finalize(self, mpresult):
if mpresult is None:
return
z_pos = self.z_position_endstop - kin_pos[2]
z_pos = self.z_position_endstop - mpresult.bed_z
self.gcode.respond_info(
"%s: position_endstop: %.3f\n"
"The SAVE_CONFIG command will update the printer config file\n"
@ -271,10 +280,11 @@ class ManualProbeHelper:
self.gcode.register_command('NEXT', None)
self.gcode.register_command('ABORT', None)
self.gcode.register_command('TESTZ', None)
kin_pos = None
mpresult = None
if success:
kin_pos = self.get_kinematics_pos()
self.finalize_callback(kin_pos)
mpresult = ProbeResult(*(kin_pos[:3] + kin_pos[:3]))
self.finalize_callback(mpresult)
def load_config(config):
return ManualProbe(config)

View file

@ -83,10 +83,10 @@ class ProbeCommandHelper:
pos = run_single_probe(self.probe, gcmd)
gcmd.respond_info("Result is z=%.6f" % (pos[2],))
self.last_z_result = pos[2]
def probe_calibrate_finalize(self, kin_pos):
if kin_pos is None:
def probe_calibrate_finalize(self, mpresult):
if mpresult is None:
return
z_offset = self.probe_calibrate_z - kin_pos[2]
z_offset = self.probe_calibrate_z - mpresult.bed_z
gcode = self.printer.lookup_object('gcode')
gcode.respond_info(
"%s: z_offset: %.3f\n"
@ -514,7 +514,9 @@ class ProbePointsHelper:
def _manual_probe_start(self):
self._raise_tool(not self.manual_results)
if len(self.manual_results) >= len(self.probe_points):
done = self._invoke_callback(self.manual_results)
results = [[mpr.bed_x, mpr.bed_y, mpr.bed_z]
for mpr in self.manual_results]
done = self._invoke_callback(results)
if done:
return
# Caller wants a "retry" - clear results and restart probing
@ -523,10 +525,10 @@ class ProbePointsHelper:
gcmd = self.gcode.create_gcode_command("", "", {})
manual_probe.ManualProbeHelper(self.printer, gcmd,
self._manual_probe_finalize)
def _manual_probe_finalize(self, kin_pos):
if kin_pos is None:
def _manual_probe_finalize(self, mpresult):
if mpresult is None:
return
self.manual_results.append(kin_pos)
self.manual_results.append(mpresult)
self._manual_probe_start()
# Helper to obtain a single probe measurement

View file

@ -206,11 +206,11 @@ class EddyCalibration:
pos, mad_mm, mad_hz)
gcode.respond_info(msg)
return filtered
def post_manual_probe(self, kin_pos):
if kin_pos is None:
def post_manual_probe(self, mpresult):
if mpresult is None:
# Manual Probe was aborted
return
curpos = list(kin_pos)
curpos = [mpresult.bed_x, mpresult.bed_y, mpresult.bed_z]
move = self.printer.lookup_object('toolhead').manual_move
# Move away from the bed
probe_calibrate_z = curpos[2]

View file

@ -221,19 +221,19 @@ class TemperatureProbe:
% (self.name, cnt, exp_cnt, last_temp, self.next_auto_temp)
)
def _manual_probe_finalize(self, kin_pos):
if kin_pos is None:
def _manual_probe_finalize(self, mpresult):
if mpresult is None:
# Calibration aborted
self._finalize_drift_cal(False)
return
if self.last_zero_pos is not None:
z_diff = self.last_zero_pos[2] - kin_pos[2]
z_diff = self.last_zero_pos - mpresult.bed_z
self.total_expansion += z_diff
logging.info(
"Estimated Total Thermal Expansion: %.6f"
% (self.total_expansion,)
)
self.last_zero_pos = kin_pos
self.last_zero_pos = mpresult.bed_z
toolhead = self.printer.lookup_object("toolhead")
tool_zero_z = toolhead.get_position()[2]
try: