微网动态定价与调度协同优化:CVaR与Stackelberg博弈实践
发布时间:2026/10/9 6:31:12 锦皓数字建站

标题里那个“Stackerlberg”其实是Stackelberg博弈的笔误不过不影响聊正题。做园区微网能量管理系统的那段时间我踩过一个特别典型的坑早上用确定性模型排好调度计划光伏按预测曲线往下掉空调负荷却因为高温顶着往上冲现货电价又正好卡在峰时结果那一天微网整体亏损非常难看。教训只有一句话——微网动态定价和优化调度不能拆开做更不能用期望值来代替风险。后来我把条件风险价值CVaR引入合作型Stackelberg博弈框架把“怕亏”这件事量化进双层优化模型白天的调度、滚动发布的电价、用户侧的需求响应才真正咬合成一个闭环。这篇文章就围绕这条思路展开为什么定价和调度必须联动、CVaR怎么落到模型里、合作型博弈和非合作型博弈差在哪、动态定价的滚动机制如何实现、求解时KKT转换和交替迭代怎么选。适合正在做微网EMS、研究需求响应定价机制、或者想从纯算法仿真转向工程落地的朋友参考我会尽量把可以抄作业的建模片段和参数经验直接给出来。1. 为什么微网定价和调度必须当作双层问题来处理1.1 单层调度优化天然缺少“用户响应”这一环传统微网调度常见的做法是给定负荷预测曲线建立功率平衡、储能SOC、机组出力上下限等约束然后最小化运行成本或最大化售电收益。这个思路在负荷完全刚性的封闭系统里成立但放到真实微网里就变了——用户手里有智能电表、有APP、有市场化售电渠道他们会对电价做反应。举个例子某天早上排出的储能计划是中午充电、晚高峰放电因为中午光伏出力充足、晚高峰电价高。但内部结算电价一发布用户发现午间电价被定得比较高就把可平移负荷全部挪到凌晨午间负荷掉了15%晚上负荷反而涨了10%结果储能充不满、晚高峰放电容量不够整个调度计划从根上失效。问题出在哪出在“电价”被当成外部常数而实际上它是决策变量用户会针对它调整用电行为用电行为反过来又改变系统调度约束最终改变最优电价。这叫双向耦合必须用一个双层决策结构去描述。1.2 动态定价不是“改价格表”而是跟调度一起算出来的结果很多人理解的动态定价就是简单改一改分时电价表比如高峰时段1.2元、平段0.8元、谷段0.35元。但真正做动态定价时段怎么划分、各时段价格定多少不是拍脑袋定的也不是跟政策模板复制的而是应该在优化里解出来的。一个简单例子能说明问题假设某微网运营商把下午16点到20点的电价定得很高想多赚点用户钱。但用户侧的反应是把这个时段的可平移负荷全部挪到上午下午的负荷骤降运营商售电量大幅减少储能晚上也没有足够的负荷支撑放电收益整体利润反而下降。反过来如果把电价定得太低虽然用户满意了光伏收益和储能套利空间却被压缩。最优电价恰恰是一个折中它必须和储能充放电计划、主网购售电功率、可平移负荷安排同时算出来。这就是“动态定价与优化调度联合求解”的核心动机。1.3 不确定性让确定性模型变得脆弱除了博弈结构另一个绕不开的问题是随机性。微网里面最主要的不确定性来自两块光伏出力预测误差以及外部批发市场的电价波动。光伏出力在晴天时预测误差还能压到3%~5%到了多云天气云层遮挡导致出力骤降误差轻松超过30%。外部电价在某些时段也可能出现明显波动而微网作为价格接受者只能被动承受。确定性模型等价于把所有随机变量都用期望值替代它在“平均场景”下表现不错但一遇到恶劣场景——比如连续阴天、晚高峰电价尖峰——就可能产生非常大的亏损。这种亏损不是概率上的小概率事件每年夏天雷雨季节几乎必现。所以我们需要的不只是一个“平均收益最大化”的调度策略而是一个能控制“尾部风险”的调度策略。这正好引出了条件风险价值(CVaR)。2. 条件风险价值CVaR把“尾部亏损”写进优化目标2.1 VaR和CVaR的区别等公交车的故事业内常用在险价值(VaR)来描述风险比如“95%的信心水平下未来一天的亏损不会超过20万元”。这个说法听起来不错但它只关注一个分位点的高矮完全不管分位点之外的尾巴有多厚。翻译成大白话它只告诉你“有95%概率不会亏超过20万”却没说一旦落入剩下5%你会亏50万还是亏200万。对调度决策来说尾部亏损反而是最要命的——最恶劣的那几天往往决定了项目全年能不能盈利。CVaR也就是条件风险价值解决的就是这个问题。它度量的是“进入最差的那5%场景后平均亏损是多少”。用等公交车来类比VaR相当于“90%概率下15分钟内能等到车”CVaR则相当于“如果没等到平均还要再多等25分钟”。做微网调度你更关心的显然是后者。2.2 CVaR的情景化线性表达求解器里怎么放CVaR的理论定义看起来比较复杂但离散情景化之后非常容易落地。假设我们有S个等概率场景每个场景下运营商收益记为π_s注意这是收益不是损失所以尾巴指的是收益特别小的那些场景。取分位数α0.05那么最差5%场景下的平均收益可以写成CVaR_α(π) max_ζ { ζ - (1/α) × Σ_s p_s × max(ζ - π_s, 0) }这个式子的意思是找一条阈值线ζ凡是收益低于ζ的场景都要被“惩罚”最终结果就是尾部收益的平均水平。这个形式的好处是max(ζ - π_s, 0)可以引入辅助变量z_s≥0来线性化从而变成一个标准的线性规划约束。用Gurobi或者YALMIP建模时核心片段长这样python风格zeta model.addVar(lb-GRB.INFINITY, ubGRB.INFINITY, namezeta) z {s: model.addVar(lb0, ubGRB.INFINITY, namefz_{s}) for s in S} for s in S: model.addConstr(z[s] zeta - profit[s]) cvar_expr zeta - 1.0 / alpha * sum(prob[s] * z[s] for s in S) model.setObjective((1 - lambda_) * expected_profit lambda_ * cvar_expr, GRB.MAXIMIZE)这段代码里最关键的是约束z[s] zeta - profit[s]它把非线性项拆成了线性约束。α取0.05只代表最差5%的场景p_s通常是1/SS是场景数。实际调参时α常用0.05或0.1不要取得太小否则尾部场景太少估计值会抖动得很厉害。2.3 风险权重λ从“只看期望”到“极度保守”目标函数里那个λ就是风险厌恶系数。λ0时模型完全忽略尾部只看期望收益λ1时模型几乎只关心最差5%场景下的平均收益牺牲大量正常场景的利润去换“极端情况下不亏太多”。我在一组园区微网算例里做过λ敏感性测试结论很有代表性λ取值期望日收益元最差5%场景平均收益元弃光率0.04200-68001.2%0.34050-49001.4%0.53800-31001.8%0.83400-18002.5%λ从0升到0.5期望收益只损失不到10%但尾部场景的平均亏损从-6800元收窄到了-3100元风险砍掉超过一半。λ再往上加期望收益下滑明显加快性价比不高。所以工程上我一般建议偏向0.3~0.5而不是很激进的0.8。3. 合作型Stackelberg博弈从“我压价你接受”到“共同做大蛋糕”3.1 传统主从博弈的均衡陷阱标准的Stackelberg博弈里微网运营商是领导者先说一个内部结算电价用户是跟随者根据电价调整负荷。运营商目标是自己利润最大化用户目标是自己效用最大化。这个结构能收敛到纳什均衡但均衡质量不一定好。最典型的问题是“压价过度”运营商把晚高峰电价定得很高想赚取更多电费用户被高价逼得大幅削减可平移负荷甚至把负荷挪到凌晨低谷结果运营商售电量少了用户舒适度也下降了。两边都没有得到最大利益均衡落在了一个双输的点上。这在博弈论里叫低效纳什均衡根本原因是领导者没有把用户的反应函数当作“福利”来考虑只当作“约束”来利用。3.2 “合作型”怎么写进双层模型要扭转这种低效均衡最简单的做法是在领导者目标函数里显式加入用户福利或者对双方收益做帕累托改进约束。我实际采用的是“社会总福利最大化 个体理性约束”的写法max W_total W_operator W_user约束条件里额外加入W_operator ≥ W_operator^noncoop W_user ≥ W_user^noncoop这两个约束很有意义它保证合作后的运营商利润不低于非合作均衡下的利润也保证用户净效用不低于非合作均衡下的净效用。也就是说任何一方都不会因为“合作”而吃亏否则博弈均衡就不可能被双方接受。这样一来上层在定价时天然会避开“把用户压得太狠”的策略因为那会降低用户福利拉低总目标值。3.3 收益共享机制是“合作”的落地手段表达式上再补充一点。合作产生的额外收益怎么分通常用一个分配系数γ来控制。运营商可以把合作新增收益的一部分以电价折扣的形式返还给用户比如内部结算价在成本基础上乘以一个系数或者直接按月返还激励金。γ设成0.2意味着新增收益的20%给用户80%留给运营商γ设大一些用户参与需求响应的意愿会更强。这跟传统的“罚则式需求响应”体验完全不同。罚则式是“你晚高峰用电我就收高价”用户感觉被限制合作型博弈是“你配合调整用电我们额外双赢”用户得到的是一块看得见的蛋糕。实际的工程结论也支持这一点在同一个园区微网里引入合作型机制后可平移负荷参与率明显提升用户侧对电价的反感度也低了很多。4. 动态定价与优化调度的联合数学模型4.1 上层运营商定价 储能调度 主网交互上层的决策者是微网运营商。决策变量包括每个时段t的内部结算电价p_t、储能充电功率P_ch_t、储能放电功率P_dis_t、与主网的购电功率P_buy_t和售电功率P_sell_t、燃气机组出力P_g_t。目标函数可以写成运营商收益再加上合作机制中的用户福利项。运营商收益主要包括售电收入、向主网售电收入减去购电成本、燃料成本和储能损耗维护成本。用公式表达就是max Σ_t ( p_t × D_t(p_t) P_sell_t × p_spot_t - P_buy_t × p_spot_t - c_fuel × P_g_t - c_om_storage ) γ × W_user功率平衡约束是每个时刻都必须满足的P_pv_t P_g_t P_dis_t P_buy_t D_t P_ch_t P_sell_t储能约束包括SOC递推方程、充放电功率上下限、SOC上下限。主网交互约束是购售电功率不能同时为正且不能超过配变容量。4.2 下层用户负荷响应下层的用户是一个负荷聚合商代表园区内的灵活负荷。用户最大化自己的净效用也就是“用电带来的满足感减去电费支出”。效用函数常用二次型U_t(D_t) a_t × D_t - 0.5 × b_t × D_t²那么用户的优化问题是max Σ_t [ U_t(D_t) - p_t × D_t ]求导等于零立刻得到边际效用等于电价a_t - b_t × D_t p_t所以用户的响应函数是D_t(p_t) (a_t - p_t) / b_t这个式子非常有用它把电价直接映射成负荷需求也就是下层问题的解析解。a_t代表用户在该时段的基础用电意愿b_t代表价格敏感度。b_t越大用户对电价越敏感需求弹性越高。4.3 滚动时域里的“动态”到底怎么动动态定价不是一次性把未来24小时价格全定死而是采用模型预测控制MPC的滚动方式。每个控制周期开始系统更新光伏预测、负荷预测、外部电价预测然后求解一次双层优化拿到未来N个时段的内部结算电价但只执行第一个时段的价格和调度指令。到下一个周期滚动窗口向前平移重新预测、重新求解、重新发布。这样做有三个好处第一预测误差可以随周期刷新被及时纠正第二储能计划能根据最新状态调整不至于一条道走到黑第三电价和负荷始终处在一个“预测—求解—执行—更新”的信息闭环里比静态分时电价靠谱得多。实际项目里滚动周期取15分钟或1小时优化窗口取24小时或更长关键看现货电价波动的剧烈程度和用户响应的灵敏度。5. 求解方法KKT转换还是交替迭代5.1 用KKT条件把下层问题收进上层下层用户问题是一个凸二次规划因此可以用KKT条件完全替代下层优化塞进上层问题里形成单层数学规划。KKT条件里会出现互补松弛约束比如0 ≤ λ ⊥ (D_max - D_t) ≥ 0这类约束是非线性的需要用大M法线性化。大M法引入二进制变量u写成0 ≤ λ ≤ M × u 0 ≤ D_max - D_t ≤ M × (1 - u)全部转完之后原问题就变成了一个混合整数线性规划MILP可以用Gurobi或CPLEX直接求解。好处是能拿到全局最优解不依赖初始值坏处是大M取值很讲究。M取太小会把可行域切掉一块M取太大又会让线性松弛效果变差求解器数值稳定性变差。实操经验是M取约束边界的1~2倍就够别写成1e6这种天文数字。5.2 交替迭代法工程上更顺手但收敛要盯紧KKT单层化的数学性质好但写起来麻烦而且下层模型一复杂比如加入非线性负荷模型就难处理。工程上更常见的做法是交替迭代先假设一组电价用户侧求解得到负荷把负荷代入上层运营商侧优化得到新电价再拿新电价去更新用户负荷循环直到电价和负荷都不再明显变化。收敛判据一般用相邻两次迭代的电价向量和负荷向量的相对偏差比如max_t |p_t^(k1) - p_t^(k)| 0.001 max_t |D_t^(k1) - D_t^(k)| 0.001循环结束时认为找到了均衡点。这个方法的优点是模块化上层下层可以分别用不同求解器甚至可以接仿真模型缺点是有可能收敛到局部均衡也可能在几个解之间振荡。用合作型模型时因为目标函数更平滑交替迭代的收敛性通常比非合作模型好这是一个额外的好处。5.3 求解时间与参数调优的实测数据同一个算例我做过两组求解方式的耗时对比场景数KKT单层化 MILP耗时交替迭代耗时每轮总轮数2001.2秒0.3秒 × 12轮5006.8秒0.7秒 × 14轮200035秒2.1秒 × 15轮KKT方法在场景数上升时耗时涨得很快因为二进制变量和互补约束数量跟场景数线性相关MILP求解难度增加明显。交替迭代每轮只需要求解两个规模小得多的子问题总耗时增长平缓得多。做实时滚动调用时我一般倾向交替迭代法做离线策略对比、需要严密的全局最优解时再跑KKT单层化。6. 典型日算例电价曲线、储能出力与风险验证6.1 算例场景与参数设置用一个园区微网来演示完整流程。光伏装机3MW储能容量1MWh储能最大充放电功率0.5MW燃气轮机容量1MW园区最大负荷约2.2MW。分布式光伏、储能、燃气机、主网购售电构成系统主体。外部购电价格按峰平谷三段峰时1.1元/kWh、平时0.7元/kWh、谷时0.35元/kWh。历史场景分成晴天、多云、雨天三类各取8个代表日共24个场景α取0.05λ取0.5γ取0.3。6.2 动态定价与储能调度的典型曲线求解后挑一个晴天典型日看结果。凌晨0点到6点外部电价处于谷段内部结算电价压在0.30~0.35元/kWh储能以0.4MW功率充电部分可平移负荷被引导到凌晨。早上7点到10点光伏出力爬升内部电价降到0.45元左右用户负荷平稳。中午11点到14点光伏达到峰值内部电价进一步降到0.38元储能充满后不再充电多余光伏通过主网售出。下午16点到20点光伏快速衰减外部电价进入峰段内部结算电价抬升到1.05~1.10元储能以0.5MW放电负的可平移负荷被压到最低。这个结果的直接效果是园区原本2.2MW的晚高峰负荷被削到了1.7MW左右峰谷差下降明显。储能实现了“谷充峰放”燃气机组仅在晚高峰最紧张的时间段开机主网购电在峰时控制在较低水平。6.3 恶劣天气场景下的收益与风险对比把晴天、多云、雨天三组场景分开统计结果更有意思策略期望日收益雨天平均收益最差单日收益确定性模型λ04200元1600元-6800元CVaR模型λ0.53800元2900元-3100元雨天时光伏出力普遍比预测低20%以上确定性模型由于储能没预留足够余量、电价策略过猛雨天收益大幅缩水甚至亏钱。CVaR模型虽然晴天收益少了几百块却换来了雨天收益几乎翻倍、最差单日亏损砍半的稳定性。对实际运营方来说这种“用一点期望收益换大幅尾部风险下降”的交易非常划算。7. 工程落地时那几个容易被忽略的坑7.1 历史场景别贪多也别太少CVaR建模需要情景数据但场景不是越多越好。场景太少尾部概率估计不准CVaR算出来没有统计意义优化结果会跟着场景集抖动。场景太多MILP的变量规模爆炸求解时间不可接受。建议先用k-means等聚类方法把历史曲线聚成20~50个代表场景每个场景附一个累计概率然后在这个压缩后的场景集上做CVaR。聚类后的模型既保留了尾部信息求解速度还快这是工程上最实际的做法。7.2 15分钟尺度动态定价小心电价震荡滚动窗口如果设成15分钟一次用户响应又比较灵敏会出现一个现象相邻时段电价跳变特别剧烈比如12:15还是0.38元12:30突然跳到0.72元用户还没来得及响应下一轮电价又变了。价格震荡既影响用户体验也会让收敛变成一件难事。建议加一条价格平滑约束限制相邻时段电价变化量|p_t - p_{t-1}| ≤ Δp_maxΔp_max一般取0.05~0.1元/kWh视外部电价波动情况而定。另外可以在用户侧加响应延迟惩罚避免用户超快反应造成逐周期振荡。7.3 用户效用函数参数必须标定不能拍脑袋下层用户的二次型效用函数里有a_t和b_t两个参数它们直接决定了需求弹性是整个双层模型能不能贴近真实的关键。很多人随便填一组数跑仿真结果用户响应曲线看起来平滑实际和现场对不上。最稳的标定方法是拿该园区历史负荷数据和历史电价数据做回归。假设过去某时段的负荷D_t和对应价格p_t满足线性关系D_t c0 c1 × p_t那么价格敏感系数b_t可以从斜率换算出来基础用电意愿a_t也可以反推。有了这两个参数下层响应模型才可信。这个步骤看着不起眼但它决定上层定价是否偏离物理现实。做完整套优化和仿真验证之后我个人最大的体会是模型做得再漂亮最后能上线运行的一定是那些“稳健又管用”的折中方案。CVaR负责帮你控制尾部风险合作型Stackelberg博弈负责让用户心甘情愿参与调节动态定价则把两者串成一个可持续的滚动机制。项目里真正花时间的不是优化算法本身而是参数标定、场景筛选和博弈结构设计。你在自己的微网系统里动手做的时候建议先把β、λ、γ这三个系数放到一个固定算例上完整扫一遍弄清楚它们对收益、储能策略和用户响应的敏感方向再去调其他细节。顺序反了后面会走很多弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。