Tendenzanzeige zur Abschalterkennung

Einlesen von 3- 5 Leistungswerten mit anschließender Tendenzerkennung

Hallo Forum,

ich habe das Problem, dass meine PV-Anlage beim abendlichen Abschalten oft ein bisschen „Schluckauf“ hat, d.H. dass die Anlage nicht die Leistung linear reduziert, sondern das die Werte zum Schluß stark schwanken können. Das gleiche ist wenn eine Wolke über die Anlage zieht.
Anhand der Leistung erkenne ich den Start und Ende des Wechselrichters führe einen Übertrag der Tagesleistung aus. Um einen sicheres Ende erkennen zu können habe ich mir überlegt eine Tendenzauswertung über mehrere Minuten einzufügen.
Als Lösung habe ich mir überlegt, die Leistung 3-5 mal in einem Abstand von 5 Minuten einzulesen, jeweils in eine Variable abzulegen und anschließend die Tendenz auszuwerten. Also wie ein Array mit einem Pointer.

Habt ihr eine andere Idee für dies Problemlösung?

Grüße
andrge

Hallo andrge,

ich habe vor einiger Zeit mal ein ähnliches Problem mit der Außentemp gehabt.
Dort habe ich dann aber für einen vernünftigen Trend mehrere Temp-Werte gemittelt und daraus über zwei Mittelwerte einen Trend gebildet.

Hier mal ein Teil des Scripts:

$temp = GetValueFloat("vKS300_Temperatur"); // aktuelle Außentemperatur
// #### Temperaturen in Array speichern ####
$string_array = GetValueString("vKS300_array_recent");
$temp_count = 12; //Anzahl zu speichernder Temps

//Array auf vorhandensein prüfen / initialisieren
if ($string_array == "")
{
   $temp_array = array_fill(0, $temp_count, $temp);
}
else
{
   $temp_array = wddx_deserialize($string_array);
}

array_unshift($temp_array, $temp);
if (count($temp_array) > $temp_count)
   unset($temp_array[$temp_count]);
//print_r($temp_array); //nur debug-Ausgabe

// Mittelwert
$mw = array_sum($temp_array) / count($temp_array);

// recent Temperatur mitteln um Kurve zu glätten
$tr = GetValueFloat("vKS300_Temperatur_recent"); // letzter gemittelter Wert

// SetValueFloat("vKS300_Temperatur_tr2",$tr1);


// Trend ermitteln
$tt = $mw-$tr;

// Zeit seit dem letzten Update ermitteln, in Minuten
$vUpdateTime = (date("U")-IPS_GetUpdateTime("vKS300_Temperatur_recent"))/3600;
/*   Debug Ausgaben
echo ("Zeit des Updates: ".IPS_GetUpdateTime($DevName."_recent")."
");
echo date("U")."
";
echo ("UpdateTime:". $vUpdateTime."
");
echo ("Temp Trend: ".$tt. "
");
*/

// $ta auf $tr schreiben
if ($vUpdateTime)
SetValueFloat("vKS300_Temperatur_recent", $mw);

$tc = $tt/$vUpdateTime;
SetValueFloat("vKS300_Temperatur_trend", $tc);

// Temperatur-Array in String zurückschreiben
SetValueString("vKS300_array_recent", wddx_serialize_value($temp_array) );


Die Anzahl der Temps ist frei wählbar ($temp_count).

Ev. ist ja eine Anregung dabei…
Wenn was unklar ist, einfach fragen.

Gruß
Fabian

Ich liebe kleine Mathematische Probleme :slight_smile:

Das Script von Prof sind prinzipiell schon mal nicht schlecht aus, hat aber den nachteil das der Trend auch „flattert“, weil wenn der letzte Temp wert niedriger ist als der Schnitt zeigt der Trend nach unten (Wolke) und wenn der letzte Höher ist nach oben. Hängt also nur vom letzen ab, was dir ja imho nicht nützen würde.

Ich würde evtl in folgende Richtung gehen:

Durchschnittwert nehmen okay, dann vergleichen ob der Durchschnitt des ersten drittel der Werte drüber und der Durchschnitt des letzen drittels der werte drunter liegt. Wenn ja sinkende Tendenz erkannt.

Oder Durchschnitt der ersten Hälfte subtrahieren vom Durchschnitt der 2ten Hälfte.

Muss man mal bischen probieren was das Szenario am besten erfasst.

Anti

Hallo Fabian und Anti,

danke für eure Vorschläge. Ich denke wir sprechen alle vom gleichen. Jetzt geht es nur darum dies praktisch umzusetzen. Der Scriptteil mit dem Array gefällt mir schon gut. Schluendlich geht es jetzt darum die entsprechende Auswertung aus dem Array zu programmieren. Bin beim Programmieren nicht so Fit, aber werde mal was versuchen. Wird aber nächste Woche werden.

Ciao
Andrge

Den Array Aufbau kannst ja von Prof übernehmen, ungetestet der etwas aussagekräftigere trend z.b. so:



$half = floor($temp_count / 2);

$trend = (array_sum(array_slice ($temp_array,-$half))-array_sum(array_slice ($temp_array,0,$half)))/$half;

Hallo anti,

interessante Bewertung des Trends! Gefällt mir. An eine Aufteilung hatte ich noch nicht gedacht.

Kannst Du mal erklären, warum der Trend bei meinem Vorschlag nur vom letzten Wert abhängt?
Ich lerne jeden Tag dazu, dank dieses Forums… :slight_smile:

Gruß an alle
Fabian

Hi prof,

na du vergleichst ja das letze arithmetische Mittel mit dem neuen, korrekt?

also sagen wir du speicherst 4 Werte dann

(a1+(a2+a3+a4)) / 4 (letzer Mittelwert) vergleich mit ((a2+a3+a4)+a5) /4 (aktueller Mittelwert)

naja okay es hängt also von a1 und von a5 ab. a2,a3,a4 sind hingegen egal. Könnetst also auch direkt a1 und a5 vergleichen.

Gruß,
Anti

Besten Dank für die Erklärung!

Nachdem ich gesehen habe, wie Du es aufschreibst, fielen mir die Tomaten von den Augen… :eek:

Grüße
Fabian

Hallo Anti,
hallo Prof,

klar, hab ich verstanden. Mit dem Script hab ich noch a bisserl Probleme. Könnte mir einer von euch beiden das Script ein wenig auskommentieren?
Meine Vorstellung vom Script war einfacher, aber mit einem richtigen Arry gefällt mir das gleich besser, nur hab ich´s noch nicht ganz verstanden. :confused:

Danke schon mal…

Ciao
Andrge

$temp = GetValueFloat("vKS300_Temperatur"); // aktuelle Außentemperatur 
// #### Temperaturen in Array speichern #### 
$string_array = GetValueString("vKS300_array_recent"); 
$temp_count = 12; //Anzahl zu speichernder Temps 

//Array auf vorhandensein prüfen / initialisieren 
if ($string_array == "") 
{ 
   $temp_array = array_fill(0, $temp_count, $temp);  // Wenn nicht vorhanden mit 0 Initialisieren
} 
else 
{ 
   $temp_array = wddx_deserialize($string_array);  // Array aus String gewinnen
} 

array_unshift($temp_array, $temp); // Vorn neuen Wert anfügen
if (count($temp_array) > $temp_count)  // Wenn länger als soll
   unset($temp_array[$temp_count]);  // Letzen Wert entfernen 
//print_r($temp_array); //nur debug-Ausgabe 

// Mittelwert 
$mw = array_sum($temp_array) / count($temp_array); 

$half = floor($temp_count / 2); 

// Array_sum addiert alle Werte des übergebenen Arrays
// array_slice splittet ein teilarray ab
// Hier wird Mittelwert zweite Hälfte des Array - Mittelwert erste Hälfte errechnet
$trend = (array_sum(array_slice ($temp_array,-$half))-array_sum(array_slice ($temp_array,0,$half)))/$half; 


// Temperatur-Array in String zurückschreiben (Aus array wiueder String machen und in IPS speichern)
SetValueString("vKS300_array_recent", wddx_serialize_value($temp_array) ); 

Ein paar Sachen könnte man da noch optimieren, z.b. ist das array immer zu lang, so das die Abfrage an sich überflüssig wäre. Naja und die Initialisierung führt dazu, dass die ersten $temp_count Werte Müll ausspucken, könnte man z.B. mit der gerade gemessenen Temperatur, statt 0 füllen. Aber … hey lassen wir die Kirche mal im dorf, das sollte schon funktionieren so :wink:

Gruß,
Anti

Hi Anti,

Ein paar Sachen könnte man da noch optimieren,

du hast schon recht, das war auch nur so ein „Schnellschuss“. :rolleyes:
Aber:

könnte man z.B. mit der gerade gemessenen Temperatur, statt 0 füllen
$temp_array = array_fill(0, $temp_count, $temp);

Diese Zeile macht genau das
Sie füllt das Array von Position <0> für die Anzahl <$temp_count> Elemete mit dem eben ausgelesenen Wert <$temp>.

Das mit der Länge ist mehr „der Ordnung halber“ :rolleyes:
Wie gesagt: manchmal ist es schneller in Code gefasst, als man darüber nachdenkt… :smiley:

Grüße
Fabian

Hallo Fabian,
Hallo Anti,

ich bin begeistert! Ich werde das Script mal testen. Mit der Kommentierung komme ich nun besser klar.
Habt ihr noch weitere Verbesserungen auf Lager? :slight_smile:

Ciao

Andrge

Hallo,

wenn ich das Skript richtig interpretiere, dann soll aus einer Serie von Messwerten, aus den zuletzt eingetroffenen ein Mittelwert gebildet werden.
Es handelt sich dabei um einen sogenannten „Gleitenden Mittelwert“. Er wird mit jedem neu eintreffenden Wert aktualisiert und berücksichtigt dabei immer z.B. die letzten 10 Messungen.

Ich mache von dieser Art der Mittelwertbildung häufig Gebrauch und verwende dabei folgendes Prinzip:

  • die letzten Messwerte werden in einem Array gespeichert
  • das Array ist als „Ringpuffer“ organisiert
  • zur Realisierung der Ringstruktur wird ein Ringzeiger (Index) verwendet
  • der Ringzeiger lässt die Schreib-/Leseposition im Array rotieren
  • statt die Summe jedesmal neu zu berechnen, wird immer nur der älteste Wert subtrahiert und dann der neue Wert addiert
  • je größer das Array ist, um so träger reagiert der Mittelwert

Folgendes Skriptfragment soll das Prinzip verdeutlichen:

// hier Array ($buffer) aus einer Variablen einlesen
$index = GetValueInteger("Buffer_Index");    // alten Zeiger einlesen
$sum = GetValueInteger("Buffer_Sum");        // alte Summe einlesen

$index = $index++ % count($buffer);          // Zeiger inkrementieren und Überlauf abfangen (Modulo-Funktion)
$sum -= $buffer[$index];                     // zuerst alten Messwert subtrahieren
$buffer[$index] = $IPS_VALUE;                // neuen Messwert speichern
$sum += $IPS_VALUE;                          // jetzt neuen Messwert aufaddieren
$average = $sum/count($buffer);              // neuen Mittelwert berechnen

SetValueInteger("Buffer_Index", $index);     // aktuellen Zeiger abspeichern
SetValueInteger("Buffer_Sum", $sum);         // aktuelle Summe abspeichern
SetValueFloat("Buffer_Average", $average);   // Mittelwert abspeichern
// hier Array ($buffer) wieder abspeichern

Gruß
HJH

Hallo HJH,

genauso mache ich z.B. 38 Tage Werte für meine Börsenkurse. Die lege ich dann in einer db ab und kann sie grafisch anzeigen.

Ich weiß, geht auch alles übers Internet…
Aber man experimentiert eben mit verschiedenen Techniken und sei es nur, um sie später einmal einsetzen zu können. :slight_smile:

Gruß nach München
Fabian

Selbstverständlich führen viele Wege nach Rom :slight_smile:

Auch ein nettes Konzept. Nur mit dem gleitenden Mittelwert wird es allerdings, wie ich schon vorher meinte, schwierig eine vernünftige Tendenzaussage ermitteln können. Um eben jene geht es ihm ja letzlich.

Im direkten Vergleich würde ich sagen deine Methode benötigt einen Hauch weniger Rechenoperationen dafür musste etwas mehr Werte speichern. Dürfte in unserem Fall aber beides ziemlich egal sein, geht ja nicht um highperformance Anwendungen :slight_smile:

Anti

Hallo Anti,
hallo HJH
hallo Fabian,

wie bereits gesagt, es geht mir darum erkennen zu können wann wirklich meine PV-Anlage abgeschaltet hat und nicht nur einen „sonnenscheinbedingten Schluckauf“ hatte, d.H. dass die Tendenzauswertung eigentlich mit den ersten Wert unterhalb meiner Schaltschwelle gestartet werden sollte. So ahb ich mir das wenigstens gedacht.
Ich werd die Scripte mal ausprobieren (mal schauen ob ich diese ohne Probleme zum laufen bekomme :wink: und werd dann Feedback geben.

Danke schon mal an alle Beteiligten, saubre Arbeit!!!

Ciao
Andrge

… und wenn man daraus ein ein Bild erzeugt, lässt es ich prima im Designer verwenden …
IPS-Brick folgt:


.
.
.
$im=imagecreate($breite,$hoehe);
$white=imagecolorallocate($im,255,255,255);
imagecolortransparent($im,$white); // Making Image Transparent
$black=imagecolorallocate($im,0,0,0);
$white=imagecolorallocate($im,255,255,255);
$yellow=imagecolorallocate($im,255,255,0);
for($x=0; $x< $breite; $x = $x + 1) {   // 0 bis 47
    $y = (($temp_array[$x])* $verstaerkung) - $mittelachse; /
     imageline  ($im, $x, $hoehe-1, $x, ($hoehe -1 - $y), $yellow  );
}
imagepng($im, "trend_1.png");
IMGP_SendEvent("trend_1.png");
.
.
.

trend_gr_demo.png

Hallo,

ich muss aus aktuellem Anlass mal diesen alten Beitrag hochholen. Ihr hatte ja hier zwei Wege zur Trendberechnung dargelegt. Ich habe prof seinen Weg mal umgesetzt und bin aber nun damit gar nicht so gluecklich, weil ich nicht weiss, wie ich das fuer mich verwenden kann.

Folgendes will ich erreichen:

Ein Objekt wird unter bestimmten Bedingungen eingeschaltet, z.B. anhand der Aussentemperatur. Nun moechte ich aber, dass es ausgeschaltet wird, wenn der Trend ueber eine laengere Zeit erkennen laesst, dass die Temperatur sinkt. Ich habe mal die Werte als Kurve mitgeschrieben, aber da kann ich nicht wirklich daraus einen Schaltzeitpunkt ableiten.

Vielleicht habt Ihr noch eine Idee, wie man das verwirklichen koennte.
Achso, die Messwerte fuer den Trend werden minuetlich erfasst und beinhalten 30 Messwerte.

trend.png

Hallo Torro,

hast Du die Änderung von Anti aus Post Nr. 5 eingefügt? Damit steigt die Aussagekraft erheblich.

Über welchen Zeitraum möchtest Du eine Betrachtung? Innerhalb welcher Zeit soll eine Aussage getroffen werden?

Tatsächlich sieht die Anzeig bei mir ähnlich aus. Wenn Du klarere Werte haben möchtest, muss man die Anzahl der Werte erhöhen.

Man könnte auch die Dauer zum letzten Minimum/Maximum ermitteln und dann bei dauerhafter Überschreitung eines bestimmten Delta über einen gewissen zeitlichen Abstand zum letzten Min/Max eine Entscheidung treffen.

Ich steuere z. B mit der Ermittlung der Tagesmitteltemp über mind. vier Tage meine Heizung automatisch in den „Sommer“-Modus.

Gruß Fabian

Hallo Fabian,

ja, die habe ich drin.

Über welchen Zeitraum möchtest Du eine Betrachtung? Innerhalb welcher Zeit soll eine Aussage getroffen werden?

also im Moment nutze ich 30 Messwerte und damit einen zeitraum von 30 Minuten. Das sollte eigentlich ausreichen.

Tatsächlich sieht die Anzeig bei mir ähnlich aus. Wenn Du klarere Werte haben möchtest, muss man die Anzahl der Werte erhöhen.

hhm. Im Moment kann ich so damit nix anfangen. Ich will beispielsweise den Solarregler fuer den Pool abschalten, wenn von dort nicht mehr genuegend Waerme abgegeben werden kann. Auf der anderen Seite will ich aber auch keine staendigen Umschaltungen haben. Das gleiche gilt fuer meine Wintergarten Markise, da will ich das einfahren damit steuern.

Man könnte auch die Dauer zum letzten Minimum/Maximum ermitteln und dann bei dauerhafter Überschreitung eines bestimmten Delta über einen gewissen zeitlichen Abstand zum letzten Min/Max eine Entscheidung treffen.

das waere eine Moeglichkeit - mal schauen, ob HJH noch eine gute Idee hat.