计及需求响应和电能交互的多主体综合能源系统主从博弈优化调度策略
最近好几个同学来问这个方向计及需求响应和电能交互的多主体综合能源系统主从博弈优化调度策略用Matlab怎么写、怎么跑通、怎么改参数。这确实是目前综合能源系统里相当高频的一个研究点期刊论文和硕博课题里经常出现但很多人从公式推导到代码落地这一步卡住了。这篇文章就顺着我从建模到求解再到Matlab实现的完整链路把思路、模型、代码骨架和踩过的坑一次性讲清楚给打算复现这个方向或者正在调代码的人一个可以“抄作业”的参考。这个课题的核心问题一句话概括园区里不是一个决策者在指挥一切而是几个主体各有各的小算盘——能源运营商想多赚钱用户侧想少花钱两边之间还隔着电能买卖和价格博弈。需求响应让用户的价格敏感度进入优化电能交互让多主体之间的富余电力有了流转通道主从博弈则负责描述“运营商先定电价、用户后调负荷”这种有先后的决策关系。最后用Matlab把这一套求解出来得到均衡状态下的调度方案和收益分配。下面我分五个部分展开讲。1. 从论文题到工程问题多主体综合能源系统主从博弈调度到底在算什么1.1 多主体综合能源系统不是一台机组是一群主体传统微电网或者单主体综合能源系统做优化调度一般假设有一个中心调度者掌握全部信息直接给出全系统成本最低或收益最大的运行方案。这种“上帝视角”的集中式优化在单一主体系统里没问题但多主体场景就不成立了。一个典型的算例里系统通常包含一个区域能源站内部有热电联产机组CHP、电锅炉、光伏、储能下面挂着商业楼宇、工业厂房、居民小区这几类用户。能源站有自己的投资运营成本和利润指标商业楼宇里的大容量中央空调负荷可调但影响舒适度居民小区里的电动汽车和热水器也有一定的时移能力工业负荷更在意供电可靠性。每个主体关心的目标不一样没有谁愿意无条件配合全局最优。所以多主体建模的第一步是把系统拆成“一个领导者 N个跟随者”的结构。领导者是能源运营商的调度中心它决定分时电价、需求响应补偿价格和本地的生产计划跟随者是各用能主体它们收到价格信息后按照自身用能成本最小化的目标去调整购电计划和柔性负荷。两者之间的信息传递靠价格信号而不是靠行政指令。这种机制设计出来以后整个系统不需要一个全能调度员各主体按市场逻辑自主行动反而更贴近实际运行。1.2 主从博弈为什么天然适配这种“上下层”问题主从博弈也叫Stackelberg博弈核心特点是决策有先后顺序领导者先出牌跟随者看到牌以后再做出最优反应。放到这个场景里就是能源运营商先发布明天的分时电价、热价和需求响应补偿价用户侧看到价格后决定每个时段买多少电、调多少负荷、充放多少储能。领导者的高明之处在于它不能一厢情愿地报高价。如果电价报得太离谱用户会把负荷压到最低或者尽可能减少向运营商购电运营商的售电收入反而下降。所以运营商的每个价格策略都必须“站在跟随者的角度”去预判用户会做什么反应然后在所有可能的用户反应里找出对自己最有利的那个价格组合。对应到数学上就是上层目标函数里嵌套了下层优化问题下层的最优解是上层目标函数的隐式表达式。这种“先后决策 最优反应”的结构和现实中园区能源服务商与用户之间的交易方式高度一致。对比集中式优化和完全竞争市场模型主从博弈处在两者中间它承认参与者之间有利益冲突也承认存在一个有市场支配力的主导者这不就是实际的区域能源市场形态吗。1.3 需求响应与电能交互是模型里的两条“活路”需求响应在这个模型里承担的是“价格引导负荷”的任务。用户不是刚性用电而是会根据分时电价调整用电行为峰时少用、谷时多用或者把一部分可转移负荷挪到电价低的时段。这部分可调节的弹性负荷就是系统里的灵活性资源。引入需求响应后峰时用电压力下降运营商的购电成本也随之优化。电能交互则是把多个用能主体之间的物理连接也纳入优化。比如中午光伏大发商业楼宇自己用不完与其低价上网不如直接卖给旁边正在生产的工业负荷再比如某个用户侧储能夜间充满电白天可以放出来供给其他主体收取一个介于上网电价和售电电价之间的交易价格。这种主体之间的点对点交互打破了每个用户都只能从电网或者单一运营商购电的约束让富余能源在系统内部先流转一圈。两条机制叠加以后模型从“运营商单方面定价、用户被动接受”变成了“价格引导 灵活响应 内部互济”的三层互动关系看起来复杂实际上更接近真实市场。这也是近年来综合能源系统调度研究里需求响应和电能交互总被放在一起讲的原因。2. 模型搭建需求响应、电能交互下的目标函数与约束条件2.1 三个目标函数谁的收益谁的成本先把参与者的目标函数写清楚。上层是能源运营商它的目标是在整个调度周期内最大化净收益。收益来源包括向用户售电的收入、售热收入以及主体之间电能交互收取的服务费支出包括向上级电网购电的成本、设备运行维护成本、向用户支付的需求响应补偿费用以及和电网交互的输配电费。写成数学表达就是max F_op Σ_t [ p_e(t)·P_sell(t) p_h(t)·H_sell(t) p_ex(t)·P_trade(t) ] - Σ_t [ c_buy(t)·P_grid(t) C_om(t) r_DR(t)·Q_DR(t) ]其中 p_e(t) 是运营商向用户售电的分时电价p_h(t) 是热价p_ex(t) 是主体间交互电价P_trade(t) 是交互电量c_buy(t) 是运营商向电网购电的价格C_om(t) 是设备运维成本r_DR(t) 是运营商支付给用户的需求响应补偿单价Q_DR(t) 是用户实际提供的响应量。下层是各用能主体目标函数是自身总成本最小化包括从运营商购电的费用、从其他主体购电的费用再减去参与需求响应获得的补偿收益。每个用户在自己的约束下独立求解这个最小化问题。需要注意的是热负荷在综合能源系统里不能忽略。商业楼宇的采暖、工业过程的用热都依赖能源站的热网供应。所以下层用户优化里往往还要加上购热成本项。这样一层模型里同时包含电和热才是完整的综合能源调度。2.2 储能、需求响应和功率平衡的关键约束约束条件是模型里最容易出bug的地方。第一类肯定是功率平衡约束包括能源站内部电功率平衡和热功率平衡。电功率方面CHP发电、光伏出力、储能放电、向电网购电要等于用户购电、储能充电、电锅炉用电以及线损。热功率方面CHP余热、电锅炉产热要等于热负荷需求。第二类是储能约束这部分最容易写错。储能模型用荷电状态SOC来表示SOC(t1) SOC(t) (η_ch·P_ch(t) - P_dis(t)/η_dis)·Δt / E_cap SOC_min ≤ SOC(t) ≤ SOC_max 0 ≤ P_ch(t) ≤ P_ch_max·u_ch(t) 0 ≤ P_dis(t) ≤ P_dis_max·u_dis(t) u_ch(t) u_dis(t) ≤ 1最后一行是充放电互斥约束MATLAB里用二进制变量实现。还有一个容易忽略的点调度周期结束时SOC要回到初始值附近否则储能等于“白嫖”了初始电量结果会偏乐观。第三类是需求响应约束。可平移负荷的调节量有上限可削减负荷不能超过用户能承受的舒适度边界且整个调度周期内总体用电量一般保持不变削峰填谷不等于节电0 ≤ Q_DR(t) ≤ Q_DR_max(t) Σ_t Q_DR(t) 0最后一个等式约束意味着需求响应是“平移电量”而不是“消灭电量”这是DR建模里比较经典的假设。第四类是电能交互约束。两个主体之间的交互功率不能超过线路容量交互电价一般设计在上网电价和售电电价之间这样买卖双方都有利可图。否则交互机制推不动算例结果会退化成无交互场景。2.3 把整个问题写成规范的双层模型把目标函数和约束整理到一起主从博弈调度模型可以规范地表示成上下层嵌套的形式。上层是运营商最大化收益问题决策变量是24时段的分时电价向量、DR补偿价格向量以及能源站内部各设备的出力计划下层是每个用户的最小化成本问题决策变量是各时段的购电功率、购热功率和DR响应量。整体写出来是上层max F_op(p_e, p_h, r_DR, P_om) s.t. 能源站运行约束、储能约束、定价上下限 下层min F_user(P_buy, Q_DR) s.t. 用户功率平衡、DR调节约束、储能约束如有下层决策变量实际上是上层价格变量的函数也就是 P_buy f(p_e, p_h, r_DR)。上层每尝试一组价格都要先算一遍下层问题拿到用户的最优响应才能评估这组价格下自己的收益。这种“你中有我、我中有你”的嵌套结构就是主从博弈建模的核心也是后面选择求解算法时的关键难点。3. 求解思路差分进化嵌套下层线性优化分工明确不乱套3.1 为什么不能直接扔给求解器把模型写出来以后第一反应当然是让Matlab直接求。但这个双层模型有几个特点直接求解困难很大。第一整体问题不是凸的。下层线性规划的最优解作为上层的约束函数会引入不可导和不连续的性质第二上层包含离散变量充放电状态、购售状态下层用户数多整个问题的可行域形状复杂普通非线性优化器很容易陷入局部最优第三虽然有KKT条件可以把下层问题替换成均衡约束形成MPEC带均衡约束的数学规划但MPEC本身存在互补松弛带来的强非凸性通用求解器依然棘手。所以工程上更常见的做法是把上下层拆开用启发式算法在外部搜索上层的价格变量每搜索到一组价格就调用一个成熟的下层优化器求用户最优响应再把响应结果带回上层算适应度。这类算法虽然不能严格证明收敛到全局最优但在实际算例里精度和稳定性都够用这也是论文里最常见的处理方式。3.2 上层差分进化下层linprog/cplex分工很明确上层搜索算法我推荐差分进化DE。相比遗传算法和粒子群DE的连续变量优化能力强参数少而且对目标函数是否光滑不敏感非常适合这种“每次适应度计算都要嵌套一层LP”的问题。差分进化的核心操作是变异、交叉、选择。每个个体编码成一组价格向量例如[24个时段的售电价, 24个时段的DR补偿价]。每代通过变异产生新个体新个体和旧个体交叉以后调用下层用户优化函数计算适应度保留更优的个体进入下一代。迭代几十代以后种群会收敛到一组稳定的价格策略对应的就是Stackelberg均衡解。下层用户问题如果模型里都是线性约束那直接用linprog就能解。如果加了储能且用二进制变量表示充放电状态就成了混合整数线性规划这时建议用cplex或者gurobiMatlab自带的intlinprog在小规模下也可以用。网格化测试下来24时段、3个用户、每用户20个决策变量的下层层LPlinprog单次求解在几十毫秒量级配合50个个体、100代的DE整体跑完在几分钟内是可以接受的。3.3 Matlab工程框架怎么组织代码不建议全塞在一个脚本里。我习惯把工程拆成五个模块参数初始化、上层DE主程序、上层适应度计算、下层用户优化、结果输出。下面是推荐的文件组织方式文件/函数职责Main.m主程序入口调用各模块保存结果Init_Para.m定义系统结构、负荷曲线、设备参数、DE参数DE_Main.m差分进化主循环实现变异交叉选择Operator_Fitness.m输入一组价格策略调用下层求解返回运营商收益User_Opt.m单个用户的下层优化调用linprog或cplexPlot_Result.m绘制调度曲线、收敛曲线、收益对比图调用关系上DE_Main.m 每评估一个个体就调用 Operator_Fitness.mOperator_Fitness.m 内部循环所有用户调用 User_Opt.mUser_Opt.m 返回用户最优购电量和DR响应量Operator_Fitness.m 汇总后计算运营商收益。这样模块清晰排查问题的时候也能直接定位到某一步。4. 实操过程与Matlab核心代码逻辑解读4.1 参数初始化一份可以直接抄的配置表初始参数直接决定模型能不能算出合理结果。我整理了一份典型配置可以在项目初期直接套用后续再根据真实数据调整。参数数值说明调度周期 T24小时日前调度步长1小时用户数量 N3商业、工业、居民各一个分时电价下限/上限0.3 / 1.2 元/kWh上层决策变量的边界电网购电价格曲线峰1.0 / 平0.6 / 谷0.3 元/kWh外生给定DR补偿价格范围0.2 / 0.8 元/kWh上层决策变量的边界交互电价系数0.6介于上网与售电之间乘以上网电价得到交互价储能容量600 kWh能源站侧储能储能SOC范围[0.1, 0.9]保护电池充放电效率0.95电化学效率DE种群规模 NP50个体数量DE最大代数 MaxGen100迭代上限DE缩放因子 F0.6变异幅度DE交叉概率 CR0.9交叉强度初始化时注意几个细节分时电价的上下界要保证高于电网购电价的最低价、低于用户心理可接受的高位否则上层优化会直接贴边界跑结果失去分析意义DR补偿价太高会侵蚀运营商收益太低用户没有响应积极性这个范围也要在多次试算后确定。4.2 上层差分进化的核心循环差分进化主循环的Matlab代码结构并不复杂关键是每个个体都要正确地传参给下层。简化后的核心循环如下% DE主循环D是决策变量维数p_dec是每个个体的决策变量 D 24 24; % 24时段电价 24时段DR补偿价 pop lb (ub - lb) .* rand(NP, D); % 均匀初始化 for gen 1:MaxGen for i 1:NP % 随机选择三个不同个体 r randperm(NP, 3); while any(r i) r randperm(NP, 3); end % 变异 v pop(r(1), :) F * (pop(r(2), :) - pop(r(3), :)); % 边界处理 v min(max(v, lb), ub); % 交叉 u pop(i, :); j_rand randi(D); for j 1:D if rand() CR || j j_rand u(j) v(j); end end % 选择适应度是运营商收益越大越好 if Operator_Fitness(u, para) Operator_Fitness(pop(i, :), para) pop(i, :) u; end end best_fitness(gen) max(arrayfun((k) Operator_Fitness(pop(k, :), para), 1:NP)); end这个地方有个性能优化的提醒上面的代码是为了表达逻辑清晰才在交叉后立即调用两次适应度函数。实际工程里应该先把种群所有个体的适应度缓存下来再进入变异选择否则每个个体多次重复调用下层求解计算量直接翻倍。我早期版本没做缓存同样的算例跑了将近四十分钟加上缓存后压缩到十分钟以内。边界处理也很关键。电价越界以后简单的裁切虽然方便但会让大量个体堆在边界上种群多样性下降。可以尝试带变异的边界反射方法实测下来搜索后期收敛更平滑。4.3 下层用户优化函数用linprog求最优响应下层每个用户的优化目标是成本最小决策变量包括各时段从运营商购电功率、自其他主体购电功率以及DR响应量。模型保持线性的话直接调linprog。下面是一个单用户下层的核心代码框架。function [x_opt, cost] User_Opt(price, para) % price包含售电价、交互电价、DR补偿价 % 决策变量组合x [P_buy(1..24), P_ex(1..24), Q_DR(1..24)] P_buy_idx 1:24; P_ex_idx 25:48; Q_DR_idx 49:72; % 目标函数购电成本 交互购电成本 - DR补偿收益 f zeros(72, 1); f(P_buy_idx) price.p_e; % 运营商售电价 f(P_ex_idx) price.p_ex; % 主体交互价 f(Q_DR_idx) -price.r_DR; % 补偿收益取负号 % 等式约束用户功率平衡 Aeq*x beq Aeq zeros(24, 72); for t 1:24 Aeq(t, t) 1; % P_buy(t) Aeq(t, 24 t) 1; % P_ex(t) Aeq(t, 48 t) -1; % 需求响应对负荷的影响 end beq para.base_load; % 基础负荷 % 不等式约束DR响应量上下限 A zeros(48, 72); b zeros(48, 1); for t 1:24 A(t, 48 t) 1; b(t) para.DR_max(t); A(24t, 48 t) -1; b(24t) -para.DR_min(t); end % 边界与求解 lb [zeros(48,1); para.DR_min]; ub [para.Pbuy_max * ones(24,1); para.Pex_max * ones(24,1); para.DR_max]; options optimoptions(linprog, Display, off); [x_opt, cost] linprog(f, A, b, Aeq, beq, lb, ub, options); end有一点要注意需求响应的总电量守恒约束Σ Q_DR 0在上面没有体现出来实际模型里需要作为等式约束加进去否则用户会只响应高补偿时段产生不真实的收益套利。常用做法是加一行对Q_DR之和等于0的等式约束。4.4 典型结果与收敛性怎么判断模型是对的模型写完以后我习惯先跑三个场景对比场景1是纯固定电价、无DR、无交互的基准场景场景2加入需求响应运营商发布分时电价和补偿价场景3在场景2的基础上打开主体间电能交互。这样可以清楚地分离每个机制带来的增量收益。场景运营商收益元用户侧总成本元系统峰谷差kW固定电价 无DR 无交互基准值基准值基准值分时电价 DR 无交互提升约10%-15%下降约5%-8%明显减小分时电价 DR 电能交互提升约15%-22%下降约8%-12%更平缓这个结果只是示意具体幅度取决于设备容量和负荷曲线形状但趋势是稳定的需求响应降低了系统峰谷差电能交互进一步降低了购电成本和弃光率。收敛性方面观察DE每一代的最优收益曲线应该在前期快速上升中期变缓后期基本平稳。如果曲线一直震荡甚至下降优先检查下层linprog是否在某些参数组合下返回无解这时适应度函数需要做惩罚否则会污染种群如果曲线收敛平缓但结果明显不合理比如电价一直贴着上限说明下层约束太松用户对高价没有抑制能力要回过去调负荷参数或者DR上限。5. 常见问题与排查技巧实录这些坑我替你踩过了5.1 求解结果震荡收敛曲线像锯齿这个现象在我第一次跑通代码的时候非常明显。复盘下来最可能的原因是下层动态链接出了问题当某组试探电价超出用户可接受范围下层linprog会给出较低的购电量甚至报错而适应度函数没有对不可行解施加足够的惩罚导致DE把这些“假优秀个体”留在种群中下一代又被纠正曲线自然震荡。处理思路有两种。一是检查上层价格变量边界保证运营商发布的dr价格和售电价格在合理区间内二是在Operator_Fitness里面加惩罚项当下层返回无解或者用户购电量为0时直接把收益设成极低值让DE自然淘汰这些个体。另外DE参数也需要配合调整F设太大种群发散设太小容易早熟0.5到0.8之间多试几次。5.2 下层频繁提示无可行解下层无可行解十有八九是约束条件之间产生了冲突。最典型的场景是某个时段的用户基础负荷很高但购电功率上限设置过严而DR最大调节量又不足以把负荷降下来这时无论怎么优化都找不到一个同时满足功率平衡和购电上限的解。排查路径很直接。第一步把购电上限和DR上限都放大看问题是否消失第二步逐条检查等式约束的构建尤其是用户功率平衡Aeq矩阵的系数是否填反第三步检查储能等式约束SOC的递推关系式里充放电效率的倒数容易写错导致能量不守恒。还有一个容易被忽略的点DR响应量的上下限如果写成和基础负荷一样大的向量有些时段本身没有可调空间也会造成不可行。调试时可以把不可行时段打印出来单独看是哪个约束被违反效率远比盲改高。5.3 求解时间太长跑不动完整算例前面提过主从博弈的求解代价是“上层每评估一个体都要解一次下层优化”。50个个体、100代、3个用户一共是15000次下层求解单次如果50毫秒总时长也接近15分钟。还算能接受但如果用户数量扩展到10个或者下层变成MILP时间会指数级上升。我用的方法有几个第一下层问题改用cplex求解比linprog在大规模问题上快不少第二把调度时段从24小时缩减到关键时段比如峰谷分时段的代表值做预研确认逻辑后再跑完整24时段第三在DE内部做并行Matlab的parfor可以直接替换循环注意算子随机数的流控制第四很多论文里会用KKT条件把下层问题转化为MPEC后再用商业求解器一次性求解这条路数学要求高但求解速度确实快很多适合后期做大规模敏感分析时用。回到选型的问题我自己的体会是第一版代码别追求“最优”。先把双层的嵌套逻辑跑通用3个用户、简化约束得到可复现的结果再慢慢往真实场景里加因素。很多人在这个课题上卡住不是模型不懂而是一上来就塞了太多设备、太多约束最后连哪个模块出错都找不到那种状态最消耗信心。先前进一小步再前进一大步。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →