C’est toujours aussi compliqué, mais je mets à disposition le code de ma fonction Node-RED. Il reprend ce que j’avais fait pour Jeedom. Le petit changement : la variation de l’erreur ne prend plus en compte la consigne (on évite ainsi les problèmes de sauts de consigne).
var root = 'mqtt';
var piece = msg.topic;
var equipement = 'regulateur';
var prefixe = root+'.'+piece+'.'+equipement+'.';
// Ouverture min/max de la vanne thermostatique
var VALEUR_MIN = 0;
var VALEUR_MAX = 99;
// La période d'échantillonnage Te correspond à la période, exprimée en heure, où est lancé ce node
// Si ce node est exécuté toutes les 5 minutes, alors il faut que Te = 5/60
var Te = 10/60;
//var Consigne = global.get(prefixe+'consigne');
var Mesure = global.get(prefixe+'mesure');
var Kp = global.get(prefixe+'kp');
var Ki = global.get(prefixe+'ki');
var Kd = global.get(prefixe+'kd');
var N = global.get(prefixe+'filtrage');
var Offset = global.get(prefixe+'offset');
var Regulation = global.get(prefixe+'regulation');
var Erreur = global.get(prefixe+'erreur');
var Erreur_Integration = global.get(prefixe+'erreur_integration');
var Erreur_Variation = global.get(prefixe+'erreur_variation');
var Erreur_Saturation = global.get(prefixe+'erreur_saturation');
var Correction = global.get(prefixe+'correction');
equipement = 'radiateur';
prefixe = root+'.'+piece+'.'+equipement+'.';
var Consigne = global.get(prefixe+'consigne');
var Temperature = global.get(prefixe+'temperature');
var debug = {};
debug.topic = msg.topic;
debug.payload = {
"Consigne" : Consigne,
"Temperature" : Temperature,
"Mesure" : Mesure,
"Kp" : Kp,
"Ki":Ki,
"Kd":Kd,
"N":N,
"Offset":Offset,
"Regulation":Regulation,
"Erreur":Erreur,
"Erreur_Integration":Erreur_Integration,
"Erreur_Variation":Erreur_Variation,
"Erreur_Saturation":Erreur_Saturation,
"Correction":Correction
}
if (Consigne == undefined) {
Consigne = 19;
}
if (Temperature == undefined) {
Temperature = 18.5;
}
if (Mesure == undefined) {
Mesure = 18;
}
if (Kp == undefined) {
Kp = 20;
}
if (Ki == undefined) {
Ki = 5;
}
if (Kd == undefined) {
Kd = 10;
}
if (N == undefined) {
N = 0;
}
if (Offset == undefined) {
Offset = 25; // 25% d'ouverture lorsque la consigne est atteinte si pas d'intégrateur dans la régulation
}
if (Regulation == undefined) {
Regulation = "P";
}
if (Erreur == undefined) {
Erreur = 0;
}
if (Erreur_Integration == undefined) {
Erreur_Integration = 0;
}
if (Erreur_Variation == undefined) {
Erreur_Variation = 0;
}
if (Erreur_Saturation == undefined) {
Erreur_Saturation = 0;
}
if (Correction == undefined) {
Correction = 0;
}
var Td = Kd/Kp;
var Ti = Kp/Ki;
// Ts : periode d'intégration de l'erreur de saturation
// Ts = [0.1 Ti, Ti]
// Ts = Ti pour un PI
var Ts = Ti;
// Ts = sqrt(Ti*Td) pour un PID
//var Ts = sqrt(Ti*Td); // Ts = Ti/2 si Td=Ti/4
// Valeurs précédentes
var Ki_Precedent = Ki;
var Mesure_Precedente = Mesure;
var Erreur_Precedente = Erreur;
var Erreur_Integration_Precedente = Erreur_Integration;
var Erreur_Variation_Precedente = Erreur_Variation;
var Erreur_saturation_Precedente = Erreur_Saturation;
//var Correction_Precedente = Correction;
// Nouvelle mesure
Mesure = Temperature;
// Erreur proportionnelle
Erreur = Consigne - Mesure;
// Recalcul de l'erreur d'intégration précédente car le % d'ouverture de vanne due a l'integration doit rester constant lors d'un changement de Ki
Erreur_Integration_Precedente = Ki_Precedent*Erreur_Precedente/Ki;
// La methode des trapezes pour le calcul de l’integration permet un calcul plus precis
Erreur_Integration = Erreur_Integration_Precedente + Te*(Erreur + Erreur_Precedente)/2;
# Sauvegarde de l'erreur proportionnelle
Erreur_Proportionnelle = Erreur;
// Erreur derivee
// Pas de dérivation des changements de consigne (comme si la consigne était constante)
// Ainsi, il n'y a pas de discontinuité de la dérivée (variation de l'erreur)
// La variation d'une constante vaut toujours 0 donc la constante peut prendre n'importe quelle valeur : 0 pour simplifier
Erreur = 0 - Mesure;
Erreur_Precedente = 0 - Mesure_Precedente;
// Filtrage
if (N == 0) {
// pas de filtrage
Erreur_Variation = (Erreur = - Erreur_Precedente)/Te;
} else {
Erreur_Variation = (Td*Erreur_Variation_Precedente + N*(Erreur - Erreur_Precedente))/(Td+N*Te);
}
// Récupération de l'erreur proportionnelle
Erreur = Erreur_Proportionnelle;
// Type de regulation
if (Regulation == "P" || Regulation == "PI" || Regulation == "PD" || Regulation == "PID") {
Terme_Proportionnel = Kp*Erreur;
} else {
Terme_Proportionnel = 0;
}
if (Regulation == "I" || Regulation == "PI" || Regulation == "ID" || Regulation == "PID") {
Terme_Integration = Ki*Erreur_Integration;
Terme_Constant = 0;
} else {
Terme_Integration = 0;
Terme_Constant = Offset;
}
if (Regulation == "D" || Regulation == "PD" || Regulation == "ID" || Regulation == "PID") {
Terme_Derive = Kd*Erreur_Variation;
} else {
Terme_Derive = 0;
}
Correction = Terme_Constant + Terme_Proportionnel + Terme_Integration + Terme_Derive;
// Limitation de l'ouverture de la vanne pour que la vanne ne sorte pas de l'intervalle de 0% à 100%.
if (Correction < VALEUR_MIN) {
Correction_limitee = VALEUR_MIN;
} else if (Correction > VALEUR_MAX) {
Correction_limitee = VALEUR_MAX;
} else {
Correction_limitee = Correction;
}
// Erreur de saturation
Erreur_Saturation = (Correction - Correction_limitee)/Kp;
//Si le régulateur possède une composante intégrale, alors on recalcule l'erreur d'intégration en lui retranchant une fraction (>=1) de l’intégration de l'erreur de saturation
//Avec un Ts=0.5*Ti, Ti/Ts=2, l'erreur d'intégration diminue 2 fois plus avec l'erreur de saturation qu'elle augmente avec l'erreur, ainsi la vanne idéale revient vers les limites physiques de la vanne réelle
Erreur_Integration = Erreur_Integration - (Ti/Ts)*Te*Erreur_Saturation;
// Methode trapeze
//Erreur_Integration = Erreur_Integration - (Ti/Ts)*Te*(Erreur_Saturation + Erreur_Saturation_Precedente)/2;
/*
// Mise a jour des variable globales
global.set(prefixe+'mesure',Mesure)
global.set(prefixe+'ki',Ki);
global.set(prefixe+'erreur',Erreur);
global.set(prefixe+'erreur_integration',Erreur_Integration);
global.set(prefixe+'erreur_variation',Erreur_Variation);
global.set(prefixe+'erreur_saturation',Erreur_Saturation);
//msg.payload = Correction_limitee;
*/
msg.payload = {
//'consigne' : Consigne,
'mesure' : Mesure,
'kp' : Kp,
'ki' : Ki,
'kd' : Kd,
'filtrage' : N,
'offset' : Offset,
'regulation' : Regulation,
'erreur' : Erreur,
'erreur_integration' : Erreur_Integration,
'erreur_variation' : Erreur_Variation,
'erreur_saturation' : Erreur_Saturation,
'correction' : Correction
}
debug.payload.sortie = msg.payload;
return [msg, debug];
Code à adapter selon vos besoins.