梯级水电加光伏互补,在电力系统优化调度里算是个常青树方向了。你手上如果握着这个标题:梯级水光互补系统最大化可消纳电量期望短期优化调度模型(Matlab代码实现),那大概率不是准备投EI小论文,就是要复现某篇文献,或者干脆是毕设中期需要一份能跑通的参考代码。这个题目拆开看,其实涉及三个核心关键词:梯级水光互补、可消纳电量期望、短期优化调度。把这三个词搞明白,模型逻辑、代码框架、结果分析就都顺了。
这篇内容我打算从模型机理、Matlab实现、排错心得三个层面展开,并且会把我在实际复现项目时踩过的坑一并写出来。不管你是刚开始接触随机优化,还是已经写过几版调度代码想找一个更完整的参考,这篇文章都能给你一条可以直接落地的实现路径。
1. 先把这个题目拆开看:它到底在做什么
1.1 为什么偏偏是“梯级水光互补”,而不是单电站
很多人看到“梯级”两个字,第一反应是“上下游好几个水电站”。方向没错,但不够。梯级电站的核心价值不在于数量多,而在于水力联系带来的时间平移能力。上游电站放水,经过几小时甚至十几小时的滞时到达下游电站,下游的水库再把这个水量重新分配。也就是说,一整条河流上相当于串联了好几个可以跨时段调节的“能量缓冲区”,这对平抑光伏出力的波动性非常关键。
光伏出力白天高、晚上为零,阴雨天直接腰斩。如果没有水电配合,电网为了让系统稳定,只能限制光伏出力,这就是弃光。而梯级水电可以在光伏大发时压低出力、多蓄水,把水量留到晚上光伏归零时再发电。这种“错峰补偿”能力,单库电站也有,但梯级组合能够通过上下游的联合调整,把调节范围拉得更宽、调节过程更平滑。所以论文标题里强调“梯级”,本质是强调这个模型要考虑多个水库的联合调度状态,而不是简单把几个电站的出力相加。
再补充一点,短期调度里梯级电站之间的水力时滞(即上游出库流量经过河道流到下游的时间)必须显式建模。如果忽略时滞,下游电站的入库过程会被算错,水量平衡约束就会出现时间和空间上的错位,最后得到的水位过程和出力过程在物理上根本不可行。
1.2 “最大化可消纳电量期望”这个目标意味着什么
这个目标函数是题目里最值得玩味的部分。它没有直接写“最大化光伏发电量”,也没有写“最小化弃光电量”,而是用了“可消纳电量期望”。这三个词透露出一个关键信息:系统允许光伏出力超过消纳能力,但我们要让能够真正被电网消纳的那部分电量尽可能大。
为什么要加“期望”二字?因为光伏出力是随机量。短期预测虽然比中长期准得多,但误差仍然存在:预测明天9点光伏出力100MW,实际可能只有70MW,也可能到120MW。如果直接把预测曲线当作确定值来调度,一旦实际偏低,水电出力补不上去,系统可能缺电;一旦实际偏高,弃光量又会超过预期。所以不能只做确定性优化,要做随机优化,把所有可能的光伏出力情况都纳入考虑,然后按概率加权取期望值。
工程上等价的做法是:对每个可能的光伏出力场景,计算对应的最大消纳电量,再乘上这个场景出现的概率,最后把全部场景的结果加起来。这就是期望值目标。它反映的是“在不知道未来真实光伏出力的情况下,系统平均能消纳多少光伏电量”这个指标。
这也是EI期刊比较喜欢这类主题的原因:随机规划框架成熟、有清晰的数学表达、能体现新能源出力不确定性的处理思路,而且案例结果可以画成各种曲线图、对比图,验证部分非常出效果。
1.3 “短期”这个时间尺度决定模型细节的丰富程度
短期优化调度的周期通常是24小时,分辨率一小时或15分钟。这个时间尺度决定了模型必须包含哪些细节:水电的库容变化、发电流量约束、出力上下限、爬坡速率限制、机组启停状态,都要显式表达。如果是中长期调度,关注的是月均水量分配,很多机组级细节可以直接聚合掉;但短期调度不行,光伏午间高峰是小时级的,水电能不能在半小时内从低负荷拉到额定出力,这是必须回答的问题。
短期的另一个好处是光伏预测精度相对较高,场景数量不需要太大,几十个场景就能覆盖主要不确定性。如果做成中长期,场景数量可能要上百个,计算量会迅速膨胀。所以短期尺度是“精度和计算复杂度平衡得最好”的一个窗口,很适合作为EI论文的模型设定。
2. 模型的数学机理:随机优化的底层逻辑
2.1 梯级水电核心约束:水量平衡与时空耦合
水电站的物理核心就是一条水量平衡方程:
V_{i,t+1} = V_{i,t} + (Qin_{i,t} − Qout_{i,t}) × Δt其中,V_{i,t}是第i个水库在第t时段末的库容,Qin是入库流量,Qout是出库流量,Δt是时段长度(小时)。这条式子看着简单,但梯级场景下Qin不是给定的外生数据,它包含上游电站的出库流量经过河道时滞后的部分:
Qin_{i,t} = Qnatural_{i,t} + Qout_{i-1,t−τ_i}τ_i是上游电站到第i个电站的水流滞时。这个耦合项让整个模型从一个独立的“单库调度”变成了一个时空耦合的联合优化问题。上游决策会影响下游未来几个小时的入库,所以上下游电站的决策必须放在同一个优化问题里联立求解,而不能分开一个个单独调度。
除了水量平衡,还要考虑以下边界条件:
- 库容上下限:V_i,min ≤ V_{i,t} ≤ V_i,max,保证防洪和供水安全。
- 末库容约束:调度周期结束时,水库库容要落到指定范围,给后续调度留出空间。
- 出库流量上下限:Qout_i,min ≤ Qout_{i,t} ≤ Qout_i,max,受泄流能力和下游河道约束。
- 出力方程:P_h_{i,t} = K_i × Qout_{i,t} × H_{i,t},K_i是综合出力系数,H_{i,t}是当前水头。
水头这一个变量其实是“隐性非线性”的来源。因为水位随库容变化,水头就随库容变化,而库容又是状态变量,所以出力方程在严格意义上是非线性的。短期调度里,如果库容变化范围不大,可以把水头近似成固定值,这样出力方程就线性化了;如果库容波动大,则需要做分段线性化处理。大多数复现代码用的是固定水头或线性水头函数,这样做MILP模型可以保持线性,求解速度也快。
2.2 光伏出力随机性建模:从预测曲线到场景集
光伏随机建模的第一步是获得一条基础预测曲线。通常在复现中直接用典型日的光伏功率预测数据,单位是MW,时间分辨率与调度间隔一致。
第二步是构造预测误差场景。一个常用的做法是假设光伏功率的预测误差服从正态分布,但正态分布在功率接近0或接近额定值时会出现截断问题。我更推荐用Beta分布或者带截断的正态分布,这样可以保证生成的场景功率不会小于0,也不会超过装机容量。实操中,如果你只是复现论文,直接用正态分布然后做截断,效果也够用。
场景生成的步骤大致是这样:
- 生成N个独立的标准正态随机数序列。
- 用AR(1)模型把序列变成有时序自相关的误差序列(光伏预测误差在同一电站不同时刻有延续性,相邻时段误差的相关性通常大于0.8)。
- 将预测功率乘以(1+误差),并裁剪到[0, P_pv_cap]区间内。
- 得到N个初始场景,每个场景对应一个长度为T的功率曲线。
生成之后场景数量通常很多(比如500个),如果直接放进优化模型,变量规模和求解时间会爆炸。这时候要用场景削减算法,把500个场景缩减为代表性的10~20个。业界最常用的是同步回代消除法,核心思想是反复合并距离最近的场景对,把被删除场景的概率累加到保留场景上,直到场景数满足要求。Matlab里可以直接调用函数,也可以用Yalmip自带的场景削减工具,或者手写一个,逻辑也就二三十行。
2.3 目标函数和约束层的标准写法
目标函数的表达方式有两种,等价但形式不同:
第一种,最大化消纳电量期望:
max ∑_{s=1}^{S} π_s × ∑_{t=1}^{T} P_pv_accept(s,t) × Δt第二种,最小化弃光期望:
min ∑_{s=1}^{S} π_s × ∑_{t=1}^{T} (P_pv_avail(s,t) − P_pv_accept(s,t)) × Δt实际中第二种更好用,因为目标函数的值天然是正的,优化器处理起来更稳定,而且“弃电量为0”这个结果可以作为一个直观的验证指标:如果最优解里弃电量严格为0,说明在这个场景组合下系统没有弃光,消纳能力充足。
约束集方面,除了上一节提到的水量平衡、库容、流量、出力约束,还要加:
- 功率平衡约束:P_h_total(t) + P_pv_accept(s,t) = P_load(t) + P_export(t)
- 备用约束:水电可调容量必须在任意时刻覆盖光伏功率波动的一定比例。
- 出力爬坡约束:水电爬坡速率有限制,不能瞬时从0到满发。
关键点在于:功率平衡约束里的P_pv_accept是带场景标签的决策变量,即每个场景下光伏的实际消纳值不同,而水电出力在预调度阶段通常不随场景变化。这是随机规划里“here-and-now”和“wait-and-see”的经典区分:水电出力和水库蓄放水计划在知道真实光伏出力之前就要定下来,光伏消纳水平则可以在知道场景后调整。这个设定必须要在变量建模时区分清楚,否则不同场景之间可以“作弊”,目标函数值会偏离实际意义。
2.4 为什么整个模型能保持为MILP
如果只有连续变量,这是个LP问题,求解很快。但考虑机组启停、最小技术出力、禁止运行区间等实际约束时,就需要引入0-1整数变量,变成MILP问题。
整数变量的典型用途包括:
- 机组开停机状态:u_{i,t} ∈ {0,1},配合出力上下限约束。
- 最小运行/停运时间:用状态变量的时间累积约束表达。
- 禁止运行区间:把出力区间分裂成两个可行段,用辅助整数变量切换。
MILP问题比LP难解,但现代求解器(CPLEX、Gurobi)配合分支定界法,在几十个整数变量的规模下通常几秒到几分钟就能收敛。短期调度模型的总变量数量一般在几千到几万,其中整数变量占比很小,这个规模对商用求解器来说非常轻松。所以复现代码时,优先用Yalmip进行建模,再调用CPLEX或Gurobi求解,是科研圈最主流、最稳妥的技术栈。
3. Matlab代码实现:从数据到结果的完整链路
3.1 数据准备:把物理参数和预测数据组织成结构化变量
我建议把输入数据统一放在一个结构体data里,代码可读性和可扩展性会好很多。典型的数据字段包括:
| 数据 | 字段名 | 说明 |
|---|---|---|
| 梯级电站数 | data.H | 比如3个 |
| 调度时段数 | data.T | 24小时 |
| 场景数 | data.S | 场景削减后的数量 |
| 水库初始库容 | data.V0 | H×1向量 |
| 库容上限/下限 | data.Vmax, data.Vmin | H×1向量 |
| 出力系数 | data.K | H×1向量 |
| 时段长度 | data.dt | 1小时 |
| 光伏预测场景 | data.Ppv | T×S矩阵 |
| 负荷曲线 | data.Pload | T×1向量 |
| 水力时滞 | data.tau | H×1向量 |
参数的单位一定要提前统一。我见过很多初学者把流量单位写成m³/s,库容写成万m³,电量写成MW·h,最后约束怎么调都无解,甚至算出负库容这种荒谬结果。建议全部采用国际单位,库容用m³,流量用m³/s,电量用MW·h。在写平衡约束时,注意流量乘以时间(秒)才等于水量(m³),功率乘以时间才是电量,这一步单位换算是错误高发区。
3.2 场景生成与削减:核心代码示意
场景生成部分我用Matlab代码做一个示例。假设光伏预测功率Ppv_pred是T×1向量,误差模型用AR(1):
% 参数设置 T = 24; % 调度时段数 N = 500; % 初始场景数 S_final = 10; % 削减后场景数 phi = 0.8; % AR(1)自回归系数 sigma = 0.1; % 预测误差标准差 % 生成初始误差场景矩阵:T×N xi = zeros(T, N); for n = 1:N noise = sigma * randn(T, 1); xi(1, n) = noise(1); for t = 2:T xi(t, n) = phi * xi(t-1, n) + noise(t); end end % 生成光伏场景:T×N,裁剪到[0, 装机容量] Ppv_cap = 300; % 光伏装机容量MW Ppv_basic = repmat(Ppv_pred, 1, N); Ppv_scen = Ppv_basic .* (1 + xi); Ppv_scen = min(max(Ppv_scen, 0), Ppv_cap); % 场景削减:同步回代消除 [Ppv_reduced, Prob] = scenario_reduction(Ppv_scen, S_final);scenario_reduction函数可以自己实现,核心是不断合并距离最近的两个场景。距离一般用欧氏距离,距离的定义是各时段功率差的平方和。合并时把被删除场景的概率加到距离最近的保留场景上。这个算法在文献里叫Fast Forward Selection,Matlab实现大约40行,网上也能找到现成版本。
削减完你会看到一个有意思的现象:概率最大的场景往往不是“平均场景”,而是与预测曲线接近、误差较小的那些场景。而极端阴雨天和极端强辐照天的场景虽然概率小,但它们会显著影响水电站的预留调节空间,所以不能随便删。
3.3 Yalmip建模:变量定义与约束组装
Yalmip是目前Matlab环境下做优化建模最省事的工具箱。先定义决策变量:
% 连续变量 V = sdpvar(H, T+1, 'full'); % 库容 Qout = sdpvar(H, T, 'full'); % 出库流量 Ph = sdpvar(H, T, 'full'); % 水电出力 Ppv_accept = sdpvar(S, T, 'full'); % 各场景光伏消纳功率 % 二进制变量(如果考虑机组启停) u = binvar(H, T, 'full');然后写约束。这里我挑几个容易出错的约束重点说明。
水量平衡约束:
constraints = []; for i = 1:H for t = 1:T inflow = Qnatural(i, t); if i > 1 && t > tau(i) + 1 inflow = inflow + Qout(i-1, t - tau(i)); end constraints = [constraints, V(i, t+1) == V(i, t) + (inflow - Qout(i,t)) * dt_sec]; end end注意dt_sec的单位换算:如果流量是m³/s,库容是m³,那么一个小时的流量换算水量需要乘以3600秒。
出力方程(固定水头简化版):
constraints = [constraints, Ph == K .* Qout]; % 简化为线性关系实际项目中水头会随库容变化,更精确的做法是写成:
constraints = [constraints, Ph(i,t) == K(i) * Qout(i,t) * (H0(i) + alpha(i) * (V(i,t) - V0(i)))];但这样会引入双线性项(Qout乘以V),变成一个非凸MINLP问题,求解困难。所以复现时一般用固定水头,或者把水头随库容的变化做分段线性近似,用PL函数表达。最省事且能在EI复现中站得住脚的做法就是固定水头,并在论文里注明“调度期内水头变化不大,按额定水头近似”。
目标函数和求解:
% 弃光期望最小化 objective = sum(sum(Prob' * (Ppv_scen_reduced - Ppv_accept))) * dt_h; % 求解 ops = sdpsettings('solver', 'cplex', 'verbose', 2, 'showprogress', 1); result = optimize(constraints, objective, ops);注意Prob是每个场景的概率向量,Ppv_accept是S×T矩阵,这里的乘法维度要对齐。更稳妥的写法是用循环逐个场景累加:
objective = 0; for s = 1:S objective = objective + Prob(s) * sum(Ppv_scen_reduced(s,:) - Ppv_accept(s,:)) * dt_h; end这样写逻辑更清晰,调试也方便。
3.4 结果输出与可视化:图表怎么出才能进论文
优化结束后,你需要输出哪些结果?
第一张图是“场景化的光伏消纳曲线”。横轴为时间,画三条曲线:光伏预测/可用功率、各场景下实际消纳功率的期望值、以及弃光功率(可以用阴影填充表示)。这张图能直观展示不确定性对消纳的影响。
第二张图是“梯级电站出力与水位过程”。画各个电站的出力柱状图、库容变化曲线,以及水位变化曲线。这里有一个很实用的小技巧:优化结果里水位过程线要画成阶梯状还是平滑曲线,取决于你输出的是调度时段末的库容点值还是连续过程。EI论文里一般画阶梯状,标注“时段末水位”或“时段平均水位”,避免审稿人质疑数据的时间分辨率。
第三张图是“场景概率敏感性分析”。把场景削减前后的期望消纳电量做个对比,画两个柱状图,说明场景削减造成的损失很小。这也是审稿人比较喜欢看到的验证材料。
输出Excel数据文件也很有必要,后续画图、做敏感性分析、写论文时都可以直接使用。代码结尾加一段:
% 保存结果 results.V = value(V); results.Qout = value(Qout); results.Ph = value(Ph); results.Ppv_accept = value(Ppv_accept); save('results.mat', 'results');4. 实操中的常见问题与排查技巧
4.1 场景数量与计算时间怎么平衡
场景削减前的N值不是越大越好。我试过N=1000、S=10和N=500、S=10,两者优化结果差异在0.1%以内,但前者的场景生成和削减时间多了近一倍。建议初始场景数控制在200~500,削减到10~20个,这就够了。
但有一个坑:如果削减到5个以内,极端场景大概率被合并掉了,期望消纳电量会偏乐观,论文里的结果会被审稿人质疑。建议至少保留10个场景,并且把削减前后的期望结果差值作为一项验证指标写进论文。
4.2 无可行解:先查这四处
跑优化最崩溃的时刻就是提示Infeasible problem。根据我的经验,九成无可行解是下面四个原因之一。
第一,末库容约束与初始条件和来水不匹配。比如初始库容本来就低于目标末库容,同时来水又偏枯,那无论如何都满足不了末库容约束,模型必然无解。遇到这种情况,先放宽末库容范围,看能不能跑通,能跑通就说明是数据预期问题。
第二,单位换算错误。m³/s和m³之间差3600倍,一旦写错,水量平衡就全是乱的。排查方法是把约束残差打印出来看,如果某个时段的水量平衡残差高达几千,基本就是单位问题。
第三,光伏强制全消纳。如果你写了Ppv_accept == Ppv_avail(即强制光伏必须全额消纳)的约束,而系统在某个场景下无论如何都装不下这些功率,就会出现不可行。解决办法是把等式约束改成≤,允许弃光,然后用目标函数去最小化弃光量。
第四,负荷与出力上下限冲突。如果负荷曲线很平坦且数值很高,而水电+光伏的最大出力不够,那就必然缺电。这时需要引入缺电决策变量(允许切负荷),并把惩罚因子设得很高,而不是直接写成等式平衡。
4.3 模型跑通但结果不合理
如果模型能解,但结果怪怪的(比如某段时间水电出力剧烈震荡、或者弃光率异常高),优先怀疑两个地方:爬坡约束写得够不够严,以及场景削减后极端场景保留得是否合理。
水电出力剧烈震荡往往是因为模型在相邻时段反复调节出力来追逐光伏波动,但这个调节在物理上做不到。排查方法是在约束里加爬坡限制:
constraints = [constraints, -ramp_rate <= Ph(i,t+1) - Ph(i,t) <= ramp_rate];ramp_rate根据实际水电机组的爬坡能力设定,一般取额定出力的5%~10%/分钟,换算成小时级就是额定出力的0.3~0.6倍/小时。
弃光率异常高的另一个隐藏原因是负荷曲线设置不合理。如果负荷曲线是平坦的,光伏午间大发时负荷没起来,系统没有足够空间消纳,弃光就会很高。这时候要么把负荷曲线的峰谷差调大,要么在模型里加储能或抽水蓄能,否则结果就是“合理但不好看”。
4.4 求解器相关:没有CPLEX/Gurobi怎么办
Yalmip的优势是求解器无关,你可以随时切换。如果没有商业求解器License,本地调试可以用开源的SCIP或GLPK,但MILP求解速度会慢不少。建议的调试流程是:先用SCIP跑一个小规模案例(比如3个电站、6个时段、5个场景),确认模型逻辑正确,再切换到CPLEX跑完整案例。
CPLEX有学术免费License,用学校邮箱注册可以申请。安装完成后确认Matlab能调用:
yalmiptest如果Yalmip识别不到CPLEX,可能是环境变量没配好。把CPLEX的bin目录添加到系统PATH,重启Matlab基本都能解决。还有一种情况是电脑上装了多个版本,Yalmip默认选了旧版本导致报错,可以在sdpsettings里显式指定:
ops = sdpsettings('solver', 'cplex', 'cplex.version', '128');4.5 复现验证:怎么证明你的代码是对的
代码跑通只是第一步,要说服审稿人或导师,至少要做三组验证。
第一组是退化测试。把所有场景的概率合并成一个,即只有一条确定性曲线,此时随机优化的结果应该与确定性优化完全一致。这个验证能证明随机框架的代码逻辑没有写错。
第二组是极端场景影响测试。人为加入一个“光伏全天功率极低”的极端场景,看水电是否会把水位抬高、预留更多水量应对可能出现的光伏不足。如果结果没有这种反应,说明场景概率或者非预期性约束的设置有问题。
第三组是敏感性分析。把场景数S从5逐步增加到30,看期望消纳电量的变化趋势。正确的结果应该是先快速增加、然后趋于平稳。如果S从10变到20时结果还在大幅波动,说明场景削减不够,需要增加保留场景数。
5. 这个模型还能怎么扩展
5.1 从静态调度到滚动优化
把24小时的一次性优化,改成“每1小时滚动一次、每次只看未来24小时”的模型预测控制结构。光伏预测信息随时间更新,调度计划也随之滚动修正,更贴近实际运行。代码改动不复杂:外层加一个循环,每个时刻更新预测数据,重新求解一次优化问题,只执行当前时段的决策。
5.2 从单目标到多目标
可以在目标函数里加上“尽量维持水库水位平稳”或者“尽量让水电出力平滑”的惩罚项,变成多目标优化。多目标处理有两种方法:线性加权和ε-约束法。前者简单,但权重难调;后者能生成Pareto前沿,适合做出差异化的结果图。
5.3 引入需求响应或储能
光伏大发时段如果有可调负荷参与消纳,比如抽水蓄能或者电解水制氢负荷,系统的可消纳电量上限会明显提升。这个扩展方向在EI论文里非常流行,模型只增加两类变量:储能系统的充放电功率和电量状态。储能约束和水库约束在数学结构上几乎一样,代码复用度高。
5.4 从期望值到风险度量
传统期望值模型对低概率高损失的场景不敏感。如果想让调度方案更保守,可以在目标里加入CVaR项,约束最坏情况下的弃光风险。Yalmip对CVaR的支持很好,用六到八行代码就能加进去,可以让论文方法部分看起来更完整。
6. 几个我实实在在踩过的坑
最后说几个实操经验,不算系统的教学,纯粹是希望能帮你少走弯路。
第一个坑:变量维度不匹配。Yalmip里sdpvar定义了三个维度,写约束时下标稍微错一位就会报维度错误。我的建议是刚开始别怕报错,先写一个极小规模(2个电站、4个时段、3个场景)的测试用例,逐个约束验证,全部跑通再换成完整数据。盲目上来就写完整模型,调试成本会翻倍。
第二个坑:场景削减算法必须验证概率之和为1。削减完一定要检查sum(Prob)是否等于1。合并概率时如果算法有bug,概率不为1,那么目标函数其实被无意义地缩放了一倍或缩小了一倍,结果“看起来能跑”,但实际数值没法解释。这个bug我在学生代码里见过不止一次,几乎看不出来。
第三个坑:固定水头不要拍脑袋取额定水头。如果一个水库调度期内库容波动巨大,实际水头可能从满库水头掉到接近死水位水头,固定用额定水头会让中后期出力计算失真。简单处理办法是取调度期内平均水位对应的水头。更好的办法是迭代一两次:先按固定水头求解,得到水位过程线后更新水头,重新求解,循环两三次,收敛后结果就相当稳定。
第四个坑:画图时注意时区分。光伏出力曲线是时段初、时段中还是时段末。如果模型里用的是时段平均功率,画图时建议画成阶梯图而不是折线图。阶梯图能直观体现数据的时间分辨率,审稿人也更习惯这种表达。
第五个坑:目标函数的数值尺度不要太小。如果库容单位是m³、出力单位是MW,目标函数算出来的电量期望可能非常大(几万到几十万),而约束的数值范围从几百到几百万,量级差异可能让MILP求解器在数值上出现问题。可以适当对目标函数做归一化,或者调整变量单位,比如库容改用亿m³,这样数值尺度更接近,求解更稳定。我在实际调试中遇到过因为单位尺度过大导致CPLEX在无可行解和可行解之间反复横跳的情况,统一量纲后秒解。
这个项目说到底,核心不在于“会调一个求解器”,而在于你对水电物理过程、光伏随机特性、优化建模这三件事分别理解到什么程度。Matlab和Yalmip只是工具,帮你把数学模型翻译成计算机能处理的代码。等你把这个模型完整跑通一遍,再把场景数、电站数、约束类型逐个改动试过来,你会发现随机优化调度类的问题基本都能举一反三了。后续想加储能、加需求响应、加CVaR,都是在这套框架上做增量。希望这篇经验贴能给你搭好第一块地基。