You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
incubator_embeded/HARDWARE/PID.c

419 lines
9.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <math.h>
#include <stdlib.h>
#include "PID.h"
#include "Relays.h"
#include "USART.h"
#include "rs485.h"
#define MODE_H 1
#define MODE_L 0
float cold_tem = 0;
float red_tem = 0;
float ti;
float ki = 0.001;
float kd = 340;
extern u8 hot_clod_flag;
extern u8 gpio_state;
extern int T;
unsigned int num = 0;
PID pid;
int min_speed_count = 2000;
int max_speed_count = 4800;
extern u8 gpio_state;
extern u8 lights[9];
extern u8 lights_addresses[9];
/**
* PID init
*/
void PID_Init()
{
if (abs(pid.out_tem) < 1e-5 && abs(pid.out_humidity) < 1e-5) {
return;
}
// // if flash have not a vaild value, just set a default value
// if (pid.Kp < 1e-7) { pid.Kp = 9.6; }
// if (pid.Ki < 1e-7) { pid.Ki = 0.01; }
// if (pid.Kd < 1e-7) { pid.Kd = 340; }
// if (pid.tem_threshold < 0.0001) { pid.tem_threshold = 0.2; }
pid.Ek = 0;
pid.Ek_prev = 0;
pid.SEk = 0;
pid.Pout = 0;
pid.Iout = 0;
pid.Dout = 0;
pid.OUT = 0;
pid.OUT0 = 0;
pid.h_percent = 0;
pid.c_speed = 0;
pid.t = 1000; // PID calc period
// pid.Ti=5000000;// integral time
// pid.Td=1000;// differential time
pid.pwmcycle = 200; // pwm cycle 200
pid.OUT0 = 1;
pid.C1ms = 0;
pid.max_compressor_tem = 30;
pid.hp_h = 6;
pid.hi_h = 0.02;
pid.hd_h = 0;
pid.h_base_h = 0;
pid.hp_l = 9;
pid.hi_l = 0.015;
pid.hd_l = 0;
pid.h_base_l = 30;
pid.hp = 0;
pid.hi = 0;
pid.hd = 0;
pid.h_base = 0;
pid.cp = 4;
pid.ci = 0.01;
pid.cd = 0;
pid.c_base = 37;
}
void light_set_3(u8 address, u8 value) {
u8 data[8] = { address, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 };
if (value > 100) {
value = 100;
}
data[4] = value / 256;
data[5] = value % 256;
GetCRC16(data, 6, data + 6, data + 7);
delay_xms(10);
RS485_3_Send_Data(data, 8);
}
void light_set_all_3(void) {
for (int i = 0; i < 9; i++) {
// set light i
light_set_3(lights_addresses[i], lights[i]);
}
}
/**
* set compressor speed count
* range of speed count: 0-6000, if speed count lower than 1500, the compressor will stop
*/
void set_compressor_power(int speed) {
u8 data[8] = {0x01, 0x06, 0x60, 0x00, 0x00, 0x09, 0xBB, 0xAA}; // speed control for compressor controller
if (speed > max_speed_count) {
speed = max_speed_count;
}
if (speed < 0) {
speed = 0;
}
data[4] = speed / 256;
data[5] = speed % 256;
GetCRC16(data, 6, data + 6, data + 7);
RS485_3_Init(9600);
delay_xms(30);
RS485_3_Send_Data(data, 8);
delay_xms(50);
light_set_all_3();
RS485_1_Init(9600);
}
/**
* set heater percent
* range of heater percent: 0-100
*/
void set_heater_power(int percent) {
u8 data[8] = { 0x10, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 };
if (percent > 100) {
percent = 100;
}
if (percent < 0) {
percent = 0;
}
data[4] = percent / 256;
data[5] = percent % 256;
GetCRC16(data, 6, data + 6, data + 7);
RS485_1_Init(9600);
delay_xms(30);
RS485_1_Send_Data(data, 8);
delay_xms(30);
}
/**
* heater power calc
*/
int calc_hp(float delta_t, float Error_calc, float DelEk, int p_hb, float pid_hp, float pid_hi, float pid_hd) {
int p_h = p_hb + pid_hp * delta_t + pid_hi * Error_calc + pid_hd * DelEk;
if (p_h > 100) {
return 100;
}
if (p_h < 0) {
return 0;
}
return p_h;
}
/**
* compressor power percent calc
*/
int calc_cp(float delta_t, float Error_calc, float DelEk, int cb, float cp, float ci, float cd) {
int percent = cb - cp * delta_t + ci * Error_calc + cd * DelEk;
if (percent > 100) {
return 100;
}
if (percent < 0) {
return 0;
}
return percent;
}
/**
* compressor speed calc
*/
int calc_compressor_speed(int percent, int v_min, int v_max) {
int v = percent * v_max / 100.0;
if (v > v_max) {
return v_max;
}
if (v < v_min) {
return v_min;
}
return v;
}
void PID_Calc() // pid calc
{
int mode = 0;
if (pid.set_tem > pid.max_compressor_tem) {
// h mode
mode = MODE_H;
} else {
// l mode
mode = MODE_L;
}
// int min_speed_count = 1800;
// int max_speed_count = 4800;
float DelEk; // The difference between the last two deviations
// float td;
float out;
// if (pid.C1ms < (pid.t)) // The calculation cycle has not yet arrived
// {
// return;
// }
float delta_t = pid.set_tem - pid.now_tem;
if (mode == MODE_H) {
pid.hp = pid.hp_h;
pid.hi = pid.hi_h;
pid.hd = pid.hd_h;
pid.h_base = pid.h_base_h;
// // if now temp is close to set temp, the heater will be less power
// // TODO::在外界温度温差比较大45-22=23度加热功率也可能不够
// if (pid.set_tem - pid.now_tem < 3) {
// pid.hp = pid.hp_h * 0.6;
// }
// // TODO::在外界温度温差比较大45-22=23度加热功率不够
// if (pid.set_tem - pid.now_tem < 1) {
// pid.hp = pid.hp_h * 0.3;
// }
// TODO::要改成根据外界温度调节参数
// TODO::A test
// 1 大于3度时候最大6000
// 2 小于3度时候在0度外界温差时最小到0
// 3 小于3度时候在12度外界温差时最小到1800
// 4 小于3度时候在23度外界温差时最小到2700
// 变量:外界温差 t_out内部温差 t_in
// 由2-4, hp_base = -3.125*t_out^2+187.5*t_out
// 由1, hp = min( (6000 - hp_base) / 3 * t_in + hp_base, 6000 )
if (abs(pid.out_tem) > 1e-5) {
float t_out = pid.set_tem - pid.out_tem; // 7
float t_in = pid.set_tem - pid.now_tem; // 1.1
// 当t_out为0时hp_base为0
// float hp_base = -0.003125 * t_out * t_out + 0.1875 * t_out;
// 采用简化模型不追求t_out为0时hp_base为0这可以同时提高在t_out较小时候的hp值
float hp_base = 0.075 * t_out + 0.9;
pid.hp = (6 - hp_base) / 2 * t_in + hp_base;
if (pid.hp < 0) {
pid.hp = 0;
}
if (pid.hp > 9) {
pid.hp = 9;
}
} else {
if (pid.set_tem - pid.now_tem < 3) {
pid.hp = pid.hp_h * 0.6;
}
if (pid.set_tem - pid.now_tem < 1) {
pid.hp = pid.hp_h * 0.4;
}
}
}
// l mode
if (mode == MODE_L) {
pid.hp = pid.hp_l;
pid.hi = pid.hi_l;
pid.hd = pid.hd_l;
pid.h_base = pid.h_base_l;
// if now temp is close to set temp, the heater will be less power
// if (pid.set_tem - pid.now_tem < 3) {
// pid.hp = pid.hp_l * 0.6;
// }
if (abs(pid.out_tem) > 1e-5) {
// hp_l = 3
// pid.hp = -0.02 * pid.hp_l * (pid.out_tem - pid.set_tem) + pid.hp_l;
// increase hp when the delta_tem is large
float delta_tem = pid.now_tem - pid.set_tem;
if (delta_tem > - 2) {
// pid.hp = pid.hp * 0.5;
pid.hp = -0.09 * delta_tem + pid.hp_l / 2;
} else {
pid.hp = (-pid.hp_l / 6 + 0.06) * delta_tem + pid.hp_l / 6 + 0.3;
}
}
}
pid.Ek = pid.set_tem - pid.now_tem;
pid.Pout = pid.Kp * pid.Ek; // Proportional output
pid.SEk += pid.Ek; // Total historical deviation
DelEk = pid.Ek - pid.Ek_prev; // The difference between the last two deviations
// h mode
if (mode == MODE_H) {
// when set temp is larger then real temp, the heater will be less power
// in h mode, when we want to heat over 5 degrees, just use hp
if (pid.set_tem > pid.now_tem + 5) {
pid.hi = 0;
pid.hd = 0;
pid.SEk = 0;
}
}
// l mode
if (mode == MODE_L) {
// make integral smaller when the temp is too low and the SEk is too small
if (pid.now_tem < pid.set_tem - 2 && pid.SEk <= - (pid.h_base / 2) / pid.hi) {
pid.SEk /= 10;
}
// SEk limit, updated func, remain a little heater power when the compressor is running in full state
if (pid.SEk < - (pid.h_base / 2) / pid.hi) {
pid.SEk = - (pid.h_base / 2) / pid.hi;
}
}
// when the compressor is in full status, then the integral will be 0
if (pid.c_speed == max_speed_count) {
pid.SEk = 0;
}
float Error_calc = pid.SEk;
if (Error_calc < - (pid.h_base + pid.hp * delta_t) / pid.hi) {
Error_calc = - (pid.h_base + pid.hp * delta_t) / pid.hi;
}
if (pid.c_speed == max_speed_count) {
Error_calc = 0;
}
// ti=pid.t/pid.Ti;
// ki=ti*pid.Kp;
pid.Iout = pid.Ki * pid.SEk; // integral output
// td=pid.Td/pid.t;
// kd=pid.Kp*td;
pid.Dout = pid.Kd * DelEk; // difference output
if (pid.Dout < 0)
{
pid.Dout = 0 - pid.Dout;
}
// out= pid.Pout+pid.Iout+ pid.Dout;
out = pid.Pout;
if (out > pid.pwmcycle)
{
pid.OUT = pid.pwmcycle;
}
else if (out <= 0)
{
pid.OUT = pid.OUT0;
}
else
{
pid.OUT = out;
}
// When the target tem is greater then max compressor tem, the compressor will stop
if (pid.set_tem > pid.max_compressor_tem) {
pid.c_speed = 0;
} else {
// if outer temp is got, we calc pid by outer temp
if (abs(pid.out_tem) > 1e-5) {
pid.cp = (pid.out_tem - pid.set_tem) * 0.28;
// pid.cp = (pid.out_tem - pid.set_tem) * 0.2;
}
// use nagetive error and error diff when calc compressor power
int p_c = calc_cp(delta_t, - Error_calc, - DelEk, pid.c_base, pid.cp, pid.ci, pid.cd);
pid.c_speed = calc_compressor_speed(p_c, min_speed_count, max_speed_count);
}
// heater percent
pid.h_percent = calc_hp(delta_t, Error_calc, DelEk, pid.h_base, pid.hp, pid.hi, pid.hd);
// in h mode, if current temp is close to set temp, the heater will close
if (pid.now_tem > pid.set_tem - 0.2 && pid.set_tem > pid.max_compressor_tem) {
pid.h_percent = 0;
}
// close heater when compressor is running in full state
if (pid.c_speed == max_speed_count) {
pid.h_percent = 0;
}
set_compressor_power(pid.c_speed);
set_heater_power(pid.h_percent);
// if the compressor is running, then open the bottom fan, else close
if (pid.c_speed > 0) {
gpio_state |= 0x40;
} else {
gpio_state &= 0xBF;
}
HC595_Send_Byte(gpio_state);
pid.Ek_prev = pid.Ek; // udpate difference
pid.C1ms = 0;
}