ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

居民负荷分层调度中的非合作博弈模型与双层鲸鱼算法求解实践

居民负荷分层调度中的非合作博弈模型与双层鲸鱼算法求解实践 做居民负荷调度这块最近不少同行都卡在同一个点上用户不是机器不会乖乖听你下指令。集中式优化假设所有用户服从全局最优调度但落到实际中每家每户都有一本自己的账省多少钱、舒不舒适优先级高得多。直接做“最优调度”算出来的方案往往没法落地。我前阵子把基于非合作博弈的居民负荷分层调度模型完整做了一遍用双层鲸鱼算法求解Matlab代码实现。这个思路的关键不是“算得有多准”而是把用户的自利行为变成可调度的资源。本文就把模型设计、算法原理、Matlab实现和踩坑记录完整盘一遍适合正在做电力需求响应、综合能源调度的研究生和工程师参考。1. 为什么一定是“分层博弈”居民负荷调度的底层逻辑1.1 居民负荷调度不是单纯的优化问题先说痛点。居民侧负荷占比越来越高分布式光伏、电动汽车充电桩、空调这些负荷同时在线时台区变压器过载、电压越限的问题越来越频繁。传统做法是负荷聚合商或配电网调度中心直接下发“什么时候开、什么时候关”的指令类似工业负荷的精益控制。但居民负荷数量大、单户容量小、隐私敏感集中控制根本不现实。更深层的问题是居民用户的行为驱动逻辑和调度目标不一致。调度方希望削峰填谷、降低峰谷差用户只关心自己的电费账单和舒适度。你把洗衣机硬调到凌晨三点用户可能不答应你把空调温度权限收走用户更不会买账。所以居民负荷调度本质上不是纯优化问题而是一个“多方利益协商”的问题既要考虑电网侧的效率和公平又要尊重用户侧的自主决策权。这就引出两个关键词一个是“分层”另一个是“非合作博弈”。分层解决的是“不同角色在不同层面决策”的问题博弈解决的是“各角色目标不一致时怎么达到稳态”的问题。把两者放在一起才符合居民负荷调度的真实场景。1.2 非合作博弈把“自私”变成调度资源我第一次接触这个模型时最大的困惑是用户都自私自利怎么可能帮我削峰填谷后来想明白了博弈论不是去消除自私而是利用自私。每个居民用户在给定电价和激励信号后会做自己的用电计划负荷能平移的就平移到低价时段能削减的就适当削减以获取补贴不能动的就保持原样。每个用户之间没有串通各自最优化自己的成本这就是典型的非合作博弈。从调度方的角度看用户的这种“自私”响应反而形成了一种可预测的弹性只要价格信号设计得当整体负荷曲线就会自动趋于削峰填谷。这里有一个很多人忽略的点用户之间的决策是相互影响的。比如一个台区里有20户如果某一时段电价很低大家都把电热水器、电动汽车充电挪过去该时段总负荷暴涨电压可能越限。用户的成本函数里如果不考虑台区公共约束这种“扎堆充电”就会发生。所以模型里通常要在上层加上峰谷差惩罚或变压器容量约束下层用户之间通过价格间接博弈。非合作博弈的纳什均衡就是在这些互相对抗的目标中找到一个“谁都不愿意单方面改变”的稳定点。1.3 分层模型与双层优化的对应关系“分层”在居民负荷调度里有两层含义。第一层是决策层级上层是系统运营商或负荷聚合商负责制定分时电价、激励补贴等价格型信号下层是居民用户负责根据价格信号安排具体设备的运行时段和功率。第二层是负荷特性层级负荷本身分刚性负荷、可转移负荷、可削减负荷和储能型负荷不同层级负荷的调度方式和约束条件完全不同。把这种分层决策用数学语言写出来就是双层优化模型。上层目标函数最大化运营商收益同时考虑削峰填谷效果约束是电价上下限、峰谷价差限制。下层目标函数最小化用户用电费用和舒适度损失约束是设备运行时间窗口、总用电量、空调温度范围等。上下层耦合关系上层定价影响下层用电计划下层用电计划汇总后反过来决定运营商的实际收益和系统峰谷差。这是一个典型的Stackelberg博弈结构也叫“领导者-追随者”模型。运营商先出价用户再响应用电运营商再根据用户响应调整价格反复迭代达到均衡。双层优化和普通单层优化最大的区别就在这个“先决策后响应再反馈”的过程无法简单把所有约束和目标写进一个大优化问题里求解。1.4 为什么不用现成的商业求解器有人会问双层优化不是可以用KKT条件转成单层吗理论上可以但实际做起来很痛苦。底层用户决策变量里常常带有整数变量比如洗衣机的启停时段、电动汽车充电的开始时间这类0-1变量让下层问题变成混合整数二次规划。把下层KKT条件代入上层后引入互补约束和非线性项问题变成一个复杂的数学规划规模稍大一点求解时间就完全失控。所以工程上倾向于用元启发式算法来逼近双层最优解。双层鲸鱼算法的思路很直接外层鲸鱼算法搜索上层的电价策略内层对每个候选电价求解下层用户的用电计划子问题再把下层的结果返回上层计算适应度值。算法对模型的解析性质要求很低适合处理这种“上层连续、下层混合整数”的嵌套结构。这也是我把双向海洋生物算法的思路进行双层改造的初衷。2. 双层鲸鱼算法怎么在上下两层之间找均衡2.1 经典鲸鱼算法的三位捕食策略鲸鱼算法WN算法模拟的是座头鲸的捕食行为核心是“收缩包围、螺旋气泡网、随机搜索”三种策略。算法里每头鲸鱼是一个候选解通过更新位置来逼近最优解。收缩包围机制当系数向量A的模小于1时鲸鱼向当前最优个体靠拢位置更新公式是新建搜索代理位置 当前最优位置 - A * D其中D是当前个体与最优个体的距离向量。螺旋气泡网策略以一定概率同时进行螺旋更新模拟鲸鱼一边吐气泡一边绕圈逼近猎物的过程位置更新中加入对数螺旋路径。随机搜索机制当A的模大于等于1时随机选取一头鲸鱼作为参照执行全局搜索避免陷入局部最优。在整个迭代过程中收敛因子a从2线性递减到0A在[-a,a]区间内波动前期偏重全局探索后期偏重局部开采。经典WN算法实现简单、参数少但随着问题复杂度上升容易出现早熟收敛。于是做双层改造时不少人会在外层加入自适应权重或差分变异算子用动态惯性权重来平衡探索和开发能力。2.2 双层鲸鱼算法的“外-内”结构设计把鲸鱼算法扩展成“双层鲸鱼算法”核心不在于把两个鲸鱼种群串起来而在于定义一个套嵌式的求解逻辑。我采用的实现框架如下外层用鲸鱼种群表示上层决策变量。一个鲸鱼个体就是一组电价策略比如[峰时电价、平时电价、谷时电价]。每个电价策略都满足上层约束比如电价必须在合理范围内、峰谷价差不超过阈值。内层对每一个外层个体把电价参数传给下层模型求解每个居民用户的最优用电计划。下层用户可以独立求解也可以按户并行。适应度评价把下层返回的负荷曲线汇总后代入上层目标函数计算当前电价策略下运营商的收益和系统峰谷差。若违反约束则加惩罚项。进化更新根据适应度值更新外层鲸鱼种群的位置重复“定价-响应-评价-更新”的过程直到达到最大迭代次数或收敛阈值。这种设计的妙处在于内层下层子问题仍然可以使用Matlab自带的linprog、intlinprog等求解器不需要硬套鲸鱼算法。外层只管搜电价内层管用电计划的精确求解避免了“上下两层都用启发式、误差层层放大”的问题。实践下来这种方式比把下层也用鲸鱼算法的方案更稳定计算时间也更可控。2.3 关键改进与参数设置说明我在实现中对标准鲸鱼算法做了几个不太大但很有效的改动加入种群精英保留策略每代最好的几个个体不参与位置更新直接复制到下一代防止最优解被扰动。对A和C参数重新初始化每个维度单独生成随机数而不是用同一个随机数增加搜索多样性。加入越界修正机制电价个体更新后如果越界不直接拖回边界而是按边界附近的反向扰动处理避免种群扎堆在边界上。对重复个体做小高斯扰动防止多个鲸鱼个体完全相同导致种群多样性下降。参数设置上外层种群大小我习惯取30~50最大迭代次数取100~200内层每个用户的下层问题用求解器解决所以不需要太多迭代。这里不建议一开始就把种群调到很大嵌套优化每多一个个体就多几十次下层求解计算量翻倍很快。先小种群跑通再逐渐放大。3. Matlab实现模型搭建、编码方式与完整求解流程3.1 算例与基础数据准备为了方便说明我设置了一个小规模算例考虑一个负荷聚合商下面有10户居民用户调度周期是24小时时间分辨率取1小时。每户包含空调、电热水器、洗衣机、洗碗机、电动汽车充电桩和若干基础照明负荷。负荷参数通过历史用电数据统计得到比如空调额定功率2kW、可削减比例30%、舒适温度区间[22,26]摄氏度电动汽车剩余充电需求10kWh必须在0点到8点之间充满。峰平谷时段的划分不必太复杂我直接按常用三段式时段类型时间段上网购电价格基准元/kWh运营商可设定范围元/kWh峰时段10:00-14:00、18:00-21:000.70[0.40, 1.00]平时段06:00-10:00、14:00-18:000.50[0.20, 0.80]谷时段00:00-06:00、21:00-24:000.30[0.10, 0.60]上层目标除了最大化运营商售电收益还要加上峰谷差最小化的惩罚项避免运营商为了赚钱把峰时电价抬得太高结果用户干脆不用电、负荷曲线更差。上层决策变量可以直接取三个时段的电价也可以取全天24小时的电价曲线。前者简单直观后者灵活但维度更高外层搜索计算量更大。第一次做建议先用三段电价。3.2 上层电价编码与下层响应函数设计对外层鲸鱼算法而言每个个体的编码向量就是[峰时电价, 平时电价, 谷时电价]。这三个变量是连续值边界就是表中给的可设定范围。此外还要加一个约束峰时电价必须大于平时电价平时电价必须大于谷时电价否则用户没有削峰填谷的响应动机。这个约束不在鲸鱼算法的位置更新里直接处理而是在适应度函数里加一个很大的惩罚值把不满足电价时序关系的个体淘汰掉。下层响应函数是整个程序的核心接口。输入是某个电价方案和用户的基本负荷参数输出是每个用户的设备运行计划、全天用电曲线、用电成本和舒适度损失。下层问题的目标函数包含两项购电费用和舒适度损失。比如空调在保证室温舒适的前提下尽量少开洗衣机和洗碗机在允许的时间窗口内选择总费用最低的运行时段电动汽车尽量在谷时段充电。设备时间变量用0-1变量表示整个下层问题可以表达成一个混合整数线性规划。这里有一个非常实用的建模技巧不要把下层问题按“所有用户一个大优化问题”求解而是让每个用户独立求解自己的小问题。因为用户之间没有直接约束独立求解既能反映非合作博弈的本质也天然适合并行计算。10个用户就是10个独立优化问题彼此不耦合用parfor并行循环轻松加速。3.3 核心Matlab代码框架下面给出简化但可运行的Matlab代码框架。省略了详细参数导入部分核心逻辑都在。%% 主程序双层鲸鱼算法求解居民负荷分层调度模型 clear; clc; rng(42); load(household_load_data.mat); % 包含load_base, price_ref, device_params等 % 鲸鱼算法参数 N 30; % 外层种群规模 MaxIt 150; % 外层最大迭代次数 dim 3; % [峰时电价, 平时电价, 谷时电价] lb [0.40, 0.20, 0.10]; ub [1.00, 0.80, 0.60]; % 初始化种群 x zeros(N, dim); fx zeros(N, 1); for i 1:N x(i,:) lb (ub - lb) .* rand(1, dim); fx(i) my_upper_fitness(x(i,:), household_load_data); end % 记录最优 [best_f, best_idx] min(fx); x_best x(best_idx, :); % 外层迭代 a_init 2; a_end 0; a_curr a_init; for t 1:MaxIt a_curr 2 - t * (2 / MaxIt); for i 1:N r1 rand; r2 rand; A 2 * a_curr * r1 - a_curr; C 2 * r2; p rand; if p 0.5 if abs(A) 1 % 收缩包围 D abs(C * x_best - x(i,:)); x_new x_best - A * D; else % 随机搜索 rand_idx randi(N); D abs(C * x(rand_idx,:) - x(i,:)); x_new x(rand_idx,:) - A * D; end else % 螺旋更新 D abs(x_best - x(i,:)); b 1; l (rand - 0.5) * 2; x_new D * exp(b*l) * cos(2*pi*l) x_best; end % 边界修正 x_new max(min(x_new, ub), lb); % 计算新个体适应度 f_new my_upper_fitness(x_new, household_load_data); if f_new fx(i) x(i,:) x_new; fx(i) f_new; end end % 精英更新 [best_f, best_idx] min(fx); x_best x(best_idx, :); % 记录收敛曲线 converge_curve(t) best_f; end %% 上层适应度函数 function f my_upper_fitness(price, data) % 对给定电价求解所有用户的下层响应 [load_curve, cost_user, comfort, op_profit, peak_valley] ... solve_lower(price, data); % 上层目标运营商收益取负 峰谷差惩罚 lambda 200; % 峰谷差惩罚系数 f -op_profit lambda * peak_valley; end下面给一个下层求解函数的缩减版结构以说明“输入电价、输出负荷曲线”的接口设计思路。真正项目中下层对每户用户分别调用intlinprog求解。function [load_curve, cost_user, comfort, op_profit, pv] ... solve_lower(price, data) n_user data.n_user; T data.T; % 24小时 load_total zeros(1, T); % 并行求解各用户 parfor u 1:n_user % 构建该用户的设备约束矩阵Aeq、beq、A、b以及电价向量c [A,b,Aeq,beq,lb,ub,intcon,c] build_user_model(u, price, data); [x_opt, fval] intlinprog(c, intcon, A, b, Aeq, beq, lb, ub); assert(~isempty(x_opt), 用户% d的子问题求解失败, u); % fval是用户购电费用舒适度损失记录并计算该用户负荷 cost_user(u) fval; comfort(u) get_comfort(x_opt, u, data); load_curve(u,:) get_user_load(x_opt, u, data); end load_curve sum(load_curve, 1); % 根据负荷曲线和电价计算运营商收益 op_profit sum(price .* reshape(load_curve, 3, []), all); % 需要按时段展开 pv max(load_curve) - min(load_curve); end这里要特别提醒上层收益计算不能简单用“峰平谷电价各自乘以时段总负荷再相加”还要扣除从电网购电的成本。如果运营商只是电网公司的售电部门购电成本就是上网电价或批发市场出清价如果运营商是聚合商还要考虑补贴发放成本。我在代码里用了一个简化的表示实际中建议把购电成本、补贴成本、设备维护成本都放进去否则算出来的收益虚高结果没有参考价值。3.4 结果分析与指标提取跑完程序后至少需要输出以下几类结果收敛曲线外层最优适应度随迭代次数的变化用来判断算法是否收敛、是否陷入局部最优。最优电价方案最终迭代出的峰平谷电价检查是否符合“峰平谷”的约束。用户负荷总曲线对比调度前后的负荷曲线计算峰谷差降幅、负荷率提升。各用户成本和舒适度看用户的成本是否真的下降了舒适度牺牲了多少这直接决定模型是否可落地。纳什均衡验证可以固定其他用户策略逐一检测单个用户单方面改变策略是否还能降低自己的成本若不能说明当前解接近均衡点。有一次我跑完模型峰谷差下降30%但用户的舒适度损失达到每小时0.8元等值费用用户根本不愿意按这个计划执行。后来我降低了舒适度惩罚系数并且保留了空调的“减免额度”机制允许用户用少量电费换舒适度才真正让模型“能用”。这个经验告诉我指标不能只看峰谷差一定要看用户侧的感受。4. 实操中踩过的坑收敛、边界与计算效率问题4.1 典型问题速查表我把实操中反复出现的几类问题整理成了一张速查表方便大家对照排查。问题现象可能原因处理办法外层收敛曲线一直震荡下层子问题求解精度不够固定随机种子提高内层求解精度减少并行随机性电价个体大量集中在边界上目标函数没有平衡削峰填谷和运营收益增加峰谷差惩罚项设置合理的峰谷价差约束用户负荷全部挤到谷时段舒适度惩罚过小增大舒适度折算成本给可削减负荷设置最低使用时长求解时间过长外层种群太大 / 下层求解器重复调用过多减小种群到20~25提高内层求解器容差用parfor并行得到的“最优”电价每一轮都不一样启发式算法存在随机性且问题非凸多次运行取中位数同时记录标准差下层子问题出现无解电价过低导致用户成本函数无法满足设备运行约束检查并放宽时间窗口约束或对不可行个体施加惩罚4.2 关于收敛性和“均衡”的验证心得双层鲸鱼算法最大的短板就是难以严格证明收敛到全局最优解。像这种嵌套结构每一代外层个体都要跑很多次下层优化下层又是混合整数规划整体目标函数凹凸性未知局部最优几乎必然存在。指望一次性找到一个全局最优不太现实。我的做法是同类问题跑20次取适应度值的中位数对应的解作为最终方案同时观察20次结果的标准差。如果标准差很小说明算法对这个算例足够稳定即使不是全局最优也具备工程可复现性。另一个容易被忽视的点是外层种群的随机种子。Matlab里如果并行开了不同worker上的随机数流不同每次结果差异很大。建议在parfor内部固定随机流。更稳妥的做法是下层全部用确定性求解器比如intlinprog保证对于同一个电价输入下层输出完全相同这样外层叠加的随机性只来自鲸鱼算法本身。我遇到过一种很隐蔽的情况下层用户各自独立优化后总负荷叠加后超过台区变压器容量上限但模型并没有这个约束。后来在上层目标里加了过载惩罚项只要总负荷超过变压器上限就大大降低适应度值。这样算出来的电价方案会在谷时段设置相对高的电价避免所有用户扎堆充电。这类约束在真实项目中比理论模型里常见的线路潮流约束更容易落地建议第一时间加上。最后一个心得不要一开始就追求复杂的双层鲸鱼变体。先用基础版WN算法跑通标准算例确认上层函数、下层响应、收益计算的逻辑没问题再逐步加入自适应权重、精英策略、混合变异这些改进。我见过很多人一上来就在算法里堆了一堆改进策略结果模型出问题时连是建模错误还是算法缺陷都分不清。先把地基打稳扩展方向自然会清晰比如把电价从三段式扩展成24小时动态电价把单台区扩展为多台区博弈或者加入分布式光伏出力和储能参与响应这套双层鲸鱼框架都能无缝接上。
返回列表