mirror of
https://github.com/Klipper3d/klipper.git
synced 2026-02-08 09:10:56 -07:00
Merge 2517b47061 into 6d7d3403e4
This commit is contained in:
commit
5b24759f9f
3 changed files with 92 additions and 0 deletions
|
|
@ -5515,6 +5515,17 @@ cs_pin:
|
|||
# above parameters.
|
||||
```
|
||||
|
||||
### [heater_pc]
|
||||
|
||||
Heater prediction correction.
|
||||
To use this feature, define a config section with a "heater_pc" prefix
|
||||
followed by the name of the corresponding heater config section.
|
||||
For example `[heater_pc heater_bed]`
|
||||
```
|
||||
[heater_pc extruder]
|
||||
#macro_template: <display_template's name>
|
||||
```
|
||||
|
||||
## Common bus parameters
|
||||
|
||||
### Common SPI settings
|
||||
|
|
|
|||
71
klippy/extras/heater_pc.py
Normal file
71
klippy/extras/heater_pc.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Klipper Heater Predictional Control
|
||||
#
|
||||
# Copyright (C) 2025 Timofey Titovets <nefelim4ag@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
import threading
|
||||
from .gcode_macro import PrinterGCodeMacro
|
||||
from .display import display
|
||||
|
||||
class HeaterPredictControl:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.reactor = self.printer.get_reactor()
|
||||
self.config = config
|
||||
self.eval_time = 0.3
|
||||
self.min_pwm = -1.0
|
||||
self.max_pwm = 1.0
|
||||
self.render_timer = None
|
||||
name_parts = config.get_name().split()
|
||||
if len(name_parts) != 2:
|
||||
raise config.error("Section name '%s' is not valid"
|
||||
% (config.get_name(),))
|
||||
# Use lock to pass data to/from heater code
|
||||
self.lock = threading.Lock()
|
||||
self.output = .0
|
||||
self.pwm_event_time = self.reactor.monotonic()
|
||||
# Link template
|
||||
template_name = config.get("macro_template")
|
||||
templates = display.lookup_display_templates(config)
|
||||
display_templates = templates.get_display_templates()
|
||||
self.create_context = PrinterGCodeMacro(config).create_template_context
|
||||
self.template = display_templates.get(template_name)
|
||||
self.printer.register_event_handler("klippy:connect",
|
||||
self.handle_connect)
|
||||
def handle_connect(self):
|
||||
pheaters = self.printer.load_object(self.config, 'heaters')
|
||||
heater_name = self.config.get_name().split()[-1]
|
||||
heater = pheaters.heaters.get(heater_name)
|
||||
if heater is None:
|
||||
self.config.error("Heater %s is not registered" % (heater_name))
|
||||
self.eval_time = heater.get_pwm_delay()
|
||||
self.min_pwm = -1.0 * heater.get_max_power()
|
||||
self.max_pwm = heater.get_max_power()
|
||||
reactor = self.reactor
|
||||
self.render_timer = reactor.register_timer(self._render, reactor.NOW)
|
||||
def callback():
|
||||
output = .0
|
||||
with self.lock:
|
||||
self.pwm_event_time = self.reactor.monotonic()
|
||||
output = self.output
|
||||
return output
|
||||
heater.set_pc_callback(callback)
|
||||
# Test template
|
||||
self._render(.0)
|
||||
def _render(self, eventtime):
|
||||
context = self.create_context()
|
||||
output = self.template.render(context)
|
||||
# Normalize output to PWM limits
|
||||
output_f = float(output) * self.max_pwm
|
||||
output_f = max(self.min_pwm, min(self.max_pwm, output_f))
|
||||
with self.lock:
|
||||
self.output = output_f
|
||||
last_pwm = self.pwm_event_time
|
||||
# if we lag behind - reschedule
|
||||
if eventtime < last_pwm + self.eval_time * 3 / 4:
|
||||
return last_pwm + self.eval_time * 3 / 4
|
||||
return eventtime + self.eval_time
|
||||
|
||||
def load_config_prefix(config):
|
||||
return HeaterPredictControl(config)
|
||||
|
|
@ -46,6 +46,8 @@ class Heater:
|
|||
# pwm caching
|
||||
self.next_pwm_time = 0.
|
||||
self.last_pwm_value = 0.
|
||||
# Predictive control callback
|
||||
self.pc_callback = self._pc_def_callback
|
||||
# Setup control algorithm sub-class
|
||||
algos = {'watermark': ControlBangBang, 'pid': ControlPID}
|
||||
algo = config.getchoice('control', algos)
|
||||
|
|
@ -93,6 +95,12 @@ class Heater:
|
|||
self.smoothed_temp += temp_diff * adj_time
|
||||
self.can_extrude = (self.smoothed_temp >= self.min_extrude_temp)
|
||||
#logging.debug("temp: %.3f %f = %f", read_time, temp)
|
||||
def _pc_def_callback(self):
|
||||
return .0
|
||||
def get_pc_correction(self):
|
||||
return self.pc_callback()
|
||||
def set_pc_callback(self, cb):
|
||||
self.pc_callback = cb
|
||||
def _handle_shutdown(self):
|
||||
self.verify_mainthread_time = -999.
|
||||
# External commands
|
||||
|
|
@ -217,6 +225,8 @@ class ControlPID:
|
|||
temp_integ = max(0., min(self.temp_integ_max, temp_integ))
|
||||
# Calculate output
|
||||
co = self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv
|
||||
co = max(-1.0 * self.heater_max_power, min(self.heater_max_power, co))
|
||||
co += self.heater.get_pc_correction()
|
||||
#logging.debug("pid: %f@%.3f -> diff=%f deriv=%f err=%f integ=%f co=%d",
|
||||
# temp, read_time, temp_diff, temp_deriv, temp_err, temp_integ, co)
|
||||
bounded_co = max(0., min(self.heater_max_power, co))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue