PID control of a heating/cooling system with node-red-contrib-pid
This flow includes a function designed to be used with node-red-contrib-pid
in applications where both heating and cooling are available to control the system.
The node is given a power value in msg.payload
in the range 0 to 1, such as is produced by node-red-contrib-pid
and splits this into heat power (o/p 1) and cool power (o/p 2) outputs where each is in the range 0 to 1. These can each then be fed directly into an output device, if this is continuously variable, or they may be passed to node-red-contrib-timeprop
nodes to generate time proportioned on/off outputs.
There are two particular issues to be dealt with in a heat/cool application. Firstly is the fact that the cooling device may be more or less powerful than the heating device. It is necessary therefore to be able to adjust the gain of the system separately for heating and cooling. Second is the highly non-linear response of some devices, notably refrigerant systems, that can have a large effect initially, then this tails off. To compensate for this it is useful to have an overlap range where both heat and cool are slightly on.
To allow for these requirements two variables can be set at the start of the function. The value of the power input value where the heating starts to come on is determined by the variable heatMin
. Above this value the heating will rise till it is fully on with an input of 1. The cooling is fully on when value of the power input is 0, and falls till the cooling is fully off at an input of coolMin
.
If the heating and cooling systems are of similar power then set heatMin
and coolMin
both to 0.5 in which case input values of 0.5 to 1.0 will map to heating outputs of 0.0 to 1.0, and 0.5 down to 0.0 will map to cooling 0.0 to 1.0. If, for example, the cooling system is more powerful than heating then they can both be set to something like 0.7 which increases the gain in the heating region and reduces it in the cooling region, to compensate for the different powers in the heating/cooling systems.
If some overlap is desired (so that both heat and cool are on slightly near the crossover point) then overlap the two settings so that, for example, heatMin
might be 0.45 and coolMin
might be 0.55.
[{"id":"c5f0cbd3.adf0f8","type":"inject","z":"55a57d67.bcc964","name":"","topic":"","payload":"0.25","payloadType":"num","repeat":"","crontab":"","once":false,"x":119,"y":240,"wires":[["9585b22a.f41dd8"]]},{"id":"b6f1f78d.502c98","type":"inject","z":"55a57d67.bcc964","name":"","topic":"","payload":"0.5","payloadType":"num","repeat":"","crontab":"","once":false,"x":120.5,"y":275,"wires":[["9585b22a.f41dd8"]]},{"id":"f597764b.bcc1e8","type":"inject","z":"55a57d67.bcc964","name":"","topic":"","payload":"0.75","payloadType":"num","repeat":"","crontab":"","once":false,"x":121.5,"y":312,"wires":[["9585b22a.f41dd8"]]},{"id":"66904193.23d82","type":"inject","z":"55a57d67.bcc964","name":"","topic":"","payload":"0.0","payloadType":"num","repeat":"","crontab":"","once":false,"x":118.5,"y":204,"wires":[["9585b22a.f41dd8"]]},{"id":"972a9f14.2ea9f8","type":"inject","z":"55a57d67.bcc964","name":"","topic":"","payload":"1.0","payloadType":"num","repeat":"","crontab":"","once":false,"x":121.5,"y":349,"wires":[["9585b22a.f41dd8"]]},{"id":"9585b22a.f41dd8","type":"function","z":"55a57d67.bcc964","name":"Split Heat/Cool","func":"/* A function designed to be used with node-red-contrib-pid in applications where both\n * heating and cooling are available to control the system.\n * The node is given a power value in msg.payload in the range 0 to 1, such as is produced by \n * node-red-contrib-pid and splits this into a heat power (o/p 1) and cool power (o/p 2) where\n * each is in the range 0 to 1. These can then be fed directly into an output device, if this\n * is continuously variable, or they may be passed to node-red-contrib-timeprop nodes to generate\n * time proportioned on/off outputs.\n * There are two particular issues to be dealt with in a heat/cool application. Firstly is the fact\n * that the cooling device may be more or less powerful than the heating device. It is necessary\n * therefore to be able to adjust the gain of the system separately for heating and cooling. Secondly\n * is the highly non-linear response of some devices, notably refrigerant systems, that can have a\n * large effect initially, then this tails off. To compensate for this it is useful to have an \n * overlap range where both heat and cool are slightly on.\n *\n * To allow for these requirements two variables can be set below. The value of the power input value\n * where the heating starts to come on is determined by the variable heatMin. Above this value the\n * heating will rise till it is fully on with an input of 1.\n * The cooling is fully on when value of the power input is 0, and falls till the cooling is fully\n * off at an input of coolMin.\n *\n * If the heating and cooling systems are of similar power then set heatMin and coolMin both to 0.5\n * in which case input values of 0.5 to 1.0 will map to heating outputs of 0.0 to 1.0,\n * and 0.5 down to 0.0 will map to cooling 0.0 to 1.0.\n * If, for example, the cooling system is more powerful than heating then they can both be set\n * to something like 0.7 which increases the gain in the heating region and reduces it\n * in the cooling region, to compensate for the different powers in the heating/cooling systems.\n * If some overlap is desired (so that both heat and cool are on slightly near the crossover\n * point) then overlap the two settings so that, for example, heatMin might be 0.45 and coolMin\n * might be 0.55\n */\n\n// set these as described above\nvar heatMin = 0.5; // the value of input corresponding to 0 heat o/p\nvar coolMin = 0.5; // the value of input corresponding to 0 cool o/p\n \nvar power = msg.payload;\nvar heat = (power - heatMin)/(1 - heatMin);\n// limit to range 0 to 1\nheat = Math.min(Math.max(heat, 0), 1);\nvar cool = (coolMin - power) / coolMin;\n// limit to range 0 to 1\ncool = Math.min(Math.max(cool, 0), 1);\nreturn [{payload: heat}, {payload: cool}];\n","outputs":"2","noerr":0,"x":384,"y":393,"wires":[["22a397ae.8af65"],["a1673eea.98ac6"]]},{"id":"a1673eea.98ac6","type":"debug","z":"55a57d67.bcc964","name":"Cool","active":true,"console":"false","complete":"payload","x":588.5,"y":407,"wires":[]},{"id":"22a397ae.8af65","type":"debug","z":"55a57d67.bcc964","name":"Heat","active":true,"console":"false","complete":"payload","x":588,"y":363,"wires":[]},{"id":"ea3f593d.c3d768","type":"comment","z":"55a57d67.bcc964","name":"Feed node-red-contrib-pid in here","info":"","x":153,"y":416,"wires":[]},{"id":"ca4c2ed8.9df65","type":"comment","z":"55a57d67.bcc964","name":"To heater","info":"","x":564,"y":328,"wires":[]},{"id":"55831b33.067024","type":"comment","z":"55a57d67.bcc964","name":"To Cooler","info":"","x":563,"y":445,"wires":[]}]