diff options
Diffstat (limited to 'klippy/extras/temperature_fan.py')
-rw-r--r-- | klippy/extras/temperature_fan.py | 153 |
1 files changed, 90 insertions, 63 deletions
diff --git a/klippy/extras/temperature_fan.py b/klippy/extras/temperature_fan.py index a9aa4d0b..8ea3808f 100644 --- a/klippy/extras/temperature_fan.py +++ b/klippy/extras/temperature_fan.py @@ -7,85 +7,100 @@ from . import fan KELVIN_TO_CELSIUS = -273.15 MAX_FAN_TIME = 5.0 -AMBIENT_TEMP = 25. -PID_PARAM_BASE = 255. +AMBIENT_TEMP = 25.0 +PID_PARAM_BASE = 255.0 + class TemperatureFan: def __init__(self, config): self.name = config.get_name().split()[1] self.printer = config.get_printer() - self.fan = fan.Fan(config, default_shutdown_speed=1.) - self.min_temp = config.getfloat('min_temp', minval=KELVIN_TO_CELSIUS) - self.max_temp = config.getfloat('max_temp', above=self.min_temp) - pheaters = self.printer.load_object(config, 'heaters') + self.fan = fan.Fan(config, default_shutdown_speed=1.0) + self.min_temp = config.getfloat("min_temp", minval=KELVIN_TO_CELSIUS) + self.max_temp = config.getfloat("max_temp", above=self.min_temp) + pheaters = self.printer.load_object(config, "heaters") self.sensor = pheaters.setup_sensor(config) self.sensor.setup_minmax(self.min_temp, self.max_temp) self.sensor.setup_callback(self.temperature_callback) pheaters.register_sensor(config, self) self.speed_delay = self.sensor.get_report_time_delta() - self.max_speed_conf = config.getfloat( - 'max_speed', 1., above=0., maxval=1.) + self.max_speed_conf = config.getfloat("max_speed", 1.0, above=0.0, maxval=1.0) self.max_speed = self.max_speed_conf - self.min_speed_conf = config.getfloat( - 'min_speed', 0.3, minval=0., maxval=1.) + self.min_speed_conf = config.getfloat("min_speed", 0.3, minval=0.0, maxval=1.0) self.min_speed = self.min_speed_conf - self.last_temp = 0. - self.last_temp_time = 0. + self.last_temp = 0.0 + self.last_temp_time = 0.0 self.target_temp_conf = config.getfloat( - 'target_temp', 40. if self.max_temp > 40. else self.max_temp, - minval=self.min_temp, maxval=self.max_temp) + "target_temp", + 40.0 if self.max_temp > 40.0 else self.max_temp, + minval=self.min_temp, + maxval=self.max_temp, + ) self.target_temp = self.target_temp_conf - algos = {'watermark': ControlBangBang, 'pid': ControlPID} - algo = config.getchoice('control', algos) + algos = {"watermark": ControlBangBang, "pid": ControlPID} + algo = config.getchoice("control", algos) self.control = algo(self, config) - self.next_speed_time = 0. - self.last_speed_value = 0. - gcode = self.printer.lookup_object('gcode') + self.next_speed_time = 0.0 + self.last_speed_value = 0.0 + gcode = self.printer.lookup_object("gcode") gcode.register_mux_command( - "SET_TEMPERATURE_FAN_TARGET", "TEMPERATURE_FAN", self.name, + "SET_TEMPERATURE_FAN_TARGET", + "TEMPERATURE_FAN", + self.name, self.cmd_SET_TEMPERATURE_FAN_TARGET, - desc=self.cmd_SET_TEMPERATURE_FAN_TARGET_help) + desc=self.cmd_SET_TEMPERATURE_FAN_TARGET_help, + ) def set_tf_speed(self, read_time, value): - if value <= 0.: - value = 0. + if value <= 0.0: + value = 0.0 elif value < self.min_speed: value = self.min_speed - if self.target_temp <= 0.: - value = 0. - if ((read_time < self.next_speed_time or not self.last_speed_value) - and abs(value - self.last_speed_value) < 0.05): + if self.target_temp <= 0.0: + value = 0.0 + if (read_time < self.next_speed_time or not self.last_speed_value) and abs( + value - self.last_speed_value + ) < 0.05: # No significant change in value - can suppress update return speed_time = read_time + self.speed_delay self.next_speed_time = speed_time + 0.75 * MAX_FAN_TIME self.last_speed_value = value self.fan.set_speed(value, speed_time) + def temperature_callback(self, read_time, temp): self.last_temp = temp self.control.temperature_callback(read_time, temp) + def get_temp(self, eventtime): return self.last_temp, self.target_temp + def get_min_speed(self): return self.min_speed + def get_max_speed(self): return self.max_speed + def get_status(self, eventtime): status = self.fan.get_status(eventtime) status["temperature"] = round(self.last_temp, 2) status["target"] = self.target_temp return status - cmd_SET_TEMPERATURE_FAN_TARGET_help = \ + + cmd_SET_TEMPERATURE_FAN_TARGET_help = ( "Sets a temperature fan target and fan speed limits" + ) + def cmd_SET_TEMPERATURE_FAN_TARGET(self, gcmd): - temp = gcmd.get_float('TARGET', self.target_temp_conf) + temp = gcmd.get_float("TARGET", self.target_temp_conf) self.set_temp(temp) - min_speed = gcmd.get_float('MIN_SPEED', self.min_speed) - max_speed = gcmd.get_float('MAX_SPEED', self.max_speed) + min_speed = gcmd.get_float("MIN_SPEED", self.min_speed) + max_speed = gcmd.get_float("MAX_SPEED", self.max_speed) if min_speed > max_speed: raise self.printer.command_error( "Requested min speed (%.1f) is greater than max speed (%.1f)" - % (min_speed, max_speed)) + % (min_speed, max_speed) + ) self.set_min_speed(min_speed) self.set_max_speed(max_speed) @@ -93,67 +108,73 @@ class TemperatureFan: if degrees and (degrees < self.min_temp or degrees > self.max_temp): raise self.printer.command_error( "Requested temperature (%.1f) out of range (%.1f:%.1f)" - % (degrees, self.min_temp, self.max_temp)) + % (degrees, self.min_temp, self.max_temp) + ) self.target_temp = degrees def set_min_speed(self, speed): - if speed and (speed < 0. or speed > 1.): + if speed and (speed < 0.0 or speed > 1.0): raise self.printer.command_error( - "Requested min speed (%.1f) out of range (0.0 : 1.0)" - % (speed)) + "Requested min speed (%.1f) out of range (0.0 : 1.0)" % (speed) + ) self.min_speed = speed def set_max_speed(self, speed): - if speed and (speed < 0. or speed > 1.): + if speed and (speed < 0.0 or speed > 1.0): raise self.printer.command_error( - "Requested max speed (%.1f) out of range (0.0 : 1.0)" - % (speed)) + "Requested max speed (%.1f) out of range (0.0 : 1.0)" % (speed) + ) self.max_speed = speed + ###################################################################### # Bang-bang control algo ###################################################################### + class ControlBangBang: def __init__(self, temperature_fan, config): self.temperature_fan = temperature_fan - self.max_delta = config.getfloat('max_delta', 2.0, above=0.) + self.max_delta = config.getfloat("max_delta", 2.0, above=0.0) self.heating = False + def temperature_callback(self, read_time, temp): current_temp, target_temp = self.temperature_fan.get_temp(read_time) - if (self.heating - and temp >= target_temp+self.max_delta): + if self.heating and temp >= target_temp + self.max_delta: self.heating = False - elif (not self.heating - and temp <= target_temp-self.max_delta): + elif not self.heating and temp <= target_temp - self.max_delta: self.heating = True if self.heating: - self.temperature_fan.set_tf_speed(read_time, 0.) + self.temperature_fan.set_tf_speed(read_time, 0.0) else: self.temperature_fan.set_tf_speed( - read_time, self.temperature_fan.get_max_speed()) + read_time, self.temperature_fan.get_max_speed() + ) + ###################################################################### # Proportional Integral Derivative (PID) control algo ###################################################################### -PID_SETTLE_DELTA = 1. -PID_SETTLE_SLOPE = .1 +PID_SETTLE_DELTA = 1.0 +PID_SETTLE_SLOPE = 0.1 + class ControlPID: def __init__(self, temperature_fan, config): self.temperature_fan = temperature_fan - 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 = config.getfloat('pid_deriv_time', 2., above=0.) - self.temp_integ_max = 0. + 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 = config.getfloat("pid_deriv_time", 2.0, above=0.0) + self.temp_integ_max = 0.0 if self.Ki: self.temp_integ_max = self.temperature_fan.get_max_speed() / self.Ki self.prev_temp = AMBIENT_TEMP - self.prev_temp_time = 0. - self.prev_temp_deriv = 0. - self.prev_temp_integ = 0. + self.prev_temp_time = 0.0 + self.prev_temp_deriv = 0.0 + self.prev_temp_integ = 0.0 + def temperature_callback(self, read_time, temp): current_temp, target_temp = self.temperature_fan.get_temp(read_time) time_diff = read_time - self.prev_temp_time @@ -162,18 +183,23 @@ class ControlPID: if time_diff >= self.min_deriv_time: temp_deriv = temp_diff / time_diff else: - temp_deriv = (self.prev_temp_deriv * (self.min_deriv_time-time_diff) - + temp_diff) / self.min_deriv_time + temp_deriv = ( + self.prev_temp_deriv * (self.min_deriv_time - time_diff) + temp_diff + ) / self.min_deriv_time # Calculate accumulated temperature "error" temp_err = target_temp - temp temp_integ = self.prev_temp_integ + temp_err * time_diff - temp_integ = max(0., min(self.temp_integ_max, temp_integ)) + temp_integ = max(0.0, min(self.temp_integ_max, temp_integ)) # Calculate output - co = self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv - bounded_co = max(0., min(self.temperature_fan.get_max_speed(), co)) + co = self.Kp * temp_err + self.Ki * temp_integ - self.Kd * temp_deriv + bounded_co = max(0.0, min(self.temperature_fan.get_max_speed(), co)) self.temperature_fan.set_tf_speed( - read_time, max(self.temperature_fan.get_min_speed(), - self.temperature_fan.get_max_speed() - bounded_co)) + read_time, + max( + self.temperature_fan.get_min_speed(), + self.temperature_fan.get_max_speed() - bounded_co, + ), + ) # Store state for next measurement self.prev_temp = temp self.prev_temp_time = read_time @@ -181,5 +207,6 @@ class ControlPID: if co == bounded_co: self.prev_temp_integ = temp_integ + def load_config_prefix(config): return TemperatureFan(config) |