ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于CVaR的微网动态定价与储能调度优化实战

基于CVaR的微网动态定价与储能调度优化实战 去年做微网优化调度项目时我在Matlab里跑了一系列基于CVaR条件风险价值的动态定价与调度策略实验起因很简单风光出力波动一上来之前用的确定性调度方案在极端天气下亏损严重。这个项目让我把随机优化里的风险度量工具、价格型需求响应和日前调度模型整个串了一遍踩了不少坑也理清了思路。这篇博客就把整套方法、Matlab代码骨架和调参经验完整写出来适合正在做微网经济调度、新能源消纳、储能运行优化的研究生和工程师参考。下面内容全程按我自己的实操路径讲公式只保留能直接落地的部分代码部分用YALMIP写求解器用Gurobi。1. 为什么确定性调度在微网场景下越来越“算不准”1.1 微网优化调度的本质与不确定性来源微网调度本质上是一个“在给定网架结构和设备参数下决定每个时段各分布式电源出力、储能充放电功率、与主网交换功率、以及面向终端用户的零售电价”的优化问题。我习惯把它拆成三层看物理层柴油发电机、光伏、风电、储能、负荷之间必须满足功率平衡储能还要受SOC递推约束。经济层购电成本、燃料成本、设备运行维护费用、售电收入这几项加总形成日运行净利润或净成本。风险层光伏和风电出力不是确定值主网分时电价也可能波动负荷预测存在误差这些不确定因素会直接改变最优解。确定性调动把光伏、风电、负荷都取期望值或预测值代入模型。这样做在新能源渗透率低、预测误差小的时候问题不大但一旦风光装机容量占比超过30%实际净负荷与预测值的偏差常常超过30%。我在一组测试数据里看到过这样的结果按预测值优化出的方案在某个实际场景下电池在夜间已经放空第二天上午光伏不足时只能高价从主网购电一个时段的购电成本比预期多出三倍。问题的根源不是优化算法不对而是目标函数里只包含了“平均情况”没有把“尾部情况”放进去。1.2 期望成本最小化为什么“不管尾部”假设我们有S个等概率场景每个场景对应一组风光出力和负荷曲线调度方案是固定的。那么日运行总成本就是C_total Σ_s p_s · C_s其中p_s是场景概率C_s是第s个场景下的运行成本。当目标函数只写成Σ p_s·C_s时优化器会把所有场景的“平均值”压到最低。对于成本特别高但概率不大的场景只要它占期望值比重不高优化器就倾向于无视它。一旦那些极端场景真实发生方案就会捉襟见肘。我在实验时用了一个直观的统计量在所有场景成本分布里观察95%分位数和平均数之间的差距。确定性期望最优方案下这个差距经常比风险规避方案高出40%-60%。这等于说我们省下来的钱其实是在赌“极端场景不会发生”。1.3 VaR与CVaR风险度量工具的取舍逻辑CVaR之前最常用的风险度量是VaRValue at Risk风险价值。VaR回答的问题是在置信水平β下损失最多不会超过多少。比如β取0.95VaR就是成本分布的95%分位数。但是VaR有一个致命问题它只告诉你“95%的情况下亏损不超过某个值”完全不关心超过这个值之后损失有多惨。两套调度方案如果VaR相同一个方案超限后最多超5%另一个方案超限后可能超50%VaR无法区分。学术界把这种特性叫“尾部信息缺失”。CVaR补上的正是这一块它计算损失超过VaR那部分的条件期望。用生活化类比VaR是“体检报告上某项指标不超过阈值”CVaR则是“连超标后的平均恶化程度也一起算进去”。从数学上看CVaR是满足次可加性的一致性风险度量组合优化时不会出现“风险度量鼓励把风险集中到单一资产”的悖论。度量指标定义关注尾部在调度中的意义期望成本Σ p_s·C_s不关注只反映平均运行经济性VaRmin{αP(C≥α) ≤ 1-β}只给阈值CVaRE[CC ≥ VaR_β]关注阈值外的均值真正让我决定用CVaR的原因还是它在线性规划框架里极其好处理。通过引入一个辅助变量V和一组松弛变量Z_sCVaR可以直接写成线性约束整个调度模型仍然是MILP没有非线性求解器参与稳定性和求解速度都有保证。2. 动态定价和物理调度的耦合建模单层优化如何统一2.1 动态定价在微网里到底扮演什么角色动态定价不是简单地把电价设为分时段的阶梯价格而是把零售电价当作决策变量参与日前优化。电价升高用户用电需求下降削峰填谷电价降低需求上升促进新能源本地消纳。这样微网运营商就不再是被动接受固定负荷的价格接受者而是通过价格信号影响负荷曲线、间接改变储能和机组运行方式。项目里我采用价格型需求响应模型L_t L0_t · (1 ε · (ρ_t - ρ0_t) / ρ0_t)其中L0_t是时段t的基准负荷ρ0_t是基准电价ε是需求价格弹性系数一般取-0.1到-0.3。当ρ_t高于基准价时响应后负荷低于基准负荷反之负荷抬升。这里有个容易踩的建模问题目标函数里会出现售电收入项ρ_t·L_t它同时包含决策变量ρ_t和由ρ_t决定的L_t属于双线性项。直接扔给优化器会造成模型非凸YALMIP会尝试调用非线性求解器速度慢且不稳定。我的工程化处理办法是分段线性化。先把ρ_t的可行区间离散成K个点对每个离散点计算对应的需求响应负荷和收入然后引入SOS2变量做凸组合。这样收入项被近似成一系列线性表达式的组合整个模型保持MILP。实际操作中K取5到7个点就足够取太多会明显增加求解时间带来收益却很小。2.2 需求侧响应建模价格弹性系数怎么进约束价格弹性系数进模型有两种路径。一种是上面说的直接令ρ_t为优化变量通过需求函数把L_t表达成ρ_t的函数再线性化收入项。另一种更简单粗暴但工程上很有效把“动态定价”等价转化为“最优削峰填谷信号”用可平移负荷和可削减负荷来体现用户响应。我在复现论文时通常用第一种能直接输出价格曲线结果展示更直观。这里给出一个模型约束片段展示弹性负荷如何参与平衡L_act_t L_fix_t L_flex_t L_flex_t L_flex0_t - γ_t · (ρ_t - ρ0_t) 0 ≤ L_flex_t ≤ L_flex_max_t其中L_fix_t是刚性负荷L_flex_t是弹性负荷γ_t是时段t的价格敏感系数。整个表达式是线性的加进功率平衡约束后不会破坏MILP结构。2.3 整体优化模型的目标函数与约束布局我的标准建模方式是两阶段随机规划。第一阶段决策变量包括柴油机各时段出力、储能充放电功率、SOC、零售电价这些在日前就要确定不随场景改变。第二阶段变量是每个场景下的主网购电量、售电量和弃风弃光量它们应对场景差异带来的净负荷偏差。目标函数写成min Σ_s p_s · C_s λ · CVaR_β(C_s)C_s Σ_t [购电成本_s 燃料成本 起停成本 运维成本 - 售电收入]其中λ是风险偏好系数。λ0退化为纯风险中性期望最优λ越大优化器越优先压低尾部场景的损失。CVaR项的线性化形式放在后面代码部分详细说。约束布局上我固定加入以下几组功率平衡约束P_pv P_wt P_dg P_dis P_buy - P_ch - P_sell L_act柴油机出力上下限和爬坡约束储能SOC递推、充放电功率限值、始末SOC相等主网购售电限值零售电价上下限和价格弹性负荷响应关系CVaR辅助变量约束3. Matlab代码落地场景生成、CVaR约束线性化与YALMIP求解3.1 场景生成蒙特卡洛抽样和场景缩减场景生成的质量直接决定CVaR是否失真。我的做法是用蒙特卡洛方法生成风速和光照强度的原始样本再通过转换模型得到风机和光伏出力。风速我按两参数威布尔分布抽样形状参数k取2.2尺度参数c根据平均风速反算。风机出力与风速的关系用分段线性模型P_w(v) 0v v_in或v v_out P_w(v) P_rated · (v - v_in) / (v_rated - v_in)v_in ≤ v ≤ v_rated P_w(v) P_ratedv_rated ≤ v ≤ v_out光伏出力则通过辐照度Beta分布抽样再乘以转换效率、装机面积和温度修正系数。每天生成24个时段的序列考虑相邻时段的相关性时用一阶自回归模型生成风速时序这样画出来的出力曲线不会出现“白天黑夜完全独立”的假象。如果直接用500个场景进MILP求解时间会飙到十几分钟甚至更久。我建议场景缩减到20到30个左右。项目里我试过用kmeans聚类对场景做缩减把500个原始场景聚成25个簇簇内成员均值作为代表场景簇大小比例作为该场景概率。这种方法比同步回代消除法容易写代码效果也足够稳定。% 风速时序场景生成基于一阶自回归威布尔分布 rng(2024); N_scen 500; T 24; % 先抽样标准正态残差 epsilon randn(N_scen, T); v zeros(N_scen, T); phi 0.85; v(:,1) wblrnd(6.5, 2.2, N_scen, 1); for t 2:T v(:,t) phi * v(:,t-1) (1-phi) * wblrnd(6.5, 2.2, N_scen, 1) 0.3 * epsilon(:,t); v(:,t) max(v(:,t), 0); end % 风机功率转换 v_in 3; v_rated 12; v_out 25; P_rated_w 100; P_wind zeros(N_scen, T); P_wind(v v_in v v_rated) P_rated_w .* (v(v v_in v v_rated) - v_in) / (v_rated - v_in); P_wind(v v_rated v v_out) P_rated_w; P_wind(v v_in | v v_out) 0;3.2 核心建模代码CVaR怎么写进一组线性约束CVaR线性化的关键公式是CVaR_β V (1 / ((1-β)·S)) · Σ_s Z_s约束Z_s ≥ C_s - V Z_s ≥ 0V是VaR近似值Z_s表示场景s中成本超过V的部分。这个转换的妙处在于V本身是一个自由变量优化器会自适应地把它放到最合适的位置不需要我们提前知道VaR是多少。% 定义核心变量日前阶段决策 Pdg sdpvar(1, T); Pch sdpvar(1, T); Pdis sdpvar(1, T); SOC sdpvar(1, T1); rho sdpvar(1, T); L_act sdpvar(1, T); % 场景相关第二阶段变量 Pbuy sdpvar(S, T); Psell sdpvar(S, T); % 弃风光量可选 curtail sdpvar(S, T); % CVaR辅助变量 V sdpvar(1); Z sdpvar(S, 1); constraints []; % 物理约束非场景相关 for t 1:T constraints [constraints, L_act(t) L_fix(t) L_flex0(t) - gamma_rho(t) * (rho(t) - rho0(t))]; constraints [constraints, rho_min rho(t) rho_max]; constraints [constraints, 0 Pdg(t) Pdg_max]; constraints [constraints, 0 Pch(t) Pch_max]; constraints [constraints, 0 Pdis(t) Pdis_max]; constraints [constraints, SOC(t1) SOC(t) eta_ch * Pch(t) - Pdis(t) / eta_dis]; constraints [constraints, SOC_min SOC(t1) SOC_max]; constraints [constraints, Pbuy(:,t) Pbuy_max, Psell(:,t) Psell_max]; end每个场景的功率平衡和成本表达式在循环里逐场景写。两阶段模型里场景差异主要体现在净负荷不同第一阶段决策固定第二阶段购售电量自动平衡偏差。3.3 求解器选择与参数设置我用的组合是YALMIP加Gurobi。YALMIP负责建模和语法转换Gurobi负责求解MILP。项目里这套模型变量在2000到5000维左右约束数量约6000条Gurobi通常在30秒到2分钟内求解完成。求解器参数里我固定设置这几项ops sdpsettings(solver, gurobi, verbose, 2); ops.gurobi.MIPGap 0.005; ops.gurobi.TimeLimit 600; ops.gurobi.Threads 8;MIPGap设为0.5%可以大幅缩短求解时间对调度方案的影响可以忽略。如果没有Gurobi授权换成Cplex或者开源求解器SCIP也可以表达式不用改只改solver名。4. 结果解读风险系数、动态定价与储能策略的联动关系4.1 最优零售电价随风险系数的演变跑完λ0、λ0.5、λ1.5、λ3.0四组对比后最明显的现象是随着风险偏好系数增大高峰时段零售电价呈现出两类不同走向。在λ0时电价完全跟随边际供电成本走哪个时段贵就涨多少储能削峰填谷的作用偏弱。λ提高到1.5以上后电价曲线的高峰被明显抬高、低谷被压低整体曲率变大。原因是CVaR约束惩罚了“主网高价时段被迫大量购电”的场景优化器通过抬高该时段零售价压低电弹性负荷从源头减少高价购电需求。但这里有个反直觉点λ过大时电价波动反而变小。我试过λ10电力价格信号被过度扭曲用户响应后负荷变得过于平坦储能调节空间被挤压总运行成本反而上升。这个现象提醒我们风险系数不是越大越好要和储能容量、负荷弹性相匹配。4.2 储能调度策略的“保守化”趋势储能行为对风险系数的响应我认为是整个方案里最有价值的部分。λ0时电池倾向于在低谷充、高峰放几乎是纯套利策略。λ提高后电池会在白天光伏出力尚不明确时保留更多SOC裕量也就是“预留余地”。这种变化在SOC曲线上看得很清楚风险中性方案下傍晚高峰开始时储能SOC往往降到30%以下风险规避方案下同样的时刻SOC保持在55%-65%。代价是低充高放套利收益减少但极端场景下主网购电量下降明显。这就是CVaR调度策略的本质用低谷时多存一点、高峰时少放一点的“保守化运行”对冲风光出力剧烈波动时被动高价购电的风险。4.3 用留一场景法和极端场景验证鲁棒性结果分析不能只看目标函数数值。我的验证方法是先在全部场景上优化得到调度计划和价格曲线然后固定这些决策变量单独跑几个“极端场景”检验实际成本。极端场景指的是把光伏出力在原场景基础上乘0.4、风速整体降低到切入风速以下同时主网购电价格上调20%。这种场景在场景缩减后的集合里已经被平滑掉了但在真实运行中完全可能出现。固定第一阶段决策后在这些极端场景下计算购电成本预期成本高出中性方案约15%但最恶劣场景的成本比风险中性方案低了约28%。这正好体现CVaR的核心价值牺牲少量正常情况下的经济性换取恶劣场景下不失控。5. 复现这套代码时容易踩的坑从求解超时到SOC约束写错5.1 YALMIP求解超时或数值病态最常遇到的报错是求解器提示“Numerical issues”或长时间不收敛。绝大多数时候不是模型错而是量纲不一致导致的病态矩阵。购电成本是万元级CVaR辅助变量是元级SOC约束是百分比几个数量级差混在一起会让单纯形法陷入数值困难。我的处理办法很粗暴全部归一化。功率用kW能量用kWh价格用元/kWh成本用万元。这样所有决策变量的数量级基本都在0-100之间Gurobi基本不需要调参就能稳定求解。5.2 场景数、置信水平和风险系数的“三角搭配”场景太少CVaR估计不稳场景太多求解时间爆炸。置信水平β太高比如0.99极端场景的Z_s约束几乎只由一两个场景决定优化器会过度拟合噪声β太低比如0.8CVaR覆盖范围太宽风险约束名存实亡。我最终用的组合是原始500个场景聚类缩减到25个场景β取0.95λ从0开始以0.25步长扫描到3。这个组合在普通台式机上每一组参数可以在2分钟内出结果画出来的Pareto前沿曲线也很平滑。5.3 储能SOC耦合约束的常见建模错误储能建模最隐蔽的坑是SOC递推公式中充放电效率的位置。正确写法是充电时把效率乘在Pch上因为能量进入电池有损耗放电时把效率放在分母上因为电池释放的能量要经过逆变器损耗才能供给负荷。如果写成SOC(t1) SOC(t) Pch(t) - Pdis(t)忽略效率会让电池的“有效容量”虚高10%-20%。另一个常见错误是忘记加始末SOC相等约束。日调度场景下如果允许SOC_T明显高于SOC_1优化器会倾向于在最后时段把电池充满这在跨日运行时等于白用了第二天早上的储能资源。项目里我设置SOC(1)SOC(T1)0.5。5.4 双线性收入项处理不当导致模型“假非线性”动态定价模块里如果直接把ρ_t和L_act_t相乘写进目标函数YALMIP会报出“Nonconvex quadratic”或直接调用非线性求解器。前面说了用SOS2分段线性化处理但另一个隐藏问题是价格弹性的有效范围。价格型需求响应模型只在基准价附近的小邻域内成立。我给ρ_t设置的可行域是[0.4, 1.6]·ρ0_t超过这个范围用户的实际响应行为很可能已经偏离线性假设。在这套代码里我把这个可行域写死进约束防止优化器为了套利把价格推到极端值导致整个需求响应模型失真。5.5 从单日调度扩展到多日滚动运行的注意事项这套代码本身是日前调度。如果想把框架扩展成多日滚动运行需要增加一个“当前SOC状态”的输入参数。我在扩展实验里做了48小时滚动窗口每24小时滚动一次发现SOC初值设置不当会让相邻两天的方案出现明显跳变。建议做法是把当前时刻实测SOC作为固定参数传入优化模型而不是让优化器自由决定SOC(1)。实测值和模型假设值偏差超过10%时应该把当天场景生成参数整体校准一次。这样才能让CVaR调度策略从仿真环境平滑过渡到实际运行环境。6. 从CVaR出发还能往哪个方向扩展6.1 把价格弹性模型换成用户效用函数我目前的实现用的是线性价格弹性需求响应好处是模型简单、求解快。但如果项目需求是要更精细地模拟消费者行为建议把L_t与ρ_t的关系替换为用户效用最大化模型的解析解。用户效用函数取二次凹函数时用户最优用电量对价格的一阶条件可以解析表达整个模型仍然能保持线性或分段线性。这样得到的价格信号具备经济学含义而不只是一个调度工具变量。6.2 引入储能寿命折损成本长时间运行场景下储能充放电循环会增加电池容量衰减成本。CVaR框架下可以把每天的电池寿命损耗也纳入C_s成本项具体做法是在放电功率上乘一个等效边际损耗成本或者按累计放电能量乘折算系数。加入之后调度策略对高频次、浅充浅放和深度充放之间的权衡会更贴近实际运营。6.3 与配电网节点电压约束结合如果微网不是单母线结构而是有多个节点、多条馈线必须处理电压越限问题。这时需要加入潮流约束或线性化DistFlow约束。CVaR的风险度量对象会从“运行成本”扩大为“运行成本越限惩罚成本”风险不只体现在经济性上还体现在安全性上。这个方向适合已经有潮流计算基础的读者。我个人在实际项目中的体会是CVaR对微网调度的价值不是简单地多了一个约束条件而是彻底改变了调度决策的“性格”。它让系统在风光波动面前不再只追求最好看的那条期望曲线而是先保证最坏的情况下还能站稳。如果你正在复现类似的研究建议从λ0先跑通再逐步加大风险系数观察储能和电价曲线的演变过程这会比直接调出一组最优参数更能帮你理解整套策略的内在逻辑。
返回列表