Mehrfache Hysterese programmieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

Hallo,
ich benötige mal Eure Hilfe, denn ich steht hier auf dem Schlauch....

Ich möchte 5 Schaltzustände erzeugen. Die Steuergröße ist eine sich verändernder Fußpunkt einer Heizkurve, die zur Verfügung steht.
(Der Fußpunkt wird aus dem Wärmebedarf basierend auf der Temp-Differenz zwischen Soll- und Ist-Temperatur ermittelt. Je höher der Wärmebedarf, also je größer der Unterschied zwischen Soll- und Ist-Temperatur, desto höher der Fußpunkt)

Die Schaltzustände:
- Tag_high
- Tag_med
- Tag_low
- Nacht
- Frostschutz

Ja nach Fußpunkt sollen die Schaltzustände aktiviert werden:
- Tag_high ein: Fußpunkt >= 24°C
- Tag_med ein: Fußpunkt >= 22°C
- Tag_low ein: Fußpunkt >= 20°C
- Nacht ein : Fußpunkt >= 17°C
- Frostschutz: Fußpunkt < 17°C

Damit im Grenzbereich zwischen 2 Temperaturschwellen die Schaltzustände nicht ständig wechseln, soll das "Abschalten" immer 1°C unterhalb des Einschaltschwelle erfolgen.
- Tag_high aus: Fußpunkt >= 23°C
- Tag_med ein: Fußpunkt >= 21°C
- Tag_low ein: Fußpunkt >= 19°C
- Nacht ein : Fußpunkt >= 16°C
- Frostschutz: Fußpunkt < 16°C

Über den Schaltzustand sollen den Pumpen, etc gesteuert werden.

Die Fragen an Euch ist, wie die Logik in Python aussehen kann / muss, damit die Schaltzustände "errechnet" werden.
Danke für die Hilfe.
Bolitho
User
Beiträge: 219
Registriert: Donnerstag 21. Juli 2011, 07:01
Wohnort: Stade / Hamburg
Kontaktdaten:

hm, vielleicht verstehe ich die Frage nicht. Aber die Logik steht doch schon da. Und das errechnen ist doch nur ein Abgleich des aktuellen Wertes mit dem aktuellen Schaltzustand (-1/+1) und dann dem Wechsel des Zustands. Das ganze in einer sich wiederholenden Schleife ggf. mit Wartezeit zwischen den einzelnen Abgleichen.

Ansätze:
- Variablen
- Vergleichsoperatoren
- Abfragen
- Endlosschleife

Poste hier doch gerne deinen Ansatz, gerne auch in Pseudocode. Dann können wir besser sehen, wo es noch fehlt und gezielter helfen.
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

So, ich habe mich mal versucht.
Das ganze ist eine Logik für smarthomeNG, ein smarthome Metagateway auf Python-Basis.

Code: Alles auswählen

#!/usr/bin/env python3
# heizung_vorlauf.py

# 4-Stufen-Steuerung des Vorlaufes in Abhängigkeit vom berechneten Fusspunkt
# Ausgelöst durch "Fusspunkt" und 'Außentemperatur'

#Deklaration lokaler Variablen
# at;        ! Aussentemperatur in °C
# fp;        ! berechneter max. Fusspunkt in °C
# tag_mittel ! Tagesdurchschittstemperatur

logger.info("Logik HeizungVorlauf ausglöst durch:" + trigger['by'] )

# Aussentemperaturen einlesen
at = sh.rpi1wire.temp_1()
tag_mittel = sh.rpi1wire.temp_1.tagesmittel_gleitend()

#logger.info("Außentemperatur: " + str(at))

#Systemvariable einlesen
if (at < 20.0) or (tag_mittel < 11.0):
    fp = sh.heizung.vorlauf.fusspunkt()
    logger.info("Fußpunkt gesetzt auf " + str(fp))
else:
    fp = 14.0
    
if not hasattr(logic, 'fp_alt'):
    logic.fp_alt = fp

richtung = round(fp - logic.fp_alt, 2) #richtung gibt an, ob der neue Fußpunkt höher ist als der alte; sinken die Wert oder steigen sie

logger.info("Richtung " + str(richtung))

#Konstanten (Schaltschwellen)
nachtaus = 15.0
tagminaus = 18.5
tagnormaus = 20.5
tagmaxaus = 22.5

nachtein = 16.5
tagminein = 19.5
tagnormein = 21.5
tagmaxein = 23.5

#Stufen
if (richtung >= 0) and (fp >= tagmaxein) or ((richtung < 0) and (fp > tagmaxaus)): 
    zustand = 'tag_max'
elif ((richtung >= 0) and (fp >= tagnormein)) or ((richtung < 0) and (fp > tagnormaus)):
    zustand = 'tag_med'
elif (richtung >= 0) and (fp >= tagminein) or ((richtung < 0) and (fp > tagminaus)):
    zustand = 'tag_min'    
elif (richtung >= 0) and (fp >= nachtein) or ((richtung < 0) and (fp > nachtaus)):
    zustand = 'nacht'
else:
    zustand = 'frost'
 
logger.info("Zustand gesetzt auf " + str(zustand))

#Aktionen
if zustand == 'tag_max':
    sh.heizung.vorlauf.nacht(0)
    sh.heizung.vorlauf.tag_min(0)
    sh.heizung.vorlauf.tag_med(0)
    sh.heizung.vorlauf.tag_max(1)
    logger.info("Tag_Max aktiviert")
elif zustand == 'tag_med':
    sh.heizung.vorlauf.nacht(0)
    sh.heizung.vorlauf.tag_min(0)
    sh.heizung.vorlauf.tag_med(1)
    sh.heizung.vorlauf.tag_max(0)
    logger.info("Tag_Med aktiviert")
elif zustand == 'tag_min':
    sh.heizung.vorlauf.nacht(0)
    sh.heizung.vorlauf.tag_min(1)
    sh.heizung.vorlauf.tag_med(0)
    sh.heizung.vorlauf.tag_max(0)
    logger.info("Tag_Min aktiviert")
elif zustand == 'nacht':
    sh.heizung.vorlauf.nacht(1)
    sh.heizung.vorlauf.tag_min(0)
    sh.heizung.vorlauf.tag_med(0)
    sh.heizung.vorlauf.tag_max(0)
    logger.info("Nacht aktiviert")
elif zustand == 'frost':
    sh.heizung.vorlauf.nacht(0)
    sh.heizung.vorlauf.tag_min(0)
    sh.heizung.vorlauf.tag_med(0)
    sh.heizung.vorlauf.tag_max(0)
    logger.info("Frostschutz aktiviert")
else:
    none

logic.fp_alt = fp
logger.info("Fußpunkt_alt gesetzt auf " + str(logic.fp_alt))
Geht das eleganter?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@sisamiwe: Skripte, in denen etliche Variablen aus dem Nichts kommen, sind mir suspect. Wenn Du Deine Variablen sinnvoll benennen würdest, müßtest Du nicht in einem Kommentar erklären, was ›at‹ oder ›fp‹ bedeuten.
Bei den if-Bedingungen sind etliche Klammern zu viel und ein paar zuwenig.
Wenn man die ganzen Konstanten in eine passende Struktur packt, dann ist die if-Kaskade ein Zweizeiler.
Statt den else-Block eines if-Switches, der nie Auftreten sollte mit einem NameError abbrechen zu lassen, sollte das ein AssertionError sein: »assert False, "sollte nicht auftreten"«.
Alternativ kann man das auch vereinfachen zu:

Code: Alles auswählen

sh.heizung.vorlauf.nacht(zustand == 'nacht')
sh.heizung.vorlauf.tag_min(zustand == 'tag_min')
sh.heizung.vorlauf.tag_med(zustand == 'tag_med')
sh.heizung.vorlauf.tag_max(zustand == 'tag_max')
logger.info("%s aktiviert", zustand)
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

@Sirius3

Erstmal danke für Deine Rückmeldung.
Ich habe die Logik überarbeitet und die Variablen besser benannt.
Der letzte Teil der Logik ist das Setzen des Schaltaktors, also die Zuschaltung der Widerstande 1 - 4 (habe ich auch besser benannt) in Abhängigkeit vom Zustand aus er Logik.

Du sagt, dass man die if Abfrage in einem 2-Zeiler ausdrücken könnte? Wie könnte das aussehen?

Code: Alles auswählen

#!/usr/bin/env python3
# heizung_vorlauf.py

# 4-Stufen-Steuerung des Vorlaufes in Abhängigkeit vom berechneten Fusspunkt
# Ausgelöst durch "Fusspunkt" und Außentemperatur

logger.info("Logik HeizungVorlauf ausglöst durch:" + trigger['by'] )

# Aussentemperaturen einlesen
aussentemp_aktuell = sh.rpi1wire.temp_1()
aussentep_tagesmittel = sh.rpi1wire.temp_1.tagesmittel_gleitend()
fusspunkt = sh.heizung.vorlauf.fusspunkt()


# Prüfung ob Heizung aufgrund Aussentemperatur gebraucht wird
if (aussentemp_aktuell < 20.0) or (aussentep_tagesmittel < 11.0):
    heizung = true

# Prüfung, ob persistente Variablen vorhanden, wenn nicht setzen dieser 
if not hasattr(logic, 'fusspunkt_alt'):
    logic.fusspunkt_alt = fusspunkt

# Prüfung, ob neuer Fußpunkt größer oder kleiner ist, um die Verlaufsrichtung des Fußpunkts festzustellen
richtung = round(fusspunkt - logic.fusspunkt_alt, 1)

logger.info("Richtung " + str(richtung))

#Konstanten (Schaltschwellen)
nachtaus = 15.0
tagminaus = 18.5
tagnormaus = 20.5
tagmaxaus = 22.5

nachtein = 16.5
tagminein = 19.5
tagnormein = 21.5
tagmaxein = 23.5

#Stufen
if (heizung == 'true') and ((richtung >= 0) and (fusspunkt >= tagmaxein)) or ((richtung < 0) and (fusspunkt > tagmaxaus)): 
    zustand = 'tag_max'
elif (heizung == 'true') and ((richtung >= 0) and (fusspunkt >= tagnormein)) or ((richtung < 0) and (fusspunkt > tagnormaus)):
    zustand = 'tag_med'
elif (heizung == 'true') and ((richtung >= 0) and (fusspunkt >= tagminein)) or ((richtung < 0) and (fusspunkt > tagminaus)):
    zustand = 'tag_min'    
elif (heizung == 'true') and ((richtung >= 0) and (fusspunkt >= nachtein)) or ((richtung < 0) and (fusspunkt > nachtaus)):
    zustand = 'nacht'
else:
    zustand = 'frost'
 
logger.info("Zustand gesetzt auf " + str(zustand))

#Aktionen
if zustand == 'tag_max':
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    logger.info("Tag_Max aktiviert")
elif zustand == 'tag_med':
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(0)
    logger.info("Tag_Med aktiviert")
elif zustand == 'tag_min':
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    logger.info("Tag_Min aktiviert")
elif zustand == 'nacht':
    sh.heizung.vorlauf.widerstand1(1)
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    logger.info("Nacht aktiviert")
elif zustand == 'frost':
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    sh.heizung.vorlauf.widerstand1(0)
    logger.info("Frostschutz aktiviert")

logic.fp_alt = fp
logger.info("Fußpunkt_alt gesetzt auf " + str(logic.fp_alt))
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@sisamiwe: Programmieren ist nicht raten. Warum wird` heizung` einmal der Wert der nicht definierten Variable `true` zugewiesen und dann mit dem String 'true' verglichen? Ersteres sollte der boolsche Wert True sein, bei zweiterem braucht man gar keinen Vergleich, weil heizung ja schon ein Wahrheitswert ist. fusspunkt wurde nicht überall verbessert.

Was ist eigentlich `sh` für ein Objekt? Warum setzt Du jetzt `sh.heizung.vorlauf.widerstand1` vier mal hintereinander?
Antworten