# Should the error term of PID be normalized?

By "normalized", I mean +/-1 ~= the maximum error the system can reasonably be expected to experience, or divided by the setpoint.

Background: I am working on a PID controller for an SSR heater which is very responsive (5°C/s up, 0.5-1°C/s cooldown). The setpoint is in the range 100-400°C. In code, this is implemented as:

$$u(t) = K_pe(t) + K_i\sum_{t}e(t) dt + K_d[e(t)-e(t_{-1})]dt^{-1} + K_k$$

(Summation is just an accumulator and Kk is a small steady-state correction)

This form is pretty common in a lot of the open-source PID code out there. However, it occurred to me that if I were to switch to °F, suddenly my K terms would be off by 1.8. This does not feel very mathematically "pure" to me. Furthermore, I found when trying to Ziegler-Nichols tune it, my critical oscillation is ~24s, but when I put this as Ki, I got wild fluctuations. After some digging, I found the equation listed as

$$u(t) = K_c\left( e(t) + \frac{1}{T_i}\sum_{t}e(t) dt + {T_d}\Delta e(t)dt^{-1} + K_k \right)$$

And I had a lightbulb moment. Is this the more "pure" form to use? This makes more sense when dealing with transfer function analysis and the like.

• I enjoyed the question and you've got some thoughtful answers, already. I just wanted to add a note for you to also consider as you proceed on this. The delay time from sensor to control should be kept as short as you can possibly manage. Shorter isn't just linearly better, it's exponentially better. Also, keep the variability as a percent of delay to the smallest possible value, as well. It's very difficult to "tune" a fast PID system when there is variability in the delay. It's never right, in effect. (I worked on RTP systems with $> 300\:\frac{^\circ\text{C}}{\text{s}}$ ramp rates.) – jonk Mar 8 '18 at 19:17
• I had a call from someone in Vancouver, B.C. working at pulling GaAs boules using PID to control the process. He was using my measuring device for temp, but using a PID controller from Omega Engineering (big company.) We didn't have PID on our device, then. So I took a few days and wrote one for him. Our device already had 0-10 V and 4-20 mA outs. So fine there. Told him to just dump the Omega and use our software. Got a call back a week later -- totally shocked and excited guy on the other end. No ripples. Perfect boules coming out! – jonk Mar 9 '18 at 1:23
• The ONLY difference, really, is that I'd removed large delays and large variability in those delays. Rather than output temperature on my device via 0-10 V/4-20 mA, read by Omega and then generating its own output at some very sloppy cycle period, we'd just short-cutted right through the mess and let the temperature sensor itself directly drive the process. I have no idea how much slop was actually saved, or its variability, but it completely solved their problems in one fell swoop. – jonk Mar 9 '18 at 1:25
• Someday, if you can, try applying your PID controller on a "plant" whose ONLY function is to DELAY the input. Read input, delay X time, write input as output. Then hook up your PID to that. See what happens with big X values. It's UGLY. Then sit down and do the math. It looks every bit as bad as it works in practice. Even a poor PID algorithm will work surprisingly good when there are very short delays. The first thing I do when there is any closed loop control problems to solve is fix the delays, first. I cut out anything I can cut and shorten the time. Only then do I worry the rest. – jonk Mar 9 '18 at 1:28
• Is this a specific application you are solving, then? I wasn't sure if you were making a general purpose PID module, like Omega does, or if this was a specific situation. Sounds specific, now. So my first direction is to ask about the temp sensor. What is it and why are you using that particular one? What are you looking at that is $100-400\:^\circ\text{C}$? Sometimes, changing the sensor is the right direction to head. If not, then I'd need to know what your noise looks like. Raw data is vital here. – jonk Mar 9 '18 at 3:09

If e(t) and u(t) are nondimensionalized then Kp is unitless, Ki has units of time^-1 and Kd has units of time. Otherwise they have units from the process.

In the second formulation you've made a mistake. This is the one to use.

$$u(t) = K_c\left( e(t) + \frac{1}{T_i}\sum_{t}e(t) dt + T_d\Delta e(t)dt^{-1}\right) + K_k$$

Then Kc has units from the process and Ti and Td have units of time. This is good because Ti and Td are on the same scale and only Kc has process units. e(t) and u(t) do not need to be normalized because just Kc relates their units.

• Oops! You're right, Td should be in the numerator. Thanks! – DeusXMachina Mar 8 '18 at 18:08
• Would it hurt in any way to divide the error (T_set - T_read) by T_set? Temp is guaranteed to be positive. I could even use Kelvin :grin: – DeusXMachina Mar 8 '18 at 18:12
• @DeusXMachina Yes. If you change T_set, for example, to a very low value, you've effectively increased Kc which might make the system unstable. – τεκ Mar 8 '18 at 18:17
• @DeusXMachina you should probably also not multiply Kk by Kc. If it's correcting for something about the output u(t), it should have units of the output. If it's correcting for something about the input, e(t) should probably include it. – τεκ Mar 8 '18 at 18:19

For simulation and comparison purposes normalization can be useful. Changing from °C to °F within the control loop doesn't make sense for me. Try to use SI units whenever possible. If you have a curious sensor which delivers °F just convert the measurement to °C before you feed it into the control loop.

For the implementation it depends on the target architecture. If you have a floating-point DSP you could keep the normalization. Using a smaller microcontroller you should use integer (e.g. as fixed-point) calculations for improved performance and reduced memory cost.

Temperature control - as in this application - is usually slow. Speed is not much of an issue so you probably won't use a DSP, but you also don't want to block other program parts with overly complicated calculations.

Tip for smaller and faster code: You can already include a scaling in your $K_C$ of the control equation that fits to your output control (e.g. the compare register of a PWM module). This costs you portability though, so only do it if you have very limited hardware ressources.

Also avoid using the form with the division by $T_i$. Division costs a lot more cycles than multiplication, as it is rarely implemented in hardware.

• I'm not saying I would change it, I too prefer SI, and the sensor is °C. I'm saying that the algorithm, mathematically, should not have to worry about a change in basis. I'm on a Cortex M4 so FP isn't too expensive. My limiting factor in this application is I/O actually. – DeusXMachina Mar 8 '18 at 18:06
• @DeusXMachina: You could normalize the temperature first based on maximum and minimum temperature of the system. That way your algorithm can be independent too. But I would only do that in simulation. For the implementation I prefer seeing the relation to the physical values. Also check that you have an integrated floating point unit (M4F when I recall correctly) as not all M4s have that. Software floating point adds a lot of code to store and run. Depening on the application that might not matter of course. – Grebu Mar 8 '18 at 18:15
• if $T_i$ is a constant $1\over{T_i}$ is also a constant, just multiply by that. – Jasen Mar 8 '18 at 19:18
• °C is not SI that would be K, but if you're only looking at the error they both have the same scale. – Jasen Mar 8 '18 at 19:20
• @Jasen: °C is a derived SI-Unit. See: en.wikipedia.org/wiki/SI_derived_unit. The multiplication with the reciprocal value is of course a possible approach for Ti. – Grebu Mar 8 '18 at 19:22