1、PID原理
PID(Proportional-Integral-Derivative)控制 是一种经典的闭环反馈控制算法,广泛应用于工业控制、机器人、自动化系统等领域。其核心是通过误差的比例、积分和微分三个环节的组合,调整系统输出,使被控量(如温度、速度、位置等)快速、稳定地达到目标值。
PID 控制器结构
PID 控制器的输出由三部分组成:
PID 调节实例
温度控制系统
-
目标:将水温稳定在 50°C。
-
调节过程:
-
比例控制(P):增大 Kp 使水温快速接近目标,但可能出现超调。
-
加入积分(I):消除稳态误差(如恒定的 2°C 偏差)。
-
加入微分(D):抑制超调,平滑温度变化。
-
2、硬件结构
该硬件系统使用proteus软件进行搭建与仿真。如下图。
3、C语言程序部分
,本次编程使用模块化编程,项目树如下:
以下仅列举关键代码
a、PID.c
#define uchar unsigned char
#define uint unsigned int
float uk ,uk1 ,duk ;//声明目前总偏差值变量、之后偏差值总变量、偏差值总变量
typedef struct PID_Para
{
float Kp;//比例系数
float Ki;//积分增益
float Kd;//微分增益
int Err;//误差
int Err_Last;//上一次误差
float Un;//输出
float Yn;//当前温度
float Rn;//期望值
float SumOfErr;//误差求和项
float KpVal;
float KiVal;
float KdVal;
}PID_Para;
PID_Para PIDRaise={2,40,5,0,0,0,0,0,0,0,0,0};
//PID算法函数//位置型
int PID_Calu(float SetPara,RealPara)
{
int PID_Out;
PIDRaise.Err = SetPara - RealPara;
PIDRaise.SumOfErr += PIDRaise.Err;
PIDRaise.KpVal = PIDRaise.Kp * PIDRaise.Err;
//PIDRaise.KiVal = PIDRaise.Ki * PIDRaise.SumOfErr;
PIDRaise.KdVal = PIDRaise.Kd * (PIDRaise.Err - PIDRaise.Err_Last);
PID_Out = PIDRaise.KpVal + PIDRaise.KiVal + PIDRaise.KdVal;
if(PIDRaise.Err >-5 && PIDRaise.Err <5)//只在温差小于5度时才开启积分项,积分限幅
{
PID_Out += PIDRaise.KiVal;
}
else
{
PIDRaise.SumOfErr = 0;
}
if(PID_Out >= 100)
{
PID_Out = 100;
}
else if(PID_Out <= 0)
{
PID_Out = 0;
}
PIDRaise.Err_Last = PIDRaise.Err;
return (int)PID_Out;
}
b、主函数
#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "Delay.h"
#include "Timer0.h"
#include "ADC0808.h"
#include "PID.h"
sbit DAC = P3^4;
sbit LED = P3^0;
//实际温度换算//ADC值,实际最大,实际最小,,电压最大,电压最小,比例系数(分压比例11)
float GetRealTemp(unsigned char ADC,RealMax,RealMin,VolMax,VolMin,Kpara);
unsigned char Counter1,Counter2; //计数值和比较值,用于输出PWM
unsigned char i;
unsigned char KeyNum;
unsigned char ADC_Data;
int Compare;
float RealTemp;
unsigned int SetTemp,UpLmtTemp,DownLmtTemp;
unsigned char IntPart,FloatPart;
void main()
{
LCD_Init();
Timer0_Init();
SetTemp = 50;//预设温度为60°
UpLmtTemp = 90;//温度上限设置为90°
DownLmtTemp = 10;//温度下限设置为10°
LCD_ShowString(1,1,"S:");
LCD_ShowString(2,1,"A:");
LCD_ShowNum(1,3,SetTemp*10,3);
while(1)
{
KeyNum = Key();
if(KeyNum)
{
LCD_ShowNum(2,1,KeyNum,1);
if(KeyNum == 1)
{
SetTemp += 10;
}
else if(KeyNum == 2)
{
SetTemp -= 10;
}
if(SetTemp>=100) SetTemp = 100;
if(SetTemp<=0) SetTemp = 0;
LCD_ShowNum(1,3,SetTemp,2);
}
ADC_Data = GetADC_Data();
RealTemp = GetRealTemp(ADC_Data,255,0,10,0,10); //计算实际温度
LCD_ShowNum(2,3,(uchar)RealTemp*10,3);
LCD_ShowNum(2,8,Compare,3);
}
}
//实际温度换算//ADC值,实际最大,实际最小,,电压最大,电压最小,比例系数(分压比例11)
float GetRealTemp(unsigned char ADC,DigMax,DigMin,VolMax,VolMin,Kpara)
{
float res = 0;
res = (float)(ADC) / (DigMax-DigMin)*(VolMax-VolMin)*Kpara;
return res;
}
/*定时器中断函数模板*///100us、、1ms
void Timer0_Routine() interrupt 1
{
//若呼吸灯闪烁,看是否定时器时钟设置为12T
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
Counter1++;
Counter2++;
Counter1%=100; //计数值变化范围限制在0~99,PWM周期10ms,100Hz
if(Counter1<Compare)
{
DAC = 0;
}
else
{
DAC = 1;
}
if(Counter2>=50)//5ms进行一次PID
{
Compare = PID_Calu(SetTemp*10,RealTemp*10)/10;
Counter2 = 0;
LED = ~LED;
}
}
单片机通过PWM实现加热器的控制。
运行实际效果:
需要更好地效果需要进一步调节PID的各项参数。