PID for pump using node-red-contrib-pid

Hi everyone,
I am a newbie with Node-Red and I am trying to make 2 pumps balance. The first pump can be controlled by slider in Node-Red. The second pump muss try to reach same pressure as first one. My idea is using PID to control PWM output of Raspberry => the pressure of second pump. The output contains many values such as msg.pv, msg.proportional, msg.integral, msg.derivative, msg.smoothed_value and my question is, how can I use this values to create PWM signal. I guess these output muss be used for the PID equation but not sure if this correct or not...
Please give me a hand.
Thanks in advance!

please, give me some advices

@torrent - Please do not bump posts!
We do read every post, this is a very active forum, and I'm sure that if someone can assist you, they will reply!

1 Like

There have been several threads recently on tuning PID loops so well worth a quick search

1 Like

I have already tried with other examples but it doesn't work very well. When the pressure of first pump is bigger, the pressure of second pump is also bigger for a small moment then suddenly become very small and again...

I assume you are using node-red-contrib-pid, in which case msg.payload contains the output of the pid algorithm, which you should use to control the PWM. It is in the range 0 to 1. The other contents of the message are for information and debug.
The process input to to the algorithm should be the current pressure from the second pump and the setpoint is the pressure from the first pump.
In the previous posts did you see the links to the tuning blog post?
Does your process allow you to use the initial tuning algorithm there? If so then do that. For initial tuning it might simplify the situation to use a fixed setpoint (again if your process allows).
The other point you might have seen from the previous posts is the vital importance of having a chart showing the process value, the pid output value and, if it is variable, the setpoint. Then when asking questions here when the process is not doing what you want then always tell us what the current PB, Integral time and Derivative time are and post the chart showing it.

1 Like

Hi, thanks for the response. I think I have found the problem here. Whenever the second pump reachs the same pressure as the first one then the output of PID is 0. It means output for PWM Signal is 0! So it turns off the second pump. That is the reason why the 2nd pump is not stable.
Not like temperature, which slowly cools down, the pump muss always have signal input. Therefore, all of typical examples in the Internet didn't work in my case.
But I don't have any idea now, how to fix this.

That is not the reason that it is not stable, it is because the loop is not tuned correctly. I presume you are seeing it continually switching off and on, but without seeing the chart I don't really know what it is doing, hence the importance of the chart as I said.
Have you tried the tuning algorithm in the blog post? If not then give it a go.

One question though, about how long does it take the pressure to rise from zero to working pressure, and how often are you measuring the pressure?

1 Like

Hi Colin,
sorry for reply lately, I was little bit busy this week. This is the diagram of 2 pumps...

and this is my json data

[{"id":"65dfb966.1b7cc8","type":"tab","label":"Home","disabled":false,"info":""},{"id":"4efcdd19.37bfd4","type":"mqtt in","z":"65dfb966.1b7cc8","name":"","topic":"pumpen1","qos":"2","datatype":"auto","broker":"e1bad40c.02766","x":160,"y":100,"wires":[["61c3e6dd.b61448","dc588398.da27c8"]]},{"id":"c4be28ab.aaf058","type":"mqtt in","z":"65dfb966.1b7cc8","name":"","topic":"pumpen2","qos":"2","datatype":"auto","broker":"e1bad40c.02766","x":170,"y":320,"wires":[["ca77eaa.85ad198","f93f7e3b.5b75d"]]},{"id":"61c3e6dd.b61448","type":"function","z":"65dfb966.1b7cc8","name":"Covert voltage to pressure (mbar)","func":"var pressurePumpen1 = parseFloat((msg.payload - 1.0066)/0.0041).toFixed(1);\nif(pressurePumpen1 < 0){\n    pressurePumpen1 = 0;\n}\n//flow.set('pressurePumpen1', pressurePumpen1);\nreturn {payload : Math.round(Number(pressurePumpen1))};","outputs":1,"noerr":0,"initialize":"","finalize":"","x":400,"y":100,"wires":[["7ee8b0f.a98e85","e4de5c4e.46d328"]]},{"id":"15721f24.7673a1","type":"debug","z":"65dfb966.1b7cc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":720,"y":320,"wires":[]},{"id":"ca77eaa.85ad198","type":"function","z":"65dfb966.1b7cc8","name":"Covert voltage to pressure (mbar)","func":"var pressurePumpen2 = parseFloat((msg.payload - 1.0066)/0.0041).toFixed(1);\nif(pressurePumpen2 < 0){\n    pressurePumpen2 = 0;\n}\n//flow.set('pressurePumpen2', pressurePumpen2);\nreturn {payload : Math.round(Number(pressurePumpen2))};","outputs":1,"noerr":0,"initialize":"","finalize":"","x":440,"y":320,"wires":[["15721f24.7673a1","b5620409.2a8d98","9e33dcb1.9bfc8","3b5ad16e.0c8b3e"]]},{"id":"563b94c8.3f4d1c","type":"rpi-gpio out","z":"65dfb966.1b7cc8","name":"PWM Output for pumpen 1","pin":"11","set":"","level":"0","freq":"","out":"pwm","x":340,"y":1560,"wires":[]},{"id":"5db182a3.1e572c","type":"comment","z":"65dfb966.1b7cc8","name":"Controller for pumpen 1","info":"","x":120,"y":1520,"wires":[]},{"id":"a311df32.075728","type":"comment","z":"65dfb966.1b7cc8","name":"Input for pumpen 2","info":"","x":120,"y":260,"wires":[]},{"id":"27b04a58.fca35e","type":"comment","z":"65dfb966.1b7cc8","name":"Input for pumpen 1","info":"","x":120,"y":20,"wires":[]},{"id":"25902b0c.78aa44","type":"function","z":"65dfb966.1b7cc8","name":"Compare pressure between 2 pumpen","func":"var pum1 = flow.get(\"pressurePumpen1\");\nvar pum2 = msg.payload;\nmsg.payload = (pum1 - pum2);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":930,"y":500,"wires":[[]]},{"id":"d0f4ae16.fca0c","type":"comment","z":"65dfb966.1b7cc8","name":"pumpen1 - pumpen 2","info":"","x":880,"y":440,"wires":[]},{"id":"7c63f007.71eeb8","type":"rpi-gpio out","z":"65dfb966.1b7cc8","name":"","pin":"13","set":"","level":"0","freq":"","out":"pwm","x":1140,"y":700,"wires":[]},{"id":"9e33dcb1.9bfc8","type":"link out","z":"65dfb966.1b7cc8","name":"","links":["192683cf.3243fc","56703a11.f82c94"],"x":645,"y":500,"wires":[]},{"id":"cc3c788e.ff3678","type":"range","z":"65dfb966.1b7cc8","minin":"0","maxin":"480","minout":"0","maxout":"100","action":"scale","round":true,"property":"payload","name":"","x":980,"y":680,"wires":[["f3cc4bc3.c22478","7c63f007.71eeb8"]]},{"id":"a02115de.1683b8","type":"trigger","z":"65dfb966.1b7cc8","name":"","op1":"","op2":"","op1type":"pay","op2type":"payl","duration":"0.3","extend":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":130,"y":1080,"wires":[[]]},{"id":"56703a11.f82c94","type":"link in","z":"65dfb966.1b7cc8","name":"","links":["9e33dcb1.9bfc8"],"x":715,"y":500,"wires":[["25902b0c.78aa44"]]},{"id":"55f43441.6976d4","type":"debug","z":"65dfb966.1b7cc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":570,"y":1260,"wires":[]},{"id":"526d2c3b.936f44","type":"ui_slider","z":"65dfb966.1b7cc8","name":"","label":"Controller","tooltip":"","group":"a8b58e7d.4d6b9","order":2,"width":0,"height":0,"passthru":true,"outs":"end","topic":"","min":0,"max":"100","step":1,"x":120,"y":1560,"wires":[["563b94c8.3f4d1c"]]},{"id":"916846a5.c0652","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":"0","height":"0","name":"Payload","label":"Payload","format":"{{msg.payload}}","layout":"row-spread","x":940,"y":860,"wires":[]},{"id":"f3cc4bc3.c22478","type":"ui_text","z":"65dfb966.1b7cc8","group":"36afd270.efac66","order":4,"width":0,"height":0,"name":"Output Mapping","label":"Output Mapping:","format":"{{msg.payload}}","layout":"row-spread","x":1140,"y":640,"wires":[]},{"id":"7ee8b0f.a98e85","type":"ui_gauge","z":"65dfb966.1b7cc8","name":"","group":"d0993a2e.f3ebd","order":1,"width":0,"height":0,"gtype":"gage","title":"Pressure","label":"mbar","format":"{{value}}","min":0,"max":"1000","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":710,"y":60,"wires":[]},{"id":"b5620409.2a8d98","type":"ui_gauge","z":"65dfb966.1b7cc8","name":"","group":"dcfb34ce.0c851","order":1,"width":0,"height":0,"gtype":"gage","title":"Pressure","label":"mbar","format":"{{value}}","min":0,"max":"1000","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":730,"y":380,"wires":[]},{"id":"2b05b190.4b2fee","type":"PID","z":"65dfb966.1b7cc8","name":"","setpoint":"0","pb":"3000","ti":"15","td":"0","integral_default":"0","smooth_factor":"0","max_interval":"300","enable":"1","disabled_op":"0","x":630,"y":1040,"wires":[["916846a5.c0652","149b6030.c39738","862efb07.5d7088","4afac22a.a7b2b4","665cf613.65f138","3be3fedb.664a72","4c4127c7.d4a9a","7482ff.44d9bd"]]},{"id":"149b6030.c39738","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"PV","label":"PV","format":"{{msg.pv}}","layout":"row-spread","x":930,"y":920,"wires":[]},{"id":"862efb07.5d7088","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"Setpoint","label":"Setpoint","format":"{{msg.setpoint}}","layout":"row-spread","x":940,"y":980,"wires":[]},{"id":"4afac22a.a7b2b4","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"Proportional","label":"Proportional","format":"{{msg.proportional}}","layout":"row-spread","x":950,"y":1040,"wires":[]},{"id":"665cf613.65f138","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"Integral","label":"Integral","format":"{{msg.integral}}","layout":"row-spread","x":940,"y":1100,"wires":[]},{"id":"3be3fedb.664a72","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"Derivative","label":"Derivative","format":"{{msg.derivative}}","layout":"row-spread","x":940,"y":1160,"wires":[]},{"id":"4c4127c7.d4a9a","type":"ui_text","z":"65dfb966.1b7cc8","group":"64012f99.40fcd","order":1,"width":0,"height":0,"name":"Smoothed value","label":"Smoothed value","format":"{{msg.smoothed_value}}","layout":"row-spread","x":960,"y":1220,"wires":[]},{"id":"a8f193e0.591ea","type":"function","z":"65dfb966.1b7cc8","name":"Convert function","func":"/*\npv ist der druck von pumpen 2\nsetpoint ist der druck von pumpen 1\nproportional ist Delta p\n*/\n\n//if p1 > p2\n/*\nif(msg.proportional < 0){\n    if(msg.setpoint < 160){\n        return {payload : Math.abs(msg.proportional) + msg.integral*3.5}\n    }\n    return {payload : Math.abs(msg.proportional)}\n}\n//if p1 < p2 and p1 is off\nif(msg.proportional > 0 && msg.setpoint < 10){\n    return {payload : 0};\n}\n//if p1 < p2 and p1 is on\nelse if(msg.proportional > 0 && msg.setpoint > 10){\n    return {payload : msg.proportional*1 + msg.integral*5};\n}\n\nif(msg.proportional == 0){\n    return {payload : msg.integral*5};\n}*/\n\nmsg.payload = -msg.proportional*0.85+msg.integral*0.05;\n//msg.payload = -msg.proportional+msg.integral;\n\nif(msg.payload < 0){\n    msg.payload = 0;\n}\n\n\nif(msg.setpoint < 170){\n    msg.payload = msg.payload + 20;\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":780,"y":800,"wires":[["514d2f3e.1dde98","dbf3f5e5.233b2","cc3c788e.ff3678"]]},{"id":"514d2f3e.1dde98","type":"debug","z":"65dfb966.1b7cc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1170,"y":800,"wires":[]},{"id":"de7e5ae1.a45d3","type":"smooth","z":"65dfb966.1b7cc8","name":"","property":"payload","action":"sd","count":"15","round":"","mult":"single","reduce":false,"x":460,"y":1180,"wires":[["2b05b190.4b2fee"]]},{"id":"5b861b41.4e992c","type":"function","z":"65dfb966.1b7cc8","name":"PID","func":"var error = msg.payload;\nmsg.error = error;\nvar last_integral = context.get('last_integral')||0;\nmsg.integral = last_integral + error;\nmsg.pwm = (0.5*msg.error) + (0.3*msg.integral);\ncontext.set('last_integral', last_integral);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":690,"y":1400,"wires":[["7ae5514a.c8317"]]},{"id":"7ae5514a.c8317","type":"debug","z":"65dfb966.1b7cc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":850,"y":1400,"wires":[]},{"id":"389ca53c.bfb4c2","type":"function","z":"65dfb966.1b7cc8","name":"Integral Save","func":"var last_integral = 0;\nlast_integral = msg.integral;\nmsg.last_integral = last_integral;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":790,"y":1320,"wires":[[]]},{"id":"dbf3f5e5.233b2","type":"ui_text","z":"65dfb966.1b7cc8","group":"36afd270.efac66","order":4,"width":0,"height":0,"name":"After convert function","label":"After convert function:","format":"{{msg.payload}}","layout":"row-spread","x":760,"y":660,"wires":[]},{"id":"e4de5c4e.46d328","type":"function","z":"65dfb966.1b7cc8","name":"set setpoint","func":"msg.setpoint = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":330,"y":1000,"wires":[["37b5437d.465a44","2a686b4b.d03f9c"]]},{"id":"3b5ad16e.0c8b3e","type":"function","z":"65dfb966.1b7cc8","name":"set prop_band","func":"msg.prop_band = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":360,"y":1260,"wires":[["55f43441.6976d4","de7e5ae1.a45d3"]]},{"id":"7482ff.44d9bd","type":"smooth","z":"65dfb966.1b7cc8","name":"","property":"payload","action":"sd","count":"15","round":"","mult":"single","reduce":false,"x":600,"y":800,"wires":[["a8f193e0.591ea"]]},{"id":"37b5437d.465a44","type":"smooth","z":"65dfb966.1b7cc8","name":"","property":"payload","action":"sd","count":"20","round":"","mult":"single","reduce":false,"x":480,"y":1020,"wires":[["2b05b190.4b2fee"]]},{"id":"ef3a882f.acaf08","type":"mqtt in","z":"65dfb966.1b7cc8","name":"","topic":"lightSensor","qos":"2","datatype":"auto","broker":"e1bad40c.02766","x":120,"y":1680,"wires":[["f651fec8.12de6","acfd0c30.e44ea"]]},{"id":"f651fec8.12de6","type":"ui_gauge","z":"65dfb966.1b7cc8","name":"","group":"eb7dec31.43ef38","order":1,"width":0,"height":0,"gtype":"gage","title":"Light sensor","label":"units","format":"{{value}}","min":"0","max":"5","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":340,"y":1680,"wires":[]},{"id":"2a686b4b.d03f9c","type":"debug","z":"65dfb966.1b7cc8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":510,"y":940,"wires":[]},{"id":"dc588398.da27c8","type":"ui_chart","z":"65dfb966.1b7cc8","name":"Chart P1","group":"d0993a2e.f3ebd","order":3,"width":"10","height":"8","label":"P1","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"PID Output","dot":false,"ymin":"1","ymax":"3.5","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":"","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"x":240,"y":180,"wires":[[]]},{"id":"f93f7e3b.5b75d","type":"ui_chart","z":"65dfb966.1b7cc8","name":"Chart P2","group":"dcfb34ce.0c851","order":3,"width":"10","height":"8","label":"P2","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"PID Output","dot":false,"ymin":"1","ymax":"3.5","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":"","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"x":260,"y":400,"wires":[[]]},{"id":"acfd0c30.e44ea","type":"ui_chart","z":"65dfb966.1b7cc8","name":"Chart: Light sensor","group":"eb7dec31.43ef38","order":2,"width":0,"height":0,"label":"Light sensor","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"PID Output","dot":false,"ymin":"","ymax":"","removeOlder":"15","removeOlderPoints":"","removeOlderUnit":"60","cutout":"","useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"x":330,"y":1740,"wires":[[]]},{"id":"e1bad40c.02766","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"a8b58e7d.4d6b9","type":"ui_group","z":"","name":"Controller","tab":"47c62619.2856e8","order":5,"disp":true,"width":"6","collapse":false},{"id":"64012f99.40fcd","type":"ui_group","z":"","name":"Result","tab":"47c62619.2856e8","order":3,"disp":true,"width":"6","collapse":false},{"id":"36afd270.efac66","type":"ui_group","z":"","name":"Result mapping","tab":"47c62619.2856e8","order":4,"disp":true,"width":"6","collapse":false},{"id":"d0993a2e.f3ebd","type":"ui_group","z":"","name":"Pumpe 1","tab":"47c62619.2856e8","order":1,"disp":true,"width":"10","collapse":false},{"id":"dcfb34ce.0c851","type":"ui_group","z":"","name":"Pumpe 2","tab":"47c62619.2856e8","order":2,"disp":true,"width":"10","collapse":false},{"id":"eb7dec31.43ef38","type":"ui_group","z":"","name":"Light Sensor","tab":"47c62619.2856e8","order":6,"disp":true,"width":"10","collapse":false},{"id":"47c62619.2856e8","type":"ui_tab","z":"","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]

One more question, my pumps don't come from the same manufacturer and don't have the same maximal pressure. Muss both pumps from same manufacturer or it is not problem?

Recently, i have changed my idea and code. The last code you could find above. Please take a look.
Thank you very much

Did you see that in my post I said the chart needs to show both the output from the PID node (which you have not shown) as well as the process value (pump 2 pressure) and setpoint (pump 1 pressure). Preferably all on the same chart if possible.

I don't understand your flow, you appear to feeding pump 2 pressure in as proportional band instead of the process variable. Pump 1 pressure should be the setpoint, which you have (but see later), but pump 2 pressure should be fed in as the payload, setting the proportional band in the PID node config. It is very rare to have to change the proportional band dynamically. Also take the smooth node out from the wire feeding the process value, at least initially. All they do is make it more difficult for the PID algorithm to do its job. If it does become necessary then it can be put back later. It is ok to have one on the Setpoint signal from pump 1 so the setpoint is more stable, if it proves necessary.
Also note this comment in the help text, pariticularly the last sentence:

" Any message received with an attribute msg.<property_name> set will change the value of the associated property to the value of that attribute. For example setting msg.setpoint will change the setpoint. Multiple properties can be changed in the same message using this method. Note that messages of this type should still have the process value in msg.payload"

So if you use this technique for setting the setpoint then the payload is taken to be a process value. In your flow, when you set the setpoint you are also passing that value in as the current process value, so the process value fed in is alternately that from pump 1 and pump 2. It is better to use the method where the topic is used, so from pump 1 feed the pressure through a change node to set the topic to the string setpoint and then into the PID node. The pressure from pump 2 can be fed in directly to the PID node and will be taken as the process value.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.