klipper icon indicating copy to clipboard operation
klipper copied to clipboard

RFC: Cooperative Heaters predictive control

Open nefelim4ag opened this issue 9 months ago • 8 comments

Some time ago I dug heaters a little on the Discourse

I don't think PID is bad or needs to be replaced. I tried to do some fun stuff, like train DNN or write my own algo to control the power. In the end, everything looks the same.

There are historically various topics, that try to replace it or tune it differently. I think the only practical reason is to handle something important for that human being.

So, there is an ultimate solution, I think. Pid is still under the control of a heater and is able to drive it. The end-user has the ability to adjust it freely, like in my doc example - drive PWM in a feedforward way if the fan is suddenly enabled, like for bridges.

If it is needed, it is definitely possible to get the current chamber temperature, adjust to the fan curve, try to parse the extruder moving queue (IDK) and drive PWM higher before the temperature falls and PID will reactively compensate for that. I hope that will add enough flexibility to stop rituals like "how to do PID autotune properly".

Another example, for some reason people do not insulate the heating beds, adding thermistors inside a thick aluminum plate and then want sort of PID control by two thermistors, and feel that combined sensor is not good enough.

Well, now it is possible to literally query the sensors and decide what to do, like return the -N value and decrease bed heater power on demand.

I hope it is made in a way to is safe and acceptable, leaves all existing safety measures in place, and mostly does not touch safety-critical code.

Thanks.


Btw, I will try to add templates to temperature FANs in a similar way, to fit most of the wishes, like curves, formulas & etc.

nefelim4ag avatar Feb 25 '25 18:02 nefelim4ag

Nice! It seems to follow the same logic as display templates. Maybe stick to the general wording and naming from there?

Sineos avatar Feb 26 '25 06:02 Sineos

Maybe stick to the general wording and naming from there?

I imported some changes from there, and I hope, now it is a little better.

nefelim4ag avatar Feb 27 '25 00:02 nefelim4ag

From the eyes emoticon, I gather you found my comment confusing. What I tried to say:

The display template functions in Klipper for LEDs and fans (example) seem to be using the same overarching logic, albeit with a bit of different nomenclature. Maybe I'm misunderstanding something?

Sineos avatar Feb 27 '25 07:02 Sineos

The display template functions in Klipper for LEDs and fans (example) seem to be using the same overarching logic, albeit with a bit of different nomenclature.

I got the idea of what you meant after your comment in the temperature_fan PR. I'm looking for what can be done to make it similar to current integration with `[display_templates]'. The current integration feels a little unintuitive from my perspective, but it is better to reuse it, of course.

Thanks for checking. I was totally unaware of that functionality, now I will try to wrap my head around it.

nefelim4ag avatar Feb 27 '25 19:02 nefelim4ag

I don't know how I feel about adding a G-code command to reload the template or directly alter the PWM output. I also think it is wrong to add custom value get/set/log functions to all templates by incorporating them in display_template. So, I dropped them for now.

So, for now, I only leave here a bare config binding between template and heater. If it is needed, technically, temperature and read time can be propagated in the template context. But I think they are mostly useless without state management inside macro.

Thanks.

nefelim4ag avatar Feb 27 '25 23:02 nefelim4ag

Interesting. I'm not sure this implementation is safe though, as the PID temperature_update() code is called from a background thread (serialhdl:_bg_thread()) and I don't know what would happen if one tried to render templates from that thread. Certainly if the template accessed any of the {printer.module.xxx} values it would result in get_status() calls from the background thread which would not be safe.

I suspect we could alter the target temperature from templates without thread issues (or set a temperature offset), but I'm not sure if that's what you are looking for.

Still interesting though. -Kevin

KevinOConnor avatar Mar 07 '25 23:03 KevinOConnor

code is called from a background thread (serialhdl:_bg_thread()) and I don't know what would happen if one tried to render templates from that thread

I will change it to run in a similar fashion to current fan/led templates in the main/reactor thread. And pass shared value to offset PWM. It seems simpler to do locks with one shared value. The only tricky part would be to run it close before PWM calculation to make it semi-synchronized. Maybe use the last PWM evaluation time and schedule the timer in <300ms after that. Even though it will mostly retain the suggested functionality.

I suspect we could alter the target temperature from templates without thread issues (or set a temperature offset), but I'm not sure if that's what you are looking for.

AFAIK, it is only useful if the target is to use variable extrusion temperature based on the flow. AFAIK, there is at least one project for that: https://github.com/sb53systems/G-Code-Flow-Temperature-Controller

It can be made on the Klipper side, but I suspect it is requires different design to trigger temperature changes ahead and do other stuff like accounting for thermal inertia/slow down printing. So, I suspect it is easier to do as an external extension/gcode preprocessor.

Thanks.

nefelim4ag avatar Mar 09 '25 19:03 nefelim4ag

Thank you for your contribution to Klipper. Unfortunately, a reviewer has not assigned themselves to this GitHub Pull Request. All Pull Requests are reviewed before merging, and a reviewer will need to volunteer. Further information is available at: https://www.klipper3d.org/CONTRIBUTING.html

There are some steps that you can take now:

  1. Perform a self-review of your Pull Request by following the steps at: https://www.klipper3d.org/CONTRIBUTING.html#what-to-expect-in-a-review If you have completed a self-review, be sure to state the results of that self-review explicitly in the Pull Request comments. A reviewer is more likely to participate if the bulk of a review has already been completed.
  2. Consider opening a topic on the Klipper Discourse server to discuss this work. The Discourse server is a good place to discuss development ideas and to engage users interested in testing. Reviewers are more likely to prioritize Pull Requests with an active community of users.
  3. Consider helping out reviewers by reviewing other Klipper Pull Requests. Taking the time to perform a careful and detailed review of others work is appreciated. Regular contributors are more likely to prioritize the contributions of other regular contributors.

Unfortunately, if a reviewer does not assign themselves to this GitHub Pull Request then it will be automatically closed. If this happens, then it is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.

Best regards, ~ Your friendly GitIssueBot

PS: I'm just an automated script, not a human being.

github-actions[bot] avatar Mar 24 '25 00:03 github-actions[bot]

I realized there is no example, so this one is for the extruder heater with accounting for fan losses and the losses due to the volumetric flow.

[display_template extruder]
text:
  {% set pwr_adj = printer.fan.speed * 0.15 %}
  {% set flow = printer.motion_report.live_extruder_velocity * (1.75 / 2)**2 * 3.1415 %}
  # 25 is around max flow, a simple, linear equation
  # 25 mm^3/s requires at least +40% of PWM
  { pwr_adj + flow/25 * 0.4 }

[heater_pc extruder]
macro_template: extruder

nefelim4ag avatar Jul 12 '25 14:07 nefelim4ag

Example of 2 termistors bed control to limit the bed heater temperature. https://klipper.discourse.group/t/thick-bed-temperature-controll/24940/48

[display_template bed_limiter]
text:
  {% set bed_heater_temp = printer['temperature_sensor bed_heater'].temperature | float %}
  {% set max_temp = 100 %}
  {% set range = 5 %}
  {% set proportional_damping = (bed_heater_temp - (max_temp - range / 2))/range %}
  {0 if proportional_damping < 0 else proportional_damping * -1 }

[heater_pc heater_bed]
macro_template: bed_limiter

This is my example of reducing the chamber heater power until the heater bed is done to heat, to limit the peak power.

[display_template chamber]
text:
  { -1.0 * printer["heater_bed"].power }

[heater_pc chamber]
macro_template: chamber

~~There is an error with how I rescheduled the timer before the PWM code, so the template runs several times before the next PWM arrives. I will fix that.~~ Fixed.

nefelim4ag avatar Oct 09 '25 19:10 nefelim4ag