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 算法会在电价低的时候(比如凌晨)选择充电,在电价高的时候(比如傍晚)选择放电。这就是「低买高卖」的套利逻辑。
4.5 实际应用中的注意事项
DP 虽然好用,但也不是万能的。我在实际项目中总结了几条经验:
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 状态空间爆炸 | SOC 离散化太细,或时段数太多 | 使用粗粒度离散化 + 插值 |
| 效率损失被忽略 | 充放电效率没建模 | 在状态转移中显式加入效率系数 |
| 电池寿命未考虑 | DP 只优化收益,不关心循环次数 | 加入惩罚项,限制充放电次数 |
| 电价预测误差 | DP 假设电价完全已知 | 结合随机规划或鲁棒优化 |
我的建议:如果你刚开始做储能调度,先用 DP 跑通一个简单版本。别一上来就搞什么强化学习、模型预测控制。DP 虽然「笨」,但它稳定、可解释、容易调试。等你把 DP 吃透了,再考虑更高级的方法。
嗯,关于 DP 在储能调度中的应用,今天就聊到这儿。记住三个关键词:状态、决策、转移方程。把这三点搞明白,DP 就没什么神秘的了。
公众号:蓝海资料掘金营,微信deep3321