找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2414|回复: 1
收起左侧

C语言PID控温讲解及源码

[复制链接]
ID:1037617 发表于 2022-6-30 08:41 | 显示全部楼层 |阅读模式
参考资料:http://www.51hei.com/bbs/dpj-181096-1.html

/*
功能:用软件模拟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();
————————————————
回复

使用道具 举报

ID:93341 发表于 2022-9-28 22:17 | 显示全部楼层
还有呢,能补全吗。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表