Files
VX5_KRO_GearPump/齿轮泵压力调节算法移植方案.md
T
2026-06-30 08:21:08 +08:00

13 KiB
Raw Blame History

齿轮泵压力调节算法移植方案

一、概述

oldProject/InkJetPrinter.cpp 中的齿轮泵压力调节算法相比当前项目进行了重大重构,引入了软启动斜坡、闭环稳定性检测、硬保护、分区Kp、滑动平均滤波等机制,显著提升了压力调节的稳定性、响应速度和安全性。本文档详细对比两个版本,并给出移植方案。


二、算法对比分析

2.1 PID_realize2 函数对比

特性 当前项目(旧版) oldProject(新版)
PID类型 位置式PID(输出绝对电压) 增量式PID(输出Δu = Kp×(e[k]-e[k-1]) + Ki×e[k])
Kp 0.8 0.15
Ki 0.02 0.02
Kd 0.5 0.0(无微分项)
积分限幅 integral_max = 12000 无(增量式不需要积分限幅)
自适应限幅 无 有:根据误差方向非对称限幅
输出含义 绝对电压值 增量值(叠加到u上)

新版PID_realize2 自适应限幅逻辑:

err < -30(压力严重偏高): delta_u ∈ [-6, +1]   → 允许快速减速,限制加速
err > 50(压力严重偏低):  delta_u ∈ [-2, +3]   → 允许正常加速,限制减速
-30 ≤ err ≤ 50(接近目标): delta_u ∈ [-3, +3]   → 小步长平稳调节

评价: 新版PID_realize2从位置式改为增量式,避免了积分饱和问题;自适应非对称限幅让超压时能快速降速、欠压时平稳加速,大幅减少了过冲和振荡。


2.2 pressureAdj() 函数对比

特性 当前项目(旧版) oldProject(新版)
调用频率 每2次调用执行1次(50%降频) 每次调用都执行
停止/告警处理 函数末尾清零 函数开头立即返回+状态全复位
硬保护 无 压力>目标+50时强制减速,优先级最高
软启动 无 自适应步长斜坡,800ms周期
闭环稳定性检测 无 等待压力稳定(连续15次变化<10)才调速
滑动平均滤波 无 10点滑动窗口均值
分区Kp 无 三档Kp(0.05/0.04/0.03),误差越大Kp越大
转速上限保护 静态maxValue截断 动态检测:转速达上限时禁止继续加速
状态变量 无(仅static局部变量) 6个成员变量(m_rampDone等)

2.3 pressureAdj() 控制流程对比

当前项目(旧版)流程:

pressureAdj() 被调用
  ├─ printOut%2 != 0 → return(跳过一次)
  ├─ if gearPumpRunStopFlag == false
  │   ├─ if gearPumpEnable && reclaimPumpEnable → PID_realize2(位置式PID)
  │   └─ else if u>0 || 压力偏低 → PID_realize(简单PID)
  ├─ u > 0 → gearPumpValue = u,否则 = 0
  ├─ 限幅800
  ├─ reclaim模式下额外限幅(基于speedAlarm)
  ├─ 最后再检查 gearPumpRunStopFlag / flagOfSpeedAlarm → 清零
  └─ WriteMoterPump()

问题:

  1. 停止/告警状态在函数末尾才处理,中间已经执行了PID计算
  2. 无条件直接输出PID结果,没有压力稳定等待
  3. 没有超压硬保护,压力飙升时响应慢
  4. 回收泵限幅逻辑复杂且硬编码

oldProject(新版)流程:

pressureAdj() 被调用
  ├─ [阶段1] 停止/告警?→ 清零状态 + 复位所有状态变量 + return
  ├─ [阶段2] gearPumpEnable==false → 简单PID + return
  ├─ [阶段3] 硬保护:actual > target+50 → 强制减速 + return
  ├─ [阶段4] 软启动(m_rampDone==false)
  │   ├─ 每8次调用(≈800ms)执行一次
  │   ├─ 自适应步长(err>200→5, >100→3, >50→2, else→1)
  │   ├─ 超压→减1
  │   ├─ 转速上限保护
  │   └─ selfCheckDone && err在±50内 → m_rampDone=true
  ├─ [阶段5] 闭环稳态调节
  │   ├─ 更新滑动平均缓冲
  │   ├─ 稳定检测:连续15次压力变化<10 → 可调速
  │   ├─ 5秒超时兜底
  │   ├─ 分区Kp调速
  │   └─ 转速上限保护
  └─ WriteMoterPump()

优势:

  1. 停止/告警立即处理,状态完全复位
  2. 硬保护优先于一切,安全性最高
  3. 软启动避免开机时压力突变
  4. 闭环等待压力稳定后再调,避免管路抖动导致误调
  5. 滑动平均滤除传感器噪声
  6. 分区Kp实现大误差快调、小误差精调
  7. 转速上限保护防止电机超速

2.4 新增成员变量

oldProject 在 InkJetPrinter.h 中新增了以下成员变量:

bool   m_rampDone     = false;  // 软启动完成标志
int    m_adjSkip      = 0;      // 节拍计数(软启动/超时复用)
int    m_lastPressure = 0;      // 上次采样压力(稳定检测用)
int    m_stableCount  = 0;      // 压力连续稳定计数
int    m_pressureBuf[10] = {};  // 压力滑动平均缓冲(10点=1s)
int    m_bufIdx       = 0;      // 滑动平均写入索引

此外还需要一个 m_lastByHand 变量用于检测手动→自动控制切换边沿,以及 m_lastReclaimMode 用于检测回收泵模式切换。


2.5 timerHandler 中的配套修改

oldProject 在 timerHandler() 中还有以下配套修改,确保状态机正确工作:

  1. 开机/重启时清零:gearPumpValue=0, u=0,确保软启动从0开始爬坡
  2. 自检完成时重置软启动:selfCheckDone 变为1时重置 m_rampDone=false 及所有缓冲
  3. 手动→自动切换时重置:m_lastByHand 边沿检测,切换回自动时重置软启动
  4. 回收泵模式切换时重置:m_lastReclaimMode 边沿检测,模式切换时重置软启动和泄漏保护学习状态

三、移植方案

3.1 需要修改的文件

文件 修改内容
InkJetPrinter.h 新增6个成员变量 + m_lastByHand + m_lastReclaimMode
InkJetPrinter.cpp 替换 PID_realize2 + 替换 pressureAdj + 修改 timerHandler 中的配套逻辑

3.2 详细修改步骤

步骤1:修改 InkJetPrinter.h — 新增成员变量

在 InkJetPrinter 类的 private 区域添加:

// 压力调节算法状态变量
bool   m_rampDone     = false;  // pressureAdj 软启动完成标志
int    m_adjSkip      = 0;      // pressureAdj 节拍计数(软启动/超时复用)
int    m_lastPressure = 0;      // pressureAdj 上次采样压力(稳定检测用)
int    m_stableCount  = 0;      // pressureAdj 压力连续稳定计数
int    m_pressureBuf[10] = {};  // pressureAdj 压力滑动平均缓冲(10点=1s)
int    m_bufIdx       = 0;      // pressureAdj 滑动平均写入索引
bool   m_lastByHand   = false;  // 上一帧手动控制标志(边沿检测用)
bool   m_lastReclaimMode = false; // 上一帧回收泵模式(边沿检测用)

步骤2:替换 PID_realize2 函数

将当前项目的 PID_realize2 替换为 oldProject 的增量式版本。

替换前(当前项目):

float PID_realize2(float speed, float act){
    pid.Kp = 0.8;
    pid.Ki = 0.02;
    pid.Kd = 0.5;
    pid.integral_max = 12000;
    // ... 位置式PID,返回绝对电压
}

替换后(oldProject):

float PID_realize2(float speed, float act){
    // 增量式 PID:Δu = Kp*(e[k]-e[k-1]) + Ki*e[k]
    pid.Kp = 0.15f;
    pid.Ki = 0.02f;
    pid.Kd = 0.0f;
    pid.SetSpeed    = speed;
    pid.ActualSpeed = act;
    pid.err         = pid.SetSpeed - pid.ActualSpeed;

    float delta_u = pid.Kp * (pid.err - pid.err_last)
            + pid.Ki * pid.err;

    // 自适应非对称限幅
    if(pid.err < -30.0f) {
        if(delta_u >  1.0f) delta_u =  1.0f;
        if(delta_u < -6.0f) delta_u = -6.0f;
    } else if(pid.err > 50.0f) {
        if(delta_u >  3.0f) delta_u =  3.0f;
        if(delta_u < -2.0f) delta_u = -2.0f;
    } else {
        if(delta_u >  3.0f) delta_u =  3.0f;
        if(delta_u < -3.0f) delta_u = -3.0f;
    }

    pid.err_last = pid.err;
    return delta_u;
}

注意: 新版 PID_realize2 返回的是增量 delta_u,而不是绝对电压。调用方需要改为 u += PID_realize2(...) 而不是 u = PID_realize2(...)。

步骤3:替换 pressureAdj() 函数

将整个 pressureAdj() 函数替换为 oldProject 版本。由于新旧版本差异较大,建议直接整体替换,不保留旧代码。

关键修改点:

  • 旧版 pressureAdj() 中调用 PID_realize2 时用的是 u = PID_realize2(...)(赋值),新版需要改为 u += PID_realize2(...)(累加),因为新版返回的是增量。
  • 注意当前项目中 sysPara->devOp->WriteMoterPump() 的调用方式一致,无需修改。
  • 当前项目中引用 m_MainWidget 而不是 ui->tabMainWindow,在新版 pressureAdj 中不涉及 UI 控件引用,所以不影响。

实际上 oldProject 的新版 pressureAdj() 已经不再调用 PID_realize2 了,而是采用分阶段手动调速逻辑(软启动 + 分区Kp闭环),所以 PID_realize2 的替换主要是为了完整性,实际在 gearPumpEnable 模式下不会用到。

步骤4:修改 timerHandler() 中的配套逻辑

需要在当前项目的 timerHandler() 中做以下修改:

4.1 开机/重启时清零控制字(在 gearPumpStartFlag 相关逻辑中):

当前项目(约第581-590行):

if(sysPara->para.gearPumpEnable) {
    sysPara->para.gearPumpValue = 150;
    if(tempFlag){
        sysPara->devOp->WriteMoterPump();
        tempFlag=0;
    }
    gearPumpStartFlag = true;
}

改为:

if(sysPara->para.gearPumpEnable) {
    // 确保开机时控制字从 0 开始,让 pressureAdj 软启动从 0 爬坡
    if(sysPara->para.gearPumpValue != 0) {
        sysPara->para.gearPumpValue = 0;
        u = 0.0f;
        sysPara->devOp->WriteMoterPump();
    }
    gearPumpStartFlag = true;
}

4.2 自检完成时重置软启动状态:

在 selfCheckDone = 1 的位置(约第883行),在设置完成后添加:

sysPara->status.selfCheckDone = 1;
// 自检完成后重置软启动,从当前控制字开始,避免突变
u = (float)sysPara->para.gearPumpValue;
m_rampDone     = false;
m_adjSkip      = 0;
m_lastPressure = 0;
m_stableCount  = 0;
for(int i = 0; i < 10; i++) m_pressureBuf[i] = 0;
m_bufIdx = 0;

4.3 手动→自动控制切换时重置软启动:

在 isPumpValveControledByHand 相关逻辑中(约第655行附近),添加边沿检测:

// 在 timerHandler 中增加 static 变量追踪上一帧状态
static bool lastByHand = false;

// 检测手动→自动边沿
if(lastByHand && !sysPara->status.isPumpValveControledByHand)
{
    ControledSwitchFlag = true;
    u              = (float)sysPara->para.gearPumpValue;
    m_rampDone     = false;
    m_adjSkip      = 0;
    m_lastPressure = 0;
    m_stableCount  = 0;
    for(int i = 0; i < 10; i++) m_pressureBuf[i] = 0;
    m_bufIdx = 0;
}
lastByHand = sysPara->status.isPumpValveControledByHand;

4.4 回收泵模式切换时重置软启动:

在 timerHandler 中增加:

static bool lastReclaimMode = false;
if(lastReclaimMode != sysPara->para.flagOfReclaimPumEnable)
{
    lastReclaimMode      = sysPara->para.flagOfReclaimPumEnable;
    gearPumpRunNormalFlag  = false;
    gearPumpRunNormalValue = 0.0f;
    m_rampDone     = false;
    m_adjSkip      = 0;
    m_lastPressure = 0;
    m_stableCount  = 0;
    for(int i = 0; i < 10; i++) m_pressureBuf[i] = 0;
    m_bufIdx = 0;
}

3.3 注意事项

  1. PID_realize 函数保留:PID_realize 和 PID_init 在非齿轮泵模式下仍被使用,保留不变。
  2. static 变量 u:static float u 在旧版和新版中都被使用,保持文件作用域定义。
  3. WriteMoterPump() 调用:确认当前项目中 sysPara->devOp->WriteMoterPump() 接口一致。
  4. 测试验证:移植后需要在以下场景重点测试:
    • 冷启动(从0开始软启动)
    • 热重启(运行中重启)
    • 目标压力突变
    • 回收泵模式切换
    • 手动/自动控制切换
    • 超压保护触发
    • 长时间运行稳定性

四、总结

维度 当前项目 oldProject 提升
超调量 较大(无软启动) 小(软启动+自适应限幅) ★★★★★
稳态精度 一般(直接输出) 高(稳定检测+分区Kp) ★★★★
响应速度 快但不稳 自适应(大误差快调,小误差精调) ★★★★
超压保护 无 有硬保护+转速上限保护 ★★★★★
抗噪声能力 弱(直接采样) 强(10点滑动平均) ★★★★
代码可维护性 一般 好(分阶段清晰) ★★★

移植风险:低。oldProject 的算法已在生产环境验证,且与当前项目共享相同的 SysPara 数据结构。修改集中在 InkJetPrinter.cpp 和 InkJetPrinter.h 两个文件,不涉及跨模块接口变更。