From 35fca0b3287f2168af0ad920c5f74b4f9a51b394 Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 19 Jul 2024 18:30:36 -0700 Subject: [PATCH 1/3] Expand ~ in results_dir setting --- autospeed/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autospeed/main.py b/autospeed/main.py index 287d0a3..36e60b6 100644 --- a/autospeed/main.py +++ b/autospeed/main.py @@ -56,7 +56,7 @@ def __init__(self, config): ): if os.path.exists(path): results_default = path - self.results_dir = config.get('results_dir',default=results_default) + self.results_dir = os.path.expanduser(config.get('results_dir',default=results_default)) self.toolhead = None self.printer.register_event_handler("klippy:connect", self.handle_connect) From c27bf560718d3eec3705abcaece85725da7b28ff Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Sat, 20 Jul 2024 16:06:46 -0700 Subject: [PATCH 2/3] Fail install for Python 2 Closes #16 --- install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install.sh b/install.sh index d2e3a15..fb18c92 100755 --- a/install.sh +++ b/install.sh @@ -26,6 +26,9 @@ else exit -1 fi +# Check for old python +~/klippy-env/bin/python -c 'import sys; assert sys.version_info[0] == 3, "Python 3 is required."' + # Link auto speed to klipper echo "Linking auto speed to Klipper..." ln -sf "${SRCDIR}/auto_speed.py" "${KLIPPER_PATH}/klippy/extras/auto_speed.py" From 3a2bf1593f3ef5fff5919ae26f04f9393ba7b9f1 Mon Sep 17 00:00:00 2001 From: legend069 <40685552+legend069@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:17:48 +1100 Subject: [PATCH 3/3] ZYZ ENDSTOP ACCURACY commands added xyz accuracy commands added support for adjusting SCV --- autospeed/main.py | 201 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 12 deletions(-) diff --git a/autospeed/main.py b/autospeed/main.py index 287d0a3..a215764 100644 --- a/autospeed/main.py +++ b/autospeed/main.py @@ -39,6 +39,7 @@ def __init__(self, config): self.accel_min = config.getfloat('accel_min', default=1000.0, above=1.0) self.accel_max = config.getfloat('accel_max', default=100000.0, above=self.accel_min) self.accel_accu = config.getfloat('accel_accu', default=0.05, above=0.0, below=1.0) + self.scv = config.getfloat('scv', default=5, above=1.0, below=50) self.veloc_min = config.getfloat('velocity_min', default=50.0, above=1.0) self.veloc_max = config.getfloat('velocity_max', default=5000.0, above=self.veloc_min) @@ -77,6 +78,15 @@ def __init__(self, config): self.gcode.register_command('AUTO_SPEED_GRAPH', self.cmd_AUTO_SPEED_GRAPH, desc=self.cmd_AUTO_SPEED_GRAPH_help) + self.gcode.register_command('X_ENDSTOP_ACCURACY', + self.cmd_X_ENDSTOP_ACCURACY, + desc=self.cmd_AUTO_SPEED_GRAPH_help) + self.gcode.register_command('Y_ENDSTOP_ACCURACY', + self.cmd_Y_ENDSTOP_ACCURACY, + desc=self.cmd_AUTO_SPEED_GRAPH_help) + self.gcode.register_command('Z_ENDSTOP_ACCURACY', + self.cmd_Z_ENDSTOP_ACCURACY, + desc=self.cmd_AUTO_SPEED_GRAPH_help) self.level = None @@ -88,6 +98,7 @@ def handle_connect(self): # Reduce speed/acceleration for positioning movement self.th_accel = self.toolhead.max_accel/2 self.th_veloc = self.toolhead.max_velocity/2 + self.th_scv = self.toolhead.square_corner_velocity # Find and define leveling method if self.printer.lookup_object("screw_tilt_adjust", None) is not None: @@ -111,8 +122,9 @@ def handle_home_rails_end(self, homing_state, rails): if name in ["stepper_x", "stepper_y", "stepper_z"]: config = self.printer.lookup_object('configfile').status_raw_config[name] microsteps = int(config["microsteps"]) - self.steppers[name[-1]] = [pos_min, pos_max, microsteps] - + homing_retract_dist = float (config["homing_retract_dist"]) + second_homing_speed = float(config["second_homing_speed"]) + self.steppers[name[-1]] = [pos_min, pos_max, microsteps, homing_retract_dist, second_homing_speed] if self.steppers.get("x", None) is not None: self.axis_limits["x"] = { @@ -192,6 +204,7 @@ def cmd_AUTO_SPEED_ACCEL(self, gcmd): accel_accu = gcmd.get_float('ACCEL_ACCU', self.accel_accu, above=0.0, below=1.0) veloc = gcmd.get_float('VELOCITY', 1.0, above=1.0) + scv = gcmd.get_float('SCV', self.scv, above=1.0) respond = "AUTO SPEED finding maximum acceleration on" for axis in axes: @@ -210,6 +223,7 @@ def cmd_AUTO_SPEED_ACCEL(self, gcmd): aw.min = accel_min aw.max = accel_max aw.veloc = veloc + aw.scv = scv self.init_axis(aw, axis) rw.vals[aw.axis] = self.binary_search(aw) rw.duration = perf_counter() - start @@ -246,6 +260,7 @@ def cmd_AUTO_SPEED_VELOCITY(self, gcmd): veloc_accu = gcmd.get_float('VELOCITY_ACCU', self.veloc_accu, above=0.0, below=1.0) accel = gcmd.get_float('ACCEL', 1.0, above=1.0) + scv = gcmd.get_float('SCV', self.scv, above=1.0) respond = "AUTO SPEED finding maximum velocity on" for axis in axes: @@ -264,6 +279,7 @@ def cmd_AUTO_SPEED_VELOCITY(self, gcmd): aw.min = veloc_min aw.max = veloc_max aw.accel = accel + aw.scv = scv self.init_axis(aw, axis) rw.vals[aw.axis] = self.binary_search(aw) rw.duration = perf_counter() - start @@ -297,12 +313,14 @@ def cmd_AUTO_SPEED_VALIDATE(self, gcmd): accel = gcmd.get_float('ACCEL', default=self.toolhead.max_accel, above=0.0) veloc = gcmd.get_float('VELOCITY', default=self.toolhead.max_velocity, above=0.0) - + scv = gcmd.get_float('SCV', default=self.toolhead.square_corner_velocity, above=1.0) + respond = f"AUTO SPEED validating over {iterations} iterations\n" respond += f"Acceleration: {accel:.0f}\n" - respond += f"Velocity: {veloc:.0f}" + respond += f"Velocity: {veloc:.0f}\n" + respond += f"SCV: {scv:.0f}" self.gcode.respond_info(respond) - self._set_velocity(veloc, accel) + self._set_velocity(veloc, accel, scv) valid, duration, missed_x, missed_y = self._validate(veloc, iterations, margin, small_margin, max_missed) respond = f"AUTO SPEED validated results after {duration:.2f}s\n" @@ -569,10 +587,10 @@ def binary_search(self, aw: AttemptWrapper): def _attempt(self, aw: AttemptWrapper): timeAttempt = perf_counter() - self._set_velocity(self.th_veloc, self.th_accel) + self._set_velocity(self.th_veloc, self.th_accel, self.th_scv) self._move([aw.move.pos["x"][0], aw.move.pos["y"][0], aw.move.pos["z"][0]], self.th_veloc) self.toolhead.wait_moves() - self._set_velocity(aw.veloc, aw.accel) + self._set_velocity(aw.veloc, aw.accel, aw.scv) timeMove = perf_counter() self._move([aw.move.pos["x"][1], aw.move.pos["y"][1], aw.move.pos["z"][1]], aw.veloc) @@ -686,7 +704,8 @@ def _move(self, coord, speed): def _home(self, x=True, y=True, z=True): prevAccel = self.toolhead.max_accel prevVeloc = self.toolhead.max_velocity - self._set_velocity(self.th_veloc, self.th_accel) + prevScv = self.toolhead.square_corner_velocity + self._set_velocity(self.th_veloc, self.th_accel, self.th_scv) command = ["G28"] if x: command[-1] += " X0" @@ -696,7 +715,7 @@ def _home(self, x=True, y=True, z=True): command[-1] += " Z0" self.gcode._process_commands(command, False) self.toolhead.wait_moves() - self._set_velocity(prevVeloc, prevAccel) + self._set_velocity(prevVeloc, prevAccel, prevScv) def _get_steps(self): kin = self.toolhead.get_kinematics() @@ -747,9 +766,167 @@ def _posttest(self, start_steps, max_missed, home: list): return valid, stop_steps, missed, dur - def _set_velocity(self, velocity: float, accel: float): + def _set_velocity(self, velocity: float, accel: float, scv: float): #self.gcode.respond_info(f"AUTO SPEED setting limits to VELOCITY={velocity} ACCEL={accel}") self.toolhead.max_velocity = velocity self.toolhead.max_accel = accel - self.toolhead.requested_accel_to_decel = accel/2 - self.toolhead._calc_junction_deviation() \ No newline at end of file + self.toolhead.requested_accel_to_decel = accel + self.toolhead.square_corner_velocity = scv + self.toolhead._calc_junction_deviation() + + def cmd_X_ENDSTOP_ACCURACY(self, gcmd): + + if not len(self.steppers.keys()) == 3: + raise gcmd.error(f"Printer must be homed first! Found {len(self.steppers.keys())} homed axes.") + + # Number of samples for accuracy check + sample_count = gcmd.get_int("SAMPLES", 10, minval=1) + + # Retrieve homing parameters for the X axis from the previously stored values + second_homing_speed = self.steppers['x'][4] + homing_retract_dist = self.steppers['x'][3] + + # Toolhead object to control the movement + toolhead = self.printer.lookup_object('toolhead') + pos = toolhead.get_position() + + # Log the starting position for X + gcmd.respond_info("X_ENDSTOP_ACCURACY at X:%.3f (samples=%d)\n" % (pos[0], sample_count)) + gcmd.respond_info("Second Homing Speed: %.2f mm/s" % second_homing_speed) + gcmd.respond_info("Homing Retract Distance: %.2f mm" % homing_retract_dist) + + + # Create a dummy gcode command for a single sample + fo_params = dict(gcmd.get_command_parameters()) + fo_params['SAMPLES'] = '1' + gcode = self.printer.lookup_object('gcode') + fo_gcmd = gcode.create_gcode_command("", "", fo_params) + + # List to store the X positions hit during each sample + positions = [] + + # Move to the X endstop sample_count times and collect the X positions + for _ in range(sample_count): + self._home(True, False, False) + pos = toolhead.get_position() # Get the current X position after homing + positions.append(pos[0]) + toolhead.manual_move([pos[0] - homing_retract_dist, None, None], speed=second_homing_speed) # Move away from the endstop + + # Calculate the maximum, minimum, average, and standard deviation for X positions + max_value = max(positions) + min_value = min(positions) + avg_value = sum(positions) / len(positions) + range_value = max_value - min_value + + deviation_sum = sum([(x - avg_value) ** 2 for x in positions]) + sigma = (deviation_sum / len(positions)) ** 0.5 + + # Display results + gcmd.respond_info( + "X endstop accuracy results: maximum %.6f, minimum %.6f, range %.6f, " + "average %.6f, standard deviation %.6f" % (max_value, min_value, range_value, avg_value, sigma)) + + + def cmd_Y_ENDSTOP_ACCURACY(self, gcmd): + + if not len(self.steppers.keys()) == 3: + raise gcmd.error(f"Printer must be homed first! Found {len(self.steppers.keys())} homed axes.") + + # Number of samples for accuracy check + sample_count = gcmd.get_int("SAMPLES", 10, minval=1) + + # Retrieve homing parameters for the Y axis from the previously stored values + second_homing_speed = self.steppers['y'][4] + homing_retract_dist = self.steppers['y'][3] + + # Toolhead object to control the movement + toolhead = self.printer.lookup_object('toolhead') + pos = toolhead.get_position() + + # Log the starting position for Y + gcmd.respond_info("Y_ENDSTOP_ACCURACY at Y:%.3f (samples=%d)\n" % (pos[1], sample_count)) + gcmd.respond_info("Second Homing Speed: %.2f mm/s" % second_homing_speed) + gcmd.respond_info("Homing Retract Distance: %.2f mm" % homing_retract_dist) + + + # Create a dummy gcode command for a single sample + fo_params = dict(gcmd.get_command_parameters()) + fo_params['SAMPLES'] = '1' + gcode = self.printer.lookup_object('gcode') + fo_gcmd = gcode.create_gcode_command("", "", fo_params) + + # List to store the Y positions hit during each sample + positions = [] + + # Move to the Y endstop sample_count times and collect the Y positions + for _ in range(sample_count): + self._home(False, True, False) + pos = toolhead.get_position() # Get the current Y position after homing + positions.append(pos[1]) + toolhead.manual_move([None, pos[1] - homing_retract_dist, None], speed=second_homing_speed) # Move away from the endstop + + # Calculate the maximum, minimum, average, and standard deviation for Y positions + max_value = max(positions) + min_value = min(positions) + avg_value = sum(positions) / len(positions) + range_value = max_value - min_value + + deviation_sum = sum([(y - avg_value) ** 2 for y in positions]) + sigma = (deviation_sum / len(positions)) ** 0.5 + + # Display results + gcmd.respond_info( + "Y endstop accuracy results: maximum %.6f, minimum %.6f, range %.6f, " + "average %.6f, standard deviation %.6f" % (max_value, min_value, range_value, avg_value, sigma)) + + def cmd_Z_ENDSTOP_ACCURACY(self, gcmd): + + if not len(self.steppers.keys()) == 3: + raise gcmd.error(f"Printer must be homed first! Found {len(self.steppers.keys())} homed axes.") + + # Number of samples for accuracy check + sample_count = gcmd.get_int("SAMPLES", 10, minval=1) + + # Retrieve homing parameters for the Z axis from the previously stored values + second_homing_speed = self.steppers['z'][4] + homing_retract_dist = self.steppers['z'][3] + + # Toolhead object to control the movement + toolhead = self.printer.lookup_object('toolhead') + pos = toolhead.get_position() + + # Log the starting position for Z + gcmd.respond_info("Z_ENDSTOP_ACCURACY at Z:%.3f (samples=%d)\n" % (pos[2], sample_count)) + gcmd.respond_info("Second Homing Speed: %.2f mm/s" % second_homing_speed) + gcmd.respond_info("Homing Retract Distance: %.2f mm" % homing_retract_dist) + + + # Create a dummy gcode command for a single sample + fo_params = dict(gcmd.get_command_parameters()) + fo_params['SAMPLES'] = '1' + gcode = self.printer.lookup_object('gcode') + fo_gcmd = gcode.create_gcode_command("", "", fo_params) + + # List to store the Z positions hit during each sample + positions = [] + + # Move to the Z endstop sample_count times and collect the Z positions + for _ in range(sample_count): + self._home(False, False, True) + pos = toolhead.get_position() # Get the current Z position after homing + positions.append(pos[2]) + toolhead.manual_move([None, None, pos[2] + homing_retract_dist], speed=second_homing_speed) # Move away from the endstop + + # Calculate the maximum, minimum, average, and standard deviation for Z positions + max_value = max(positions) + min_value = min(positions) + avg_value = sum(positions) / len(positions) + range_value = max_value - min_value + + deviation_sum = sum([(z - avg_value) ** 2 for z in positions]) + sigma = (deviation_sum / len(positions)) ** 0.5 + + # Display results + gcmd.respond_info( + "Z endstop accuracy results: maximum %.6f, minimum %.6f, range %.6f, " + "average %.6f, standard deviation %.6f" % (max_value, min_value, range_value, avg_value, sigma)) \ No newline at end of file