硬件:
1。用可控硅控制4.5KW电炉 用K型热电偶采集温度,采用cs1242做温度转换,可以到正负一度的精度
实验目标:
在300度到1000度内可对任意设定的温度恒温,精度先做到+/-1度吧
基本的控制实现方法:
因为是对加热的炉子温度进行控制,属于滞后效应系统,所以采样周期先定为2秒(这里指的是PID计算的周期,注意我的温度采样是实时的),所以CPU外部中断次数为15次/S,对应的功率计算结果输出为0~255,就是说把这2秒钟划分为255等份,根据计算的结果来决定在这2秒钟内应该加热多少等份
下面是控制曲线输出的表格 0.1度为单位
源码下载: https://whycan.cn/files/members/338/fuzzy.rar
最近编辑记录 演技担当黄晓明 (2018-05-08 21:18:42)
离线
/******************************************************************************
FILE: FUZZY.C
POPURSE:
WRITER: Xukaiming
DATE: 2007.03.08
******************************************************************************/
#include "main.h"
#include "task.h"
#include "fuzzy.h"
#include "ied_ctrl.h"
#include "math.h"
#include "x5043.h"
#include "manctrl.h"
#define ERRORCNT 9 //误差记录
#define DERRORCNT 10 //误差变化率记录
//#define DOOROPENSPEED -15L //开门时的升温速度,如果低于此温度,认为电阻丝的热量完全散发出来或者开门升温
xdata long DOOROPENSPEED = -15L;
#define MAXNEGINTERG (-200L*KI/Ki)
extern code unsigned int K_Temp_Tab[];
typedef struct AI_CONTROL
{
long gDest; //目标温度的AD值
long CTEMP; //开始控制温度
long Error[ERRORCNT]; //偏差
long dErr[DERRORCNT] ; //偏差变化率
long ECSUM; //10S内的偏差变化率之和
long SumErrLimit ; //加热初值
long PreviewOut;
long HFDownTemp ;
long du;
long DoorOpenValue; //保存炉子门状态
char LowSpeedCnt;
unsigned int iDestTemp; //目标温度值
unsigned int iStableCnt; //输出稳定次数
unsigned int iPreStabCnt; //保存的稳定次数
};
#define Kp 15 //输出变量u比例因子
#define Ki 1/10 //衰减系数;
#define KI 15
//Kp 变小的时候 ki要变大
xdata struct AI_CONTROL Ai_CTRL;
void TaskFuzzy(void)
{
char cnt;
_nop_();
if(IED.lADTemp>MAX_TEMP) //超温保护
{
SetPWM(PWM_PITCED);
return ;
}
//求偏差
for(cnt=ERRORCNT-1;cnt>0;cnt--)
{
Ai_CTRL.Error[cnt] = Ai_CTRL.Error[cnt-1];
} //保存上次偏差值
Ai_CTRL.Error[0] = (long)(((Ai_CTRL.gDest-IED.lADTemp)/KC));
for(cnt=DERRORCNT-1;cnt>0;cnt--)
{
Ai_CTRL.dErr[cnt] = Ai_CTRL.dErr[cnt-1];
} //保存上次偏差率值
Ai_CTRL.dErr[0] = Ai_CTRL.Error[0]-Ai_CTRL.Error[1]; //计算偏差变化率
Ai_CTRL.ECSUM = Ai_CTRL.dErr[0] + Ai_CTRL.dErr[1] + Ai_CTRL.dErr[2] + Ai_CTRL.dErr[3] + Ai_CTRL.dErr[4]; //10s的温升速率 =iol
if(Ai_CTRL.Error[0]<Ai_CTRL.CTEMP)
{
if(((Ai_CTRL.Error[0]*Ai_CTRL.dErr[0])>0) || ((Ai_CTRL.Error[0]==0) &&( Ai_CTRL.dErr[0]!=0))) //误差变大的情况
{
Ai_CTRL.SumErrLimit +=Ai_CTRL.Error[0]; //误差变大的时候,求积分
if(Ai_CTRL.SumErrLimit<MAXNEGINTERG) //限幅,如果超过负的最大值, 保持为负的最大值
Ai_CTRL.SumErrLimit=MAXNEGINTERG;
////////////////////////////////////////////////////////////
Ai_CTRL.du = Ai_CTRL.Error[0]*Kp+Ai_CTRL.SumErrLimit*Ki/KI*Kp; //增益抑制模式 //
}
else //开环保持模式
{
if(((Ai_CTRL.Error[0]*Ai_CTRL.dErr[0])<0)||(Ai_CTRL.dErr[0]==0)) //误差变小的情况
{
Ai_CTRL.du = Ai_CTRL.SumErrLimit*Ki/KI*Kp;
}
else
{
Ai_CTRL.du = Ai_CTRL.PreviewOut;
}
}
}
else
Ai_CTRL.du=PWM_PERIOD; //升温
if(Ai_CTRL.du<0) Ai_CTRL.du=0;
if(Ai_CTRL.du>PWM_PERIOD) Ai_CTRL.du=PWM_PERIOD;
#ifdef _DEBUG
printf("%ld,",Ai_CTRL.CTEMP);
printf("%ld,%ld,",Ai_CTRL.Error[0],Ai_CTRL.dErr[0] );
printf("%ld,%ld,%d\n",Ai_CTRL.ECSUM,Ai_CTRL.SumErrLimit,(int)Ai_CTRL.du);
#endif
SetPWM((unsigned char)Ai_CTRL.du);
//保存上次输出
Ai_CTRL.PreviewOut=Ai_CTRL.du;
}
//functions prototype
/************************************************************************
仿人智能控制器//Humanoid Intelligent Controller
************************************************************************/
/*******************************************************************
模糊控制数据变量
********************************************************************/
void InitFuzzy(void)
{
Ai_CTRL.DoorOpenValue = 0 ;// 重新统计升温速度
Ai_CTRL.LowSpeedCnt=0;
SetPWM(PWM_PITCED);
}
/*********************************************************************
启动高温炉
**********************************************************************/
void CtrlStove(UCHAR cOpen, long lDefTemp, UCHAR cRate)
{
xdata StableTempTab psTmpTab;
//停止
SuspendTask(TASK_FUZZY);
InitFuzzy();
if(cOpen) //启动
{
Ai_CTRL.gDest = (((double)(0xFFFFFFUL<<2))/5000000UL)*K_Temp_Tab[lDefTemp]+20; //将 目标 温度值 转换 为 AD值
Ai_CTRL.CTEMP = (1010L-lDefTemp) ; //控温范围
ActiveTask(TASK_FUZZY, PWM_SCAN);
}
}
/**********************************************************************
设置PWM,PWM0控制高温炉
16MHz时,T=2*512/16=64uS,F=42.666KHz,计数周期=256,占空比=0%--99.61%
***********************************************************************/
sbit POUT = P1^3;
char gcPoutTimer = -1;
void StopPOut()
{
POUT = 1;
}
void SetPWM(UCHAR cPeriod)
{
/*
CMOD = 0x00; // Setup PCA timer
// Configure PCA0 Counter operating mode
CCAP0L=CCAP0H=cPeriod; // Set duty for TCM0
CCON =0x40;
CCAPM0=0x42; // Set TCM0 operationg mode
*/
long period;
StopTimer(&gcPoutTimer);
POUT = 1;
period = PWM_SCAN*cPeriod/255/10;
if(period>0)
{
POUT = 0;
gcPoutTimer = StartTimer(TIMER_MODE_ONCEROUTINE,period,StopPOut,0);
}
}
离线