Plugin régulateur correcteur *PID* (proportionnel, intégral, dérivé)

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.

2 « J'aime »