Writing my own PID

Additional features:

- Control Action toggle (reverse acting/direct acting). This simply causes my raw P, I and D values to be negated after being calculated.

- Gain Type toggle (independent/dependent). If set to dependent, the raw I and D values are multiplied by kP after being calculated.

- Derivative Of toggle (PV/Error). If set to Error, the Error is used to calculate the derivative instead of the PV. As Peter has mentioned previously, using the error in the derivative calculation can cause large output spikes on setpoint changes, so the default is to use the PV - but we can use the error if the application would benefit from the additional "kickstart" and can tolerate the output spikes

- Deadband with zero crossing toggle and deadband timer - so that I can determine whether the PV must cross the SP before activating the deadband, and I ca specify a minimum time for the PV to be within deadband before holding the controller output

- Bumpless restart. This one is a little more complicated, and I'm interested to get some input on my approach here.

When the loop is in manual, the Cumulative_I value is not updated, and nor is the controller output, allowing the operator to manipulate the output as desired.

If the application uses Integral (kI > 0) then when the loop is in manual, I track the manually manipulated output, and adjust the Cumulative_I such that when the loop returns to auto, the combination of P, Cumulative_I, D and Bias will result in the same output set by the operator. Effectively, I'm adjusting the integral value to match the existing controller output. When the loop returns to auto, there will be no immediate change - if there is an error, the integral gain will start accumulating again to bring it back to setpoint.

If integral gain is not being used (kI = 0), then I do the same thing with the bias - effectively adjusting the bias so that the combination of P, D and Bias is equal to the output set by the operator.

Of course, if this behaviour is not desired, I can toggle off the Bumpless Restart option. In that case, when switched to manual, the Cumulative_I simply stops being updated until the loop is switched back to auto.
 
Maybe throw Bias into Cumulative_I (or vice versa), because they are the same thing. I.e. when transitioning from Manual to Auto, assign Cumulative_I = CV and get rid of Bias (or assign Bias = CV and get rid of Cumulative_I).

You've seen me coming a mile away :ROFLMAO: see my next post for how I approached this.


Also, you may need to clamp CV e.g. for the case where (P+D) is outside the range of [0:1], although the overage or underage could be carried in the Bias.
Yes, after scaling the raw value to the controller output, I then limit the output according to the CO Min and CO Max values set.
 
...also I'm still trying to work out what my units of derivative gain will be.

kP is unitless.
kI is minutes/repeat
kD is ????

If I had to take a stab, I'd say it's [CO Units] per [PV Units] per second. But how is that typically represented? In AB-world they use either "Derivative Rate (seconds)" or "Rate Time (minutes)". But I can't see that either of those are the correct description of my derivative value.
 
I'd recommend, instead of re-creating the wheel, to create your own PID AOI and place a PIDE into it. Use FBD to link any tags you'll be using from the AOI's data structure into the AB PIDE block. You can even lock the AOI down so it is just as abstracted as AB's and the customer will never know there's FBD in their PLC. You can use the FBD to add in logic that you wish the PIDE had or account for logic you wish it didn't.

This will also be much better for support down the road (assuming you don't lock down your AOI). Some poor schmuck is going to troubleshoot your code at some point (these things just happen) and anyone can debug a PIDE issue, but learning someone's proprietary PID will be annoying. Even if your code is perfect across all scenarios, in 20 years your PLC will be outdated and may have to be converted, so someone else having to dig through your code is all but guaranteed.

I've created my own PID logic by scratch before and, while it isn't hard, it has virtually no realistically appropriate use in the real world. It is also missing a whole lot of features and logic behind the scenes that makes AB's (or any other big name's) work much better than a bare-bones PID implementation.
 
Don't forget that kI is inverted in dependent, so it isn't quite this simple.
I'm not sure I follow.

Let's say I have an independent gains loop with kP = 2 and kI = 1 minute/repeat. Currently the PV = SP.

Now I introduce a 10% error. Immediately, the kP will add 20% to the controller output (immediately adds 2*Error). Over the course of one minute, the kI will add a further 10% to the controller output (repeats the error every minute).

Now consider the same scenario with dependent gains.

I introduce the 10% error. Once again, the kP adds 20% to the output. Over the course of one minute, the kI should now add 20% to the controller output (because it's multiplied by kP, which is 2). You could suggest that the integral time would decrease - i.e. it would now repeat the error every 30 seconds instead of every 60 seconds - but that's only because my kP is > 1.

I can't see how the integral term would be inverted here? What am I missing?
 
Last edited:
Dependent PID generally isn't as simple as just moving kP to the outside of parentheses surrounding your individual error terms. I recommend doing a bit more research on how these terms are calcated. The instruction help for AB's PID and PIDE instructions have the formulae. I'd link you or supply photos, but I'm on my phone and that's a bit annoying to do.
 
Here we are. It looks very much like all they've done is move the Kp to the outside of the parenthesis. Of course, they've also changed from integral/derivative gain to integral/derivative time, so what's inside the brackets has been tweaked to suit, but it's essentially the equivalent as far as I can tell. And of course their approach uses the previous controller output and the change in the error, whereas my approach does not require those, so it's not quite an apples to apples comparison.

Screen Shot 2021-10-29 at 10.57.24 am.png
 
I'd recommend, instead of re-creating the wheel, to create your own PID AOI and place a PIDE into it...
I was trying not to get drawn into this conversation again but...

I work for a small family business. In just under 10 years I've built the engineering department of this company up from just me, doing a few hours a week of drawings and code, to four full-time engineers across two states, with the strong possibility of adding a fifth in the new year. I say this not to blow my own horn, but to make the following point...

By a significant margin, the greatest contribution to our engineering workload is company X. They started out giving us little program updates and modifications to do, and slowly starting offering us larger and larger projects. Now, we generally have at least one engineer onsite there on any given day, sometimes two or three.

A big part of the reason we're now this company's go-to systems integrator is because they like the code we write. Their maintenance guys like it. They can read it, they can understand it, they can troubleshoot it, they can work with it. I almost never get calls from this site to troubleshoot how my code works. If I go in there with ST or FB code, that attitude will change real quick.

Now, I won't for a second argue with anyone that says "it's 2021, maintenance technicians need to be able to read ST or at the very least FB". I do not for a second contest the idea that perhaps company X should train their maintenance team up to a higher standard, or at least hire some maintenance technicians that have an interest in learning new things. But here's the thing: I don't control the decisions of company X. Company X is a huge, very successful, multinational company. What do you think their response to me will be if I just say "I'm just going to code it this way because it's easier for me, your maintenance technicians need to get with the times"?

It doesn't do me any good being "right" if nobody will pay me to be "right". I'd rather put in the effort to write the code that the customer wants, so that I can keep paying my bills using the profits company X are making, using the hundreds of PLC's running my code.
 
If I go in there with ST or FB code, that attitude will change real quick.

No way @ASF. If you want 100% ladder, there are ways around it. But, plopping a PIDE in FB and "mapping" in ladder is nothing. There is no way any customer would have a problem with that. Like you, we deal with lots of different end users and are the go-tos. One reason we are the go-tos, we don't spend needless hours reinventing the wheel. We judge our efficiency not on how many calls we get, but the fact we get none! In this case, there is nothing different with a PID in ladder vs PIDE in FB. If a maintenance can get confused, he will get confused. You're logic on this matter is quite strange.
 
I could add so much more but it is too geeky for a PLC forum.
You guys are arguing over the details.
The big picture is that you want to control something precisely. This requires knowing what the "something" is.
It requires know what kind of precision/response is required.
Most people are told that this gain does this and that gain does that. What they don't say is that the gain place poles and zeros. If you know where you want to place the poles and zeros then it is possible work backwards to compute the gains.


BTW, what do poles and zeros do? I really don't expect a response.
 
BTW, what do poles and zeros do? I really don't expect a response.
Yeah, no idea. Like I said, I've never formally trained as an engineer so I don't have background knowledge like that unfortunately.

Peter, you mentioned in a previous post that managing integral windup is easy if you think about it - what do you think of my approach (post #29)?

You mentioned the necessity for variations, e.g.
Peter Nachtwey said:
For instance some PID have derivative gains that only act on the rate of change in the PV
I know that I can't possibly cover every use case in one PID implementation, but do you think the configuration options I've included in post #31 will at least cover enough ground to give me a halfway decent general-purpose PID?

Does my approach for managing bumpless transfer (also in post 31) make sense to you?
 
Thanks for the advice, everyone. Here's what I've got so far.

I pass an analog AOI as an inOut parameter. This gives me my process variable, my setpoint, and the Min and Max range of the process variable. I have the programmer specify the Min and Max range of the Controller Output.

I then calculate the error in PV units, and convert the error to a "raw" value where 0...1 = 0...100% of the PV range. So if the PV min/max scale is e.g. 50-100L/min, and my error is 5L/min, my error is 10% of the PV range, and my "raw" error is 0.1

I calculate "raw" values for my PV and my Bias in the same way, so that PVRaw 0...1 = 0...100% of the PV input range, and BiasRaw 0...1 = 0.100% of the controller output range.

My P value is kP * RawError
My I value is kI * RawError * LoopUpdateTime(ms) / 60000
My D value is kD * RateOfChange, where RateOfChange = (RawPV - RawPVLastScan) / 1000

Because I use the raw values in my calculations, P, I and D values are also "raw".

Now I move on to managing integral windup.


I have a "Cumulative_I" value. Each time my PID is executed, I add my new raw I value to the "Cumulative_I" value.

Next, I add together my raw P, D and Bias values. This should add up to a number between 0 and 1. I therefore determine that my "Cumulative_I" value cannot be greater than (1 - P - D - Bias), because that would cause my CO to exceed 100%. It likewise cannot be less than -1*(P + D + Bias), because this would cause my CO to fall below 0%. If the Cumulative_I value exceeds this limit, I clamp it.

Finally, I add together my P, Cumulative_I, D and Bias values. These are all raw values so I will get a value between 0 and 1. This value is scaled according to the CO Min and CO Max values input by the programmer, and becomes my CO.

I have added some more features, but I'll make a second post for them - I'd be interested to get some feedback on the "bare bones" of this PID attempt.


1. Personaly, I prefer my PID gains to be in Engineering Units/CV%. Just gotta make sure the Americans don't come and change your units between C and F (4°F / CV% != -15°C / CV%). But I find Engineering Units change less frequently than AI Input range. If one minute you have a transmitter ranged 0-100°C, someone will come along and re-range it 20°C to 50°C (better resolution? new sensor? Continuous improvement? who knows!). Often this person doesn't know that they must alter all or some of the gains.


2. You do not mention the Scan rate in your derivative calculation. So your units are:
Kp := CO%/PV%
Ki := CO%/PV%/Minute

LoopUpdate Time = 1s, then:
Kd := CO% . killoseconds /PV%

LoopUpdateTime = 1ms, then:
Kd := CO% . seconds /PV%


3. Your anti-windup looks appropriate.




4. I would hate to be the only one who didn't say "If PIDE has the features you need, you should wrap it in a ladder AOI."
 
Did you really mean to use PV%

That should be

Ki := CO%/(Error*Minute)
KD :=CO%/(Error/Minute) Error/Minute is a rate.

I use minutes for temperature control and seconds for motion control or anything fast.
When doing motion control I simply multiply Kd*(target_velocity-actual_velocity)


If you want fast then
CV(n)=max(min(CV(n-1)+K0*E(n)+K1*E(n-1)+K2*E(n-2),100),-100)
Where:
K0=Ki*Δt+Kp+Kd/Δt
K1=-Kp-2*Kd/Δt
K2=Kd/Δt
Pretty simple, fast and efficient.


It is possible in incorporate a low pass filter with a little extra work to compute the K constants. To incorporate a low pass filter at the sample frequency/PI

K0=Ki*Δt/4+Kp/2+Kd/Δt
K1=Ki*Δt/2-2*Kd/Δt
K2=Ki*Δt/4+Kp/2+Kd/Δt


It isn't much filtering but it removes some of the sample noise.
Notice that the K constants only need to be calculated once.
One can do a lot of PID variations this way. Everything boils down to multiply and adds.


I prefer using state space methods where the current state, x, is a vector of actual/estimated position, velocity and acceleration.
I say estimated because the feedback doesn't have infinite resolution.
 

Similar Topics

I have 3 parameters for a product: Diameter Lengte Hoek (corner) all reals... I need some batching system with a batchlist. Something...
Replies
8
Views
2,743
Hi, I'm having an issue in crimson 3.0 when I create a programme using a case statement referencing a fault word that each bit needs to change the...
Replies
5
Views
262
Hello all, I'm currently working on a servo motor linear positioning system (ball screw). I'm all set up regarding communication between my HMI...
Replies
1
Views
101
Hello All: I have a Windows 10 PC running a FTView ME Station 10.00. I have a .mer file version 6.00. It has been running well on it for a long...
Replies
1
Views
174
My R55 Ingersollrand is tripping on motor overload and im falling to see the trip history it is writing Acquarring texts
Replies
0
Views
137
Back
Top Bottom