diff --git a/README.md b/README.md index 61beaedd8..dd39089e0 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,6 @@ If I want my printer to light itself on fire, I should be able to make my printe - [core: no Python2 tests; no PRU boards](https://github.com/DangerKlippers/danger-klipper/pull/39) -- [core: git-untracked folder, plugins for user-plugins](https://github.com/DangerKlippers/danger-klipper/pull/82) - -- [core: danger_options](https://github.com/DangerKlippers/danger-klipper/pull/67) - - [fan: normalising Fan PWM power](https://github.com/DangerKlippers/danger-klipper/pull/44) ([klipper#6307](https://github.com/Klipper3d/klipper/pull/6307)) - [fan: reverse FAN](https://github.com/DangerKlippers/danger-klipper/pull/51) ([klipper#4983](https://github.com/Klipper3d/klipper/pull/4983)) @@ -26,8 +22,6 @@ If I want my printer to light itself on fire, I should be able to make my printe - [heater: velocity PID](https://github.com/DangerKlippers/danger-klipper/pull/47) ([klipper#6272](https://github.com/Klipper3d/klipper/pull/6272)) -- [heater: PID-Profiles](https://github.com/DangerKlippers/danger-klipper/pull/145) - - [gcode: jinja2.ext.do extension](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#5149](https://github.com/Klipper3d/klipper/pull/5149)) - [gcode: gcode_shell_command](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#2173](https://github.com/Klipper3d/klipper/pull/2173) / [kiuah](https://github.com/dw-0/kiauh/blob/master/resources/gcode_shell_command.py) ) @@ -40,20 +34,20 @@ If I want my printer to light itself on fire, I should be able to make my printe - [z_tilt: z-tilt calibration](https://github.com/DangerKlippers/danger-klipper/pull/105) ([klipper3d#4083](https://github.com/Klipper3d/klipper/pull/4083) / [dk/ztilt_calibration](https://github.com/DangerKlippers/danger-klipper/pull/54)) -- [stepper: home_current](https://github.com/DangerKlippers/danger-klipper/pull/65) +- [core: danger_options](https://github.com/DangerKlippers/danger-klipper/pull/67) -- [stepper: current_change_dwell_time](https://github.com/DangerKlippers/danger-klipper/pull/90) +- [stepper: home_current](https://github.com/DangerKlippers/danger-klipper/pull/65) - [homing: post-home retract](https://github.com/DangerKlippers/danger-klipper/pull/65) - [homing: sensorless minimum home distance](https://github.com/DangerKlippers/danger-klipper/pull/65) -- [homing: min_home_dist](https://github.com/DangerKlippers/danger-klipper/pull/90) - - [virtual_sdcard: scanning of subdirectories](https://github.com/DangerKlippers/danger-klipper/pull/68) ([klipper#6327](https://github.com/Klipper3d/klipper/pull/6327)) - [retraction: z_hop while retracting](https://github.com/DangerKlippers/danger-klipper/pull/83) ([klipper#6311](https://github.com/Klipper3d/klipper/pull/6311)) +- [core: git-untracked folder, plugins for user-plugins](https://github.com/DangerKlippers/danger-klipper/pull/82) + - [danger_options: allow plugins to override conflicting extras](https://github.com/DangerKlippers/danger-klipper/pull/82) - [danger_options: expose the multi mcu homing timeout as a parameter](https://github.com/DangerKlippers/danger-klipper/pull/93) @@ -64,6 +58,10 @@ If I want my printer to light itself on fire, I should be able to make my printe - [temperature_mcu: add reference_voltage](https://github.com/DangerKlippers/danger-klipper/pull/99) ([klipper#5713](https://github.com/Klipper3d/klipper/pull/5713)) +- [stepper: current_change_dwell_time](https://github.com/DangerKlippers/danger-klipper/pull/90) + +- [homing: min_home_dist](https://github.com/DangerKlippers/danger-klipper/pull/90) + - [adxl345: improve ACCELEROMETER_QUERY command](https://github.com/DangerKlippers/danger-klipper/pull/124) - [extruder: add flag to use the PA constant from a trapq move vs a cached value](https://github.com/DangerKlippers/danger-klipper/pull/132) diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 424c6e8fd..95701c3e8 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -757,14 +757,6 @@ above the supplied MINIMUM and/or at or below the supplied MAXIMUM. [TARGET=]`: Sets the target temperature for a heater. If a target temperature is not supplied, the target is 0. -#### SET_SMOOTH_TIME -`SET_SMOOTH_TIME HEATER= [SMOOTH_TIME=] -[SAVE_TO_PROFILE=0|1]`: Sets the smooth_time of the specified heater. -If SMOOTH_TIME is omitted, the smooth_time will be reset to the value -from the config. -If SAVE_TO_PROFILE is set to 1, the new value will be written to the -current PID_PROFILE. - ### [idle_timeout] The idle_timeout module is automatically loaded. @@ -976,52 +968,6 @@ require a value of 0.03 or higher. allow one to manually change PID parameters of heaters without a reload of the firmware. -### [pid_profile] - -The PID_PROFILE module is automatically loaded if a heater is defined -in the config file. - -#### PID_PROFILE -`PID_PROFILE LOAD= HEATER= [DEFAULT=] -[VERBOSE=] [RESET_TARGET=0|1] [LOAD_CLEAN=0|1]`: -Loads the given PID_PROFILE for the specified heater. If DEFAULT is specified, -the Profile specified in DEFAULT will be loaded when then given Profile for LOAD -can't be found (like a getOrDefault method). If VERBOSE is set to LOW, minimal -info will be written in console. -If set to NONE, no console outputs will be given. -If KEEP_TARGET is set to 1, the heater will keep it's target temperature, -if set to 0, the target temperature will be set to 0. -By default the target temperature of the heater will be set to 0 so the -algorithm has time to settle in. -If LOAD_CLEAN is set to 1, the profile would be loaded as if the printer just -started up, if set to 0, the profile will retain previous heating information. -By default the information will be kept to reduce overshoot, change this value -if you encounter weird behaviour while switching profiles. - -`PID_PROFILE SAVE= HEATER=`: -Saves the currently loaded profile of the specified heater to the config under -the given name. - -`PID_PROFILE REMOVE= HEATER=`: -Removes the given profile from the profiles List for the current session and config if SAVE_CONFIG is issued afterwards. - -`PID_PROFILE SET_VALUES= HEATER= TARGET= TOLERANCE= -CONTROL= KP= KI= KD= [RESET_TARGET=0|1] [LOAD_CLEAN=0|1]`: -Creates a new profile with the given PID values, CONTROL must either be `pid` or -`pid_v`, TOLERANCE and TARGET must be specified to create a valid profile, -though the values themselves don't matter. -If KEEP_TARGET is set to 1, the heater will keep it's target temperature, -if set to 0, the target temperature will be set to 0. -By default the target temperature of the heater will be set to 0 so the -algorithm has time to settle in. -If LOAD_CLEAN is set to 1, the profile would be loaded as if the printer just -started up, if set to 0, the profile will retain previous heating information. -By default the information will be kept to reduce overshoot, change this value -if you encounter weird behaviour while switching profiles. - -`PID_PROFILE GET_VALUES HEATER=`: -Outputs the values of the current loaded pid_profile of the given heater to the console. - ### [pause_resume] The following commands are available when the diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index a6553d7bf..243031985 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -3,9 +3,7 @@ # Copyright (C) 2016-2020 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. -import collections import os -import logging import threading @@ -17,24 +15,12 @@ MAX_HEAT_TIME = 5.0 AMBIENT_TEMP = 25.0 PID_PARAM_BASE = 255.0 -PID_PROFILE_VERSION = 1 -PID_PROFILE_OPTIONS = { - "pid_target": (float, "%.2f"), - "pid_tolerance": (float, "%.4f"), - "control": (str, "%s"), - "smooth_time": (float, "%.3f"), - "pid_kp": (float, "%.3f"), - "pid_ki": (float, "%.3f"), - "pid_kd": (float, "%.3f"), -} class Heater: def __init__(self, config, sensor): self.printer = config.get_printer() self.name = config.get_name().split()[-1] - self.config = config - self.configfile = self.printer.lookup_object("configfile") # Setup sensor self.sensor = sensor self.min_temp = config.getfloat("min_temp", minval=KELVIN_TO_CELSIUS) @@ -56,8 +42,7 @@ def __init__(self, config, sensor): self.max_power = config.getfloat( "max_power", 1.0, above=0.0, maxval=1.0 ) - self.config_smooth_time = config.getfloat("smooth_time", 1.0, above=0.0) - self.smooth_time = self.config_smooth_time + self.smooth_time = config.getfloat("smooth_time", 1.0, above=0.0) self.inv_smooth_time = 1.0 / self.smooth_time self.lock = threading.Lock() self.last_temp = self.smoothed_temp = self.target_temp = 0.0 @@ -65,12 +50,14 @@ def __init__(self, config, sensor): # pwm caching self.next_pwm_time = 0.0 self.last_pwm_value = 0.0 - # Those are necessary so the klipper config check does not complain - config.get("control", None) - config.getfloat("pid_kp", None) - config.getfloat("pid_ki", None) - config.getfloat("pid_kd", None) - config.getfloat("max_delta", None) + # Setup control algorithm sub-class + algos = { + "watermark": ControlBangBang, + "pid": ControlPID, + "pid_v": ControlVelocityPID, + } + algo = config.getchoice("control", algos) + self.control = algo(self, config) # Setup output heater pin heater_pin = config.get("heater_pin") ppins = self.printer.lookup_object("pins") @@ -83,33 +70,15 @@ def __init__(self, config, sensor): # Load additional modules self.printer.load_object(config, "verify_heater %s" % (self.name,)) self.printer.load_object(config, "pid_calibrate") - self.gcode = self.printer.lookup_object("gcode") - self.pmgr = self.ProfileManager(self) - self.control = self.lookup_control( - self.pmgr.init_default_profile(), True - ) - self.gcode.register_mux_command( + gcode = self.printer.lookup_object("gcode") + gcode.register_mux_command( "SET_HEATER_TEMPERATURE", "HEATER", self.name, self.cmd_SET_HEATER_TEMPERATURE, desc=self.cmd_SET_HEATER_TEMPERATURE_help, ) - self.gcode.register_mux_command( - "SET_SMOOTH_TIME", - "HEATER", - self.name, - self.cmd_SET_SMOOTH_TIME, - desc=self.cmd_SET_SMOOTH_TIME_help, - ) - self.gcode.register_mux_command( - "PID_PROFILE", - "HEATER", - self.name, - self.pmgr.cmd_PID_PROFILE, - desc=self.pmgr.cmd_PID_PROFILE_help, - ) - self.gcode.register_mux_command( + gcode.register_mux_command( "SET_HEATER_PID", "HEATER", self.name, @@ -117,16 +86,6 @@ def __init__(self, config, sensor): desc=self.cmd_SET_HEATER_PID_help, ) - def lookup_control(self, profile, load_clean=False): - algos = collections.OrderedDict( - { - "watermark": ControlBangBang, - "pid": ControlPID, - "pid_v": ControlVelocityPID, - } - ) - return algos[profile["control"]](profile, self, load_clean) - def set_pwm(self, read_time, value): if self.target_temp <= 0.0: value = 0.0 @@ -165,9 +124,6 @@ def get_max_power(self): def get_smooth_time(self): return self.smooth_time - def set_inv_smooth_time(self, inv_smooth_time): - self.inv_smooth_time = inv_smooth_time - def set_temp(self, degrees): if degrees and (degrees < self.min_temp or degrees > self.max_temp): raise self.printer.command_error( @@ -192,17 +148,13 @@ def check_busy(self, eventtime): eventtime, self.smoothed_temp, self.target_temp ) - def set_control(self, control, keep_target=True): + def set_control(self, control): with self.lock: old_control = self.control self.control = control - if not keep_target: - self.target_temp = 0.0 + self.target_temp = 0.0 return old_control - def get_control(self): - return self.control - def alter_target(self, target_temp): if target_temp: target_temp = max(self.min_temp, min(self.max_temp, target_temp)) @@ -230,7 +182,6 @@ def get_status(self, eventtime): "temperature": round(smoothed_temp, 2), "target": target_temp, "power": last_pwm_value, - "pid_profile": self.get_control().get_profile()["name"], } cmd_SET_HEATER_TEMPERATURE_help = "Sets a heater temperature" @@ -240,19 +191,6 @@ def cmd_SET_HEATER_TEMPERATURE(self, gcmd): pheaters = self.printer.lookup_object("heaters") pheaters.set_temperature(self, temp) - cmd_SET_SMOOTH_TIME_help = "Set the smooth time for the given heater" - - def cmd_SET_SMOOTH_TIME(self, gcmd): - save_to_profile = gcmd.get_int("SAVE_TO_PROFILE", 0, minval=0, maxval=1) - self.smooth_time = gcmd.get_float( - "SMOOTH_TIME", self.config_smooth_time, minval=0.0 - ) - self.inv_smooth_time = 1.0 / self.smooth_time - self.get_control().update_smooth_time() - if save_to_profile: - self.get_control().get_profile()["smooth_time"] = self.smooth_time - self.pmgr.save_profile() - cmd_SET_HEATER_PID_help = "Sets a heater PID parameter" def cmd_SET_HEATER_PID(self, gcmd): @@ -268,339 +206,6 @@ def cmd_SET_HEATER_PID(self, gcmd): if kd is not None: self.control.Kd = kd / PID_PARAM_BASE - class ProfileManager: - def __init__(self, outer_instance): - self.outer_instance = outer_instance - self.profiles = {} - self.incompatible_profiles = [] - # Fetch stored profiles from Config - stored_profs = self.outer_instance.config.get_prefix_sections( - "pid_profile %s" % self.outer_instance.name - ) - for profile in stored_profs: - self._init_profile(profile, profile.get_name().split(" ", 2)[2]) - - def _init_profile(self, config_section, name): - version = config_section.getint("pid_version", 1) - if version != PID_PROFILE_VERSION: - logging.info( - "Profile [%s] not compatible with this version " - "of pid_profile.\n" - "Profile Version: %d Current Version: %d" - % (name, version, PID_PROFILE_VERSION) - ) - self.incompatible_profiles.append(name) - return None - temp_profile = {} - control = self._check_value_config( - "control", config_section, str, False - ) - if control == "watermark": - temp_profile["max_delta"] = config_section.getfloat( - "max_delta", 2.0, above=0.0 - ) - elif control == "pid" or control == "pid_v": - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): - can_be_none = ( - key != "pid_kp" and key != "pid_ki" and key != "pid_kd" - ) - temp_profile[key] = self._check_value_config( - key, config_section, type, can_be_none - ) - if name == "default": - temp_profile["smooth_time"] = None - else: - raise self.outer_instance.printer.config_error( - "Unknown control type '%s' " - "in [pid_profile %s %s]." - % (control, self.outer_instance.name, name) - ) - temp_profile["control"] = control - temp_profile["name"] = name - self.profiles[name] = temp_profile - return temp_profile - - def _check_value_config(self, key, config_section, type, can_be_none): - if type is int: - value = config_section.getint(key, None) - elif type is float: - value = config_section.getfloat(key, None) - else: - value = config_section.get(key, None) - if not can_be_none and value is None: - raise self.outer_instance.gcode.error( - "pid_profile: '%s' has to be " - "specified in [pid_profile %s %s]." - % (key, self.outer_instance.name, config_section.get_name()) - ) - return value - - def _compute_section_name(self, profile_name): - return ( - self.outer_instance.name - if profile_name == "default" - else ( - "pid_profile " - + self.outer_instance.name - + " " - + profile_name - ) - ) - - def _check_value_gcmd( - self, - name, - default, - gcmd, - type, - can_be_none, - minval=None, - maxval=None, - ): - if type is int: - value = gcmd.get_int( - name, default, minval=minval, maxval=maxval - ) - elif type is float: - value = gcmd.get_float( - name, default, minval=minval, maxval=maxval - ) - else: - value = gcmd.get(name, default) - if not can_be_none and value is None: - raise self.outer_instance.gcode.error( - "pid_profile: '%s' has to be specified." % name - ) - return value.lower() if type == "lower" else value - - def init_default_profile(self): - return self._init_profile(self.outer_instance.config, "default") - - def set_values(self, profile_name, gcmd, verbose): - current_profile = self.outer_instance.get_control().get_profile() - target = self._check_value_gcmd("TARGET", None, gcmd, float, False) - tolerance = self._check_value_gcmd( - "TOLERANCE", - current_profile["pid_tolerance"], - gcmd, - float, - False, - ) - control = self._check_value_gcmd( - "CONTROL", current_profile["control"], gcmd, "lower", False - ) - kp = self._check_value_gcmd("KP", None, gcmd, float, False) - ki = self._check_value_gcmd("KI", None, gcmd, float, False) - kd = self._check_value_gcmd("KD", None, gcmd, float, False) - smooth_time = self._check_value_gcmd( - "SMOOTH_TIME", None, gcmd, float, True - ) - keep_target = self._check_value_gcmd( - "KEEP_TARGET", 0, gcmd, int, True, minval=0, maxval=1 - ) - load_clean = self._check_value_gcmd( - "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 - ) - temp_profile = { - "pid_target": target, - "pid_tolerance": tolerance, - "control": control, - "smooth_time": smooth_time, - "pid_kp": kp, - "pid_ki": ki, - "pid_kd": kd, - } - temp_control = self.outer_instance.lookup_control( - temp_profile, load_clean - ) - self.outer_instance.set_control(temp_control, keep_target) - msg = ( - "PID Parameters:\n" - "Target: %.2f,\n" - "Tolerance: %.4f\n" - "Control: %s\n" % (target, tolerance, control) - ) - if smooth_time is not None: - msg += "Smooth Time: %.3f\n" % smooth_time - msg += ( - "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - "have been set as current profile." % (kp, ki, kd) - ) - self.outer_instance.gcode.respond_info(msg) - self.save_profile(profile_name=profile_name, verbose=True) - - def get_values(self, profile_name, gcmd, verbose): - temp_profile = self.outer_instance.get_control().get_profile() - target = temp_profile["pid_target"] - tolerance = temp_profile["pid_tolerance"] - control = temp_profile["control"] - kp = temp_profile["pid_kp"] - ki = temp_profile["pid_ki"] - kd = temp_profile["pid_kd"] - smooth_time = ( - self.outer_instance.get_smooth_time() - if temp_profile["smooth_time"] is None - else temp_profile["smooth_time"] - ) - name = temp_profile["name"] - self.outer_instance.gcode.respond_info( - "PID Parameters:\n" - "Target: %.2f,\n" - "Tolerance: %.4f\n" - "Control: %s\n" - "Smooth Time: %.3f\n" - "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - "name: %s" - % (target, tolerance, control, smooth_time, kp, ki, kd, name) - ) - - def save_profile(self, profile_name=None, gcmd=None, verbose=True): - temp_profile = self.outer_instance.get_control().get_profile() - if profile_name is None: - profile_name = temp_profile["name"] - section_name = self._compute_section_name(profile_name) - self.outer_instance.configfile.set( - section_name, "pid_version", PID_PROFILE_VERSION - ) - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): - value = temp_profile[key] - if value is not None: - self.outer_instance.configfile.set( - section_name, key, placeholder % value - ) - temp_profile["name"] = profile_name - self.profiles[profile_name] = temp_profile - if verbose: - self.outer_instance.gcode.respond_info( - "Current PID profile for heater [%s] " - "has been saved to profile [%s] " - "for the current session. The SAVE_CONFIG command will\n" - "update the printer config file and restart the printer." - % (self.outer_instance.name, profile_name) - ) - - def load_profile(self, profile_name, gcmd, verbose): - verbose = self._check_value_gcmd( - "VERBOSE", "low", gcmd, "lower", True - ) - load_clean = self._check_value_gcmd( - "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 - ) - if ( - profile_name - == self.outer_instance.get_control().get_profile()["name"] - and not load_clean - ): - if verbose == "high" or verbose == "low": - self.outer_instance.gcode.respond_info( - "PID Profile [%s] already loaded for heater [%s]." - % (profile_name, self.outer_instance.name) - ) - return - keep_target = self._check_value_gcmd( - "KEEP_TARGET", 0, gcmd, int, True, minval=0, maxval=1 - ) - profile = self.profiles.get(profile_name, None) - defaulted = False - default = gcmd.get("DEFAULT", None) - if profile is None: - if default is None: - raise self.outer_instance.gcode.error( - "pid_profile: Unknown profile [%s] for heater [%s]." - % (profile_name, self.outer_instance.name) - ) - profile = self.profiles.get(default, None) - defaulted = True - if profile is None: - raise self.outer_instance.gcode.error( - "pid_profile: Unknown default " - "profile [%s] for heater [%s]." - % (default, self.outer_instance.name) - ) - control = self.outer_instance.lookup_control(profile, load_clean) - self.outer_instance.set_control(control, keep_target) - - if verbose != "high" and verbose != "low": - return - if defaulted: - self.outer_instance.gcode.respond_info( - "Couldn't find profile " - "[%s] for heater [%s]" - ", defaulted to [%s]." - % (profile_name, self.outer_instance.name, default) - ) - self.outer_instance.gcode.respond_info( - "PID Profile [%s] loaded for heater [%s].\n" - % (profile["name"], self.outer_instance.name) - ) - if verbose == "high": - smooth_time = ( - self.outer_instance.get_smooth_time() - if profile["smooth_time"] is None - else profile["smooth_time"] - ) - msg = ( - "Target: %.2f\n" - "Tolerance: %.4f\n" - "Control: %s\n" - % ( - profile["pid_target"], - profile["pid_tolerance"], - profile["control"], - ) - ) - if smooth_time is not None: - msg += "Smooth Time: %.3f\n" % smooth_time - msg += ( - "PID Parameters: pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - % (profile["pid_kp"], profile["pid_ki"], profile["pid_kd"]) - ) - self.outer_instance.gcode.respond_info(msg) - - def remove_profile(self, profile_name, gcmd, verbose): - if profile_name in self.profiles: - section_name = self._compute_section_name(profile_name) - self.outer_instance.configfile.remove_section(section_name) - profiles = dict(self.profiles) - del profiles[profile_name] - self.profiles = profiles - self.outer_instance.gcode.respond_info( - "Profile [%s] for heater [%s] " - "removed from storage for this session.\n" - "The SAVE_CONFIG command will update the printer\n" - "configuration and restart the printer" - % (profile_name, self.outer_instance.name) - ) - else: - self.outer_instance.gcode.respond_info( - "No profile named [%s] to remove" % profile_name - ) - - cmd_PID_PROFILE_help = "PID Profile Persistent Storage management" - - def cmd_PID_PROFILE(self, gcmd): - options = collections.OrderedDict( - { - "LOAD": self.load_profile, - "SAVE": self.save_profile, - "GET_VALUES": self.get_values, - "SET_VALUES": self.set_values, - "REMOVE": self.remove_profile, - } - ) - for key in options: - profile_name = gcmd.get(key, None) - if profile_name is not None: - if not profile_name.strip(): - raise self.outer_instance.gcode.error( - "pid_profile: Profile must be specified" - ) - options[key](profile_name, gcmd, True) - return - raise self.outer_instance.gcode.error( - "pid_profile: Invalid syntax '%s'" % (gcmd.get_commandline(),) - ) - ###################################################################### # Bang-bang control algo @@ -608,11 +213,10 @@ def cmd_PID_PROFILE(self, gcmd): class ControlBangBang: - def __init__(self, profile, heater, load_clean=False): - self.profile = profile + def __init__(self, heater, config): self.heater = heater self.heater_max_power = heater.get_max_power() - self.max_delta = profile["max_delta"] + self.max_delta = config.getfloat("max_delta", 2.0, above=0.0) self.heating = False def temperature_update(self, read_time, temp, target_temp): @@ -628,12 +232,6 @@ def temperature_update(self, read_time, temp, target_temp): def check_busy(self, eventtime, smoothed_temp, target_temp): return smoothed_temp < target_temp - self.max_delta - def update_smooth_time(self): - self.smooth_time = self.heater.get_smooth_time() # smoothing window - - def get_profile(self): - return self.profile - def get_type(self): return "watermark" @@ -647,27 +245,17 @@ def get_type(self): class ControlPID: - def __init__(self, profile, heater, load_clean=False): - self.profile = profile + def __init__(self, heater, config): self.heater = heater self.heater_max_power = heater.get_max_power() - self.Kp = profile["pid_kp"] / PID_PARAM_BASE - self.Ki = profile["pid_ki"] / PID_PARAM_BASE - self.Kd = profile["pid_kd"] / PID_PARAM_BASE - self.min_deriv_time = ( - self.heater.get_smooth_time() - if profile["smooth_time"] is None - else profile["smooth_time"] - ) - self.heater.set_inv_smooth_time(1.0 / self.min_deriv_time) + self.Kp = config.getfloat("pid_Kp") / PID_PARAM_BASE + self.Ki = config.getfloat("pid_Ki") / PID_PARAM_BASE + self.Kd = config.getfloat("pid_Kd") / PID_PARAM_BASE + self.min_deriv_time = heater.get_smooth_time() self.temp_integ_max = 0.0 if self.Ki: self.temp_integ_max = self.heater_max_power / self.Ki - self.prev_temp = ( - AMBIENT_TEMP - if load_clean - else self.heater.get_temp(self.heater.reactor.monotonic())[0] - ) + self.prev_temp = AMBIENT_TEMP self.prev_temp_time = 0.0 self.prev_temp_deriv = 0.0 self.prev_temp_integ = 0.0 @@ -707,12 +295,6 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): or abs(self.prev_temp_deriv) > PID_SETTLE_SLOPE ) - def update_smooth_time(self): - self.smooth_time = self.heater.get_smooth_time() # smoothing window - - def get_profile(self): - return self.profile - def get_type(self): return "pid" @@ -723,31 +305,18 @@ def get_type(self): class ControlVelocityPID: - def __init__(self, profile, heater, load_clean=False): - self.profile = profile + def __init__(self, heater, config): self.heater = heater self.heater_max_power = heater.get_max_power() - self.Kp = profile["pid_kp"] / PID_PARAM_BASE - self.Ki = profile["pid_ki"] / PID_PARAM_BASE - self.Kd = profile["pid_kd"] / PID_PARAM_BASE - smooth_time = ( - self.heater.get_smooth_time() - if profile["smooth_time"] is None - else profile["smooth_time"] - ) - self.heater.set_inv_smooth_time(1.0 / smooth_time) - self.smooth_time = smooth_time # smoothing window - self.temps = ( - ([AMBIENT_TEMP] * 3) - if load_clean - else ( - [self.heater.get_temp(self.heater.reactor.monotonic())[0]] * 3 - ) - ) + self.Kp = config.getfloat("pid_Kp") / PID_PARAM_BASE + self.Ki = config.getfloat("pid_Ki") / PID_PARAM_BASE + self.Kd = config.getfloat("pid_Kd") / PID_PARAM_BASE + self.smooth_time = heater.get_smooth_time() # smoothing window + self.temps = [AMBIENT_TEMP] * 3 # temperature readings self.times = [0.0] * 3 # temperature reading times self.d1 = 0.0 # previous smoothed 1st derivative self.d2 = 0.0 # previous smoothed 2nd derivative - self.pwm = 0.0 if load_clean else self.heater.last_pwm_value + self.pwm = 0.0 # the previous pwm setting def temperature_update(self, read_time, temp, target_temp): # update the temp and time lists @@ -795,12 +364,6 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): abs(temp_diff) > PID_SETTLE_DELTA or abs(self.d1) > PID_SETTLE_SLOPE ) - def update_smooth_time(self): - self.smooth_time = self.heater.get_smooth_time() # smoothing window - - def get_profile(self): - return self.profile - def get_type(self): return "pid_v" diff --git a/klippy/extras/pid_calibrate.py b/klippy/extras/pid_calibrate.py index d526aa491..138da4fcd 100644 --- a/klippy/extras/pid_calibrate.py +++ b/klippy/extras/pid_calibrate.py @@ -24,7 +24,6 @@ def cmd_PID_CALIBRATE(self, gcmd): target = gcmd.get_float("TARGET") write_file = gcmd.get_int("WRITE_FILE", 0) tolerance = gcmd.get_float("TOLERANCE", TUNE_PID_TOL, above=0.0) - profile_name = gcmd.get("PROFILE", "default") pheaters = self.printer.lookup_object("heaters") try: heater = pheaters.lookup_heater(heater_name) @@ -32,13 +31,13 @@ def cmd_PID_CALIBRATE(self, gcmd): raise gcmd.error(str(e)) self.printer.lookup_object("toolhead").get_last_move_time() calibrate = ControlAutoTune(heater, target, tolerance) - old_control = heater.set_control(calibrate, False) + old_control = heater.set_control(calibrate) try: pheaters.set_temperature(heater, target, True) except self.printer.command_error as e: - heater.set_control(old_control, False) + heater.set_control(old_control) raise - heater.set_control(old_control, False) + heater.set_control(old_control) if write_file: calibrate.write_file("/tmp/heattest.csv") if calibrate.check_busy(0.0, 0.0, 0.0): @@ -47,28 +46,17 @@ def cmd_PID_CALIBRATE(self, gcmd): Kp, Ki, Kd = calibrate.calc_pid() logging.info("Autotune: final: Kp=%f Ki=%f Kd=%f", Kp, Ki, Kd) gcmd.respond_info( - "PID parameters for %.2f\xb0C: " - "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - "Heater: %s\n" - "Tolerance: %.4f\n" - "Profile: %s" - % (target, Kp, Ki, Kd, heater_name, tolerance, profile_name) + "PID parameters: pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" + "The SAVE_CONFIG command will update the printer config file\n" + "with these parameters and restart the printer." % (Kp, Ki, Kd) ) + # Store results for SAVE_CONFIG + configfile = self.printer.lookup_object("configfile") control = "pid_v" if old_control.get_type() == "pid_v" else "pid" - - profile = { - "pid_target": target, - "pid_tolerance": tolerance, - "control": control, - "pid_kp": Kp, - "pid_ki": Ki, - "pid_kd": Kd, - "smooth_time": None, - "name": profile_name, - } - - heater.set_control(heater.lookup_control(profile, True), False) - heater.pmgr.save_profile(profile_name=profile_name, verbose=False) + configfile.set(heater_name, "control", control) + configfile.set(heater_name, "pid_Kp", "%.3f" % (Kp,)) + configfile.set(heater_name, "pid_Ki", "%.3f" % (Ki,)) + configfile.set(heater_name, "pid_Kd", "%.3f" % (Kd,)) TUNE_PID_DELTA = 5.0 diff --git a/test/klippy/pid_profile.cfg b/test/klippy/pid_profile.cfg deleted file mode 100644 index 26cc2d636..000000000 --- a/test/klippy/pid_profile.cfg +++ /dev/null @@ -1,75 +0,0 @@ -# Config for extruder testing -[stepper_x] -step_pin: PF0 -dir_pin: PF1 -enable_pin: !PD7 -microsteps: 16 -rotation_distance: 40 -endstop_pin: ^PE5 -position_endstop: 0 -position_max: 200 -homing_speed: 50 - -[stepper_y] -step_pin: PF6 -dir_pin: !PF7 -enable_pin: !PF2 -microsteps: 16 -rotation_distance: 40 -endstop_pin: ^PJ1 -position_endstop: 0 -position_max: 200 -homing_speed: 50 - -[stepper_z] -step_pin: PL3 -dir_pin: PL1 -enable_pin: !PK0 -microsteps: 16 -rotation_distance: 8 -endstop_pin: ^PD3 -position_endstop: 0.5 -position_max: 200 - -[extruder] -step_pin: PA4 -dir_pin: PA6 -enable_pin: !PA2 -microsteps: 16 -rotation_distance: 33.5 -nozzle_diameter: 0.500 -filament_diameter: 3.500 -heater_pin: PB4 -sensor_type: EPCOS 100K B57560G104F -sensor_pin: PK5 -control: pid -pid_Kp: 22.2 -pid_Ki: 1.08 -pid_Kd: 114 -min_temp: 0 -max_temp: 210 - -[extruder_stepper my_extra_stepper] -extruder: extruder -step_pin: PH5 -dir_pin: PH6 -enable_pin: !PB5 -microsteps: 16 -rotation_distance: 28.2 - -[mcu] -serial: /dev/ttyACM0 - -[printer] -kinematics: cartesian -max_velocity: 300 -max_accel: 3000 -max_z_velocity: 5 -max_z_accel: 100 - -[pid_profile extruder FROM_CONFIG] -pid_version: 1 -control: pid -pid_kp: 22.200 -pid_ki: 1.080 -pid_kd: 114.000 diff --git a/test/klippy/pid_profile.test b/test/klippy/pid_profile.test deleted file mode 100644 index 7962fe6f7..000000000 --- a/test/klippy/pid_profile.test +++ /dev/null @@ -1,26 +0,0 @@ -# Pid hot modify tests -DICTIONARY atmega2560.dict -CONFIG pid_profile.cfg - -# Extrude only -G1 E5 -G1 E-2 -G1 E7 - -# Home and extrusion moves -G28 -G1 X20 Y20 Z1 -G1 X25 Y25 E7.5 - - -# create a new profile and save it -PID_PROFILE SAVE=TESTPROFILE HEATER=extruder -PID_PROFILE LOAD=TESTPROFILE HEATER=extruder - -# set the smooth time and save to the profile -SET_SMOOTH_TIME HEATER=extruder SMOOTH_TIME=1 SAVE_TO_PROFILE=1 - -# remove an existing profile -PID_PROFILE REMOVE=FROM_CONFIG HEATER=extruder - -# PID_PROFILE SET_VALUES cant be tested