Pour observer le comportement du régulateur de puissance sur le RMS il est utile de pouvoir superposer la courbe Sortie ( Triac ou SSR) sur la courbe de puissance, sur un periode de 10 min plutot que quelques secondes.
j'ai donc mis cette courbe de sortie à la place de la puissance VA ( qui ne sert pas à grand chose) sur le premier graphe de la page d'accueil.
La problematique etant la différence d'echelle entre la puissance qui s'auto ajuste et la sortie qui est limitée entre 0 et 100%
J'ai donc mis deux echelles différentes pour les deux courbes , la courbe Sortie regulateur entre 0 et 100 qui est aussi possible de rendre auto ajustable en cochant la case auto pour "zoomer" si besoin
Le resultat ici
https://f1atb.fr/forum_f1atb/attachment.php?aid=4934
Pour ceux qui le souhaite je decris ci dessous ce qu'il faut faire sur la base d'une version 16.09 , par contre je ne peux pas vous mettre vraiement les numeros de lignes car j'ai fait d'autres modifications mais il vous suffira de faire les recherches avec les mots clés que je donne.
1) rajouter la variable globale pidout quelques part dans le fichier Solar_Router_V16_09.ino
2) dans le fichier JS_Accueil.h remplacer entierement la function Plot(SVG, Tab, couleur1... par celle ci-dessous. ne vous inquietez pas elle reste compatible par les différents appels car tout ce que j'ai rajouté est en optionnel
3) dans le fichier Server.ino remplacer la VA par pidout , pour simplifier les explication remplacer la void handleAjaxData() par celle ci-dessous
4) retour sur dans le fichier Solar_Router_V16_09.ino pour préremplir les tableau avec pidout à la place de la VA
rechercher les lignes
if (tps - previousTimer2sMillis > 2000) {
unsigned long dt = tps - previousTimer2sMillis;
previousTimer2sMillis += 2000; //Pou caler exactement à 2s
tabPw_Maison_2s[IdxStock2s] = PuissanceS_M - PuissanceI_M;
tabPw_Triac_2s[IdxStock2s] = PuissanceS_T - PuissanceI_T;
et juste apres modifier la ligne
tabPva_Maison_2s[IdxStock2s] = PVAS_M - PVAI_M;
par
tabPva_Maison_2s[IdxStock2s] = pidout;//PVAS_M - PVAI_M; //LBE
5) et pour finir toujour dans le fichier Solar_Router_V16_09
chercher la ligne
Retard[i] = round(RetardF[i]); //Valeure entiere pour piloter le Triac et les relais
et juste apres rajouter
pidout=1*(100-Retard[i]);
Voilà ca fait pas mal de petits endroits à modifier mais qui ne sera pas un obstacle vule niveau de compétence croisé ici
Je vous proposerai prochainement de modifier l'algorithme PID d 'André pour le rendre beaucop plus réactif lors des changements brusques sans le passer en autooscillation. Le probleme du PID d'André est que pour anticiper il faudrait mettre de l action Dérivée, mais pour éviter les sauts brusques de la Dérivée provoqués par l’échantillonnage, André a été obligé de mettre un filtrage "fort" de cette action ce qui introduit un retard, et donc anticiper avec du retard ca fonctionne pas top.
Sans dérivée le régulateur ne bouge pas sa sortie tant que l erreur ne change pas de signe ( intégrale et proportionnel) ce qui conduit à de forts dépassements lors des changement de production ( Nuages) ou de consommation ( arrêt marche d'un gros consommateur) , je vous expliquerai le moment venu le principe du nouvel algorithme mis en place par la technique de l anti-windup avec back-calculation , qui permet de desaturer l intégrale avant le changement de signe de l erreur
j'ai donc mis cette courbe de sortie à la place de la puissance VA ( qui ne sert pas à grand chose) sur le premier graphe de la page d'accueil.
La problematique etant la différence d'echelle entre la puissance qui s'auto ajuste et la sortie qui est limitée entre 0 et 100%
J'ai donc mis deux echelles différentes pour les deux courbes , la courbe Sortie regulateur entre 0 et 100 qui est aussi possible de rendre auto ajustable en cochant la case auto pour "zoomer" si besoin
Le resultat ici
https://f1atb.fr/forum_f1atb/attachment.php?aid=4934
Pour ceux qui le souhaite je decris ci dessous ce qu'il faut faire sur la base d'une version 16.09 , par contre je ne peux pas vous mettre vraiement les numeros de lignes car j'ai fait d'autres modifications mais il vous suffira de faire les recherches avec les mots clés que je donne.
1) rajouter la variable globale pidout quelques part dans le fichier Solar_Router_V16_09.ino
Code :
int pidout=0;2) dans le fichier JS_Accueil.h remplacer entierement la function Plot(SVG, Tab, couleur1... par celle ci-dessous. ne vous inquietez pas elle reste compatible par les différents appels car tout ce que j'ai rajouté est en optionnel
Code PHP :
function Plot(SVG, Tab, couleur1, titre1, couleur2, titre2, echelleAutoY2, memeEchelle) {
var Vmax = 0;
var Vmin = 0;
var Vmax2 = 0; // Max pour la deuxième courbe
var Vmin2 = 0; // Min pour la deuxième courbe
var TabY0 = [];
var TabY1 = [];
couleur1 = "#" + couleur1;
couleur2 = "#" + couleur2;
var dX = 900 / Tab.length;
const d = new Date();
var dI = 1;
var label = 'heure';
var pixelTic = 72;
var dTextTic = 4;
var moduloText = 24;
var H0 = d.getHours() + d.getMinutes() / 60;
var H00 = 4 * Math.floor(H0 / 4);
var X0 = 18 * (H00 - H0);
var Y0 = 250;
var Yamp = 230;
var dy = 2;
var dispVA = false;
var echellePID = 100; // Échelle fixe pour le PID par défaut
// Si echelleAutoY2 n'est pas défini, on utilise false par défaut (échelle fixe 0-100)
if (echelleAutoY2 === undefined) {
echelleAutoY2 = false;
}
// Si memeEchelle n'est pas défini, on utilise false par défaut
if (memeEchelle === undefined) {
memeEchelle = false;
}
switch (SVG) {
case 'SVG_PW48hM':
break;
case 'SVG_PW48hT':
break;
case 'SVG_Temp48h':
Y0 = 450;
Yamp = 430;
dy = 1;
break;
case 'SVG_PW2sM':
label = 'mn';
pixelTic = 90;
X0 = 0;
dTextTic = 1;
moduloText = -100;
H00 = 0;
dI = 2; //2 courbes PW et PVA
GID(SVG + '_L').style = 'color:' + couleur2 + ';display:block;';
dispVA = GID(SVG + '_C').checked; //Plot courbe VA
localStorage.setItem(SVG + '_LS', dispVA);
break;
case 'SVG_PW2sT':
label = 'mn';
pixelTic = 90;
X0 = 0;
dTextTic = 1;
moduloText = -100;
H00 = 0;
dI = 2; //2 courbes PW et PVA
GID(SVG + '_L').style = 'color:' + couleur2 + ';display:block;';
dispVA = GID(SVG + '_C').checked; //Plot courbe VA
localStorage.setItem(SVG + '_LS', dispVA);
break;
case 'SVG_Wh1an':
label = 'Mois';
pixelTic = dX * 30.4375;//Mois moyen
var dTextTic = 1;
moduloText = 12;
H00 = d.getMonth();
X0 = dX * (1 - d.getDate());
var Mois = ['Jan', 'Fev', 'Mars', 'Avril', 'Mai', 'Juin', 'Juil', 'Août', 'Sept', 'Oct', 'Nov', 'Dec'];
break;
}
// Calcul du max pour la courbe principale (indices pairs)
for (var i = 0; i < Tab.length; i++) {
if (i % 2 == 0 || dI == 1) {
Tab[i] = Math.min(Tab[i], 10000000);
Tab[i] = Math.max(Tab[i], -10000000);
Vmax = Math.max(Math.abs(Tab[i]), Vmax);
}
}
// Si même échelle, calculer le max des DEUX courbes ensemble
if (dI == 2 && memeEchelle && dispVA) {
for (var i = 1; i < Tab.length; i = i + 2) {
Tab[i] = Math.min(Tab[i], 10000000);
Tab[i] = Math.max(Tab[i], -10000000);
Vmax = Math.max(Math.abs(Tab[i]), Vmax); // Utilise Vmax pour les deux courbes
}
}
// Calcul du max pour la deuxième courbe (indices impairs) si échelle auto demandée et pas même échelle
if (dI == 2 && echelleAutoY2 && !memeEchelle && dispVA) {
for (var i = 1; i < Tab.length; i = i + 2) {
Tab[i] = Math.min(Tab[i], 10000000);
Tab[i] = Math.max(Tab[i], -10000000);
Vmax2 = Math.max(Math.abs(Tab[i]), Vmax2);
}
}
var cadrageMax = 1;
var cadrage1 = 1000000;
var cadrage2 = [10, 8, 5, 4, 2, 1];
for (var m = 0; m < 7; m++) {
for (var i = 0; i < cadrage2.length; i++) {
var X = cadrage1 * cadrage2[i];
if ((Vmax) <= X) cadrageMax = X;
}
cadrage1 = cadrage1 / 10;
}
// Calcul du cadrage pour la deuxième courbe
var cadrageMax2 = 100; // Par défaut échelle fixe 0-100
if (memeEchelle) {
// Si même échelle demandée, utiliser la même que la courbe 1
cadrageMax2 = cadrageMax;
echellePID = cadrageMax;
} else if (echelleAutoY2) {
// Si échelle auto demandée (et pas même échelle)
cadrageMax2 = 1;
cadrage1 = 1000000;
for (var m = 0; m < 7; m++) {
for (var i = 0; i < cadrage2.length; i++) {
var X = cadrage1 * cadrage2[i];
if ((Vmax2) <= X) cadrageMax2 = X;
}
cadrage1 = cadrage1 / 10;
}
echellePID = cadrageMax2; // Utilise l'échelle calculée
}
var c1 = '"' + couleur1 + '"';
var c2 = '"' + couleur2 + '"';
var cT = "#" + Koul[Coul_Graphe][1];
var style = 'background:linear-gradient( #' + Koul[Coul_Graphe][5] + ',#' + Koul[Coul_Graphe][3] + ',#' + Koul[Coul_Graphe][5] + ');border-color:#' + Koul[Coul_Tab][5] + ';';
var S = "<svg viewbox='0 0 1030 500' style='" + style + "' height='500' width='100%' id='S_" + SVG + "' onmousemove ='DispVal(this,event);' >";
// Axe vertical gauche (Puissance Active)
S += "<line x1='100' y1='20' x2='100' y2='480' style='stroke:" + cT + ";stroke-width:2' />";
S += "<line x1='100' y1='" + Y0 + "' x2='1000' y2='" + Y0 + "' style='stroke:" + cT + ";stroke-width:2' />";
// Graduations horizontales (temps)
for (var x = 1000 + X0; x > 100; x = x - pixelTic) {
var X = x;
var Y2 = Y0 + 6;
S += "<line x1='" + X + "' y1='" + Y0 + "' x2='" + X + "' y2='" + Y2 + "' style='stroke:" + cT + ";stroke-width:2' />";
X = X - 8;
Y2 = Y0 + 22;
if (SVG == 'SVG_Wh1an') {
X = X + 8;
S += "<text x='" + X + "' y='" + Y2 + "' style='font-size:16px;fill:" + cT + ";'>" + Mois[H00] + "</text>";
} else {
S += "<text x='" + X + "' y='" + Y2 + "' style='font-size:16px;fill:" + cT + ";'>" + H00 + "</text>";
}
H00 = (H00 - dTextTic + moduloText) % moduloText;
}
Y2 = Y0 - 3;
S += "<text x='980' y='" + Y2 + "' style='font-size:14px;fill:" + cT + ";'>" + label + "</text>";
// Graduations verticales gauche (Puissance Active)
for (var y = -10; y <= 10; y = y + dy) {
Y2 = Y0 - Yamp * y / 10;
if (Y2 <= 480) {
S += "<line x1='100' y1='" + Y2 + "' x2='1000' y2='" + Y2 + "' style='stroke:" + cT + ";stroke-width:1;stroke-dasharray:2 10;' />";
Y2 = Y2 + 7;
var T = cadrageMax * y / 10; T = T.toString();
var X = 90 - 9 * T.length;
S += "<text x='" + X + "' y='" + Y2 + "' style='font-size:16px;fill:" + cT + ";'>" + T + "</text>";
}
}
// Axe vertical pour PID (à côté de l'axe principal) seulement si pas même échelle
if (dI == 2 && Pva_valide && dispVA && !memeEchelle) {
S += "<line x1='150' y1='20' x2='150' y2='480' style='stroke:" + couleur2 + ";stroke-width:2;stroke-dasharray:5 5;' />";
// Graduations verticales pour le PID
for (var y = 0; y <= 10; y = y + dy) {
Y2 = Y0 - Yamp * y / 10;
if (Y2 <= 480) {
var T = echellePID * y / 10;
T = T.toString();
S += "<text x='155' y='" + (Y2 + 7) + "' style='font-size:14px;fill:" + couleur2 + ";'>" + T + "</text>";
}
}
}
// Courbe PID
if (dI == 2 && Pva_valide && dispVA) {
S += "<text x='450' y='40' style='font-size:18px;fill:" + couleur2 + ";'>" + titre2 + "</text>";
S += "<polyline points='";
var j = 0;
for (var i = 1; i < Tab.length; i = i + dI) {
var valeurPID = Tab[i];
if (!echelleAutoY2 && !memeEchelle) {
// Échelle fixe : limiter entre 0 et 100
valeurPID = Math.max(0, Math.min(100, valeurPID));
}
var Y = Y0 - Yamp * valeurPID / echellePID;
var X = 100 + dX * i;
S += X + "," + Y + " ";
TabY1[j] = parseFloat(Tab[i]);
j++;
}
S += "' style='fill:none;stroke:" + couleur2 + ";stroke-width:2' />";
}
// Courbe Puissance Active (avec échelle automatique)
S += "<text x='450' y='18' style='font-size:18px;fill:" + couleur1 + ";'>" + titre1 + "</text>";
S += "<polyline points='";
var j = 0;
for (var i = 0; i < Tab.length; i = i + dI) {
var Y = Y0 - Yamp * Tab[i] / cadrageMax;
var X = 100 + dX * i;
S += X + "," + Y + " ";
TabY0[j] = parseFloat(Tab[i]);
j++;
}
S += "' style='fill:none;stroke:" + couleur1 + ";stroke-width:2' />";
S += "</svg>";
GID(SVG).innerHTML = S;
TabVal["S_" + SVG] = [TabY0, TabY1];
TabCoul["S_" + SVG] = [couleur1, couleur2];
}
3) dans le fichier Server.ino remplacer la VA par pidout , pour simplifier les explication remplacer la void handleAjaxData() par celle ci-dessous
Code :
{ // Données page d'accueil
String DateLast = "Attente d'une mise à l'heure par internet";
if (Horloge == 1)
DateLast = "Attente d'une mise à l'heure par le Linky";
if (ModeReseau == 0 && WiFi.getMode() != WIFI_STA)
DateLast = "Sélectionnez un réseau <a href='/Wifi'>Wifi</a>";
if (Horloge > 1 && Horloge < 5)
DateLast = "Attente d'une mise à l'heure <a href='/Heure' >manuellement</a> ";
if (Horloge == 5)
DateLast = "Attente d'une mise à l'heure un ESP externe (maître)";
if (HeureValide)
{
DateLast = DATE;
}
int pva_temps_reel = pidout; // LBE
String S = LesTemperatures();
S = "Deb" + RS + DateLast + RS + Source_data + RS + LTARF + RS + STGEt + RS + S + RS + String(Pva_valide);
// LBE remplacée par la ligne suivante S += GS + String(PuissanceS_M) + RS + String(PuissanceI_M) + RS + String(PVAS_M) + RS + String(PVAI_M);
S += GS + String(PuissanceS_M) + RS + String(PuissanceI_M) + RS + String(pva_temps_reel) + RS + "0";
S += RS + String(EnergieJour_M_Soutiree) + RS + String(EnergieJour_M_Injectee) + RS + String(Energie_M_Soutiree) + RS + String(Energie_M_Injectee);
if (Source_data == "UxIx2" || ((Source_data == "ShellyEm" || Source_data == "ShellyPro") && EnphaseSerial.toInt() != 3))
{ // UxIx2 ou Shelly monophasé avec 2 sondes
S += GS + String(PuissanceS_T) + RS + String(PuissanceI_T) + RS + String(PVAS_T) + RS + String(PVAI_T);
S += RS + String(EnergieJour_T_Soutiree) + RS + String(EnergieJour_T_Injectee) + RS + String(Energie_T_Soutiree) + RS + String(Energie_T_Injectee);
}
S += GS + "Fin\r";
server.sendHeader("Connection", "close");
server.send(200, "text/html", S);
}4) retour sur dans le fichier Solar_Router_V16_09.ino pour préremplir les tableau avec pidout à la place de la VA
rechercher les lignes
if (tps - previousTimer2sMillis > 2000) {
unsigned long dt = tps - previousTimer2sMillis;
previousTimer2sMillis += 2000; //Pou caler exactement à 2s
tabPw_Maison_2s[IdxStock2s] = PuissanceS_M - PuissanceI_M;
tabPw_Triac_2s[IdxStock2s] = PuissanceS_T - PuissanceI_T;
et juste apres modifier la ligne
tabPva_Maison_2s[IdxStock2s] = PVAS_M - PVAI_M;
par
tabPva_Maison_2s[IdxStock2s] = pidout;//PVAS_M - PVAI_M; //LBE
5) et pour finir toujour dans le fichier Solar_Router_V16_09
chercher la ligne
Retard[i] = round(RetardF[i]); //Valeure entiere pour piloter le Triac et les relais
et juste apres rajouter
pidout=1*(100-Retard[i]);
Voilà ca fait pas mal de petits endroits à modifier mais qui ne sera pas un obstacle vule niveau de compétence croisé ici
Je vous proposerai prochainement de modifier l'algorithme PID d 'André pour le rendre beaucop plus réactif lors des changements brusques sans le passer en autooscillation. Le probleme du PID d'André est que pour anticiper il faudrait mettre de l action Dérivée, mais pour éviter les sauts brusques de la Dérivée provoqués par l’échantillonnage, André a été obligé de mettre un filtrage "fort" de cette action ce qui introduit un retard, et donc anticiper avec du retard ca fonctionne pas top.
Sans dérivée le régulateur ne bouge pas sa sortie tant que l erreur ne change pas de signe ( intégrale et proportionnel) ce qui conduit à de forts dépassements lors des changement de production ( Nuages) ou de consommation ( arrêt marche d'un gros consommateur) , je vous expliquerai le moment venu le principe du nouvel algorithme mis en place par la technique de l anti-windup avec back-calculation , qui permet de desaturer l intégrale avant le changement de signe de l erreur
