/****************************************************************************** FILE: FUZZY.C POPURSE: WRITER: DATE: 段吉飞修改于20250626 ******************************************************************************/ /* 20250626修改说明: 仅修改TaskFuzzy()函数, 假定TaskFuzzy()每秒被调用一次, 使用到的数据 Ai_CTRL.gDest 假定为当前目标温度,单位0.1℃ Ai_CTRL.cur_Temp 假定为当前温度,单位0.1℃ 输出 Ai_CTRL.du 范围0~100的加热输出功率,对应0%-100%加热功率 因未知原Ai_CTRL.du与实际输出功率映射关系,因此需做必要的映射输出,TaskFuzzy()末尾处 建议使用过零型固态继电器,使用周期1s的PWM输出,Ai_CTRL.du就是占空比 或者输出电压控制调压继电器,但调压器非线性且干扰大,不建议 但原TaskFuzzy()函数还修改了其他一些变量,比如DOOROPENSPEED Ai_CTRL.CTEMP等 未知是否有其他作用,因此请谨慎测试 增加一个结构体变量 DJF_PID djf_pid,用作pid控制 增加一个常数 POW800, 预估控温800℃时的实际平均功率 */ #include "STM32Lib\\stm32f10x.h" #include "main.h" #include "fuzzy.h" //#include //#include "temperature_table.h" #include "MENU.h" #include "old_fuzzy.h" StableTempTab tabTmp; //#define DOOROPENSPEED -15L //开门时的升温速度,如果低于此温度,认为电阻丝的热量完全散发出来或者开门升温 long DOOROPENSPEED = -15L; #define MAXNEGINTERG (-200L*KI/Ki) //extern const u16 k_table[]; #define Kp 15 //输出变量u比例因子 #define Ki 1/10 //衰减系数; #define KI 15 //Kp 变小的时候 ki要变大 extern AI_CONTROL Ai_CTRL; extern MFL_PARA MFL_PARA_A; extern u8 FLASH_DATA_TEMP[528]; //****Add by DuanJiFei ***************************************** typedef struct _DJF_PID { float his_tmpr[16]; //记录每秒一次的历史温度 float target_tmpr; //目标温度 float pid_err_sum; //pid控温误差积分项 float pid_err; //pid控温本次的误差 uint32_t P_duty; //当前加热功率占空比(%) float JiangWen_5s; //预测5秒降温℃,考虑了降温的加速度 float kw_range; //pid控温范围 float c_term, p_term, i_term, d_term; } DJF_PID; DJF_PID djf_pid; #define POW800 30 //假设控温800时平均功率为30% //************************************************************** void TaskFuzzy(void) { //记录历史温度 int i; float p; //DYM for ( i = 15; i; i--) //for (uint32_t i=15; i; i--) djf_pid.his_tmpr[i] = djf_pid.his_tmpr[i - 1]; djf_pid.his_tmpr[0] = Ai_CTRL.cur_Temp * 0.1; //记录当前温度 djf_pid.JiangWen_5s = djf_pid.his_tmpr[5] * 3 - djf_pid.his_tmpr[0] * 2 - djf_pid.his_tmpr[10]; djf_pid.target_tmpr = Ai_CTRL.gDest * 0.1; //目标温度 djf_pid.pid_err = djf_pid.target_tmpr - djf_pid.his_tmpr[0]; #define RANG_0 120.0f djf_pid.kw_range = RANG_0 - djf_pid.target_tmpr * (RANG_0 / 1200.0f); //dym if (djf_pid.target_tmpr <= 30) //目标温度不到30则关闭加热 { djf_pid.P_duty = 0; } else //在pid控制范围内则采用pid控制 { //计算误差积分 djf_pid.pid_err_sum *= 0.996f;//积分项衰减 if (djf_pid.JiangWen_5s > -0.2f) //降温或升温很慢时 { if (djf_pid.pid_err_sum < 0) // { djf_pid.pid_err_sum *= 0.99f;//积分项衰减 } if (djf_pid.pid_err > 0) //在目标温度下方降温 { if (djf_pid.pid_err > 2) djf_pid.pid_err_sum += 2; else djf_pid.pid_err_sum += djf_pid.pid_err * 1; //积分项累加 } } if (djf_pid.JiangWen_5s < 0.2f) //升温或降温很慢时 { if (djf_pid.pid_err_sum > 0) { djf_pid.pid_err_sum *= 0.99f;//积分项衰减 } if (djf_pid.P_duty > 0 && djf_pid.pid_err < 0) //在目标温度上方升温 { if (djf_pid.pid_err < -2.0f) djf_pid.pid_err_sum += -2.0f; else djf_pid.pid_err_sum += djf_pid.pid_err * 1.0f; //积分项累加 } } djf_pid.c_term = (POW800 / (800.0f - 25.0f)) * (djf_pid.target_tmpr - 25.0f); //计算加热功率常数项,25算是环境温度 djf_pid.p_term = djf_pid.pid_err * (80.0f / djf_pid.kw_range); //计算加热功率比例项 djf_pid.i_term = djf_pid.pid_err_sum * (POW800 * (1.0f / 800.0f)); //计算加热功率积分项 djf_pid.d_term = djf_pid.JiangWen_5s * 20.0f; //计算加热功率微分项 if (djf_pid.i_term > 50.0f) djf_pid.i_term = 50.0f; else if (djf_pid.i_term < -50.0f) djf_pid.i_term = -50.0f; p = djf_pid.c_term + djf_pid.p_term + djf_pid.i_term + djf_pid.d_term; //DYM //根据情况适当调整功率 //挥发分 /*if (在做挥发分试验) { ... }*/ if (djf_pid.pid_err > 0) p *= 1.1f; //未到恒温点,放大加热功率 else if (djf_pid.pid_err < -0.1f) //恒温点以上,加热功率减小或关闭 { if (djf_pid.pid_err < -2 || djf_pid.his_tmpr[0] > djf_pid.his_tmpr[2]) p = 0; //高出2度或在还升温 else p *= 0.7f; } if (p > 100) p = 100; else if (p < 0) p = 0; djf_pid.P_duty = p; } //这里建立djf_pid.P_duty和Ai_CTRL.du的映射关系 Ai_CTRL.du = djf_pid.P_duty; //根据实际情况修改 ************** } //functions prototype /************************************************************************ 仿人智能控制器//Humanoid Intelligent Controller ************************************************************************/ /******************************************************************* 模糊控制数据变量 ********************************************************************/ void InitFuzzy(void) { Ai_CTRL.DoorOpenValue = 0 ;// 重新统计升温速度 Ai_CTRL.LowSpeedCnt = 0; Ai_CTRL.du = 0; } /********************************************************************* 启动高温炉 **********************************************************************/ void CtrlStove(u8 cOpen, long lDefTemp, u8 cRate) { StableTempTab psTmpTab; u8 temp; //停止 InitFuzzy(); //Ai_CTRL.HFDownTemp=HUIFA_900_QIKONG*10; if(cOpen) //启动 { Ai_CTRL.gDest = lDefTemp * 10; //将 目标 温度值 转换 为 LoadStable(lDefTemp, &psTmpTab); //这里是否需要乘以10 ? if((psTmpTab.iDestTemp / 100) == (lDefTemp / 100)) //已经保存过积分表 { Ai_CTRL.SumErrLimit = psTmpTab.integral; //加载保存的积分数据 Ai_CTRL.iPreStabCnt = psTmpTab.iStableCnt; Ai_CTRL.iDestTemp = lDefTemp; } else //未保存. { Ai_CTRL.SumErrLimit = -(1010 - lDefTemp) / 10 * KI * 2; //扣掉(1010-lDefTemp)/10的脉宽 ; Ai_CTRL.iPreStabCnt = 10; Ai_CTRL.iDestTemp = lDefTemp; } // Ai_CTRL.iStableCnt = 0; Ai_CTRL.CTEMP = (1010 - lDefTemp) ; //XC20121108 调节控温范围 temp = readHFDownTemp(); if((temp == 0) || (temp == 0xFF)) //设置初始值 { //Ai_CTRL.HFDownTemp = 1000; //Ai_CTRL.HFDownTemp = 600; //Ai_CTRL.HFDownTemp=HUIFA_900_QIKONG*10; Ai_CTRL.HFDownTemp = 1000; saveHFDownTemp (Ai_CTRL.HFDownTemp / 10); } else Ai_CTRL.HFDownTemp = (long)temp * 10; //Ai_CTRL.HFDownTemp = ByteRead(EEOROM_HFTEMP_ADDR)*10; /*FlashPageRead (HFDownTemp_BASE,FLASH_DATA_TEMP); //读取Ai_CTRL.HFDownTemp,原始函数都是字节!!! Ai_CTRL.HFDownTemp=FLASH_DATA_TEMP[0]; if((Ai_CTRL.HFDownTemp==0)||(Ai_CTRL.HFDownTemp==0xFF)) //设置初始值 { Ai_CTRL.HFDownTemp = 1000; //ByteWrite(EEOROM_HFTEMP_ADDR,Ai_CTRL.HFDownTemp/10); FLASH_DATA_TEMP[0]=Ai_CTRL.HFDownTemp; FlashPageWrite (HFDownTemp_BASE,FLASH_DATA_TEMP); }*/ //控温范围 } else { Ai_CTRL.gDest = 0; Ai_CTRL.temp_out = 0; } } void SaveStable(StableTempTab *psTmpTab) { u16 n, temp; u8 eeprom_data_temp[32]; //M24C64为32个字节一个page temp = psTmpTab->iDestTemp / 100; if (temp > 199) temp = 199; temp += TEMP_TABLE_BASE; temp <<= 5; n = 0; eeprom_data_temp[n++] = psTmpTab->iStableCnt >> 8; eeprom_data_temp[n++] = psTmpTab->iStableCnt; eeprom_data_temp[n++] = psTmpTab->iDestTemp >> 8; eeprom_data_temp[n++] = psTmpTab->iDestTemp; eeprom_data_temp[n++] = psTmpTab->integral >> 24; eeprom_data_temp[n++] = psTmpTab->integral >> 16; eeprom_data_temp[n++] = psTmpTab->integral >> 8; eeprom_data_temp[n++] = psTmpTab->integral; eeprom_data_temp[31] = DATA_SET_FLAG; eeprom_24c64_write (temp, 32, eeprom_data_temp); } void LoadStable(u16 iTemp, StableTempTab *psTmpTab) { u16 n, temp; u8 eeprom_data_temp[32]; //M24C64为32个字节一个page temp = iTemp / 100; if (temp > 199) temp = 199; temp += TEMP_TABLE_BASE; temp <<= 5; eeprom_24c64_read (temp, 32, eeprom_data_temp); n = 0; psTmpTab->iStableCnt = eeprom_data_temp[n++]; psTmpTab->iStableCnt <<= 8; psTmpTab->iStableCnt += eeprom_data_temp[n++]; psTmpTab->iDestTemp = eeprom_data_temp[n++]; psTmpTab->iDestTemp <<= 8; psTmpTab->iDestTemp += eeprom_data_temp[n++]; psTmpTab->integral = eeprom_data_temp[n++]; psTmpTab->integral <<= 8; psTmpTab->integral += eeprom_data_temp[n++]; psTmpTab->integral <<= 8; psTmpTab->integral += eeprom_data_temp[n++]; psTmpTab->integral <<= 8; psTmpTab->integral += eeprom_data_temp[n++]; } // u8 readHFDownTemp (void) { u16 n; u8 eeprom_data_temp[32]; //M24C64为32个字节一个page //n=HFDownTemp_BASE; //n<<=5; n = HUIFA_TEMP_DROP_ADD; eeprom_24c64_read (n, 2, eeprom_data_temp); if (eeprom_data_temp[0] == 0) return eeprom_data_temp[1]; else return 100; } // void saveHFDownTemp (u8 temp) { u16 n; u8 eeprom_data_temp[32]; //M24C64为32个字节一个page /*eeprom_data_temp[0]=temp; eeprom_data_temp[31]=DATA_SET_FLAG; n=HFDownTemp_BASE; n<<=5; eeprom_24c64_write (n,32,eeprom_data_temp);*/ eeprom_data_temp[0] = 0; eeprom_data_temp[1] = temp; n = HUIFA_TEMP_DROP_ADD; n <<= 5; eeprom_24c64_write (n, 2, eeprom_data_temp); }