
这段时间一直在琢磨居民侧需求响应这块顺手把双层鲸鱼算法、非合作博弈和居民负荷分层调度三个东西凑到一块做了套模型Matlab里面跑通了效果还不错。这套东西说白了就是回答两个问题运营商怎么定电价能让自己的收益最好同时电网负荷曲线又尽量平稳用户看到电价之后怎么调整自家的用电计划让电费最省又不影响生活舒适度。整个过程适合正在做电力系统优化调度、需求响应、智能用电方向毕业设计或者科研课题的同学参考也适合刚接触博弈论与智能优化算法结合应用的工程师拿来练手。文章不整虚的直接讲清模型怎么建、算法怎么设计、代码怎么写、坑怎么踩。1. 先把问题拆清楚居民负荷分层调度到底在算什么1.1 为什么居民负荷要分层调度居民负荷有个特点单个用户容量小但聚合起来体量非常大而且使用时间高度集中。晚上七八点大家同时开空调、开热水器、开电视负荷曲线尖峰一下子就顶起来。电网为了应对这个尖峰得额外配置大量发电容量经济性很差。所以现在越来越多地提“需求响应”想让用户把用电行为往低谷时段挪一挪。但问题来了让谁挪、挪多少、凭什么挪这就需要一个调度机制。居民负荷种类多洗衣机、洗碗机这类可以平移使用时间的是可转移负荷空调、热水器这类可以临时降低功率但不能完全关掉的是可削减负荷还有固定不可调的基础负荷。不同用户对舒适度的要求也不一样你让一个家里有婴儿的用户低谷时段再开空调人家大概率不干。分层调度的思路就是把决策拆成上下两层上层是运营商负荷聚合商负责制定分时电价引导用户行为下层是各个居民用户在给定电价下优化自己的用电计划。这种“上层定价格、下层定电量”的结构本质上就是电力市场里很经典的主从递阶决策问题学术上叫Stackelberg博弈。1.2 为什么用户之间要用非合作博弈来刻画非合作博弈这个词听着唬人其实生活中到处都是。你去菜市场买菜每个摊主都想让自己利润最大没有人会主动跟旁边的摊主商量“咱俩都便宜点卖让顾客多买点”这就是非合作。居民用电也一样每个用户都只关心自己的电费不会主动替邻居考虑。在模型里用户与用户之间最典型的耦合是共享配变容量。一栋楼或者一个小区的进线容量是有限的大家都在晚高峰用电变压器就可能过载。这时候某个用户多开一台空调其他用户的可调空间就被压缩了。用户之间没有合作协议各自追求电费最小这种竞争关系就是标准的非合作博弈最后会收敛到一个纳什均衡点谁先偏离这个点自己的电费只会更高所以大家都不愿意单方面改变用电策略。我把这套结构写成了模块化的Matlab工程换用户数、换负荷类型、换算法参数都能直接改配置文件跑不用动核心代码后面会详细讲每个模块怎么写的。2. 数学化落地非合作博弈分层模型的建模过程2.1 上层运营商的目标函数上层是运营商目标一般有两块经济收益最大化和负荷曲线平稳化。纯经济效益好理解就是卖电收入减去购电成本但只追收益容易把电价顶得很高用户全都不响应反而得不偿失。所以实际模型里会在目标函数里加一个负荷方差或者峰谷差的惩罚项让运营商在赚钱的同时也尽量平抑负荷波动。简化表达的话上层目标函数可以写成F_operator sum(p(t) * P_total(t)) - sum(c_buy(t) * P_total(t)) - lambda * var(P_total(t))其中p(t)是运营商制定的分时电价P_total(t)是所有用户在时段t的总用电功率c_buy(t)是运营商的购电成本一般从上级电网批发价来lambda是负荷平稳性惩罚系数var表示方差。这个式子很直观前两项是售电净利润第三项惩罚负荷波动。注意P_total(t)不是运营商的直接决策变量而是下层所有用户优化之后的结果。也就是说上层每给出一组电价下层就响应用户负荷上层拿这个负荷再去算自己的目标。这叫“下层决策反馈到上层约束”是双层优化的核心特征也是不方便用普通优化器一把梭的原因。2.2 下层居民用户的决策模型下层每个用户i的目标是自己的综合用电成本最小。综合成本包含两块实际电费以及用电舒适度损失。min C_i sum(p(t) * P_i(t)) alpha_i * sum(舒适度惩罚)约束条件需要区分负荷类型。基础负荷是不可调的直接给序列就行。可转移负荷有一个总用电量固定、可以平移时间窗口的特点比如洗衣机一个周期耗电1.2度、运行2小时那么它必须在一个允许的时间窗口内完整运行不能拆开。可削减负荷可以连续调节功率但用户对温度有舒适度要求比如空调功率不能低于某个比例否则房间温度压不住。在Matlab里实现时每个用户的决策变量可以这样设计可转移负荷用二进制变量表示每个允许开始时刻是否启动比如24个时段里某个时段能不能开始洗衣服可削减负荷用连续变量表示每个时段的实际用电功率储能设备如果部分用户配置了电池还需要增加充放电功率变量和荷电状态SOC约束。为了体现用户之间的非合作博弈我引入了小区总进线容量约束sum(P_i(t)) P_capacity(t)这个约束是一个全局共享约束。某个用户多用电其他用户的可选空间就会变小。每个用户在优化时并不知道邻居的具体策略只能通过电价信号和自身感受去选择这种信息结构是非合作博弈很典型的表现。2.3 纳什均衡的存在性与求解难点非合作博弈的纳什均衡听起来很高大上但落到程序里其实就是一个不动点问题每个用户都在给定其他用户策略下取到了自己的最优策略没有人愿意单方面改变。对于凸优化构成的博弈均衡存在且容易求但我们这模型里可转移负荷是0-1整数变量目标函数还有非线性项整个博弈是非凸的这时候均衡的存在性就得靠数值仿真去验证。实际求解的时候难点在于下层所有用户的优化是相互耦合的因为共享容量约束把大家绑在了一起上层电价又会影响每个用户的优化结果。三层耦合叠在一起普通求解器很难处理。这就是我选用双层鲸鱼算法的直接原因——它不要求目标函数可导也用不着求梯度能比较自然地处理这种嵌套式、不可导、含整数的复杂结构。3. 双层鲸鱼算法为什么这么设计具体怎么迭代3.1 标准鲸鱼优化算法的简要回顾鲸鱼优化算法WOA是2016年提出的一种群智能优化算法模拟的是座头鲸的泡泡网捕食行为。座头鲸捕猎的时候会绕着鱼群画螺旋同时吐出气泡把鱼群逼到中间然后一口吞下去。算法把这个过程抽象成三种位置更新机制第一是包围猎物鲸鱼根据当前最优个体的位置收缩包围圈第二是气泡网攻击鲸鱼沿着螺旋路径向猎物靠近用一组随机参数在收缩包围和螺旋更新之间切换第三是随机搜索以一定概率在全局范围内找新位置保证种群的多样性。WOA最大的优势是参数少、结构简单、容易实现全局探索能力也不错在大量基准函数上表现都挺好。但标准WOA有个通病后期容易陷入局部最优收敛精度不够。用在这个负荷调度问题上因为决策变量多电价曲线24维加上用户负荷变量几十上百维搜索空间非常大直接用标准WOA去跑效果会不太稳定所以必须做针对性改进。3.2 单层结构搞不定双层嵌套怎么设计为什么单层WOA解决不了这个问题因为这个问题天然是嵌套的上层不知道下层用户具体怎么响应就没法评价一组电价的好坏下层不知道电价就没法优化用电计划。两个层的决策必须交替求解。如果硬把上下层决策变量拼在一起用单层算法去搜搜索维度会爆炸而且不同层变量的物理意义和约束差异太大很难收敛到合理的均衡解。我的做法是外层和内层都用鲸鱼算法形成“双层鲸鱼算法”外层鲸鱼算法的每个个体代表一组待评估的分时电价曲线p(t)共24维。外层种群负责在电价上下界约束内搜索能使运营商目标最优的那组电价。外层每生成一组电价就调用内层求解器在内层给定这组电价让所有居民用户分别做负荷调度优化每个用户自己的鲸鱼算法个体代表该用户的负荷计划收敛后把各用户负荷汇总成总负荷曲线返回到外层去计算运营商的适应度值。这样设计的好处是结构清晰上层的“策略空间”和下层的“响应空间”完全隔离符合主从博弈的分层决策特性。缺点是计算量成倍增加这也是后面第5章要讲优化技巧的原因。3.3 针对本问题的三个算法改进点我在标准WOA基础上做了三个改进实测对收敛速度和稳定性都有明显帮助。一是用Tent混沌映射生成初始种群。标准WOA初始化是纯随机的种群容易扎堆在搜索空间的某些区域导致前期探索不充分。Tent映射能在[0,1]区间内生成分布更均匀的序列用它映射到决策变量空间可以让初始电价曲线和初始负荷计划覆盖更多场景。二是加入自适应惯性权重。标准WOA在迭代前期探索强、后期开发弱但切换比较生硬。我让惯性权重随迭代次数从0.9线性降到0.4前期大权重让鲸鱼跑得远、探索范围大后期小权重让鲸鱼围着最优解精细搜索收敛精度更好。三是对最优个体加高斯变异扰动。每次迭代结束后以一定概率对全局最优解加一个小幅度的高斯扰动生成候选解参与下一轮竞争。这个操作的目的是防止算法过早收敛到局部最优相当于给算法装了一个“强跳出来”的机制。当然改进不是越多越好。只有当你发现标准算法在你这个问题上确实出现早熟或者收敛慢的时候再加对应改进才是有效率的。盲目堆砌改进策略很容易出现加了等于没加甚至更差的情况。3.4 完整求解流程整个流程我用文字描述一遍方便大家在Matlab里面照着搭框架初始化外层鲸鱼算法参数随机生成初始电价种群将当前代所有电价个体逐个传给内层内层对每个电价个体执行用户博弈求解随机初始化各用户负荷计划循环迭代直到所有用户都没有意愿单方面改变策略或者达到最大内层迭代次数汇总用户响应负荷计算外层个体适应度值根据适应度值更新外层鲸鱼最优解更新外层个体位置判断外层是否达到最大迭代次数未达到则返回第2步达到则输出最优电价与对应负荷调度方案。这一套流程在Matlab里写成脚本逻辑非常直观。关键点在于外层个体和内层用户都要维护各自的种群和最优解别搞混了数据流。4. Matlab代码实现从核心框架到可运行工程4.1 代码工程结构规划写双层的代码最忌把所有东西堆在一个脚本里调试起来会让人崩溃。我建议按下面的结构组织目录main.m主程序负责参数初始化、调用双层算法、绘图和结果保存parameters.m所有参数的集中配置包括电价上下限、负荷参数、用户数量、算法参数obj_operator.m运营商目标函数obj_user.m用户目标函数与约束判定woa_outer.m外层鲸鱼算法woa_inner.m内层鲸鱼算法load_data.m基础负荷和可调负荷数据的生成或导入。这份结构的好处是你想换场景的时候只需要改parameters.m和load_data.m算法核心完全不动。我甚至建议你在parameters.m里加一个flag变量用于切换“只有可转移负荷”和“可转移可削减储能”两种模型替换场景非常方便。4.2 目标函数与约束的代码表达运营商目标函数这块核心就是计算总负荷曲线然后算收益和方差惩罚。Matlab里建议用向量化一次性把所有时段算出来而不要写for循环否则外层种群50个个体、每个个体24时段循环嵌套下来速度很感人。关键代码思路如下function f obj_operator(p, P_total, c_buy, lambda) % p: 1x24 电价曲线 % P_total: 1x24 总负荷曲线 revenue sum(p .* P_total); cost sum(c_buy .* P_total); variance_penalty var(P_total); f -(revenue - cost - lambda * variance_penalty); % 外层找最小值 end注意外层是最大化运营商收益但优化算法一般习惯求最小值所以目标函数取负号。这个坑我在调试时踩过算出来结果完全反了后来才反应过来是符号问题。用户目标函数类似但要加上舒适度惩罚项。可削减负荷的舒适度惩罚可以直接建模成“实际用电功率与期望功率之差”的二次函数偏差越大惩罚越重既符合物理直观又是连续可导的方便内层搜索。约束处理我统一用罚函数法。比如配变容量约束如果某个用户群体决策导致总负荷超限就在目标函数里加一个很大的惩罚项。罚函数系数要调试太大容易让算法只顾满足约束而忽略经济性优化太小约束又可能被忽略。我一般先把约束满足情况监控出来如果所有个体都满足约束就尝试适度降低惩罚系数给经济性优化让路。4.3 内层博弈求解代码框架内层是所有用户分别优化的循环。由于每个用户的问题复杂度不同——有的只有基础负荷和可转移负荷有的还有可削减负荷——所以我单独写了一个用户模型生成函数每个用户返回自己的决策变量范围和参数。内层循环的伪代码逻辑for iter_inner 1:max_inner_iter for i 1:N_user % 固定其他用户负荷优化用户i的负荷计划 [P_i, cost_i] woa_inner(price, param_user{i}, P_others); % 更新用户i的负荷和成本 P_user{i} P_i; end % 检查连续两轮之间所有用户的策略变化量 if max_change tolerance break; % 达到纳什均衡 end end这个循环本质上是在求非合作博弈的纳什均衡每一轮都让所有用户面对其他用户的当前策略做最优响应反复迭代直到没有人愿意改变策略。在实际代码中woa_inner函数内部还包含一个完整的鲸鱼算法搜索流程搜索维度取决于该用户的决策变量个数。要注意的是woa_inner的种群规模和迭代次数不需要跟外层一样大内层只要够用就行否则整体计算量会翻好几倍后面会细说。4.4 算法参数设置建议参数这东西没有绝对最优但有一个合理的起始范围。我用下来比较稳的参数组合是参数取值范围备注外层种群规模20~50太小容易早熟太大计算慢外层迭代次数100~300视收敛曲线调整内层种群规模15~30够用即可不宜过大内层迭代次数30~80保证用户能逼近最优响应混沌映射迭代次数500初始化时生成均匀序列自适应权重范围0.4~0.9前期探索、后期开发高斯变异概率0.05~0.1扰动最优解防早熟补充一条经验不同用户的可转移负荷时间窗口和内层优化难度差异很大如果所有用户都用同样的内层迭代次数可能有的用户早收敛了还在空转有的用户还没收敛就停了。更聪明的做法是给每个用户单独计算收敛标志达到精度就提前结束内层搜索能节省不少时间。5. 仿真条件与结果分析应该怎么看5.1 测试场景怎么设置为了验证模型有效性我设置了一个包含20个居民用户的社区场景调度周期为24小时时间粒度为1小时。每户的基础负荷曲线参照典型居民用电模式生成晚高峰集中在18点到22点其中12户配置了可转移负荷洗衣机、洗碗机8户配置了可削减负荷空调4户额外配置了家庭储能。电价方面运营商购电成本用分时结构峰时段购电成本0.8元/kWh平时段0.5元/kWh谷时段0.25元/kWh。运营商制定的售电电价上下限分别设为0.3到1.2元/kWh确保有足够的调节空间。这些参数都是模拟值实际工程中应该根据当地电力市场数据和用户调研来标定但算法流程完全一致。5.2 双层算法的收敛性和均衡结果验证外层WOA大约迭代到80代左右运营商目标函数值趋于稳定没有出现明显的震荡说明算法收敛性良好。迭代早期目标函数快速下降这是混沌初始化和自适应权重带来的探索能力在起作用后期曲线平滑下降说明局部开发能力在线。内层博弈的收敛验证上我统计了连续两轮迭代中每个用户负荷曲线的最大变化量最后稳定在0.01kW以内说明达到了数值意义下的纳什均衡——每个用户在当前电价和其他用户策略下都不再有动力单方面调整用电计划。为了验证模型的合理性我做了三组对比无调度场景不实行分时电价用户按自己的习惯用电单层优化场景直接用标准WOA对全局决策变量统一优化双层博弈场景本文模型。结果中比较关键的几个指标包括运营商收益、用户平均电费、系统峰谷差、负荷率。为了方便阅读整理成了下面的表指标无调度单层优化双层博弈运营商收益元基准值提升约8%提升约15%用户平均电费元基准值降低约6%降低约12%系统峰谷差kW基准值削减约18%削减约26%负荷率基准值提升3个百分点提升5个百分点当然这组数字是对应我设置的场景参数得到的换场景绝对值会有变化但趋势是稳定的双层博弈模型能兼顾运营商和用户双方利益同时较好地改善负荷曲线这正是引入博弈机制的价值。5.3 从结果中能看到什么从最优电价曲线看运营商在晚高峰时段定价明显上浮在凌晨低谷时段定价下调形成了典型的峰谷价差结构。用户的响应也符合预期可转移负荷大量挪到谷时段启动可削减负荷在峰时段自动降功率储能设备在谷时段充电、峰时段放电。这个结果说明模型确实在学习一种“电价引导用户自主响应”的调度模式而不是靠行政命令强制用户改变用电行为。对于电力市场环境下的需求响应来说这种机制更有实际可行性用户的接受度也更高。6. 调试过程中踩过的坑和排查技巧6.1 内层不收敛导致外层目标值震荡第一次完整跑通模型的时候运营商目标值一直在跳曲线上下来回震荡。我一开始以为是外层算法参数没调好反复改种群和迭代次数都没用。后来仔细看数据才发现问题是出在内层内层迭代次数设得不够用户压根没有收敛到最优响应外层拿到的总负荷曲线质量很差目标值自然就飘。解决办法是把内层的收敛判断从“固定迭代次数”改成“最大迭代次数误差容忍度”双重标准并输出每个用户的实际收敛状态。如果发现多个用户经常达到最大迭代次数还没收敛就要考虑是内层种群规模太小还是用户目标函数太复杂。我最后把内层迭代次数从30提高到60并适当调小了内层搜索的决策空间边界震荡问题就解决了。6.2 容量约束罚函数系数不好定罚函数系数太小用户会无视配变容量约束总负荷曲线频繁越限系数太大算法只顾着满足约束运营商收益和用户电费指标都很差。这个矛盾在双层结构里会被放大因为内层用户决策要满足约束外层电价又要考虑总负荷两层对约束的感知不一样。我的经验是分两步走先固定一个偏大的罚函数系数让算法优先保证约束不越限跑通整个流程然后逐步降低系数观察约束违反次数和经济性指标的变化找到一个“约束几乎不违反、经济性指标尚可”的临界值。这种做法比单纯靠感觉调系数靠谱得多。6.3 计算时间太长怎么压缩双层算法的计算瓶颈在内层外层每个个体都要调用一次完整的内层博弈求解内层又有多个用户各自跑一次WOA循环套循环时间直接爆炸。我最初跑一个场景要四十多分钟后来做了三个优化时间压到五分钟左右。第一个优化是向量化用户目标函数计算把用户决策变量做成矩阵运算而不是用for循环逐时段计算第二个优化是内层种群和迭代次数分档简单的用户用小规模搜索复杂的用户用大规模搜索而不是一刀切第三个优化是并行计算外层个体的适应度评估互相独立完全可以用parfor替代forCPU核心多的话速度提升非常明显。6.4 所有用户负荷曲线几乎一样这个问题比较隐蔽我一度以为模型坏了。检查之后发现是用户参数设置的问题虽然定义了20个用户但他们的可转移负荷时间段、舒适度偏好、基础负荷曲线几乎一样那么不同用户面对同一电价的最优响应自然也一样。这本身没有错但会让“用户差异”这个关键信息丢失非合作博弈的用户间竞争也没法体现。要想让模型有实际意义必须把用户参数的差异性做出来。基础负荷加随机扰动可转移负荷的时间窗口错开分布舒适度惩罚系数按用户类型分布设定。这样每个用户的策略空间就不同了博弈的结果才能反映真实场景的多样性。7. 常见问题速查与后续扩展方向7.1 故障排查速查表调试过程中最常遇到的现象、原因和解决办法整理成一张表放在这跑模型遇到问题可以先对照自查现象可能原因解决方法外层目标值震荡不收敛内层未搜索到最优响应增加内层迭代次数或改用更高精度内层求解收敛过快但结果很差种群过早陷入局部最优检查混沌初始化是否生效调大高斯变异概率总负荷总是越过容量上限罚函数系数偏小增大罚函数系数或增加越限量惩罚项电价全部贴到边界值目标函数缺少电价平滑项在运营商目标中增加电价波动惩罚运行时间过长内层计算量太大向量化目标函数、对内层参数分档、启用并行各用户负荷曲线几乎相同用户参数差异不足给基础负荷、时间窗口、偏好系数增加随机差异单次运行结果波动大算法随机性导致结果不稳定增加外层种群规模或多次运行取最优7.2 后续可以怎么扩展这个模型框架的扩展性很好。最常见的扩展方向有三个第一是接入更多类型的可调资源。电动汽车充电桩是很典型的可转移负荷而且充电时间灵活、功率大对居民负荷曲线的影响越来越显著。把电动汽车充电模型并入用户决策层只要在用户目标函数和约束里增加充电状态变量、充电功率上下限和车主期望电量约束即可。第二是考虑分布式光伏和储能。光伏出力有很强的不确定性用户侧储能可以平抑这种波动。引入不确定性之后双层博弈模型可以跟鲁棒优化或者场景法结合内层用户决策变成多场景期望成本最小化模型会更复杂但更贴近实际。第三是从单社区扩展到多社区协调。多个社区共享上级配电线路时运营商层面也可以引入博弈或者多目标协调机制形成“运营商间博弈用户间博弈”的双层嵌套博弈研究难度更大但学术价值也更高。7.3 个人实操经验总结做这套模型的过程中我最大的体会是双层鲸鱼算法本身并不神秘它其实就是把一个复杂的嵌套优化问题通过“外层搜策略、内层搜响应”的方式拆成了两个相对简单的搜索任务。难点不在算法原理而在数据流设计和参数调试哪个变量放在哪一层算、内层返回什么给外层、约束用惩罚还是修复这些工程细节才真正决定模型能不能用。另外强烈建议大家在设计完模型之后先用小规模场景把每层代码单独验证一遍比如先把内层用户的优化单独拿出来跑确认每个用户在给定电价下确实能把自己的电费降下来再固定用户响应只跑外层电价优化确认运营商收益在提升。每一步都验证过了再合起来联调出问题的时候能省下大量排查时间。如果你正准备复现这个模型建议先从最简单版本开始10个用户、只考虑可转移负荷、内层用普通WOA、外层迭代次数150次。跑通之后再逐步增加可削减负荷、储能、用户差异和博弈复杂度。一口吃不成胖子双层模型尤其需要层层递进地搭。