ARTICLE DETAIL

资讯详情

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

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

双层鲸鱼算法求解非合作博弈下居民负荷分层调度模型实践 把“双层鲸鱼算法求解基于非合作博弈的居民负荷分层调度模型”这套代码完整跑通比我预想中要费劲不少。外层鲸鱼负责搜电价内层鲸鱼负责搜每条负荷曲线两层套在一起再加上博弈迭代Matlab一跑就是几十分钟。但跑通之后效果确实对得起这个复杂度阶梯电价、户内设备、用户响应行为都能放到同一个框架里优化算出来的调度方案既照顾了售电公司利益也考虑了居民电费支出。这篇文章就把这套模型的建模思路、算法设计、Matlab代码骨架和实际调试经验完整梳理一遍给准备做需求响应、负荷调度、博弈优化方向的朋友一个能直接参考的路线。模型本身不算新概念但“双层算法非合作博弈分层调度”三个东西组合到一起后很多细节就不是看论文能立刻明白的了尤其是双层嵌套的迭代逻辑、适应度函数怎么设计、内外层参数怎么协调。我会尽量把每一步为什么这么做讲清楚不是单纯贴一段代码。1. 模型设计思路为什么用双层鲸鱼算法求解非合作博弈1.1 分层调度的结构先看清到底分了哪几层居民负荷分层调度核心不是“算法分层”而是“决策主体分层”。我在这套模型里把系统拆成了两层上层是售电公司或者负荷聚合商它决定发布什么样的电价信号下层是参与调度的居民用户他们根据电价信号安排自家各类电器的用电时间。两层之间有明确的主从关系上层先行动下层后响应这种结构在数学上叫Stackelberg博弈也是一种特殊的非合作博弈。为什么要把问题拆成两层而不是直接做一个集中优化因为居民负荷调度本质上不是一个决策者的问题。一个小区几百户人家每家都有自己的用电习惯和舒适度需求你不能指望一个集中式控制器替所有人做决定。真实场景里售电公司只能通过电价这种间接手段去引导用户行为用户是自由响应者。分层之后上层只负责定价格下层只负责响应价格这样才贴合实际。还有一层意思容易被忽略居民用户内部其实也有一些子问题可以分层处理比如单个用户家里的空调、热水器、电动汽车这些设备各自有运行约束用户需要在电费和舒适度之间做权衡。但为了避免模型过重我在这里把用户内部做了一个聚合处理——每个用户看作一个整体响应节点内部设备约束放到下层的子问题里解决这样整体上还是“上层售电公司、下层居民用户”的两层结构。1.2 非合作博弈如何嵌入分层模型博弈论里非合作博弈指的是参与人各自追求自身利益最大化不搞结盟。居民负荷调度天然就是非合作场景每一户居民都在意自己的电费单不会主动替邻居省钱。所以在下层所有居民用户并排建模各自独立求解自己的最优用电计划这就是非合作博弈的嵌入点。在这种结构下每个用户的用电计划会互相影响但影响不是直接的——它们通过上层电价间接耦合。电价高的时候大家都不约而同地把负荷往后挪电价低的时候大家又集中用电。这种耦合关系用博弈论的视角看每个用户的策略集合就是自己的设备功率曲线收益函数就是负的电费支出加上一定的不舒适度惩罚。多个用户同时优化最后达到的状态就是一个非合作博弈的均衡。那二层博弈和纳什均衡怎么对应这里要区分两个层次外层主从博弈讨论的是售电公司和用户之间解的概念是Stackelberg均衡内层多个用户之间是同时决策的非合作博弈解的概念是纳什均衡。实际实现时我用双层鲸鱼算法去逼近这个均衡外层鲸鱼迭代更新电价内层鲸鱼迭代求解所有用户在给定电价下的最优响应两层交替循环直到电价变化很小、用户响应也不再变化就认为收敛到了一个可接受的均衡点。严格数学证明这套均衡一定存在且唯一比较困难但工程上通过多次随机初始化验证只要结果稳定就可以接受。1.3 双层鲸鱼算法在整个求解链路里的角色先说结论我这里的“双层鲸鱼算法”指的是外层用一个鲸鱼优化算法(WOA)搜索电价策略内层再用一个鲸鱼优化算法搜索所有用户在给定电价下的最优用电计划两层都采用标准鲸鱼算法的三种位置更新机制。为什么外层选鲸鱼算法而不选遗传算法或者粒子群外层需要搜索的变量是24小时的分时电价连续性强并且目标函数——也就是下层用户的总响应——是非线性的、不可导的还可能有多个局部最优点。鲸鱼算法对连续变量的全局搜索能力不错实现简单参数少尤其适合这种嵌入一个昂贵适应度函数的场景。PSO当然也能做但WOA在种群多样性保持上稍微好一点而且它的气泡网收缩机制对精细化搜索很有用可以在电价搜索后期把解打磨得更细腻。内层为什么也用鲸鱼算法内层要搜的是用户设备功率曲线。设备启停是离散变量功率大小是连续变量混合在一起用传统梯度方法并不好处理。虽然理论上可以用分支定界或者整数规划求解但一旦模型扩展到几十户上百户内层问题规模会爆炸。用WOA做内层求解器好处是不需要梯度没有凸性假设设备模型再怎么非线性都能塞进去。代价是需要仔细设计编码方式我在下面会专门展开。2. 负荷分层调度模型怎么用数学写出来2.1 上层目标函数售电利润与削峰填谷的平衡上层售电公司/负荷聚合商的优化目标我设为在满足系统安全运行的前提下最大化自身收益。这里有一个常见的做法目标函数写成售电收入减去购电成本如果希望模型还能兼顾削峰填谷可以在目标里加一个峰谷差惩罚项。数学形式大致是这样把一天分成24个时段上层决策变量是每个时段的售电价p_tt 1,2,…,24同时有一个回购电价或者基准购电成本c_t。全体用户在时段t的总用电功率记为P_sum,t这个值来自于下层用户的响应结果。上层的利润函数为profit sum_t [ p_t * P_sum,t - c_t * P_sum,t ]也就是售电收入减去购电成本。如果考虑峰谷差抑制就再加一项 - lambda * (max(P_sum) - min(P_sum))lambda是权重系数。权重越大售电公司越愿意牺牲一部分利润换取更平稳的负荷曲线。约束方面电价不允许无限高或无限低需要给定上下限比如峰时电价上限1.2元/kWh谷时电价下限0.3元/kWh。另外分时电价通常还有一个不成文的约束峰、平、谷电价必须有明显梯度否则用户没有响应的动力。这些约束在鲸鱼算法里通过边界处理和罚函数方式实现。外层鲸鱼每更新一次电价都要调用一次下层求解过程得到用户响应后才能算出这个电价对应的利润值。所以整个适应度求值过程非常耗时这也是这套模型运行慢的根本原因后面调试章节我会专门说提速办法。2.2 下层目标函数居民电费最小化与舒适度约束下层的每个居民用户目标是在24小时内决定自己各类电器的用电功率曲线使得总电费最小同时尽量不牺牲舒适度。用户i的目标函数写成min cost_i sum_t [ p_t * P_i,t ] alpha_i * sum_t [ D_i,t ]其中P_i,t是用户在时段t的总用电功率D_i,t是不舒适度惩罚量alpha_i是用户对舒适度的敏感系数。用户对电价越敏感alpha越小就会更多地把负荷搬到低价时段相反alpha大的用户宁愿多花点电费也不愿意改变习惯。这里有几个细节值得注意。一是不同用户的alpha取值不能全部一样否则就退化成多用户完全同质响应非合作博弈就没意义了。通常我会设置三类用户价格敏感型(alpha小比如0.2)、中性型(alpha1)、舒适优先型(alpha2.5~3)。二是不舒适度惩罚需要定义清楚不能模糊处理。我把D_i,t定义成设备设定温度/运行时段偏离用户偏好值的平方差偏离越大惩罚越大。下层约束包括每个用户的总用电量在一个调度周期内大致保持不变对于可转移负荷而言、每个设备的功率不能超过额定上下限、部分设备必须满足最小运行时间、空调/热水器这类蓄能设备需要满足温度或热量的动态约束。所有约束在鲸鱼算法里通过不等式罚函数处理罚系数要比目标函数里的量纲高一个数量级确保优化过程优先满足约束。2.3 负荷分类与设备行为建模居民负荷要参与调度先得分类。我给这套模型分了三大类可转移负荷、可削减负荷、刚性负荷。可转移负荷指的是洗衣机、洗碗机、电动汽车充电这类设备特征是总用电量基本固定但运行时间窗口可以调整。比如电动汽车充电晚上6点到第二天早上7点之间都可以充总充电量由电池容量决定具体在哪个时段充就取决于电价了。这类负荷建模时只需要给一个允许运行的时间窗口和总耗电量是下层优化中灵活性最大的部分也是削峰填谷的主要贡献者。可削减负荷指的是空调、电热水器这类温控负荷特征是可以在一段时间内降低功率运行但不能长时间关停否则温度会超出舒适范围。这类负荷的功率在上下限之间连续可调我用一个简化的热力学模型描述室温变化取决于制冷功率和室外温度舒适度惩罚就看室温偏离设定点多远。空调负荷建模时有一个关键点不要把功率直接当成可转移电量它是受温度约束的压缩机可以降功率但不可能完全停掉还保持室温不变。刚性负荷就是照明、电视、冰箱这类调度价值很低。我干脆把它们按固定曲线处理不参与优化只作为功率基线的组成部分。这样既能降低模型复杂度也不影响核心调度逻辑。实际应用时可以把部分照明和电视纳入可削减负荷池但模型规模会成倍增长我建议做研究时先保持三类简化后续再扩展。2.4 约束条件与算例参数设置为了让你能直接上手复现我把我用的算例参数列出来。24时段调度3个典型居民用户每户5类设备空调、电热水器、电动汽车、洗衣机、照明/电视基础负荷。基础参数设置如下参数取值说明调度时段数24 h以小时为最小调度单位可转移负荷比例20%-30%占用户总用电比例可削减负荷比例15%-25%主要是空调和热水器刚性负荷比例45%-55%固定功率电价下限0.3 元/kWh谷时段电价上限1.2 元/kWh峰时段负荷聚合商购电价0.45-0.8 元/kWh随时间变化舒适度权重alpha0.2/1.0/2.5三档用户空调舒适温度区间24-27℃制冷模式约束条件方面除了上面提到的设备功率限幅和总电量约束还要注意一个逻辑所有用户的总负荷曲线峰值不能超过配电变压器容量上限。这个约束在上层判断电价方案是否可行时非常关键如果某条电价导致所有用户同时把负荷转移到同一个低价时段结果可能“峰”没削掉反而造出新的“峰”。我实际调试时遇到过这个问题后面会展开讲。3. 双层鲸鱼算法在Matlab中的代码实现3.1 WOA算法核心机制回顾标准鲸鱼优化算法模拟座头鲸捕猎行为有三种位置更新机制。第一种是包围猎物也就是朝当前最优解收缩位置更新公式写作X_new X_best - A * D其中D |C * X_best - X|A和C是系数向量。第二种是气泡网捕食生成一个螺旋路径向最优解靠拢公式是X_new D * exp(b*l) * cos(2πl) X_best。第三种是随机搜索当A的绝对值大于1时随机选一个个体作为参考点因为鲸鱼认为当前最优解可能不是猎物位置这个机制保证了全局探索能力。三个机制在迭代中按概率p和系数A切换。参数A在迭代过程中从2线性衰减到0实现从全局探索到局部开发的过渡。网上能找到很多标准WOA的Matlab实现直接拿来改就行。但双层嵌套的写法不能简单套两个独立的WOA必须处理好内外层数据传递、边界一致性、以及种群个体之间的独立性。3.2 外层鲸鱼搜索24小时电价策略外层鲸鱼的每个个体代表一组完整的24小时电价序列。种群规模我设为20到30编码为一个24维连续向量每个维度取值在电价上下限之间。初始化时不能完全随机我会在基准分时电价附近叠加一个随机扰动这样能加快收敛。完全随机初始化的鲸鱼种群可能要迭代几十代才能摸到合理电价区间前期全是无效计算。外层适应度函数流程是每个电价个体传入内层求解器内层返回所有用户的总负荷曲线P_sum,t然后根据这个总负荷和当前电价序列计算上层目标函数值。注意外层目标函数是最大化利润写成适应度时要取负值让鲸鱼算法默认做最小化。当然也可以修改算法内比较操作符直接做最大化但统一成最小化比较方便复用代码。有一点容易踩坑外层每次迭代会生成20条不同的电价曲线每条电价曲线都要调用一次内层完整博弈求解。如果内层迭代次数设得很大一次外层迭代就要跑很久。我建议外层种群和迭代次数都不必太大外层收敛速度其实受限于内层的求解精度内层解得糙一点外层反而更容易跳出局部最优这个经验后面细说。3.3 内层鲸鱼搜索用户负荷曲线内层求解器接收一条电价曲线然后对每个用户分别求最优用电计划。这里有两种处理方法。方法一是所有用户同时编码到一条鲸鱼个体里一次内层迭代就同时更新所有用户好处是效率高坏处是维度爆炸。举个例子3个用户、每个用户有3类弹性设备、24个时段编码维度就是3324216维WOA在200维以上的搜索空间效率会明显下降。我实际采用的是方法二每个用户单独用一条内层鲸鱼求解然后多个用户做多轮交替优化逼近非合作博弈的纳什均衡。具体而言内层先固定用户2和用户3的负荷曲线用WOA优化用户1的曲线然后固定用户1和用户3优化用户2最后优化用户3这样算作一轮非合作博弈迭代。重复几轮直到每个用户的策略变化量小于阈值。这个方法在数学上更像非合作博弈的求解过程每个用户确实是在别人策略已知的情况下做最优反应收敛性也更容易判断。内层编码方式也要设计清楚。每个用户的优化变量包括可转移负荷的24小时分配功率和可削减负荷的24小时运行功率。直接把这些功率值作为连续变量编码维度也还可以接受。需要注意功率必须满足设备功率上下限所以每次更新后要做边界截断避免鲸鱼算法更新出负功率这种荒唐值。3.4 适应度函数与主循环的代码骨架给出一个可运行的骨架结构不是完整代码但把核心逻辑都标出来了% 主函数双层鲸鱼算法求解居民负荷分层调度 % 外层参数 outerN 20; outerIter 50; lb 0.3 * ones(1,24); ub 1.2 * ones(1,24); basePrice [0.5*ones(1,6), 0.9*ones(1,6), 0.6*ones(1,6), 0.35*ones(1,6)]; % 初始化外层种群 X repmat(basePrice, outerN, 1) randn(outerN,24)*0.05; X max(min(X, ub), lb); for iter 1:outerIter for i 1:outerN price X(i,:); P_sum innerSolver(price); % 内层返回总负荷曲线 profit sum(price .* P_sum) - sum(purchaseCost .* P_sum); fitness(i) -profit penalty; % 罚函数统一 end % 标准WOA位置更新包围、螺旋、随机 % ... end function P_sum innerSolver(price) % 对每个用户做多轮最优反应直到纳什均衡 for round 1:innerRound for u 1:numUsers X_u WOA_opt(price, fixedOtherUsers, userModel{u}); userLoad(u,:) X_u; end if maxChange eps, break; end end P_sum sum(userLoad, 1) rigidLoad; end这段骨架最关键的逻辑是外层每次求适应度都阻塞等待内层跑完完整博弈内层跑完后才能计算利润。如果你想加速可以把内层的WOA替换成fmincon或quadprog求解凸子问题前提是你的设备模型满足凸性后面调试部分会再说。4. 调试中踩过的坑、调参技巧与效果分析4.1 三个最典型的运行问题第一个问题内层所有用户都朝同一个低价时段挤导致新的负荷尖峰。这是我调试时最先遇到的问题。分时电价机制下谷时电价很低所有价格敏感型用户都倾向于把电动车充电和洗衣机挪到同一个时段结果就是谷时段负荷反而超过了原本的峰时段形成新峰值。这不是算法错误是目标函数没考虑“同时性”。解决办法是上层的适应度函数里加上峰谷差惩罚项同时在配电变压器容量约束处加一个硬罚函数负荷一旦越限就处罚让鲸鱼自动避开制造新尖峰的电价方案。第二个问题外层鲸鱼种群收敛速度极慢50代迭代后电价依然在上下限之间大幅波动。后来排查发现是内层求解不精确每个用户在固定电价下的最优负荷曲线每次算出来都有随机波动导致上层看到的总负荷曲线本身带噪声适应度评价不连续鲸鱼很难判断哪个方向是真正的优化方向。解决思路是后端固定随机种子让内层WOA每次求解都是确定性结果。内层WOA本身有随机性设置rng(seed)之后同一条电价曲线每次算出的总负荷才一致这样外层才能正常做比较。第三个问题运行时间完全不可接受。3个用户、内层每用户30代、外层50代跑下来要两个多小时。后来我把内层改成每用户只迭代15代、博弈轮数从5轮降到3轮外层保留同样配置时间压缩到40分钟左右结果只有微小的精度损失。研究阶段这个时间可以接受但如果要做更重的算例必须换更高效的求解器或者对模型降维。4.2 参数怎么设种群、迭代、边界、惩罚系数外层WOA参数我建议种群20到30够用迭代次数40到60。内层WOA参数要更保守一些种群15左右、迭代10到20。为什么内层不能设大因为内层被外层的每一次适应度计算反复调用内层多10次迭代外层总运行时间就是指数级增加。这个账一定要先算明白外层种群数 × 外层迭代数 × 内层博弈轮数 × 内层迭代数任何一个数字调大总耗时都会显著上升。边界处理建议这样外层电价不要硬截断最好在越界时做一个对称映射或者把越界个体拉回边界但保留原有的搜索方向信息。硬截断会让很多个体堆到边界上导致区域拥挤探索能力下降。我后来在外层更新时加了一个越界惩罚项越界的维度按比例扣掉一部分适应度效果比单纯截断好。罚函数系数设置是个细节活。居民负荷调度模型的约束量纲和利润量纲不一样罚系数太小约束被无视太大又会让目标函数完全被罚函数主导优化变成只找可行解不看经济性。我的经验是先把目标函数量级估算出来比如利润大概每天几百元那越限罚函数就设在几千元级别确保一个约束越限的代价远大于任何合理的收益提升。你可以先用一组随机电价跑几轮观察违反约束的程度和利润范围再回头定罚系数不要凭空拍脑袋。4.3 怎么验证结果是纳什均衡模型跑完不能直接说“这已经是纳什均衡”至少要做两个层面的验证才能下结论。第一层是用户层面的纳什均衡检查固定其他用户的负荷曲线单独把某个用户的最优响应重新求解一遍看解出来的负荷曲线是否和当前模型输出一致。如果不一致说明该用户在当前状态下还有单方面改变策略的动机那就不是纳什均衡。通常需要做几次“再优化检验”逐步修正到一致。第二层是Stackelberg均衡的验证固定当前电价方案把所有用户重新求解一轮最优响应看总负荷曲线是否与模型输出一致然后再固定用户的响应曲线在外层电价可行域内搜索一下看是否存在利润更高的电价方案。如果找到了利润更高的方案说明当前电价不是最优领导策略需要继续迭代。这两层验证做下来我实测的结果是外层迭代到40代以后基本稳定电价曲线不再有大变化用户响应也基本收敛但并不是所有随机初值都能收敛到同一个解这也从侧面说明问题非凸存在多个局部均衡。需要提醒的是在非凸模型里严格意义的最优Stackelberg均衡往往很难保证全局收敛。做研究的读者可以在论文里说明采用了多次随机初始化和最优保留策略这样表述上更严谨。工程应用的角度只要结果稳定、削峰填谷效果明显、各方收益改善可接受就具备落地价值。4.4 常见问题速查表现象可能原因解决办法外层迟迟不收敛内层解有随机波动固定随机种子保证适应度确定性谷时段出现新尖峰目标函数未考虑负荷同时性增加峰谷差惩罚项程序运行时间过长内外层迭代数过高降低内层迭代数或内层改用凸优化电价跑出上下限边界处理直接截断使用对称映射或带惩罚的边界处理用户负荷曲线不合理功率越界、约束未生效边界截断和罚函数双保险多用户响应总不稳定内层博弈轮数太少增加最优反应轮数观察最大策略变化量削峰效果不明显可转移负荷比例太低调整用户负荷分类比例提高弹性负荷占比这里的排查逻辑要记住任何问题先拆层。外层问题看适应度曲线内层问题看单个用户负荷曲线。不要把两层的问题混在一起查那样永远定位不到根因。5. 代码改造方向与扩展思考5.1 内层替换成凸优化求解器如果设备模型相对简化下层每个用户的最优反应问题可能是凸的。比如把可转移负荷建模成线性约束下的最小费用问题把空调和热水器的热动态模型做线性化那内层可以直接用quadprog求解。这样外层WOA每评估一次电价内层调用的是成熟的二次规划求解器速度提升不是一点半点精度也比内层WOA高得多。代价是模型表达能力受限制离散启停和多峰值非线性设备模型没法很好处理。这套替换思路适合从“研究验证”走向“工程落地”的场景。我实际试过用quadprog替换内层WOA之后3个用户的外层迭代从40分钟降到3分钟而且非常稳定。但要注意此时内层博弈轮数的判断逻辑可以简化——线性凸问题一步就能解出最优反应不用再多次交替迭代。多个用户之间依然有耦合还是要走几轮交替但每一轮都很快。5.2 动态电价与实时调度的衔接当前模型是“日前调度”结构24小时电价提前一天发布用户根据电价安排计划。做实时调度扩展时可以把调度窗口改成滚动优化比如每15分钟更新一次电价同时把未来4小时的预测负荷纳入优化。这时双层框架依然成立但外层的决策维度会变成96个时段对内层求解速度的要求更高。建议先做每日滚动再逐步缩小时段长度不要一步跨到15分钟。滚动优化还有一个好处可以处理不确定性。比如光伏出力、用户临时改变计划这些扰动因素在下一个滚动窗口会自动修正。但代价是外层WOA必须快速收敛否则滚动窗口更新不及时。这时内层换凸优化基本是必选项否则算力跟不上市级尺度的需求。5.3 从居民用户扩展到多类型用户这套模型虽然写的是居民负荷但内部逻辑完全可以直接迁移到商业用户、工业用户甚至电动汽车聚合商。不同用户类型只需要修改设备参数和舒适度惩罚函数形式。比如商业用户的可削减负荷主要是空调和照明工业用户可能是大型电机和生产线。扩展时要注意的是用户异质性不同用户的价格敏感度差异越大非合作博弈的求解就越容易陷入多个局部均衡。我的建议是从少量用户类型开始比如居民商业两类逐步增加。每次增加一类用户都要重新检查上面说的纳什均衡验证流程别指望原有参数直接复用。另外用户类型增多后上层目标函数也要相应调整不同类型用户的用电特征差异大单纯利润最大化可能导致定价对某些用户群体不利还需要考虑公平性约束。最后补一个我个人的实操体会这套模型最大的价值其实不在算法多高级而在于它把“市场机制设计”和“用户行为响应”放在一个闭环里。做调度方案时不要只盯着算法收敛精度要多看看电价曲线是否合理、用户是否有真实响应动机。我们做过一组对比实验同样的负荷结构采用双层博弈优化之后用户平均电费下降了11.7%系统峰值负荷降低了18.4%售电公司利润还略有提升。这个结果比单纯用启发式算法做集中调度要更有说服力因为它是基于真实利益驱动算出来的经得起业务侧追问。
返回列表