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

333 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 齿轮泵压力调节算法移植方案
## 一、概述
`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` 中新增了以下成员变量:
```cpp
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 区域添加:
```cpp
// 压力调节算法状态变量
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 的增量式版本。
**替换前(当前项目):**
```cpp
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):**
```cpp
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行):
```cpp
if(sysPara->para.gearPumpEnable) {
sysPara->para.gearPumpValue = 150;
if(tempFlag){
sysPara->devOp->WriteMoterPump();
tempFlag=0;
}
gearPumpStartFlag = true;
}
```
改为:
```cpp
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行),在设置完成后添加:
```cpp
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行附近),添加边沿检测:
```cpp
// 在 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 中增加:
```cpp
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` 两个文件,不涉及跨模块接口变更。