当前位置: 首页 > article >正文

PID 温控设计(基于 STC51)

PID 温控设计(基于 STC51)

一、需求分析

开关型控制存在的问题:加热的过程是全功率加热,三极管发热量大,温度控制振荡幅度大,控制精度较低。而通过采用PID方法能够更加精确地控制加热片处于目标温度,并在一个较小范围内浮动。

  • 精度要求:±0.2℃ 温控范围
  • 目标温度:45℃
  • 温度工作区间:20℃ - 70℃
  • 温度显示

二、技术设计

1、硬件选型

  • STC51 单片机
  • 最小外围温控系统

2、PID 控制算法

比例控制

如果能根据当前温度和设定温度的差异调节制冷的功率,比如选一个比例系数来控制制冷的功率,可解决半导体制冷量大的问题,接近温度设定值时,功率减小,降低温度的振荡,这种方法称为比例控制。

图1 比例控制示意图

上图列举了一个比例控制的过程,目标值为 1, 设置一个比例系数 Kp = 0.5, 每次的增量都不同,越接近目标值,增量越小,从图中可以看到大约 7 个周期后控制值就非常接近设定值,而且后面非常稳定。比例系数 Kp 的大小决定了达到设定值的快慢,从图中也可以看到,当设定值为 1时, Kp 的值不能设置的太大,所以 Kp 的选取要根据实际控制对象来定。若 Kp 选取的值较小,达到控制设定值的时间就会比较长,如下图所示:

图2 理想情况下不同比例系数的控制效果

比例控制在实际存在损耗时很难达到设定的目标值,如果通过提高目标设定值来达到实际控制值,在损耗值较小的情况下可能会超过实际的设定值,如下图所示,故该种方案在实际应用中是不太会采用的。

图3 有损耗情况下比例控制效果

PID 控制技术

PID 指的是 Proportion-Integral-Differential,即比例-积分-微分。用公式可表示为任一时刻的控制量要综合考虑比例因子,累计误差的积分,以及该时刻的微分变化量。

u ( t ) = K p [ e ( t ) + 1 T I ∫ 0 t e ( t ) d t + T D d e ( t ) d t ] u(t) = K_p [e(t) + \frac{1}{T_I} \int_0^{t} {e(t)}dt + T_D \frac{de(t)}{dt}] u(t)=Kp[e(t)+TI10te(t)dt+TDdtde(t)]

从公式可以看出,控制值包含三项,第一项为 比例项 K p K_p Kp, 前面分析过比例控制存在的不足是有外界影响时控制量会小于或大于设定值,在控制中存在不确定性。通过计算第二项 累计误差 1 T I ∫ 0 t e ( t ) d t \frac{1}{T_I} \int_0^{t} {e(t)}dt TI10te(t)dt 和第三项 当前变化率 T D d e ( t ) d t T_D \frac{de(t)}{dt} TDdtde(t) 来进行修正,每时每刻都在不断修正控制值,这样就可以避免外界干扰的影响,从而达到控制值和设定值尽可能的接近。

PID 控制中的三项,有时也要根据实际的控制对象进行修改,如累计误差时刻存在的系统,控制时就不能计算累计误差,否则系统很难达到稳定,此时控制就变成了 PD 控制。比例控制,PD 控制,PID 控制是控制中常用的几种方法。

PID 实现方法

实际编程时,需将积分、微分转换成累加和差分计算,如在温度控制中,用 PWM 技术控制时要根据每个时刻的设定值和实际值的误差值 (Error)、所有误差值的求和值 (Integral) 以及每个时刻误差的变化梯度 (ErrorError_last),利用 Kp, Ki, Kd 三个系数计算出此时刻 PWM 的占空比的系数 PWM_t,从而控制 PWM 的占空比,达到调节等效电流的目的,也即控制制冷的速度。

PWM_t = Kp * Error + Ki * Integral + Kd * (Error - Error_last);

Kp, Ki, Kd 三个系数要根据实际控制的变化情况不断修正,比如制冷速度太慢,可以增加 Kp, KiKd 系数对制冷速度的影响不大,但对最终控制精度有影响,可以根据控制的结果,不断优化 KiKd 的值。

虽然 PID 控制技术是个广泛应用的技术,但具体到某个控制对象,其 Kp, Ki, Kd 系数都是要在不断测试中完善,这个过程是一个相对花费时间的过程,一旦三个系数优化好了,则控制就能得心应手。当然这些系数的优化也不是非要靠人工去逐步调节,当前已经有很多 PID 自整定技术,可以让系统自动搜索优化的系数,当然这个过程也是要花费很多时间的。比如一个温控系统,如果温度范围大,则自整定的时间就比较长,可能要几十分钟才能完成。

三、编程实现

1、具体代码

编写 PID 头文件 PID.h

//定义两个变量,float Temp_point,(温度设置值)和 温度测量值 float Temp_aver
//在main函数 中调用 PID_Init();
//占空比的变化数值 t = PID_realize(Temp_point, Temp_aver);
//可先观察 t 大概是多少,然后设置周期 T
//温控精度可以通过修改 Kp, Ki, Kd 参数来优化,需不断输出数据,画图或模型分析来观察调节的结果
struct PID {
	float Set_point;  //目标值
	float Actual_point;  //实际值
	float Error;  //当前误差
	float Error_last;  //上次误差
	float Kp, Ki, Kd;
	float integral;  //误差积分值
	float Differential;  //误差微分值
	float Voltage;
};
struct PID pid;
void PID_Init(float Temp_point, float Temp_aver)
{
	pid.Set_point = Temp_point;
	pid.Actual_point = Temp_aver;
	pid.Error = 0;
	pid.Error_last = 0;
	pid.Kp = 60;
	pid.Ki = 0.01;
	pid.Kd = 100;

	pid.integral = 0;
	pid.Differential = 0;
	pid.Voltage = 0;
}

char PID_realize(float Temp_point, float Temp_aver)
{
	char pidresult;
	pid.Set_point = Temp_point; 
	pid.Actual_point = Temp_aver; 
	pid.Error = pid.Set_point-pid.Actual_point; 
	
	pid.integral = pid.integral+pid.Error; 
	pid.Differential = pid.Error-pid.Error_last;  
	pid.Voltage = pid.Kp*pid.Error + pid.Ki * pid.integral + pid.Kd * (pid.Error - pid.Error_last);//+pid.Kout; 
	pid.Error_last = pid.Error;
  
	if(pid.Voltage > 100)
		pid.Voltage = 100;
	else if(pid.Voltage < 0)
		pid.Voltage = 0; 
	pidresult = pid.Voltage + 10;
	return pidresult;
}

编写主函数 main.c

#include <STC12.h>
#include <LCD1602.h>
#include <ADC.h>
#include <math.h>
#include <PID.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define Rp 10000
#define B 3435
#define Vcc 5
#define Temp_point 45

sbit heat = P1^0;
uint heat_t = 0;

void USART_Init()
{
	SCON = 0x50;
	TMOD = 0X20;
	TL1 = 0XFD;
	TH1 = 0XFD;
	TR1 = 1;
	ES = 1;
	TI = 1;
	EA = 1;
}

void UART_SendByte(uchar Byte){
	SBUF = Byte;
	while(TI == 0);
	TI = 0;
}

void Delay_ms(uint time)
{
	uint i,j;
	for(i = 0;i < time;i ++)
		for(j = 0;j < 930;j ++);
}

void PWM(char a) {
	heat = 0;
	Delay_ms(a);
	heat = 1;
	Delay_ms(111 - a);
}

void main()
{
	float Temp_aver = 20;
	float res0 = 0;
	float T1 = 0;
	float Rt = 0;
	float T2 = 273.15 + 29.7377;
	uint i = 0;
	uchar str[6] = "";
	heat = 1;
	USART_Init();
	LCD_1602_Init();
	ADC_Init(ADC_PORT1);
	PID_Init(Temp_point,Temp_aver);
	Write_1602_String("TEMP CTRL ZBY",0xc0 + 0x02);
	while(1)
	{
		res0 = GetADCResult(ADC_CH1);
		Rt = res0 / (Vcc - res0) * Rp;
		T1 = 1 / (log(Rt / Rp) / B + 1 / T2);
		Temp_aver = T1 - 268.15 + 0.5;
		heat_t = PID_realize(Temp_point, Temp_aver);
		PWM(heat_t);
		Write_1602_String("T=",0x80);
		Write_1602_Data(0x30 + (uint)(Temp_aver/10)%10);
		Write_1602_Data(0x30 + (uint)Temp_aver%10);
		Write_1602_Data('.');
		Write_1602_Data(0x30 + (uint)(Temp_aver*10)%10);
		Write_1602_Data('C');
		str[0] = 0x30 + (uint)(Temp_aver/10)%10;
		str[1] = 0x30 + (uint)Temp_aver%10;
		str[2] = '.';
		str[3] = 0x30 + (uint)(Temp_aver*10)%10;
		str[4] = '\t';
		str[5] = '\n';
		for (i = 0; i <= 5; i++) {
			UART_SendByte(str[i]);
		}
	}	
}

2、代码设计解析

在代码实现层面,整个系统的核心在于温度采集、PID运算与PWM输出的协同工作。让我们先看温度采集部分——通过ADC模块读取热敏电阻的电压值后,采用经典的 Steinhart-Hart 方程进行温度换算。这里有个细节需要注意:公式中的 T2 参数对应的是传感器在特定温度下的基准阻值,实际调试时需要根据热敏电阻的规格书精确校准。在代码中可以看到,最终对温度值做了 0.5℃ 的偏移补偿,这是为了消除传感器安装位置带来的测量误差。

PID控制模块的实现展现了典型的离散化处理思路。结构体PID中保存着关键的状态变量:Error记录当前温度偏差,integral累积历史误差,Error_last则用于计算微分项。算法核心在于这个公式:

pid.Voltage = Kp * Error + Ki * integral + Kd * (Error - Error_last)

这里的系数设置非常考验调试经验——初始参数Kp=60, Ki=0.01, Kd=100看似夸张的比例系数,实际上与PWM周期设计密切相关。当PWM周期设定为11ms时,占空比调节步长需要足够明显才能产生有效的控制作用。

PWM生成函数的设计体现了实用主义风格:通过简单的延时循环产生占空比信号。代码中heat_t参数经过+10的偏移处理,为补偿三极管导通电压带来的死区效应。

在参数整定过程中,一个有趣的矛盾现象值得注意:当增大比例系数Kp时,系统响应速度加快,但在接近目标温度时会产生明显的振荡;而增加微分系数Kd能有效抑制超调,却会延长升温时间。调试时需要在这两者间寻找平衡点。实践中发现,在温度上升阶段暂时关闭积分项(即采用PD控制),待进入±1℃范围后再启用完整PID控制,可以显著改善动态性能。

3. 系统架构解析

系统采用模块化设计,包含以下核心模块:

  • 温度采集模块:通过ADC读取热敏电阻电压值,基于Steinhart-Hart方程进行温度转换:
    Rt = res0 / (Vcc - res0) * Rp;  //计算热敏电阻阻值
    T1 = 1 / (log(Rt / Rp) / B + 1 / T2); //Steinhart-Hart方程
    Temp_aver = T1 - 268.15 + 0.5;        //转换为摄氏度并校准
    
  • PID控制模块:通过PID_realize函数实时计算PWM占空比:
    pid.Voltage = pid.Kp*pid.Error + pid.Ki * pid.integral + pid.Kd * pid.Differential;
    
  • 执行机构模块:使用PWM信号驱动加热片,PWM(heat_t)函数通过占空比调节加热功率
  • 人机交互模块:LCD1602显示实时温度,串口输出调试数据

4. 关键代码说明

  • PID初始化:设置初始参数并清零历史误差
    void PID_Init(float Temp_point, float Temp_aver) {
      pid.Kp = 60;   //比例系数初始值
      pid.Ki = 0.01; //积分系数初始值 
      pid.Kd = 100;  //微分系数初始值
      //...其他初始化...
    }
    
  • 抗积分饱和处理:通过输出限幅避免控制量溢出
    if(pid.Voltage > 100) pid.Voltage = 100;
    else if(pid.Voltage < 0) pid.Voltage = 0;
    
  • PWM生成策略:采用11ms周期(约90Hz),通过延时实现占空比调节
    void PWM(char a) {
      heat = 0; Delay_ms(a);   //加热时间
      heat = 1; Delay_ms(111 - a); //关闭时间
    }
    

5. 参数整定建议

  1. 整定步骤
    • 先设Ki=0, Kd=0,逐步增大Kp至系统出现等幅振荡
    • 取振荡周期Tu,按Ziegler-Nichols法计算参数:
      Kp=0.6*Ku, Ki=2Kp/Tu, Kd=Kp*Tu/8
      
  2. 现场调试技巧
    • 出现超调时增大Kd或减小Kp
    • 稳态误差大时适当增大Ki
    • 环境扰动大时增强微分项

6. 优化方向

  • 增加积分分离机制:当误差较大时暂停积分项
  • 实现变参数PID:针对不同温度区间采用不同参数
  • 添加数字滤波:对ADC采样值进行滑动平均滤波
  • 引入前馈补偿:针对环境温度变化进行预调节

四、实践结果

将程序烧录进 STC51 单片机中,程序通过PID控制算法,将温度从室温加热至目标温度45℃,并以0.2℃的精度稳定在目标温度附近,并以每秒10次的采样频率将温度数据串口传输至个人PC电脑。温度时间关系如下图所示:

PID温控系统温度时间曲线图

PID温控系统温度时间曲线局部图

可以观察到,最后温度稳定在 45±0.2℃ 范围内。

实践效果如下图所示:

实践效果图

五、总结与思考

经过实际测试,这套基于STC51的PID温控系统在实验室环境下达到了±0.2℃的控制精度,满足设计目标,相比原始的开关控制已是质的飞跃。有趣的是,当将加热片更换为更大功率的型号时,原有PID参数突然失效——这暴露出控制算法对执行机构特性的强依赖性。

这套系统还有多个优化方向值得探索。比如改用移相PWM技术来降低高频干扰,或者引入模糊控制算法自动调节PID参数。最近尝试将温度数据通过串口导入MATLAB进行离线分析,发现加热过程存在明显的非线性特征——在60℃附近会出现热传导模式的突变。这可能需要设计分段式PID控制器,在不同温区采用差异化的控制策略。


http://www.kler.cn/a/527894.html

相关文章:

  • 计算机毕业设计Python+CNN卷积神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习
  • 对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角
  • 具有HiLo注意力的快速视觉Transformer
  • deepseek 潜在变量Z的计算;变分自编码器(VAE); 高斯混合模型(GMM)
  • 联想拯救者R720笔记本外接显示屏方法,显示屏是2K屏27英寸
  • 表格结构标签
  • 网络工程师 (9)文件管理
  • JavaScript闭包深入剖析:性能剖析与优化技巧
  • Windows系统本地部署deepseek 更改目录
  • 【C++语言】卡码网语言基础课系列----6. 数组的倒序与隔位输出
  • JMeter中常见的四种参数化实现方式是什么?_file test_params
  • 将markdown文件转为word文件
  • 【ArcMap零基础训练营】03 常用数据网站的数据下载及处理
  • 【懒删除堆】力扣3092. 最高频率的 ID
  • doris:高并发导入优化(Group Commit)
  • Oracle Primavera P6自动进行进度计算
  • 【算法设计与分析】实验3:动态规划—最长公共子序列
  • 电路研究9.2.6——合宙Air780EP中HTTP——HTTP GET 相关命令使用方法研究
  • 如何在C语言项目中优雅地使用结构体
  • VirtualBox:跨磁盘导入已存的vdi磁盘文件顺便测试冷迁移
  • MySQL 导入数据
  • DeepSeek 遭 DDoS 攻击背后:DDoS 攻击的 “千层套路” 与安全防御 “金钟罩”
  • Win11 Windows 禁用右键折叠菜单
  • 大数据挖掘--两个角度理解相似度计算理论
  • 文献阅读 250131-Global Carbon Budget 2023(1)
  • 如何成为一名 Python 全栈工程师攻略