3. 蓄电池荷电状态区间约束下的充电启停逻辑设计

为什么需要带迟滞的SOC区间约束

浮标充电控制器如果只用单一阈值判断(例如SOC低于80%就开充、高于80%就停充),在日照波动或负载脉冲时,SOC会在阈值附近来回穿越,导致充电开关频繁动作。频繁切换带来两类损耗:一是继电器/MOSFET开关损耗与触点磨损,二是每次启停都伴随控制逻辑与MPPT重新寻优的瞬态损失。工程上常用双阈值迟滞把单一阈值拆成“启动下限SOC_L”和“停止上限SOC_H”,中间形成回差带,SOC必须在带内走完一个完整行程才会触发下一次切换。

本节设计一个三状态充电启停状态机:IDLE(充电关闭,等待SOC跌到SOC_L)、CHARGING(充电开启,等待SOC升到SOC_H)、FAULT_HOLD(异常保护,SOC过低或过高时强制进入并锁定,需人工或延时复位)。状态迁移只由SOC与两个阈值的关系决定,并附加最小驻留时间约束,进一步抑制抖动。

SOC 回差带(迟滞区间) SOC_H = 90%(停止充电) SOC_L = 55%(启动充电) 回差带 ΔSOC = 35% 充电上升 放电下降 充电启停状态机 IDLE 充电关闭 · 等待SOC≤SOC_L CHARGING 充电开启 · 等待SOC≥SOC_H FAULT_HOLD SOC越限/超时 · 锁定 SOC ≤ SOC_L 且驻留≥T_min SOC ≥ SOC_H SOC < 20% SOC > 98% 复位 切换判据:仅当 SOC 穿越对应阈值且满足最小驻留时间 T_min 时才迁移;回差带 ΔSOC 越大,切换越少但可用容量利用率下降。 能量损耗估算:E_loss ≈ N_switch × (E_sw + E_mppt_reinit),N_switch 为统计周期内启停次数。

状态机实现(Python,可直接运行)

下面实现一个纯逻辑状态机类,输入为逐时SOC序列(由能量流模型给出),输出为充电开关指令序列、切换次数与能量损耗估算。阈值与最小驻留时间作为构造参数,便于后续做参数扫描。

from dataclasses import dataclass, field

@dataclass
class ChargeFSM:
    soc_low: float = 0.55      # 启动充电下限
    soc_high: float = 0.90     # 停止充电上限
    soc_fault_low: float = 0.20
    soc_fault_high: float = 0.98
    t_min: int = 2             # 最小驻留步数(小时)
    e_switch: float = 0.002    # 单次开关损耗 kWh
    e_mppt: float = 0.001      # 单次MPPT重寻优损耗 kWh

    state: str = "IDLE"
    dwell: int = 0
    switches: int = 0
    log: list = field(default_factory=list)

    def step(self, soc: float) -> bool:
        """输入当前SOC,返回充电开关是否开启"""
        prev = self.state
        self.dwell += 1

        if self.state == "FAULT_HOLD":
            # 仅当SOC回到安全区间才复位
            if self.soc_fault_low <= soc <= self.soc_fault_high:
                self.state = "IDLE"
                self.dwell = 0
        elif self.state == "IDLE":
            if soc < self.soc_fault_low:
                self.state = "FAULT_HOLD"; self.dwell = 0
            elif soc <= self.soc_low and self.dwell >= self.t_min:
                self.state = "CHARGING"; self.dwell = 0
        elif self.state == "CHARGING":
            if soc > self.soc_fault_high:
                self.state = "FAULT_HOLD"; self.dwell = 0
            elif soc >= self.soc_high and self.dwell >= self.t_min:
                self.state = "IDLE"; self.dwell = 0

        if self.state != prev:
            self.switches += 1
        self.log.append((soc, self.state))
        return self.state == "CHARGING"

    def energy_loss(self) -> float:
        return self.switches * (self.e_switch + self.e_mppt)

连续阴雨与强日照交替场景验证

构造一段48小时教学示例SOC序列:前12小时连续阴雨(SOC从0.85缓降至0.50),随后12小时强日照(SOC回升至0.92),再12小时阴雨(降至0.52),最后12小时强日照(升至0.91)。用同一序列分别跑“单阈值策略”和“迟滞状态机”,对比切换次数。

import numpy as np

# 教学示例SOC序列(假设值,非实测)
soc_seq = np.concatenate([
    np.linspace(0.85, 0.50, 12),   # 阴雨
    np.linspace(0.50, 0.92, 12),   # 强日照
    np.linspace(0.92, 0.52, 12),   # 阴雨
    np.linspace(0.52, 0.91, 12),   # 强日照
])

# 单阈值基准:SOC<0.80开充,>=0.80停充
def single_threshold(seq, thr=0.80):
    on, n = False, 0
    for s in seq:
        new = s < thr
        if new != on: n += 1
        on = new
    return n

fsm = ChargeFSM(soc_low=0.55, soc_high=0.90, t_min=2)
for s in soc_seq:
    fsm.step(s)

print("单阈值切换次数:", single_threshold(soc_seq))
print("迟滞状态机切换次数:", fsm.switches)
print("能量损耗估算(kWh):", round(fsm.energy_loss(), 4))

预期现象(由给定输入直接推出):单阈值策略在SOC穿越0.80附近时,因序列在0.80上下多次往返,切换次数明显偏高;迟滞状态机因回差带35%且带最小驻留约束,只在SOC真正走完“≤0.55→≥0.90”或反向行程时才切换,切换次数显著下降。具体数值取决于序列形状,运行代码即可得到本例序列下的实际计数。

调参建议:回差带ΔSOC过小(如5%)抑制抖动效果有限;过大(如50%)会导致电池长期工作在浅充浅放区间,可用容量利用率下降。工程上常取ΔSOC为额定容量的20%~35%,并结合浮标日均负载与光伏峰值功率做校核。
FAULT_HOLD的复位条件必须明确:若SOC因传感器故障持续报低,状态机会永久锁定在FAULT_HOLD,此时应叠加超时复位或人工干预通道,避免浮标“假性停机”。

切换次数与损耗统计表(示例输出格式)

策略阈值设置切换次数能量损耗估算(kWh)判读
单阈值基准0.80 单点由代码输出N×0.003抖动明显,需改进
迟滞状态机0.55 / 0.90由代码输出N×0.003切换受控,可接受

验证通过条件:在给定交替场景下,迟滞状态机的切换次数应低于单阈值基准,且不出现同一小时内反复启停;若出现,检查T_min是否被正确累加、阈值是否被误设为相等。