首页 / 资讯中心 / 文章详情

基于深度强化学习的主动配电网电压控制策略:从MDP建模到工程落地

基于深度强化学习的主动配电网电压控制策略:从MDP建模到工程落地 ★ FEATURED ARTICLE
简介这份资源面向电力系统、配电网优化与强化学习方向的学习者与研究人员聚焦利用深度强化学习实现主动配电网电压控制解决分布式能源高渗透下电压波动、传统规则控制适应性不足的问题。压缩包共4个文件以m脚本与xls数据表为主包含IEEE33节点标准配电系统数据、潮流计算与SOCP相关脚本以及训练所需的数据文件整体约14KB结构紧凑便于直接运行与二次开发。资源围绕DRL模型构建、环境模拟、奖励函数设计、训练实施与性能评估展开读者可据此复现智能体与IEEE33节点环境的交互流程理解变压器分接头调节、电容器组投切等控制动作的建模方式并对比传统方法在收敛速度与控制精度上的差异。目前已有778人学习下载适合具备一定电力系统与强化学习基础、希望快速上手主动配电网电压控制实验的读者参考。1. 主动配电网电压控制为什么开始转向深度强化学习去年夏天跟一个做配网自动化的朋友聊天他说他们那条馈线上光伏渗透率刚过 40%中午光伏大发时段末端电压能冲到 1.08 p.u.传统调压手段基本失效——电容器组一天动作几十次寿命掉得飞快有载调压变压器分接头来回切结果电压还是压不住。这不是个例。分布式光伏、储能、电动汽车充电桩大规模接入之后配电网从「无源」变成「有源」电压波动的随机性和双向潮流让基于规则的调压策略越来越力不从心。这就是「基于深度强化学习的主动配电网电压控制策略」要解决的问题。核心思路是把配电网的电压调节建模成马尔可夫决策过程用深度强化学习DRL训练一个智能体让它学会在光伏出力波动、负荷变化的情况下自主决定无功补偿装置出力、储能充放电功率、OLTC 分接头位置等控制变量把各节点电压维持在安全范围内。适合谁看做配网自动化、新能源并网、微电网能量管理的工程师以及想从传统优化方法如二阶锥规划、模型预测控制转向学习型控制的研究生和研发人员。2. 把电压控制写成 MDP状态、动作、奖励怎么定2.1 为什么是 MDP 而不是最优潮流传统做法是每个控制周期解一次最优潮流OPF把电压约束、功率平衡、设备容量都写进去用二阶锥松弛或者半定松弛求解。问题在于光伏出力秒级波动OPF 求解速度跟不上而且配电网三相不平衡、参数不精确模型误差大。深度强化学习不依赖精确模型它通过与环境交互学策略推理时只需一次前向传播毫秒级出动作。但前提是你得把问题正确地写成 MDP。写错了训练不收敛或者收敛到一个「摆烂」策略——比如智能体发现什么都不做奖励也不差就干脆不动作。2.2 状态空间设计别把量测当状态状态设计有个血泪经验不是量测越多越好。我一般会选以下几类各节点电压幅值标幺值尤其是末端节点和光伏接入点光伏有功出力、负荷有功无功储能 SOC当前 OLTC 分接头位置、电容器组投切状态时间信息时刻编码因为光伏出力有强日周期状态维度控制在 50 维以内太多会导致训练慢且容易过拟合。归一化必须做电压用标幺值本身就在 0.9~1.1 附近光伏出力除以额定容量SOC 除以容量。import numpy as np def build_state(voltages, pv_power, load_power, soc, tap_pos, cap_state, hour): 构建强化学习状态向量 voltages: 各节点电压标幺值, shape (n_bus,) pv_power: 光伏有功出力, shape (n_pv,), 单位 MW load_power: 负荷有功, shape (n_load,), 单位 MW soc: 储能荷电状态, shape (n_ess,), 0~1 tap_pos: OLTC 分接头档位, 标量 cap_state: 电容器投切状态, shape (n_cap,), 0/1 hour: 当前时刻, 0~23 # 电压归一化到 [-1, 1] 附近 v_norm (voltages - 1.0) / 0.1 # 光伏出力按额定容量归一化 pv_norm pv_power / 5.0 # 假设单台逆变器额定 5MW load_norm load_power / 10.0 # 时间编码为正弦余弦避免 23 和 0 之间跳变 hour_sin np.sin(2 * np.pi * hour / 24) hour_cos np.cos(2 * np.pi * hour / 24) state np.concatenate([ v_norm, pv_norm, load_norm, soc, [tap_pos / 10.0, hour_sin, hour_cos], cap_state.astype(float) ]) return state.astype(np.float32)这段代码的关键点电压用(v - 1.0) / 0.1归一化因为配电网电压通常在 0.9~1.1 p.u. 之间这样归一化后范围在 -1 到 1 附近对神经网络友好。时间用正弦余弦编码避免 23 点到 0 点之间出现数值跳变这个细节很多初学者会忽略导致智能体在午夜附近策略抖动。2.3 动作空间连续还是离散配电网电压控制变量分两类连续变量储能充放电功率、SVC 无功出力和离散变量电容器投切、OLTC 分接头。常见做法是如果全是连续变量用 DDPG、TD3、SAC 这类连续控制算法如果全是离散变量用 DQN 及其变体混合动作空间用混合动作网络或者把离散动作也松弛成连续再量化我一般推荐 SAC因为它对超参数不敏感样本效率高而且输出的是随机策略天然有探索能力。动作空间设计成连续向量离散变量通过取整和映射实现。import torch import torch.nn as nn class Actor(nn.Module): SAC 策略网络输出动作均值和标准差 def __init__(self, state_dim, action_dim, hidden_dim256): super().__init__() self.net nn.Sequential( nn.Linear(state_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), ) self.mu_head nn.Linear(hidden_dim, action_dim) self.log_std_head nn.Linear(hidden_dim, action_dim) def forward(self, state): x self.net(state) mu self.mu_head(x) log_std self.log_std_head(x).clamp(-20, 2) # 限制标准差范围 return mu, log_std def sample(self, state): mu, log_std self.forward(state) std log_std.exp() normal torch.distributions.Normal(mu, std) x_t normal.rsample() # 重参数化采样 action torch.tanh(x_t) # 压缩到 [-1, 1] log_prob normal.log_prob(x_t) - torch.log(1 - action.pow(2) 1e-6) log_prob log_prob.sum(dim-1, keepdimTrue) return action, log_problog_std的 clamp 范围 -20 到 2 是经验值太小会导致策略过早收敛到确定性动作太大则训练不稳定。tanh压缩后动作在 [-1, 1]再线性映射到实际控制范围比如储能功率 -1MW 到 1MW电容器档位 0 到 5 档。2.4 奖励函数电压偏差和设备动作成本怎么平衡奖励函数是 DRL 电压控制里最玄学的部分。设计不好智能体会学出「疯狂动作」或者「完全不动」两种极端。我一般用分段奖励def compute_reward(voltages, action, prev_action, tap_change, cap_change): 奖励 电压偏差惩罚 设备动作成本 越限硬惩罚 voltages: 各节点电压标幺值 action: 当前动作 prev_action: 上一时刻动作 tap_change: OLTC 是否动作 cap_change: 电容器是否动作 v_ref 1.0 v_dev np.abs(voltages - v_ref) # 电压偏差惩罚偏差越大惩罚越重 reward_voltage -np.sum(v_dev ** 2) * 100 # 越限硬惩罚 violation np.sum((voltages 1.05) | (voltages 0.95)) reward_violation -violation * 500 # 设备动作成本抑制频繁动作 action_cost -np.sum(np.abs(action - prev_action)) * 10 # 离散设备动作额外惩罚 switch_cost -(tap_change cap_change) * 50 return reward_voltage reward_violation action_cost switch_cost电压偏差用平方惩罚因为电压偏离越远危害越大。越限硬惩罚系数要远大于偏差惩罚让智能体把「不越限」作为第一优先级。设备动作成本是抑制频繁调节的关键没有这一项智能体会让电容器组每秒投切一次仿真里看着电压很稳实际设备早坏了。注意奖励系数的量级要匹配。如果电压偏差惩罚是 -100 量级动作成本是 -0.01 量级那动作成本形同虚设。建议先跑几百步打印各项奖励的数值范围再调系数。3. 用 Python 搭一个配电网电压控制仿真环境3.1 仿真环境选型为什么不用真实配网数据真实配网数据难拿而且涉及安全合规问题。常见做法是用 IEEE 33 节点、IEEE 69 节点或者某地区典型配网拓扑在 OpenDSS 或 pandapower 里搭仿真环境。pandapower 是纯 Python 的和 PyTorch 集成方便我一般用它。环境需要实现reset()和step(action)两个方法符合 Gym 接口。光伏出力和负荷用历史曲线或者概率模型生成每个 episode 模拟一天 24 小时步长 15 分钟共 96 步。3.2 环境代码骨架import pandapower as pp import numpy as np class VoltageControlEnv: 配电网电压控制仿真环境 def __init__(self, net, pv_profile, load_profile): self.net net self.pv_profile pv_profile # shape (96, n_pv) self.load_profile load_profile # shape (96, n_load) self.n_step 96 self.current_step 0 self.voltages None self.prev_action np.zeros(self.action_dim) property def action_dim(self): # 储能功率 SVC 无功 电容器档位 OLTC 档位 return self.n_ess self.n_svc self.n_cap 1 def reset(self): self.current_step 0 self.prev_action np.zeros(self.action_dim) self._apply_power_flow() return self._get_state() def step(self, action): # 动作映射到实际控制量 self._apply_action(action) self._apply_power_flow() reward self._compute_reward(action) self.current_step 1 done self.current_step self.n_step next_state self._get_state() if not done else None return next_state, reward, done, {} def _apply_power_flow(self): 运行潮流计算获取各节点电压 try: pp.runpp(self.net, algorithmnr, numbaFalse) self.voltages self.net.res_bus.vm_pu.values except pp.LoadflowNotConverged: # 潮流不收敛时给一个惩罚性电压 self.voltages np.ones(len(self.net.bus)) * 0.5runpp是 pandapower 的潮流计算函数algorithmnr指定牛顿-拉夫逊法。潮流不收敛在配电网里很常见尤其是光伏出力大、电压越限严重的时候。这里给一个惩罚性电压 0.5 p.u.让智能体学到「这种动作会导致潮流崩溃」从而避开。3.3 训练循环与关键超参数import torch from collections import deque import random def train_sac(env, actor, critic, target_critic, replay_buffer, n_episodes1000): SAC 训练主循环 gamma 0.99 # 折扣因子 tau 0.005 # 目标网络软更新系数 alpha 0.2 # 熵温度系数 batch_size 256 warmup_steps 1000 # 预热步数先随机探索 total_steps 0 for episode in range(n_episodes): state env.reset() episode_reward 0 done False while not done: if total_steps warmup_steps: action np.random.uniform(-1, 1, env.action_dim) else: with torch.no_grad(): state_t torch.FloatTensor(state).unsqueeze(0) action, _ actor.sample(state_t) action action.squeeze(0).numpy() next_state, reward, done, _ env.step(action) replay_buffer.push(state, action, reward, next_state, done) state next_state episode_reward reward total_steps 1 if len(replay_buffer) batch_size and total_steps warmup_steps: # 从经验回放池采样并更新网络 batch replay_buffer.sample(batch_size) update_networks(batch, actor, critic, target_critic, alpha, gamma, tau) if episode % 50 0: print(fEpisode {episode}, Reward: {episode_reward:.2f})gamma0.99意味着智能体关注未来约 100 步的累积奖励对应 96 步的 episode 刚好覆盖一天。tau0.005是目标网络软更新比硬更新每隔 N 步复制参数更平滑。alpha0.2是 SAC 的熵温度系数控制探索强度配电网控制里一般 0.1~0.3 比较合适。warmup_steps1000是先随机探索填充经验池避免一开始就用随机初始化的策略网络产生垃圾数据。4. 训练不收敛、电压振荡、设备频繁动作避坑与排查4.1 奖励曲线震荡不上升现象训练了几百个 episode奖励一直在 -5000 到 -3000 之间震荡没有上升趋势。原因最常见的是状态归一化没做好或者奖励系数量级不匹配。电压偏差惩罚如果是 -100 量级而动作成本是 -0.01 量级智能体根本感知不到动作成本就会乱动。另一个原因是经验回放池太小样本相关性太强。解决先打印每一步的各项奖励分量确认量级。经验回放池至少 10 万条。如果状态里有量纲差异大的变量比如电压 1.0 和功率 10.0必须分别归一化。4.2 电压在 0.95 和 1.05 之间来回振荡现象智能体学会了「打乒乓球」——电压高了就切电容器切完电压低了又断开一个 episode 里电容器动作几十次。原因奖励函数里设备动作成本太低或者动作空间映射有问题。比如电容器档位从 0 到 5智能体输出连续值 2.5取整后变成 2 或 3相邻步之间跳变。解决加大设备动作成本系数尤其是离散设备。另外可以在动作输出后加一个低通滤波或者用「动作保持」机制——连续 N 步动作不变才允许切换。4.3 潮流不收敛导致训练崩溃现象训练到某个 episode潮流计算频繁不收敛奖励突然掉到 -10000 以下之后再也恢复不了。原因智能体探索到了极端动作比如储能满功率放电同时电容器全切导致电压崩溃。潮流不收敛时如果给一个固定的惩罚电压智能体可能学不到「避免崩溃」而是学到「崩溃也没事」。解决潮流不收敛时给一个非常大的负奖励比如 -1000并立即结束 episode。同时限制动作范围储能功率不超过额定值电容器档位有上下限。4.4 训练好的策略换到测试场景就失效现象训练时奖励很好换一组光伏出力曲线或者负荷曲线电压越限严重。原因过拟合到训练场景。如果训练时只用了一条光伏曲线智能体可能记住了「中午 12 点切电容器」而不是学会了「电压高时切电容器」。解决训练时随机化光伏出力和负荷曲线每个 episode 从历史数据里随机采样一天或者在基准曲线上加高斯噪声。这叫域随机化是提升泛化性的关键。4.5 推理时动作抖动现象训练好的策略在实际部署时相邻控制周期动作差异很大设备频繁调节。原因SAC 是随机策略推理时如果还采样动作会有随机性。另外状态里的量测噪声也会导致策略输出抖动。解决推理时用确定性动作取均值而不是采样。状态输入前做滑动平均滤波。如果还抖可以在动作输出后加一个变化率限制比如相邻步动作变化不超过 0.1。5. 从仿真到落地策略迁移与在线微调的三个技巧仿真里训练好的策略直接搬到实际配电网大概率翻车。原因很简单仿真模型和实际网络有偏差量测有噪声通信有延迟。我一般用三个技巧做迁移。第一用实际量测做状态对齐。仿真里的电压是潮流计算出来的实际是量测装置采的两者有系统误差。部署前先收集一周实际量测数据和仿真结果对比如果某节点电压长期偏差 0.02 p.u.就在状态输入里加一个偏置校正。这个校正值可以离线算也可以在线自适应。第二在线微调而不是从头训练。把仿真预训练的策略网络作为初始化在实际环境里用少量数据做微调。微调时学习率调小比如 1e-5只更新策略网络不更新价值网络。这样既能适应实际网络又不会把预训练学到的知识忘掉。第三加安全层兜底。不管策略输出什么动作都经过一个安全校验层如果动作会导致电压越限或者设备越限就裁剪到安全范围。安全层用简单的规则实现比如「电容器档位变化不超过 1 档/步」「储能功率不超过额定值」。这样即使策略输出异常也不会造成实际危害。def safety_layer(action, prev_action, voltages, action_limits): 安全层裁剪动作到安全范围 action: 策略网络输出, [-1, 1] prev_action: 上一时刻动作 voltages: 当前电压 action_limits: 各动作维度的上下限 # 映射到实际控制范围 action_real action * (action_limits[:, 1] - action_limits[:, 0]) / 2 \ (action_limits[:, 1] action_limits[:, 0]) / 2 # 限制相邻步变化率 max_delta (action_limits[:, 1] - action_limits[:, 0]) * 0.1 delta np.clip(action_real - prev_action, -max_delta, max_delta) action_real prev_action delta # 电压越限时强制动作 if np.any(voltages 1.05): # 电压偏高增加无功吸收或降低分接头 action_real[0] min(action_real[0], 0) # 储能吸收无功 if np.any(voltages 0.95): action_real[0] max(action_real[0], 0) # 储能发出无功 return action_real这个安全层的关键是「变化率限制」和「越限强制动作」。变化率限制防止设备频繁调节越限强制动作保证极端情况下电压能被拉回来。实际部署时安全层的规则可以写得更细比如考虑设备动作次数寿命、通信延迟补偿等。最后说一个我自己的教训早期做这个方向时我花了两周调奖励函数怎么调都不收敛。后来发现是状态里的电压没有归一化范围在 0.9 到 1.1 之间而光伏出力归一化后在 0 到 1 之间神经网络对这两个量纲的敏感度差异巨大导致策略网络学偏了。归一化做完当天就收敛了。所以如果你也在做深度强化学习电压控制先把状态归一化和奖励量级检查一遍能省很多时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站