colinl / node-red-contrib-pid

A node-red PID loop controller node intended for the control of real world processes
Apache License 2.0
26 stars 17 forks source link

General Questions #19

Closed Mahai17 closed 1 year ago

Mahai17 commented 3 years ago

Hi, I use this node for room heating (P and I only, planning for D). I am happy so far, but I have some questions. I looked into your code to better understand the algorithm. 1: the 0.5 offset power = -1.0/node.prop_band * (proportional + node.integral + node.derivative) + 0.5 Why do you use 0.5 as an offset? I seems like a virtual baseline or virtual "zero"?

2: positiv integral Maybe I am wrong, but I think for a heating application, the integral should not get positiv. Because, a heating can't actively accelerate the decrease of PV. Example: PV=21°, setpoint=21°, integral=-0.2, system (room temperature) is stable. Now I reduce setpoint to 20°. Power=0, and integral increases over time. Integral becomes even positiv. Some times later, PV reaches setpoint and still decreasing. Now comes the strange thing: Even PV falls below setpoint, the Power output remains 0, because of a positiv integral. In other words. In a heating environment, I always need a negative integral to keep the PV at setpoint, but I never need a positiv integral to keep the PV. Or am I wrong?

3: Feature request: Different settings for derivative time In a heating application, it would be nice to have two different derivative time settings. I mean, if PV approaches setpoint from below, t_derivative should be larger, than if PV approaches setpoint from above. In other words, opening the valve in a radiator immediately adding a lot of energy to the "system", t_derivative should be small. But after closing the valve, there is still energy emitted, t_derivative should be large.

colinl commented 3 years ago

Why do you use 0.5 as an offset? I seems like a virtual baseline or virtual "zero"?

Because the maths is symetric about zero the power it calculates has a centre point of zero, so if we wanted the scale the output to have a span of 1 then the output would be in the range -0.5 to +0.5. As a mathematician I would be perfectly happy with that, but for practical usage, such as heating, the output is more convenient in the range 0 to 1, hence the addition of 0.5.

In other words. In a heating environment, I always need a negative integral to keep the PV at setpoint, but I never need a positiv integral to keep the PV. Or am I wrong?

Your logic may well make some sense, however what you have to remember is that PID is a very simple way of attempting to take the non-linearities out of a complex real world process to make it more easy to control. As soon as you start adding complexity to the algorithm, such as clamping the integral, if that is what you are suggesting, then you are adding non-linearities back in. Very often tweaks like that have unforeseen consequences. By all means adjust the algorithm and see if it helps for your process, it may well improve the control marginally, or it may not. You might even find that the system goes unstable in unexpected ways. It is virtually impossible to know what effect it might have in your process. Another problem is that if you consider a cooling process such as a refrigeration system then the opposite is true of the integral, so the clamp would have to work the other way. Even worse would be an exothermic chemical process. They commonly that requires heating to get up to temperature and then cooling once the reaction starts, so the integral may be anywhere in the range.

if PV approaches setpoint from below, t_derivative should be larger, than if PV approaches setpoint from above.

Again, something which appears to be entirely logical can have often have unforeseen side effects as it is introducing non-linearities into the system. By all means try it, but I shall be surprised if it makes much difference, and could easily make the control worse. In this particular case consider what will happen as the pv crosses the setpoint. At that point the derivative would jump from one value to another which would cause the power output to step from one value to another, which would not be good. What would you do about that? Possibly you could adjust the I term as it crossed in order to take out the step. But then what would happen if the Integral happened to be against the clamp that you suggested earlier? It wouldn't be able to adjust it. It all just gets so complex that it becomes impossible to predict the consequences under all circumstances. Believe me, I have tried such tweaks on control systems over a period of 50 years in process control and they are rarely helpful.

The most important point is to make sure that you have tuned the loop optimally before embarking on any such exploits. If you wanted me comment on whether I think the tuning could be improved then post some plots showing process value, setpoint and power output and I will happily take a look. Tell me a bit about the process too.

An unrelated question, what resolution have the sensors that you are measuring temperature with got? If you are using, for example, DS18B20 they have a resolution of 0.0625C, if my memory is correct. That sounds better than is needed for room temperature control, but it can play havoc with derivative. The problem is that you get long periods with no change in temperature at all and then it steps by 0.0625 over one sample. This causes great difficulty getting a good estimate of the rate of change of pv and very much limits the amount of derivative you can use, so you may well find that you can't use as much as you would like. If you are using a much better sensor then that becomes less of a problem.

Mahai17 commented 3 years ago

Many thanks for your long reply. I really appreciate it. I use a BME280 which has a pretty good resolution, 1 minute time interval. When it comes to derivative, opening the room door for a minute gives a lot larger spikes than the resolution limit. So, I smoothed the PV over 10 minutes (10 intervals) before feeding the PID node. Works good.

I modified the code, that integral can't be positive. But then, I needed to remove the 0.5 offset, as well. (Because it would need a positive integral to push the power below 0.5 (ignoring the proportional for a second). That works great , so far.

Regarding the derivate, you are right. I will think about it.

colinl commented 3 years ago

Can you show me exactly what changes you have made please?

Mahai17 commented 3 years ago

change this: // if ((Math.abs(error + node.integral) < pbo2) && node.enable) { to this: if ((Math.abs(error + node.integral) < 1) && node.enable) { // let integral grow to -1

behind line: node.integral = node.integral + error delta_t/node.t_integral; add this: if (node.integral >= 0) { // prevent integral to become positiv node.integral = 0;
} change this: // var power = -1.0/node.prop_band
(proportional + node.integral + node.derivative) + 0.5; to this: var power = -1.0/node.prop_band * (proportional + node.integral + node.derivative); // remove offset

colinl commented 3 years ago

I think there are some issues with some of that.

change this: // if ((Math.abs(error + node.integral) < pbo2) && node.enable) { to this: if ((Math.abs(error + node.integral) < 1) && node.enable) { // let integral grow to -1

Consider the case where the prop band is 10. The first line locks the integral to the range +-5. If the prop band is 0.1 then the integral is locked to the range +-0.05. In both cases you are locking it to +-1, which is much too much in one case and much to small in the other.

behind line: node.integral = node.integral + error * delta_t/node.t_integral; add this: if (node.integral >= 0) { // prevent integral to become positiv node.integral = 0; }

Now you are limiting it to again, so it is clamped to the range -1 to 0.

change this: // var power = -1.0/node.prop_band (proportional + node.integral + node.derivative) + 0.5; to this: var power = -1.0/node.prop_band (proportional + node.integral + node.derivative); // remove offset

Consider now the case when the process has stabilised onto the setpoint so that proportional and derivative are zero. The equation is now power = -integral/prop_band where integral is in the range 0 to -1. The range of power output that can be achieved in the stable condition is therefore 0 to 1/prop_band. so if the prop_band were 10 then the maximum power output available in the steady state condition would be 0.1. So if the process actually needed power of 0.5 to maintain the setpoint then it would stabilise sufficiently far from the setpoint that the proportional term would make up the difference.

Mahai17 commented 3 years ago

oups, you are right. It worked for me, because my prop_band is between 1 and 1.5 I have to think about that, and how to fix.

colinl commented 3 years ago

Even with prop band of two there is a problem. the first change does not have any effect, the second still limits to the range to 0 to -1. Then for the third change the output power will be limited to the range 0.0 to 0.5. So if your process were to need more than 50% power to maintain the pv at the setpoint then the integral would be at the end stop and it would not control on the sp. If you find that you get better control with the modified code, and know that it will never need more than 50% power to maintain the setpoint, then that may mean that your process is overpowered (heater too powerful). I think you would get similar performance with the original code if you scaled the node output so that full power from the node only gives 50% power from the heater, and halved the prop band so the proportional effect is unchanged. Alternatively it may just mean that the loop is not optimally tuned. I would really like to know what aspect of the control you think the modified code improves. Or to put it another way, what about the control with the original code is not as you would like it?

Mahai17 commented 3 years ago

With the original code, I observed the following behavior. Starting with a room temperature of 21°. System is stable. error = 0. integral = -0.3 (for example). Now, I set new setpoint = 20°. ==> error = 1. Power = 0. Since it took, let' say, 1 h for the room to cool down to 20°, the integral will increase positive to its maximum. So, now with pv=20°, error=0, the integral is so high, that the power still remains 0. (But of course, it should start increasing now). Room cools down to 19.5° before integral turns negative and power slowly comes up to heat.

I understand, that your code is universal and support heating and cooling appliacations. But for heating only, I think that behavior is not suitable. So, for my room heating application, I am looking for a way to prevent intergral to become positive. Does this make sense? Or do I something wrong?

colinl commented 3 years ago

Can you post some charts showing the pv, power and setpoint, showing it heating up to setpoint and cooling down to setpoint please. Also tell me what PI values you have configured. What you describe should not happen.

Mahai17 commented 3 years ago

I will, but since I didn't keep the old node red dashboard charts, I have to create new ones. (Restore original code, prepare my living room for a couple of hours and rerun the test).

colinl commented 3 years ago

Off Topic, but if you were to use Influx and Grafana to store data and chart it then you can easily go back and look at historic data. I find it particularly useful when tuning loops as, provided you keep a record of what you have done, you can easily go back to remind yourself how it behaved with a particular set of parameters. There is a fair learning curve to using influx and grafana however.

If you were to fork my github repository and make the changes in your fork then you could install your version using npm install <your_github_id>/node-red-contrib-pid and revert to the npm version by uninstalling the node using npm remove node-red-contrib-pid and reinstalling using npm install node-red-contrib-pid. That has to be done in your .node-red folder and you have to restart node-red after manually installing using npm. Alternatively if you just clone the repository to a folder on your node=red system (not inside .node-red) then you can install from there using npm install path/to/node-red-contrib-pid and similarly revert to the standard one as above. If you do it that way then if you edit the files you just have to restart node-red and it will use them.

Mahai17 commented 3 years ago

I use InfluxDB and Grafana (within Home Assistant) and do record tons of sensor data. But I missed to store the pid-node outputs. I will do that, good point. Is there an easy way to store the node outputs? Or do I have to create entities in Home Assistant? I got it, there is a node-red node to store msg to influxDB.

Mahai17 commented 3 years ago

Hi Colin, I have to apologize. Nearly all I wrote was wrong. I rerun my tests and I detect a lot of errors in my setup and my measurements. Therefore, I had wrong conclusions. I modified the code and things got even worse. Now I better understand. Your code works really perfect. I am still in the process of running the tuning methods.

colinl commented 3 years ago

Excellent :-)

Mahai17 commented 3 years ago

Hi Colin, is that correct?

  1. t_integral changes the "speed" of the integral to adopt a new system behavior.
  2. I cannot specify the weighting of integral compared to proportional.
  3. t_derivative changes the weighting of derivative. The larger t_derivative, the larger the impact.
colinl commented 3 years ago

t_integral changes the "speed" of the integral to adopt a new system behavior.

Yes, the smaller the integral time the less time it takes to have an effect, so the greater the effect of the integral. It basically sets the Time Constant that the integral term imposes on the system.

I cannot specify the weighting of integral compared to proportional.

Sorry, I don't understand what you mean by that.

t_derivative changes the weighting of derivative. The larger t_derivative, the larger the impact.

Yes.

Did you see the link in the readme to the tuning guide on my blog? http://blog.clanlaw.org.uk/pid-loop-tuning.html

Mahai17 commented 3 years ago

Hi Colin, I tried pidtuner.com A really good tool for helping PID tuning with use of a step response. Now I am struggling with the formula. The tool gives Kp, Ti and Td for a given step response.

image

Td is "your" derivates time. Ti is "your" integral time. But is Kp the proportional band?

colinl commented 3 years ago

I haven't studied the maths of that procedure, and without spending a couple of hours working out the maths of what they are doing I can't really comment in detail. In principle their Ti and Td may be at least proportional to the ones in my algorithm, maybe the same, but Kp will be proportional to 1/prop_band. It isn't clear what the units of Kp are though. It also isn't clear what 's' is.

I recommend using the technique in the guide I link to in the readme, http://blog.clanlaw.org.uk/pid-loop-tuning.html I think you would find that just as easy to do. The most important thing is to make sure you are creating charts showing the process value and the power output from the PID node and preferably the setpoint. Then you should find the procedure fairly easy. It is always going to be an iterative procedure as real processes are not the simple processes the tuning procedures assume, so even if we were to use their procedure to estimate the parameters it will only be a first estimate and iterative tuning would still be required.

If you have any trouble post the chart here and I will do my best to help. Start a new issue though please, this isn't really related to the problem this issue is about.

juangburgos commented 2 years ago

Hi Colin, I tried pidtuner.com A really good tool for helping PID tuning with use of a step response. Now I am struggling with the formula. The tool gives Kp, Ti and Td for a given step response. Td is "your" derivates time. Ti is "your" integral time. But is Kp the proportional band?

I can help with the usage of the pidtuner tool, here you have a FAQ:

If @colinl can point me out to the actual equations of the PID, I can tell whether the gains from the pidtuner need some kind of transformation or not.

colinl commented 2 years ago

Kp should by 1/PB. The units of PB are process units (degrees C or whatever) which is a much easier concept to understand, as it relates directly to the process. However there may be an additional constant involved in the tool, depending on exactly how the maths of the tool is implemented. PB in the node is defined so that if the process input changes by 1 PB then the output (from the proportional term) will change by 100% (or 1.0 in the node output). That it why it is the proportional band, or band of proportionality. So the question for the tool is, with a value of Kp of (say) 1, what is the output change when the process changes by one process unit (1 degrees C or whatever)? The equations are not documented except in the code. It is not complex code if you have a look.

juangburgos commented 2 years ago

Thanks for your explanation, the conversion of PB to standard PID Proportional Gain (Kp) is still not clear to me though, I might have to take a look at the code. The pidtuner assumes the standard PID implementation (wikipedia), and also works with other modified-standard forms, like the canonical Arduino PID block implementation.

If you know off the top of your head what is the mathematical relation between your PB and the standard Kp, then we can derive the inverse relation and be done becasue the intergal time in seconds and derivative time in seconds are indeed standard. Else we will see later whether if we can figure it out from the code.

colinl commented 2 years ago

The wikipedia link you posted describes exactly what the proportional band is in the section "Reciprocal gain, a.k.a. proportional band".

juangburgos commented 2 years ago

Alright, so PB = 100/Kp then? The that's it!

colinl commented 2 years ago

It depends on the output range in the tool. Is it 0 to 1 or 0 to 100? It is easy to check what the relationship is. If you set Kp to 1 and disable integral and derivative, then change the process by 1 degree does the output change by 1? If so then PB = 1/Kp. That is clear since, with the node, with a PB of 1 then changing the process by 1 also changes the output by 1.

colinl commented 2 years ago

The above assumes, of course, that an output of 1 corresponds to full power to the heater (or whatever is the output device).