Back to the old PID algorythm. Version 0.3.1.

Sun, 17 May 2015 19:34:55 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 17 May 2015 19:34:55 +0200
changeset 363
468ec0d96cce
parent 362
c92651a54969
child 364
2f9bbbcd2407

Back to the old PID algorythm. Version 0.3.1.

configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
thermferm/Makefile file | annotate | diff | comparison | revisions
thermferm/README file | annotate | diff | comparison | revisions
thermferm/pid.c file | annotate | diff | comparison | revisions
thermferm/pid.h file | annotate | diff | comparison | revisions
thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/simulator.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
--- 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"
 
--- 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)
--- 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
--- 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.
--- 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)
--- 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	*/
--- 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"))) {
--- 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,
--- 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);
 		    	}
 		    }
 

mercurial