含风电电力系统低碳调度:源荷双侧不确定性建模与Matlab实现

发布时间:2026/10/11 6:20:05
含风电电力系统低碳调度:源荷双侧不确定性建模与Matlab实现 做含风电的调度优化最难的一关不是把目标函数写出来而是让模型真正扛得住实时运行。我最早跑经济调度时系统里所有风电、负荷都给死值求解器几分钟就出结果看上去一切完美。可一放到带随机波动的真实场景里方案立刻破功——要么风电预测偏高导致出力缺口要么负荷预测偏低导致备用被吃穿。后来我把源荷两侧不确定性正式建模进调度模型并且把碳交易成本写进目标函数整个项目的复杂度上了一个台阶但结果也终于像样了。这篇文章围绕考虑源荷两侧不确定性的含风电电力系统低碳调度这个题目展开完整记录我基于Matlab的实现思路从风电和负荷的不确定性建模到场景生成与削减到含碳交易的低碳调度模型再到Yalmip求解和排坑经验。适合正在做电力系统优化调度方向的研究生、刚接触新能源消纳与低碳调度的工程师以及想用Matlab快速验证算法思路的读者。全程会给出可直接运行的代码逻辑和调试心得照着搭一套模型不成问题。1. 为什么源荷两侧不确定性会成为调度模型的分水岭1.1 风电接入之前系统是怎么调度的传统经济调度的思路很简单给定负荷预测值在满足机组出力上下限、爬坡约束和网络约束的前提下让燃料成本最小。这个范式在负荷相对可控、电源以火电为主的年代基本够用因为负荷预测误差通常只有百分之几靠系统预留的固定旋转备用就能消化掉。风电进来之后情况变了。风电出力不是预测值加个小误差那么简单它的波动幅度大、间歇性强短期功率预测误差经常达到10%到20%极端天气下更高。如果调度模型里把风电当成一个固定出力节点那等于默认风电说的数一定靠谱一旦实时出力达不到系统功率就不平衡只能靠火电紧急爬坡或切负荷来补救。我做第一个含风电模型时踩过这个坑把风电预测功率直接代入功率平衡结果某天实际风速偏低风电出力只有预测值的六成备用全部耗尽频率险些越限。从那以后我意识到调度模型必须把不确定性当成一个正式的输入量而不是事后补救的事。1.2 双侧不确定性到底是什么意思源荷两侧不确定性里的源指的是以风电为代表的电源侧不确定出力荷就是负荷侧的不确定性。电源和负荷两边都有预测误差如果建模时只考虑一边另一边用固定预测值模型就会系统性低估风险。一个很直观的例子某日负荷预测偏低了5%风电预测又偏高了10%两边叠加起来净负荷的实际值比预测值高出15%。这个量级已经超过常规备用容量传统确定性模型直接失守。所以源荷双侧不确定性不是一个可有可无的装饰而是含风电电力系统调度必须面对的基本现实。更准确地说调度关心的不是一个单独的负荷不确定性和一个单独的风电不确定性而是二者叠加后的净负荷不确定性。用公式表达就是净负荷预测值 负荷预测值 - 风电预测值实际净负荷 实际负荷 - 实际风电出力净负荷预测误差 负荷预测误差 - 风电出力预测误差注意符号负荷误差是正的实际高于预测风电误差是负向偏差实际低于预测它们在功率平衡上是叠加的。这就是为什么双侧不确定性要比单侧更麻烦——它们不是简单抵消很多时候是互相放大。1.3 净负荷视角和备用需求的量级从净负荷视角看调度问题一切会清晰很多。调度员真正要做的是安排火电出力计划使得在各种可能的净负荷场景下系统都能保持功率平衡。火电出力不能瞬间变化它需要在预测净负荷附近留出足够的上下调节空间。我一般用两个指标量化不确定性对备用需求的影响P50预测误差净负荷预测的中位数误差对应调度基准场景P95预测误差95%置信水平下的净负荷误差对应备用需求的保守估计。实测下来含风电系统的P95净负荷误差可能是负荷P95误差的2到3倍。如果备用只按负荷误差设风电一波动就抓瞎。这个量化过程直接决定了后面场景生成和备用约束怎么设置。2. 场景法不确定性建模与场景削减的完整链路2.1 随机规划、鲁棒优化、分布鲁棒我为什么选场景法处理不确定性主要有三条路鲁棒优化、随机规划、分布鲁棒优化。它们各有适用场景我简单说下取舍。鲁棒优化考虑的是最坏情况不需要概率分布只要给定不确定集合比如风电出力在某个区间内波动就能保证所有情况下方案可行。它的优点是稳健缺点是过于保守——为了一个概率极低的极端场景牺牲大量经济性。随机规划也叫场景法用一组带概率的场景来代表不确定性分布目标函数取期望值。它不会为极小概率事件过度牺牲经济性也更贴近实际运行逻辑。缺点是场景数量对求解速度影响很大需要做场景削减。分布鲁棒优化介于两者之间假设知道分布的矩信息或支撑集在最坏分布下优化。理论上更精巧但建模和求解复杂度高工程落地门槛不低。我做这个项目选的是场景法。原因有三第一Wind和Load预测误差的分布信息相对容易获得场景法能充分利用概率信息第二场景法的物理含义清晰电力系统工程师容易理解和接受第三Matlab里有成熟工具支持场景生成、削减和优化求解代码实现效率高。2.2 风电场景生成功率域直接加误差这个做法有隐患风电场景生成看起来简单预测功率加上一个随机误差项就行。但直接这么做会踩一个隐蔽的坑。风电出力有上下界0到装机容量。如果用正态误差直接加在功率域上采样结果很容易超过这些界限你得做截断处理。截断之后概率分布的形状就变了本来该有的尾部风险被强行抹掉。比如一个200MW的风电场预测出力是120MW误差标准差30MW正态采样会出现负出力截断成0之后原本出力很低的样本全堆在0上场景集合失真。我建议换一个思路在风速域做采样再通过风机功率曲线转换成出力场景。风速分布一般用Weibull分布描述采样值天然为正经过功率曲线转换后出力自然落在0到额定功率之间不会出现截断失真的问题。功率曲线的分段函数我贴在下面风速 v v_ci切入风速或 v v_co切出风速出力为0v_ci ≤ v v_r额定风速出力按线性或非线性插值v_r ≤ v ≤ v_co出力为额定功率。Matlab里生成风速场景的核心代码大概长这样% 生成Weibull分布的风速场景 n_scenes 500; k_shape 2.2; % Weibull形状参数 c_scale 8.5; % Weibull尺度参数 v_scene wblrnd(c_scale, k_shape, n_scenes, n_wind); % 通过风机功率曲线转换为出力 P_w_scene zeros(n_scenes, n_wind); for i 1:n_wind P_w_scene(:, i) wind_curve(v_scene(:, i), wind_params(i)); end注意如果有多台风电场风速场景之间要带相关性。实际做法是先用Cholesky分解处理相关矩阵生成相关的标准正态样本再做变换到Weibull分布。2.3 负荷场景生成与相关性处理负荷不确定性比风电好处理一些预测误差通常认为是正态分布标准差根据预测模型精度取负荷预测值的1%到3%。负荷场景直接在功率域加正态误差是可以的因为负荷没有硬性上下界问题而且预测误差的分布特性相对稳定。要注意的是不同节点的负荷误差不能全部独立同一地区负荷受温度、天气共同影响会有空间相关性。处理方法同样是相关系数矩阵加Cholesky分解。当负荷场景和风电场景都要考虑时还需要考虑负荷预测误差与风电预测误差之间的相关性吗从物理上说温度会影响负荷水平也影响风速确实存在一定耦合关系。但在工程实现中这两者通常先独立采样再组合因为耦合关系的数据很难拿到而且对最终调度结果的影响量级不大。这个假设在文献里也很常见实测下来问题不大。场景组合代码示意% 假设已有 wind_scene (n_scenes, n_wind) 和 load_error_scene (n_scenes, n_load) % 组合成净负荷场景 net_load_scene repmat(P_load_base, n_scenes, 1) - wind_scene * ones(1, n_wind);2.4 场景削减K-means与同步回代削减的取舍500个场景直接丢进优化模型求解规模会非常大尤其是带0-1整数变量的时候可能几个小时都算不完。所以必须做场景削减把500个场景压缩到20到50个同时保持场景集对原始分布的代表性。主流的削减方法有两个同步回代削减SBR和K-means聚类。SBR的思路是反复合并距离最近的两个场景把被合并场景的概率累加到保留场景上直到场景数量达到目标。它的优点是保留的场景概率分配严格符合原始分布缺点是计算复杂度高而且容易丢掉一些极值特征。K-means的思路是把所有场景按距离聚成K簇用聚类中心代表该簇场景簇内场景总概率作为中心场景的概率。实现简单计算快Matlab自带kmeans函数。实测用K-means配合适当的K值效果很好。两种方法我都跑过对比下来当原始场景数在1000以内时SBR削减质量略好一点但差异不大当原始场景数大或者需要频繁重复削减时K-means的效率优势很明显。我的做法是先用快速前向选择法粗削到100个再用K-means聚到30个速度和精度都兼顾了。场景削减还有一个很容易被忽略的细节削减后要检查极端场景有没有丢。我见过削减完的场景集里最大净负荷点没了结果备用约束事实上被放松了。所以削减后我总会补做一步校验% 校验极端场景是否保留 extreme_scene [max(net_load_scene); min(net_load_scene)]; [~, idx_keep] ismembertol(extreme_scene, scene_reduced, 0.01, OutputAllIndices, true);如果极端场景没保住手动把它加进削减后的集合赋予一个小概率。这样既保证分布代表性又不至于漏掉风险点。3. 带碳交易的低碳调度模型构建3.1 碳交易机制在调度模型中的表达方式低碳调度的核心是把碳排放成本化。比较常见的有三种表达碳税、碳排放上限约束、碳交易机制。我项目里用的是碳交易碳排放权交易因为它最贴近当前电力行业实际而且对调度结果的影响更有意思。碳交易的基本逻辑是系统或者每个发电主体先拿到一个碳排放配额配额不够就要到市场上买配额多了可以卖。这一进一出就构成碳成本或碳收益。配额分配方式我采用基准线法根据机组发电量和单位发电量基准碳排放强度的乘积来分配。基准强度定得太严大多数机组都要买配额定得太松碳交易就形同虚设。实际建模中基准强度可以根据行业平均水平设定也可以通过参数灵敏度分析来确定。3.2 目标函数与约束集含风电电力系统低碳调度的目标函数包含三部分火电燃料成本、碳交易成本、弃风惩罚成本可选。写成数学形式就是最小化 F Σ_i (a_i P_i^2 b_i P_i c_i) C_carbon × (E_actual - E_quota) ξ × Σ弃风量其中P_i是火电机组i的出力和对应成本系数C_carbon是碳价单位元/吨CO2E_actual是系统实际碳排放量按机组出力乘排放强度累计E_quota是系统获得的碳配额总量ξ是弃风惩罚系数用来鼓励消纳风电。注意碳交易项可能是正的也可能是负的排放超标时为成本排放低于配额时变成收益。这个正负切换不需要额外处理数学上直接放进目标函数求最小化就行。约束条件里除了传统的功率平衡、机组出力上下限、爬坡约束、最小启停时间约束之外还要加旋转备用约束和风电出力上限约束。旋转备用约束是我重点强调的上行备用Σ min(P_i,max − P_i, 爬坡上限) ≥ 净负荷P95误差 风电预测偏差下行备用Σ min(P_i − P_i,min, 爬坡下限) ≥ 净负荷P95误差的反向部分这个约束直接承担不确定性在调度模型中的落地作用方向一定要写对很多初学者的代码在这里出错。模型中碳约束可以设成硬约束排放不能超过某上限也可以只通过目标函数成本化调节。工业项目里两种都用我代码里留了一个开关use_emission_limit 1; % 1为硬约束0为仅成本化 if use_emission_limit constraints [constraints, sum(emission_rate .* P_g) E_max]; end3.3 不确定性与低碳目标的内在权衡低碳调度有趣的地方在于不确定性和低碳目标不是两个并列的模块它们之间存在内在权衡。风电是零碳电源调度模型在碳成本的压力下会更愿意消纳风电这没问题。但风电不确定性大要保证系统安全就得预留更多备用。备用主要靠火电提供火电多发电就多排放——于是多消纳风电减排和多留备用增排之间产生了张力。碳价越高系统越愿意承受备用成本去消纳风电碳价低系统可能更倾向让火电稳定出力宁可少量弃风。这个权衡在确定性模型里看不到只有把源荷两侧不确定性建模进去碳价的调度信号才真正体现出来。这也是考虑源荷两侧不确定性的含风电电力系统低碳调度这个题目的核心价值点。4. Matlab代码实现从数据输入到求解4.1 程序模块划分整个程序我按四个模块组织每个模块独立成文件调试和复用都方便main.m主入口负责调用数据读取、场景生成、模型构建、求解和结果输出scenario_generate.m生成风电和负荷场景输出削减后的场景集合及概率low_carbon_dispatch.m构建优化模型调用求解器求解report_result.m解析求解结果输出各机组出力、碳排放、成本、弃风量等指标。其中low_carbon_dispatch.m是核心我把模型构建和求解器调用放这里。在项目早期我把所有代码堆在一个文件里改一个参数要翻半天逻辑还容易错。后来拆分成模块调试效率明显提升——强烈建议你从一开始就按模块写别偷懒。系统数据我直接采用了Matpower格式的IEEE节点数据在case文件里加了风电场所在节点、预测出力、装机容量等字段。这样可以用现成的节点数据和线路参数不用自己造数据出问题也好排查。4.2 核心建模代码Yalmip写法优化建模我推荐直接用Yalmip工具箱配合求解器使用比手写矩阵约束省事得多。Yalmip的符号变量声明方式很直观声明变量、写约束、定目标、调求解器四个步骤非常清晰。下面是一个核心代码骨架展示火电出力、风电出力、碳交易成本和备用约束怎么落进Yalmip% 决策变量火电出力风电调度出力弃风量 P_g sdpvar(1, n_gen, full); P_w sdpvar(1, n_wind, full); curtail_w sdpvar(1, n_wind, full); % 目标函数燃料成本 碳交易成本 弃风惩罚 fuel_cost sum(a .* P_g.^2 b .* P_g c); carbon_cost C_carbon * (sum(mu .* P_g) - E_quota); curtail_cost xi * sum(curtail_w); objective fuel_cost carbon_cost curtail_cost; % 约束 constraints []; % 功率平衡基准预测场景下的净负荷平衡 constraints [constraints, sum(P_g) sum(P_w) net_load_base]; % 火电出力上下限和爬坡约束 constraints [constraints, P_g_min P_g P_g_max]; % 风电调度出力不能超过可用出力且不能低于0 constraints [constraints, 0 P_w P_w_forecast]; constraints [constraints, 0 curtail_w P_w_forecast]; % 弃风关系调度出力 弃风 可用风电 constraints [constraints, P_w curtail_w P_w_forecast]; % 旋转备用约束考虑源荷不确定性 constraints [constraints, sum(min(P_g_max - P_g, ramp_up)) reserve_up]; constraints [constraints, sum(min(P_g - P_g_min, ramp_down)) reserve_down]; % 求解 ops sdpsettings(solver, gurobi, verbose, 2); result optimize(constraints, objective, ops);这里要特别说明一下功率平衡按基准净负荷场景来写不确定性的影响通过旋转备用约束传导进模型。这是一种简化但工程上很实用的建模方式求解规模小速度快方案也具备足够的运行可靠性。如果你的审稿人或导师要求更严格的场景约束可以把功率平衡约束写成对每个削减后场景都成立for s 1:K constraints [constraints, sum(P_g) sum(P_w) net_load_scene(s)]; end但这种写法会让调度方案只针对一组固定的风电出力本质上是把多场景变成同时满足多个确定性平衡对风电消纳的灵活性刻画不够细需要谨慎使用。我的代码里用一个参数控制用哪种模式算是个可配置的选项。4.3 场景削减代码实现用K-means做场景削减时有几个操作细节值得注意。首先是归一化。风电场景和负荷场景的量纲虽然都是MW但不同节点的数值范围可能差异很大。如果不归一化直接聚类数值大的变量会主导距离计算。我通常先做z-score标准化聚类后再把中心场景映射回原始数值。其次是K值选取。K太小场景代表性不够K太大求解负担重。我实测教训IEEE 30节点系统配3台风电场原始500个场景削减到30个求解时间还在可接受范围削减到50个求解时间翻倍不止。建议先做一组K值扫描比如20、30、50对比目标函数值和求解时间找出拐点。% K-means场景削减 [idx, C, ~] kmeans(scene_norm, K, Distance, sqeuclidean, Replicates, 5); % 计算每个聚类中心的概率权重 prob zeros(K, 1); for k 1:K prob(k) sum(idx k) / length(idx); end % 还原场景到原始量纲 scene_reduced C .* (scene_std 1e-6) scene_mean;Replicates参数建议设置为5以上因为K-means对初始点敏感多次重复可以避免陷入差局部最优。我一开始没设这个参数结果不同批次跑出来的削减场景差异很大后来加了Replicates才稳定下来。4.4 结果输出与后处理求解完成后必须把结果指标提取出来否则一堆决策变量看不出所以然。我每次会输出以下指标各火电机组出力、风电实际消纳量燃料成本、碳交易成本、弃风惩罚成本系统总碳排放量和碳配额盈余/缺口弃风率、碳排强度、单位电量成本。一个小经验Yalmip求解后目标函数值可以用value(objective)取到但碳交易成本是正还是负要单独计算不要只看总目标。我迭代调试时发现某个碳价下燃料成本上升但总成本下降原因是碳交易收益增加了——只看总数根本定位不到这个关系。5. 实测排坑求解器、参数与模型调试经验5.1 求解器选型与MIP参数模型里如果有机组启停变量或最小启停时间约束问题就变成混合整数规划MIP。我的建议是优先用Gurobi或CPLEX远超Matlab内置求解器的性能。实测同一个模型500场景削减到30个Gurobi用不到一分钟就收敛内置求解器可能要跑十几分钟甚至卡死。Yalmip调用Gurobi很简单装好求解器后在sdpsettings里指定solver参数即可。几个我常用的关键参数mipgap设为0.0001或更小保证解的质量但注意求解时间会上升timeLimit设一个上限防止极端案例跑不完verbose调试时开2正式批量跑时关掉。如果二次成本项让问题变成MIQPGurobi也能处理。但设计模型时尽量把目标函数控制在二次以内不要出现三次或更复杂的非线性否则求解器要落到MINLP稳定性会差很多。5.2 几个踩过的坑我实际调试中踩过的坑挺多挑几个最有代表性的分享。第一个坑是碳价数量级问题。有一次碳价设置不当导致碳交易成本比燃料成本小了三个数量级模型完全感受不到碳约束的压力调度结果和普通经济调度几乎一样。建议拿一个具体工况手动估算一下碳排放量和碳价乘积确认碳成本占到燃料成本的5%到20%这个区间碳价信号才有效。第二个坑是旋转备用方向写反。reserve_up约束里用的是min(P_g_max - P_g, ramp_up)我一开始误写成min(P_g - P_g_min, ramp_up)导致系统以为有大量上备用实际根本没有。这类错误用单测试算例验证时就能发现把净负荷台阶跳上去看备用是否真的够。第三个坑是场景削减后概率权重没有归一化。K-means聚类后如果某簇为空prob(k)算出来是0权重数组加起来不等于1目标函数的期望成本就会整体偏小导致模型的风向不对。我后来加了归一化和空簇清理的逻辑。第四个坑是Matlab版本与Yalmip/Gurobi的兼容性问题。我项目用的是Matlab R2025b配Yalmip最新版和Gurobi 11跑得很顺。但同一套代码在不同的Matlab版本上出现过求解器License连接失败、Yalmip函数不可用的问题。建议开始前先跑一个sdpvar最小示例验证环境没问题再进行完整模型调试。5.3 模型求解变慢时怎么降维我遇到过最头疼的情况是场景数增加后模型怎么都算不快。把500个场景削减到20个还慢就得考虑降维手段。第一个手段是削减场景数比如从30割到20代价是代表性下降但速度可能提升数倍。第二个手段是固定整数变量先解一个LP松弛看趋势找到最优开机组合后再把整数变量固定住只优化连续变量。第三个手段是把二次成本分段线性化把MIQP转成MILP。线性化后求解器能充分发挥分支定界的效率。这几种方法我都试过组合起来使用单次求解时间从十几分钟降到了两三分钟效果非常明显。如果你场景数很大、节点数又多建议优先尝试前两种手段。6. 算例验证低碳调度的效果到底从哪看出来6.1 测试系统与对比实验设计我用的是含风电接入的IEEE标准算例系统来做验证风电场接入点选在负荷相对集中、线路传输容量相对宽裕的节点上。为了对比低碳调度的效果我设计了三组实验第一组确定性常规经济调度不考虑源荷不确定性不考虑碳成本作为基准 第二组确定性低碳调度只加碳交易成本不建模源荷不确定性 第三组随机低碳调度同时考虑源荷两侧不确定性和碳交易成本即本文核心模型。这样对比有一个明显好处第二组和第三组的差异能单独反映源荷不确定性建模带来的影响而第一组和第二组的差异反映低碳约束带来的影响。6.2 结果解读以一个典型的测试工况为例具体数值因系统规模和数据而异看相对变化趋势即可方案燃料成本碳排放弃风率总成本含碳成本确定性常规调度基准基准较高基准确定性低碳调度略升下降降低略升或持平随机低碳调度小升进一步下降更低与第二组互有高低从现象看第三组方案在碳排放和弃风率上表现最好燃料成本比基准会上升一点但这是正常代价——为了应对不确定性预留了更多备用同时为了降低碳排放调整了机组出力排序。最关键的一点是第三组方案的可执行性远高于第一组因为它在生成调度计划时就把净负荷波动考虑进去了实时运行时不容易出现功率不平衡。另外我还做了碳价敏感性分析从碳价0元/吨一路升到200元/吨观察调度结果如何变化。碳价低时系统排碳配置基本不受影响碳价升高到某个临界值模型的机组出力排序会明显向低碳机组倾斜风电消纳率也会跨过一个台阶继续升到很高后减排增量趋缓说明碳价信号在某个区间内最有效。这个临界值受系统燃料结构和碳排放强度影响很大需要针对自己的算例实际算一遍不能拍脑袋定碳价参数。6.3 置信水平与碳价敏感性的联动备用约束里置信水平的选择比如P80、P90、P95对结果影响不小。我扫描过一组置信水平从80%升到99%备用需求量显著增加火电出力下限被抬高导致风电消纳空间变小碳排放不降反升。这是一个反直觉但很重要的发现过度追求不确定性覆盖可能反过来损害低碳目标。所以在实际项目里置信水平要根据运行可靠性标准和碳减排目标联合校准不要盲目设高。碳价和置信水平还会联动高碳价时系统愿意承担备用成本去消纳风电此时即便置信水平升高碳排放增幅也不会太大低碳价时系统本来就不太愿意为风电预留太多备用置信水平一升高低碳效果就被明显侵蚀。二者联合调参是让模型跑到最优工况的关键。最后再分享两个小技巧第一跑任何不确定性调度模型之前先跑一遍确定性模型并保证结果合理。这样可以把问题拆分如果确定性模型结果都有问题说明是模型约束写错了如果确定性模型正常但随机模型异常大概率是场景生成或削减的锅。逐层定位调试效率会高很多。第二把场景生成模块做成可复现的固定随机种子。源荷不确定性研究里场景生成自带随机性如果不固定随机种子每次跑出来的场景都不同结果难以对比。我在main.m开头加一行固定随机种子所有对比实验都在同一组场景下进行结论才可信。含风电电力系统的低碳调度说实话不是一个能一蹴而就的模型。源荷两侧不确定性、碳交易成本、备用约束、场景削减每一个模块单拎出来都有讲究组合在一起更是充满权衡。我做完这个项目最大的体会是调度模型的价值不在于算法多花哨而在于每个假设是否清晰、每个参数方向是否正确、每类不确定性是否真正进入了决策。把这几点想清楚Matlab代码反而是最简单的部分。