pid controller configurable through menu
authorRobin Krens <robin@robinkrens.nl>
Tue, 23 Jan 2024 20:28:11 +0000 (21:28 +0100)
committerRobin Krens <robin@robinkrens.nl>
Tue, 23 Jan 2024 20:28:11 +0000 (21:28 +0100)
package.json
src/app.js
src/menu.js
src/menu.js.bak [new file with mode: 0644]
src/pid-controller.js
src/styles.css

index 4405128..4345c99 100644 (file)
   "author": "Robin Krens",
   "license": "ISC",
   "dependencies": {
-    "planck": "^1.0.0"
+    "planck": "^1.0.0",
+    "tweakpane": "^3.1.4"
   },
   "devDependencies": {
     "eslint": "^8.56.0",
-    "parcel": "^2.11.0",
-    "tweakpane": "^4.0.3"
+    "parcel": "^2.11.0"
   }
 }
index 49b3bb8..816d89d 100644 (file)
@@ -8,12 +8,12 @@ const ctx = canvas.getContext("2d");
 
 const world = World(Vec2(0, 0));
 
-canvas.width = 500;
-canvas.height = 500;
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
 
 const PPM = 30;
 
-const targetX = 10;
+var targetX = 10;
 const targetY = -2.5;
 
 // Define the box properties
@@ -25,10 +25,16 @@ const box = world.createDynamicBody(Vec2(1, -2.5));
 box.createFixture({
        shape: Box(boxWidth, boxHeight),
        density: 1.0,
+       friction: 0.01,
 });
 
-MENU.initMenu();
 const pid = new PIDController();
+MENU.initMenu(pid);
+
+canvas.addEventListener("click", function(event) {
+       targetX = (event.clientX - canvas.getBoundingClientRect().left) / PPM;
+       console.log("Updated targetX:", targetX);
+});
 
 // Render the box on the canvas
 function render() {
@@ -48,7 +54,7 @@ function render() {
        ctx.fillRect(position.x * PPM, (-position.y * PPM), boxWidth * PPM, boxHeight * PPM);
        ctx.restore();
        var r = pid.Update(1/60, position.x, targetX);
-       console.log(r);
+       //console.log(r);
        var force = {x: r, y: 0};
        box.applyForce(force, box.getWorldCenter());
 }
index 0e4e0b4..f78a5be 100644 (file)
@@ -1,12 +1,31 @@
 import {Pane} from "tweakpane";
 
-function initMenu()
+function initMenu(pid)
 {
        const pane = new Pane();
-       const tab = pane.addTab({
-               pages: [{title: "Creative Mode"}, {title: "Preset"}]
+       const pidSettings = pane.addFolder({
+               title: "PID settings"
+       });
+       pidSettings.addInput(
+               pid.PIDParams, "proportionalGain",
+               {min: 0, max: 10, step: 0.1}
+       );
+       
+       pidSettings.addInput(
+               pid.PIDParams, "derivativeGain",
+               {min: 0, max: 10, step: 0.1}
+       );
+       
+       pidSettings.addInput(
+               pid.PIDParams, "integralGain",
+               {min: 0, max: 10, step: 0.1}
+       );
+       pane.addMonitor(pid.PIDParams, "force", {
+               title: "Force (N)",
+               view: "graph",
+               min: -10000,
+               max: +10000,
        });
-
 }
 
 export {initMenu};
diff --git a/src/menu.js.bak b/src/menu.js.bak
new file mode 100644 (file)
index 0000000..946cf2b
--- /dev/null
@@ -0,0 +1,15 @@
+import {Pane} from "tweakpane";
+
+function initMenu(pid)
+{
+       const pane = new Pane();
+       const pidSettings = pane.addFolder({
+               title: "PID settings"
+       }); 
+       pidSettings.addInput(
+               pid.PIDParams, "proportionalGain",
+               {min: 0, max: 100, step: 1}
+       );
+}
+
+export {initMenu};
index 2ec15d2..31240a5 100644 (file)
@@ -4,10 +4,10 @@ class PIDController {
        //    ErrorRateOfChange
        //}
        constructor() {
-       this.PIDParams = {proportionalGain: 50, integralGain: 0.01, derivativeGain: 35, outputMin: -1, outputMax: 1, integralSaturation: false, derivativeInitialized: false};
+       this.PIDParams = {proportionalGain: 2, integralGain: 0.0, derivativeGain: 2, iMin: -1, iMax: 1, integralSaturation: false, derivativeInitialized: false, outputMax: 1, outputMin: -1, magnitude: 10, force: 0};
                this.valueLast; 
                this.errorLast;
-       this.integrationStored;
+       this.integrationStored = 0;
        this.velocity;
        }
        Reset() {
@@ -21,8 +21,8 @@ class PIDController {
         
                var P = this.PIDParams.proportionalGain * error;
         
-               this.integrationStored = Math.min(Math.max(this.integrationStored + (error * dt), -this.integralSaturation), -this.integralSaturation);
-               var I = this.integralGain * this.integrationStored;
+               this.integrationStored = Math.min(Math.max(this.integrationStored + (error * dt), this.PIDParams.iMin), this.PIDParams.iMax);
+               var I = this.PIDParams.integralGain * this.integrationStored;
 
                var errorRateOfChange = (error - this.errorLast) / dt;
                this.errorLast = error;
@@ -42,56 +42,15 @@ class PIDController {
                } else {
                        this.derivativeInitialized = true;
                }
-
                var D = this.PIDParams.derivativeGain * deriveMeasure;
-               var result = P + D;
-               return result;
+               var result = P + I + D;
+               result *= this.PIDParams.magnitude;
+               this.PIDParams.force = result * 60; /* this routine is called ~60seconds */
 
-               //return Math.min(Math.max((result, this.outputMin), this.outputMax));
+               //console.log(result*60 + " N");
+               return result;
+               //return Math.min(Math.max((result, this.PIDParams.outputMin), this.PIDParams.outputMax));
        }
-
-       // float AngleDifference(float a, float b) {
-       //     return (a - b + 540) % 360 - 180;   //calculate modular difference, and remap to [-180, 180]
-       // }
-
-       // public float UpdateAngle(float dt, float currentAngle, float targetAngle) {
-       //     if (dt <= 0) throw new ArgumentOutOfRangeException(nameof(dt));
-       //     float error = AngleDifference(targetAngle, currentAngle);
-
-       //     //calculate P term
-       //     float P = proportionalGain * error;
-
-       //     //calculate I term
-       //     integrationStored = Mathf.Clamp(integrationStored + (error * dt), -integralSaturation, integralSaturation);
-       //     float I = integralGain * integrationStored;
-
-       //     //calculate both D terms
-       //     float errorRateOfChange = AngleDifference(error, errorLast) / dt;
-       //     errorLast = error;
-
-       //     float valueRateOfChange = AngleDifference(currentAngle, valueLast) / dt;
-       //     valueLast = currentAngle;
-       //     velocity = valueRateOfChange;
-
-       //     //choose D term to use
-       //     float deriveMeasure = 0;
-
-       //     if (derivativeInitialized) {
-       //         if (derivativeMeasurement == DerivativeMeasurement.Velocity) {
-       //             deriveMeasure = -valueRateOfChange;
-       //         } else {
-       //             deriveMeasure = errorRateOfChange;
-       //         }
-       //     } else {
-       //         derivativeInitialized = true;
-       //     }
-
-       //     float D = derivativeGain * deriveMeasure;
-
-       //     float result = P + I + D;
-
-       //     return Mathf.Clamp(result, outputMin, outputMax);
-       // }
 }
 
 export default PIDController;
index 3ec4728..3b84f96 100644 (file)
@@ -8,8 +8,8 @@ body, html {
        margin: 0;
        padding: 0;
        display: block;
-       width: 500px;
-       height: 500px;
+       width: 100%;
+       height: 100%;
        background: #ccc;
 }