FHEM: Heizung mit pid-Regler steuern

Von | 3. November 2019

Die Ausgangslage

Wir haben an unserer Heizung für einen Gebäudeteil einen 3-Wege-Mischer und noch schöne alte Gussheizkörper. Diese könnte man zwar auch mit Homatic-Thermostate ausstatten, aber ich wollte hier einen preiswerteren Weg gehen.

Die Idee:
Ich nehme ein Referenzraum (das Wohnzimmer) und messe dort die Temperatur und steuere darüber die Vorlauftemperatur. Dafür muss ich nur den Mischer entsprechend auf oder zu fahren. Die originalen Thermostat-Köpfe bleiben erhalten. Im Referenzraum sind diese aber ganz aufgedreht (24Grad).

der Weg:

Zuerst habe ich versucht, das ganze mit DoIF-Schleifen zu lösen. Es funktioniert, aber das Ergebnis war nicht wirklich optimal. Durch die Trägheit des Systems kam es immer zum Überschwingen. Die Räume wurden zu warm. Das kann man zwar mit etwas Wartezeit und Logik abfangen, das macht aber die Steuerung noch träger und unnötig kompliziert. Die bessere Lösung ist daher ein PID-Regler. Diesen bringt FHEM auch gleich mit.

Der PID20-Regler

Auf den ersten Blick sieht es ganz schon verwirrend und kompliziert aus. Ist es aber gar nicht.
FHEM bringt das Modul PID20 mit. Das ist ein einfach zu implementierender PID-Regler (proportional-integral-derivative controller), der genau für diesen Zweck vorgesehen ist.
Wer sich etwas in die Materie einsteigen möchte findet in der Wikipedia ein guten Anfang.
Zum Regeln benötigt der PID20-Regler eigentlich nur die aktuelle Temperatur, die Wunschtemperatur und natürlich das Stellglied (Aktor).

define pid.heizung PID20 HM_654XXX_Climate:measured-temp Ventil

Die Wunschtemperatur geben wir dem Regler einfach mit set pid.heizung desired temperatur:

set pid.heizung desired 22

Damit der Regler aber richtig funktioniert, sollten wir Ihm noch unsere Besonderheiten mitteilen. Das wird über die Attribute geregelt:

attr pid.heizung event-min-interval actuation:1800,actuationCalc:1800,delta:1800,desired:1800,measured:1800,p_d:1800,p_i:1800,p_p:1800
attr pid.heizung pidActorErrorPos 99
attr pid.heizung pidActorInterval 60
attr pid.heizung pidActorLimitLower 1
attr pid.heizung pidActorLimitUpper 100
attr pid.heizung pidActorTreshold 2
attr pid.heizung pidActorValueDecPlaces 0
attr pid.heizung room Heizung

Noch kurz die Erklärung zu den Werten:
pidActorErrorPos 99 # sollte z.B. HM_654XXX_Climate:measured-temp keine Werte liefern, dann stellt der Regler diesen Ventilwert ein
pidActorInterval 60 # höchsten alle 60 Sekunden wir der Aktor (Ventil) gestellt
pidActorLimitLower 1 # kleinster Ventilwert
pidActorLimitUpper 100 # größter Ventilwert
pidActorTreshold 2 # nur wen der berechnete Ventilwert um 2 abweicht, setze ihn auch (das spart Schaltvorgänge)
pidActorValueDecPlaces 0 # Anzahl der Nachkommastellen für Ausgabewert zu Aktor (Ventil)

Ich habe bei mir noch das Attribut event-min-interval gesetzt, welches aber nur für die Diagramme/SVG-Plotts interessant ist. Hier werden jetzt alle 1800 Sekunden Log-Einträge mit den Werten geschrieben.

Der Mischer / Ventil
Bevor wir aber den Regler in Betrieb nehmen können, brauchen wir erst noch die Ventil-Steuerung.
Auch dafür gibt es bereits ein fertiges Modul STELLMOTOR in FHEM. Das ist genau das Passende für ein 3 Wege-Mischer. Für die Ansteuerung des Mischers habe ich mich aufgrund der örtlichen Gegebenheiten für ein 4x Fernschalter auf Hutschiene entschieden (HM-LC-SW4-DR-2). Damit geht die Installation recht einfach hat aber auch Nachteile und birgt auch ein gewisses Risiko!

1.) Der Schalter ist ein Funkschalter und damit nicht 100% zuverlässig
2.) Es wird immer zu kleineren Schaltverzögerungen kommen, die sich im Betrieb aufsummieren können. Im besten Fall heben Sie sich jedoch auf.
2.) Der HM-LC-SW4-DR-2 besitzt keine Wechselschalter, daher könnte es passieren, dass AUF & ZU gleichzeitig geschallten werden und es zur Beschädigung am Stellmotor kommt!

Ich bin mir dessen bewusst und habe es trotzdem so eingerichtet. Wenn möglich, solltet Ihr auf eine Wired-Lösung setzen. Zum Beispiel HMW-IO-12-SW7-DR oder ein Relay-Board für den Raspberry PI (HLS8L-DC5V-S-C).

define Ventil STELLMOTOR FhemDev

Auch hier müssen wir wieder das Modul anpassen:

attr Ventil STMcalibrateDirection L
attr Ventil STMdebugToLog3 0
attr Ventil STMfhemDevRL RelaisRL
attr Ventil STMfhemDevSTART RelaisSTART
attr Ventil STMinvertOut 0
attr Ventil STMlastDiffMax 1
attr Ventil STMmapOffCmd off
attr Ventil STMmapOnCmd on
attr Ventil STMmaxDriveSeconds 120
attr Ventil STMmaxTics 100
attr Ventil STMpollInterval 0.1
attr Ventil STMresetOtherDeviceAtCalibrate 0
attr Ventil STMrlType einzel
attr Ventil STMtimeTolerance 0.01
attr Ventil room Heizung

Zur Erklärung der Atrribute verweise ich hier mal auf das Wiki
Nur soviel:
attr Ventil STMrlType einzel # das ist der wichtigste Parameter. Eine falsche Einstellung kann zu Kurzschluss am Motor führen!
attr Ventil STMfhemDevRL RelaisRL # das Relais für AUF
attr Ventil STMfhemDevSTART RelaisSTART # das Relais für ZU

Bonus, die Heizkreispumpen steuern
Da ich noch 2 Schaltausgänge frei hatte, konnte ich auch gleich die Pumpensteuerung an FHEM übergeben. Die Idee dazu ist recht einfach.
Wenn das Ventil geschlossen ist, benötigt das Haus gerade keine Wärme und die Pumpe muss nicht sinnlos Wasser im Kreis pumpen. Das senkt hoffentlich die Bereitschaftsverluste etwas und spart Strom.
Aber Vorsicht, nicht alle Heizungen vertragen das. Gerade Wandhängeende Thermen mit wenig Wasser-Volumen im Wärmetauscher können sehr schnell überhitzen. Diese brauchen auch meisten ein Mindestdurchfluss! Meine alte Viessmann hat genug Wasser im Kessel, da muss ich mir erst mal keine Gedanken zu machen.

Ist das Ventil zu mehr als 5% geöffnet, besteht Heizbedarf und ich schalte die Pumpe ein.
Sollte die Pumpe mal im Status set_on oder set_off sein (also nicht on oder off), weil der Schaltbefehl per Funk verloren gegangen ist oder der Aktor nicht geantwortet hat, dann setze ich den on oder off-Befehl einfach noch einmal ab.

define doif_Pumpe_Haus \
DOIF ([Ventil:state]>5 and [Pumpe_Haus_WG:state] ne "on") (set Pumpe_Haus on) \
DOELSEIF ([Ventil:state]<5 and [Pumpe_Haus:state] ne "off") (set Pumpe_Haus off)

Mit cmdpause und wait könnt ihr ruhig etwas spielen. Wichtig ist nur do always, damit die Schleife immer wieder durchlaufen wird.

attr doif_Pumpe_Haus cmdpause 60:60
attr doif_Pumpe_Haus do always
attr doif_Pumpe_Haus room Heizung
attr doif_Pumpe_Haus wait 60:60

Eventuell müsste noch der Frostschutz berücksichtigt werden. Aber das beobachte ich noch.
Wenn ja, wird einfach die Abfrage des Außenthermometers mit berücksichtigt:

define doif_Pumpe_Haus \
DOIF ([Ventil:state]>5 and [Pumpe_Haus_WG:state] ne "on") (set Pumpe_Haus on) \
DOELSEIF ([Ventil:state]<5 and [Pumpe_Haus:state] ne "off" and [HM_417B50:temperature]>3) (set Pumpe_Haus off)

Fotos dazu kommen eventuell später noch dazu ;)