UNPKG

advanced-pid-controller

Version:

An advanced PID controller for Node.js with anti-windup, deadband, and configurable output limits, developed by Marc Alzen @ Rasche und Wessler GmbH

121 lines (97 loc) 6.6 kB
/*** advanced-pid-controller/sample-application-template.js Copyright 2025, Marc Alzen @ Rasche & Wessler GmbH. This project began as a fork of `simple-pid-controller` by Harshad Joshi (hj91). His foundational work provided the basis for this enhanced PID controller. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ***/ // Import the PID Controller. Ensure the path is correct. // If you renamed the project (e.g., 'rasche-wessler-pid-controller'), adjust this accordingly. const PIDController = require('./index.js'); // Uses 'index.js' as it's the standard export. // --- Simulation Parameters --- const SIMULATION_DT_MS = 100; // Simulation step interval in milliseconds const TOTAL_SIMULATION_TIME_MS = 35000; // Total simulation time in milliseconds (35 seconds) let currentSimulationTimeMs = 0; // --- Process Parameters (Example: Simple Heating System) --- let currentTemperature = 20.0; // Current temperature in °C const ROOM_TEMPERATURE = 20.0; // Ambient room temperature const HEATING_RATE_FACTOR = 0.2; // Temperature change per unit of control output per second const COOLING_RATE_FACTOR = 0.5; // Natural cooling per degree difference from room temp per second // --- PID Controller Configuration --- const Kp = 0.1; // Proportional gain const Ki = 0.5; // Integral gain const Kd = 0.1; // Derivative gain const CONTROLLER_DT_SEC = SIMULATION_DT_MS / 1000; // dt for the controller in seconds // Controller output limits (e.g., heating power from 0% to 100%) const OUTPUT_MIN = 0; const OUTPUT_MAX = 100; // Deadband: +/- tolerance around the setpoint within which the controller output is zero const DEADBAND = 0.5; // +/- 0.5 °C // Create a PID Controller instance with all new parameters const controller = new PIDController(Kp, Ki, Kd, CONTROLLER_DT_SEC, OUTPUT_MIN, OUTPUT_MAX, DEADBAND); console.log("--- PID Controller Simulation Start ---"); console.log(`Initial Temperature: ${currentTemperature.toFixed(2)} °C`); console.log(`Controller dt: ${CONTROLLER_DT_SEC}s`); console.log(`Output Limits: ${controller.outputMin} to ${controller.outputMax}`); console.log(`Deadband: +/- ${controller.deadband} °C`); console.log("-------------------------------------"); // --- Simulation Loop --- const simulationInterval = setInterval(() => { currentSimulationTimeMs += SIMULATION_DT_MS; // --- Phase-based Target Changes & Feature Demonstrations --- if (currentSimulationTimeMs === 100) { controller.setTarget(25); // Phase 1: Initial target console.log(`\n\nTime: ${currentSimulationTimeMs / 1000}s - Phase 1: Target set to ${controller.target.toFixed(1)} °C.`); } else if (currentSimulationTimeMs === 10000) { controller.setTarget(95); // Phase 2: High target to demonstrate saturation and anti-windup console.log(`\n\nTime: ${currentSimulationTimeMs / 1000}s - Phase 2: Target set to ${controller.target.toFixed(1)} °C (Demonstrating Anti-Windup).`); } else if (currentSimulationTimeMs === 23000) { controller.setTarget(59.2); // Phase 3: Target close to current temp to demonstrate deadband console.log(`\n\nTime: ${currentSimulationTimeMs / 1000}s - Phase 3: Target set to ${controller.target.toFixed(1)} °C (Demonstrating Deadband).`); // Optionally, dynamically change deadband during runtime // controller.setDeadband(0.2); // console.log(`Deadband dynamically set to +/- ${controller.deadband} °C.`); } else if (currentSimulationTimeMs === 30000) { console.log(`\n\nTime: ${currentSimulationTimeMs / 1000}s - Demonstrating controller.reset().`); controller.reset(); // Demonstrates controller.reset() console.log("Controller state reset."); controller.setTarget(28); // Set a new target after reset console.log(`Target set to ${controller.target.toFixed(1)} °C after reset.`); } // Read the current process variable (PV) from the "sensor" const currentValue = currentTemperature; // Update the PID controller and get the control output const controlOutput = controller.update(currentValue); // --- Process Simulation --- // Calculate temperature increase from heater (based on control output) let tempChangeFromHeater = controlOutput * HEATING_RATE_FACTOR * CONTROLLER_DT_SEC; // Calculate natural cooling (system cools towards room temperature) let tempChangeFromCooling = (currentTemperature - ROOM_TEMPERATURE) * COOLING_RATE_FACTOR * CONTROLLER_DT_SEC; currentTemperature += tempChangeFromHeater - tempChangeFromCooling; let currentSimulationTimeSec = currentSimulationTimeMs / 1000 // Log current state and PID components if (currentSimulationTimeSec % 1 === 0){ console.log( `Time: ${currentSimulationTimeSec}.0s | Temp: ${currentTemperature.toFixed(2)} | Target: ${controller.target.toFixed(2)} | Output: ${controlOutput.toFixed(2)} | P:${controller.p.toFixed(2)} I:${controller.i.toFixed(2)} D:${controller.d.toFixed(2)}` ); } else { console.log( `Time: ${currentSimulationTimeSec}s | Temp: ${currentTemperature.toFixed(2)} | Target: ${controller.target.toFixed(2)} | Output: ${controlOutput.toFixed(2)} | P:${controller.p.toFixed(2)} I:${controller.i.toFixed(2)} D:${controller.d.toFixed(2)}` ); } // --- End Simulation --- if (currentSimulationTimeMs >= TOTAL_SIMULATION_TIME_MS) { clearInterval(simulationInterval); console.log("\n--- Simulation End ---"); console.log("Note: Observe 'Output' and 'I:' (Integral component)"); console.log("during saturation (e.g., Temp targets 40°C) and deadband demonstration (Temp around 21°C)."); console.log("Also observe 'D:' during setpoint changes (no strong 'kick' due to PV derivative)."); console.log("After the reset, I and D components should return to 0."); } }, SIMULATION_DT_MS);