前馈+PI调参记录

前馈+PI调参记录
最近搞了一个测PL的加热样品台计划使用前馈PI的方法来进行温度控制。首先使用恒定功率加热获得稳态温度用于前馈参数的确定。不过由于我加热棒功率选小了整个样品台的热容又比较大升温到稳态非常慢。因此我先加热到差不多快稳态的温度然后使用公式来拟合这里可以直接使用origin软件的曲线拟合工具。前馈参数的确定cubeMonitor的使用温度曲线数据获取我这里用的是stm32cubeMonitor打开以后自带一个work flow首先双击上面的my variables然后点击加号选择之前在cubeIDE编译后生成的elf文件然后再variable list里面同时选择上想要监测的变量值 我这里吧加热功率跟温度都选了温度数据不需要获取太频繁选择1Hz在work flow的my probe out这里设置同样 在my probe in这里同样设置自己的stlink然后设置下面的my variables因为我这里是记录两个数据因此选择multiple columns。设置完成后点击右上角的deploy然后点击dashboard就可以打开记录界面结果是自动保存在C盘 user 用户名 logs里面。以csv格式存储。恒功率拟合结果PWM占空比Y0稳态20%80.765640%118.931160%150.452980%176.3988100%196.6839用二阶多项式拟合将拟合得到的系数用于前馈部分的计算代码在下面。初始Kp Ki系数的估计方法Kp的估计维持目标温度所需的功率随温度的变化率为其中a和b为也就是之前用于拟合温度与功率的a和b一般取Kp在0.2~0.5倍S大小Ki的估计Ki与热时间常数有关也就是之前用于拟合稳态温度使用的单指数公式中的tau一般来说对于响应比较慢的系统积分时间Ti等于tau~2tau范围。加热温控程序heater_control.c#include heater_control.h #include math.h #include stddef.h #define HEATER_CONTROL_EPSILON (1.0e-6f) static float HeaterControl_Clamp(float value, float minimum, float maximum) { if (isnan(value)) { return minimum; } if (value maximum) { return maximum; } if (value minimum) { return minimum; } return value; } static float HeaterControl_ModelTemperature( const HeaterControl_t *controller, float duty) { return controller-config.a * duty controller-config.b * duty * duty controller-config.c; } static float HeaterControl_Absolute(float value) { return (value 0.0f) ? value : -value; } static float HeaterControl_FindClosestSafeDuty( const HeaterControl_t *controller, float target_temperature) { float duty_min controller-config.output_min; float duty_max controller-config.output_max; float best_duty duty_min; float best_error 0.0f; uint8_t candidate_found 0U; float candidates[3]; uint8_t candidate_count 0U; uint8_t index; /* 只在模型斜率为正的区域以及抛物线顶点中选择安全工作点。 */ if ((controller-config.a 2.0f * controller-config.b * duty_min) HEATER_CONTROL_EPSILON) { candidates[candidate_count] duty_min; } if ((controller-config.a 2.0f * controller-config.b * duty_max) HEATER_CONTROL_EPSILON) { candidates[candidate_count] duty_max; } if (HeaterControl_Absolute(controller-config.b) HEATER_CONTROL_EPSILON) { float vertex -controller-config.a / (2.0f * controller-config.b); if ((vertex duty_min) (vertex duty_max)) { candidates[candidate_count] vertex; } } for (index 0U; index candidate_count; index) { float error HeaterControl_Absolute( target_temperature - HeaterControl_ModelTemperature(controller, candidates[index])); if ((candidate_found 0U) || (error best_error)) { best_error error; best_duty candidates[index]; candidate_found 1U; } } /* 整个输出区间都处于负斜率区时退回最小输出。 */ return (candidate_found ! 0U) ? best_duty : duty_min; } void HeaterControl_Init( HeaterControl_t *controller, const HeaterControl_Config_t *config) { if ((controller NULL) || (config NULL)) { return; } controller-config *config; if (!isfinite(controller-config.a)) { controller-config.a 0.0f; } if (!isfinite(controller-config.b)) { controller-config.b 0.0f; } if (!isfinite(controller-config.c)) { controller-config.c 0.0f; } if (!isfinite(controller-config.output_min)) { controller-config.output_min 0.0f; } if (!isfinite(controller-config.output_max)) { controller-config.output_max 100.0f; } if (controller-config.output_min controller-config.output_max) { float temporary controller-config.output_min; controller-config.output_min controller-config.output_max; controller-config.output_max temporary; } if (controller-config.integral_min controller-config.integral_max) { float temporary controller-config.integral_min; controller-config.integral_min controller-config.integral_max; controller-config.integral_max temporary; } if (!isfinite(controller-config.integral_min)) { controller-config.integral_min -100.0f; } if (!isfinite(controller-config.integral_max)) { controller-config.integral_max 100.0f; } if (controller-config.integral_min controller-config.integral_max) { float temporary controller-config.integral_min; controller-config.integral_min controller-config.integral_max; controller-config.integral_max temporary; } if ((!isfinite(controller-config.sample_time_s)) || (controller-config.sample_time_s 0.0f)) { controller-config.sample_time_s 0.2f; } if ((!isfinite(controller-config.kp_factor)) || (controller-config.kp_factor 0.0f)) { controller-config.kp_factor 0.0f; } if ((!isfinite(controller-config.ki_tau_factor)) || (controller-config.ki_tau_factor 0.0f)) { controller-config.ki_tau_factor 0.0f; } if ((!isfinite(controller-config.tau_s)) || (controller-config.tau_s 0.0f)) { controller-config.tau_s 0.0f; } HeaterControl_Reset(controller); } void HeaterControl_Reset(HeaterControl_t *controller) { if (controller NULL) { return; } controller-integral 0.0f; controller-error 0.0f; controller-model_slope 0.0f; controller-sensitivity 0.0f; controller-dynamic_kp 0.0f; controller-dynamic_ki 0.0f; controller-feedforward 0.0f; controller-p_term 0.0f; controller-i_term 0.0f; controller-output 0.0f; } float HeaterControl_CalculateFeedforward( const HeaterControl_t *controller, float target_temperature) { float duty_min; float duty_max; float a; float b; float c; if (controller NULL) { return 0.0f; } if (!isfinite(target_temperature)) { return 0.0f; } if ((!isfinite(controller-config.a)) || (!isfinite(controller-config.b)) || (!isfinite(controller-config.c)) || (!isfinite(controller-config.output_min)) || (!isfinite(controller-config.output_max)) || (controller-config.output_min controller-config.output_max)) { return 0.0f; } duty_min controller-config.output_min; duty_max controller-config.output_max; a controller-config.a; b controller-config.b; c controller-config.c; /* 解方程bP^2 aP (c - target) 0 */ if (HeaterControl_Absolute(b) HEATER_CONTROL_EPSILON) { /* b接近0时退化为线性模型T aP c */ if (a HEATER_CONTROL_EPSILON) { return duty_min; } return HeaterControl_Clamp( (target_temperature - c) / a, duty_min, duty_max); } else { float discriminant; float square_root; float root_1; float root_2; uint8_t root_1_valid; uint8_t root_2_valid; uint8_t root_1_increasing; uint8_t root_2_increasing; discriminant a * a - 4.0f * b * (c - target_temperature); if (discriminant 0.0f) { return HeaterControl_FindClosestSafeDuty( controller, target_temperature); } square_root sqrtf(discriminant); root_1 (-a square_root) / (2.0f * b); root_2 (-a - square_root) / (2.0f * b); root_1_valid ((root_1 duty_min) (root_1 duty_max)) ? 1U : 0U; root_2_valid ((root_2 duty_min) (root_2 duty_max)) ? 1U : 0U; /* 模型斜率dT/dP a 2bP正常加热曲线应取正斜率根。 */ root_1_increasing ((a 2.0f * b * root_1) 0.0f) ? 1U : 0U; root_2_increasing ((a 2.0f * b * root_2) 0.0f) ? 1U : 0U; if ((root_1_valid ! 0U) (root_1_increasing ! 0U)) { return root_1; } if ((root_2_valid ! 0U) (root_2_increasing ! 0U)) { return root_2; } /* 不使用负斜率根在正斜率边界和顶点中选择最接近的值。 */ return HeaterControl_FindClosestSafeDuty( controller, target_temperature); } } float HeaterControl_Update( HeaterControl_t *controller, float target_temperature, float measured_temperature) { float proposed_integral; float unsaturated_output; if (controller NULL) { return 0.0f; } if ((!isfinite(target_temperature)) || (!isfinite(measured_temperature)) || (!isfinite(controller-config.a)) || (!isfinite(controller-config.b)) || (!isfinite(controller-config.c)) || (!isfinite(controller-config.output_min)) || (!isfinite(controller-config.output_max)) || (!isfinite(controller-config.integral_min)) || (!isfinite(controller-config.integral_max)) || (!isfinite(controller-config.sample_time_s)) || (!isfinite(controller-config.kp_factor)) || (!isfinite(controller-config.ki_tau_factor)) || (!isfinite(controller-config.tau_s)) || (controller-config.output_min controller-config.output_max) || (controller-config.integral_min controller-config.integral_max) || (controller-config.sample_time_s 0.0f) || (controller-config.kp_factor 0.0f) || (controller-config.ki_tau_factor 0.0f) || (controller-config.tau_s 0.0f)) { HeaterControl_Reset(controller); return 0.0f; } /* 1. 计算误差 */ controller-error target_temperature - measured_temperature; if (!isfinite(controller-error)) { HeaterControl_Reset(controller); return 0.0f; } /* 2. 计算目标温度对应的前馈占空比 */ controller-feedforward HeaterControl_CalculateFeedforward( controller, target_temperature); if (!isfinite(controller-feedforward)) { HeaterControl_Reset(controller); return 0.0f; } /* 3. 根据前馈工作点计算动态Kp、Ki */ /* * 当前目标温度所对应的稳态工作点为 feedforward。 * * 温度模型 * T aP bP^2 c * * 模型斜率 * dT/dP a 2bP * * 灵敏度 * S dP/dT 1 / (dT/dP) */ controller-model_slope controller-config.a 2.0f * controller-config.b * controller-feedforward; if (!isfinite(controller-model_slope)) { HeaterControl_Reset(controller); return 0.0f; } /* * 只允许在正斜率区域计算动态PI参数。 * 正常加热系统中占空比增加时稳态温度应升高。 */ if (controller-model_slope HEATER_CONTROL_EPSILON) { controller-sensitivity 1.0f / controller-model_slope; controller-dynamic_kp controller-config.kp_factor * controller-sensitivity; if ((controller-config.ki_tau_factor HEATER_CONTROL_EPSILON) (controller-config.tau_s HEATER_CONTROL_EPSILON)) { controller-dynamic_ki controller-dynamic_kp / (controller-config.ki_tau_factor * controller-config.tau_s); } else { /* 参数无效时暂时关闭积分 */ controller-dynamic_ki 0.0f; } } else { /* * 模型斜率为零或负数说明拟合曲线在该工作点异常。 * 为防止除零或增益反向关闭PI修正。 */ controller-sensitivity 0.0f; controller-dynamic_kp 0.0f; controller-dynamic_ki 0.0f; } /* 动态比例项 */ controller-p_term controller-dynamic_kp * controller-error; /* 动态积分项 */ proposed_integral controller-integral controller-dynamic_ki * controller-error * controller-config.sample_time_s; if (!isfinite(proposed_integral)) { HeaterControl_Reset(controller); return 0.0f; } proposed_integral HeaterControl_Clamp( proposed_integral, controller-config.integral_min, controller-config.integral_max); unsaturated_output controller-feedforward controller-p_term proposed_integral; if (!isfinite(unsaturated_output)) { HeaterControl_Reset(controller); return 0.0f; } /* 条件积分抗饱和。 */ if (!((unsaturated_output controller-config.output_max controller-error 0.0f) || (unsaturated_output controller-config.output_min controller-error 0.0f))) { controller-integral proposed_integral; } controller-i_term controller-integral; unsaturated_output controller-feedforward controller-p_term controller-i_term; controller-output HeaterControl_Clamp( unsaturated_output, controller-config.output_min, controller-config.output_max); return controller-output; } void HeaterControl_SetDynamicPI( HeaterControl_t *controller, float kp_factor, float ki_tau_factor, float tau_s) { if (controller NULL) { return; } if ((!isfinite(kp_factor)) || (kp_factor 0.0f)) { kp_factor 0.0f; } if ((!isfinite(ki_tau_factor)) || (ki_tau_factor 0.0f)) { ki_tau_factor 0.0f; } if ((!isfinite(tau_s)) || (tau_s 0.0f)) { tau_s 0.0f; } controller-config.kp_factor kp_factor; controller-config.ki_tau_factor ki_tau_factor; controller-config.tau_s tau_s; } void HeaterControl_SetFeedforwardCoefficients( HeaterControl_t *controller, float a, float b, float c) { if (controller NULL) { return; } if ((!isfinite(a)) || (!isfinite(b)) || (!isfinite(c))) { return; } controller-config.a a; controller-config.b b; controller-config.c c; }heater_control.h#ifndef HEATER_CONTROL_H #define HEATER_CONTROL_H #ifdef __cplusplus extern C { #endif #include stdint.h /* * 前馈模型 * * T a * P b * P^2 c * * T稳态温度单位 °C * P加热占空比范围通常为 0~100 * * 控制器会根据目标温度反解前馈占空比 Pff * 然后叠加 PI 修正 * * Pout Pff Kp * error integral */ typedef struct { /* 前馈拟合系数T aP bP^2 c */ float a; float b; float c; /* * 动态PI参数 * * kp_factor * Kp kp_factor * S * 推荐初始范围0.2 ~ 0.5 * * ki_tau_factor * Ki Kp / (ki_tau_factor * tau_s) * 推荐初始范围1.0 ~ 2.0 * * tau_s * 热系统时间常数单位秒 */ float kp_factor; float ki_tau_factor; float tau_s; /* 每次调用 HeaterControl_Update() 的时间间隔单位秒 */ float sample_time_s; /* 最终输出限幅通常设置为0和100 */ float output_min; float output_max; /* 积分项限幅 */ float integral_min; float integral_max; } HeaterControl_Config_t; typedef struct { HeaterControl_Config_t config; float integral; /* 以下变量便于调试和OLED显示 */ float error; /* 当前工作点计算出的动态参数便于调试 */ float model_slope; float sensitivity; float dynamic_kp; float dynamic_ki; float feedforward; float p_term; float i_term; float output; } HeaterControl_t; void HeaterControl_Init( HeaterControl_t *controller, const HeaterControl_Config_t *config); void HeaterControl_Reset(HeaterControl_t *controller); float HeaterControl_CalculateFeedforward( const HeaterControl_t *controller, float target_temperature); float HeaterControl_Update( HeaterControl_t *controller, float target_temperature, float measured_temperature); void HeaterControl_SetDynamicPI( HeaterControl_t *controller, float kp_factor, float ki_tau_factor, float tau_s); void HeaterControl_SetFeedforwardCoefficients( HeaterControl_t *controller, float a, float b, float c); #ifdef __cplusplus } #endif #endif /* HEATER_CONTROL_H */

最新新闻

日新闻

周新闻

月新闻