Stabilizing a PID controller in Node-RED

I am currently working on a project that involves maintaining a setpoint of 20,000 using a PID controller in Node-RED. The goal is to use a slider to simulate a variable input and ensure that the charge remains above the setpoint at a rate of 250ms.

I have set up the PID node in Node-RED, with the setpoint and sample time set to 20,000 and 250ms respectively. I am also using a slider to simulate the variable input. However, I am currently facing some challenges in terms of getting the remanent of over 20,000 and I am not sure if my approach is correct.

I am trying to optimize the PID controller by adjusting the proportional, integral and derivative values, but I am still having trouble in controlling the oscillation of the PID and keeping the charge above the setpoint. I am also not sure if I need a loop with an inject node in the PID setup.

I am looking for guidance on the correct approach to take in order to achieve my goal of maintaining the charge above the setpoint using a PID controller in Node-RED.

[
    {
        "id": "25511a172a9f40cf",
        "type": "tab",
        "label": "Test",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "79e0595bde8dcf14",
        "type": "function",
        "z": "25511a172a9f40cf",
        "name": "function 27",
        "func": "var sma = 50000;\nvar slider = global.get(\"slider\");\nvar curtailing = msg.payload * sma;\nglobal.set(\"sliderm\", slider - curtailing);\n\nmsg.curtailing = curtailing;\nmsg.percent = msg.payload * 100;\nmsg.sliderm = global.get(\"sliderm\");\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 790,
        "y": 520,
        "wires": [
            [
                "bbd9bef796e86996",
                "73b3792ab99fa833",
                "623a2405a00adb8e",
                "0d5ed7ecb0513345"
            ]
        ]
    },
    {
        "id": "5c305fd8ab339896",
        "type": "ui_chart",
        "z": "25511a172a9f40cf",
        "name": "",
        "group": "4a2c7d37a008276e",
        "order": 1,
        "width": "22",
        "height": "6",
        "label": "chart",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": 1,
        "removeOlderPoints": "200",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "outputs": 1,
        "useDifferentColor": false,
        "className": "",
        "x": 1310,
        "y": 500,
        "wires": [
            []
        ]
    },
    {
        "id": "0e6106264f21c19f",
        "type": "PID",
        "z": "25511a172a9f40cf",
        "name": "PID",
        "setpoint": "20000",
        "pb": "-400",
        "ti": "10",
        "td": "0.01",
        "integral_default": "0",
        "smooth_factor": "0",
        "max_interval": "0.2",
        "enable": "1",
        "disabled_op": "0",
        "x": 610,
        "y": 520,
        "wires": [
            [
                "79e0595bde8dcf14"
            ]
        ]
    },
    {
        "id": "bbd9bef796e86996",
        "type": "change",
        "z": "25511a172a9f40cf",
        "name": "Slider",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Slider",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "slider",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1030,
        "y": 460,
        "wires": [
            [
                "5c305fd8ab339896",
                "0abaab7e082aba32"
            ]
        ]
    },
    {
        "id": "73b3792ab99fa833",
        "type": "change",
        "z": "25511a172a9f40cf",
        "name": "Curtailing",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Curtailing",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "curtailing",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1040,
        "y": 560,
        "wires": [
            [
                "5c305fd8ab339896",
                "954ae1bcc34e5cb6"
            ]
        ]
    },
    {
        "id": "623a2405a00adb8e",
        "type": "change",
        "z": "25511a172a9f40cf",
        "name": "Percent",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "Percent",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "percent",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1040,
        "y": 660,
        "wires": [
            [
                "a71fef6820276dd3"
            ]
        ]
    },
    {
        "id": "0d5ed7ecb0513345",
        "type": "change",
        "z": "25511a172a9f40cf",
        "name": "Percent",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "SliderM",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "sliderm",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1040,
        "y": 760,
        "wires": [
            [
                "11d5ad61356a59e1",
                "5c305fd8ab339896"
            ]
        ]
    },
    {
        "id": "b6d75aefa43ac238",
        "type": "change",
        "z": "25511a172a9f40cf",
        "name": "",
        "rules": [
            {
                "t": "move",
                "p": "payload.data",
                "pt": "msg",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1030,
        "y": 340,
        "wires": [
            [
                "5c305fd8ab339896"
            ]
        ]
    },
    {
        "id": "c3e9fe026c39c6ae",
        "type": "function",
        "z": "25511a172a9f40cf",
        "name": "Get Context",
        "func": "var sliderm = global.get(\"sliderm\") || 0;\nmsg.payload = global.get(\"slider\");\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 410,
        "y": 520,
        "wires": [
            [
                "0e6106264f21c19f",
                "1a1bf85ffe13a0f7"
            ]
        ]
    },
    {
        "id": "0abaab7e082aba32",
        "type": "ui_numeric",
        "z": "25511a172a9f40cf",
        "name": "",
        "label": "Slider",
        "tooltip": "",
        "group": "4a2c7d37a008276e",
        "order": 2,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": 0,
        "max": 10,
        "step": 1,
        "className": "",
        "x": 1030,
        "y": 420,
        "wires": [
            []
        ]
    },
    {
        "id": "954ae1bcc34e5cb6",
        "type": "ui_numeric",
        "z": "25511a172a9f40cf",
        "name": "",
        "label": "Curtailing",
        "tooltip": "",
        "group": "4a2c7d37a008276e",
        "order": 4,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": 0,
        "max": 10,
        "step": 1,
        "className": "",
        "x": 1040,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "a71fef6820276dd3",
        "type": "ui_numeric",
        "z": "25511a172a9f40cf",
        "name": "",
        "label": "Percent",
        "tooltip": "",
        "group": "4a2c7d37a008276e",
        "order": 4,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}} %",
        "min": 0,
        "max": 10,
        "step": 1,
        "className": "",
        "x": 1040,
        "y": 620,
        "wires": [
            []
        ]
    },
    {
        "id": "11d5ad61356a59e1",
        "type": "ui_numeric",
        "z": "25511a172a9f40cf",
        "name": "",
        "label": "SliderM",
        "tooltip": "",
        "group": "4a2c7d37a008276e",
        "order": 4,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": 0,
        "max": 10,
        "step": 1,
        "className": "",
        "x": 1040,
        "y": 720,
        "wires": [
            []
        ]
    },
    {
        "id": "c49301c210bb3512",
        "type": "inject",
        "z": "25511a172a9f40cf",
        "name": "Clear chart on deploy",
        "repeat": "",
        "crontab": "",
        "once": true,
        "topic": "",
        "payload": "{\"data\":[]}",
        "payloadType": "json",
        "x": 740,
        "y": 340,
        "wires": [
            [
                "b6d75aefa43ac238"
            ]
        ]
    },
    {
        "id": "efc16d9579745d5c",
        "type": "function",
        "z": "25511a172a9f40cf",
        "name": "Send to context",
        "func": "global.get(\"slider\") || 0;\nglobal.set(\"slider\", msg.payload);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 560,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "1a1bf85ffe13a0f7",
        "type": "debug",
        "z": "25511a172a9f40cf",
        "name": "debug 6",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 780,
        "y": 760,
        "wires": []
    },
    {
        "id": "6dc31edeeb256a12",
        "type": "inject",
        "z": "25511a172a9f40cf",
        "name": "enable",
        "repeat": "",
        "crontab": "",
        "once": false,
        "topic": "enable",
        "payload": "true",
        "payloadType": "bool",
        "x": 390,
        "y": 360,
        "wires": [
            [
                "0e6106264f21c19f"
            ]
        ]
    },
    {
        "id": "03d3c34234ad397b",
        "type": "inject",
        "z": "25511a172a9f40cf",
        "name": "disable",
        "repeat": "",
        "crontab": "",
        "once": false,
        "topic": "enable",
        "payload": "false",
        "payloadType": "bool",
        "x": 390,
        "y": 400,
        "wires": [
            [
                "0e6106264f21c19f"
            ]
        ]
    },
    {
        "id": "5da1e3f0f598245b",
        "type": "inject",
        "z": "25511a172a9f40cf",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "0.5",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 190,
        "y": 520,
        "wires": [
            [
                "c3e9fe026c39c6ae"
            ]
        ]
    },
    {
        "id": "c5fa654d6e3d96f8",
        "type": "ui_slider",
        "z": "25511a172a9f40cf",
        "name": "",
        "label": "slider",
        "tooltip": "",
        "group": "4a2c7d37a008276e",
        "order": 12,
        "width": 0,
        "height": 0,
        "passthru": true,
        "outs": "all",
        "topic": "topic",
        "topicType": "msg",
        "min": "-10000",
        "max": "50000",
        "step": 1,
        "className": "",
        "x": 210,
        "y": 260,
        "wires": [
            [
                "efc16d9579745d5c"
            ]
        ]
    },
    {
        "id": "4a2c7d37a008276e",
        "type": "ui_group",
        "name": "Controls",
        "tab": "ae7513ea3f975290",
        "order": 2,
        "disp": true,
        "width": "27",
        "collapse": false,
        "className": ""
    },
    {
        "id": "ae7513ea3f975290",
        "type": "ui_tab",
        "name": "Tab 1",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": false
    }
]

Welcome to the forum @diegitoo113

Are you attempting to simulate a real process? As you have it, the output of the PID does not affect the input so I don't see how it is ever going to do anything.

I see you have a proportional band of -400, pb should always be positive, I have no idea what effect a -ve number will have on the pid algorithm, it was never tested with -ve numbers.

Can you explain in a little more detail exactly what you are trying to do?

Thank you for your previous message. I understand that my configuration may not be correct, and I appreciate your feedback.

Regarding my setup, I am currently using a slider as the input for my simulation and attempting to maintain a setpoint value of 20,000. My proportional band value is set to 40000 and integral time is set to 60, with a derivative value of 0. I hope that with a static input value, my output will gradually try to correct the error and reach the setpoint value. However, I am currently encountering an issue where the integral is not calculating the error. I would greatly appreciate any suggestions or advice to help resolve this issue.

You have the max sample interval set to 0.2 seconds. Max sample interval, as described in the help text, is there to detect a fault condition. If there is more than 0.2 seconds between samples then it locks the integral. Since you are supplying samples every 0.5 seconds it is permanently in the locked state. Set max sample interval to 5 seconds and see what happens.

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