ARTICLE DETAIL

资讯详情

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

风光储联合调度建模与滚动优化:从约束条件到Matlab实现

风光储联合调度建模与滚动优化:从约束条件到Matlab实现 1. 为什么要把风电、光伏、储能放在一个盘子里调度做新能源调度的人最头疼的往往不是某个电源本身的建模而是几个电源凑到一起之后整个系统的脾气变得很难捉摸。风电看天吃饭光伏也看天吃饭可它们吃饭的时间经常错开白天光伏出力大晚上风电往往更猛。如果只盯着单一电源做优化很容易出现白天光伏过剩、深夜风电过剩而早晚高峰却什么都不能补的局面。把风光储放到同一个优化框架里本质上是让三种性格不同的设备形成一个互助小组。储能在这里的角色不是简单的后备电源而是整个调度的缓冲垫——光伏多了就充电风电少了就放电尽量让每一度电都踩在负荷曲线的刀刃上。而废弃矿井小型抽水蓄能则是这套方案里比较特殊的一张牌矿区改造出来的上、下库天然存在落差不需要新建大型坝体环境成本低对地形要求也没有传统抽蓄那么苛刻。相比电化学电池它胜在循环寿命长、度电成本低单次充放电的边际损耗几乎可以忽略。我在实际项目里见过很多初学者一上来就钻进最优两个字恨不得让Matlab算出每15分钟该充多少、该放多少的完美序列。但真正落地的时候约束条件比目标函数重要得多——机组爬坡限制、线路输送极限、抽水蓄能机组的工况切换时间这些硬约束不满足算出来的最优解就是废纸一张。所以本文讨论的调度模型我会把等式约束、不等式约束放在和收益函数同等重要的位置去讲因为这才是实际工程里真正卡脖子的地方。这篇内容适合谁看一个是正在做新能源消纳、微电网能量管理相关课题的研究生另一个是刚接触风光储联合调度、手里有一套Matlab代码却不知道怎么改进的工程师。我会按照目标函数 → 运行约束 → 日内滚动修正 → 代码实现细节的顺序展开最后补一些我在调试过程中踩过的坑。如果你现在只想确认一件事那就是储能并不是越大越好关键在于它能不能出现在它该出现的时间窗口里。2. 风光储互补调度的本质时间和容量错位2.1 风电、光伏出力曲线的时间错位特征先说风电。风电出力在多数地区呈现夜间大、白天小的特征尤其是春夏季节白天地面受热后大气趋于稳定风速反而下降到了夜间低空急流发展风速抬升出力明显增大。光伏恰好相反出力曲线从日出开始爬升正午前后达到峰值之后就一路下滑夜间出力为零。两条曲线叠加之后会给系统造成两个麻烦。第一个麻烦是午间净负荷凹陷。光伏大发的时候如果系统里还有必须带基荷的火电那留给光伏的消纳空间就会被挤占多余的电只能丢弃或者储能吸纳。第二个麻烦是晚高峰爬坡。下午四五点以后光伏开始衰减但负荷往往正处于一天中第二甚至第一个峰值段这时候风电如果还没起来就需要储能顶上。风电和光伏的时间错位决定了储能必须能够覆盖清晨爬坡和傍晚爬坡两个关键过渡段这就是我要在优化模型里引入时序约束的原因不能只看总量平衡。为了更直观地表现这种错位我把典型的24小时曲线分段列出时段光伏出力特征风电出力特征系统状态00:00–05:00无出力较高低负荷风电可能过剩06:00–09:00开始爬升下降早高峰来临需要储能10:00–14:00峰值区间低位午间净负荷凹陷15:00–18:00快速衰减缓慢增长晚高峰爬坡最紧张19:00–23:00无出力逐渐走高负荷仍高储能配合放能2.2 电池储能与抽水蓄能的时间尺度差异电池储能的好处是响应快毫秒级到分钟级都能跟上适合平滑日内每分钟、每五分钟的波动缺点是自放电率比抽蓄高而且循环深度直接影响寿命。磷酸铁锂电池如果每天满充满放一次循环寿命大约在3000到5000次按五年生命周期算每天可以用一次到两次如果把调度周期压缩到15分钟一个点每次都是深度充放电池衰减会非常明显。抽水蓄能则完全相反。它的响应速度以分钟计但胜在皮实充放电循环寿命基本上不用考虑极端工况下也不会出现容量衰减。小型废弃矿井抽水蓄能的装机规模通常在几十到一两百兆瓦对新能源装机几百兆瓦的园区来说刚好能做主力储能而电池储能更适合作为高频缓冲处理那些持续十几分钟到一两个小时的短时不平衡。所以一个合理的设计是抽蓄承担能量搬移的主任务电池承担功率波动平抑的辅助任务。联合调度模型里并不需要物理建模到水泵水轮机的每个工况点但至少要区分两类储能的上下界、功率爬坡速率和日循环限制不然程序跑出来的结果在工程上没法解释。2.3 模型预测控制思想在调度里的映射风电和光伏都是不可精确预测的电源。提前一天做的调度计划到实际运行那天往往偏差超过20%。因此完全依赖一次求解、全天执行的开环调度放在工程里风险很大。我推荐的方式是滚动调度把一天分成96个15分钟时段每15分钟重新读取最新预测只执行未来一个小时或两个小时的指令然后再滚动向前。这个思路对应到代码里就体现为两层循环外层循环推进时间窗口内层循环在每个窗口内求解优化问题。窗口大小至少要能覆盖储能一个完整循环的雏形——比如电池容量能支撑两小时额定功率那调度时域就取8个时段抽蓄的水量调节能力可能支持四个小时那时域就要取到16个时段。窗口太短储能没用满窗口太长预测误差会把模型带偏。实际项目里我会先用前一天的实测数据和天气预报数据离线回测确定一个适合当地气象特点的窗口长度。3. 目标函数与约束的完整数学描述3.1 目标函数从净利润到弃电惩罚先给出一套通用目标函数把调度运行的经济性和物理可行性统一表达出来[ \max \sum_{t1}^{T} \left[ C_{sell,t} \cdot P_{sell,t} - C_{buy,t} \cdot P_{buy,t} - C_{pen} \cdot \left( P_{curtail,t}^{PV} P_{curtail,t}^{WT} \right) - C_{bat,op} \cdot |P_{bat,t}| - C_{psh,op} \cdot |P_{psh,t}| \right] ]其中(C_{sell,t})t时段售电电价元/MWh(C_{buy,t})t时段购电电价元/MWh(P_{sell,t})t时段向外部电网售出的功率MW(P_{buy,t})t时段从外部电网购入的功率MW(C_{pen})弃风弃光惩罚系数元/MWh通常设得比售电电价高迫使求解器优先消纳新能源(P_{curtail,t}^{PV})、(P_{curtail,t}^{WT})光伏、风电的弃电功率MW(P_{bat,t})电池储能出力正为放电、负为充电MW(P_{psh,t})抽蓄出力正为发电、负为抽水MW(C_{bat,op})、(C_{psh,op})电池和抽蓄的运行维护分摊成本元/MWh。这里有一个细节经常被忽略弃电惩罚系数如果设置得太低求解器宁可弃电也不愿意给储能充电尤其当电价低谷期的充电收益不明显时结果会偏离你希望看到的多消纳目标。我自己做仿真时(C_{pen})通常取现货电价的1.2到1.5倍这样在低电价时刻给电池充电虽然经济收益不大但因为它能减少弃电惩罚模型仍然愿意充电。3.2 关键约束功率平衡与备用约束目标函数之外约束条件才是真正体现调度模型水平的地方。最基本的功率平衡方程[ P_{PV,t} P_{WT,t} P_{bat,t} P_{psh,t} P_{buy,t} P_{load,t} P_{sell,t} P_{bat,ch,t} P_{psh,pump,t} ]这个方程里等式左边是供给侧等式右边是需求侧。需要特别注意的是(P_{bat,t})和(P_{psh,t})如果写成正值表示放电/发电那么充电时就应为负值别同时在(P_{bat,ch,t})里再出现一遍否则功率平衡会出现重复计数。很多刚接触模型的人在这个地方出错导致总功率凭空多出一截。旋转备用约束也是必不可少的一项。新能源出力预测不可能完全准确储能必须留出部分容量应对突发偏差[ P_{bat,res,t} P_{psh,res,t} P_{res,grid,t} \ge r \cdot P_{load,t} q \cdot \left( P_{PV,t} P_{WT,t} \right) ]其中(r)是负荷备用系数一般取3%~5%(q)是新能源功率备用系数取5%~10%。这条约束的含义很直白储能不是为了好看才装在系统里的关键时刻必须拿得出功率。我见过有些模型省掉这条约束系统一次性把储能放空结果下一时段光伏突然骤降整个系统的频率立刻失去支撑。调度模型可以不涉及频率动态但备用约束至少能帮你在静态框架里保住安全底线。3.3 储能设备约束电池和抽蓄的区别对待电池储能的状态量是荷电状态SOC其递推关系为[ SOC_{t1} SOC_t - \frac{P_{bat,t} \cdot \Delta t}{E_{bat}} \cdot \eta_{bat}^{sign} ]其中充电时效率为(\eta_{ch})放电时为(1/\eta_{dis})。这个式子看起来和教科书一模一样但代码里要小心处理符号。比较稳妥的做法是分两个变量建模充电功率(P_{bat,ch,t})和放电功率(P_{bat,dis,t})并加入互斥约束(P_{bat,ch,t} \cdot P_{bat,dis,t} 0)。实际用线性规划时这个乘积约束往往用二进制变量实现或者干脆在目标函数里让充放电同时发生的成本足够高求解器自然不敢那么干。抽水蓄能除了能量状态(E_{psh,t})的递推之外还要加一个工况切换约束。水轮机从抽水切换到发电最少需要10到20分钟一个15分钟调度时段内不一定能完成切换。所以代码里通常要增加最小连续运行时间约束或者限制单位时间内工况变化次数[ \sum_{t} \left| u_{gen,t} - u_{gen,t-1} \right| \le N_{switch,max} ](u_{gen,t})是表示抽蓄是否处于发电工况的0-1变量。如果不加这个约束求解结果可能出现抽水、发电、再抽水、再发电的抖动工况实际工程中根本做不到。这个小细节也是很多Matlab仿真代码跑得通、现场却无法执行的根源之一。3.4 联络线功率约束与机组爬坡约束园区或风电场和外部电网的交换功率通常受线路容量限制因此必须有[ -P_{line}^{max} \le P_{buy,t} - P_{sell,t} \le P_{line}^{max} ]有些模型会把购电和售电分别设为两个变量如果两个变量同时大于零目标函数会出现低价买高价卖的套利空间这在现货市场里是允许的但如果你建模的是园区自备系统通常要加(P_{buy,t} \cdot P_{sell,t} 0)的互斥约束。风电、光伏虽然不消耗燃料但机组调节速率依然有限。风电场的出力变化率通常限制在每分钟0.05到0.3倍额定功率光伏逆变器也有爬坡限制[ -\Delta P_{PV}^{max} \le P_{PV,t} - P_{PV,t-1} \le \Delta P_{PV}^{max} ]这部分约束在数据里经常被忽略但实际会显著影响早、晚爬坡段的出力曲线形态。加了爬坡限制之后光伏从峰值掉下来的过程变成有斜率的连续曲线储能需要多承担一部分快速调节任务这也是电池储能和抽蓄联合调度有意义的原因之一。4. 手算算例一个两时段滚动调度演示数学模型不落到数字上总感觉隔了一层。我专门设计一个简化算例把两时段调度过程手算一遍。假设系统参数如下时段t1为午间光伏预测功率(P_{PV,1} 80) MW负荷(P_{load,1} 100) MW时段t2为傍晚光伏衰减到(P_{PV,2} 20) MW负荷升高到(P_{load,2} 120) MW风功率假设都为10 MW电池容量40 MWh初始SOC为50%最大充放电功率20 MW效率90%抽蓄最大发电功率30 MW最大抽水功率20 MW水库初始能量15 MWh发电工况下损耗小为方便手算先忽略弃电惩罚系数为30元/MWh购电电价0.5元/MWh售电电价0.6元/MWh充放电损耗费用按0.02元/MWh估算。先做t1的决策。午间光伏大负荷一般如果不给储能充电多余电力要么弃掉、要么低价卖给外部电网。因为弃电惩罚高于充电损耗成本最优选择是让电池以最大功率20 MW充电。此时功率平衡80 10 0 (0) - 20 70 MW的净电力供应给负荷100 MW不足的30 MW从外部购入。总购电成本为30×0.515万元充电后SOC从50%升到40/40×100%×(1-0.9×2×20×0.25/40)58.75%左右简化计算。再看t2的决策。光伏出力骤降负荷升高系统净负荷120-20-1090 MW。如果此时电池放电20 MW抽蓄发电30 MW仍缺40 MW需要购电。购电成本就是40×0.520万元。两时段总购电成本35万元。但如果t1不让电池充电t1购电成本会降为(100-80-10)×0.55万元t2因为电池没有存量需要购电90-3060 MW购电成本30万元。两时段合计35万元。两种方案总成本相同这是因为算例里电价没有变化。把电价改成峰谷差异就会立刻看出差别t1购电价0.3元/MWht2购电价0.7元/MWh那充电方案为t1购电成本9万元、t2购电28万元合计37万元不充电方案为t1成本3万元、t2成本42万元合计45万元。储能的价值完全由峰谷价差和时间错位决定。这个手算过程虽然简单但揭示了一个核心逻辑储能不是单纯让系统更清洁而是在时间维度上搬运功率让每一时段都能买到最便宜的电力、卖出最高价的电力。滚动调度就是把上面这个过程每15分钟重做一遍每做一次只采用第一个时段的决策。5. Matlab代码实现的关键细节与避坑经验5.1 用YALMIP还是用Optimization Toolbox做联合调度最常用的工具是Matlab的Optimization Toolbox自带函数比如linprog或intlinprog。如果你的模型只涉及连续变量用linprog非常快一旦加入抽蓄工况切换的0-1变量就要转用intlinprog但大规模场景下整数变量一多求解速度会骤降。另一个选择是YALMIP配合gurobi或cplex求解器建模和调试都更顺手。YALMIP的好处是约束表达接近数学公式不容易发生索引错位坏处是需要额外安装第三方求解器许可证问题有时候会比较麻烦。我个人建议刚起步阶段直接用linprog或intlinprog把连续模型跑通再加入0-1变量。不要一开始就上Gurobi等你确认模型本身没有约束矛盾时再换求解器否则排查问题时会分不清是建模错误还是求解器配置错误。5.2 滚动调度代码框架双层循环结构滚动调度在代码层面的核心结构是这样的T 96; % 一天96个15分钟时段 N_window 8; % 滚动窗口长度相当于未来2小时 results zeros(T, 5); % 存储每时段的决策结果 for t 1:T-N_window % 截取从t到tN_window-1的预测数据 PV_win PV_forecast(t:tN_window-1); WT_win WT_forecast(t:tN_window-1); load_win load_forecast(t:tN_window-1); % 在窗口内求解优化模型 [x_opt, fval] solve_window(PV_win, WT_win, load_win, SOC_bat, E_psh); % 只取第一个时段的决策执行 results(t, :) x_opt(1, :); % 更新储能状态 SOC_bat update_battery(SOC_bat, results(t, 3)); E_psh update_psh(E_psh, results(t, 4)); end这段代码有一个很关键的细节外层循环推进时储能状态必须沿用已执行决策更新后的值而不是重新读取初始状态。初学者最容易犯的错误就是每次进入窗口优化时把SOC重置为初始值导致优化结果始终停留在从50%开始的同一状态完全失去了滚动修正的意义。5.3 yalmip建模中的一个典型坑符号约定如果你用YALMIP建模电池充放电混合符号经常引发约束错误。建议这样分变量建模Pbat_ch sdpvar(N_window, 1); Pbat_dis sdpvar(N_window, 1); Pbat Pbat_dis - Pbat_ch; % 净放电功率然后追加约束Constraints [Constraints, Pbat_ch 0, Pbat_dis 0]; Constraints [Constraints, Pbat_ch 20, Pbat_dis 20]; Constraints [Constraints, Pbat_ch Pbat_dis 20];最后一条约束保证充放电不会同时发生虽然它略微放宽了自由度但比引入二进制变量高效得多。抽蓄的发电/抽水状态同理只是功率上限不同。SOC的状态方程在YALMIP中写作SOC(1) SOC_init; for k 1:N_window-1 SOC(k1) SOC(k) (Pbat_ch(k) * eta_ch - Pbat_dis(k) / eta_dis) * dt / E_bat; end Constraints [Constraints, SOC 0.2, SOC 0.9];SOC上下限我一般设0.2~0.9而不是0~1。留出10%上下空间的原因很简单光伏出力预测突然暴增或负荷骤降时储能还要有吸收剩余电力的能力如果运行点长期顶着上限系统就没有了向上调节的余量。5.4 等式约束与松弛变量的处理功率平衡约束在滚动调度里是最容易造成不可行的元凶。因为预测数据难免有误差而储能功率、购电功率都有上限某些极端时段可能出现怎么配平都差一点的情况。解决办法是给功率平衡约束引入松弛变量(\delta_t)[ P_{PV,t} P_{WT,t} P_{bat,t} P_{psh,t} P_{buy,t} P_{load,t} P_{sell,t} \delta_t ]然后在目标函数里加上松弛惩罚项(M \cdot \sum |\delta_t|)M取一个远大于其他成本项的值。这样模型在极端场景下不会直接报infeasible而是让(\delta_t)承担无法平衡的那部分功率同时通过惩罚迫使(\delta_t)尽量小。本质上这是把硬约束软化的工程化处理。我在实际调试里发现加入松弛项之后模型对异常数据的鲁棒性提升极其明显代码能从三天两头报错变成稳定运行一整天。5.5 结果可视化和敏感性分析调度模型的输出不能只看目标函数值还要看功率时间序列是否合理。我通常用三个图验证代码逻辑各电源出力堆叠图观察光伏、风电、储能、购电的时序组合是否和负荷曲线匹配能快速发现储能到底有没有在该工作的时间工作SOC变化曲线看SOC是否频繁触顶触底如果是说明储能容量配置或者充放电策略不合理弃电率累计曲线算每日弃风弃光率判断调度策略对新能源消纳的实际提升。敏感性分析方面至少要做两组一组把电池容量从40 MWh逐步改到120 MWh看弃电率和购电成本怎么变另一组把峰谷价差从0.2元改到0.6元看储能的充放电循环次数怎么变。这两组分析比任何花哨的算法改进都更能说明方案的经济价值也是论文和报告里审稿人最喜欢看的图表之一。6. 实际项目中的经验教训与后续扩展方向6.1 弃电惩罚系数别乱设我见过一个仿真案例作者把弃电惩罚系数设成电价的5倍结果模型为了多消纳宁可让电池在低效区间里反复充放也不愿意看到一丁点弃电。对应到实际电池寿命和维护成本这种策略完全是亏本的。正确做法是把电池循环寿命折算进充放电成本比如磷酸铁锂电池每度电的循环损耗成本大约在0.1~0.3元如果你的弃电惩罚系数高于这个值模型才会愿意让电池多干活否则电池根本不应该频繁动作。我在目标函数里加入的(C_{bat,op} \cdot |P_{bat,t}|)就是用来体现这一点的。6.2 老旧矿井改造的边界条件废弃矿井抽水蓄能虽然听起来很美好但实际落地要看三个条件上库和下库的落差至少要有30米以上否则能量密度太低库容要求会大到不现实矿区地质条件要稳定不能有水渗漏和地面沉降风险距离电网要近否则并网线路成本直接吃掉多年运营收益。仿真模型可以假设库容无限大但方案汇报时这些边界条件才是决策者们真正关心的东西。做技术方案的人一定要在报告里把这几个条件列清楚不然模型再精细项目也落不了地。6.3 从开环调度走向闭环反馈滚动调度本身已经引入了预测更新机制但真正的闭环系统还需要引入实测反馈。Matlab仿真阶段你可以用一段模拟的真实出力曲线替代预测曲线在每个滚动窗口执行完第一个时段的决策后用真实出力更新储能状态再进入下一个窗口。这个闭环测试比单纯把预测数据塞进模型更能反映算法在工程现场的鲁棒性。很多论文里的调度算法一换到实测数据就崩就是因为没做过这种预测—执行—反馈—再预测的仿真闭环测试。6.4 与碳交易、需求响应结合的扩展调度框架搭好之后后续扩展方向非常多。比如在目标函数里加入碳排放成本就能把各种电源的出力顺序重新洗牌加入可中断负荷和价格型需求响应则能改变负荷侧的等效形状。我在做低碳园区调度时会在负荷曲线上叠加一个可转移负荷模型让部分工业负荷从傍晚移到午夜结果发现储能的利用率下降了不少——因为需求响应本身就在源头压缩了峰谷差储能套利的空间自然小了。这说明储能和需求响应是一对竞争者做多能源系统规划时不要只顾着加大储能也要算算需求侧响应的替代效益。最后分享一个小技巧不管模型怎么改先把固定输入数据和目标函数写成一个函数文件用几个简单用例跑通再回来加约束和整数变量。这个习惯能让你在调试混乱的调度代码时始终有一个能对照的基准版本。我的经验是风光储联合调度真正难的不是求解而是在纷繁的约束之间找到一组彼此不打架、又足够逼真的假设。把这步做扎实了剩下的只是时间和耐心的叠加。
返回列表