Régulation d'une chaudière Frisquet ECO radio System

Vous avez crée un script lua dont vous êtes fier, un .sh génial, un programme python hors du commun, un tuto , c'est ici que vous pouvez les partager.
Soyez précis quant aux prérequis, les manips à faire pour que votre bijou fonctionne (des chmod ?, un apt-get à faire ...)
Décrivez précisément son fonctionnement
Placez votre code entre
et {/Quote]

Répondre
bruch05
Messages : 43
Enregistré le : 27 févr. 2016, 21:06

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par bruch05 »

Bonsoir AllezLeLosc,

Je suis en train de finaliser ma régulation avec le module de commande TCW112-CM que je viens juste de recevoir. (Ethernet ==> Contact sec)
Afin de comprendre le code, je suis repartie de ta dernière version que j'ai abondamment commentée et enrichie avec une trace (fichier CSV pour Excel) et un mode Debug de 1 à 3.

Ma question porte sur le bout de code ci-dessous ou je ne comprends pas la raison d'un passage en mode boost dans le contexte d'un température de départ calculé inférieure à la limite basse.

Merci pour ton éclairage.

Code : Tout sélectionner

          if debug > 1: DbError.DebugPrint("PID",'SetPointTempDepart calculé : ' + str(SetPointTempDepart))          
          if SetPointTempDepart<LimitBasTempDepart: # (4-0) - Si Consigne T° Chaudière inférieure à la limite basse
            if ErrNowTempAmb>Bm1: # Et si écart entre T° Consigne et T° Ambiante > 1°C, on fixe la consigne T° Chaudière à la limite basse  
              SetPointTempDepart=LimitBasTempDepart
            else: # Sinon on fixe la consigne T° Chaudière à 0°C 
              SetPointTempDepart=0.0
              if not BoostDemand: # Et on demande le boost mode durant le DelayTimeBoostDemand sur les cycles suivants, pourquoi ???
                if debug > 1: DbError.DebugPrint("PID",'Boost Demand ! ')                                    
                BoostDemand=True
                StartTimeBoostDemand=time.time()
              else:
                if (time.time()-StartTimeBoostDemand)>DelayTimeBoostDemand:
                  BoostMode=True
                  if debug > 1: DbError.DebugPrint("PID",'Forcage boost mode PID 1 - ???')
                  BoostDemand=False


bruch05
Messages : 43
Enregistré le : 27 févr. 2016, 21:06

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par bruch05 »

Bonjour à tous,

Voici le compte-rendu de la mise en oeuvre de la régulation.
Les 3 premiers jours de fonctionnement sont très prometteurs quant au potentiel de régulation que permet cette solution.
Un GRAND merci à AllezLeLosc qui a développé cette solution.

1°) Le contexte

Maison de 180 m² avec
Sondes de température dans :
- Grand salon cathédrale (Sud - Bien isolé - Robinets ouverts aux max)
- Chambre parents de 15m² (Sud - Bien isolé - Robinet Thermostatique à 3)
- Chambre enfants 1 de 14m² (Nord - Garage en dessous sans isolation - Robinets ouverts aux max)
- Chambre enfant 2 de 12m² (Nord - Tres bien isolé - Robinet Thermostatique à 3)

2°) L'implémentation

- Hébergement : NAS Synology DS415+
- Sondes de température: 4 sondes connectées par module FIBARO System FGBS001
- Commande chaudière :Ethernet controller TCW112-CM avec le cable gris acheté connecté au contact sec :shock:

Image

On voit le cable gris branché à la chaudière en bas à gauche de la photo.

Image

Le switch 5 est mis à OFF afin de stopper la régulation par EcoRadioSystem. La régulation par rapport cyclique par le cable est alors prise en compte.

Image

- Consolidation d'une température consolidée basée sur les 4 températures réelles via le script LUA suivant.
(Quand la T° d'une pièce passe en dessous de 18°C, on garde la T° conservée dans la variable Domoticz. Evite que lorsque l'on aère une pièce, la chaudière démarre à fond !)

Code : Tout sélectionner

--
-- Script       : script_device_calcul_temp_consolidée.lua
--
-- Objectif     : Calcul température consolidée basée sur les 4 sondes de température
--
-- Auteur       : CBR
-- Date         : 12/11/2016
-- Version      : 1.1
---------------------------------------------------------------------------
commandArray = {}

if devicechanged['Temp. Ch Marie'] or devicechanged['Temp. Ch Léa'] or devicechanged['Temp. Ch Parents'] or devicechanged['Temp. Salon'] then

    print "=========== Script Calcul Température Consolidée==========="

    local temp_marie   = tonumber(otherdevices_svalues['Temp. Ch Marie'])   -- Temp relevée dans la chambre de Marie
    local temp_lea     = tonumber(otherdevices_svalues['Temp. Ch Léa'])   -- Temp relevée dans la chambre de Léa
    local temp_parents = tonumber(otherdevices_svalues['Temp. Ch Parents'])   -- Temp relevée dans la chambre des Parents
    local temp_salon   = tonumber(otherdevices_svalues['Temp. Salon'])   -- Temp relevée dans le Salon
    
    local user_variable_name = "temp_marie" -- Nom de la variable 
    local temp_marie_previous = uservariables[user_variable_name]
    
    local user_variable_name = "temp_lea" -- Nom de la variable 
    local temp_lea_previous = uservariables[user_variable_name]

    local user_variable_name = "temp_parents" -- Nom de la variable 
    local temp_parents_previous = uservariables[user_variable_name]

    local user_variable_name = "temp_salon" -- Nom de la variable 
    local temp_salon_previous = uservariables[user_variable_name]

    if temp_marie < 18 then temp_marie = temp_marie_previous end -- Si température < 17 on garde la précédente mesure (ouverture fenetre)
    if temp_lea < 18 then temp_lea = temp_lea_previous end
    if temp_parents < 18 then temp_parents = temp_parents_previous end
    if temp_salon < 18 then temp_salon = temp_salon_previous end

    print ('Temp. Marie réelle = '..temp_marie)
    print ('Temp. Léa réelle = '..temp_lea)
    print ('Temp. Parents réelle = '..temp_parents)
    print ('Temp. Salon réelle = '..temp_salon)

    local temp_consolidee = ((temp_marie * 1) + (temp_lea * 0.5) + (temp_parents * 0.5) + (temp_salon * 0.75)) / 2.75
    local idx = 80 -- Idx Temp. Consolidée
    
    temp_consolidee = math.floor(temp_consolidee * 100 + 0.5) / 100 -- Arrondi au centième
    
    commandArray['UpdateDevice'] = idx .. '|0|' .. tostring(temp_consolidee) -- On écrit la température calculée
    
    commandArray['Variable:temp_marie'] = tostring(temp_marie) -- Sauvegarde pour prochain cycle et récupération dans _previous
    commandArray['Variable:temp_lea'] = tostring(temp_lea)
    commandArray['Variable:temp_parents'] = tostring(temp_parents)
    commandArray['Variable:temp_salon'] = tostring(temp_salon)

    print ('Temp. Marie prise en compte = '..temp_marie)
    print ('Temp. Léa prise en compte = '..temp_lea)
    print ('Temp. Parents prise en compte = '..temp_parents)
    print ('Temp. Salon prise en compte = '..temp_salon)
    print ('Temp. Consolidée = '..temp_consolidee)
    
    
    
	print "=========== Fin Script Calcul Température Consolidée ==========="   
end

return commandArray
3°) Le tableau de bord

Image

SwitchRegul : Arrêt / Marche régulation
BoilerFault : Arrêt de la régulation en cas de défaut chaudière : En cours d'implémentation via entrée module TCW112 (Fera l'objet d'un post)
RegulFault : Indique un passage dans la section Exception du script Python. (Notamment quand accès HTTP au module TCW112 est KO)

4°) Le fichier de paramétrage actuel (NewPID.cfg)

Code : Tout sélectionner

[ParamGlobal]
Trace=1
Debug=2
Periode=30
ScsAdress=34

[ParamPID1]
Kp=10
Ki=0.25
Kd=10
Bm=0.1
LimitBas=24
ErrMaxTempAmb=1
SetPointStepTempDepart=64

[ParamPID2]
K1=1
K2=0
K3=0
Bm=1
ErrMaxTempDepart=10
RapCyc0=50
RapCycMax=100

[ParamBoost]
Delay=300
DelayTimeBoostDemand=120
BoostDeltaTemp=10
SlowDownDelay=60
5°) Le script Python de régulation
Basé sur la dernière version d'AllezLeLosc, j'ai effectué les évolutions suivantes :
- Mise en place d'un mode Trace qui alimente le fichier TRACE.CSV afin de permettre l'analyse du comportement de la régulation dans EXCEL
- Mise en place d'un mode Debug avec plusieurs niveaux 1:Infos / 2: Détails / 3: Détails + I/O
- Intégration du code d'I/O domoticz dans le script (En lieu et place des appels au module DomoJson) - Pas de différence sur le fond
- Mise en oeuvre d'une fonction Boiler afin de faciliter l'adaptation au module de commande de la chaudière.
- Ajouts de nombreux commentaires permettant de comprendre la cinématique de la régulation.

Code : Tout sélectionner

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#***************************************************************
# Régulation Chaudière Frisquet
#***************************************************************

__namescript__='Regulateur-Prod-V2r4.py'
__author__= 'SSO & CBR'
__version__= '2.4'
__status__= 'Exploitation'
__created__='13/02/16'
__modified__='03/11/16'

#***************************************************************
# Repertoire par défaut 
#***************************************************************

workpath = '/volume1/APPSTORE/domoticz/var/scripts/python'
#workpath = '/home/administrator/Regulation'

#***************************************************************
# Imports Module Python 
#***************************************************************

#import pdb #Activation du mode Debug Python
#import sys
#import json
#import domoticz
#import time

import re
import ConfigParser
import requests
from requests.auth import HTTPBasicAuth
import time
import DbError
from datetime import datetime
import csv

#***************************************************************
# Log trace mesures 
#***************************************************************
def Trace(Phase,BoostMode,BoostDemand,SetPointTempAmb,TempAmb,ErrNowTempAmb,ErrPreviousTempAmb,Derivee1,Integral1,TempDepart,SetPointTempDepart,ErrPrevious0TempDepart,ErrPreviousTempDepart,Periode,RapCyc):
    if  trace != 0:
        now = time.localtime(time.time())
        fname = workpath + '/trace.csv'
        file = open(fname, "ab")
        writer = csv.writer(file)
        writer.writerow( (time.strftime("%d/%m/%y %H:%M:%S", now),Phase,BoostMode,BoostDemand,SetPointTempAmb,TempAmb,ErrNowTempAmb,ErrPreviousTempAmb,Derivee1,Integral1,TempDepart,SetPointTempDepart,ErrPrevious0TempDepart,ErrPreviousTempDepart,Periode,RapCyc) )
        file.close()
    return(0)

#***************************************************************
# Renvoie le nombre de secondes d'une date "16/08/2013 09:51:43" 
#***************************************************************
def Secondes(s):
    #s = "2013-08-16 09:51:43"
    d = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
    r = time.mktime(d.timetuple())
    return (r)

#*********************************************
# Extrait valeur numérique
#*********************************************
def Valeur(chaine):
    pat = "[^A-Za-z%]+"             # expression régulière à utilise
    prog = re.compile(pat)          # création de l'objet d'analyse
    result = prog.search(chaine)    # extraction de la chaîne à l'aide de l'expression régulière
    return (result.group())


#*********************************************
# Ecriture Device Domoticz
#*********************************************
def WriteDevice(domoticz_idx,param,val):

    write_url_text1='/json.htm?type=command&param='
    write_url_text2='&idx='
    write_url_text3='&'

    requete='http://'+domoticz_ip+':'+domoticz_port+write_url_text1+param+write_url_text2+str(domoticz_idx)+write_url_text3+val 
    if debug > 2: DbError.Write("Write Device requete",requete)    
    r=requests.get(requete,auth=HTTPBasicAuth(user,password))
    
    if  r.status_code != 200:
        DbError.Write(__namescript__,"-1-Erreur Lecture API Domoticz")
        return("KO")
    else:
        return("OK")
 

#*********************************************
# Lecture Device Domoticz
#*********************************************
def ReadDevice(domoticz_idx,param):

    read_url_text1='/json.htm?type=devices&rid='


    requete='http://'+domoticz_ip+':'+domoticz_port+read_url_text1+str(domoticz_idx)
    if debug > 2: DbError.Write("Read Device requete",requete)
    r=requests.get(requete,auth=HTTPBasicAuth(user,password))
    
    domoticzjson=r.json()
    for i, v in enumerate(domoticzjson["result"]):
       val= domoticzjson["result"][i][param] 
    if  r.status_code != 200:
        DbError.Write(__namescript__,"-2-Erreur Lecture API Domoticz")
        return("KO")
    else:
        return(val)    

#*********************************************
# Ecriture Device Pilotage chaudière
#*********************************************
# Cette section peut etre remplacée pour piloter un device quelconque.

def WriteBoiler(val):

    requete='http://192.168.0.251/status.xml?r1='+str(val)
    
    if debug > 2: DbError.Write("Write Boiler requete",requete)
    r=requests.get(requete,auth=HTTPBasicAuth(user,password))
    if debug > 2: DbError.Write("Write Boiler return",r)
    
    if  r.status_code != 200:
        DbError.Write(__namescript__,"-1-Erreur Ecriture carte TCW112")
        return("KO")
    else:
        return("OK")

#*********************************************

#***************************************************************
# Paramètres 
#***************************************************************

#--------------Généraux-------------------
domoticz_ip='192.168.0.252'
domoticz_port='8084'
user='admin'
password='Omc2jay6;'
StandBy=False

CfgPID=ConfigParser.ConfigParser()
CfgPID.read(workpath + '/NewPid.cfg')
trace=CfgPID.getint('ParamGlobal','Trace')
debug=CfgPID.getint('ParamGlobal','Debug')
Periode=CfgPID.getint('ParamGlobal','Periode')
#----------------------------------

#--------------Idx-------------------
IdxSwitchRegul='78'        # Regulation On/Off    
IdxBoilerFault='79'        # Chaudière en sécurité
IdxSetPointTempAmb='105'   # Consigne température ambiante
IdxTempAmb='80'            # Mesure température ambiante
IdxRapCyclique='81'        # Rapport Cyclique commande  
IdxCommande='110'          # Switch commande chaudière  
IdxTempDepart='100'        # Mesure température départ chaudière
IdxSetPointTempDepart='82' # Consigne température départ chaudière
IdxRegulFault='113'        # Erreur Regulation    
#----------------------------------


#------Paramètres PID1--------------
ErrPreviousTempAmb=0.0
T0=Secondes(ReadDevice(IdxTempAmb,'LastUpdate'))
Integral1=0.0
Derivee1=0.0
SetPointTempDepart=0.0
StepTempDepart=False
ReleaseSetPointTempDepart=False
#----------------------------------

#------Paramètres PID2--------------
ErrPreviousTempDepart=0.0
ErrPrevious0TempDepart=0.0
RapCyc=0.0
#----------------------------------

#------Paramètres Relance----------
BoostMode=True     # Au 1er cycle du script, on est par défaut en "boost mode"
BoostDemand=False
ExitFromBm=False
#----------------------------------

TempDepart=0

Status=Trace("Phase","BoostMode","BoostDemand","SetPointTempAmb","TempAmb","ErrNowTempAmb","ErrPreviousTempAmb","Derivee1","Integral1","TempDepart","SetPointTempDepart","ErrPrevious0TempDepart","ErrPreviousTempDepart","Periode","RapCyc") 

while True:
  try:
    if debug > 0: DbError.Write("==================================================================","=")
    SwitchRegul=ReadDevice(IdxSwitchRegul,"Data")    
    BoilerFault=ReadDevice(IdxBoilerFault,"Data")
    if SwitchRegul=='On' and BoilerFault=='Off': # Si arrêt demandé ou en sécurité, on passe en section ARRET COMPLET
      StandBy=False
      CfgPID.read(workpath + '/NewPid.cfg')

      #------Paramètres Trace debug --------------
      trace=CfgPID.getint('ParamGlobal','Trace')
      debug=CfgPID.getint('ParamGlobal','Debug')

      #------Paramètres PID1--------------
      Kp=CfgPID.getfloat('ParamPID1','Kp')                       # 10
      Ki=CfgPID.getfloat('ParamPID1','Ki')                       # 0,25
      Kd=CfgPID.getfloat('ParamPID1','Kd')                       # 10
      Bm1=CfgPID.getfloat('ParamPID1','Bm')                      # 0,1°C
      ErrMaxTempAmb=CfgPID.getfloat('ParamPID1','ErrMaxTempAmb') # 1°C
      LimitBasTempDepart=CfgPID.getfloat('ParamPID1','LimitBas') # 24°C
      SetPointStepTempDepart=CfgPID.getfloat('ParamPID1','SetPointStepTempDepart') # 64°C
      #----------------------------------

      #------Paramètres PID2--------------
      K1=CfgPID.getfloat('ParamPID2','K1')                             # 1
      K2=CfgPID.getfloat('ParamPID2','K2')                             # 0
      K3=CfgPID.getfloat('ParamPID2','K3')                             # 0
      Bm2=CfgPID.getfloat('ParamPID2','Bm')                            # 1
      ErrMaxTempDepart=CfgPID.getfloat('ParamPID2','ErrMaxTempDepart') # 10
      RapCyc0=CfgPID.getfloat('ParamPID2','RapCyc0')                   # 50
      RapCycMax=CfgPID.getfloat('ParamPID2','RapCycMax')               # 100
      #----------------------------------

      #------Paramètres Relance----------
      BoostDelay=CfgPID.getfloat('ParamBoost','Delay') # 300s, Durée de la phase de boost, tant que delta t° non atteint
      DelayTimeBoostDemand=CfgPID.getfloat('ParamBoost','DelayTimeBoostDemand') # 120s
      BoostDeltaTemp=CfgPID.getfloat('ParamBoost','BoostDeltaTemp') # 10°C, delta t° définissant la sortie de la phase de boost
      SlowDownDelay=CfgPID.getfloat('ParamBoost','SlowDownDelay') # 60s, délai fin de de boucle boost
      #----------------------------------

      SetPointTempAmb=float(Valeur(ReadDevice(IdxSetPointTempAmb,"Data")))
      TempAmb=float(Valeur(ReadDevice(IdxTempAmb,"Data")))
      ErrNowTempAmb=SetPointTempAmb-TempAmb

      if BoostMode: # Au 1er cycle du script, on est par défaut en "boost mode"
        if debug > 0: DbError.Write("BoostMode",BoostMode)
        if ErrNowTempAmb>Bm1: # Si Boost Mode et Si l'écart de température entre T° Consigne et T° Ambiante > 0,1°C, on lance la phase de boost
          if debug > 1: DbError.Write("BoostMode",'ErrNowTempAmb>Bm1')
          TempDepart=float(Valeur(ReadDevice(IdxTempDepart,"Data")))
          StartTempDepart=TempDepart
          StopTempDepart=StartTempDepart+BoostDeltaTemp # quand temp stop atteinte, on sort du "boost mode" **1
          StartTimeBoostMode=time.time()
          RapCyc=100.0
          Status=WriteDevice(IdxRapCyclique,"udevice","svalue=" + str(RapCyc)) # on demande 100% de puissance à la chaudière
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          Status=WriteBoiler(1)
          if Status!='OK':
            DbError.Write(__namescript__,Status)
          TempDepart=float(Valeur(ReadDevice(IdxTempDepart,"Data")))
          while TempDepart<StopTempDepart and (time.time()-StartTimeBoostMode)<BoostDelay: # **1 ou délai "boost mode" dépassé
            if debug > 1: DbError.Write("BoostMode","Boucle montée en température durant " + str(BoostDelay) + " s tant que T° départ " + str(TempDepart) + "°C n'atteint pas " + str(StopTempDepart) + "°C")
            time.sleep(Periode)
            TempDepart=float(Valeur(ReadDevice(IdxTempDepart,"Data")))
          RapCyc=0.0
          Status=WriteDevice(IdxRapCyclique,"udevice","svalue=" + str(RapCyc)) # on stoppe la commande chaudière
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          Status=WriteBoiler(0)
          if Status!='OK':
            DbError.Write(__namescript__,Status)
          SetPointInitTempDepart=float(Valeur(ReadDevice(IdxTempDepart,"Data"))) # la temp de départ devient en sortie de boost la temp de référence de départ PID
          SetPointTempDepart=SetPointInitTempDepart
          if debug > 1: DbError.Write("BoostMode","Latence " + str(SlowDownDelay) + " s")
          time.sleep(SlowDownDelay) # On attend afin de laisser place à l'inertie de la chaudière afin qu'elle redescende en puissance. (Commande n'implique pas une réaction immédiate)
          BoostMode=False
          Integral1=0.0
          Status=Trace("ErrNowTempAmb>Bm1",BoostMode,BoostDemand,SetPointTempAmb,TempAmb,ErrNowTempAmb,ErrPreviousTempAmb,Derivee1,Integral1,TempDepart,SetPointTempDepart,ErrPrevious0TempDepart,ErrPreviousTempDepart,Periode,RapCyc)  
        else: # Si Boost Mode et Si l'écart de température entre T° Consigne et T° Ambiante =< 0,1°C, on met la chaudière à l'arrêt
          if debug > 1: DbError.Write("BoostMode",'ErrNowTempAmb<Bm1')          
          Status=WriteDevice(IdxSetPointTempDepart,"udevice","svalue=0.0")
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz de la consigne de température de départ")
          Status=WriteDevice(IdxRapCyclique,"udevice","svalue=0")          
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          Status=Trace("ErrNowTempAmb<Bm1",BoostMode,BoostDemand,SetPointTempAmb,TempAmb,ErrNowTempAmb,ErrPreviousTempAmb,Derivee1,Integral1,TempDepart,SetPointTempDepart,ErrPrevious0TempDepart,ErrPreviousTempDepart,Periode,RapCyc)  
          time.sleep(Periode) #CBR rajout afin d'éviter une boucle sans délai

      else: # Si hors "Boost Mode" 
        #----------------PID 1-------------------------
        if ErrNowTempAmb>ErrMaxTempAmb: # (1-0) Si écart entre T° Consigne et T° Ambiante > 1°C, on force le "Boost Mode" pour le prochain cycle et on passe au PID 2 en gardant la meme consigne (==> 2)
          BoostMode=True # (1-1)
          if debug > 1: DbError.Write("PID",'Forcage boost mode PID 1 - Ecart > ' + str(ErrMaxTempAmb))
        elif abs(ErrNowTempAmb)>Bm1: # (1-2) Sinon, si l'écart est supérieur à +/- 0,1°C, on calcule le PID
          if debug > 1: DbError.Write("PID",'Ecart > +/- 0,1°C, calcul PID')
          if ErrNowTempAmb!=ErrPreviousTempAmb: # Si l'écart actuel est différent de l'écart précédent, on calcule la dérivée.            
            T=Secondes(ReadDevice(IdxTempAmb,'LastUpdate'))
            try:
              Derivee1=(ErrNowTempAmb-ErrPreviousTempAmb)/(T-T0)
            except ZeroDivisionError: # Si T = T0, la dérivée = 0
              Derivee1=0.0
            ErrPreviousTempAmb=ErrNowTempAmb
            T0=T
          if ExitFromBm: # (3) On sort de la Bande Morte, dans ce cas on soustrait le facteur "P" afin qu'il ne soit pas pris en compte dans le calcul et on annule le facteur "I", on ne garde que la Dérivée 
            if debug > 1: DbError.Write("PID",'ExitFromBm')
            SetPointInitTempDepart=SetPointTempDepart-Kp*ErrNowTempAmb
            Integral1=0.0
            ExitFromBm=False # Au prochain cycle, on calculera le PID complet 
          SetPointTempDepart=SetPointInitTempDepart+Kp*ErrNowTempAmb+Ki*(Integral1+ErrNowTempAmb)+Kd*Derivee1 # On calcul le PID 1 afin de définir la consigne T° Chaudière
          if debug > 1: DbError.Write("PID",'SetPointTempDepart calculé : ' + str(SetPointTempDepart))          
          if SetPointTempDepart<LimitBasTempDepart: # (4-0) - Si Consigne T° Chaudière inférieure à la limite basse
            if ErrNowTempAmb>Bm1: # Et si écart entre T° Consigne et T° Ambiante > 1°C, on fixe la consigne T° Chaudière à la limite basse  
              SetPointTempDepart=LimitBasTempDepart
            else: # Sinon on fixe la consigne T° Chaudière à 0°C 
              SetPointTempDepart=0.0
              if not BoostDemand: # Et on demande le boost mode durant le DelayTimeBoostDemand sur les cycles suivants, pourquoi ???
                if debug > 1: DbError.Write("PID",'Boost Demand ! ')                                    
                BoostDemand=True
                StartTimeBoostDemand=time.time()
              else:
                if (time.time()-StartTimeBoostDemand)>DelayTimeBoostDemand:
                  BoostMode=True
                  if debug > 1: DbError.Write("PID",'Forcage boost mode PID 1 - ???')
                  BoostDemand=False
          elif SetPointTempDepart>=SetPointStepTempDepart: # (4-1) - Si Consigne T° Chaudière supérieure à la limite haute
            if not StepTempDepart: # Initialisation séquence de 3600 secondes, soit une heure
              StepTempDepart=True
              StartTimeStepTempDepart=time.time()
            else:
              if (time.time()-StartTimeStepTempDepart)>3600 and not ReleaseSetPointTempDepart: # Tant que l'on boucle depuis moins d'une heure, ...  
                ReleaseSetPointTempDepart=True
            if not ReleaseSetpointTempDepart: # ... On fixe la Consigne T° Chaudière à la limite haute 
              SetPointTempDepart=SetPointStepTempDepart
            else: # Sinon, au dela du délai d'une heure, on calcule l'intégrale par la somme des écarts
              Integral1=Integral1+ErrNowTempAmb
          else: # (4-2) - Si Consigne T° Chaudière entre les limite basses et hautes, on calcule l'intégrale par la somme des écarts
            Integral1=Integral1+ErrNowTempAmb
            StepTempDepart=False # On réinitialise
            ReleaseSetPointTempDepart=False # On réinitialise
        else: # (1-3) Sinon, si l'écart est inférieur à +/- 0,1°C, on applique uniquement le "D" du PID pour stabiliser 
          ExitFromBm=True # On positionne cette variable pour qu'au prochain calcul PID les facteurs "P" et "I" ne soient pas appliqués (==> 3)
          if debug > 1: DbError.Write("PID",'Ecart =< +/- 0,1°C, calcul P uniquement')     
          if ErrNowTempAmb!=ErrPreviousTempAmb:
            T=Secondes(ReadDevice(IdxTempAmb,'LastUpdate'))
            try:
              Derivee1=(ErrNowTempAmb-ErrPreviousTempAmb)/(T-T0)
            except ZeroDivisionError:
              Derivee1=0.0
            ErrPreviousTempAmb=ErrNowTempAmb
            T0=T
            SetPointTempDepart=SetPointTempDepart+Kd*Derivee1
        # (2)    
        Status=WriteDevice(IdxSetPointTempDepart,"udevice","svalue=" + str(round(SetPointTempDepart,1)))
        if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz de la consigne de température de départ")
        #----------------------------------------------

        #----------------PID 2-------------------------
        TempDepart=float(Valeur(ReadDevice(IdxTempDepart,'Data')))
        ErrNowTempDepart=SetPointTempDepart-TempDepart
        if ErrNowTempDepart>ErrMaxTempDepart: # Si écart entre T° Consigne chaudière et T° départ chaudière > 10°C ...
          BoostMode=True # ..., on passe en "boost mode" à la prochaine boucle
          if debug > 1: DbError.Write("PID",'Forcage boost mode PID 2 - Ecart > ' + str(ErrMaxTempDepart))
        else: # Si écart entre T° Consigne chaudière et T° départ chaudière < 10°C 
          if SetPointTempDepart==0.0: # Si consigne T° Chaudière à 0, on demande l'arret de la chauffe
            RapCyc=0.0
          else:
            if abs(ErrNowTempDepart)>Bm2: # Si écart entre T° Consigne chaudière et T° départ chaudière supérieur à +/- 1°C
              if ErrNowTempDepart>0: # Si écart positif, on majore le rapport cyclique par rapport au 50% proportionnellement à l'écart (K1) + au delta de l'écart T-1 (K2) + aux deltas des écarts T-2 (K3) 
                RapCyc=RapCyc0+K1*(ErrNowTempDepart-Bm2)+K2*(ErrNowTempDepart-ErrPreviousTempDepart)+K3*(ErrNowTempDepart+ErrPrevious0TempDepart-2*ErrPreviousTempDepart)
              else:                  # Si écart négatif, on minore le rapport cyclique par rapport au 50% proportionnellement à l'écart (K1) + au delta de l'écart T-1 (K2) + aux deltas des écarts T-2 (K3)
                RapCyc=RapCyc0+K1*(ErrNowTempDepart+Bm2)+K2*(ErrNowTempDepart-ErrPreviousTempDepart)+K3*(ErrNowTempDepart+ErrPrevious0TempDepart-2*ErrPreviousTempDepart)
              #Si en asservissement on veut accélérer alors on interdit de freiner, si on va trop vite et inversement.
              if RapCyc<RapCyc0 and ErrNowTempAmb>Bm1: # Si RapCyc < 50% et écart entre T° Consigne et T° ambiante > 0,1°C... 
                RapCyc=RapCyc0 # ..., alors on fixe RapCyc à 50%, autrement dit, on garde la meme température départ chaudière
              elif RapCyc>RapCyc0 and ErrNowTempAmb<-Bm1: # Si RapCyc > 50% et écart entre T° Consigne et T° ambiante < -0,1°C...
                RapCyc=RapCyc0 # ..., alors on fixe RapCyc à 50%, autrement dit, on garde la meme température départ chaudière
            else: # Si écart entre T° Consigne chaudière et T° départ chaudière inférieur à +/- 1°C
              RapCyc=RapCyc0 # ..., alors on fixe RapCyc à 50%, autrement dit, on garde la meme température départ chaudière
          if RapCyc>RapCycMax: # Si max atteint,on bride au max
            RapCyc=RapCycMax
          elif RapCyc<0.0:     # Si 0 atteint,on bride à 0
            RapCyc=0.0
        #----------------------------------------------

        #------------Debug & Trace PID---------------

        if debug > 0: DbError.Write("---------------------------------------------------------","-")
        if debug > 1: DbError.Write("PID1 : BoostDemand",BoostDemand)
        if debug > 1: DbError.Write("PID1 : SetPointTempAmb",SetPointTempAmb)        
        if debug > 0: DbError.Write("PID1 : TempAmb",TempAmb)

        if debug > 0: DbError.Write("PID1 : ErrNowTempAmb",ErrNowTempAmb)
        if debug > 1: DbError.Write("PID1 : ErrPreviousTempAmb",ErrPreviousTempAmb)
        if debug > 1: DbError.Write("PID1 : Derivee1",Derivee1)
        if debug > 1: DbError.Write("PID1 : Integral1",Integral1)

        if debug > 0: DbError.Write("PID2 : TempDepart",TempDepart)
        if debug > 0: DbError.Write("PID2 : SetPointTempDepart",SetPointTempDepart)
        if debug > 0: DbError.Write("PID2 : ErrNowTempDepart",ErrNowTempDepart)
        if debug > 1: DbError.Write("PID2 : ErrPrevious0TempDepart",ErrPrevious0TempDepart)
        if debug > 1: DbError.Write("PID2 : ErrPreviousTempDepart",ErrPreviousTempDepart)
        if debug > 1: DbError.Write("PID2 : RapCyc",RapCyc)                        

        if debug > 0: DbError.Write("----","---------------------------------")
        Status=Trace("PID",BoostMode,BoostDemand,SetPointTempAmb,TempAmb,ErrNowTempAmb,ErrPreviousTempAmb,Derivee1,Integral1,TempDepart,SetPointTempDepart,ErrPrevious0TempDepart,ErrPreviousTempDepart,Periode,RapCyc)  
 
        #------------ Sauvegarde pour cycle suivant --------------- 
        ErrPrevious0TempDepart=ErrPreviousTempDepart # On sauvegarde l'écart T-2
        ErrPreviousTempDepart=ErrNowTempDepart       # On sauvegarde l'écart T-1
   
        #------------Pilotage chaudière---------------
 
        Status=WriteDevice(IdxRapCyclique,"udevice","svalue=" + str(RapCyc))  
        if Status!='OK':
          DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
        TOn=RapCyc*Periode/100 # On calcule la durée ON proportionnellement au rapport cyclique
        TOff=Periode-TOn       # On calcule la durée OFF afin que le TOn + TOff soit égal à la période 
        if TOn!=0.0:
          Status=WriteBoiler(1)
          if Status!='OK':
            DbError.Write(__namescript__,Status)
          else:
            time.sleep(TOn) # On laisse à On durant la durée TOn
        if TOff!=0.0:
          Status=WriteBoiler(0)
          if Status!='OK':
            DbError.Write(__namescript__,Status)
          else:
            time.sleep(TOff) # On laisse à On durant la durée TOff
        #----------------------------------------------

    else: # Si arrêt demandé ou en sécurité, on passe en section ARRET COMPLET
      #-------- Mise à zéro de la commande------------
      if debug > 0: DbError.Write("STOP - SwitchRegul",SwitchRegul)
      if debug > 0: DbError.Write("STOP - BoilerFault",BoilerFault)
      Status=WriteDevice(IdxRapCyclique,"udevice","svalue=0")
      if Status!='OK':
        DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
      #----------------------------------------------

      if not StandBy: # On exécute cette section si passage de ON à OFF de la régulation 
        #-------- Mise au repos du relais--------------
        Status=WriteBoiler(0)
        if Status!='OK':
          DbError.Write(__namescript__,Status)
        #----------------------------------------------

        #------Paramètres PID1--------------
        ErrPreviousTempAmb=0.0
        T0=Secondes(ReadDevice(IdxTempAmb,'LastUpdate'))
        Integral1=0.0
        Derivee1=0.0
        SetPointTempDepart=0.0
        #----------------------------------

        #------Paramètres PID2--------------
        ErrPreviousTempDepart=0.0
        ErrPrevious0TempDepart=0.0
        #----------------------------------

        #------Paramètres Relance----------
        BoostMode=True
        #----------------------------------
        StandBy=True
      time.sleep(Periode) # Temporisation en mode ARRET
      # Fin de la boucle WHILE
  except Exception, e: # Exception, on ne doit jamais passer par là.
    try:
      DbError.Write("************** EXCEPTION **************","************** EXCEPTION **************")
      DbError.Write("EXCEPTION : ",e)   
      Status=WriteDevice(IdxRegulFault,"switchlight","switchcmd=On")
      if Status!='OK':
         DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz de l'erreur de régulation")
    except Exception, e:
      pass
    time.sleep(Periode)
6°) Le script regulation.sh qui lance la régulation au démarrage du NAS. (Dans /usr/local/etc/rc.d avec un chmod 755)

Code : Tout sélectionner

#!/bin/sh

cd /volume1/APPSTORE/domoticz/var/scripts/python
./Regulateur-Prod-V2r4.py >>Regulateur-Prod-V2r4.txt 2>&1 </dev/null &
Le fichier Regulateur-Prod-V2r4.txt constitue le LOG que le mode Debug alimente.

7°) 1er bilan

Asservissement global de la température consolidée à la consigne de 20°c.
(Le PIC est lié au fait que ma fille a mis un chauffage d'appoint car elle avait froid à son bureau)

Image

Asservissement de la température de départ chaudière à la consigne calculée par l’algorithme PID.
(On voit que l'asservissement a passé à 0°C la consigne départ chaudière en réponse au fort écart positif de T° ambiante suite au PIC)
Les PICs vert sont liés au chauffage de l'Eau Sanitaire (Ballon de la chaudière) Douches de ces dames :D

Image

On voit ci-après que la régulation sur la base de la Température Consolidée ne permet pas d'avoir une température correctement régulée partout, notamment dans la chambre de Marie. Je dois travailler le calcul de la température consolidée afin d'améliorer l'équilibre global et aussi régler les têtes thermostatiques des chambres de Parents & Léa, si tant est que cela ait un impact sur la T° de ces 2 pièces.

Image

8°) Gestion T°C Jour / Nuit (Non activée dans le bilan ci-dessus)

Via scheduler du NAS:

Image

9°) A venir

- Optimisation du calcul de la T° Consolidée
- Mise en place de l'acquisition du défaut chaudière via le module TCW112 (Si AllezLeLosc a une idée de comment, je suis preneur)

Tof
Modifié en dernier par bruch05 le 13 nov. 2016, 07:51, modifié 13 fois.

allezlelosc
Messages : 49
Enregistré le : 21 oct. 2015, 21:34

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par allezlelosc »

bruch05 a écrit :Bonsoir AllezLeLosc,

Je suis en train de finaliser ma régulation avec le module de commande TCW112-CM que je viens juste de recevoir. (Ethernet ==> Contact sec)
Afin de comprendre le code, je suis repartie de ta dernière version que j'ai abondamment commentée et enrichie avec une trace (fichier CSV pour Excel) et un mode Debug de 1 à 3.

Ma question porte sur le bout de code ci-dessous ou je ne comprends pas la raison d'un passage en mode boost dans le contexte d'un température de départ calculé inférieure à la limite basse.

Merci pour ton éclairage.

Code : Tout sélectionner

          if debug > 1: DbError.DebugPrint("PID",'SetPointTempDepart calculé : ' + str(SetPointTempDepart))          
          if SetPointTempDepart<LimitBasTempDepart: # (4-0) - Si Consigne T° Chaudière inférieure à la limite basse
            if ErrNowTempAmb>Bm1: # Et si écart entre T° Consigne et T° Ambiante > 1°C, on fixe la consigne T° Chaudière à la limite basse  
              SetPointTempDepart=LimitBasTempDepart
            else: # Sinon on fixe la consigne T° Chaudière à 0°C 
              SetPointTempDepart=0.0
              if not BoostDemand: # Et on demande le boost mode durant le DelayTimeBoostDemand sur les cycles suivants, pourquoi ???
                if debug > 1: DbError.DebugPrint("PID",'Boost Demand ! ')                                    
                BoostDemand=True
                StartTimeBoostDemand=time.time()
              else:
                if (time.time()-StartTimeBoostDemand)>DelayTimeBoostDemand:
                  BoostMode=True
                  if debug > 1: DbError.DebugPrint("PID",'Forcage boost mode PID 1 - ???')
                  BoostDemand=False

Bonjour Bruch05,

La variable LimitBasTempDepart correspond à une phase de régulation où la vanne est fermée depuis quelques minutes et la pompe se coupe donc plus de chauffage. Donc l'idée à la base c'était d'arrêter le pilotage de la chaudière en dessous de cette limite. J'ai réglé la valeur à 23°C chez moi. Et si la demande de chauffage revient il faut donc réveiller "mémère" et la booster.

Concernant le bosstmode, il y a des bugs ou des phases pas très bien pensées. Je m'explique, il y a deux semaines nous partons pour quelques jours dans la famille. le lendemain je vois que la chaudière ne s'est pas relancée à la température que j'avais réglée. Impossible de la relancer à distance. Donc on est revenu quelques jours plus tard et le capteur de pression de la chaudière était HS (pas de panique, pièce très facile à remplacer et qui ne coûte pas bien chère : 32€ttc).
Là je remets la chaudière en route et c'est un peu le grand n'importe quoi ! on monte à 60°C on redescent à 30 et la température ambiante ne remonte le plus rapidement possible.
Je pense que tu as trouvé des pistes pour corriger le problème. Je vais regarder de mon côté mais je suis pas mal occupé en ce moment.

Par contre la régulation en mode "maintien de consigne" va très bien, les bugs sont présents quand on est loin de la consigne. c'est à dire quand l'écart est supérieur à ErrMaxTempAmb

Bon courage à plus

allezlelosc
Messages : 49
Enregistré le : 21 oct. 2015, 21:34

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par allezlelosc »

Pendant que j'y pense quand je cherchais comment remplacer le capteur de pression, je suis tombé sur ce document qui m'en a appris plus sur le fonctionnement de notre marmite. je vous joins donc le document. En espérant aller un peu plus loin dans l'algorithme qui nous permettra de réellement maîtriser cette chaudière.

http://formatherm.free.fr/cariboost_fil ... 20ERS.ppsx

allezlelosc
Messages : 49
Enregistré le : 21 oct. 2015, 21:34

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par allezlelosc »

Bonjour à tous,

j'ai pris un peu de temps pour corriger deux trois trucs et renseigner à quoi servaient les variables, voir le script ci-après

@Bruch05
Pour récupérer un défaut sur la chaudière j'ai quelques pistes (des plus simples aux plus farfelues)
- Récupérer par le biais d'une sonde la température du bain marie de la chaudière : si elle n'est pas supérieure à 60°C c'est que la chaudière ne chauffe pas donc en dérangement (défaut ou interrupteur coupé)
- il existe peut-être un renvoi de défaut mais je ne sais pas où
- Prendre périodiquement une photo du bandeau en façade et faire un peu de traitement d’image. :geek:

Code : Tout sélectionner

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""RegulChauff.py : Script pour la régulation de chauffage"""

"""   Historique des modifications
Information sur le BoostMode:
Ce mode permet de réveiller le chauffage quand la chaudière est à l'arrêt (voyants de température en façade éteints ==> pompe à l'arrêt)
2 possibilités de sortir du bootmode:
- au bout d'une tempo rentrée en paramètre
- si la température de départ a suffisament augmentée

12/11/2016:
-----------
  - Ajout tempo manquante, merci à CBR alias Bruch05
  - Ajout du Mode PowerMax qui sert quand les écarts sont importants (ErrMaxTempAmb et ErrMaxTempDepart) --> on veut toute la puissance disponible
  - Le BoostMode ne sert plus que pour réveiller la chaudière
"""

__namescript__='RegulChauff.py'
__author__= 'SSO'
__version__= '2.3"'
__status__= 'Exploitation'
__created__='13/2/16'
__modified__='12/11/16'

import ConfigParser
import DomoJson
import time
import DbError
import SCS

#------Paramètres généraux ----------
CfgPID=ConfigParser.ConfigParser()
CfgPID.read('/home/pi/domoticz/scripts/python/Chauffage/NewPID.cfg')
Periode=CfgPID.getint('ParamGlobal','Periode')
ScsAdress=CfgPID.get('ParamGlobal','ScsAdress')
#----------------------------------

#--------------Idx-------------------
IdxSwitchRegul='112'
IdxSetPointTempAmb='111'
IdxTempAmb='16'
IdxCommande='116'
IdxTempDepart='130'
IdxBoilerFault='132'
IdxSetPointTempDepart='168'
#----------------------------------

#------Paramètres PID1--------------
ErrPreviousTempAmb=0.0
T0=DomoJson.Read(IdxTempAmb,0)
Integral1=0.0
Derivee1=0.0
SetPointTempDepart=0.0
StepTempDepart=False
ReleaseSetPointTempDepart=False
ExitFromBm=False
#----------------------------------

#------Paramètres PID2--------------
ErrPreviousTempDepart=0.0
ErrPrevious0TempDepart=0.0
RapCyc=0.0
#----------------------------------

#------Paramètres de réveil de la chaudière----------
BoostMode=True
BoostDemand=False
#----------------------------------

#------Paramètres de puissance maximale----------
PowerMax=False #ajout du 12/11/2016
#----------------------------------

#*********************************************



while True:
  try:
    SwitchRegul=DomoJson.Read(IdxSwitchRegul,3)#interrupteur Domoticz
    BoilerFault=DomoJson.Read(IdxBoilerFault,3)#varaible domoticz pour signaler le défaut (pas encore fonctionnel)
    if SwitchRegul=='On' and BoilerFault=='Off':
      StandBy=False
      CfgPID.read('/home/pi/domoticz/scripts/python/Chauffage/NewPID.cfg')

      #------Paramètres PID1--------------
      Kp=CfgPID.getfloat('ParamPID1','Kp')
      Ki=CfgPID.getfloat('ParamPID1','Ki')
      Kd=CfgPID.getfloat('ParamPID1','Kd')
      Bm1=CfgPID.getfloat('ParamPID1','Bm')
      ErrMaxTempAmb=CfgPID.getfloat('ParamPID1','ErrMaxTempAmb')#Erreur maximale tolérée avant d'aller chercher la puissance maximale
      LimitBasTempDepart=CfgPID.getfloat('ParamPID1','LimitBas') #en dessous de cette limite le chauffage est supposé être à l'arrêt (voyants de température en façade éteints)
      SetPointStepTempDepart=CfgPID.getfloat('ParamPID1','SetPointStepTempDepart') #il y a une étape dans la montée en température du circuit de chauffage (procédure Frisquet) : il reste à 60°C pendant 1 heure avant d'augmenter de nouveau
      #----------------------------------

      #------Paramètres PID2--------------
      K1=CfgPID.getfloat('ParamPID2','K1')
      K2=CfgPID.getfloat('ParamPID2','K2')
      K3=CfgPID.getfloat('ParamPID2','K3')
      Bm2=CfgPID.getfloat('ParamPID2','Bm')
      ErrMaxTempDepart=CfgPID.getfloat('ParamPID2','ErrMaxTempDepart')#Erreur maximale tolérée avant d'aller chercher la puissance maximale
      RapCyc0=CfgPID.getfloat('ParamPID2','RapCyc0')#Rapport cyclique moyen permemttant de maintenir le chauffage à une tempéarure donnée (normalement c'est 50)
      RapCycMax=CfgPID.getfloat('ParamPID2','RapCycMax')#Rapport cyclique maximal auquel on peut piloter le chauffage (100 par défaut)
      #----------------------------------

      #------Paramètres Relance----------
      BoostDelay=CfgPID.getfloat('ParamBoost','Delay') #Durée maximale du BoostMode, pendant ce temps on maintient le contact de pilotage fermé
      DelayTimeBoostDemand=CfgPID.getfloat('ParamBoost','DelayTimeBoostDemand')
      BoostDeltaTemp=CfgPID.getfloat('ParamBoost','BoostDeltaTemp')#Delta Temp à partir du quel on sort du BoostMode
      SlowDownDelay=CfgPID.getfloat('ParamBoost','SlowDownDelay') #après un boostmode et afin de ne pas dépasser la consigne en ambiance on coupe le contact de pilotage de la chaudière
      #----------------------------------

      SetPointTempAmb=float(DomoJson.Read(IdxSetPointTempAmb,3))
      TempAmb=DomoJson.Read(IdxTempAmb,1)
      ErrNowTempAmb=SetPointTempAmb-TempAmb

      if BoostMode: 
        if ErrNowTempAmb>Bm1:
          TempDepart=DomoJson.Read(IdxTempDepart,1)
          StartTempDepart=TempDepart
          StopTempDepart=StartTempDepart+BoostDeltaTemp
          StartTimeBoostMode=time.time()
          RapCyc=100.0
          Status=DomoJson.WritePercent(IdxCommande,RapCyc)
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          Status=SCS.LightCommand(16,ScsAdress)
          if Status!='CommandSucceeded':
            DbError.Write(__namescript__,Status)
          TempDepart=DomoJson.Read(IdxTempDepart,1)
          while TempDepart<StopTempDepart and (time.time()-StartTimeBoostMode)<BoostDelay:
            time.sleep(Periode)
            TempDepart=DomoJson.Read(IdxTempDepart,1)
          RapCyc=0.0
          Status=DomoJson.WritePercent(IdxCommande,RapCyc)
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          Status=SCS.LightCommand(0,ScsAdress)
          if Status!='CommandSucceeded':
            DbError.Write(__namescript__,Status)
          SetPointInitTempDepart=DomoJson.Read(IdxTempDepart,1)
          SetPointTempDepart=SetPointInitTempDepart
          time.sleep(SlowDownDelay)
          BoostMode=False
          Integral1=0.0
        else:
          Status=DomoJson.WriteTemp(IdxSetPointTempDepart,0.0)
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz de la consigne de température de déaprt")
          Status=DomoJson.WritePercent(IdxCommande,0.0)
          if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
          time.sleep(Periode) #ajout suite à remarque de CBR alias Bruch05, merci à lui
      else:
        #----------------PID 1-------------------------
        if ErrNowTempAmb>ErrMaxTempAmb:
          # modification du 12/11/2016
          # Si l'erreur est supérieure à l'erreur max souhaitée on reste à 100% on ne passe plus par le boostmode.
          PowerMax = True
        elif abs(ErrNowTempAmb)>Bm1:
          if PowerMax=True:
            PowerMax=False #après un PowerMax on initialise la consigne et l'intérgale
            SetPointInitTempDepart=DomoJson.Read(IdxTempDepart,1)
            SetPointTempDepart=SetPointInitTempDepart
            Integral1=0.0
          if ErrNowTempAmb!=ErrPreviousTempAmb:
            T=DomoJson.Read(IdxTempAmb,0)
            try:
              Derivee1=(ErrNowTempAmb-ErrPreviousTempAmb)/(T-T0)
            except ZeroDivisionError:
              Derivee1=0.0
            ErrPreviousTempAmb=ErrNowTempAmb
            T0=T
          if ExitFromBm:
            SetPointInitTempDepart=SetPointTempDepart-Kp*ErrNowTempAmb
            Integral1=0.0
            ExitFromBm=False
          SetPointTempDepart=SetPointInitTempDepart+Kp*ErrNowTempAmb+Ki*(Integral1+ErrNowTempAmb)+Kd*Derivee1
          if SetPointTempDepart<LimitBasTempDepart:
            if ErrNowTempAmb>Bm1:
              SetPointTempDepart=LimitBasTempDepart
            else:
              SetPointTempDepart=0.0
              if not BoostDemand:
                BoostDemand=True
                StartTimeBoostDemand=time.time()
              else:
                if (time.time()-StartTimeBoostDemand)>DelayTimeBoostDemand:
                  BoostMode=True
                  BoostDemand=False
          elif SetPointTempDepart>=SetPointStepTempDepart:
            if not StepTempDepart:
              StepTempDepart=True
              StartTimeStepTempDepart=time.time()
            else:
              if (time.time()-StartTimeStepTempDepart)>3600 and not ReleaseSetPointTempDepart:
                ReleaseSetPointTempDepart=True
            if not ReleaseSetpointTempDepart:
              SetPointTempDepart=SetPointStepTempDepart
            else:
              Integral1=Integral1+ErrNowTempAmb
          else:
            Integral1=Integral1+ErrNowTempAmb
            StepTempDepart=False
            ReleaseSetPointTempDepart=False
        else:
          if PowerMax=True:
            PowerMax=False#après un PowerMax on initialise la consigne et l'intérgale
            SetPointInitTempDepart=DomoJson.Read(IdxTempDepart,1)
            SetPointTempDepart=SetPointInitTempDepart
            Integral1=0.0
          ExitFromBm=True
          if ErrNowTempAmb!=ErrPreviousTempAmb:
            T=DomoJson.Read(IdxTempAmb,0)
            try:
              Derivee1=(ErrNowTempAmb-ErrPreviousTempAmb)/(T-T0)
            except ZeroDivisionError:
              Derivee1=0.0
            ErrPreviousTempAmb=ErrNowTempAmb
            T0=T
            SetPointTempDepart=SetPointTempDepart+Kd*Derivee1
        Status=DomoJson.WriteTemp(IdxSetPointTempDepart,round(SetPointTempDepart,1))
        if Status!='OK':
            DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz de la consigne de température de départ")
        #----------------------------------------------

        #----------------PID 2-------------------------
        TempDepart=DomoJson.Read(IdxTempDepart,1)
        ErrNowTempDepart=SetPointTempDepart-TempDepart
        if ErrNowTempDepart>ErrMaxTempDepart:
          PowerMax=True
        else:
          if PowerMax=True
            PowerMax=False#après un PowerMax on initialise la consigne et l'intérgale
            SetPointInitTempDepart=DomoJson.Read(IdxTempDepart,1)
            SetPointTempDepart=SetPointInitTempDepart
            Integral1=0.0
          if SetPointTempDepart==0.0:
            RapCyc=0.0
          else:
            if abs(ErrNowTempDepart)>Bm2:
              if ErrNowTempDepart>0:
                RapCyc=RapCyc0+K1*(ErrNowTempDepart-Bm2)+K2*(ErrNowTempDepart-ErrPreviousTempDepart)+K3*(ErrNowTempDepart+ErrPrevious0TempDepart-2*ErrPreviousTempDepart)
              else:
                RapCyc=RapCyc0+K1*(ErrNowTempDepart+Bm2)+K2*(ErrNowTempDepart-ErrPreviousTempDepart)+K3*(ErrNowTempDepart+ErrPrevious0TempDepart-2*ErrPreviousTempDepart)
              #Si en asservissement on veut accélérer alors on interdit de freiner si on va trop vite et inversement.
              if RapCyc<RapCyc0 and ErrNowTempAmb>Bm1:
                RapCyc=RapCyc0
              elif RapCyc>RapCyc0 and ErrNowTempAmb<-Bm1:
                RapCyc=RapCyc0
            else:
              RapCyc=RapCyc0
          if RapCyc>RapCycMax:
            RapCyc=RapCycMax
          elif RapCyc<0.0:
            RapCyc=0.0
          ErrPrevious0TempDepart=ErrPreviousTempDepart
          ErrPreviousTempDepart=ErrNowTempDepart
        #----------------------------------------------

        #------------Pilotage chaudière---------------
        if PowerMax:
          RapCyc=RapCycMax
        Status=DomoJson.WritePercent(IdxCommande,RapCyc)
        if Status!='OK':
          DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
        TOn=RapCyc*Periode/100
        TOff=Periode-TOn
        if TOn!=0.0:
          Status=SCS.LightCommand(11,ScsAdress)
          if Status!='CommandSucceeded':
            DbError.Write(__namescript__,Status)
          else:
            time.sleep(TOn)
        if TOff!=0.0:
          Status=SCS.LightCommand(0,ScsAdress)
          if Status!='CommandSucceeded':
            DbError.Write(__namescript__,Status)
          else:
            time.sleep(TOff)
        #----------------------------------------------

    else:
      #-------- Mise à zéro de la commande------------
      Status=DomoJson.WritePercent(IdxCommande,RapCyc)
      if Status!='OK':
        DbError.Write(__namescript__,"Erreur d'écriture dans Domoticz du rapport cyclique")
      #----------------------------------------------

      if not StandBy:
        #-------- Mise au repos du relais--------------
        Status=SCS.LightCommand(0,ScsAdress)
        if Status!='CommandSucceeded':
          DbError.Write(__namescript__,Status)
        #----------------------------------------------

        #------Paramètres PID1--------------
        ErrPreviousTempAmb=0.0
        T0=DomoJson.Read(IdxTempAmb,0)
        Integral1=0.0
        Derivee1=0.0
        SetPointTempDepart=0.0
        #----------------------------------

        #------Paramètres PID2--------------
        ErrPreviousTempDepart=0.0
        ErrPrevious0TempDepart=0.0
        #----------------------------------

        #------Paramètres Relance----------
        BoostMode=True
        #----------------------------------
        StandBy=True
      time.sleep(Periode)
  except Exception, e:
    try:
      DbError.Write(__namescript__,str(e))
    except Exception, e:
      pass
    time.sleep(Periode)



allezlelosc
Messages : 49
Enregistré le : 21 oct. 2015, 21:34

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par allezlelosc »

Bonjour à tous et plus particulièrement gplantro

en effet Gplantro m'a envoyé un MP mais je pense de ses questions et les réponses que je vais tenter d'apporter peuvent en intéresser d'autres.

On commence donc par le mail en question
Bonjour Allez le losc,

Je suis depuis qq temps tes explications sur le fonctionnement de la chaudière Frisquet.

J'ai le meme modèle dans la maison que j'ai acheté il y a un an, et j'aime bien comprendre comment fonctionnent les choses qui habitent sous mon toit
:)

L'hiver dernier, j'ai gardé le TA d'ambiance eco radio.
Cet hiver, comme sur mon ancienne maison, j'ai branché un TA qivivo qui est censé apprendre le fonctionnement de la chaudière. Ca marcahit du feu de dieu sur mon ancienne chaudière qui fonctionnait en tout ou rien.

J'ai bien remarqué qu'une fermetrure du contact sec n'engendre pas effectivement une mise en route du chauffage.

Il y a un truc que je ne comprends pas: Quand tu dis:

-->Pour vérifier cette hypothèse j'ai donc piloté la chaudière avec un
créneau de période 30 secondes

Comment as tu pensé çà faire un creneau de 30s?
Est ce que ca veut dire que ton contact sec ne cesse de claquer toutes les 15secondes? (fort desagréable :) )

En fait je cherche à comprendre comment tout fonctionne, je "pense" que mon TA ne fait pas de creneau, mais des demandes plus ou moins longues.
j'aimerai comprendre comment reagit la chaudière.

En lisant tes messages, je comprends:

Plus le contact est fermé (demande est longue), plus la chaudière va se lancer à haute température.
--> As tu reussi à comprendre au bout de combien de temps?

Je concois que j'ai plein de question, j'espère que tu as compris ce que je demandais :)

Merci d'avance pour ton temps!
Comment as tu pensé çà faire un creneau de 30s?
En fait la réflexion était plutôt une intuition : dans mon boulot il m'arrive fréquemment d'opter pour des régulations chrono proportionnelle ou PWM (notamment pour le pilotage de batteries électriques qui équipent des centrales de traitement d'air) Donc l'idée met venue naturellement mais comme je l'ai déjà dit j'ai eu de la chance quand même.
Concernant le relais qui colle toutes les 15s, ça dépend où se troue le relais. Pour ma part dans la buanderie donc pas de désagrément.
As tu reussi à comprendre au bout de combien de temps?
Non mais une fois lancé ça monte vite quand même


pour le reste je t'invite à lire la doc que j'ai postée le 11 novembre. On y apprend notamment qu'on ne pilote pas le brûleur directement mais la vanne 3(4) voies.

bon courage a+

Yanyan0
Messages : 3
Enregistré le : 01 déc. 2016, 11:43

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par Yanyan0 »

Bonjour à Tous,

Je me suis inscrit sur le forum parce que je cherche des personnes qui seraient capables de me fournir les requêtes HTTP nécessaires pour piloter ma chaudière FRISQUET Condensation Prestige Visio. J'aimerai l'intégrer à ma box domotique qui est une Fibaro.

Est-ce que quelqu'un peut me donner un coup de main là-dessus ?

Merci pour vos réponses.

Si je ne suis pas au bon endroit merci de me le faire savoir, je me désinscrirais dans la foulée. Je ne suis pas là pour vous poluer.

Merci

bruch05
Messages : 43
Enregistré le : 27 févr. 2016, 21:06

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par bruch05 »

Salut YanYan,

Comme tu peux le voir dans les posts de présentation de nos installations de AllezLeLosc et moi-même, le pilotage passe par un module interface relais qui est connecté sur l'entrée "contact sec" de la chaudière via un câble acheté une vingtaine d'euro sur un site de pièces détachées.

Ce contact peut être utilisé en tout ou rien pour piloter la chaudière.

Contact fermé : La chaudière augmente la consigne de température de départ
Contact ouvert : La chaudière réduit la consigne de température de départ

Le contact ne pilote pas directement le brûleur. C'est le calculateur de la chaudière qui gère en fonction d'un algorithme qui lui est propre.

En conclusion et pour répondre à ta question.

En théorie.

1°) Acquisition d'une interface disposant d'un mini serveur Web permettant de passer une URL de commande du contact
2°) Acquisition du câble (CF référence dans les posts) si la chaudière visio dispose de cette connexion. (On a des prestige de la génération précédente)
3°) Via le paramétrage de ta chaudière, arrêter le pilotage via la sonde Visio pour laisser la main au contact.

Tof
Modifié en dernier par bruch05 le 02 déc. 2016, 12:02, modifié 1 fois.

bruch05
Messages : 43
Enregistré le : 27 févr. 2016, 21:06

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par bruch05 »

Salut AllezLeLosc

Je rencontre deux cas de figure particulier et je voudrais ton avis.

1°) Quand on demande 60°c en consigne de départ, la température de départ n'arrive pas à monter jusqu'à cette valeur.
J'ai remis la régulation d'origine et la chaudière monte bien en température.

Ne serais pas le rapport cyclique qui ne monte pas suffisamment pour le coup ?
Si oui, ne faut-il pas augmenter K1, voire valoriser K2 et K3 ?

3°) Un soir, quand la consigne de départ était de 26°C, que la consigne perçu par la chaudière était de 20°C (un seul voyant orange) j'ai observé que lorsque le contact se fermait, la pompe se mettait en route, quand le contact s'ouvrait, la pompe s'arrêtait, cela 2 fois par 30 secondes. La régulation était dans la boucle PID.

Je suis repassé sur la régulation d'origine pour la nuit.

Je suis repassé sur la régulation Domoticz et je n'ai pas re-constaté le phénomène, les nuits étant froide et la chaudière tournant à 30°C départ minimum.

Je n'ai pas eu le temps de refaire des tests en simulant la consigne Départ à de faible valeur.
As tu le même comportement ?

Merci pour ta contribution.
Tof

allezlelosc
Messages : 49
Enregistré le : 21 oct. 2015, 21:34

Re: Régulation d'une chaudière Frisquet ECO radio System

Message par allezlelosc »

Salut Bruch05,

Pour les 60°C, il y a un palier dans la régulation Frisquet (le paramètre 'SetPointStepTempDepart' sert justement dans ce mode)
Quand la chaudière monte en température un palier autour des 60°C est appliquer pendant une heure avant d'augmenter de nouveau la température de départ. (le but est de limiter les chocs thermiques quand un simple thermostat est utilisé)

le bout de code qui prend en compte ce palier est

Code : Tout sélectionner

elif SetPointTempDepart>=SetPointStepTempDepart:
            if not StepTempDepart:
              StepTempDepart=True
              StartTimeStepTempDepart=time.time()
            else:
              if (time.time()-StartTimeStepTempDepart)>3600 and not ReleaseSetPointTempDepart:
                ReleaseSetPointTempDepart=True
            if not ReleaseSetpointTempDepart:
              SetPointTempDepart=SetPointStepTempDepart
            else:
              Integral1=Integral1+ErrNowTempAmb
après la valeur de 60°C est peut être plus ou moins précise, autour de quelle valeur de température de départ la chaudière s'est elle stabilisée ?

Pour les 26 °C, j'en ai déjà parlé dans les autres posts mais sans évoquer le on/off sur la pompe. Le paramètre 'LimitBas' sert à ça : si la consigne de départ passe en dessous on coupe la commande. Chez moi le paramètre est fixé à 25°C justement.

Répondre