# 储能 EMS 的 PID 功率调节落地：从原理到工程实现（含死区、分离阈值、限幅、稳态启用）

## 1\. 写在前面：这不是“教科书 PID”，而是“能跑的 PID”

在储能系统里，EMS 常见的目标是：让并网点/总柜电表功率尽可能跟随计划功率或限值（充电、放电、削峰、跟随负载等）。但真实系统里你会遇到：

*   电表采样噪声、刷新慢
    
*   PCS/电池执行响应慢、非线性、限幅
    
*   外部扰动频繁（负载突变、PV 波动、网侧波动）
    

如果把目标功率直接下发给执行端，常见结果就是：**误差大、输出抖、甚至振荡**。  
因此工程里引入了“PID 调节”，但为了适配现场约束，我们更关心的是：

*   能否**网页在线调参**
    
*   能否**安全启停**（不带积分历史冲击）
    
*   能否**抗噪、抗扰、限幅保护**
    
*   能否**快速定位问题**（日志/指标可观测）
    

本文结合一套实际工程代码，讲清楚它的整体思路与关键实现。

* * *

## 2\. 核心目标：功率闭环要控制什么？

先定义闭环的三个核心量：

*   **目标功率** (P\_{target})：来自业务逻辑（计划/功率限制/可用充放能力等）
    
*   **反馈功率** (P\_{meter})：来自电表/总柜表（工程里用柜内表功率）
    
*   **误差** (e = P\_{target} - P\_{meter})
    

闭环的目的不是“让 PCS 输出等于某个数”，而是更贴近业务：  
**让电表侧的实际功率更接近目标功率**（这也是防逆流/防过载类策略更看重的量）。

* * *

## 3\. 方案选择：增量式 PI + 补偿项（Compensation）

很多项目 PID 做不稳，不是算法错，而是“工程形态”没选对。这里采用的是非常典型、也很工程化的结构：

*   不用位置式 PID（直接算输出）
    
*   用 **增量式 PI** 计算“补偿增量”
    
*   用一个 **补偿量 compensation** 累加变化
    
*   最终输出 = 目标功率 + 补偿量
    

核心关系：

*   (e = P\_{target} - P\_{meter})
    
*   ( \\Delta P = K\_p \\cdot (e - e\_{prev}) + K\_i \\cdot e )
    
*   (comp \\leftarrow comp + \\Delta P)
    
*   (P\_{out} = P\_{target} + comp)
    

为什么这样做更适合现场？

*   输出变化天然平滑（增量式）
    
*   易加“工程约束”（补偿限幅、清零条件、稳态启用）
    
*   目标变化期可以选择“不让补偿乱动”（很关键）
    

工程实现位于 `Power_Control_Loop()`（`ems_management.c`），核心计算就是上述逻辑。

* * *

## 4\. 参数体系：把 PID 做成“可在线调参”的产品能力

能不能在线调参，决定了 PID 是“实验室 demo”还是“现场可维护能力”。

工程里把参数拆成两类：

### 4.1 PID 参数（Parameters）

包括：

*   `Kp / Ki`
    
*   `Dead Band`（死区）
    
*   `Proportional Separation Threshold`（比例项分离阈值）
    
*   `Integral Separation Threshold`（积分项分离阈值）
    

并且**充电与放电分两套**参数（因为系统响应方向、动态特性常不同）：

*   `stPIDChgParams`
    
*   `stPIDDischgParams`
    

结构体定义在 `blob_model.h`：

*   `PID_Params`
    
*   `PID_Ctx`（里面包含两套 PID\_Params）
    

### 4.2 PID 上下文（Context / State）

运行时状态包括：

*   `prev_error / prev_prev_error`
    
*   `compensation`
    
*   `prev_target`
    
*   “目标稳态计数/阈值”
    

为什么要把状态独立出来？  
因为**启停切换时必须可清零**，否则积分残留会让系统“突然一脚油门”。

* * *

## 5\. 工程化增强：现场稳定性来自这些“小细节”

下面这些机制，决定了“能不能跑稳”。

### 5.1 使能条件：只在合适工况启用 PID

PID 只在“充电/放电且使能”的条件下才生效；否则：

*   输出直接等于原目标功率（不加补偿）
    
*   清空 PID 历史状态（误差、补偿、稳态计数等）
    

这能避免“未启用时积分累积，重新启用瞬间冲击”。

### 5.2 目标稳态启用：目标不稳定时，先别调

工程里做了一个非常实用的策略：

*   如果目标变化超过阈值：稳态计数清零
    
*   目标持续稳定一段时间后：才允许 PID 调节进入有效区
    
*   未进入稳态：误差置零 + 清历史误差 + 关闭某些高灵敏度逻辑
    

直觉解释：  
**目标本身在变时，误差并不代表“控制偏差”，而更像“目标变化的瞬态”**。  
这时候让 PID 去追，只会引入更多抖动。

### 5.3 死区 Dead Band：抑制小误差抖动

当误差很小时（可能主要是噪声），直接当作 0，避免输出不断微调抖动。

### 5.4 分离阈值：控制器的“安全阀”

*   误差太大时，限制比例/积分项的参与
    
*   特别是积分分离：避免积分饱和导致长时间偏置
    

这是很多现场 PID 之所以稳的原因：  
**不是因为 PID 精准，而是因为它知道什么时候该“收手”。**

### 5.5 双限幅：补偿限幅 + 输出限幅

*   `compensation` 有自己的上限（防越积越大）
    
*   最终 `raw_output` 也有上下限（防下发越界）
    

这两层限幅属于“工程必备”，是对执行端与系统安全的保护。

### 5.6 清补偿阈值：小目标功率时回到干净状态

当目标功率接近 0 时，补偿归零，避免零点漂移长期残留。

* * *

## 6\. 代码结构怎么组织？（读代码的正确路径）

如果你要在工程里快速理解 PID 功能，建议按这个路径：

1.  **数据模型/结构体**：`blob_model.h`
    
    *   `PID_Params`
        
    *   `PID_Ctx`
        
    *   `POWER_CTRL_ENABLE_STATE`
        
    *   `EMSMgmtData` 中 `stPidCtx`、`emPIDEnableState`、PID 输出变量等
        
2.  **控制主回路**：`ems_management.c`
    
    *   `Power_Control_Loop()`：核心算法、约束策略、输出写回
        
3.  **参数入口（网页配置 → DPR）**：`config/System.sql`
    
    *   PID 相关 SettingParameters：使能、Kp/Ki、阈值、限幅、稳态阈值等
        
4.  **使能/状态同步**：`st_batt_sampler.c`（事件更新）
    
    *   对 `PID Control Enable`、`High Sensitivity Enable` 的读取与状态更新
        

* * *

## 7\. 调试与验证：现场怎么判断“PID 在起作用且没副作用”

建议按以下 checklist 排查（非常实用）：

*   **使能是否正确**：当前工况是充电/放电？使能状态是否允许？
    
*   **稳态是否达标**：目标是否稳定？稳态计数是否达到阈值？
    
*   **补偿是否合理**：`compensation` 是小幅变化，还是经常顶到限幅？
    
*   **输出是否频繁限幅**：若频繁打到最大/最小输出，通常不是 PID 参数问题，而是上层目标或限值策略不合理
    
*   **调参顺序建议**：
    
    *   先调 `Dead Band` 抑制抖动
        
    *   再调 `Ki` 消除稳态误差（小步慢调）
        
    *   最后调 `Kp` 改善响应速度（防振荡）
        
    *   分离阈值更多是“保护参数”，别设太小导致 PID 总被关断
        

* * *

## 8\. 常见坑与经验（建议直接贴到项目 Wiki）

1.  **目标变化太快时不要 PID**：先稳态判断，再介入补偿
    
2.  **积分不是越大越好**：Ki 太大会“慢性振荡/累积偏置”
    
3.  **死区不是偷懒**：它是对噪声与小功率抖动的工程解
    
4.  **限幅一定要双层**：补偿限幅保护“积累”，输出限幅保护“执行端”
    
5.  **启停一定要清状态**：否则现场会出现“突然冲一下”的玄学问题
    

* * *

## 9\. 结语：把控制算法做成“可维护能力”

真正有价值的 PID，不是公式写对，而是具备：

*   可配置（网页在线调参）
    
*   可诊断（日志、关键状态可观测）
    
*   可保护（死区、分离阈值、限幅）
    
*   可安全启停（状态可清零）
    
*   可工程扩展（充/放分参数、不同工况策略）
    

这套实现的意义就在于：把“控制理论”落成“可运营、可维护、可交付”的产品能力。

* * *
