ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

源荷双侧不确定性下的电力系统低碳鲁棒调度及Matlab实现

源荷双侧不确定性下的电力系统低碳鲁棒调度及Matlab实现 1. 项目概述与核心问题拆解1.1 这个项目到底在解决什么问题先说结论这个题目的本质是在做一个电力系统经济调度Unit Commitment / Economic Dispatch的优化问题只不过比教科书版本多了三个现实约束风电场并网、源荷两侧不确定性、碳减排目标。翻译成人话就是在风能出力忽高忽低、用电负荷难以精准预测的前提下怎么安排火电机组的启停和出力让系统总成本最低同时碳排放也尽量少。老规矩我先把这个领域里大家默认的背景铺开。传统电力系统调度是源随荷动——负荷预测出来后调度中心按预测值安排发电机出力。但风电大规模并网后这套逻辑就不好使了风电出力受风速影响天然带随机性和间歇性你没法把它当成一个稳定的电源来调度。更麻烦的是负荷侧也越来越复杂电动车充电、分布式光伏、用户侧储能这些东西一多负荷预测误差比过去大得多。两边都有不确定性如果调度方案不考虑这些不确定性轻则弃风弃光重则切负荷甚至系统失稳。再叠加上低碳调度这个目标。双碳背景下电力系统减排是大头火电厂烧煤烧气必然是排碳主力。于是调度模型里除了常规的发电成本还要引入碳交易成本、碳惩罚成本或者碳捕集成本这些新变量。说白了现在做调度优化成本函数里低碳两个字越来越值钱。1.2 这类问题最适合用什么方法做如果你在电气工程专业读过研究生应该对这类题目不陌生这基本是电力系统优化领域最经典的课题之一。做这类问题主流思路有三种。第一是随机规划Stochastic Programming核心思路是给风电和负荷的不确定性假设一个概率分布用蒙特卡洛抽样生成大量场景然后求期望成本最小化。好处是模型精细坏处是场景一多计算量爆炸而且概率分布本身很难精准估计。第二是鲁棒优化Robust Optimization核心思路是划定一个不确定集比如风电出力可能在某个区间内波动然后找最恶劣场景下成本最小的方案。好处是不需要概率分布只要区间设得合理方案就一定可行。坏处是过于保守——你把最恶劣的情况全兜住了平时经济性就会打折扣。第三是分布鲁棒优化Distributionally Robust Optimization算是前两者的折中用矩信息或者Wasserstein距离来刻画不确定性既不需要精确分布又不至于保守到离谱。最近几年论文特别多但实现难度也大。这个题目里考虑源荷两侧不确定性我猜大概率是落到了鲁棒优化或者两阶段鲁棒优化这个框架上因为这是目前最成熟、Matlab代码实现也最容易复现的方案。而且低碳调度通常要通过碳交易机制引入用价格信号引导减排这部分在建模里很容易和火电成本放在同一个目标函数里。2. 数学模型与优化方案选型2.1 目标函数拆解成本项一个都不能少无论你最终用哪种不确定性处理方式低碳调度的目标函数基本长这样$$min \sum_{t1}^{T} \left( \sum_{i \in G} C_i(P_{i,t}) C_{start,i} \cdot u_{i,t}^{start} C_{stop,i} \cdot u_{i,t}^{stop} \right) C_{carbon} C_{wind_curtail}$$我拆开跟你讲每一项是什么干什么用。第一项是火电机组的燃料成本通常用二次函数近似$C_i(P_{i,t}) a_i P_{i,t}^2 b_i P_{i,t} c_i$。注意如果你用的是CPLEX、Gurobi这类商业求解器二次项可以直接处理如果你用Yalmip转成线性规划就需要分段线性化。这一步是很多新手代码跑不动的第一个坎——不等式约束非线性求解器报错提示看不懂。第二项是启停成本。机组从停机到并网锅炉要重新烧起来这里面有煤耗、有损耗、有寿命损耗都得换算成钱。启停成本在UC问题里通过二进制变量表达也是为什么这本质是一个混合整数规划问题的原因。第三项是碳成本。一般做法是引入碳配额和碳交易机制给系统一个总排放配额实际排放超过配额就要去碳市场买配额低于配额可以把多余的配额卖掉。这个机制翻译成模型非常直观$C_{carbon} P_{co2} \times (E_{total} - E_{quota})$其中$E_{total} \sum e_i P_{i,t}$$e_i$是机组i的碳排放强度。这一项可正可负你说它有没有引导力就看碳价设了多少。第四项是弃风惩罚成本。风电明明不排碳、边际成本几乎为零但由于系统调峰能力不足或者电网输送受限有时候必须让它少发电。为了在优化里避免这种情况可以给弃风一个很高的惩罚系数相当于告诉优化器宁可多花点钱开火电调峰也别轻易弃风。2.2 源荷双侧不确定集的构造方法现在到了这个题目的灵魂——源荷两侧不确定性怎么建模。很多人代码写不下去其实就是卡在这一步不知道不确定性集怎么设设大了结果太保守设小了又怕实际运行时扛不住。最经典也是最容易实现的是盒式不确定集Box Uncertainty Set结合预算约束。以风电为例假设第t时段风电预测值为$\hat{W}_t$允许波动的幅度为$\Delta W_t$那么$$W_t \in [\hat{W}_t - \Delta W_t,\ \hat{W}_t \Delta W_t]$$就这么一个区间。但如果你直接把这个区间塞进优化模型结果会极度保守因为优化器会假设每个时段的风电都同时取最恶劣值——实际中不太可能。所以引入一个预算参数Budget of Uncertainty$\Gamma$限制在整个调度周期内最多只有$\Gamma$个时段的风电出力偏离预测值。用数学语言表达就是加上约束$\sum_t \delta_t \leq \Gamma$其中$\delta_t$是0-1变量表示第t时段风电是否取到边界值。负荷侧同理。负荷不确定性一般设为$D_t \in [\hat{D}_t - \Delta D_t,\ \hat{D}_t \Delta D_t]$注意负荷取上限对系统最不利需要更多出力支撑所以鲁棒优化里负荷往往向上看。有个细节你要留意源荷两个不确定是耦合的。风电出力小的时候负荷如果还很大那火电就得拼了命顶上去。所以在两阶段鲁棒优化的第二阶段——也就是不确定场景下的再调度阶段——你要把风电和负荷当做一个整体场景来处理不能分开建模。这个我在后面代码部分会再强调。2.3 为什么选择两阶段鲁棒优化说了半天为什么这个题目大概率落在两阶段鲁棒优化框架上原因有三个。第一现实需求明确。你不能等到风电实际出力出来了再去做机组启停——机组的启动和停机需要时间锅炉升温要几个小时。所以决策天然分两阶段第一阶段是今天先定哪些机组开、哪些机组关here-and-now决策用二进制变量第二阶段是等明天看到实际风电和负荷后在线调整机组出力wait-and-see决策用连续变量。第二理论成熟。两阶段鲁棒优化的标准解法——CCGColumn and Column Generation列与约束生成算法在电力系统领域已经被大量论文验证过收敛性有保证而且Matlab Yalmip 求解器的组合足以实现。第三代码复用性好。你把不确定性从风电扩展到源荷双侧只需要改不确定集的定义主问题和子问题的结构几乎不用动。这也是为什么你做一遍风电的代码稍微改改就能讲成源荷两侧的原因。3. CCG算法matlab实现详解3.1 从抽象模型到可运行代码的思路整理两阶段鲁棒优化的数学模型其实是一堆嵌套的min-max-min问题。我先给你一个直观的画面外层min对应第一阶段决策也就是机组启停计划中间max是我假设最坏的不确定性场景内层min是在这个最坏场景下怎么调整输出。手写这些约束很复杂但好消息是实际实现不需要你手写全部的KKT条件或线性对偶公式。用CCG算法核心思路是拆成主问题和子问题交替求解主问题MP已知一组不确定场景求解机组启停和出力方案得到一个下界。子问题SP固定主问题的第一层决策在不确定集中寻找让调整成本最大的场景得到一个上界。迭代把子问题找到的最恶劣场景加回主问题重复直到上下界收敛。我讲一个别人不怎么会细讲的点CCG算法里加回主问题的不是场景概率而是场景本身——每轮迭代往主问题里加一个确定性场景的完整约束组。所以主问题越迭代越大计算耗时也越来越长。收敛速度取决于两界gap设得多宽一般设0.01或者0.001就够用了。3.2 Yalmip环境下主问题与子问题的建模要点在Matlab里做优化建模我强烈建议直接用Yalmip工具箱。你不需要手动判定变量类型是否匹配、矩阵维度是否一致Yalmip会用符号运算自动处理。写代码时核心就是三行事% 主问题核心代码框架 u binvar(n_gen, T); % 机组启停状态 P sdpvar(n_gen, T); % 机组出力 % ...定义目标函数和约束... optimize(MP_Constraints, MP_Objective, options);子问题稍微复杂一点因为涉及最恶劣场景的搜索。你可以写成一个内层优化问题用Yalmip直接求它的对偶形式也可以暴力迭代。跟新手说一句优先用Yalmip内置的dual操作来求对偶不要手算KKT条件手算很容易错前后搞三天也很正常。还有求解器的选择直接影响你能不能跑出结果。电气工程圈子里精度要求高就上Gurobi、CPLEX但这两个是商业软件学校没买License的话改用SCIP或者GLPK也能凑合。实测下来小规模算例10台机组以内、24时段用SCIP速度完全够用几十秒就能出结果。3.3 风电和负荷场景的Matlab数据处理技巧我一直觉得这类项目里最费时间的不是建模而是数据处理。你要做源荷不确定性的鲁棒优化首先得有风电出力数据和负荷预测数据而且这些数据必须按时间序列对齐。给你一个我常用的做法。风电出力通常用历史风速数据反推功率曲线用标准风机P-v曲线% 风速转风电功率示例 P_wind zeros(T, 1); for t 1:T v wind_speed(t); if v v_cut_in || v v_cut_out P_wind(t) 0; elseif v v_rated P_wind(t) P_rated * (v - v_cut_in) / (v_rated - v_cut_in); else P_wind(t) P_rated; end end注意这只是一个最基础的线性化功率曲线实际风机功率曲线是分段非线性的但在调度尺度下用这个精度足够了。负荷数据就好办多了直接用典型日负荷曲线乘以一个预测误差系数来构造不确定区间。比如负荷预测值$\hat{D}_t$允许波动$\delta_t$个百分点区间就是$[\hat{D}_t(1-\delta_t), \hat{D}_t(1\delta_t)]$。新手经常栽的坑是风电和负荷数据的量纲不一致。风电装机可能是几百MW负荷峰值可能是几GW如果你直接把两个数放一起优化数值尺度差太大求解器迭代容易出问题。我一般会把所有数据归一化到标幺值pu体系下再写进模型这个习惯强烈推荐。4. 算例测试与参数灵敏度分析4.1 标准测试系统与基础参数设定做这类项目首选算例是IEEE 30节点系统或者IEEE 118节点系统节点参数在MATPOWER工具包里直接能拿到。但要注意MATPOWER默认是交流潮流数据而低碳调度一般用直流潮流DC Power Flow简化因为你要跑混合整数规划交流潮流非线性没法直接放进MIP求解器。我自己常用的是一个6机24节点的系统规模适中肉眼能跟踪每台机组的启停状态变化。机组参数包括最大/最小出力、爬坡速率、燃料成本系数、启动成本、碳排放强度。碳交易参数方面我设碳配额总量为不减排情景下排放的80%碳价从20元/吨试到100元/吨看它对结果的影响。不确定性参数方面风电接入节点设两个风电场预测出力按某天实际风速曲线换算允许波动幅度取预测值的15%~20%。负荷预测误差取5%。预算不确定参数$\Gamma$从0取到24看不同值情况下成本怎么变。4.2 碳价和预算参数变化对调度结果的影响趋势我把碳价分别设成20、50、80、100元/吨跑了几组对比发现三个非常有意思的规律。第一碳价升高时碳捕集机组如果模型里有出力占比显著上升。这个逻辑很直观——碳价高于捕集成本时捕碳比买配额划算系统自然会倾向于多启用这类机组。但注意如果模型里没引入碳捕集机组低碳调度就只剩一个手段让高效率低排放的机组多发电、高排放机组少发电甚至停机减排空间有限。第二预算参数$\Gamma$从0增大到12的过程中总运行成本几乎线性上升但碳排放在一开始下降很快之后趋平。这个现象解释是不确定性的存在迫使系统预留备用容量机组组合变得更不灵活高排碳的机组因为启动慢反而可能在前半段被顶上去。换句话说过度追求鲁棒性在一定程度上会牺牲低碳性这是模型内在的张力不是bug。第三负荷侧不确定性和风电侧不确定性叠加后系统的总备用需求大于两者单独考虑之和。你可以理解为风电低谷期负荷高峰期两个不确定性同时往不利方向偏移这个时候最考验系统的爬坡能力。这个耦合特性也解释了为什么源荷双侧比单纯源侧或荷侧更有研究价值。4.3 一个具体算例的结果快照我手上有个跑出来的典型结果直接贴出来给你做个参照。6机系统24时段碳价50元/吨$\Gamma8$风电装机渗透率20%总运行成本约68万元含碳交易成本约4.5万元风电利用率91.3%弃风率8.7%碳排放总量2150吨相比不引入低碳约束的2530吨降低了15%机组启停次数全天共启机5次、停机4次其中一台老旧高排机组全天停机这几个数字你跑的时候可能会略有出入但趋势应该一致。如果弃风率超过15%八成是风电功率数据处理出了偏差或者不确定性区间设得过于保守。这是我跑了十几遍以后总结出来的经验阈值。5. Matlab实现中的关键代码片段与避坑经验5.1 CCG算法主循环的标准写法直接上核心代码注释写详细一点你照着抄基本能跑% CCG主循环核心代码 LB -inf; UB inf; K 0; % 迭代计数 U []; % 存储生成的恶劣场景 while (UB - LB) / UB 0.005 K 10 K K 1; % 第一步求解主问题MILP [P, u, theta, MP_obj] solve_MP(U); LB MP_obj; % 第二步固定第一层决策求解子问题查找恶劣场景 [worst_scenario, sp_obj, feasible] solve_SP(P, u); UB min(UB, sp_obj); if feasible U [U; worst_scenario]; % 将恶劣场景添加进主问题 else % 引入可行性割重新求解MP % 这里我给一个兜底逻辑实际代码要展开 U [U; worst_scenario]; % 继续迭代 end end这个循环在Matlab里直接写就行不用搞什么复杂工程框架。唯一要注意的是每次迭代主问题规模都在增大建议把优化选项里的mipgap设置到1e-3否则后面几轮迭代求解时间会飙升。5.2 子问题里的对偶转换技巧子问题求解是整个算法里最容易出错的地方。我先给你一个直观的理解子问题的输入是机组启停状态第一层决策输出是最恶劣场景和对应的最小调整成本。从结构上看它是一个max-min问题——外层的max在搜索场景内层的min在调整出力。直接求解max-min问题很麻烦常规做法是取内层min的对偶让问题变成一个max-max问题也就是一个单层max问题。Yalmip里你可以这样操作function [worst_scenario, obj] solve_SP(u_fixed) % 定义场景变量 w_p, d_p % 定义调整变量 delta_P % 约束都是线性的直接求对偶 constraints [...]; objective ...; options sdpsettings(solver, gurobi, verbose, 0); optimize(constraints, -objective, options); % 注意这里求的是max obj value(objective); worst_scenario.wind value(w_p); worst_scenario.load value(d_p); end我见过很多同学在这里卡两三天原因是对偶后的约束符号搞反了、或者拉格朗日乘子维度对不上。我的建议是先用纸笔把对偶问题推导清楚再写代码。不要直接抄网上的模板——网上模板的场景定义、变量顺序和你自己的模型往往不一致套进去全是坑。5.3 风电接入节点和线路潮流的处理如果你直接用直流潮流约束需要注意风电接入节点和负荷节点的潮流平衡。直流潮流的表达式是$$P_{line} B \times \theta$$其中$B$是节点导纳矩阵的虚部$\theta$是节点相角。在Yalmip里把$\theta$定义为连续变量然后约束线路潮流在容量范围内。这里有一个关键操作风电场接入节点后节点注入功率不再是单纯的正数——风电出力大时可能造成倒送潮流所以线路容量约束要关注双向正向和反向潮流极限。很多论文只写了正常运行场景约束忽略了不确定性场景下潮流方向可能反转导致算出来的风电场渗透率高于实际可接纳水平。5.4 Matlab版本和工具箱兼容性问题说个跟模型无关但很实际的问题。Matlab在做这类优化项目时版本兼容性真的会影响解题效率。我最早用的Matlab R2019b Yalmip GLPK小算例能跑一旦机组数量和不确定性场景一多直接跑不动。后来换了Gurobi的License才真正解脱。另外提一句Yalmip对Matlab版本是有兼容期问题的。R2023b之后的版本老版本Yalmip可能会在sdpvar定义时报错建议先把Yalmip升级到最新版再回头排查。你要是遇到莫名其妙报错第一件事先查版本这个问题大概率能解决一半。还有个老生常谈的问题关于Matlab中文注释乱码。某些版本下Matlab默认字符编码是GBK你用中文写注释切到Linux系统或者新版Matlab打开就乱码。我的习惯是在文件开头加一行% coding:utf-8或者干脆代码注释全用英文。别小看这个细节等代码传到别人手上跑不起来你就知道痛了。6. 常见问题排查与效果优化建议6.1 迭代不收敛的排查思路整理我可以很负责任地说一句话两阶段鲁棒优化代码写出来后第一次跑就完美收敛的概率基本为零。最常见的三种收敛问题每一种我都遇到过。第一种子问题不可行。含义是无论不确定性场景怎么变化固定第一阶段的机组启停后系统无法满足负荷需求。这个问题的根源通常是第一阶段没有预留足够的备用容量或者机组组合方案本身不够灵活。解决方向是给主问题增加旋转备用约束$$\sum_{i \in G} u_{i,t} \cdot P_{i}^{max} \geq \hat{D}_t \times (1 r) \hat{W}_t$$其中的$r$是备用率常规取5%~10%。第二种上下界振荡不收敛。这个往往是子问题的对偶gap过大导致建议缩小求解器容差或者改用更稳定的求解器。我还遇到过一次情况是Yalmip建模时把optimize的第三个参数options漏了导致求解器用默认参数精度不足上下界差一直缩不小。第三种风电出力过大的极端场景下系统没办法消纳全部风电子问题强制弃风才能可行。这种时候与其加惩罚项硬解不如直接在模型里允许弃风给弃风变量一个适度的成本系数。我前面提到过的8.7%弃风率就是这么设计出来的。6.2 求解效率提升的几个实测有效的小改动除了换求解器这种釜底抽薪的手段实践中有三个小改动在提速上非常有效。第一给启动和停机决策变量加时间耦合约束时别一股脑全写进主问题循环。把每个机组的状态变化约束用implies函数写成逻辑表达式这样Yalmip生成的约束数量会更精简求解器预处理阶段也能砍掉不少冗余行。第二子问题每次迭代时用上一次迭代的最优解作为热启动点warm start。Matlab代码里就是给solve_SP传入上轮的解向量然后在sdpvar初始化时赋初值。别小看这个实测能减少30%~40%的求解时间。第三最容易被忽视的一点把不活跃的线路约束注释掉。有些线路容量足够宽、基本不可能越限与其让求解器每次迭代都花时间检查这些约束不如直接省略。当然做严谨研究时不能随便砍约束但做算例验证阶段完全可行。等到最终汇报时再把完整约束加回去就行。6.3 从代码到论文/报告的成果整理建议这个项目的成果不仅是能跑的代码更是要有故事的。我做过几个类似的课题深知在报告里呈现结果的套路这里一并分享了。首先敏感性分析是必须要做的。你光给出一个最优调度方案没太大说服力但是把碳价从低到高扫一遍、把预算不确定参数从0到24扫一遍然后把成本、排放量、风电利用率画成三条曲线论文核心图就出来了。用Matlab的plot或者heatmap画不确定性参数和调度成本的关系图时建议加GridMinor属性显示细网格观赏性会好很多。其次对比实验要有区分度。至少要跑三个对照实验确定性模型不考虑不确定性、只考虑风电不确定性、源荷双侧不确定性都考虑。三者的成本差和鲁棒性差异正是你文章的核心贡献。很多评审老师一眼就看这三个结果。最后代码文件组织建议用模块化结构主脚本main.m、数据生成data_generate.m、主问题构建build_MP.m、子问题构建build_SP.m、结果可视化plot_results.m。这样你改参数、做测试、写报告都会省心很多。7. 最后几点实操心得这个项目做下来最深的体会是理论推导和代码实现之间有一道不小的鸿沟两者缺一不可。光看论文觉得自己什么都懂了一动手全是问题光调代码不懂理论出了问题连错在哪都不知道。我的建议是先花时间把两阶段鲁棒优化和CCG算法的推导在纸面上过一遍再动手写代码。另外做这类优化课题一定要学会跟求解器打交道。很多人一看到优化失败或者infeasible就慌其实你只要在优化调用前把约束数量、变量数量打印一遍大概就能判断出是哪一类问题。Yalmip有个yalmiptest函数可以诊断模型可解性我遇到bug时第一件事就是跑它来缩小排查范围。再分享一个小技巧不确定性预算参数$\Gamma$别设得太满。我在实验中发现$\Gamma24$每个时段都允许取恶劣值和$\Gamma12$一半时段取恶劣值相比系统成本上升了将近20%但风电利用率只提升了2个百分点。也就是说过度保守的鲁棒解在碳排放和经济效益上双输。实际工程取$\Gamma$为调度周期长度的1/3左右是比较合理的折中。最后补充一个关于代码注释和文档的习惯建议。Matlab代码跑通之后回头写注释、整理README看起来是在浪费时间但等你半个月后回头改模型参数时就会知道这些文档有多救命了。尤其是各变量名的物理含义、每个约束对应论文里的哪个公式一定要写清楚。反正这篇代码以后大概率还要用在毕设或者小论文里提前整理好只会赚不会亏。
返回列表