尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微网虚拟电厂日前调度:碳交易与多种需求响应Matlab建模实现

微网虚拟电厂日前调度:碳交易与多种需求响应Matlab建模实现 先交代一句这个项目是我最近整理微网/虚拟电厂这块代码时重点打磨的一个场景。标题里的几个关键词其实已经把核心讲透了——微网/虚拟电厂日前优化调度碳排放交易多种需求响应Matlab实现。现在做这类问题的人不少但我发现很多人的模型要么只做了碳交易没做需求响应要么把需求响应简单当成一个固定比例削峰一深入就露馅。这篇文章把我实际建模和写代码过程中整理出来的思路完整铺开包括模型怎么拆、目标函数怎么列、约束怎么加、碳成本怎么处理、需求响应怎么分类、代码调试容易栽在哪里都会讲到。适合正在做相关课题的研究生、写论文需要算例的以及想从传统经济调度往低碳调度方向转的工程师参考。1. 为什么要做“碳交易需求响应”的日前优化调度1.1 微网调度的本质提前给未来24小时排一张可靠的运行时刻表微网/虚拟电厂的日前优化调度说白了就是在前一天根据负荷预测、新能源出力预测、分时电价这些信息把未来24小时里每一台分布式电源的出力和启停、储能的充放电功率、和上级电网的买卖电功率全部定下来。目标是让整个系统在满足用户用电需求的前提下运行成本最低或者综合效益最高。这套逻辑听起来不复杂但真正落地的难点在于约束条件非常多。燃气轮机有出力上下限和爬坡速率限制储能不能同时充放电而且SOC要保持在合理区间买电和卖电不能同时发生功率平衡每时每刻都得成立。更麻烦的是这些约束不是独立存在的它们通过功率平衡方程耦合在一起牵一发而动全身。可以把它理解成排班问题你有值班人员、设备、库存需要在各种限制条件下排出一天最优的班表。运行调度只是把“人员”换成了“机组和储能”把“库存”换成了“SOC状态”本质是一个带约束的优化问题。1.2 碳交易加入后问题从“省钱”变成了“省碳钱”传统经济调度只关心购电成本、燃料成本这些直接费用。但碳排放交易机制引入后系统中每一吨二氧化碳排放都有了价格。如果微网内的实际碳排放量超过免费配额就需要去碳市场购买配额这部分费用直接进入运行成本如果排放量低于配额多余的配额反而可以出售变成收益。这就改变了机组的调度优先级。比如一台燃气轮机的发电成本可能比外网购电便宜但如果它的碳排放因子较高加上碳成本之后综合成本可能反而更贵。于是调度结果就会从“多用气电”变成“多买网电”或者让储能更积极地在高排放时段放电。所以在代码里不能只加一个简单的“碳排总量约束”就完事那样只是一个硬限制体现不了“交易”的含义。碳交易的特点在于配额是可以买卖的它通过价格信号引导调度决策而不是一刀切。1.3 需求响应让负荷从“固定值”变成了“可调节资源”传统调度里负荷曲线是给定不变的输入调度只能被动适应。但实际用户是有调节潜力的——高峰时段让一部分负荷削减或者转移到低谷时段不仅能缓解供电压力还能降低系统整体运行成本。需求响应不是一种单一资源。电价敏感的用户会主动调整用电行为这是价格型需求响应签订协议的用户按照调度指令削减或转移负荷并获得补偿这是激励型需求响应。这两类响应的时间尺度和对象完全不同在建模时必须分开处理一股脑塞进一个公式里很容易出错。把需求响应资源和碳交易一起纳入日前调度系统就同时具备了“电源侧调节”“储能侧搬运”“负荷侧配合”三种灵活性这样得到的调度方案才更接近实际运行场景。2. 模型怎么搭目标函数、约束和决策变量2.1 先用一个清晰的优化问题框架把任务定下来不管代码写得多么花哨底层一定是一个标准优化模型决策变量需要求出来的量比如各时段燃气轮机出力、储能充放电功率、购售电功率、负荷削减量、可平移负荷的启动状态。目标函数需要最小化或最大化的量这里就是系统总运行成本。约束条件决策变量必须满足的物理限制和运行限制。我用的是单目标最小化把碳交易成本和需求响应补偿成本全部折算成经济量放进目标函数。这样做的好处是模型直观求解稳定而且方便做碳价、补偿价格的灵敏度分析。如果你想做成多目标比如“成本最小碳排放最小”也可以但要注意两个目标的量纲和权重选取处理起来会复杂不少。2.2 目标函数里有哪些成本项它们分别代表什么我这个模型的目标函数主要包括六块与上级电网交互成本向电网买电的费用减去向电网售电的收益分时电价下这部分是调度决策的重要驱动力。燃气轮机的燃料成本和启停成本燃料成本通常用出力的二次函数或分段线性函数表示启停成本在日内多次启停时不可忽略。储能运行维护成本充放电会造成电池循环损耗一般按充放电功率乘以一个较小的单位成本系数来计。碳排放交易成本实际碳排放量与免费配额之差对应的费用阶梯碳价下是一组分段线性成本。需求响应补偿成本可削减、可转移、可平移负荷参与调度后需要支付给用户的补偿费用。弃风弃光惩罚成本新能源出力没有被完全消纳时的惩罚项用来避免模型为了省钱而随意丢弃清洁能源。这六项加起来就是一个完整的日前运行成本。写代码的时候我习惯把每一项单独算出来存成变量最后在目标函数里直接相加这样做完优化后可以单独查看每一项的成本构成对分析调度结果非常有帮助。2.3 约束条件功率平衡、机组、储能、碳排放、需求响应一个都不能少约束条件可以根据对象分成几组功率平衡约束是核心它要求任何时刻系统内的发电加上购电等于负荷加上售电和充电。这里要注意负荷侧用的是“参与需求响应之后的实际负荷”所以在约束里要把削减量减掉、把转移负荷加上细节很容易漏。机组约束包括出力上下限、爬坡速率约束、最小启停时间约束。储能约束包括SOC递推关系、充放电上下限、同一时段不能同时充放电的互补约束。购售电约束包括联络线功率上限和买卖互斥不能让模型在同一时段又买又卖来套利。碳排放约束在这类问题里往往不是硬约束而是通过碳交易成本在目标函数中体现。但如果配额分配方式或者阶梯碳价设置得很宽松模型几乎感受不到碳压力这时候就需要单独检查碳排放总量再决定要不要加约束。3. 碳排放交易机制怎么落到目标函数里3.1 从配额分配到实际排放先把流程走通要在代码里实现碳交易成本首先要搞清楚三个量免费配额是多少、实际排放是多少、二者差值对应多少成本。微网碳排放源有两个一是本地燃气轮机发电产生的直接排放二是从上级电网购电间接产生的排放因为网电在上游火电厂发电时同样会排放二氧化碳。这一点很多人会漏掉漏掉的后果是模型会倾向于无限外购电来逃避本地排放导致调度方案失真。免费配额的处理我通常采用按负荷占比或装机容量的简化方式配额总量等于某一系数乘以预测总负荷再分配到24小时。这样处理不是为了精确模拟碳市场规则而是为了让模型抓住碳交易的本质配额以内不花钱超额要买少排可以卖。3.2 阶梯碳价的线性化处理为什么不能直接写一个分段判断实际碳交易机制中碳价往往不是固定的。一种常见的方式是阶梯碳价即碳排放量超出配额的部分按照超额量落在不同区间采用递增的单价。这样做比单一碳价更能反映减排压力递增的效果。初写代码的人很容易想当然在目标函数里用if判断超额量落在哪个区间然后直接乘对应单价。这种思路在C语言或者普通脚本里没问题但在优化模型里是行不通的——基于变量的if判断会引入非光滑特性求解器根本没法处理。正确的做法是把碳排放量拆成几个顺序区间用额外的连续变量分别表示落在每个阶梯内的排放量再用递增碳价对应的成本系数乘上去。核心逻辑是既然碳价逐级递增最小化成本的优化过程会自动优先填满低价区间再往高价区间走所以不需要引入额外的0-1变量来强制排序。为了直观我给出一个三段阶梯碳价的成本计算示意% E_total: 总排放量, E_quota: 免费配额 % 超出配额部分按三个阶梯计费 E_excess E_total - E_quota; % 拆分成三段 E_level1 sdpvar(1); % 第一段: limit1 E_level2 sdpvar(1); % 第二段: limit1 ~ limit2 E_level3 sdpvar(1); % 第三段: limit2 ~ 超过部分 Constraints [Constraints, E_excess E_level1 E_level2 E_level3]; Constraints [Constraints, 0 E_level1 limit1]; Constraints [Constraints, 0 E_level2 limit2 - limit1]; Constraints [Constraints, 0 E_level3]; % 阶梯碳价递增, 优化会自动先填满低价区间 C_carbon lambda1 * E_level1 lambda2 * E_level2 lambda3 * E_level3;如果你只想做一个简单版本用单一碳价直接乘以超出配额部分也可以跑通。但阶梯碳价的结论会更丰富特别是做碳价灵敏度分析时能看出不同碳价水平下调度策略的拐点。3.3 碳成本进目标函数后的连锁反应碳成本进入目标函数之后会发生一个很有意思的连锁反应当碳价较低时燃气轮机照常出力因为碳成本占比太小随着碳价升高燃气轮机的综合发电成本逐渐超过外购电和储能放电的成本调度策略开始转变碳价极高时系统甚至会牺牲部分经济性来压低碳排放总量。这在实际碳市场中对应的含义是碳价只有高到一定程度才会真正改变电源结构的运行顺序。我在代码里预留了碳价扫描接口可以一次性跑一组不同碳价下的调度结果画出一条“碳价-总碳排放量”曲线。这条曲线通常存在一个明显的拐点对分析碳交易机制的有效性非常有说服力。另外要提醒一句阶梯碳价写进目标函数后模型性质仍然是线性的没有二次项也没有整数变量的情况下用LP求解器即可一旦需求响应部分加入可平移负荷的0-1变量模型就变成了MILP需要切换到支持整数规划的求解器。4. 多种需求响应怎么建模从经济概念到代码约束4.1 价格型需求响应用弹性矩阵描述用户对电价的反应价格型需求响应不直接出现在调度约束里它改变的是负荷基线。基本原理是电价变化会引导用户改变用电行为电价高的时候少用电价低的时候多用。这种响应程度用需求价格弹性系数来描述。一个常用的数学模型是用弹性矩阵把各时段负荷变化率和电价变化率联系起来。矩阵对角线元素是自弹性系数通常为负值表示本时段电价上升导致本时段负荷下降非对角线元素是交叉弹性系数通常为正值表示某时段电价上升导致相邻时段负荷上升因为用户把部分用电转移到了其他时段。代码实现时可以先算出一个“响应后的负荷曲线”再把它作为调度模型的输入。这一步发生在优化之前所以价格型需求响应本质上是改变了调度面对的需求背景。需要注意的是弹性矩阵系数的大小直接决定了负荷调整幅度取值太大会出现负荷曲线严重变形的现象取值太小则响应效果微弱一般需要根据实际用户聚合数据调试。4.2 激励型需求响应可削减、可转移、可平移要分开处理激励型需求响应是真正作为决策变量进模型的也是代码里最容易写乱的部分。我把它们分成三类可削减负荷是最简单的。用户允许调度方在特定时段削减一部分负荷代价是按削减电量支付补偿。约束上只需要限制每个时段削减量不超过该时段可削减容量的上限目标函数里加上削减补偿成本即可。可转移负荷的复杂程度高一些。它要求转移前后总用电量保持不变也就是说高峰时段减少的用电量必须转移到其他时段去不能让总用电量凭空消失。建模时需要引入转移变量并保证一天内转移负荷的总电量为零目标函数里一般只计转移行为产生的操作成本或补偿成本。可平移负荷是最麻烦的一类。它代表的是洗衣机、烘干机这类一旦启动就必须连续运行若干小时的负荷不能拆散转移只能把整条负荷块从一个时段搬到另一个时段。这类负荷必须用0-1整数变量来表示平移后的启动时刻同时还要保证一个块在24小时内只能被启动一次。三类负荷的组合让模型成为一个典型的混合整数线性规划问题。变量数量不算吓人但逻辑关系必须梳理清楚每个时段参与平衡的实际负荷是多少要对照着公式一项项核对。4.3 需求响应成本给不到位模型会“疯狂削负荷”需求响应建模中最典型的错误是只加了削减负荷的约束却没在目标函数里给足补偿成本。这样做的后果是对优化求解器来说削减负荷相当于减少用电需求又不要额外付钱它会毫不犹豫地把所有可削减负荷全部砍掉得到一堆毫无实际意义的“削峰”结果。正确做法是给每一类需求响应设置合理的单位补偿价格。可削减负荷的补偿价格一般参考售电电价的一定比例可转移负荷的补偿要考虑用户调整用电习惯的不便程度可平移负荷因为涉及人工操作或者设备预约补偿标准通常更高。还有一个容易忽略的点可转移负荷转移后不能造成新的峰谷倒置。有些模型跑出来低谷时段原本闲置的变压器在深夜被大量转移负荷塞满这是不合理的。需要给每个时段的转移后负荷设一个上限或者限制相邻时段的负荷变化率避免模型为了省几个小钱制造出新的运行风险。5. Matlab代码实现从数据到结果的完整流程5.1 代码模块怎么划分才方便调试我写这类代码有一个原则不要把所有逻辑塞进一个几百行的脚本里。哪怕只是学术验证我也建议按模块组织。实际项目里我的文件结构大致是这样数据输入模块定义24时段的基础数据包括负荷预测、新能源出力、分时电价、燃气轮机参数、储能参数、碳配额参数、需求响应容量及价格。变量定义模块用sdpvar和binvar定义所有决策变量并为每个变量写好注释说明它代表什么。约束生成模块按照上文说的几组约束逐条添加每加一组就加一行注释。目标函数模块把各项成本拆开计算方便后续单独分析。求解模块设置求解器参数并调用optimize。结果后处理模块提取变量值计算核心指标画图。看起来模块多了几步但调试的时候优势非常明显。出了问题你可以直接定位是哪个约束组导致的不可行而不需要从头到尾翻找。5.2 用Yalmip建模时的核心变量写法和约束写法Yalmip是我比较推荐的Matlab建模工具箱它把变量定义、约束添加、目标函数书写和求解器调用都统一了代码可读性高。核心变量定义大致是这个风格%% 决策变量 % 燃气轮机出力 P_mt sdpvar(1, 24, full); % 储能充放电功率正值充电负值放电可拆成两个变量 P_ch sdpvar(1, 24, full); P_dis sdpvar(1, 24, full); % 储能SOC SOC sdpvar(1, 25, full); % 购售电功率 P_buy sdpvar(1, 24, full); P_sell sdpvar(1, 24, full); % 可削减负荷量 P_curt sdpvar(1, 24, full); % 可平移负荷启动状态0-1变量 u_shift binvar(1, 24, full);功率平衡约束是最容易写错的一行。我一般这样写所有电源出力加上购电和储能放电等于固定负荷加上充电和售电同时还要扣除削减负荷、加上转移过来的负荷。这个等式里的每一项都必须严格对应上数据调试的时候可以先跑一个不包含需求响应的版本让等式左右两边平衡后再加入需求响应项。5.3 求解器配置和结果后处理的几个实用技巧求解器方面我优先选Gurobi或CPLEX这两个对MILP的支持非常成熟。学术许可证可以免费申请对大多数论文算例来说完全够用。配置代码一般是ops sdpsettings(solver, gurobi, verbose, 2, debug, 1); ops.gurobi.MIPGap 0.01; % 设置1%的MIP间隙 optimize(Constraints, Objective, ops);MIPGap的设置很关键。如果不设求解器为了证明最优性可能会花很长时间设成0.01表示允许1%的差距对日前调度这种工程问题来说精度完全足够求解速度可以快很多。结果后处理我习惯输出几组核心数据各时段电源出力和购售电组成的功率平衡表、储能SOC曲线、需求响应前后负荷曲线对比、碳交易成本及各成本项占比。画图时用Matlab自带绘图功能就够重点是把功率平衡堆叠图、SOC曲线图、需求响应效果对比图这三张画清楚论文里基本能直接用。6. 调试实录求解失败和结果不合理的几个典型案例6.1 求解器报infeasible时先怀疑数据不一致很多初写者一看到infeasible就懵了其实这个报错大部分不是代码逻辑错而是数据本身不一致。我遇到最典型的情况是负荷数据已经用了需求响应后的曲线另一边又把削减负荷、转移负荷变量加进了功率平衡约束等于同一份需求被扣减了两次导致等式永远无法满足。另一种常见数据冲突是储能SOC初值和末值设置不合理。如果要求一天结束时SOC回到初始值但初始SOC设置过低、储能容量又不够在夜间完成充电模型同样会不可行。调试时的建议很简单把约束分成几组每次只激活一组跑一遍很快就能定位到底是哪一组约束把可行域锁死了。6.2 Unbounded objective多数是漏了成本项或上限目标函数无界这个报错在碳交易模型里尤其容易踩。原因往往是碳配额免费量给得偏大配方出售部分又没有任何约束模型发现“多发点电然后卖配额”比正常卖电还赚钱就会无限提高出力目标函数一路向负无穷跑。解决办法有两个方向一是给燃气轮机出力设好上限这本来就是物理约束一般不会漏二是给碳配额出售量设一个上限模拟碳市场对交易量的实际限制。我自己一般两个都加上既符合物理实际又能让模型稳定收敛。从数学上看配方出售收益是目标函数中的负项如果负项的系数绝对值大于发电收益项的系数优化就会趋向无穷解。及时检查这一点可以省下大量排查时间。6.3 负荷被切碎和储能频繁启停的“伪最优”问题如果模型结果出现可削减负荷在不同时段以极其零碎的小数值出现这虽然不违反约束实际中却根本没法执行。因为没有用户愿意配合一个只削减0.01kW的调度指令这种操作指令没有实用性。解决方法是给可削减负荷加最小削减量和最小连续削减时长约束让每一条削减指令达到用户能响应的门槛。储能端也有类似问题如果不加最小充放电时间约束求解器会给出频繁切换充放状态的锯齿形曲线虽然数学上最优工程上却会急剧缩短电池寿命。我建议在写优化问题时就加入“储能不能连续在两个时段充电/放电切换过多次”这种限制或者事后对锯齿曲线做一次平滑处理。从我的经验来看在模型层面加约束比在结果后处理时修数据更可靠因为后处理强行平滑会导致功率不平衡。6.4 别忘了单位和步长功率和能量是最容易混的两件事这是最基础但最容易犯的错误。如果调度时段间隔是1小时功率MW乘以1小时就是能量MWh数值上正好相等写起来确实方便。但一旦把时间尺度改成15分钟就必须在能量递推公式里乘0.25否则SOC轨迹会完全失真。我的习惯是所有和储能能量有关的约束统一用MWh作为单位功率约束统一用MW在SOC递推和功率平衡两个关键位置反复确认单位是否一致。这个错误往往不会直接报错而是表现为SOC曲线漂移、功率不平衡等看似“随机”的异常排查起来比infeasible还费时间。7. 这套模型还可以怎么扩展这个模型的框架其实是开放性的做好基础版本后可以往多个方向扩展。如果从确定性走向不确定性可以把风光预测出力从固定序列改为多场景把日前调度扩展成两阶段随机优化或分布鲁棒优化研究风光不确定性和碳价的联合影响。这个方向是目前论文里比较常见的前沿切入。如果研究重心在需求响应侧可以把弹性系数从固定常数改成随时间变化的动态参数或者把激励型需求响应升级为考虑用户满意度损失的非线性效用函数。还有一个很实用的方向是参数灵敏度分析。代码本来就能跑通直接把碳价、需求响应补偿价格、配额分配系数设置成循环变量跑批看结果如何变化可以高效得到不同机制参数对系统成本和碳排放的影响规律。我个人在实际调试中的体会是这类模型真正难的不是目标函数怎么写而是每个决策变量之间的时序耦合关系。比如可平移负荷的持续状态需要往前回溯好几小时储能SOC需要一直从初始时刻向后推碳成本要分段拆解这些都需要非常明确地从时段维度去理解。遇到不合理结果时先在草稿纸上把某一时刻的功率平衡手工算一遍大多数情况下都比盯着代码更先找到问题。最后再分享一个小习惯每次跑完优化我都会顺手把各成本项单独存出来看一眼确认各项占比在合理范围里。如果碳交易成本突然变成绝对值巨大的负数或者需求响应补偿成本高到离谱那基本不是求解器的问题而是前面哪一项收益或成本被重复计算了。这个习惯帮我拦下了不少让结果看起来“聪明到失真”的隐藏bug。
返回列表