# HG changeset patch # User Michiel Broek # Date 1431884095 -7200 # Node ID 468ec0d96cceb737f6f787b7e38b0021c6a3f26a # Parent c92651a5496968dea0c7907b93c61134d9e470b0 Back to the old PID algorythm. Version 0.3.1. diff -r c92651a54969 -r 468ec0d96cce configure --- a/configure Sat May 16 17:39:30 2015 +0200 +++ b/configure Sun May 17 19:34:55 2015 +0200 @@ -2034,7 +2034,7 @@ PACKAGE="mbsePi-apps" -VERSION="0.3.0" +VERSION="0.3.1" COPYRIGHT="Copyright (C) 2014-2015 Michiel Broek, All Rights Reserved" CYEARS="2014-2015" diff -r c92651a54969 -r 468ec0d96cce configure.ac --- a/configure.ac Sat May 16 17:39:30 2015 +0200 +++ b/configure.ac Sun May 17 19:34:55 2015 +0200 @@ -8,7 +8,7 @@ dnl General settings dnl After changeing the version number, run autoconf! PACKAGE="mbsePi-apps" -VERSION="0.3.0" +VERSION="0.3.1" COPYRIGHT="Copyright (C) 2014-2015 Michiel Broek, All Rights Reserved" CYEARS="2014-2015" AC_SUBST(PACKAGE) diff -r c92651a54969 -r 468ec0d96cce thermferm/Makefile --- a/thermferm/Makefile Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/Makefile Sun May 17 19:34:55 2015 +0200 @@ -67,5 +67,5 @@ xutil.o: thermferm.h xutil.h server.o: rdconfig.h thermferm.h logger.h devices.h server.h lcd-buffer.h xutil.h simulator.o: thermferm.h simulator.h -rdconfig.o: rdconfig.h thermferm.h futil.h xutil.h +rdconfig.o: rdconfig.h thermferm.h pid.h futil.h xutil.h # End of generated dependencies diff -r c92651a54969 -r 468ec0d96cce thermferm/README --- a/thermferm/README Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/README Sun May 17 19:34:55 2015 +0200 @@ -136,7 +136,7 @@ 6. PID adjustments. -* Start with Kp, Kd and Ki set to 0. +* Start with pGain, dGain and iGain set to 0. * Increase Kp until small oscillation. * Increase Kd until a little damping. * Increase Ki after Kp and Kd are set until longterm convergence. diff -r c92651a54969 -r 468ec0d96cce thermferm/pid.c --- a/thermferm/pid.c Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/pid.c Sun May 17 19:34:55 2015 +0200 @@ -26,8 +26,8 @@ void InitPID(pid_var *pid, int type) { - pid->Err = pid->ErrLast = pid->ErrLastLast = 0.0; - pid->Input = pid->InputD = pid->OutP = pid->InputLast = pid->SetP = 0.0; + pid->Err = pid->ErrLast = pid->iState = 0.0; + pid->Input = pid->OutP = pid->SetP = 0.0; pid->pGain = pid->iGain = pid->dGain = 0.0; pid->idleRange = 0.4; pid->Mode = PID_MODE_NONE; @@ -41,39 +41,55 @@ { if (pid->Mode == PID_MODE_AUTO) { - pid->InputD = pid->Input + (pid->Input - pid->InputLast) * pid->dGain * PID_TIMES; - pid->InputLast = pid->Input; + double pTerm, dTerm, iTerm; + if (pid->Type == PID_TYPE_HEAT) - pid->Err = pid->InputD - pid->SetP; + pid->Err = pid->SetP - pid->Input; else - pid->Err = pid->SetP - pid->InputD; + pid->Err = pid->Input - pid->SetP; - pid->OutP = pid->OutP + pid->pGain * (pid->Err - pid->ErrLast + pid->iGain * pid->Err + pid->dGain * (pid->Err - pid->ErrLast * 2 + pid->ErrLastLast)); - pid->ErrLastLast = pid->ErrLast; + /* + * Calculate the integral state with appopriate limiting. + * Use ErrLastLast as iState + */ + pid->iState += pid->Err; + if (pid->iState > PID_WINDUP_GUARD) + pid->iState = PID_WINDUP_GUARD; + else if (pid->iState < -PID_WINDUP_GUARD) + pid->iState = -PID_WINDUP_GUARD; + + pTerm = pid->pGain * pid->Err; + iTerm = pid->iGain * pid->iState; + dTerm = pid->dGain * (pid->Err - pid->ErrLast); + + pid->OutP = pTerm + dTerm + iTerm; pid->ErrLast = pid->Err; - } if (pid->Mode == PID_MODE_BOO) { + } else if (pid->Mode == PID_MODE_BOO) { /* * Mode Bang On Off */ - pid->InputLast = pid->Input; + pid->ErrLast = pid->Err; + if (pid->Type == PID_TYPE_HEAT) - pid->ErrLastLast = pid->ErrLast = pid->Err = pid->SetP - pid->Input; + pid->Err = pid->SetP - pid->Input; else - pid->ErrLastLast = pid->ErrLast = pid->Err = pid->Input - pid->SetP; + pid->Err = pid->Input - pid->SetP; - if (pid->Err > 0.0) + if (pid->OutP && (pid->Err <= 0.0)) + pid->OutP = 0.0; + else if ((pid->OutP == 0.0) && (pid->Err > pid->idleRange)) pid->OutP = pid->iMax; - else - pid->OutP = 0.0; + + pid->iState = 0.0; } else { /* * While in manual mode, stay ready for bumpless switch to * auto. */ - pid->InputLast = pid->Input; - pid->ErrLastLast = pid->ErrLast = pid->Err; + pid->ErrLast = pid->Err = 0.0; + pid->OutP = pid->iState = 0.0; } if (pid->OutP > pid->iMax) diff -r c92651a54969 -r 468ec0d96cce thermferm/pid.h --- a/thermferm/pid.h Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/pid.h Sun May 17 19:34:55 2015 +0200 @@ -9,6 +9,7 @@ #define PID_TYPE_COOL 1 /* PID is used for cooling */ #define PID_TIMES 60 /* 60 calculations per minute */ +#define PID_WINDUP_GUARD 10.0 /* Error windup guard */ typedef struct _pid_var { @@ -19,11 +20,9 @@ double idleRange; /* Idle range */ double Input; /* Input value */ - double InputD; /* Process input plus derivative */ - double InputLast; /* Process input from last pass */ double Err; /* Error, diff between input and set point */ double ErrLast; /* Error from last pass */ - double ErrLastLast; /* Error from next last pass */ + double iState; /* Error from next last pass */ double SetP; /* Set point */ double OutP; /* Output of PID algorithm */ int Mode; /* Value is 'PID_AUTO' if loop is automatic */ diff -r c92651a54969 -r 468ec0d96cce thermferm/rdconfig.c --- a/thermferm/rdconfig.c Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/rdconfig.c Sun May 17 19:34:55 2015 +0200 @@ -22,6 +22,7 @@ #include "rdconfig.h" #include "thermferm.h" +#include "pid.h" #include "futil.h" #include "xutil.h" @@ -516,14 +517,6 @@ syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUTD", "%.2f", tmp3->PID_cool->InputD)) < 0) { - syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); - return 1; - } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUTLAST", "%.2f", tmp3->PID_cool->InputLast)) < 0) { - syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); - return 1; - } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERR", "%.2f", tmp3->PID_cool->Err)) < 0) { syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; @@ -532,7 +525,7 @@ syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERRLASTLAST", "%.2f", tmp3->PID_cool->ErrLastLast)) < 0) { + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ISTATE", "%.2f", tmp3->PID_cool->iState)) < 0) { syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } @@ -578,14 +571,6 @@ syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUTD", "%.2f", tmp3->PID_heat->InputD)) < 0) { - syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); - return 1; - } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUTLAST", "%.2f", tmp3->PID_heat->InputLast)) < 0) { - syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); - return 1; - } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERR", "%.2f", tmp3->PID_heat->Err)) < 0) { syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; @@ -594,7 +579,7 @@ syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } - if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERRLASTLAST", "%.2f", tmp3->PID_heat->ErrLastLast)) < 0) { + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ISTATE", "%.2f", tmp3->PID_heat->iState)) < 0) { syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement"); return 1; } @@ -1247,18 +1232,6 @@ unit->PID_cool->Input = val; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUTD"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_cool->InputD = val; - xmlFree(key); - } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUTLAST"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_cool->InputLast = val; - xmlFree(key); - } if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERR"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &val) == 1) @@ -1271,10 +1244,10 @@ unit->PID_cool->ErrLast = val; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERRLASTLAST"))) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ISTATE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_cool->ErrLastLast = val; + unit->PID_cool->iState = val; xmlFree(key); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_SETP"))) { @@ -1335,18 +1308,6 @@ unit->PID_heat->Input = val; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUTD"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_heat->InputD = val; - xmlFree(key); - } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUTLAST"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_heat->InputLast = val; - xmlFree(key); - } if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERR"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &val) == 1) @@ -1359,10 +1320,10 @@ unit->PID_heat->ErrLast = val; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERRLASTLAST"))) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ISTATE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &val) == 1) - unit->PID_heat->ErrLastLast = val; + unit->PID_heat->iState = val; xmlFree(key); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_SETP"))) { diff -r c92651a54969 -r 468ec0d96cce thermferm/simulator.c --- a/thermferm/simulator.c Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/simulator.c Sun May 17 19:34:55 2015 +0200 @@ -49,6 +49,13 @@ if (debug) fprintf(stdout, "Thread my_simulator_loop started\n"); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + /* + * Heater and cooler have the air temperature + */ + simulator->s_heat_temp = simulator->s_cool_temp = simulator->room_temperature; + } + for (;;) { for (simulator = Config.simulators; simulator; simulator = simulator->next) { if (my_shutdown) @@ -81,7 +88,11 @@ * Finally, calculate the new air and plate temperature. */ if (SIMheating) { - simulator->air_temperature += 0.01; + if (simulator->s_heat_temp < simulator->heater_temp) + simulator->s_heat_temp += 0.05; + } else { + if (simulator->s_heat_temp > simulator->room_temperature) + simulator->s_heat_temp -= 0.05; } /* @@ -90,9 +101,16 @@ * Finsally, calculate the new air and plate temperature. */ if (SIMcooling) { - simulator->air_temperature -= 0.01; + if (simulator->s_cool_temp > simulator->cooler_temp) + simulator->s_cool_temp -= 0.05; + } else { + if (simulator->s_cool_temp < simulator->room_temperature) + simulator->s_cool_temp += 0.05; } + simulator->air_temperature += ((simulator->s_heat_temp - simulator->air_temperature) / 50.0); + simulator->air_temperature -= ((simulator->air_temperature - simulator->s_cool_temp) / 50.0); + /* * Calculate the extra beer temperatur rise to simulate the heat produced by the * fermentation process. Peak about one day after start and slowly decrease after @@ -107,6 +125,12 @@ /* * Calculate final temperature of the beer and the air. */ + // Cheap trick, just follow slowly the air temp. + simulator->beer_temperature += ((simulator->air_temperature - simulator->beer_temperature) / 200.0); + + syslog(LOG_NOTICE, "air=%.3f beer=%.3f heater=%.3f cooler=%.3f", simulator->air_temperature, simulator->beer_temperature, + simulator->s_heat_temp, simulator->s_cool_temp); + if (debug) fprintf(stdout, "sqm_room_air=%f air=%f air_heat_transfer=%f air_change=%f sqm_beer_air=%f beer=%f beer_heat_transfer=%f\n", sqm_room_air, simulator->air_temperature, air_heat_transfer, air_change, diff -r c92651a54969 -r 468ec0d96cce thermferm/thermferm.c --- a/thermferm/thermferm.c Sat May 16 17:39:30 2015 +0200 +++ b/thermferm/thermferm.c Sun May 17 19:34:55 2015 +0200 @@ -1403,22 +1403,22 @@ UpdatePID(unit->PID_heat); if (debug) - fprintf(stdout, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n", - unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP); + fprintf(stdout, "Heat: sp=%.2f Input=%.2f iState=%.2f Err=%.2f Out=%.2f\n", + unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP); if (((unit->PID_heat->OutP >= 1) && unit->heater_address) || (seconds == 60) || unit->heater_state) { - syslog(LOG_NOTICE, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f", - unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP); + syslog(LOG_NOTICE, "Heat: sp=%.2f Input=%.2f iState=%.2f Err=%.2f Out=%.2f", + unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP); } } if (unit->cooler_address) { UpdatePID(unit->PID_cool); if (debug) - fprintf(stdout, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n", - unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP); + fprintf(stdout, "Cool: sp=%.2f Input=%.2f iState=%.2f Err=%.2f Out=%.2f\n", + unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP); if (((unit->PID_cool->OutP >= 1) && unit->cooler_address) || (seconds == 60) || unit->cooler_state) { - syslog(LOG_NOTICE, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f", - unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP); + syslog(LOG_NOTICE, "Cool: sp=%.2f Input=%.2f iState=%.2f Err=%.2f Out=%.2f", + unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP); } }