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与两个阈值的关系决定,并附加最小驻留时间约束,进一步抑制抖动。
状态机实现(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”或反向行程时才切换,切换次数显著下降。具体数值取决于序列形状,运行代码即可得到本例序列下的实际计数。
切换次数与损耗统计表(示例输出格式)
| 策略 | 阈值设置 | 切换次数 | 能量损耗估算(kWh) | 判读 |
|---|---|---|---|---|
| 单阈值基准 | 0.80 单点 | 由代码输出 | N×0.003 | 抖动明显,需改进 |
| 迟滞状态机 | 0.55 / 0.90 | 由代码输出 | N×0.003 | 切换受控,可接受 |
验证通过条件:在给定交替场景下,迟滞状态机的切换次数应低于单阈值基准,且不出现同一小时内反复启停;若出现,检查T_min是否被正确累加、阈值是否被误设为相等。