Dual Loop PID, a accurate bed surface temperature

Usually users who have big and thick beds have an offset between the bed surface and the thermistor temperature located on the heater, also they usually need to wait some time for the bed surface to reach the thermal equilibrium with the heater.

I opened a pull request with a dual loop PID.
This new algorithm uses two PID loops, one for controlling the heater and one for controlling the bed surface, it works using the minimum control values beetwen the both PID loops.
The main target for the heater will be always the bed surface, the maximum heater temperature is set on the heater section.

But how useful it is this feature, it can help most of users or a specific group?
You can see more details about this idea here: https://github.com/Klipper3d/klipper/pull/5972

Please, share your opinions and feedbacks

1 Like

This is a nice feature!
On my bigger printer I created a custom thermistor with values derived from a K type sensor attached onto the bed surface.

Hmmm… I can’t see the point for this feature. How do you want to control two target values with one control value? If you would have two heating devices, ok. But with only one output value…

A useful feature could be to split the thermal runout safety loop physically from the surface temperature, means to use two sensors (one close to the heater for runout protection, one closer on the surface for “real” temp control). But you can only use one PID if you have one heater. Else you get a feedback from “the other loop”…

Could you maybe draw a diagram, how the two PIDs should work? Maybe I don’t get the point.

Both PID are merged into a single signal, this control will not exceed the tempeature limit for the heater set in the configuration file when you set a desired temperature for the surface.
Because there is a huge delay between the curve of surface and the heater, if you control only the surface without controling the heater, you can exceed temperature limits for you heater.
If you check the pictures in the pull request, I think it will be clear to understand the idea

This is actually a fairly well established area of control theory - it’s called “MISO” (Multiple Input, Single Output) with the classical example being building heating. In a building, you have a furnace (or air conditioner or both) at different parts of the building and you want to keep the interior temperature at a set temperature without exceeding the limitations of the heating/cooling units.

There are lots of papers written on it like:

It probably is the “right” solution to the problem at hand (as a Voron 2.4 owner, I appreciate any possible solution to the problem of long bed heating times) but I don’t understand enough about the theory behind it to comment as to what is the right way to implement a MISO algorithm so that there is no chance of a thermal runaway but would still result in minimized bed heating times.

In keeping with my lack of understanding about the science/math behind MISO, before I would be comfortable implementing it on one of my systems, I’d like to see the establishment of an automated tool that finds/calculates the PID values rather than relying on the user to come up with them on their own.

I think @rmda is on the right track on this and I look forward to seeing it becoming part of the Klipper build.

OK, got that one. I really played around a lot with PI and PID controler, but never heared of MISO besides japanese soups. You learn every day! But mathematically, this is something like “if loop_temp1 > max_temp, controle_delta_loop_input1=0”)…?

So you want to limit the maximum temp of the heater and controle the surface temp of the plate and get rid of the manual limit 0,60 for “strong” heating devices? But what about overswinging? If you force a heat flow larger than the dissipation heat flow from the surface, you will get a (more or less) strong overswinging (because of dT/dt the gradient of the flow and the energy will not get lost somehow, if it reached the surface).

For my part: I don’t care too much about overswinging during heat up. Why should I? If so, there are simpler approaches to decrease heat up time.

Final “post signature”: If you will cover you aluminium plate with e.g. a magnetic sheet and a PEI sheet, how you measure the temp on the surface then? “Pa! Only some fractions of degrees?” :wink:

And: Of course I appreciate any tecnological improvement. This discussion is just a discussion, no critics!

I have worked a lot with classical SISO (single input single output i.e. “typical”) controllers both personally and professionally, but I don’t have a very deep understanding of MISO. Somebody who knows MISO well might have a more elegant solution than this, but this block diagram is how I would tackle the problem.

The outer loop is the typical heater PID but the thermistor has been replaced with the surface temp of the bed. Then you have a separate check on the heater thermistor. If the heater is above the thermal limit, then it reduces the control effort by that error * some factor K_lim so that the further it is above the thermal limit, the more it scrubs off of the control effort. If the heater is less than the thermal limit then nothing is done.

I guess you are doing MISO control, if I understood mykepredko’s attachment in the right way. For me is MISO like Berggipfel’s explanation.

I think you need to read the article I linked as well as other ones to put together a MISO implementation properly.

In your block diagram, you have a proportional temperature control of the heater with additional input from the surface thermistor. I’m concerned that you have positive feedback from the surface thermistor to the proportional scaling of the heater thermistor; this means that the heater will continue injecting energy into the system past the “Thermal Limit” as long as the Surface Thermistor is less than the set point. In terms of the heater bed itself, you’re going to have very poor temperature control with very temperature large swings - exactly what a PID is supposed to prevent.

Can I suggest that you look at something like Python Control Systems Library to develop and test a simulation of your system before you implement it to understand how it will respond to different situations.


I’m suggesting the Python library because there are lots of references to using it for simulate control situations, like:

Sorry. I really don’t have much more than undergraduate EE control theory but it seems to me your block diagram needs to be revised so that the input from both the surface thermistor and the heater thermistor are factored in together for a proper PID action and I would suggest taking the time to run simulations on the approach before you try implementing it.

1 Like

I’m not the one trying to implement this, just offering my two cents. But, you got me curious so I did some simulations :slight_smile:

Note: these numbers are not to be taken literally. The characteristics of the heater, heat transfer through the bed to the build surface, PID parameters, etc. would all have to be tuned. But this should be enough to show that the concept has merit. Is it the best solution? Probably not. But it is a solution.

Fig. 1, standard PID loop with a build surface thrown on top. Note the build surface (blue) takes longer to heat up than the heater (yellow).

Fig. 2, moving the thermistor to the build surface. Note that the characteristics of the heat up have changed, particularly that the heater (yellow) has larger overshoot than the build surface (blue).

Fig. 3, the proposed block diagram from earlier. The block just after the 10 is a saturation block forcing the output to be zero if the heater is below the thermal limit, and allows it to be negative if the heater is above the thermal limit (taking place of the if statement in the original diagram). Note that once the heater (yellow) passes the thermal limit it reduces the power to the heater and limits the overshoot past the thermal limit. But after some time things stabilize to where they should be.

(Apologies for the one supersized image. New users are only allowed one embedded image…)


Interesting thank you - what tool did you use for the simulations?

I would be surprised if the systems here were properly modeled in terms of thermal mass and convection cooling but I have two comments from what I see here:

  1. In trial 3, that saturation block behaves differently than how I interpreted your original drawing with the if statement. In the original drawing, the output could never go negative (with zero being output when the thermal limit was reached) but in your blurb you say the “saturation block” allows the output to be a negative value which is an important difference. My main concern about the original was that there wasn’t an opportunity for a negative value to be passed to the summing operation so even if the heater had reached its thermal limit, more power could be sent to the heater and it could exceed its thermal limit.
  2. This is really surprising to me, but I don’t see any advantages of using your MISO approach (third case) to the traditional SISO approach in the first case. In the first case, there are no overshoots (and undershoots) and target temperature is reached faster than the other two cases.

Again, thank you for doing the simulation. It’s appreciated that you put in the work to do that.

1 Like

This was done in Simulink, but unfortunately that’s not feasible for most people to have access to. OpenModelica is free software that could have done this just as well if people reading are looking for a tool.

To your first point, I always find decision blocks in flow charts to be prone to misinterpretation (including by myself). What it’s intending to convey is if the error is negative (threshold - temp < 0, in other words temp is higher than threshold) then multiply that number by K which will in turn be a negative number. Otherwise make it zero.

Good eye on your second point. With these oversimplified, uncorrelated plant models for the heater and bed there isn’t much difference between the two (there is a very slight difference when I zoom way in). Getting realistic models would be advisable. I used to know how to do that from empirical data, but the cobwebs have moved in to that recess :smiley:

1 Like


I also have a similar bed temperature monitoring setup on my Voron2 printer. The bed heating pad has a thermistor on it, and I have added a thermistor to the bottom of the aluminum plate ( voron2-mods/bed.md at master · KevinOConnor/voron2-mods · GitHub ).

At least on my printer I am able to get stable bed temperatures using some simple macros.

I have found that during normal printing there is a consistent temperature difference between the heater thermistor and the plate thermistor. Thus, the heater thermistor seems sufficient to control the bed during normal printing. (It is worth pointing out that neither sensor will report an accurate temperature for the bed surface - both sensors are at best a “proxy” for the surface temperature.)

The heater thermistor, however, is not a good proxy for bed temperature during initial heatup. So, I use a macro to get the bed to the initial temperatures.

[gcode_macro start_print_abs]
    {% set BED_TEMP = 105 %}
    {% set EXTRUDER_TEMP = 245 %}
    {% set BED_HEAT = 115 %}
    {% set WAIT_CHAMBER_TEMP = 43 %}
    {% set WAIT_PLATE_TEMP = 95 %}
    # Prep
    G90                 ; absolute positioning
    # Warm chamber
    M104 S1
    M140 S{BED_HEAT}    # Set bed to heat chamber
    G1 X125 Y100 Z20 F800
    M106 S255         # Turn on layer cooling fan to distribute heat
    TEMPERATURE_WAIT sensor="temperature_sensor chamber" minimum={WAIT_CHAMBER_TEMP}
    TEMPERATURE_WAIT sensor="temperature_sensor bed_plate" minimum={WAIT_PLATE_TEMP}
    # Heat extruder
    M106 S0
    G1 X5 Y5 Z5 F4000
    G1 Z0.25 F400
    M140 S{BED_TEMP}
    M109 S{EXTRUDER_TEMP} # Wait for extruder temp
    M190 S{BED_TEMP}    # Wait for bed temp
    G92 E0

The above macro also includes code for heating the hotend and chamber.

The main idea, however, is to enable a high temperature for the heater PID during heatup, wait for the bed plate temperature to reach an intermediate temperature, and then lower the heater PID to its actual target temperature. One should be able to avoid overshoot by selecting an appropriate “intermediate temperature”.


Hi Kevin,

I’m still new to macros but why do you have two assignment statements for “BED_HEAT” with different values?

    {% set BED_TEMP = 105 %}
    {% set EXTRUDER_TEMP = 245 %}
    {% set BED_HEAT = 115 %}

Wouldn’t the second assignment overwrite the first?

I think you misread BED_TEMP vs BED_HEAT.


1 Like

I did. So many apologies.

Hmmm… For me, this discussion went a little away from the targets (see below). I think, the drawing of the above case is no longer a “PID” and from my gutt feeling. That’s what Mykepredko points to.

Targets are still:

  1. Heat up quicker
  2. Don’t allow (too much?) overswing
  3. Include runout protection

The Focus is (2): How to assess overswinging? At what point in time?

For me, this is all solveable with a single PID. The Problem is not the PID, the problem is the setup:

  • Position of the sensor
  • Mass+conductivity of the table (=Time-constant)
  • Insulation
  • Power of the heater
1 Like

I’m on the same page as you on this - the basic PID setup works acceptably for this application.

However, it is an interesting discussion topic as I would expect in the optimal situation the bed heater (and the lower surface of the bed) would exceed the target temperature but the top of the bed (the print surface) would never exceed the target temperature but heat up much faster than the basic PID setup.

It’s probably an important discussion topic for some of the ridiculously large 3D printers that some people are working on:

Sorry, mike, this is no large 3D printer.
This is a large 3d Printer :grinning:


If it’s “important” for what I consider a large format printer, I would expect it’s critical for something like that one.