PV Wechselrichter Drosselung wenn Akku voll

Für welche Projekte verwendet Ihr OpenHAB? Was habt Ihr automatisiert? Stellt eure Projekte hier vor.

Moderatoren: Cyrelian, seppy

Antworten
Benutzeravatar
PeterA
Beiträge: 1110
Registriert: 8. Feb 2019 12:12
Answers: 13

PV Wechselrichter Drosselung wenn Akku voll

Beitrag von PeterA »

Hallo zusammen,

Ich habe hier schon einige Zeit nichts mehr beigetragen.
Hier möchte ich nun mein aktuelles Projekt vorstellen:
"PV-Wechselrichter drosseln", wenn der Akku voll ist."
Ja, da haben auch "Klaus" & "Ingo" etwas mitgeholfen :-)
Die Rule läuft hier seit einiger Zeit im Produktivsystem und tut, was sie soll.

Beste Grüße Peter.

Code: Alles auswählen

// =====================================================
// PV-Regelung Version 3.6
//
// Funktionen:
// - SOC Hysterese
// - gleitender Mittelwert Netzleistung
// - Lastspitzenfilter
// - variable Schrittweite
// - variable Totzone
// - Dämpfung
// - Zeitsperre
// - WR Online-Erkennung
// - Schutz gegen doppelte WR-Befehle
// - Manueller Freigabe-Schalter (PV_Regelung_Freigabe)
// - Freigabe erst wenn die Verschattung erstmalig gelaufen ist.
//
// Aktivierung:
//   Akku >= 97 %
//
// Deaktivierung:
//   Akku <= 90 %
//
// Netzleistung:
//   > 0 W = Netzbezug
//   < 0 W = Einspeisung
// -----------------------------------------------------
// Globale Variablen
// -----------------------------------------------------

var Number gLastSentLimit = null
var DateTime gLastCommandTime = null

var boolean gPvControlActive = false

// merkt sich, ob der WR zuletzt offline war,
// um beim Wiederkommen die Historie zu leeren
var boolean gInverterWasOffline = false

var java.util.List<Integer> gGridPowerHistory =
    newArrayList()


rule "PV Begrenzung bei vollem Akku"
when
    Item us3000c_soc changed or
    Item em0_total_act_power changed or
    Item OpenDTU1_inv1_reachable changed or
    Item PV_Regelung_Freigabe changed or
    Item Rolloautomatik_start_last changed
then

    // -------------------------------------------------
    // Prüfung auf gültige Werte
    // -------------------------------------------------

    if(
        us3000c_soc.state == NULL ||
        us3000c_soc.state == UNDEF ||
        em0_total_act_power.state == NULL ||
        em0_total_act_power.state == UNDEF ||
        OpenDTU1_inv1_limit_absolute.state == NULL ||
        OpenDTU1_inv1_limit_absolute.state == UNDEF ||
        OpenDTU1_inv1_reachable.state == NULL ||
        OpenDTU1_inv1_reachable.state == UNDEF ||
        PV_Regelung_Freigabe.state == NULL ||
        PV_Regelung_Freigabe.state == UNDEF ||
        Rolloautomatik_start_last.state == NULL ||
        Rolloautomatik_start_last.state == UNDEF
    ){
        return;
    }

    val soc =
        (us3000c_soc.state as Number).intValue

    val rawGridPower =
        (em0_total_act_power.state as Number).intValue

    val itemLimit =
        (OpenDTU1_inv1_limit_absolute.state as Number).intValue

    val inverterReachable =
        (OpenDTU1_inv1_reachable.state as Number).intValue

    val timestamp =
        now.toString("HH:mm")

    // -------------------------------------------------
    // Verschattung heute bereits aktiv gewesen?
    // -------------------------------------------------

    val verschattungHeute =
        Rolloautomatik_start_last.state // Verschattung West 
            .toString()
            .substring(0,10) ==
        now.toString().substring(0,10)

    // -------------------------------------------------
    // WR offline
    // -------------------------------------------------

    if(inverterReachable == 0){

        gInverterWasOffline = true

        if(gPvControlActive){

            gPvControlActive = false

            logInfo(
                "PV",
                "Wechselrichter offline - PV-Regelung pausiert"
            )

            sendBroadcastNotification(
                timestamp +
                " | WR offline | PV-Regelung pausiert"
            )
        }

        return;
    }

    // -------------------------------------------------
    // WR war offline und ist jetzt wieder da
    // -> Historie zurücksetzen, damit keine veralteten
    //    Messwerte den gleitenden Mittelwert verfälschen
    // -------------------------------------------------

    if(gInverterWasOffline){

        gInverterWasOffline = false

        gGridPowerHistory.clear()

        logInfo(
            "PV",
            "Wechselrichter wieder erreichbar - Messwertverlauf zurückgesetzt"
        )

        sendBroadcastNotification(
            timestamp +
            " | WR wieder erreichbar"
        )
    }

    // -------------------------------------------------
    // Manuelle Deaktivierung
    //
    // Ab hier ist inverterReachable garantiert == 1,
    // sendCommand(1500) erreicht also einen echten WR
    // -------------------------------------------------

    if(PV_Regelung_Freigabe.state != ON){

        if(gPvControlActive){

            gPvControlActive = false

            if(itemLimit != 1500){

                OpenDTU1_inv1_limit_absolute.sendCommand(1500)

                gLastSentLimit = 1500
                gLastCommandTime = now
            }

            gGridPowerHistory.clear()

            logInfo(
                "PV",
                "PV-Regelung manuell deaktiviert"
            )

            sendBroadcastNotification(
                timestamp +
                " | PV-Regelung manuell deaktiviert"
            )
        }

        return;
    }

    // -------------------------------------------------
    // Messwert in Filter übernehmen
    // -------------------------------------------------

    gGridPowerHistory.add(rawGridPower)

    if(gGridPowerHistory.size > 6){
        gGridPowerHistory.remove(0)
    }

    // -------------------------------------------------
    // Mittelwert berechnen
    // -------------------------------------------------

    var gridPowerSum = 0

    gGridPowerHistory.forEach[value |
        gridPowerSum = gridPowerSum + value
    ]

    val gridPower =
        (gridPowerSum / gGridPowerHistory.size)

    // -------------------------------------------------
    // Aktuelles WR Limit bestimmen (geglättet, für die
    // eigentliche Regelung)
    // -------------------------------------------------

    var currentLimit = itemLimit

    if(
        gLastSentLimit !== null &&
        Math::abs(
            itemLimit -
            gLastSentLimit.intValue
        ) < 30
    ){
        currentLimit =
            gLastSentLimit.intValue
    }


    val minPower = 200
    val maxPower = 1500

    // -------------------------------------------------
    // PV-Regelung erst zulassen wenn die
    // Verschattung heute bereits gelaufen ist
    // -------------------------------------------------

    if(!verschattungHeute){

        if(gPvControlActive){

            gPvControlActive = false

            gGridPowerHistory.clear()

            if(itemLimit != maxPower){

                OpenDTU1_inv1_limit_absolute.sendCommand(maxPower)

                gLastSentLimit = maxPower
                gLastCommandTime = now
            }

            logInfo(
                "PV",
                "PV-Regelung gesperrt - Verschattung heute noch nicht aktiv"
            )
        }

        return;
    }

    // -------------------------------------------------
    // Akku <= 90 %
    // Regelung deaktivieren
    // -------------------------------------------------

    if(soc <= 90){

        if(gPvControlActive){

            gPvControlActive = false

            if(itemLimit != maxPower){

                OpenDTU1_inv1_limit_absolute.sendCommand(maxPower)

                gLastSentLimit = maxPower
                gLastCommandTime = now
            }

            logInfo(
                "PV",
                "Akku {}% -> PV-Regelung AUS",
                soc
            )

            sendBroadcastNotification(
                timestamp +
                " | Akku " +
                soc +
                "% | PV-Regelung deaktiviert | WR " +
                maxPower +
                " W"
            )
        }

        return;
    }

    // -------------------------------------------------
    // Akku >= 97 %
    // Regelung aktivieren
    //
    // Historie wird bei jeder Neu-Aktivierung geleert,
    // damit alte Werte aus der Pause nicht in den ersten
    // Mittelwert einfließen
    // -------------------------------------------------

    if(soc >= 97 && !gPvControlActive){

        gPvControlActive = true

        gGridPowerHistory.clear()
        gGridPowerHistory.add(rawGridPower)

        logInfo(
            "PV",
            "Akku {}% -> PV-Regelung EIN",
            soc
        )

        sendBroadcastNotification(
            timestamp +
            " | Akku " +
            soc +
            "% | PV-Regelung aktiviert"
        )
    }

    // -------------------------------------------------
    // Nur weiter wenn PV-Regelung aktiv
    // -------------------------------------------------

    if(!gPvControlActive){
        return;
    }

    // -------------------------------------------------
    // Zeitsperre
    // -------------------------------------------------

    if(
        gLastCommandTime !== null &&
        now.isBefore(
            gLastCommandTime.plusMillis(10000)
        )
    ){
        return;
    }

    // -------------------------------------------------
    // Variable Totzone
    // -------------------------------------------------

    var deadBand = 50

    if(currentLimit < 500){

        deadBand = 20

    } else if(currentLimit < 1000){

        deadBand = 35
    }

    if(
        gridPower >= -deadBand &&
        gridPower <= deadBand
    ){
        return;
    }

    // -------------------------------------------------
    // Variable Schrittweite
    // -------------------------------------------------

    var maxStep = 150

    if(currentLimit < 500){

        maxStep = 50

    } else if(currentLimit < 1000){

        maxStep = 100
    }

    // -------------------------------------------------
    // Dämpfung
    // -------------------------------------------------

    var delta = 0

    if(Math::abs(gridPower) < 150){

        delta = gridPower

    } else {

        delta =
            (gridPower * 0.6).intValue
    }

    // -------------------------------------------------
    // Schrittweite begrenzen
    // -------------------------------------------------

    if(delta > maxStep){
        delta = maxStep
    }

    if(delta < -maxStep){
        delta = -maxStep
    }


    var newLimit =
        currentLimit + delta

    // -------------------------------------------------
    // Begrenzung
    // -------------------------------------------------

    if(newLimit < minPower){
        newLimit = minPower
    }

    if(newLimit > maxPower){
        newLimit = maxPower
    }

    // -------------------------------------------------
    // Auf 10 W runden (kaufmännisch)
    // -------------------------------------------------

    newLimit =
        (((newLimit + 5) / 10) * 10)

    // -------------------------------------------------
    // Kleine Änderungen ignorieren
    // -------------------------------------------------

    if(
        Math::abs(
            newLimit - currentLimit
        ) < 20
    ){
        return;
    }

    // -------------------------------------------------
    // Doppeltes Senden verhindern
    // -------------------------------------------------

    if(
        gLastSentLimit !== null &&
        Math::abs(
            newLimit -
            gLastSentLimit.intValue
        ) < 20
    ){
        return;
    }

    // -------------------------------------------------
    // Neues Limit senden
    // -------------------------------------------------

    OpenDTU1_inv1_limit_absolute.sendCommand(newLimit)

    gLastSentLimit = newLimit
    gLastCommandTime = now

    // -------------------------------------------------
    // Laufende Regelschritte werden nur geloggt, nicht
    // per Push gemeldet, um Notification-Spam bei
    // häufigen Anpassungen zu vermeiden. Status-Wechsel
    // (EIN/AUS/Offline/Online/manuell) werden weiterhin
    // per Push gemeldet.
    // -------------------------------------------------

    if(gridPower < 0){

        logInfo(
            "PV",
            "Einspeisung {} W (gefiltert) | WR {} -> {} W",
            gridPower,
            currentLimit,
            newLimit
        )

    } else {

        logInfo(
            "PV",
            "Netzbezug {} W (gefiltert) | WR {} -> {} W",
            gridPower,
            currentLimit,
            newLimit
        )
    }

end
- OpenHab
#PWRUP

Antworten