# I'm confused a bit on basic control stuff for a PID controller

I'm doing a PID controller demonstration project in which heat is applied to a K type thermocouple and I'm supposed to maintain the temperature read at 50 °C using a PWM signal. I know the basic control materials, but since this is the first time I'm supposed control an actual physical system I'm a bit overwhelmed. Can you please correct me if I'm misunderstanding something here?

The main components are: Arduino used as a controller, Nichrome wire as the heating element, a K type thermocouple and a 12 V source. I send a PWM signal to a MOSFET to switch on/off the 12 V across the nichrome wire. My step input is the 12 V(?), my system is an open loop when I manually turn off the power at 50 °C(?) and it is closed loop when I for example say if temperature > 50 °C, switch off the MOSFET(?).

This is my open-loop response:

Where I switched off manually. In the other one, the sensor output is used to turn off the MOSFET by a simple if (temperature > 50 °C) and these are the closed-loop responses:

And finally the last is where I added a proportional controller instead of on/off and this is the response:

Now I think I can add I and D to my controller just by trial and error, but I want to take a more simulation based approach. But I don't have a transfer function model of this system to do any simulation on it in MATLAB. I know how to model one if I have a stable closed loop response, but mine never reaches a steady state. Only the one with the P controller is stable. I'm so confused...

• nice graphs ... first one got messed up – jsotola Mar 1 at 20:07
• The transfer function involves a PTS temperature coefficient on R and thermal mass – Tony Stewart EE75 Mar 1 at 20:15
• PID is simple from a textbook, only. You do need to implement D-filtering, an integral anti-windup, then you could also implement a feed-forward path from LUT. But get some good code based on Aström / Hägglund anti windup, and first order filter for D-component. You do the Ziegler/Nichols when you have the controller with Kp, Ti(s), Td(s), gamma (for D filter) – Marko Buršič Mar 1 at 20:20
• You have a lot of 2nd order stuff going on there if you shut it off at 50°C and it overshoots to 85°C. That's very unpleasant. Suggest you fix your 'plant' before you try to control it. – Spehro Pefhany Mar 1 at 20:56
• The instability is created by the nonlinear transfer function due to PTC effects on reducing gain as it heats up This is something nobody else picked up on. But the thermal resistance of your temp sensor and the lag might also be an issue...unless you had moving air to improve that – Tony Stewart EE75 Mar 1 at 23:08

Your control is quite good despite if you feel in other way. I do think that a P-control with feed forward LUT (Look Up Table) would solve 98% of your need.

Try this way:

For each temperature setpoint you create a LUT table with output % that maintain the setpoint temperature at the steady state. If this setpoint is just 50 deg. C, then you don't even need a LUT, just feed approx. 20% as from your open loop tests. Then close the loop with a P-controller, the Kp=4 seems very good from your tests.

u=Kp*(T_sp-T_act)+20; // in %
if u>=100
u=100;
else if u<=0
u=0;
end if


As the error is subject to the whole loop gain, there will be always a little bit off, but maybe not so big error if you want to avoid complexity with PID. You don't even need sample time interrupt, you will be surprised by this simple control.

Once you get a stable control, then you can experiment with PID as well, then it's up to you to choose.

EDIT 1: You can improve the initial overshoot by using the ramp on a setpoint value. This will allow you to set a higher Kp.

EDIT 2:

Aström controller with tracking anti-windup (Source):

Aström controller with tracking anti-windup and with filtered D-part (Source):

input parameters
Ti ...integral time (s)
Td ...derivative time (s)
Kp ...proportional gain
b ...setpoint weighing factor for proportional part (0..1), default 1.0
Ts ...sampling time (s)
N ...D-part filter 1.5 to 30, default 15
umax ... max output limit
umin ... min output limit


--

float bi, ad, bd, a0, Tt, Kpold, Tiold, Tdold, ysp, y, yold, P, I, D;
float Kp = 7;
float b = 1.0;
float Ti = 0;
float Td = 0;
float N = 15.0;
float umin = 0.0;
float umax = 100.0;
float Ts = 0.1; // 100ms sample time, same for ISR interrupt
int UpdateParam = 0; // flag for parameter update


--

func init(void)
{
if Td>0.0
Tt = sqrt(Ti*Td);
else
Tt = Ti/2.0;

if (Ti>0.0) // switch for disabling integral part
{
bi = Kp*Ts/Ti;
a0 = Ts/Tt;
}
else
{
bi = 0.0;
a0 = 0.0;
}
if Td>0.0 && (N>=0.0)
{
bd = (2*Kp*N*Td)/(2*Td+N*Ts);
}
else
{
bd = 0.0;
}
}


--

func ISR(void)
{
if UpdateParam
{
init();
I=I+Kpold*(bold*ysp-y)-Kp*(b*ysp-y); //bumpless transfer of new parameters
bold = bi;
Kpold = Kp;
Tiold = Ti;
Tdold = Td;
UpdateParam = 0;
}
P = Kp*(b*ysp-y);  // compute proportional part
D = ad*D - bd*(y-yold);  // update derivative part
v = P + I + D; // compute temporary output
if v>=umax  // limit output
u=umax;
else if v<=umin
u=umin;
else
u=v;
output = write(u);
I = I + bi*(ysp-y) + a0(u-v);
yold = y;

}


--

main
{
init();
I = 0.0;
bold = bi;
Kpold = Kp;
Tiold = Ti;
Tdold = Td;
__ISR_Enable();

while(1)
{
if (Kp<>Kpold) || (Ti<>Tiold) || (Td<>Tdold)
UpdateParam = 1;
}
}


You start with Ti=0, Td=0 and then execute the ZN tuning procedure. According to ZN you do calculate Kp, Ti, Td. A 20% oversoot is a normal thing, you can avoid the overshoot by introducing a ramp function on the setpoint value. It would gradually increase the temperature.

--

func Ramp(void)  //execution in ISR at every Ts
{
// delta_max ... ramp slope (degrees C/s)
// yspin ...  input setpoint value
step = delta_max * Ts;
if yspin > (ysp + step)
ysp = ysp + step;
else if yspin < (ysp - step)
ysp = ysp - step;
else
ysp = yspin;
}


--

• Yes, feed forward control is underappreciated (as feedback is more mathematically interesting). – Peter Mortensen Mar 2 at 11:11
• What is "LUT"? Lookup table? – Peter Mortensen Mar 2 at 11:21
• The code formatting is inconsistent (three kinds of indentation (0, 1, and 2 spaces)). Only one space indent makes it too hard to read. Zero even harder. I suggest 2 or 4. – Peter Mortensen Mar 2 at 11:32
• @PeterMortensen I beg your pardon, what kind of indentation? It very hard to write a code in this editor, so please give me some advice. – Marko Buršič Mar 2 at 12:07

The graph with the P-control response looks quite decent already. Based on the P-control response, you just need a little integral action to take out the DC error. (unless you also want to boost the closed loop bandwidth...)

You can use that P-control setup to characterize the open loop response even better, by feeding it sine wave inputs, superimposed over a DC level. Set the DC level by trial and error so that the output temperature is approximately in the range you will be controlling, and keep the sinusoidal part small in amplitude. Measure gain and phase (i.e. measured-temperature vs signal-to-plant) at several frequencies (walk them up by a factor of 3 between data points, add more points if you see bumps in the curve etc). From that you construct the open loop bode plot and after that, the small-signal design should be pretty much "textbook", either algebraically, graphically, or by simulation.

AFTER you have got the small signal behavior entirely to your satisfaction, look at the "large signal" aspect of it (i.e. control of saturation behavior, saturation-recovery, and/or "ARW"). In short, set a positive and negative limit for both slewrate and value of either the output (meaning signal to the plant) or the measured-value. When any of the positive limits are exceeded, do not allow the controller to add to the integrator, but still allow the controller to subtract from it. And vice versa for the negative limits. You can also limit how large a value you allow the integrator to accumulate to.

A minor footnote to the small-signal part, that sometimes goes unnoticed, is that PI control inherently introduces a closed-loop zero, which can manifest as overshoot in the step response. Just keep that in mind.