4. 动态规划在储能调度中的应用

动态规划(DP)这个词,听起来挺唬人的。说白了,它就是一种「把大问题拆成小问题,逐个击破」的数学方法。我在做储能调度算法时,DP 是我最早接触、也是用得最顺手的方法之一。今天咱们就聊聊,怎么把 DP 用在储能调度里。

4.1 动态规划原理:从斐波那契到储能调度

先讲个最简单的例子。斐波那契数列,你肯定知道:F(n) = F(n-1) + F(n-2)。如果用递归硬算,复杂度是 O(2^n),n 稍微大一点就卡死了。但如果你用 DP,把中间结果存起来,复杂度就降到 O(n)。

储能调度也是这个道理。一天的调度周期有 24 个时段,每个时段你都要决定:充多少电?放多少电?还是闲着?如果暴力枚举所有可能,组合数会爆炸。但 DP 告诉我们:当前时段的最优决策,只取决于上一时段的状态和当前时段的电价。这就是「最优子结构」和「无后效性」——DP 的两大基石。

核心思想:把 24 小时的全局优化问题,拆成 24 个 1 小时的小问题。每个小时只关心「当前电量」和「当前电价」,然后做出最优决策。

我在项目中遇到过一个问题:一开始我用递归写 DP,结果 n=24 时跑得飞快,但 n=48 时直接内存溢出。后来才意识到,DP 的「状态」和「决策」必须精打细算,不能无脑枚举。

4.2 状态空间构建:三个关键要素

构建 DP 的状态空间,就像搭积木。你需要三块积木:状态变量决策变量状态转移方程

4.2.1 状态变量

在储能调度中,状态变量通常有两个:

  • 时段 t:0 到 23,代表当前是第几个小时。
  • 电量 SOC(t):储能系统在时段 t 开始时的荷电状态,范围 0% 到 100%。

你想想看,有了这两个变量,你就能唯一确定当前系统的「处境」。比如 t=10, SOC=50%,意味着现在是上午 10 点,电池还有一半电。

4.2.2 决策变量

每个时段,你要做三个决策之一:

  • 充电:从电网买电,存入电池。
  • 放电:把电池的电卖给电网。
  • 闲置:什么都不做。

每个决策都会影响下一时段的 SOC。比如你选择充电,SOC 就会增加;放电则减少。

4.2.3 状态转移方程

这是 DP 的灵魂。公式很简单:

V(t, SOC) = max{ 
   充电收益 + V(t+1, SOC + ΔSOC_charge),
   放电收益 + V(t+1, SOC - ΔSOC_discharge),
   0 + V(t+1, SOC)
}

其中 V(t, SOC) 表示「从时段 t、电量 SOC 开始,到一天结束能获得的最大收益」。这个方程的意思是:当前时段的最优收益,等于当前决策的即时收益,加上未来所有时段的最优收益

我的习惯:写 DP 时,我会先画一张表格,横轴是时段 t,纵轴是 SOC。然后从最后一行(t=23)往前填。这样思路特别清晰,不容易出错。

4.3 DP 在日前调度中的实现

好了,理论讲完了,咱们直接上代码。下面是一个简化版的日前调度 DP 实现,用 Python 写的。

import numpy as np

# 参数设置
T = 24  # 时段数
SOC_min, SOC_max = 0.2, 0.9  # SOC 范围
SOC_step = 0.01  # SOC 离散化步长
SOC_levels = int((SOC_max - SOC_min) / SOC_step) + 1

# 电价数据(假设已知)
price = np.array([0.3, 0.3, 0.25, 0.25, 0.2, 0.2, 0.25, 0.3,
                 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.8, 0.7,
                 0.6, 0.5, 0.4, 0.3, 0.25, 0.2, 0.2, 0.3])

# 电池参数
capacity = 100  # kWh
charge_eff = 0.95  # 充电效率
discharge_eff = 0.95  # 放电效率
max_power = 20  # kW

# DP 表格
V = np.zeros((T+1, SOC_levels))
decision = np.zeros((T, SOC_levels), dtype=int)

# 从后往前递推
for t in range(T-1, -1, -1):
    for s_idx in range(SOC_levels):
        soc = SOC_min + s_idx * SOC_step
        best_value = -np.inf
        
        # 决策 0:闲置
        value_idle = V[t+1, s_idx]
        if value_idle > best_value:
            best_value = value_idle
            best_decision = 0
        
        # 决策 1:充电
        if soc < SOC_max:
            delta_soc = min(max_power * 1 / capacity, SOC_max - soc)
            new_soc = soc + delta_soc * charge_eff
            new_idx = int((new_soc - SOC_min) / SOC_step)
            cost = delta_soc * capacity * price[t] / charge_eff
            value_charge = -cost + V[t+1, new_idx]
            if value_charge > best_value:
                best_value = value_charge
                best_decision = 1
        
        # 决策 2:放电
        if soc > SOC_min:
            delta_soc = min(max_power * 1 / capacity, soc - SOC_min)
            new_soc = soc - delta_soc / discharge_eff
            new_idx = int((new_soc - SOC_min) / SOC_step)
            revenue = delta_soc * capacity * price[t] * discharge_eff
            value_discharge = revenue + V[t+1, new_idx]
            if value_discharge > best_value:
                best_value = value_discharge
                best_decision = 2
        
        V[t, s_idx] = best_value
        decision[t, s_idx] = best_decision

print("DP 计算完成,最优收益为:", V[0, 0])

我曾经踩过的坑:SOC 离散化步长设得太大会导致精度丢失,设得太小又会让计算量暴增。我建议步长取 0.01(即 1%),这样精度和速度都能接受。另外,别忘了考虑充放电效率——我见过有人直接忽略,结果算出来的收益虚高 10% 以上。

4.4 状态空间的可视化

为了让你更直观地理解 DP 的决策过程,我画了一张图。这张图展示了从 t=0 到 t=23,不同 SOC 下的最优决策路径。

DP 决策路径示意图 时段 t 0 6 12 18 23 SOC (%) 20% 50% 90% 电价曲线 最优 SOC 路径 绿色:最优决策路径 红色:电价曲线(参考)

从这张图可以看出,DP 算法会在电价低的时候(比如凌晨)选择充电,在电价高的时候(比如傍晚)选择放电。这就是「低买高卖」的套利逻辑。

4.5 实际应用中的注意事项

DP 虽然好用,但也不是万能的。我在实际项目中总结了几条经验:

问题 原因 解决方法
状态空间爆炸 SOC 离散化太细,或时段数太多 使用粗粒度离散化 + 插值
效率损失被忽略 充放电效率没建模 在状态转移中显式加入效率系数
电池寿命未考虑 DP 只优化收益,不关心循环次数 加入惩罚项,限制充放电次数
电价预测误差 DP 假设电价完全已知 结合随机规划或鲁棒优化

我的建议:如果你刚开始做储能调度,先用 DP 跑通一个简单版本。别一上来就搞什么强化学习、模型预测控制。DP 虽然「笨」,但它稳定、可解释、容易调试。等你把 DP 吃透了,再考虑更高级的方法。

嗯,关于 DP 在储能调度中的应用,今天就聊到这儿。记住三个关键词:状态、决策、转移方程。把这三点搞明白,DP 就没什么神秘的了。


公众号:蓝海资料掘金营,微信deep3321