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

PID setpoint tracking #28

Closed isabido closed 2 weeks ago

isabido commented 6 months ago

Hi @colinl

The first to thank you for your excellent job!

I am designing a system at home where I need all photovoltaic production to be used to heat water.

What I need is, if the solar panels are generating 1kw (this measurement is extracted by mqtt from the inverter and it would be the setpoint remote) I need to divert all that power to a resistor controlled by a dimmer with 0-10v signal input through a Shelly Dimmer 0-10v

Regulator 25A https://es.aliexpress.com/item/20000003997748.html?spm=a2g0o.cart.0.0.eec37a9dSt4vWf&mp=1&gatewayAdapt=glo2esp

Shelly Dimmer 0-10V https://shelly-api-docs.shelly.cloud/gen2/Devices/ShellyPlus10V

SP is the instantaneous solar power extracted by MQTT from the solar inverter, logically the SP will be continuously oscillating according to the solar generation.

PV will be a ShellyEM with a clamp measuring the consumption of the dimmer. (This value can be obtained by Json HTTP)

OUT .Output will be sent to the Shelly Dimmer (this expects a range of 0-100%) and then in proportion it will act with 0-10v on the regulator where the resistance that heats the water is connected.

The ultimate goal is that the PV = SP

Do you think could do it with your node?

At the moment I'm trying this initial modified version of your example, but I'm getting the Integral Locked error. I imagine it is because I am not entering the PV value correctly in the PID block.

image flows.json

Greetings from Spain and sorry for my terrible English.

colinl commented 6 months ago

What do you see from debug 256 and 258? Set 258 to Output Complete Message and fully expand it in the debug pane. Integral locked is not an error, it is just telling you that the PV is a long way from the setpoint so the output will be clamped to 0 or 1, depending on which side of the setpoint it is. The default operation of the node is to assume that the process is a 'heating' type process, so the output will be 0 when the PV is a long way above the SP and 1 when it is a long way below the setpoint.

isabido commented 6 months ago

HI @colinl Thanks for your prompt response.

Debug256= (power in W range 0-2000W) It is the value that I want to enter into the PID in PV.

Debug258= PID OUT output value, to control a dimmer with 0-10v input. Sorry, I'm starting with nodered and I don't know Set 258 to Output Complete Message and fully expand it in the debug panel.

I have scaled the input signal to the PV PID 0-2000W to 0-1, and I have scaled the OUT from 0-1 to 0-100% (input to the ShellyDimmer, which will convert 0-100% to 0-10V which will serve to control the regulator)

I don't know if it is necessary to scale the PV input and OUT output of the PID.

Is there any way to know what values are in/out of the PID block?

I think the problem is that I am not knowing how to correctly connect the PV value (0-2000W) to the PID. and that's why it says it gives that "warning" of Integral Locked, because the SP is normally close to the PV.

In this case the loop works correctly in heating mode. If the PV is less than the SP, the OUT increases. It's right.

Greetings and Happy New Year!

colinl commented 6 months ago

As a beginner, I recommend watching this playlist: Node-RED Essentials. The videos are done by the developers of node-red. They're nice & short and to the point. You will understand a whole lot more in about 1 hour. A small investment for a lot of gain.

I don't know Set 258 to Output Complete Message and fully expand it in the debug panel.

image

To expand the data in the debug window, click against the values in the debug window and it will expand the objects. Keep clicking the contained objects till they are all shown. You should end up with something like this image

Is there any way to know what values are in/out of the PID block?

That is what we will now see in the debug window.

isabido commented 6 months ago

Thanks @colinl

I have now been able to open the complete debug.

Do you think that the PID could respond well to even a remote SP that will be oscillating continuously.

The objective is for two signals to follow each other....

SP=power that the inverter is generating PV=power consumed by the heating resistance, and which must be equal to SP.

Another option... since the resistive heating element behaves in a "linear" way, you could simply make a range function.

Input 0-2000W and output 0-100% (knowing that at 50% the load consumes 1000W), it may have some deviation watts since the internal resistance of the heating element itself increases when it heats up, but it would respond very quickly and without having than tuning a PID. You could take the PV value just to monitor and verify.

What is your opinion?

colinl commented 6 months ago

I agree, you don't need a PID loop for this. For PID to work the sample rate needs to be significantly faster than the process responds, but when you change the dimmer setting the PV will change very rapidly. My approach would be to sample the current as quickly as you can, and at each sample compare the value with the inverter output. If, for example, it said that the PV is 10 units below the inverter output then I would increase the dimmer setting to take the current 3/4 of the way from where it is to the required value. If you try and take it all the way in one step then you might find the loop is unstable. Then you can tweak the 3/4 setting to get it to respond as quickly as you can without it becoming unstable.

isabido commented 6 months ago

Thanks Colin, I think it's a good idea to start trying.

So if I understood you wrong, we wouldn't use your PID node, right?

Could you please, when you have a little time, make an example json. I'm sure it will help me better understand how to do it.

Thank you again for your time and patience!

colinl commented 6 months ago

At what rate do you get a value from the current clamp?

isabido commented 6 months ago

Initially, the watt value to follow is obtained by mqtt every 5 seconds.

colinl commented 6 months ago

I meant the actual power going into the resistor, that is the PV value from the current clamp.

isabido commented 6 months ago

The PV value measured on the heating resistance clamp could be done at a maximum of 1 second.

colinl commented 6 months ago

I have changed my mind, using the PID node is how I would do it. I have built a test flow with a simulation of your dimmer and heater.

image

You should be able to Import the flow below and see how it works. It is pretty much what you had, except that I have added a 2 second low pass filter to help keep the loop stable. Sample the current as quickly as you can. Replace the simulation with your hardware and see how it goes. If the output hops up and down at each sample then increase the prob band. The integral time determines how quickly it homes in on the setpoint, but if you go below about 4 times the sample rate then it may go unstable. I have got it at 4 seconds which should be ok for 1 second sample rate. Reducing the prop band will bring it in quicker, but if you go too far it will go unstable. If you have to sample the PV at a slower rate then set the filter to twice the sample period, and the integral at four times it.

[{"id":"11eda06c2b800da1","type":"comment","z":"bdd7be38.d3b55","name":"Actual measured value in watts (PV)","info":"","x":160,"y":4480,"wires":[]},{"id":"65391c5cc1280bd8","type":"link in","z":"bdd7be38.d3b55","name":"link in 11","links":["86ea8967949d7710"],"x":335,"y":4480,"wires":[["a65a480287ee2764","a604f5227071c0a5"]]},{"id":"815d714116d29ea0","type":"comment","z":"bdd7be38.d3b55","name":"Required value in watts (SP)","info":"","x":140,"y":4300,"wires":[]},{"id":"86bb53abe66cb64b","type":"inject","z":"bdd7be38.d3b55","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"setpoint","payload":"100","payloadType":"num","x":130,"y":4380,"wires":[["3886d6d4962287a5"]]},{"id":"a65a480287ee2764","type":"PID","z":"bdd7be38.d3b55","name":"","setpoint":"0","pb":"2000","ti":"4","td":0,"integral_default":"0","smooth_factor":3,"max_interval":600,"enable":1,"disabled_op":0,"x":530,"y":4380,"wires":[["3bce6d5b86ad11f7","e5f55ebe7cf034b2"]]},{"id":"cd8dac5283070133","type":"inject","z":"bdd7be38.d3b55","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"setpoint","payload":"0","payloadType":"num","x":120,"y":4340,"wires":[["3886d6d4962287a5"]]},{"id":"b932d25908e735b7","type":"inject","z":"bdd7be38.d3b55","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"setpoint","payload":"1000","payloadType":"num","x":130,"y":4420,"wires":[["3886d6d4962287a5"]]},{"id":"3bce6d5b86ad11f7","type":"range","z":"bdd7be38.d3b55","minin":"0","maxin":"1","minout":"0","maxout":"100","action":"clamp","round":false,"property":"payload","name":"Scale 0 to 100","x":750,"y":4380,"wires":[["df676488333ff807"]]},{"id":"99f38f0402b0410a","type":"link out","z":"bdd7be38.d3b55","name":"link out 28","mode":"link","links":["31e2d20c5433e56d"],"x":1145,"y":4380,"wires":[]},{"id":"31e2d20c5433e56d","type":"link in","z":"bdd7be38.d3b55","name":"link in 12","links":["99f38f0402b0410a"],"x":65,"y":4640,"wires":[["9d3ba56efb2ea143"]]},{"id":"86ea8967949d7710","type":"link out","z":"bdd7be38.d3b55","name":"link out 29","mode":"link","links":["65391c5cc1280bd8"],"x":1215,"y":4820,"wires":[]},{"id":"d375d9dfc3f8ba0f","type":"ui_chart","z":"bdd7be38.d3b55","name":"","group":"c45a83a3.d00908","order":10,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"2000","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1090,"y":4480,"wires":[[]]},{"id":"a604f5227071c0a5","type":"change","z":"bdd7be38.d3b55","name":"topic: PV","rules":[{"t":"set","p":"topic","pt":"msg","to":"PV","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":4480,"wires":[["d375d9dfc3f8ba0f"]]},{"id":"410cb8ff585e8ca0","type":"debug","z":"bdd7be38.d3b55","name":"Scaled PID Output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1130,"y":4420,"wires":[]},{"id":"e5f55ebe7cf034b2","type":"change","z":"bdd7be38.d3b55","name":"topic: Demand","rules":[{"t":"set","p":"topic","pt":"msg","to":"Demand","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":4440,"wires":[["516783e442a7f6ab"]]},{"id":"516783e442a7f6ab","type":"range","z":"bdd7be38.d3b55","minin":"0","maxin":"1","minout":"0","maxout":"500","action":"clamp","round":false,"property":"payload","name":"Scale 0 to 500","x":900,"y":4440,"wires":[["d375d9dfc3f8ba0f"]]},{"id":"4c7a801d8a07c326","type":"comment","z":"bdd7be38.d3b55","name":"Output to dimmer","info":"","x":1140,"y":4340,"wires":[]},{"id":"df676488333ff807","type":"function","z":"bdd7be38.d3b55","name":"2 sec RC","func":"// Applies a simple RC low pass filter to incoming payload values\nvar tc = 2*1000;       // time constant in milliseconds\n\nvar lastValue = context.get('lastValue');\nif (typeof lastValue == \"undefined\") lastValue = msg.payload;\nvar lastTime = context.get('lastTime') || null;\nvar now = new Date();\nvar currentValue = msg.payload;\nif (lastTime === null) {\n    // first time through\n    newValue = currentValue;\n} else {\n    var dt = now.getTime() - lastTime.getTime();\n    var newValue;\n    \n    if (dt > 0) {\n        var dtotc = dt / tc;\n        newValue = lastValue * (1 - dtotc) + currentValue * dtotc;\n    } else {\n        // no time has elapsed leave output the same as last time\n        newValue = lastValue;\n    }\n}\ncontext.set('lastValue', newValue);\ncontext.set('lastTime', now);\n\nmsg.payload = newValue;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":940,"y":4380,"wires":[["99f38f0402b0410a","410cb8ff585e8ca0"]]},{"id":"3886d6d4962287a5","type":"junction","z":"bdd7be38.d3b55","x":340,"y":4340,"wires":[["a65a480287ee2764"]]},{"id":"b9e51a3357199286","type":"group","z":"bdd7be38.d3b55","name":"Process Simulation","style":{"label":true},"nodes":["9d3ba56efb2ea143","8314d7341bc8260b","de22788849c4b1fa","73f72dd28cef81ed","6e6f1975900d627d","c5a1ce224d5b111b","9064309b1f74e79d","4cd86fd0004bd87f","cdf1bcaf43480cc9"],"x":94,"y":4559,"w":1052,"h":302},{"id":"9d3ba56efb2ea143","type":"range","z":"bdd7be38.d3b55","g":"b9e51a3357199286","minin":"0","maxin":"100","minout":"0","maxout":"2000","action":"clamp","round":false,"property":"payload","name":"Scale 0:100 to 0:2000","x":220,"y":4640,"wires":[["8314d7341bc8260b"]]},{"id":"8314d7341bc8260b","type":"delay","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":410,"y":4640,"wires":[["de22788849c4b1fa"]]},{"id":"de22788849c4b1fa","type":"change","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"topic: input","rules":[{"t":"set","p":"topic","pt":"msg","to":"input","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":4640,"wires":[["73f72dd28cef81ed"]]},{"id":"73f72dd28cef81ed","type":"join","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":4680,"wires":[["9064309b1f74e79d"]]},{"id":"6e6f1975900d627d","type":"inject","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":430,"y":4600,"wires":[["de22788849c4b1fa"]]},{"id":"c5a1ce224d5b111b","type":"inject","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"Trigger 1 sec","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"trigger","payload":"0","payloadType":"num","x":600,"y":4720,"wires":[["73f72dd28cef81ed"]]},{"id":"9064309b1f74e79d","type":"switch","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"Trigger?","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"trigger","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":600,"y":4820,"wires":[["4cd86fd0004bd87f"]]},{"id":"4cd86fd0004bd87f","type":"change","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"Move actual to payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.input","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":4820,"wires":[["cdf1bcaf43480cc9"]]},{"id":"cdf1bcaf43480cc9","type":"function","z":"bdd7be38.d3b55","g":"b9e51a3357199286","name":"Clear other properties","func":"msg = {payload: msg.payload}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1020,"y":4820,"wires":[["86ea8967949d7710"]]},{"id":"c45a83a3.d00908","type":"ui_group","name":"PID","tab":"80cd4062.93a5","order":1,"disp":false,"width":"12","collapse":false},{"id":"80cd4062.93a5","type":"ui_tab","name":"PID","icon":"dashboard","disabled":false,"hidden":false}]
isabido commented 6 months ago

Ohhhhh @colinl thank you very much for this great work. I'm still waiting for the Shelly 0-10V dimmer to arrive from Aliexpress.

Looking forward to trying it....I have some questions.

Remember that the setpoint we have there is for testing or to make it work manually.

In NORMAL operation, the SP signal will be the Power generated by the solar inverter, and it will oscillate continuously.

Here is the question of how this PID will work, when a new SP is arriving approximately once per second.

Another question I have... why have you used a 0-500 scaling? It should be 0-2000W right? image

Thanks again for your time!

colinl commented 6 months ago

SP signal will be the Power generated by the solar inverter, and it will oscillate continuously.

What do you mean by oscillate?

why have you used a 0-500 scaling

I didn't want the power chart line to take up the whole chart. It is easier to see what is going on if it only fills the bottom 1/4 of the chart.

isabido commented 6 months ago

I mean that the SP will change continuously depending on the solar radiation and the clouds, it is a measurement that is never stable. The SP will be the measure of power (watts) generated by the inverter.

I don't know if I can explain.

Thank you!

colinl commented 6 months ago

The pid node is designed to be able to track varying setpoints. Of course, since it takes a number of seconds to home in on a new value it will not track rapid changes quickly. It will do its best to follow the setpoint. You can see how it performs if you configure some inject nodes with the sort of values you expect and click them at an appropriate rate. The average power in the heater over a period should match the average incoming setpoint.

isabido commented 6 months ago

That is why I insist, the SP will change once per second (I told you 5sec, but I have managed to have an instantaneous power reading of up to 1sec) each time the power value of the inverter is updated by MQTT.

colinl commented 6 months ago

That is why I insist, the SP will change once per second

What is why you do that? The control loop can only control at a certain rate.

isabido commented 6 months ago

I don't think I'm able to explain myself with my bad English.

Let's see if I can explain it now...

Imagine that my SOLAR PANEL/Inverter is generating 1KW, I need all that power to be consumed by the water heating resistance, adjusting the dimmer dynamically.

That's why I was trying to use the power value per mqtt generated by the inverter as SP input.

colinl commented 6 months ago

That is what the flow i posted does, isn't it?

isabido commented 6 months ago

Not…. 🥹 I can't be manually adjusting the SP! The heating resistance has to be adjusted automatically to consume all the energy produced by the photovoltaic inverter.

colinl commented 6 months ago

I see now why are concerned. I thought it was obvious. The inject nodes are to simulate the solar input. Replace those with an input from the inverter.

isabido commented 6 months ago

Yes, I had actually understood that it was just for testing!

But what I want to tell you is that the value of SP will be changing every time MQTT updates the value collected from the INSTANT SOLAR POWER variable. (as you can see in the debug approximately 1 time per second)

I have modified the json, I will send it to you again to see what you think.

Apparently it seems to work, I have not yet been able to connect the DIMMER to the output, (I hope from Aliexpress).

image flows.json

isabido commented 6 months ago

I'm injecting the real SP into it, and it seems to respond well, although of course the PV value is still simulated.

image

more later....

image

colinl commented 6 months ago

what I want to tell you is that the value of SP will be changing every time MQTT updates the value collected from the INSTANT SOLAR POWER

As I said, that should not be a problem.

isabido commented 6 months ago

Thank you very much for your help! It would never have occurred to me to start with a kP value of 2000!!

I have to learn to tune a PID it's obvious.

colinl commented 6 months ago

Let us know how it performs when you have the hardware running.

isabido commented 6 months ago

Of course! Don't hesitate, I'll post photos of the installation so you can get an idea of the project.

As an improvement to this JSON, I would like to create some buttons to be able to select SP (remote=mqtta value) or SP Local with a manual setpoint that I can select from HomeAssistant.

And also implement a logic in which the PID does not act until the power generated by the Solar inverter does not exceed at least 300W (less than this power it is ridiculous to put it into the resistor).

Thank you again for all your time and dedication.

colinl commented 6 months ago

implement a logic in which the PID does not act until the power generated by the Solar inverter does not exceed at least 300W

You can do that by sending enable or disable to the pid node based on the power value.

isabido commented 6 months ago

Hi @colinl , I need a little help.....

I want to create a button in HA to choose a local SP (slider) and a remote SP (mqtt value).

I can't find a way to create a NODE that has 2 IN (sp local/sp remote) and 1 OUT (topic setpoint PID). And a selector to change it that would come from HA.

image image flows.json

As you will see in the flow I am creating the entry and exit points from HA, but I don't like the idea. It would be better to create those BUTTONS inside NODERED.

image

colinl commented 6 months ago

I can't find a way to create a NODE that has 2 IN (sp local/sp remote) and 1 OUT (topic setpoint PID). And a selector to change it that would come from HA.

So that is three inputs, including the selector. Since those three come in different messages the first thing you have to do is to get them all into one message. To do that use a Join node. See this article in the cookbook for an example of how to join messages into one object. Have a play with the flow there and see if you can work out how to do it.

It would be better to create those BUTTONS inside NODERED.

What is the problem with doing that?

isabido commented 6 months ago

flows.json I'm playing with the JOIN, but it doesn't generate the message until all 3 entries change their value.

For example, if I do not change sp switch, although sp remote and sp local change, the JOIN message is not generated.

image

colinl commented 6 months ago

Select 'and every subsequent message`. Then after there has been an input on each channel you will get an output every time any of the inputs change. If necessary you can use the topic of the outgoing message to determine which input triggered the output. If your HA nodes don't give you an initial message on startup then connect two Inject nodes configured to fire on startup injecting default messages for those channels.

isabido commented 6 months ago

Thank you @colinl , it is already working as expected, even if you change ONLY one of the entries it sends the complete message.

Now once I have this message, {"setpoint remote":494.4,"setpoint local":"560.0","remote":true}

I would need to do:

If "remote" is true, sends the "setpoint remote" payload as topic "setpoint" for the PID block input.

And if "remote" is false, it sends the "local setpoint" payload as topic "setpoint" for the PID block input.

That's how you were trying to explain it to me, right?

image

colinl commented 6 months ago

You can use a switch node to send the message down separate wires dependent on the remote setting, then Change nodes to set the topic and payload.

isabido commented 6 months ago

Thanks @colinl , I have already achieved it, I leave you the flow/json so you can see how I have solved it. I don't know if it is in an elegant way, possibly it could be simplified much more.

image flows.json

and here is an overview of how it is looking with all the options.

image

isabido commented 5 months ago

Hi @colinl

I have already received the regulator and the 3.3v to 4-20mA converter.

I have configured an ESP32 with ESPHOME and added a DIMMER and a DS18B20 temperature sensor.

I am "practicing" trying to control the temperature, the reading I make from the temperature sensor is once per second, the sensor is "glued" to the heat source, which is simply an incandescent bulb.

image

I'm trying to tune the PID and the truth is that although the graph seems to be regulating correctly. Because if you look at it, it only oscillates 1.5Cº but really the control output is 100% and 0%, it is not regulating and maintaining a constant output, it is as if it were an ON-OFF

image

image flows (1).json

I have tried various values in the PID, but I have not achieved any improvement. It behaves like an ON-OFF.

Let's see if you could help me understand what's happening. Thank you!

colinl commented 5 months ago

You are supposed to be controlling the current, not the temperature.

colinl commented 5 months ago

Also you need to chart the pid output to see why it is oscillating. Also need to know the pid settings.

isabido commented 5 months ago

Indeed @colinl Colin, what I am asking you is a test for control by temperature, not by power. And it's just an experiment to play around with.

Actually this weekend, it will be installed in the electric heater controlling by power, as we had designed and thought.

Here you can see the relationship between PV and OP, as I explained, it is working as ON-OFF, it seems to behave like a SIMPLE thermostat with a hysteresis. image

The PID data is this, but I was testing with a Kp of 1 and it behaved in the same way.

image

isabido commented 5 months ago

Here is another example now with setpoint 50ºc, and these PIDs image

image image

colinl commented 5 months ago

You had the integral time at zero, which means it reacts infinitely fast, which effectively puts it into on/off control. Set the integral time to 40 seconds and the prop band to 1, derivative time 0.

isabido commented 5 months ago

Ohhh @colinl Colin, black magic! SP=50 and ranges between 49.9 and 50.1ºc.

I don't know what you think of OP's oscillation, surely it can be left more stable.

Any suggestions?

This looks very good

image image

colinl commented 5 months ago

The DS18B20 has a resolution of .0625C if I remember correctly, so to control to +/- 0.1 is pretty good. Try setting the prop band to 1.5.

isabido commented 5 months ago

The SP=50 continues oscillating between 49.9 and 50.1ºc.

But the OP oscillation has improved... it moves between a band of 14-15% (OP min 37% and OP max 51% approx.)

image

I later tried putting 5sec in Kd and it was a disaster, better at 0.

On the other hand, does it seem like an improvement to run the PID directly in the ESP with something like this? https://esphome.io/components/climate/pid.html?highlight=pid

Obviously I would have to see how it behaves controlling by POWER, since this ESPhome component is designed to control by temperature.

isabido commented 5 months ago

With these parameters it improves again, the temperature also oscillates between +-1º but the OP output oscillates only 8% kp=3 ki=60 kd=0 image

The loop seems quite stable and regulated, but also the external conditions (air flows for example) are very stable.

It would be necessary to see how it behaves when, for example, I blow it with a fan. In short, these are all tests, to learn.

colinl commented 5 months ago

Please don't call the parameters kp, ki and kd, those are the names of the unitless coefficients when the pid calculation uses the traditional mathematical formula. They are not the same as PB, Ti and Td, which have units of Degrees (in this case) and seconds.

Those residual +-0.1 are unusual. They PV and power are absolutely in phase, so they are not induced by the integral term, yet they do not increase or decrease in amplitude, which is odd. I suspect it is to do with the rather poor conduction between the lamp and the sensor, which will be very much affected by the convection currents in the air around the bulb. I would not worry about it, whatever it is, it is not relevant to your eventual system.

does it seem like an improvement to run the PID directly in the ESP with something like this? https://esphome.io/components/climate/pid.html?highlight=pid

I don't see that it has any advantages over using node-red.

isabido commented 5 months ago

Thanks for the clarification, I'll note it down for my information!

Tomorrow I will do the test on the heating resistance. I will send you photos although it will have little to do with it, it will be provisional.

I found a "fault" in your "2 sec RC" node, it starts to drift and goes off the 0-100% scale, which ends up causing an error on the ESP32 input.

colinl commented 5 months ago

I found a "fault" in your "2 sec RC" node, it starts to drift and goes off the 0-100% scale, which ends up causing an error on the ESP32 input.

Yes, you are right, it fails if the interval between inputs is greater than 2 seconds. Add these two lines

        // clamp to max of 1 to prevent silly results
        dtotc = Math.min(dtotc, 1)

after the line which calculates dtotc

        var dtotc = dt / tc;

I don't remember how at what rate you are getting new PV measurements, can you remind me?

isabido commented 5 months ago

Hi @colinl

This is how it is, and it no longer "overflows"; it always seems to be between 0 and 100%. image

This is how the provisional installation turned out! image image

When I started the test, the system was not working as I expected. I got the amperage reading from the Shelly at 1000ms, and it was a complete disaster, I put it at 500ms and the change was great, here you can see how it behaves in both configurations with a setpoint of 800W

image image

Another important thing to keep in mind

The behavior of the regulator with the connected load does not have a "linear" output

I mean...

25% load consumes 280W 50% load consumes 1420W 75% load consumes 2420W 100% load consumes 2700W

Taking into account that solar production will never exceed 2000W, it would be good to be able to tell the PID to never exceed 60%, I understand that this way it will be able to work more precisely.

Since even though the SP is far from the PV, it should never give an output of more than 60%, because it would always exceed the 2000W generated by the solar panels.

Another improvement would be to be able to load the linearization curve between % and W

What do you think, am I saying too much nonsense?

isabido commented 5 months ago

Another important thing is with your 2sec RC FILTER OFF, the PID is not able to regulate. Here you can see it

Setpoint = 500W

image