Насколько я понял - классы не реализованы в этом диалекте js. Но они и не нужны.)
Вспомнил как работает.)
Для изменения направления ПИД-регулятора, в строке где происходит инициализация объекта, нужно указать параметр “reverse” или “direct”.
var ctr = new PID(temperature, temperatureSetpoint, Kp, Ki, Kd, 'reverse');
Перед тем как включать в работу второй ПИД, его сначала нужно объявить:
// Инициализация второго ПИД-регулятора
var temperature2 = dev["PIDTest/enabled2"];
var temperatureSetpoint2 = dev["PIDTest/setPoint2"];
var Kp2 = dev["PIDTest/kp2"],
Ki2 = dev["PIDTest/ki2"],
Kd2 = 0;
var ctr2 = new PID(temperature2, temperatureSetpoint2, Kp2, Ki2, Kd2, 'direct');
ctr2.setSampleTime(timeframe);
ctr2.setOutputLimits(0, maxOutput);
ctr2.setMode('auto');
Код всего правила
var PID = function(Input, Setpoint, Kp, Ki, Kd, ControllerDirection) {
this.input = Input;
this.mySetpoint = Setpoint;
this.inAuto = false;
this.setOutputLimits(0, 255); // default output limits
this.SampleTime = 100; // default Controller Sample Time is 0.1 seconds
this.setTunings(Kp, Ki, Kd);
this.setControllerDirection(ControllerDirection);
this.lastTime = this.millis() - this.SampleTime;
this.ITerm = 0;
this.myOutput = 0;
};
PID.prototype.setInput = function(current_value) {
this.input = current_value;
};
PID.prototype.setPoint = function(current_value) {
this.mySetpoint = current_value;
};
PID.prototype.millis = function() {
var d = new Date();
return d.getTime();
};
PID.prototype.compute = function() {
if (!this.inAuto) {
return false;
}
var now = this.millis();
var timeChange = (now - this.lastTime);
if (timeChange >= this.SampleTime) {
var input = this.input;
var error = this.mySetpoint - input;
this.ITerm += (this.ki * error);
var dInput = input - this.lastInput;
var output = (this.kp * error + this.ITerm - this.kd * dInput) * this.setDirection;
if (output > this.outMax) {
output = this.outMax;
} else if (output < this.outMin) {
output = this.outMin;
}
this.myOutput = output;
this.lastInput = input;
this.lastTime = now;
return true;
} else {
return false;
}
};
PID.prototype.setTunings = function(Kp, Ki, Kd) {
if (Kp < 0 || Ki < 0 || Kd < 0) {
return;
}
this.dispKp = Kp;
this.dispKi = Ki;
this.dispKd = Kd;
this.SampleTimeInSec = (this.SampleTime) / 1000;
this.kp = Kp;
this.ki = Ki * this.SampleTimeInSec;
this.kd = Kd / this.SampleTimeInSec;
};
PID.prototype.setSampleTime = function(NewSampleTime) {
if (NewSampleTime > 0) {
var ratio = NewSampleTime / (1.0 * this.SampleTime);
this.ki *= ratio;
this.kd /= ratio;
this.SampleTime = Math.round(NewSampleTime);
}
};
PID.prototype.setOutput = function(val) {
if (val > this.outMax) {
this.myOutput = val;
} else if (val < this.outMin) {
val = this.outMin;
}
this.myOutput = val;
};
PID.prototype.setOutputLimits = function(Min, Max) {
if (Min >= Max) {
return;
}
this.outMin = Min;
this.outMax = Max;
if (this.inAuto) {
if (this.myOutput > this.outMax) {
this.myOutput = this.outMax;
} else if (this.myOutput < this.outMin) {
this.myOutput = this.outMin;
}
if (this.ITerm > this.outMax) {
this.ITerm = this.outMax;
} else if (this.ITerm < this.outMin) {
this.ITerm = this.outMin;
}
}
};
PID.prototype.setMode = function(Mode) {
var newAuto;
if (Mode == PID.AUTOMATIC || Mode.toString().toLowerCase() == 'automatic' || Mode.toString().toLowerCase() == 'auto') {
newAuto = 1;
} else if (Mode == PID.MANUAL || Mode.toString().toLowerCase() == 'manual') {
newAuto = 0;
} else {
throw new Error("Incorrect Mode Chosen");
}
if (newAuto == !this.inAuto) {
this.initialize();
}
this.inAuto = newAuto;
};
PID.prototype.initialize = function() {
this.ITerm = this.myOutput;
this.lastInput = this.input;
if (this.ITerm > this.outMax) {
this.ITerm = this.outMax;
} else if (this.ITerm < this.outMin) {
this.ITerm = this.outMin;
}
};
PID.prototype.setControllerDirection = function(ControllerDirection) {
if (ControllerDirection == 0 || ControllerDirection.toString().toLowerCase() == 'direct') {
this.setDirection = 1;
} else if (ControllerDirection == 1 || ControllerDirection.toString().toLowerCase() == 'reverse') {
this.setDirection = -1;
} else {
throw new Error("Incorrect Controller Direction Chosen");
}
};
PID.prototype.getKp = function() {
return this.dispKp;
};
PID.prototype.getKd = function() {
return this.dispKd;
};
PID.prototype.getKi = function() {
return this.dispKi;
};
PID.prototype.getMode = function() {
return this.inAuto ? "Auto" : "Manual";
};
PID.prototype.getDirection = function() {
return this.controllerDirection;
};
PID.prototype.getOutput = function() {
return this.myOutput;
};
PID.prototype.getInput = function() {
return this.input;
};
PID.prototype.getSetPoint = function() {
return this.mySetpoint;
};
module.exports = PID;
// Создаем виртуальное устройство с несколькими ПИД-регуляторами
defineVirtualDevice("PIDTest", {
title: "PID",
cells: {
enabled1: {
type: "range",
value: 20,
max: 40,
min: 10
},
setPoint1: {
type: "range",
value: 20,
min: 15,
max: 40
},
log1: {
type: "text",
value: ''
},
kp1: {
type: "range",
value: 1,
min: 1,
max: 100,
},
ki1: {
type: "range",
value: 0,
min: 0,
max: 100,
},
enabled2: {
type: "range",
value: 20,
max: 40,
min: 10
},
setPoint2: {
type: "range",
value: 20,
min: 15,
max: 40
},
log2: {
type: "text",
value: ''
},
kp2: {
type: "range",
value: 1,
min: 1,
max: 100,
},
ki2: {
type: "range",
value: 0,
min: 0,
max: 100,
}
},
});
// Время вызова
var timeframe = 2000;
var maxOutput = 10000;
// Инициализация первого ПИД-регулятора
var temperature1 = dev["PIDTest/enabled1"];
var temperatureSetpoint1 = dev["PIDTest/setPoint1"];
var Kp1 = dev["PIDTest/kp1"],
Ki1 = dev["PIDTest/ki1"],
Kd1 = 0;
var ctr1 = new PID(temperature1, temperatureSetpoint1, Kp1, Ki1, Kd1, 'direct');
ctr1.setSampleTime(timeframe);
ctr1.setOutputLimits(0, maxOutput);
ctr1.setMode('auto');
// Инициализация второго ПИД-регулятора
var temperature2 = dev["PIDTest/enabled2"];
var temperatureSetpoint2 = dev["PIDTest/setPoint2"];
var Kp2 = dev["PIDTest/kp2"],
Ki2 = dev["PIDTest/ki2"],
Kd2 = 0;
var ctr2 = new PID(temperature2, temperatureSetpoint2, Kp2, Ki2, Kd2, 'direct');
ctr2.setSampleTime(timeframe);
ctr2.setOutputLimits(0, maxOutput);
ctr2.setMode('auto');
//цикл работы ПИД-а
var myControl = function() {
// Обработка первого ПИД-регулятора
var temperature1 = dev["PIDTest/enabled1"];
var temperatureSetpoint1 = dev["PIDTest/setPoint1"];
var Kp1 = dev["PIDTest/kp1"],
Ki1 = dev["PIDTest/ki1"];
ctr1.setInput(temperature1);
ctr1.setPoint(temperatureSetpoint1);
ctr1.setTunings(Kp1, Ki1, Kd1);
ctr1.compute();
var output1 = ctr1.getOutput();
log("Output1 : " + output1);
dev["PIDTest/log1"] = String(output1);
// Обработка второго ПИД-регулятора
var temperature2 = dev["PIDTest/enabled2"];
var temperatureSetpoint2 = dev["PIDTest/setPoint2"];
var Kp2 = dev["PIDTest/kp2"],
Ki2 = dev["PIDTest/ki2"];
ctr2.setInput(temperature2);
ctr2.setPoint(temperatureSetpoint2);
ctr2.setTunings(Kp2, Ki2, Kd2);
ctr2.compute();
var output2 = ctr2.getOutput();
log("Output2 : " + output2);
dev["PIDTest/log2"] = String(output2);
};
setInterval(myControl, timeframe);