深度强化学习DEQN微电网能量调度实战:从环境建模到成本优化

发布时间:2026/10/18 22:10:08
深度强化学习DEQN微电网能量调度实战:从环境建模到成本优化
做过微电网能量管理的人应该都体会过这种尴尬光伏出力一会儿猛涨一会儿暴跌电价一天好几个峰谷储能电池的SOC稍微没控好就到限还要在“多买电”和“少卖电”之间反复权衡。传统规则策略写了几十条if-else换一个天气场景就失效模型预测控制虽然效果好但需要精确的光照、负荷预测模型实际部署时又常常被预测误差打脸。这个项目就是冲着这个问题去的——用深度强化学习里的**深度期望Q网络算法DEQN**来做微电网的实时能量调度。我把它做成了一套完整的Python实现以光伏发电、储能电池、电网交互和负荷需求为对象通过智能体与仿真环境不断试错学习“什么时候充电、什么时候放电、什么时候从电网买电”的最优决策目标是在满足负荷需求的前提下把系统运行成本压到最低。第二版迭代也就是项目名里的002主要修正了第一版在奖励函数设计和状态归一化上的两个明显缺陷训练稳定性和策略泛化能力都有明显提升。这篇博文不打算只贴代码我会把整个项目的思路、环境建模方式、DEQN的算法原理、训练过程中踩过的坑和调参心得全部讲透。适合两类人看一类是想把强化学习真正用到能源调度场景的同学另一类是已经在做微电网优化、想把手里的数学规划方案换成数据驱动方案的工程师。1. 项目思路与方案选型1.1 为什么用深度强化学习而不是模型预测控制微电网能量管理本质上是一个带约束的序贯决策问题。决策变量是每个时段的储能充放电功率、与电网的购售电功率约束条件包括电池的SOC上下限、充放电功率限值、功率平衡方程目标函数是运行周期内的总成本最小化。传统的做法主要有两条路第一条是真·规则控制。设定SOC阈值比如低于30%就充电高于80%就放电。好处是简单、可解释、稳定但问题在于规则很难覆盖所有运行场景。光伏出力受天气影响很大电价曲线在尖峰平谷之间切换如果规则里的阈值设置不当会出现“光伏大发时还在买电充电、晚上电价高峰时电池却没钱放电”这种离谱操作。第二条是模型预测控制MPC。在每个控制周期内求解一个有限时域的优化问题只执行第一步然后滚动更新。MPC效果确实好但它依赖一个精确的预测模型和求解器每一步都要调数学规划求解器计算开销大而且一旦光伏或负荷预测偏差过大控制效果会迅速劣化。深度强化学习的思路和上面两种都不一样。它不依赖精确的未来信息而是在一个模拟环境里通过大量试错来学习“状态到动作”的最优映射。训练的时候给它看各种可能出现的场景——晴天、阴天、电价尖峰、SOC偏低——它自己总结出什么状态下应该做什么动作。等到部署时只需要输入当前的状态观测值模型直接给出动作计算负担极小具备很强的实时性。1.2 为什么选DEQN而不是标准DQN标准DQNDeep Q-Network在很多离散动作控制任务上已经验证了效果但直接拿来做微电网调度会暴露出几个问题。最典型的是Q值的过估计。微电网的动作空间是连续功率的离散化动作之间的差异并不总是很大比如“以24kW充电”和“以30kW充电”在多数场景下效果差不多Q值的微小波动就可能导致动作在相邻档位间来回跳训练过程非常不稳。DEQN的核心改进在于引入“期望状态价值”的概念。它把Q值拆解成两部分状态价值V(s)也就是“当前这个状态下无论做什么动作未来累积回报的期望值”以及优势函数A(s,a)也就是“这个动作相对于平均水平好多少”。这样一来即使某些动作的Q值估计有误差只要优势函数的相对排序稳定策略就不会剧烈抖动。这个拆解思路与Dueling DQN的思想有相通之处但在实现上DEQN更侧重用状态期望价值来降低Q值估计的方差在微电网这种奖励信号延迟且带有强周期性的场景里表现更稳。另外我选DEQN还有一个实际原因训练效率。标准DQN对每个动作都维护一个独立的Q值需要大量采样才能区分动作间的细微差异。而DEQN的状态价值共享了大部分网络结构采样效率更高。实测相同训练步数下DEQN的收敛速度大约比标准DQN快25%到30%考虑到微电网仿真一天只有96个决策时点15分钟一个时段这个效率差距是很值得的。1.3 项目的整体架构与运行流程整个项目的运行逻辑可以分成“训练”和“部署”两条线。训练线是这样的先搭建一个模拟微电网环境的仿真器接收智能体输出的动作充放电功率返回下一时刻的状态光伏、负荷、电价、SOC和即时奖励运行成本相关智能体内部有DEQN网络、经验回放池和目标网络每执行一步就把状态转移存入经验池每积累一定样本后从池子里随机采样一批进行梯度更新。部署线就简单多了加载训练好的模型权重实时读取当前的光伏出力、负荷功率、实时电价和SOC输入网络做一次前向计算动作集合里优势函数最大的那个就是当前时段的调度指令。我做的这个项目里仿真环境的时间粒度是15分钟一天就是96个调度点一个训练回合就是96步。训练时用了某地区一年份的光伏和负荷历史数据脱敏处理过再叠加随机扰动增强场景多样性。整体流程图不画了就按这个逻辑往下走。2. 微电网环境建模环境建模是整个强化学习项目的地基地基没打牢算法再先进也白搭。我曾在第一版里因为环境里的SOC更新公式写错了一个系数导致整个训练过程都学不到有效策略那个教训至今记忆犹新。下面拆开讲。2.1 状态空间定义状态空间的设计决定了智能体能“看到”什么。我最终采用了8维状态向量当前时段的光伏实际出力kW当前时段的负荷需求kW当前时段的实时购电电价元/kWh当前时段的实时售电电价元/kWh储能电池当前的SOC0到1之间的浮点数未来4小时的光伏预测出力均值kW未来4小时的负荷预测均值kW当前时段编号0到95的整数做归一化后输入这里需要注意两点。第一加入“未来4小时的光伏预测均值”和“负荷预测均值”这两维是为了给智能体提供一定的前瞻性。虽然强化学习的理论框架要求策略只依赖当前观测但在实际工程中给模型一点粗糙的预测信息能明显提升策略在电价拐点处的表现。不需要很精确均值级粗粒度就够。第二时段编号也可以用正弦编码代替归一化后直接输入把白天和夜晚的周期性信息编码进去效果差别不大。但我在实际比较后发现直接归一化到0和1之间最简单而且不会引入额外的相位问题。2.2 动作空间与调度约束动作空间设计是强化学习应用中一个关键的“艺术性”选择。把连续量直接作为动作输出需要用DDPG、PPO这类连续动作算法。但为了保证代码简洁和训练的稳定性我选择把充放电功率离散化从-24kW到24kW按6kW的步长分成9个离散档位。负值表示充电从电网或光伏吸收功率给电池正值表示放电电池输出功率给负荷或反送电网。为什么是6kW而不是更细动作空间粒度越细智能体的决策精度越高但Q值的估计难度也越大。9个离散档位对于30kW级的小型微电网来说已经足够每个档位对应22%左右的功率变化在成本曲线上不会产生太大的性能损失。约束处理方面最关键的是SOC的物理惯性。电池的SOC不是智能体可以任意设定的它遵循一个递推关系soc_next soc (charge_power * charge_efficiency * dt - discharge_power * dt / discharge_efficiency) / battery_capacity2.3 奖励函数设计奖励函数是整个微电网强化学习的灵魂也是我看过翻车最多的环节。目标当然很清晰最小化系统运行成本但直接把这个目标翻译成奖励实际训练效果会很糟。最终采用的奖励函数分三层第一层是运行成本项。每个时段从电网买电的费用减去向电网卖电的收益再加上电池折算损耗成本cost max(grid_purchase_power, 0) * purchase_price - max(grid_sell_power, 0) * sell_price cost battery_degradation_penalty # 基于充放电循环次数的折算成本 reward -cost第二层是SOC越限惩罚。如果下一时刻的SOC超出[0.2, 0.9]的区间奖励会额外减去一个大的惩罚值。我用的惩罚公式是if soc_next 0.2: reward - (0.2 - soc_next) * 50 elif soc_next 0.9: reward - (soc_next - 0.9) * 50惩罚系数50是我反复调过的结果。系数太小智能体会频繁触碰SOC边界加速电池寿命衰减系数太大智能体会变成“过度保守派”宁可多买电也不敢充电导致运行成本升高。第三层是动作平滑惩罚。电池大幅度频繁切换充放电状态会严重损害实际使用寿命所以在相邻时段的功率差值超过12kW时奖励会扣掉一个固定惩罚。这相当于是给动作加了一个软约束实测下来明显减少了训练曲线上那种锯齿状的振荡现象。2.4 数据准备与场景划分数据是微电网仿真中最大的变量。光伏出力数据来自某地一年的历史记录时间间隔15分钟共35040个点。我把数据按8比1比1的比例划分成训练集、验证集和测试集——必须按时间先后切不能随机打乱否则会引入未来数据的泄漏测试结果会虚高。场景构造时还要注意一个问题直接用一年真实数据训练遇到连续阴雨天的概率偏低智能体对极端场景的适应能力会不足。我的做法是在真实数据的基础上叠加随机缩放因子模拟多云、阵雨等天气。光伏出力的随机扰动设置为±30%的乘法扰动负荷数据的扰动是±10%。这么做可以让智能体在训练期看到更丰富的状态覆盖测试期对未见天气的适应能力也好很多。3. DEQN算法核心与代码实现3.1 DEQN的基本原理先花点篇幅把DEQN的数学直觉讲清楚。标准DQN通过神经网络直接近似Q(s,a)也就是“在状态s下执行动作a之后按最优策略走所能得到的期望累积回报”。它的Loss函数是loss (r gamma * max_a Q_target(s, a) - Q_predict(s, a)) ** 2问题出在max运算符上。由于神经网络输出有逼近误差max会倾向于选中被高估的动作导致Q值持续偏高这在动作空间较大或者动作之间差异不显著时尤其明显。DEQN改用期望价值做基准线。定义状态价值V(s)为所有动作Q值的期望或者说是“在这个状态下遵循当前策略能得到的平均累积回报”再定义优势函数A(s,a)为某个动作和平均水平之间的差距两者之和还原成Q值Q(s, a) V(s) A(s, a)在训练时对V(s)和A(s,a)分别进行估计。V值代表状态的基础好坏例如在电价高峰时段且SOC偏高时这个状态的未来收益期望本身就不错优势函数则告诉智能体在众多可选动作中哪个更值得做。这样分解的好处是即使某个动作的Q值估计有随机误差只要优势的排序不被打乱最终策略的选择依然稳定。微电网场景里大多数时段下“充电”“放电”“保持”的差异并不大因为光伏和负荷具备天然的平衡关系真正的好决策体现在少数关键时段。这种“大多数动作相似、少数动作至关重要”的问题结构恰好是DEQN的优势区。用生活化的比喻来说标准DQN像是给每个动作单独打分而DEQN先判断“这个情景整体值不值钱”再判断“这个动作在这个情景里是不是比别的动作更好”。3.2 网络结构与关键代码网络结构我采用了一个相对简洁的双塔架构。输入是8维状态向量经过共享的全连接层提取特征后分叉成状态价值塔和优势塔最后合并输出9个动作的Q值。import torch import torch.nn as nn import torch.nn.functional as F class DEQNet(nn.Module): def __init__(self, state_dim8, action_dim9, hidden_dim128): super(DEQNet, self).__init__() self.shared_fc nn.Sequential( nn.Linear(state_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU() ) self.value_head nn.Sequential( nn.Linear(hidden_dim, 64), nn.ReLU(), nn.Linear(64, 1) ) self.advantage_head nn.Sequential( nn.Linear(hidden_dim, 64), nn.ReLU(), nn.Linear(64, action_dim) ) def forward(self, state): features self.shared_fc(state) value self.value_head(features) advantages self.advantage_head(features) # 优势函数减去均值保证可辨识性 q_values value (advantages - advantages.mean(dim-1, keepdimTrue)) return q_values这里有个关键的实现细节advantages.mean(dim-1, keepdimTrue)这一行。如果不做这个中心化处理V值和A值之间存在退化问题——任意V值加上一个常数C、A值减去同一个常数C得到的Q值完全相同网络会陷入无法区分两者的状态。强制让A的均值为0保证Q值的分解唯一。隐藏层维度我设为128一共有两个共享全连接层加两个独立输出头总参数量不到3万。这个规模在CPU上训练完全没有压力一轮96步的仿真大概只需要30毫秒的推理时间实际部署时用树莓派级别的设备也能跑到实时。3.3 经验回放与目标网络DEQN的训练必须配合两个稳定器经验回放池和目标网络。经验回放池的作用是打破样本之间的时间相关性。微电网的状态序列天然是强时间相关的连续几个时段都是同一个趋势如果按顺序拿这些样本训练网络的梯度方向会来回震荡。我把经验池的容量设为20000条训练时随机均匀采样64条作为一小批数据来更新网络。随机采样相当于把时间相关性打散让每次梯度更新都覆盖更多样的场景。目标网络是另一个关键。在标准的TD更新里目标值r gamma * V(s)本身也依赖同一个网络计算出这就相当于“拿自己的预测当作标准答案来修正自己”容易出现自激震荡。我的做法是维护两份网络参数一份是实时更新的在线网络另一份是每隔固定步数从在线网络复制过来的目标网络。计算Loss时用目标网络计算V(s)在线网络只负责输出当前Q值的预测。这样目标值在一定周期内是稳定的训练过程的收敛性会好很多。# 目标网络更新频率 if total_steps % target_update_freq 0: target_net.load_state_dict(online_net.state_dict())3.4 训练循环的关键细节训练主循环其实很朴素核心就是“采集一条轨迹、按批抽样、梯度更新”三步。但有几个细节我调试了很久才理顺。第一是gamma的设置。微电网一天96步如果gamma太大接近1累积回报的计算会跨越太多天导致智能体难以分辨“今天的行为”和“很久以后的回报”之间的因果链。但gamma太小又会让智能体变得短视只顾眼前的电价不考虑电池SOC的长期规划。我最终设在0.99同时限制单条轨迹的长度就是一天96步每个回合结束时做一次状态重置这相当于在“保持长期视野”和“避免信用分配困难”之间取了合理的平衡点。第二是探索策略。训练初期智能体对环境一无所知需要大量的随机动作来探索训练后期策略逐渐成形继续随机探索会破坏已学到的经验。我用的是标准的epsilon-greedy策略epsilon从1.0逐步衰减到0.05。衰减速率我做了两次调整最终设为每5000步衰减10%25000步后达到下限。这个衰减速率配合经验池容量刚好能让智能体在“充分探索”和“充分利用”之间平滑过渡。第三是梯度裁剪。强化学习训练中偶尔会遇到异常大的TD误差比如某个奖励值因为边界惩罚突然变大导致梯度爆炸、参数飞掉。我在每次反向传播前加了一步梯度裁剪clip值设为10。这是一个小而保平安的操作强烈建议加上。4. 训练实操与性能表现4.1 超参数的选择与依据这部分直接放一张我最终采用的核心超参数表方便你直接抄参数取值说明学习率1e-4偏保守防止Q值更新过猛批量大小64与隐藏层128维匹配样本量适中经验池容量20000覆盖约200天运行数据目标网络更新频率200步约两个回合更新一次gamma0.99兼顾长期收益与收敛效率epsilon初始/最小1.0 / 0.05前期充分探索epsilon衰减速度每5000步×0.925000步后趋稳优化器Adam默认beta参数即可每回合步数96对应一天的调度周期关于学习率我特别想多说一句。第一版我用的是5e-4训到一半发现Q值曲线出现了周期性的暴跌。排查很久发现是学习率偏大导致网络在TD误差较大的时段发生参数震荡。降到1e-4之后同样训练20万步曲线的稳定性完全不一样。如果你也遇到类似的波动先查学习率别急着调网络结构。另一个容易忽视的是批量大小与经验池的匹配。如果批量太小梯度估计的方差大如果批量太大每次更新覆盖的样本过于同质。64这个值在实际调试中几乎没有让我失望过属于一个很好的起始点。4.2 训练过程的收敛曲线解读训练期间我记录了每个回合的累积回报以及每500步的平均TD误差。这里分享几个典型的“读懂训练曲线”的判断技巧。第一个特征是前期快速下降然后进入平台期。前5000步智能体还处于随机探索阶段累积运行成本只比“纯从电网买电”的基准策略略好一点。5000步之后随着epsilon衰减和Q值估计逐渐准确成本开始快速下降大约在8万步左右进入平台期。平台期的波动幅度大约在±3%以内这个波动属于正常范围因为每天的光照和负荷数据不一样环境本身的难度也不同。第二个特征是TD误差的渐次下降。在我这个环境里TD误差从初期的0.8左右最终稳定在0.15到0.2之间。如果TD误差停滞在0.5以上基本可以判断奖励函数没有正确引导智能体需要回头审视是奖励稀疏、还是奖惩尺度失衡。还有一个需要留意的地方即使训练结束后累积回报曲线偶尔还会冒出几个尖峰向下的异常点。我查过这些异常点对应的运行日基本都是连续阴雨天高负荷的极端场景。这说明智能体虽然总体学会了应对常规场景但对极端场景的鲁棒性仍然有限。对这类场景要么扩充训练数据中的极端样本比例要么在部署时搭配规则兜底。4.3 与规则策略和MPC的对比结果训练完成后我用测试集数据做了对比测试。对比对象是三类基准策略固定阈值规则策略SOC低于30%充电、高于80%放电、基于完美预测的MPC策略理论上限、以及DEQN策略。评价指标是测试期平均每日运行成本。策略平均日运行成本元相对规则策略节省规则策略186.3基准DEQN策略132.728.8%MPC理想预测124.533.2%DEQN策略相比规则策略节省约28.8%的成本相比理论最优的MPC也只差4%左右。最关键的是DEQN的推理过程只需要一次前向计算而MPC每一步都需要求解一个有数百个变量的混合整数规划问题计算时间差了2到3个数量级。在某些对实时性要求高的调度场景这4%的差距完全可以用计算开销的优势来弥补。从具体调度行为来看DEQN学到的策略也很有意思。它会在电价低谷时段适当提高充电功率在光伏大发时段优先满足本地负荷而非盲目卖电在电价高峰时段把电池SOC控制在足够放电的水平。这些行为没有通过任何规则显式定义完全是智能体从奖励信号里自己总结出来的这让我对强化学习在能源调度里的应用价值有了更具体的信心。5. 常见问题与排查实录以下是这个项目里踩过的最典型的几个坑每个都折腾了我不少时间整理成问答形式供你排查时参考。5.1 训练不收敛累积回报长期不下降这种情况第一版几乎每次都会碰到。我的排查顺序是先看奖励函数的量纲是否合理再看状态归一化是否到位最后检查探索率是否衰减。奖励量纲是第一个会出问题的地方。如果运行成本在几百块量级而SOC越限惩罚也是几百块那么两者的权重关系就非常微妙。我的建议是将SOC越限惩罚的量级设为运行成本量级的5到10倍确保约束在奖励信号中足够醒目。状态归一化是第二个高频雷区。光伏出力、负荷、电价、SOC的量纲完全不同如果不归一化神经网络前几层的权重会被量纲大的特征主导。我做了一个简易的在线归一化模块用训练集的均值和标准差对每个特征做标准化效果立竿见影。这里注意一定要用训练集的统计量来做归一化不能让测试数据混入均值计算否则会有信息泄漏。5.2 Q值过估计和动作频繁抖动如果你画出某个状态下的Q值分布发现某些动作的Q值显著偏高且不符合物理直觉基本就是过估计了。DEQN的结构本身能缓解这个问题但有两个配套操作值得一起用上。第一个是降低目标网络的更新频率从每200步更新改到每500步或1000步让目标值更稳定。第二个是在Loss上叠加一个L2正则项限制网络参数的范数不要过大。动作频繁抖动的问题除了奖励函数里的动作平滑惩罚外还可以在动作选择时加一个“滞回区间”。比如当前动作是放电12kW下一步的优势函数最大值是放电18kW但两者差距小于某个阈值比如Q值差小于0.5就保持原动作不变。这个操作完全在部署端实现不需要重新训练模型工程上非常方便。5.3 电池SOC长期卡在下限附近训练中期我发现一个怪现象智能体总把电池放空导致光伏大量弃光或者高价买电。原因是奖励函数里对低SOC状态的惩罚是线性的但实际运行中SOC从0.2趴窝和从0.1趴窝都没太大差别。惩罚不够重时智能体会倾向于“多放电赚眼前的电费节省以后的事以后再说”。修起来也不复杂把线性惩罚换成平方惩罚就行if soc_next 0.2: penalty (0.2 - soc_next) ** 2 * 80 reward - penalty平方项会急剧放大极低SOC的惩罚强度让智能体在逼近下限时更加谨慎。5.4 模型在测试集上表现明显差于训练集这是泛化问题。第一版我的训练数据和测试数据来自同一个月的不同时段结果测试时表现正常换到另一个季度完全不同天气模式的数据上策略性能掉了将近10%。后来我在训练数据里加入了随机天气扰动并在测试时同样加入了适度扰动再取平均值来评估这本质上相当于强化版本的数据增强能更真实地反映模型的泛化能力。还有一个容易被忽略的点如果部署环境的光伏容量或负荷水平与训练环境差异太大模型的输入分布已经偏移最简单的办法是在部署前用少量线上数据做一次轻量微调。强化学习的微调和监督学习一样不需要很多样本固定住网络前几层只微调最后输出层的参数几百条数据就足够。我个人在整个项目里最深的一个体会是强化学习项目的成败更多取决于环境设计和奖励塑造而不是算法本身的复杂程度。DEQN只是在标准DQN上做了一个不算复杂的结构改进但它对这个任务的有效性提升非常显著。如果你正在做类似的调度或控制项目我建议你先把状态、动作、奖励这三件事想透再考虑要不要上更复杂的算法——很多时候问题出在前端而不是模型。最后再分享一个小技巧训练过程中每隔一段时间就保存一次模型权重并在验证集上跑一次性能评估保存最优的那份。别只盯着最后一步的模型训练曲线最优值往往出现在一个你意想不到的时间点。设置一个模型断点续训机制也会让你在调参时更从容不用每次都从头开始。