PID算法应用于室内温度控制的C语言实现

我最近在学习PID算法,对此很感兴趣。因此与大伙分享下,有不足的地方欢迎指出,很是谢谢。c++

PID算法的基本内容本篇博客就不作阐述了,网上有不少资料。算法

文章的主题是用C语言实现PID算法。为了更好的理解,我采用软件模拟室内温度控制的方式与大伙分享。windows

模拟原理很简单,假定23摄氏度为人类温馨的环境。夏天室外温度为37度,咱们想要室内保持23左右。因此须要用到PID算法调节。假设室内温度每分钟上升0.1摄氏度,咱们拥有制冷剂,每释放一包可下降5摄氏度,有制热剂,同理每放出一包可升高五度(一切为理想状态,房间温度到处相同,每次释放温度控制包都能立刻起做用)。函数

实现起来也很简单,假定放出初始时为37摄氏度,则调用制冷剂函数,使房间温度降低,直到23度左右时中止。转而使用PID算法精调,使之更接近于23摄氏度。到这里假如看懂了,咱们能够发散思惟,当实际温度在23正负0.5度以内,咱们中止加热或者制冷,以节省资源,当温度超出此范围时再调用相应的函数。学习

具体请看代码,我写了详细的备注,变量名与函数名都是用单词的意思。测试

<textarea readonly=”readonly” name=”code” class=”c++”> 
/*
功能:用软件模拟PID控制温度

模型一:夏天要求室内的温度为23摄氏度左右,假定房间受室外高温影响,1分钟上升0.1摄氏度(上限为室外温度37摄氏度)。
		释放一包制冷剂可下降5摄氏度,一包制热剂,可上升五度,温度变化由传感器采集,可是我用软件模拟温度变化,
		假设一切处于理想状态。

*/

#include "SoftSimulation_PID_Header.h"

int main()
{
	init_PID();

	time_t timep;
	struct tm *tp;
	
	while (1)
	{
		if (pid.ActualTemperature < outdoorTemperature)
		{
			// 获取时间
			time(&timep);
			tp = gmtime(&timep);

			AcquireTemperature(tp->tm_hour,tp->tm_min,tp->tm_sec);
		}

		PID_controlTemperature();

		/* 当温度处于人体温馨的温度时,则不用制冷或加热 */
		while (fabs(pid.SetTemperature - pid.ActualTemperature) < ComfortTemperature)
		{
			// 获取时间
			time(&timep);
			tp = gmtime(&timep);
			
			// 获取计算机时间,10秒钟打印一次
			printf("%d/%d/%d %d/%d/%d\n",1900+tp->tm_year,(1+tp->tm_mon),tp->tm_mday,tp->tm_hour + 8, tp->tm_min,tp->tm_sec);
			printf("Display:设定温度为:%.2f\t室内温度为:%f\t\n\n", pid.SetTemperature, pid.ActualTemperature);			
				
			// 获取实时温度			
			AcquireTemperature(tp->tm_hour, tp->tm_min, tp->tm_sec);
			
			Sleep(500);			
		}
	}
}

/* Acquire indoor Temperature */
void AcquireTemperature(int hour,int minute,int second)
{
	static int lastTime[3] = { 0 };
	static float Minute = 0;
	if (!lastTimeFlag )
	{
		lastTime[0] = hour;
		lastTime[1] = minute;
		lastTime[2] = second;
		lastTimeFlag = 1;
	}
	else
	{
		if (pid.ActualTemperature <= outdoorTemperature)
		{
			Minute = (hour - lastTime[0]) * 60 + (minute - lastTime[1]) + float(second - lastTime[2]) / 60.0;
			pid.ActualTemperature = pid.ActualTemperature + Minute * IncrementTemperature;
				
			lastTime[0] = hour;
			lastTime[1] = minute;
			lastTime[2] = second;
			//printf("Test:设定温度为:%.2f\t室内温度为:%f\t\n\n", pid.SetTemperature, pid.ActualTemperature);
		}
		else
		{
			// 温度升高过快,只记录时间,程序会在PID_controlTemperature函数中处理
			lastTime[0] = hour;
			lastTime[1] = minute;
			lastTime[2] = second;
		}
		
	}
}

/* release the refrigeration */
void Refrigeration()
{
	pid.ActualTemperature = pid.ActualTemperature - 5;
}
/* release the heating */
void Heating()
{
	pid.ActualTemperature = pid.ActualTemperature + 5;
}

/* the PID arithmetic control the temperature of indoor */
void PID_controlTemperature()
{
	if (pid.SetTemperature >= pid.ActualTemperature + TemperatureErr)		
	{
		// 室内温度偏低,调用制热模块
		Heating();
	}
	// 室内温度处于23度左右,使用PID精调
	else if (fabs(pid.SetTemperature - pid.ActualTemperature) < TemperatureErr)
	{
		IncrementPID_realize();    // 增量式PID控制
		//PositionPID_realize();    // 位置式PID控制,二者只要一个就能够了,之因此都贴出来,为了试试两种方式有什么不一样
	}
	else if (pid.SetTemperature + TemperatureErr <= pid.ActualTemperature) 
	{
		// 室内温度偏高,调用制冷模块
		Refrigeration();
	}
	else
	{
		printf("区间考虑不周全\n");
	}

}

/* initialize PID */
void init_PID()
{
	pid.Kp = 0.2;
	pid.Ki = 0.015;
	pid.Kd = 0.2;
	pid.SetTemperature = 23.0;
	pid.ActualTemperature = outdoorTemperature;
	lastTimeFlag = 0;
	pid.ErrVal[0] = 0;
	pid.ErrVal[1] = 0;
	pid.ErrVal[2] = 0;
	pid.Integral = 0;

	printf("accomplish initialize PID \n");
}

/* 
功能:使用增量式PID调节 
公式:U(k)+Kp*[E(k)-E(k-1)]+Ki*E(k)+Kd*[E(k)-2E(k-1)+E(k-2)]    
备注:网上的公式Kp乘以的东西有两种,第一种是Kp*[E(k)-E(k-1)],第二种是Kp*([E(k)-E(k-1)]+Ki*E(k)+Kd*[E(k)-2E(k-1)+E(k-2)])
	  这两种我都测试过,都能实现功能,前者更快一些,后者慢,可是我我的喜欢第二种
*/
void IncrementPID_realize()
{
	pid.ErrVal[0] = pid.SetTemperature - pid.ActualTemperature;			// 计算设定值与实际值直接的偏差
	float Temp0 = pid.Kp * (pid.ErrVal[0] - pid.ErrVal[1]);
	//float Temp0 = (pid.ErrVal[0] - pid.ErrVal[1]);
	float Temp1 = pid.Ki * pid.ErrVal[0];
	float Temp2 = pid.Kd * (pid.ErrVal[0] - 2 * pid.ErrVal[1] + pid.ErrVal[2]);
	float Increment = Temp0 + Temp1 + Temp2;							// 经过公式计算增量
	// float Increment = pid.Kp * (Temp0 + Temp1 + Temp2);				// 经过公式计算增量
	pid.ActualTemperature += Increment;									// 计算实际温度
	pid.ErrVal[1] = pid.ErrVal[0];
	pid.ErrVal[2] = pid.ErrVal[1];
	//printf("Realize:设定温度为:%.2f\t室内温度为:%.2f\t\n\n", pid.SetTemperature, pid.ActualTemperature);
}

/*
功能:使用位置式PID调节
公式:Kp*(E(k)+Ki*∑E(j)+Kd*[E(k)-E(k-1)])   
备注:参考增量式的备注
*/
void PositionPID_realize()
{
	pid.ErrVal[0] = pid.SetTemperature - pid.ActualTemperature;			// 计算设定值与实际值直接的偏差
	pid.Integral += pid.ErrVal[0];
	pid.ActualTemperature = pid.Kp * pid.ErrVal[0] + pid.Ki * pid.Integral + pid.Kd * (pid.ErrVal[0] - pid.ErrVal[1]);
	pid.ErrVal[1] = pid.ErrVal[0];
}
</textarea>
头文件以下:
 
#include <stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<windows.h>

typedef struct PID_Value
{
	float ErrVal[3];			// 三次偏差值
	float Kp;					// 比例系数
	float Ki;					// integral 系数
	float Kd;					// derivative 系数
	float Integral;				// 积分
	float SetTemperature;
	float ActualTemperature;
}PID_ValueStr,*PID_ValueT;

PID_Value pid;

#define		outdoorTemperature		36.2
#define		TemperatureErr			3
#define		ComfortTemperature		0.5
#define		IncrementTemperature	1			// 每分钟房间上升的温度

int lastTimeFlag = 0;

void AcquireTemperature(int hour, int minute, int second);
void PID_controlTemperature();
void Refrigeration();
void init_PID();
void IncrementPID_realize();
void PositionPID_realize();

如需转载请标明出处,谢谢。ui