Files
generalFunctions/test/pid-controller.test.js
znetsixe 27a6d3c709 updates
2026-03-11 11:13:05 +01:00

106 lines
2.7 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const { PIDController, CascadePIDController } = require('../src/pid/index.js');
test('pid supports freeze/unfreeze with held output', () => {
const pid = new PIDController({
kp: 2,
ki: 0.5,
kd: 0.1,
sampleTime: 100,
outputMin: 0,
outputMax: 100,
});
const t0 = Date.now();
const first = pid.update(10, 2, t0 + 100);
pid.freeze({ output: first, trackMeasurement: true });
const frozen = pid.update(10, 4, t0 + 200);
assert.equal(frozen, first);
pid.unfreeze();
const resumed = pid.update(10, 4, t0 + 300);
assert.equal(Number.isFinite(resumed), true);
});
test('pid supports dynamic tunings and gain scheduling', () => {
const pid = new PIDController({
kp: 1,
ki: 0,
kd: 0,
sampleTime: 100,
outputMin: -100,
outputMax: 100,
gainSchedule: [
{ min: Number.NEGATIVE_INFINITY, max: 5, kp: 1, ki: 0, kd: 0 },
{ min: 5, max: Number.POSITIVE_INFINITY, kp: 3, ki: 0, kd: 0 },
],
});
const t0 = Date.now();
const low = pid.update(10, 9, t0 + 100, { gainInput: 4 });
const high = pid.update(10, 9, t0 + 200, { gainInput: 6 });
assert.equal(high > low, true);
const tuned = pid.update(10, 9, t0 + 300, { tunings: { kp: 10, ki: 0, kd: 0 } });
assert.equal(tuned > high, true);
});
test('pid applies deadband and output rate limits', () => {
const pid = new PIDController({
kp: 10,
ki: 0,
kd: 0,
deadband: 0.5,
sampleTime: 100,
outputMin: 0,
outputMax: 100,
outputRateLimitUp: 5, // units per second
outputRateLimitDown: 5, // units per second
});
const t0 = Date.now();
const out1 = pid.update(10, 10, t0 + 100); // inside deadband -> no action
const out2 = pid.update(20, 0, t0 + 200); // strong error but limited by rate
assert.equal(out1, 0);
// 5 units/sec * 0.1 sec = max 0.5 rise per cycle
assert.equal(out2 <= 0.5 + 1e-9, true);
});
test('cascade pid computes primary and secondary outputs', () => {
const cascade = new CascadePIDController({
primary: {
kp: 2,
ki: 0,
kd: 0,
sampleTime: 100,
outputMin: 0,
outputMax: 100,
},
secondary: {
kp: 1,
ki: 0,
kd: 0,
sampleTime: 100,
outputMin: 0,
outputMax: 100,
},
});
const t0 = Date.now();
const result = cascade.update({
setpoint: 10,
primaryMeasurement: 5,
secondaryMeasurement: 2,
timestamp: t0 + 100,
});
assert.equal(typeof result.primaryOutput, 'number');
assert.equal(typeof result.secondaryOutput, 'number');
assert.equal(result.primaryOutput > 0, true);
assert.equal(result.secondaryOutput > 0, true);
});