#include "provalctrl.h" //定义比例阀相结构体 propotion_valve pv_one; propotion_valve pv_two; //PID参数缓存,用于EEPROM读写 // float data_wr[3] = {0}; //[0,1,2] -> “Kp, Ti, Td” // float data_rd[3] = {0}; //比例阀参数初始化 void prov_init(void) //不同比例阀的初始参数可能会不一样,因此未使用结构体指针传参 { //ee_readfloats(PRO1_ADDR,data_rd,3); //读取存储在EEPROM里的PID参数 pv_one.status = PROV_HOLDING; pv_one.tag = 1; //标签,比例阀1 for(uint8_t i = 0;i < SLDW_PRES_OUT;i++)//前n个时刻的输出气压 { pv_one.previous_pressure[i] = 0; } pv_one.current_pressure = 0; //当前气压,Kpa pv_one.current_percent = 0; //当前气压百分比( 0~900Kpa -> 0~100% ) pv_one.target_pressure = 0; //目标气压,Kpa pv_one.target_percent = 0; //目标气压百分比( 0~900Kpa -> 0~100% ) pv_one.target_current = 0; //目标值的理论电流 pv_one.current_input = 0; //当前输入电流 pv_one.input_min = 3; //输入电流下限 pv_one.input_max = 20; //输入电流上限 pv_one.ctrl_min = 4; //理论控制范围下限 pv_one.ctrl_max = 20; //理论控制范围上限 pv_one.bias = 0; //偏差 = 目标气压百分比 - 当前气压百分比 pv_one.bias_previous = 0; //前一个时刻的偏差 pv_one.bias_area = 0.5; //允许的误差范围,±a(%) // pv_one.Kp = (isnan(data_rd[0]))?(0):(data_rd[0]); //比例系数,NAN时视作0 // pv_one.Ti = (data_rd[1] == 0)?(1000000):(data_rd[1]); //积分时间,防止分母为零 // pv_one.Ing = 0; //积分输出 // pv_one.Ing_max = 5000; //积分上限 // pv_one.Ing_min = 0; //积分下限 // pv_one.Ing_sum = 0; //偏差求和 // pv_one.Td = data_rd[2]; //微分时间 // pv_one.Ts = 0.05; //采样周期,50ms // pv_one.Div = 0; //微分输出 // pv_one.pidout = 0; //PID输出 // pv_one.pidout_max = 0; //PID输出上限 // pv_one.pidout_min = 0; //PID输出下限 // pv_one.slow_down_flg = 0; //减速标志 pv_one.pvout = ao_blf1_set; //DAC输出函数 pv_one.pvout(0); //输出0mA /************************************************/ //ee_readfloats(PRO2_ADDR,data_rd,3); pv_two.status = PROV_HOLDING; pv_two.tag = 2; //标签,比例阀2 for(uint8_t i = 0;i < SLDW_PRES_OUT;i++)//前n个时刻的输出气压 { pv_two.previous_pressure[i] = 0; } pv_two.current_pressure = 0; pv_two.current_percent = 0; pv_two.target_pressure = 0; pv_two.target_percent = 0; pv_two.target_current = 0; //目标值的理论电流 pv_two.current_input = 0; pv_two.input_min = 3; pv_two.input_max = 20; pv_two.ctrl_min = 4; //理论控制范围下限 pv_two.ctrl_max = 20; //理论控制范围上限 pv_two.bias = 0; pv_two.bias_previous = 0; //前一个时刻的偏差 pv_two.bias_area = 0.5; // pv_two.Kp = (isnan(data_rd[0]))?(0):(data_rd[0]);; // pv_two.Ti = (data_rd[1] == 0)?(1000000):(data_rd[1]); //积分时间 // pv_two.Ing = 0; // pv_two.Ing_max = 5000; // pv_two.Ing_min = 0; // pv_two.Ing_sum = 0; // pv_two.Td = data_rd[2]; // pv_two.Ts = 0.05; //采样周期,50ms // pv_two.Div = 0; // pv_two.pidout = 0; // pv_two.pidout_max = 0; // pv_two.pidout_min = 0; // pv_two.slow_down_flg = 0; pv_two.pvout = ao_blf2_set; pv_two.pvout(0); } //绝对值计算 float abs_bias(float bias) { bias =( bias>=0 )?(bias):(-bias); return bias; } //模拟量控制 float ao_dwq = 0; //AO输出电流值(定位器) float ao_blf1 = 0; //AO输出电流值(比例阀) float ao_blf2 = 0; //AO输出电流值(比例阀) float ao_ee_save_flag = 0; // float atm_pressure = 0; //用于存放大气绝压,单位:0.1Kpa // adj_steps astep = ADJ_WAIT; //自整定步骤,继电反馈 // adj_section_steps astep_s = SECTION_WAIT; //自整定步骤,分段 //电流输出校准 float AO_table[SECTION_NUM + 1] = {0}; float ao_cal_interval = 0; //将记录的实际输出值在数组定义处填入此处 float AO_0_table[SECTION_NUM + 1] = {0}; float AO_1_table[SECTION_NUM + 1] = {0}; float AO_2_table[SECTION_NUM + 1] = {0}; void analog_ctrl(void) { if(ao_ee_save_flag == 1) { ee_writefloats(AO_0_SAVE_ADDR, AO_0_table); ee_writefloats(AO_1_SAVE_ADDR, AO_1_table); ee_writefloats(AO_2_SAVE_ADDR, AO_2_table); AO_init_flag[AO_0] = 0; AO_init_flag[AO_1] = 0; AO_init_flag[AO_2] = 0; current_output_calibrate_init(); ao_ee_save_flag = 0; } if(ao_dwq != (float)(HoldReg[0]) / 1000) //保持寄存器值发生变化时dac输出 { ao_dwq = (float)(HoldReg[0]) / 1000; // uA -> mA ao_dwq = current_output_calibrate(AO_0, ao_dwq); ao_dwq_set(ao_dwq); } if( ao_blf1!= (float)(HoldReg[1]) / 1000) //保持寄存器值发生变化时dac输出 { ao_blf1 = (float)(HoldReg[1]) / 1000; // uA -> mA ao_blf1 = current_output_calibrate(AO_1, ao_blf1); ao_blf1_set(ao_blf1); //prov_set(ao_blf1,&pv_one); //控制比例阀1 } if(ao_blf2 != (float)(HoldReg[2]) / 1000) //保持寄存器值发生变化时dac输出 { ao_blf2 = (float)(HoldReg[2]) / 1000; // uA -> mA ao_blf2 = current_output_calibrate(AO_2, ao_blf2); ao_blf2_set(ao_blf2); //prov_set(ao_blf2,&pv_two); //控制比例阀2 } #if ENABLE_SECTION_CAL if( (CoilState[3]&(0x40)) == 0x40 ) //触发比例阀1自整定 { if( (astep_s == SECTION_WAIT) && (pv_two.status != PROV_ADJUSTING) ) { pv_one.status = PROV_ADJUSTING; } } if( (CoilState[3]&(0x20)) == 0x20 ) //触发比例阀2自整定 { if( (astep_s == SECTION_WAIT) && (pv_one.status != PROV_ADJUSTING) ) { pv_two.status = PROV_ADJUSTING; } } if(it_100ms_flag_pv == 1) //每隔100ms更新一次数据 { it_100ms_flag_pv = 0; InputReg[7] = ( (InputReg[7]<16000)&&(InputReg[7]>8000) )?(InputReg[7]):(12000); //应对没接大气压力的情况 atm_pressure = ( (InputReg[7] - 4000)/(float)16000.0 ) * 2000; //大气绝压更新,4~20mA->0~200Kpa for(uint8_t i = 1;i< SLDW_PRES_OUT;i++) //滑动窗口,记录历史纯输出气压 { pv_one.previous_pressure[SLDW_PRES_OUT - i] = pv_one.previous_pressure[SLDW_PRES_OUT - i - 1]; pv_two.previous_pressure[SLDW_PRES_OUT - i] = pv_two.previous_pressure[SLDW_PRES_OUT - i - 1]; } pv_one.previous_pressure[0] = pv_one.current_pressure; pv_two.previous_pressure[0] = pv_two.current_pressure; //比例阀1数据更新:当前气压、当前气压百分比、百分比偏差、当前输入电流(单片机->比例阀) pv_one.current_pressure = (InputReg[16] - atm_pressure)/(float)10; //Kpa,sensor1 A口绝压转表压 pv_one.current_percent = pv_one.current_pressure/900*100; pv_one.bias = pv_one.target_percent - pv_one.current_percent; //比例阀2数据更新:当前气压、当前气压百分比、百分比偏差、当前输入电流(单片机->比例阀) pv_two.current_pressure = (InputReg[17] - atm_pressure)/(float)10; //Kpa,sensor1 B口绝压转表压 pv_two.current_percent = pv_two.current_pressure/900*100; pv_two.bias = pv_two.target_percent - pv_two.current_percent; } if(it_50ms_flag_pv == 1) //每隔50ms校准一次 { it_50ms_flag_pv = 0; //每个比例阀对应两个电磁阀,一个是气源一个是锁止阀 if( (CoilState[0]&(0x03)) == 0x03 ) //2个电磁阀都开启的情况下才进行控制比例阀1,否则保持 { if(pv_two.status != PROV_ADJUSTING) //对其中一个比例阀进行自整定时,不对另一个进行控制 { prov_ctrl(&pv_one, &adj_pv1); } } if( (CoilState[0]&(0x0C)) == 0x0C ) //2个电磁阀都开启的情况下才进行控制比例阀2,否则保持 { if(pv_one.status != PROV_ADJUSTING) //对其中一个比例阀进行自整定时,不对另一个进行控制 { prov_ctrl(&pv_two, &adj_pv2); } } } #endif } int8_t AO_init_flag[3] = {0}; void current_output_calibrate_init(void) { ao_cal_interval = ( (float)(AO_CAL_END - AO_CAL_START) ) / ( (float)SECTION_NUM ); for(uint8_t i = 0; i < SECTION_NUM + 1; i++) { AO_table[i] = AO_CAL_START + i * ao_cal_interval; } ee_readfloats(AO_0_SAVE_ADDR, AO_0_table); ee_readfloats(AO_1_SAVE_ADDR, AO_1_table); ee_readfloats(AO_2_SAVE_ADDR, AO_2_table); if( abs_bias( AO_0_table[0] - AO_table[0] ) > 1) { AO_init_flag[AO_0] = -1; } if( abs_bias( AO_1_table[0] - AO_table[0] ) > 1) { AO_init_flag[AO_1] = -1; } if( abs_bias( AO_2_table[0] - AO_table[0] ) > 1) { AO_init_flag[AO_2] = -1; } } float current_output_calibrate(uint8_t tag, float target) { float result = 0; if(target > 25) target = 25; //限幅 if(target < 0) target = 0; //限幅 switch (tag) { case AO_0: { if(AO_init_flag[AO_0] == -1) { result = target; return result; } for(uint8_t i = 0; i < SECTION_NUM; i++) { if( (AO_0_table[i] <= target) && (target <= AO_0_table[i + 1]) ) { result = ( (target - AO_0_table[i]) / ( AO_0_table[i + 1] - AO_0_table[i] ) ) * \ ao_cal_interval + AO_table[i]; if(result > 25) result = 25; //限幅 if(result < 0) result = 0; //限幅 return result; } } } break; case AO_1: { if(AO_init_flag[AO_1] == -1) { result = target; return result; } for(uint8_t i = 0; i < SECTION_NUM; i++) { if( (AO_1_table[i] <= target) && (target <= AO_1_table[i + 1]) ) { result = ( (target - AO_1_table[i]) / ( AO_1_table[i + 1] - AO_1_table[i] ) ) * \ ao_cal_interval + AO_table[i]; if(result > 25) result = 25; //限幅 if(result < 0) result = 0; //限幅 return result; } } } break; case AO_2: { if(AO_init_flag[AO_2] == -1) { result = target; return result; } for(uint8_t i = 0; i < SECTION_NUM; i++) { if( (AO_2_table[i] <= target) && (target <= AO_2_table[i + 1]) ) { result = ( (target - AO_2_table[i]) / ( AO_2_table[i + 1] - AO_2_table[i] ) ) * \ ao_cal_interval + AO_table[i]; if(result > 25) result = 25; //限幅 if(result < 0) result = 0; //限幅 return result; } } } break; default: break; } return 0; } #if ENABLE_SECTION_CAL //比例阀目标值设定,调节范围计算 void prov_set(float target_p, propotion_valve *pvx) { target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅 target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min); pvx->target_percent = (target_p - pvx->ctrl_min) / ( pvx->ctrl_max - pvx->ctrl_min )*100; //目标百分比,按照理论范围计算 pvx->target_pressure = pvx->target_percent/100*900; //Kpa, 比例阀 (4~20mA -> 0~0.9Mpa) switch (pvx->tag)//分段校准,不同比例阀的数据表不一样 { case 1: { target_p = (prov_section_calculate(pvx->target_pressure,&adj_pv1) > 0)?(prov_section_calculate(pvx->target_pressure,&adj_pv1)):(target_p); target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅 target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min); } break; case 2: { target_p = (prov_section_calculate(pvx->target_pressure,&adj_pv2) > 0)?(prov_section_calculate(pvx->target_pressure,&adj_pv2)):(target_p); target_p = (target_p < pvx->input_max)?(target_p):(pvx->input_max); //dac输出限幅 target_p = (target_p > pvx->input_min)?(target_p):(pvx->input_min); } break; default: break; } pvx->target_current = target_p; //记录当前理论模拟输出 pvx->gas_direction = (pvx->target_current >= pvx->current_input)?(GAS_IN):(GAS_OUT); //判断即将进行充气还是排气 pvx->current_input = pvx->target_current; //理论值作为当前输出值 pvx->pvout(pvx->current_input); //dac输出 } //分段计算达到目标气压所需的电流 float prov_section_calculate(float target_pressure, prov_adjust *adj_pvx) { float target_current = 0; if(adj_pvx->table_pressure[SECTION_NUM_ADJ - 1] == -1) return -1; //未经过整定 //寻找目标值所在区间,并根据区间占比计算所需输出 for(uint8_t i = 0;i < SECTION_NUM_ADJ - 1;i++) { if( (target_pressure >= adj_pvx->table_pressure[i]) && (target_pressure < adj_pvx->table_pressure[i+1]) ) { target_current = adj_pvx->table_current[i]+(adj_pvx->table_current[i+1] - adj_pvx->table_current[i]) \ * (target_pressure - adj_pvx->table_pressure[i])/(adj_pvx->table_pressure[i+1] - adj_pvx->table_pressure[i]); //目标气压在所在区间内的百分比 } if(target_pressure >= adj_pvx->table_pressure[SECTION_NUM_ADJ - 1]) target_current = adj_pvx->table_current[SECTION_NUM_ADJ - 1]; } return target_current; } //比例阀控制 void prov_ctrl(propotion_valve *pvx, prov_adjust *adj_pvx) { switch(pvx->status) { case PROV_RUNNING: //介入控制 { if(adj_pvx->adj_flag == 1) //进入此处说明整定被打断 { //整定结束 prov_adj_init(adj_pvx); astep = ADJ_WAIT; astep_s = SECTION_WAIT; } // prov_calibrate_step(&pvx); prov_calibrate_pid(pvx); } break; case PROV_ADJUSTING: //自整定 { // //继电反馈 // if(astep == ADJ_WAIT) // { // prov_adj_init(adj_pvx); // adj_pvx->adj_flag = 1; //整定开始 // astep = ADJ_START; // } // prov_adj(pvx, adj_pvx); //分段 if(astep_s == SECTION_WAIT) { prov_adj_section_init(adj_pvx); adj_pvx->adj_flag = 1; //整定开始 astep_s = SECTION_START; } prov_adj_section(pvx, adj_pvx); } break; case PROV_HOLDING: //保持原样,不介入控制 { if(adj_pvx->adj_flag == 1) //进入此处说明整定被打断 { //整定结束 prov_adj_init(adj_pvx); astep = ADJ_WAIT; astep_s = SECTION_WAIT; } } break; default: { return; } } } #endif #if ENABLE_PID_CTRL //初始化自整定相关的参数 prov_adjust adj_pv1; prov_adjust adj_pv2; //初始化继电反馈整定参数 void prov_adj_init(prov_adjust *adj_pvx) { adj_pvx->adj_flag = 0; //自整定标志,0:空闲,1:整定中 for( uint8_t i = 0;i < OSCILL_TIMES;i++) //继电整定,振荡幅值,振荡周期 { adj_pvx->relay_a[i] = 0; adj_pvx->relay_tc[i] = 0; } adj_pvx->relay_d = 0.15; //继电整定,回环幅值 adj_pvx->air_source = 0; //气源,单位Kpa adj_pvx->middle_current = 0; //中间气压电流 adj_pvx->oscil_times = 0; //振荡次数 } int tick_previous = -1; //用于计时 int tick_current = -1; uint8_t hys_flag = 0; //0:充气, 1:排气 //PID参数自整定,继电反馈 void prov_adj(propotion_valve *pvx, prov_adjust *adj_pvx) { switch(astep) { case ADJ_START: { if(tick_previous == -1) { tick_previous = tick500ms; prov_set(4,pvx);//排空气体 } tick_current = tick500ms; if( (tick_current - tick_previous) > 20) //等待20*500ms { //记录此时气源压力 adj_pvx->air_source = (InputReg[18] - atm_pressure) / (float)10; tick_previous = -1; tick_current = -1; astep++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case ADJ_MOVE2MIDDLE: { if(tick_previous == -1) { tick_previous = tick500ms; //输出气源50%压力,根据目标压力倒推所需电流值 adj_pvx->middle_current = (adj_pvx->air_source/2/900)*(pvx->input_max - pvx->input_min) + pvx->input_min; adj_pvx->middle_current = (adj_pvx->middle_current < pvx->input_max)?(adj_pvx->middle_current):(pvx->input_max); //dac输出限幅 adj_pvx->middle_current = (adj_pvx->middle_current > pvx->input_min)?(adj_pvx->middle_current):(pvx->input_min); prov_set(adj_pvx->middle_current,pvx); } tick_current = tick500ms; if( (tick_current - tick_previous) > 10) //等待10*500ms { tick_previous = -1; tick_current = -1; astep++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case ADJ_OSCILLATE: { if(adj_pvx->oscil_times < OSCILL_TIMES) //振荡次数是否达到目标 { if(tick_previous == -1) //计时开始,记录起始时间 { tick_previous = tick500ms; }else { //记录第 oc_times 个周期的峰值 adj_pvx->relay_a[adj_pvx->oscil_times] = (adj_pvx->relay_a[adj_pvx->oscil_times] < pvx->current_pressure)?(pvx->current_pressure):(adj_pvx->relay_a[adj_pvx->oscil_times]); } tick_current = tick500ms; //记录当前时间 if( (tick_current - tick_previous) > TICK_LIMIT ) //是否超时,TICK_LIMIT*500ms { astep = ADJ_END; //强制打断 }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } if( ( pvx->current_pressure < (adj_pvx->air_source/2*(1 + adj_pvx->relay_d)) ) && (hys_flag == 0) ) //充气振荡 { pvx->current_input = adj_pvx->middle_current*(1 + adj_pvx->relay_d); //输出气源 50*(1+d)% 压力 pvx->current_input = (pvx->current_input < pvx->input_max)?(pvx->current_input):(pvx->input_max); //dac输出限幅 pvx->current_input = (pvx->current_input > pvx->input_min)?(pvx->current_input):(pvx->input_min); prov_set(pvx->current_input,pvx); }else if( pvx->current_pressure >= (adj_pvx->air_source/2*(1 + adj_pvx->relay_d)) && (hys_flag == 0)) //切换至排气 { hys_flag = 1; } if( ( pvx->current_pressure > (adj_pvx->air_source/2*(1 - adj_pvx->relay_d)) ) && (hys_flag == 1) ) //排气振荡 { pvx->current_input = adj_pvx->middle_current*(1 - adj_pvx->relay_d); //输出气源 50*(1-d)% 压力 pvx->current_input = (pvx->current_input < pvx->input_max)?(pvx->current_input):(pvx->input_max); //dac输出限幅 pvx->current_input = (pvx->current_input > pvx->input_min)?(pvx->current_input):(pvx->input_min); prov_set(pvx->current_input,pvx); }else if( pvx->current_pressure <= (adj_pvx->air_source/2*(1 - adj_pvx->relay_d)) && (hys_flag == 1) ) //切换至充气 { hys_flag = 0; adj_pvx->relay_tc[adj_pvx->oscil_times] = (tick_current - tick_previous) * 500; //振荡周期,单位ms tick_previous = -1; //重新开始计时 tick_current = -1; adj_pvx->oscil_times++; //开始下一次振荡 } }else //振荡次数达到目标 { if(tick_previous == -1) { tick_previous = tick500ms; } tick_current = tick500ms; if( (tick_current - tick_previous) > 6) //等待6*500ms后排气 { prov_set(4,pvx); tick_previous = -1; tick_current = -1; astep++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } } break; case ADJ_CALCULATE: { for(uint8_t i = 0;i < OSCILL_TIMES; i++) //对振荡周期和峰值作均值滤波 { adj_pvx->relay_A += adj_pvx->relay_a[i]; adj_pvx->relay_Tc += adj_pvx->relay_tc[i]; } adj_pvx->relay_A /= OSCILL_TIMES * ( adj_pvx->air_source/2 * (1 + adj_pvx->relay_d) ); adj_pvx->relay_Tc /= OSCILL_TIMES; /* PI [0.45,0.8]*/ /* PID [0.6,0.5,0.12]*/ pvx->Kp = (float)0.6 * ( 4 * (float)0.1 ) / ( (float)3.1415 * adj_pvx->relay_A ) / 3; pvx->Ti = (float)0.5 * adj_pvx->relay_Tc/1000 / 20; pvx->Td = (float)0.12 * adj_pvx->relay_Tc/1000; pvx->Ing_sum = 0; switch(pvx->tag) { case 1: { data_wr[0] = pvx->Kp; data_wr[1] = pvx->Ti; data_wr[2] = pvx->Td; ee_writefloats(PRO1_ADDR,data_wr,3); } break; case 2: { data_wr[0] = pvx->Kp; data_wr[1] = pvx->Ti; data_wr[2] = pvx->Td; ee_writefloats(PRO2_ADDR,data_wr,3); } break; default: { } break; } astep++; } break; case ADJ_END: { if(tick_previous == -1) { tick_previous = tick500ms; } tick_current = tick500ms; if( (tick_current - tick_previous) > 10) //等待10*500ms后恢复控制(等待期间:排气 + 计算结果) { switch(pvx->tag) //重新触发保持寄存器 { case 1: { ao_blf1 = -1; CoilState[3] &= 0xBF; //D6 = 0, [1011 1111] } break; case 2: { ao_blf2 = -1; CoilState[3] &= 0xDF; //D5 = 0, [1101 1111] } break; default: { } break; } tick_previous = -1; tick_current = -1; prov_adj_init(adj_pvx); pvx->status = PROV_RUNNING; //恢复控制 astep++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case ADJ_WAIT: { } break; default: { return; } } } //pid控制 float beta_in = 0, beta_out = 0; //变速积分 void prov_calibrate_pid(propotion_valve *pvx) { if( (abs_bias(pvx->bias) > pvx->bias_area ) && (abs_bias(pvx->bias) < BIAS_MAX) ) //误差进入目标±BIAS_MAX%以内后再进行控制 { //变速积分 beta_in = ( abs_bias(pvx->bias*pvx->bias) + BIAS_MAX ) / ( BIAS_MAX ); beta_out = ( abs_bias(pvx->bias) + BIAS_MAX/2 ) / ( BIAS_MAX ); //充放气过程分开处理 switch(pvx->gas_direction) { case GAS_IN: //充气 { pvx->Ing_sum += pvx->bias * beta_in; //偏差求和 pvx->Ing_sum = (pvx->Ing_sum > pvx->Ing_max)?(pvx->Ing_max):(pvx->Ing_sum); //积分限幅 pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum); pvx->Ing = ( pvx->Ts / pvx->Ti ) * pvx->Ing_sum; //积分项 pvx->Div = ( pvx->Td / pvx->Ts ) * ( pvx->bias - pvx->bias_previous ); //微分项 pvx->pidout = pvx->Kp*( pvx->bias + pvx->Ing + pvx->Div ); //pid输出 pvx->current_input = (pvx->pidout/100) * (pvx->input_max - pvx->input_min) + pvx->input_min; } break; case GAS_OUT: //排气 { pvx->Ing_sum += pvx->bias * beta_out; //偏差求和 pvx->Ing_sum = (pvx->Ing_sum > pvx->Ing_max)?(pvx->Ing_max):(pvx->Ing_sum); //积分限幅 pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum); pvx->Ing = ( pvx->Ts / pvx->Ti ) * pvx->Ing_sum; //积分项 pvx->Div = ( pvx->Td / pvx->Ts ) * ( pvx->bias - pvx->bias_previous ); //微分项 pvx->pidout = pvx->Kp*( pvx->bias + pvx->Ing + pvx->Div ); //pid输出 pvx->current_input = (pvx->pidout/100) * (pvx->input_max - pvx->input_min) + pvx->input_min; } break; default: { } break; } pvx->current_input = (pvx->current_input < pvx->pidout_max)?(pvx->current_input):(pvx->pidout_max); //充气时限制输出上限 pvx->current_input = (pvx->current_input > pvx->pidout_min)?(pvx->current_input):(pvx->pidout_min); //排气时限制输出下限 pvx->bias_previous = pvx->bias; //更新前一个时刻的偏差 if( abs_bias(pvx->previous_pressure[0] - pvx->previous_pressure[SLDW_PRES_OUT - 1]) <= 10 ) //判断是否趋于稳定 { pvx->slow_down_flg = 1; } pvx->current_input = (pvx->target_current < pvx->input_max*(float)(0.995))?(pvx->current_input):(pvx->input_max); //小信号切除 if( pvx->target_current <= pvx->input_min*(float)(1.005) ) { pvx->current_input = pvx->input_min; pvx->Ing_sum = 0; } pvx->pvout(pvx->current_input); //dac输出 }else if(abs_bias(pvx->bias) >= BIAS_MAX) { switch(pvx->gas_direction) //根据气体方向对输出范围做补偿 { case GAS_IN: { pvx->Ing_sum = (pvx->target_percent - 100*out_makeup) / pvx->Kp / ( pvx->Ts / pvx->Ti ); //预估积分值,改善滞后 pvx->Ing_sum = (pvx->Ing_sum < pvx->Ing_min)?(pvx->Ing_min):(pvx->Ing_sum); } break; case GAS_OUT: { pvx->Ing_sum = pvx->target_percent / pvx->Kp / ( pvx->Ts / pvx->Ti ); //预估积分值,改善滞后 } break; default: { } break; } } } #endif #if ENABLE_SECTION_CAL //分段校准参数初始化 void prov_adj_section_init(prov_adjust *adj_pvx) { adj_pvx->adj_flag = 0; adj_pvx->stable_area = 9; adj_pvx->wait_tick = 2 * 30; for(uint8_t i = 0; i < SECTION_NUM_ADJ; i++) { adj_pvx->table_current[i] = -1; adj_pvx->table_pressure[i] = -1; } } uint8_t temp_cnt = 0; //分段校准 void prov_adj_section(propotion_valve *pvx, prov_adjust *adj_pvx) { switch (astep_s) { case SECTION_START: { if(tick_previous == -1) { tick_previous = tick500ms; prov_set(pvx->input_min,pvx); //排空气体 } tick_current = tick500ms; if( (tick_current - tick_previous) > 20) //等待20*500ms { tick_previous = -1; tick_current = -1; astep_s++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case SECTION_RECORD_AIRSOURCE: { //记录此时气源压力 adj_pvx->air_source = (InputReg[18] - atm_pressure) / (float)10; //计算分段电流,上限为气源倒推得到的电流值 adj_pvx->table_current[SECTION_NUM_ADJ-1] = adj_pvx->air_source/900 * (pvx->ctrl_max - pvx->ctrl_min) + pvx->ctrl_min; for(uint8_t i = 0;i < SECTION_NUM_ADJ - 1;i++) { adj_pvx->table_current[i] = pvx->ctrl_min + i * (adj_pvx->table_current[SECTION_NUM_ADJ-1] - pvx->ctrl_min)/(SECTION_NUM_ADJ - 1); } astep_s++; } break; case SECTION_MOVE: { if(tick_previous == -1) { tick_previous = tick500ms; prov_set( adj_pvx->table_current[temp_cnt], pvx ); //设定分段电流 } tick_current = tick500ms; if( (tick_current - tick_previous) > adj_pvx->wait_tick) //等待tick*500ms { adj_pvx->table_pressure[temp_cnt] = pvx->current_pressure; //记录当前实际气压 tick_previous = -1; //准备下一次计时 tick_current = -1; temp_cnt++; //下一个 if(temp_cnt >= SECTION_NUM_ADJ) { temp_cnt = 0; astep_s++; } }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case SECTION_END: { if(tick_previous == -1) { tick_previous = tick500ms; } tick_current = tick500ms; if( (tick_current - tick_previous) > 10) //等待10*500ms后恢复控制(等待期间:排气 + 计算结果) { switch(pvx->tag) //重新触发保持寄存器 { case 1: { ao_blf1 = -1; CoilState[3] &= 0xBF; //D6 = 0, [1011 1111] } break; case 2: { ao_blf2 = -1; CoilState[3] &= 0xDF; //D5 = 0, [1101 1111] } break; default: { } break; } tick_previous = -1; tick_current = -1; adj_pvx->adj_flag = 0; pvx->status = PROV_HOLDING; //恢复控制 astep_s++; }else if( (tick_current - tick_previous) < 0) { tick_previous = -1; tick_current = -1; } } break; case SECTION_WAIT: { } break; default: break; } } #endif