From cd196fffb5efaa6fc12b1911525d78eb5c1166a7 Mon Sep 17 00:00:00 2001 From: Byons Date: Thu, 30 Jul 2026 15:23:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E5=8E=8B=E5=8A=9B=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=88=B0VX5=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- InkJetPrinter.cpp | 391 ++++++++++++---------------------------------- 1 file changed, 99 insertions(+), 292 deletions(-) diff --git a/InkJetPrinter.cpp b/InkJetPrinter.cpp index f2967b7..64606ff 100644 --- a/InkJetPrinter.cpp +++ b/InkJetPrinter.cpp @@ -347,100 +347,6 @@ struct _pid{ float integral_max; //积分限幅(根据实际调整) }pid; -// 双模式PID参数配置 -struct DualModePIDParams { - // 快速收敛模式(大偏差) - float fast_Kp; - float fast_Ki; // 通常为0 - float fast_Kd; - float fast_maxDelta; // 单次调整限幅 - int fast_cycle; // 调整周期(单位:100ms) - - // 精准稳压模式(小偏差) - float precision_Kp; - float precision_Ki; - float precision_Kd; - float precision_maxDelta; - int precision_cycle; - float integral_limit; // 积分限幅 - int stable_threshold; // 稳定判定阈值 - int stable_count_req; // 需要稳定的次数 - - // 模式切换阈值 - float switch_threshold; // 切换到精准模式的误差阈值 - float switch_hysteresis; // 模式切换滞后带宽,避免反复切换 - float deadzone; // 死区:误差小于此值时不调整 -}; - -// 无分流管设备参数 -static DualModePIDParams params_no_reclaim = { - // 快速模式:保持不变 - 0.06f, // fast_Kp - 0.0f, // fast_Ki - 0.02f, // fast_Kd - 8.0f, // fast_maxDelta - 10, // fast_cycle (1000ms) - - // 精准模式:降低Kp,大幅增加Kd抑制振荡 - 0.004f, // precision_Kp (从0.010降到0.004,降低60%,减少过调) - 0.0015f, // precision_Ki (从0.002降到0.0015,降低25%,减缓积分) - 0.025f, // precision_Kd (从0.005增加到0.025,提高5倍,抑制振荡) - 1.0f, // precision_maxDelta (从1.5降到1.0,更平滑) - 15, // precision_cycle (1500ms) - 80.0f, // integral_limit (从100降到80,防止积分饱和) - 8, // stable_threshold (保持8) - 8, // stable_count_req (从15降到8,更快响应) - - 80.0f, // switch_threshold - 15.0f, // switch_hysteresis - 5.0f // deadzone (从15降到5,缩小不动区,减少振荡幅度) -}; - -// 有分流管设备参数 -static DualModePIDParams params_with_reclaim = { - // 快速模式 - 0.08f, // fast_Kp - 0.0f, // fast_Ki - 0.03f, // fast_Kd - 10.0f, // fast_maxDelta - 10, // fast_cycle (1000ms) - - // 精准模式:降低Kp,大幅增加Kd抑制振荡 - 0.006f, // precision_Kp (从0.015降到0.006,降低60%,减少过调) - 0.002f, // precision_Ki (从0.003降到0.002,降低33%,减缓积分) - 0.035f, // precision_Kd (从0.008增加到0.035,提高4倍,抑制振荡) - 1.2f, // precision_maxDelta (从2.0降到1.2,更平滑) - 15, // precision_cycle (1500ms) - 100.0f, // integral_limit (从120降到100,防止积分饱和) - 12, // stable_threshold (保持12) - 8, // stable_count_req (从12降到8,更快响应) - - 80.0f, // switch_threshold - 15.0f, // switch_hysteresis - 8.0f // deadzone (从18降到8,缩小不动区,减少振荡幅度) -}; - -// PID控制器状态 -struct PIDController { - float integral; // 积分累积 - float last_actual; // 上次实际值(用于微分先行) - float last_output; // 上次输出值(用于变化率限制) - int adjust_skip; // 周期计数器 - int stable_count; // 稳定计数器 - bool in_precision_mode; // 当前是否在精准模式 - - void reset() { - integral = 0.0f; - last_actual = 0.0f; - last_output = 0.0f; - adjust_skip = 0; - stable_count = 0; - in_precision_mode = false; - } -}; - -static PIDController pid_ctrl; - void PID_init(){ pid.SetSpeed=0.0; @@ -452,109 +358,6 @@ void PID_init(){ pid.Kp=0.21; //0.25 pid.Ki=0.006; //0.015 pid.Kd=0.0; - - // 重置双模式PID控制器 - pid_ctrl.reset(); -} - -// 微分先行PID计算函数 -// derivative_on_measurement: 只对实际值求微分,避免设定值突变冲击 -float DualModePID_Calculate(float setpoint, float actual, const DualModePIDParams& params, - PIDController& ctrl, float u_current, float u_min, float u_max) { - float error = setpoint - actual; - float abs_error = (error < 0) ? -error : error; - - // 判断当前应该使用哪种模式 - bool use_precision = (abs_error <= params.switch_threshold); - - float Kp, Ki, Kd, max_delta; - - if (use_precision) { - // 精准稳压模式 - Kp = params.precision_Kp; - Ki = params.precision_Ki; - Kd = params.precision_Kd; - max_delta = params.precision_maxDelta; - ctrl.in_precision_mode = true; - } else { - // 快速收敛模式 - Kp = params.fast_Kp; - Ki = params.fast_Ki; // 通常为0 - Kd = params.fast_Kd; - max_delta = params.fast_maxDelta; - ctrl.in_precision_mode = false; - - // 快速模式下清零积分,避免积分累积 - ctrl.integral = 0.0f; - } - - // 比例项 - float p_term = Kp * error; - - // 积分项(带抗饱和和变速积分) - float i_term = 0.0f; - if (use_precision && Ki > 0.0f) { - // 积分分离:只在精准模式下累积 - // 积分抗饱和:转速到达限位时停止累积 - bool at_upper_limit = (u_current >= u_max - 1.0f) && (error > 0); - bool at_lower_limit = (u_current <= u_min + 1.0f) && (error < 0); - - if (!at_upper_limit && !at_lower_limit) { - // 变速积分:误差越大,积分增益越小,防止积分饱和 - float ki_adjust = 1.0f; - if (abs_error > 30.0f) { - ki_adjust = 0.3f; // 大误差时降低积分作用 - } else if (abs_error > 15.0f) { - ki_adjust = 0.6f; // 中等误差时适度降低 - } - - ctrl.integral += error * ki_adjust; - - // 积分限幅 - if (ctrl.integral > params.integral_limit) { - ctrl.integral = params.integral_limit; - } else if (ctrl.integral < -params.integral_limit) { - ctrl.integral = -params.integral_limit; - } - } - - i_term = Ki * ctrl.integral; - } - - // 微分先行:只对实际值求微分,避免设定值突变时微分项冲击 - // 传统微分:Kd * (error - error_last) = Kd * [(set-act) - (set_last-act_last)] - // 微分先行:-Kd * (actual - actual_last) 当设定值不变时两者等价,但设定值变化时微分先行无冲击 - float d_term = 0.0f; - if (Kd > 0.0f) { - float derivative = -(actual - ctrl.last_actual); // 负号因为是对实际值求导 - d_term = Kd * derivative; - ctrl.last_actual = actual; - } - - // PID输出 - float delta_u = p_term + i_term + d_term; - - // 输出限幅 - if (delta_u > max_delta) { - delta_u = max_delta; - } else if (delta_u < -max_delta) { - delta_u = -max_delta; - } - - // 输出变化率限制(精准模式下更平滑) - if (use_precision && ctrl.last_output != 0.0f) { - float max_rate = max_delta * 0.5f; // 最大变化率为max_delta的50% - float output_change = delta_u - ctrl.last_output; - if (output_change > max_rate) { - delta_u = ctrl.last_output + max_rate; - } else if (output_change < -max_rate) { - delta_u = ctrl.last_output - max_rate; - } - } - - ctrl.last_output = delta_u; - - return delta_u; } static float u = 1.0; @@ -584,7 +387,7 @@ float PID_realize2(float speed, float act){ pid.err = pid.SetSpeed - pid.ActualSpeed; float delta_u = pid.Kp * (pid.err - pid.err_last) - + pid.Ki * pid.err; + + pid.Ki * pid.err; // 自适应非对称限幅: // 压力严重偏高(err < -30):允许快速减速,限制慢速加速 @@ -617,14 +420,12 @@ void InkJetPrinter::pressureAdj() m_stableCount = 0; for(int i = 0; i < 10; i++) m_pressureBuf[i] = 0; m_bufIdx = 0; - pid_ctrl.reset(); // 重置双模式PID控制器 sysPara->devOp->WriteMoterPump(); return; } if(!sysPara->para.gearPumpEnable) { - // 非使能状态保留旧PID逻辑 if(u > 0 || ((sysPara->para.pressure - sysPara->status.PRESSURE) > 0)) u = PID_realize((float)sysPara->para.pressure, (float)sysPara->status.PRESSURE); sysPara->para.gearPumpValue = (u > 0) ? (unsigned short)u : 0; @@ -634,11 +435,6 @@ void InkJetPrinter::pressureAdj() return; } - // 根据是否有分流管选择参数集 - const DualModePIDParams& params = sysPara->para.flagOfReclaimPumEnable - ? params_with_reclaim - : params_no_reclaim; - int maxValue = sysPara->para.flagOfReclaimPumEnable ? 420 : 800; if(maxValue < 60) maxValue = 60; @@ -651,8 +447,9 @@ void InkJetPrinter::pressureAdj() int actual = sysPara->status.PRESSURE; int target = (int)sysPara->para.pressure; + int cur; - // 每次进入都更新滑动平均缓冲 + // 每次进入都更新滑动平均缓冲(软启动用瞬时值,闭环用平均值) m_pressureBuf[m_bufIdx] = actual; m_bufIdx = (m_bufIdx + 1) % 10; int avgPressure = 0; @@ -661,6 +458,7 @@ void InkJetPrinter::pressureAdj() // --------------------------------------------------------------- // 硬保护:压力超过目标+50,立即强制减速,优先级最高 + // 覆盖自检、软启动、闭环所有阶段 // --------------------------------------------------------------- if(actual > target + 50) { @@ -669,132 +467,142 @@ void InkJetPrinter::pressureAdj() if(delta < -15.0f) delta = -15.0f; u += delta; clampU(); - - // 超调时清零积分,避免积分饱和 - pid_ctrl.integral = 0.0f; - + m_lastPressure = actual; sysPara->para.gearPumpValue = (unsigned short)(int)u; sysPara->devOp->WriteMoterPump(); return; } // --------------------------------------------------------------- - // 双模式PID控制:根据误差大小自动切换快速/精准模式 - // 快速模式:大偏差快速收敛,周期1000ms - // 精准模式:小偏差精准稳压,周期1500ms,等待压力稳定后调整 - // 带死区和模式切换滞后,避免频繁调整和模式抖动 + // 软启动:自适应步长,每 800ms 一次 + // 自检期间也走此逻辑(硬保护兜底),自检完成后重置重新收敛 + // 软启动阶段不做压力稳定检测,优先快速收敛 // --------------------------------------------------------------- - - // 计算误差(统一使用平均压力,避免瞬时噪声) - float error_avg = (float)(target - avgPressure); - float abs_error_avg = (error_avg < 0) ? -error_avg : error_avg; - - // 死区控制:误差很小时不调整,避免频繁微调 - // 使用平均压力判断,与后续调整逻辑一致 - // 优化:在死区内不清零积分,保持积分补偿效果 - if(abs_error_avg < params.deadzone) + if(!m_rampDone) { - // 在死区内:保持积分不变,不再清零 - // 只有误差超过死区时才会重新累积或调整积分 - pid_ctrl.adjust_skip = 0; - pid_ctrl.stable_count = 0; + if(++m_adjSkip < 8) + { + sysPara->para.gearPumpValue = (unsigned short)(int)u; + sysPara->devOp->WriteMoterPump(); + return; + } + m_adjSkip = 0; - sysPara->para.gearPumpValue = (unsigned short)(int)u; + int err = target - actual; + + if(err < 0) + { + // 超压:小步减速 + u -= 1.0f; + } + else + { + // 自适应步长:差得远步子大,差得近步子小 + float step; + if (err > 200) step = 5.0f; + else if(err > 100) step = 3.0f; + else if(err > 50) step = 2.0f; + else step = 1.0f; + u += step; + } + + clampU(); + + // 转速上限保护 + if(sysPara->para.flagOfReclaimPumEnable && + sysPara->status.gearPumpRunSpeed >= maxSpeed) + { + u -= 3.0f; + clampU(); + } + + cur = (int)u; + + // 进入闭环条件:压力已稳定在目标±50以内 + if(sysPara->status.selfCheckDone && err >= -50 && err <= 50) + { + m_rampDone = true; + m_adjSkip = 0; + } + + sysPara->para.gearPumpValue = (unsigned short)cur; sysPara->devOp->WriteMoterPump(); return; } - // 模式切换逻辑(带滞后,避免在阈值附近反复切换) - bool use_precision; - if(pid_ctrl.in_precision_mode) - { - // 当前在精准模式,需要误差大于阈值+滞后才切回快速模式 - use_precision = (abs_error_avg <= params.switch_threshold + params.switch_hysteresis); - } - else - { - // 当前在快速模式,误差小于阈值即切到精准模式 - use_precision = (abs_error_avg <= params.switch_threshold); - } - - int cycle = use_precision ? params.precision_cycle : params.fast_cycle; - - // 周期控制 - if(++pid_ctrl.adjust_skip < cycle) - { - sysPara->para.gearPumpValue = (unsigned short)(int)u; - sysPara->devOp->WriteMoterPump(); - return; - } - - // 精准模式需要等待压力稳定 - if(use_precision) + // --------------------------------------------------------------- + // 闭环:等压力稳定后再调速,自适应管路响应时间 + // + // 每100ms采样一次,连续3次压力变化<15认为稳定,才执行调速 + // 最长等待5s(50次)兜底,防止管路持续抖动导致永远不调 + // + // 调速后立即重置稳定计数,重新等待下一次稳定 + // + // 分区Kp:误差大步子大,误差小步子小 + // |err| > 100 → Kp=0.05,限幅±5 + // |err| 50~100→ Kp=0.04,限幅±4 + // |err| < 50 → Kp=0.03,限幅±3 + // --------------------------------------------------------------- { // 稳定检测用平均值,滤掉传感器噪声 int pressureChange = avgPressure - m_lastPressure; if(pressureChange < 0) pressureChange = -pressureChange; m_lastPressure = avgPressure; + m_adjSkip++; // 复用为超时计数 - bool stable = (pressureChange < params.stable_threshold); + bool stable = (pressureChange < 10); + bool timeout = (m_adjSkip >= 50); // 5s兜底 if(stable) - pid_ctrl.stable_count++; + m_stableCount++; else - pid_ctrl.stable_count = 0; + m_stableCount = 0; - // 如果还未稳定,继续等待 - if(pid_ctrl.stable_count < params.stable_count_req) + bool readyToAdj = (m_stableCount >= 15) || timeout; + + if(!readyToAdj) { sysPara->para.gearPumpValue = (unsigned short)(int)u; sysPara->devOp->WriteMoterPump(); return; } - // 压力已稳定,执行调速,重置计数 - pid_ctrl.adjust_skip = 0; - pid_ctrl.stable_count = 0; - pid_ctrl.in_precision_mode = true; + // 平均压力已稳定(或超时兜底),执行调速,重置计数 + m_adjSkip = 0; + m_stableCount = 0; - // 精准模式使用平均压力计算 - float delta_u = DualModePID_Calculate((float)target, (float)avgPressure, params, - pid_ctrl, u, 60.0f, (float)maxValue); + // 误差用平均值计算,避免瞬时噪声影响调速幅度 + float absErr = (float)(target - avgPressure); + if(absErr < 0.0f) absErr = -absErr; + + float kp, capDelta; + if(absErr > 100.0f) { kp = 0.05f; capDelta = 5.0f; } + else if(absErr > 50.0f) { kp = 0.04f; capDelta = 4.0f; } + else { kp = 0.03f; capDelta = 3.0f; } + + float err = (float)(target - avgPressure); + float delta_u = kp * err; + + if(delta_u > capDelta) delta_u = capDelta; + if(delta_u < -capDelta) delta_u = -capDelta; // 转速上限软限制:转速已达上限时禁止继续加速 if(sysPara->para.flagOfReclaimPumEnable && - sysPara->status.gearPumpRunSpeed >= maxSpeed) + sysPara->status.gearPumpRunSpeed >= maxSpeed) { if(delta_u > 0.0f) delta_u = 0.0f; } u += delta_u; clampU(); + cur = (int)u; + + sysPara->para.gearPumpValue = (unsigned short)cur; + sysPara->devOp->WriteMoterPump(); } - else - { - // 快速模式:立即调整,重置计数 - pid_ctrl.adjust_skip = 0; - pid_ctrl.stable_count = 0; - pid_ctrl.in_precision_mode = false; - - // 快速模式也使用平均压力,避免瞬时噪声导致过度调整 - float delta_u = DualModePID_Calculate((float)target, (float)avgPressure, params, - pid_ctrl, u, 60.0f, (float)maxValue); - - // 转速上限软限制 - if(sysPara->para.flagOfReclaimPumEnable && - sysPara->status.gearPumpRunSpeed >= maxSpeed) - { - if(delta_u > 0.0f) delta_u = 0.0f; - } - - u += delta_u; - clampU(); - } - - sysPara->para.gearPumpValue = (unsigned short)(int)u; - sysPara->devOp->WriteMoterPump(); } + static bool firstTime = true; extern void MySleep(unsigned int msec); extern unsigned char test_flag; @@ -3448,6 +3256,5 @@ void InkJetPrinter::onDlgFileOpen(const QString& filename) return; } - auto ret = loadWorkFileAndPreparePrint(filename); - LogInfo::setRunStatusText(QString("Work file opened: %1, result: %2").arg(filename).arg(ret)); + loadWorkFileAndPreparePrint(filename); }