ARTICLE DETAIL

资讯详情

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

微网优化调度模型实战:基于Yalmip的建模与求解全解析

微网优化调度模型实战:基于Yalmip的建模与求解全解析 1. 微网优化调度的思路拆解与模型架构做微网优化调度模型这件事我在项目里已经反复折腾过好几轮了。最早用的是纯Matlab手写线性规划后来换到Yalmip之后明显感觉建模效率上了一个档次尤其当你面对的是蓄电池、风电、光伏、柴油机四个不同特性的发电单元时约束关系非常容易绕晕Yalmip这种声明式的建模方式能把代码逻辑和物理逻辑对齐后面维护起来也轻松得多。先说说这个项目到底要干什么。微网系统里面有风电和光伏这两个不可控电源有柴油机这个可控主力还有蓄电池这个快速响应单元。优化调度的目标就是在满足负荷需求、机组运行约束、储能充放电约束的前提下以最低的运行成本决定未来24小时内每个时段各单元的出力值。说白了就是把什么时候该用柴油机发多少电、什么时候该充电池、什么时候该放电池这个问题变成一个数学优化问题然后求解器去解。我选择Yalmip有几个实际因素。第一Yalmip的语法非常接近数学表达式的自然写法一个约束写出来什么样代码里就是什么样不容易把逻辑写错。第二底层求解器可以随时切换同一个模型换成Cplex或者Gurobi只是改一行代码的事情。第三和Matlab自带的优化工具箱相比Yalmip在线性规划、混合整数规划上的支持成熟很多。对于微网调度这种典型的混合整数线性规划问题——里面有二进制变量表示柴油机启停、蓄电池充放电状态——Yalmip处理起来非常顺手。微网系统的运行逻辑需要从一个整体视角去看风电和光伏属于不可控电源出力由天气决定。调度模型里把它们当作负的负荷处理优先消纳如果预测出力过剩可以弃风弃光。柴油机可控电源是系统里的主力调节手段。响应速度中等启动需要时间有爬坡约束燃料成本是最主要的经济指标。蓄电池快速响应单元既能充电又能放电是系统里的缓冲池用来削峰填谷、平抑风光波动。但它的约束也比较多——容量限制、SOC上下限、充放电功率限制、还有充放电不能同时进行的逻辑约束。调度的本质就是把这几种特性完全不同的单元组合在一起在24小时时间尺度上做协同优化。关于模型架构我当时设计的是典型的两层逻辑边界条件层和优化决策层。边界条件层负责处理负荷预测、风光出力预测、柴油机参数、蓄电池参数这些输入优化决策层基于Yalmip构建目标函数和约束集调用求解器求最优解。数据流向是预测数据进模型约束逻辑在模型里形成可行域目标函数指引寻优方向最后解出结果再做校验和后处理。整个过程说起来简单实际操作中每个环节都有不少值得注意的坑后面我会详细展开。2. 四大发电单元的建模细节与约束处理这一章是核心中的核心建模的质量直接决定了求解结果是不是合理。我在这个项目里把四个发电单元分开建模然后通过功率平衡约束把它们耦合起来。当初这样做最大的好处就是每个单元的内部逻辑相对独立改一个单元的约束不会影响到其他单元调试起来效率高很多。2.1 蓄电池储能单元建模的要点蓄电池是模型中最容易出问题的地方因为涉及到状态变量、二进制变量还有递推关系。它的核心物理逻辑是SOC荷电状态随时间步进递推SOC(t1) SOC(t) - P_ch(t) * η_ch * Δt / Cap P_dis(t) * Δt / (η_dis * Cap)公式里P_ch是充电功率正数P_dis是放电功率正数Cap是电池容量Δt是调度步长。充放电效率一般取0.9到0.95之间如果做规划类模型也可以都取1简化处理但实际项目里最好保留效率系数不然算出来的结果会偏乐观。蓄电池建模的三个关键约束SOC上下限约束一般设置在0.1到0.9之间避免过度充放损伤电池寿命。那些直接写0到1的做法在学术文章里能看到做实际项目不建议这么干。充放电功率限幅P_ch和P_dis都要小于最大充放功率这是由电池本身的特性决定的。充放电互斥约束这里需要一个二进制变量让充电和放电不能同时发生。写成表达式就是P_ch ≤ M * u和P_dis ≤ M * (1-u)u是二进制变量M是足够大的常数。有一个细节我刚做的时候踩过坑如果时间步长不是1小时Δt必须写进递推公式里。我做过一个步长15分钟的调度模型忘记把Δt换算成0.25小时结果SOC曲线全乱了充电量对不上电池储能越算越多。这个问题排查了一个多小时最后才发现是单位的问题。2.2 风光机组模型怎么处理才合理风电和光伏在微网模型里通常作为不可控电源处理。它们对调度模型来说就是一个预测出力的输入序列不需要也不应该去调度它们因为风光的出力取决于天气条件调度所能做的决定只有消纳多少或者弃掉多少。光伏出力受光照强度影响曲线形状是一个典型的钟形——夜间为0中午峰值最高我在项目里直接采用预测数据风机则看风速预测数据是时间的函数。这两种电源的基本处理方式是一样的P_wind_use(t) ≤ P_wind_forecast(t) P_pv_use(t) ≤ P_pv_forecast(t)最优解里通常会把风光预测出力全部消纳掉因为风电光伏的边际成本接近0。但有一种情况会出现弃风弃光比如中午光伏出力最大时负荷很低或者蓄电池已经充满了柴油机又因为技术出力下限不能压得很低。这时候模型为了满足功率平衡只能舍弃部分光伏出力。这种约束写法允许模型选择不用多少可再生能源在最优解里它自然有判断。我在模型里给风光加了一个弃用惩罚成本让模型在一堆可行方案中优先选择尽量多消纳风光的方案。惩罚成本设成零的话经常会出现求解器随意弃用风光的情况因为对目标函数没有任何影响导致结果看起来不太符合物理直觉。加了一个微小的正系数之后模型的行为就正常多了。2.3 柴油机建模要留意成本函数线性化柴油机在微网系统里充当的是保底电源的角色风电、光伏不足的时候它就是最可靠的出力来源。它的一个特点是出力有下限——柴油机技术上的最小稳定出力大概在额定功率的30%左右低于这个值机组运行不稳定甚至熄火。所以模型中必须加入这个约束这也是很多微网模型后半夜风光不足时没法完全依赖柴油机少量发电的原因。柴油机的成本由三部分组成F_diesel(t) a * P_diesel(t)^2 b * P_diesel(t) c其中a是二次项系数——燃料消耗的边际递增特性决定了这部分的存在b是一次项系数代表每度电的燃料成本c是固定启停成本。由于标准线性规划不能直接处理二次项Yalmip中最简单的做法是把成本函数做分段线性化近似二次项用分段线性函数去拟合段数取3-5段。段数太多会额外增加变量和约束求解速度下降段数太少拟合精度不够解的出力值会有偏差。我在这类项目中一般取3段。固定启停成本用二进制变量处理如果柴油机在时段t启动则在此后的几个时段内无论出力多少都要支付一笔固定费用。这个逻辑在调度结果中表现为——柴油机尽量保持持续运行状态频繁启停会被成本约束自动排除掉。爬坡约束也是柴油机建模中不能忽略的一环。柴油机出力变化的斜率有限不可能从一个时段到下一个时段瞬间从2MW跳到10MW。爬坡约束写成P_diesel(t1) - P_diesel(t) ≤ R_up * Δt P_diesel(t) - P_diesel(t1) ≤ R_down * Δt如果不加这个约束模型可能会给出一种极端方案——比如某时段风光突然消失柴油机出力瞬间拉满实际运行中这根本做不到。爬坡约束其实就是模型里机组物理特性的体现。2.4 功率平衡与旋转备用约束的写法把四个单元衔接起来的最关键约束就是功率平衡方程它也是整个模型的硬性约束意思是任何时刻都必须满足供需平衡P_pv_use(t) P_wind_use(t) P_diesel(t) P_dis(t) - P_ch(t) P_load(t)这个式子的含义是光伏和风电的消纳量加上柴油机出力加上蓄电池放电减去充电功率等于负荷需求。蓄电池充电算负的负荷放电算正电源从数学形式上来看非常干净。实际模型里还需要加入旋转备用约束。简单来说就是系统要有能力应对负荷预测偏差或者风光出力突然下跌的情况确保系统的安全性和稳定性。旋转备用约束的写法是系统可用总出力上限要大于等于负荷加备用需求P_load(t) R_require(t) ≤ P_pv_max(t) P_wind_max(t) P_diesel_max(t) P_dis_max(t) - P_ch_min(t)这个约束在实际项目中容易被遗漏。但如果微网是孤岛运行的没有大电网作支撑备用需求必须覆盖最恶劣的场景否则系统随时可能面临断电风险。3. Yalmip建模实操与求解全流程上一章讲的是建模层面的物理逻辑这一章说说怎么把这些物理逻辑翻译成Yalmip代码以及求解过程中我踩过的坑。Yalmip的最大优势在于声明式建模——你要做的事情就是定义变量、写约束、声明目标函数剩下的求解细节交给求解器处理。3.1 决策变量怎么定义才方便后续处理Yalmip中定义变量的核心函数是sdpvar和binvar。针对24时段微网调度问题我的做法是直接把决策变量定义成一个向量这样后面写约束和结果处理都更方便。% 24时段调度时间步长1小时 T 24; P_diesel sdpvar(1, T); % 柴油机出力连续变量 P_ch sdpvar(1, T); % 蓄电池充电功率 P_dis sdpvar(1, T); % 蓄电池放电功率 u_start binvar(1, T); % 柴油机启动状态 u_run binvar(1, T); % 柴油机运行状态 u_ch binvar(1, T); % 蓄电池充电状态1充电0放电 SOC sdpvar(1, T1); % 荷电状态多一个元素是为了表示0时刻初始值注意SOC定义成了T1个元素因为需要SOC(1)对应初始时刻的荷电状态SOC(t1)对应第t个时段末的状态。这是我做这个项目时的一个重要习惯宁可多一个变量把递推关系写清楚也不要为了省一个变量让索引关系变得难维护。Yalmip既然允许向量定义就用向量思维来处理在循环里处理的时候效率也更稳定。3.2 约束组装用循环还是向量化Yalmip支持两种约束写法一种是向量约束整体写入另一种是for循环逐条添加。对于维数不高的24时段模型我用for循环最直观每行约束对应一个时段逻辑清晰。Constraints []; % 柴油机出力上下限约束与运行状态绑定 for t 1:T Constraints [Constraints, P_diesel_min * u_run(t) P_diesel(t) P_diesel_max * u_run(t)]; end % 蓄电池充放电约束包含互斥逻辑 for t 1:T Constraints [Constraints, 0 P_ch(t) P_ch_max * u_ch(t)]; Constraints [Constraints, 0 P_dis(t) P_dis_max * (1 - u_ch(t))]; end % SOC递推关系 for t 1:T Constraints [Constraints, SOC(t1) SOC(t) - P_ch(t) * eta_ch / Cap P_dis(t) / (eta_dis * Cap)]; end % 功率平衡约束 for t 1:T Constraints [Constraints, P_pv(t) P_wind(t) P_diesel(t) P_dis(t) - P_ch(t) P_load(t)]; end这里有一个细节值得注意蓄电池的充放电互斥约束我用了u_ch(t)这个二进制变量来实现没有用两变量相乘的表达式因为那样模型就变成非线性了。Yalmip对非线性问题的处理能力虽然也不错但非线性求解器在效率和稳定性上远不如线性求解器能用线性处理的地方我坚决不用非线性。Yalmip约束拼接有一个比较隐蔽的性能坑。如果约束条数非常多比如几百条以上写成Constraints [Constraints, new_constraint]这种追加方式在Matlab里会有复制的开销求解器在启动阶段会比较慢。这个场景下把约束放进cell数组里最后用一个循环Constraints [Constraints{:}]拼接速度会快很多。24时段调度约束也就几百条的量级影响不明显但如果以后扩展到96时段15分钟步长这个优化就很有必要了。3.3 目标函数与求解器配置的实操细节目标函数代表的是整个系统的运行成本。这个项目里我把它定义为三部分之和——柴油机燃料成本、蓄电池维护成本、弃风弃光惩罚成本Objective 0; % 柴油机燃料成本 % 这里假设成本函数已经线性化为三段F1/F2/F3分别是各段的成本变量 for t 1:T Objective Objective F_diesel(t) * P_diesel(t) * dt; end % 蓄电池维护成本充放电都会产生电池损耗 for t 1:T Objective Objective C_bat * (P_ch(t) P_dis(t)) * dt; end % 弃风弃光惩罚成本 for t 1:T Objective Objective C_curtail * ((P_pv_max(t) - P_pv_use(t)) (P_wind_max(t) - P_wind_use(t))) * dt; end在调度的24小时范围内成本计算把每个时段的值乘以时间步长dt再累加求和。dt是小时数如果步长是15分钟则dt0.25。刚开始做的时候我总觉得用元/小时的功率乘1小时好像多此一举但实际上功率乘以时间才是能量乘以电价才是成本。举个例子调度步长改成15分钟却忘记乘0.25算出来的成本就比实际值放大了4倍调度结果的倾向性也会出问题。求解器调用是整个过程里最简单的一步ops sdpsettings(solver, gurobi, verbose, 0, showprogress, 0); optimize(Constraints, Objective, ops);solver参数可以设定为gurobi、cplex、sedumi等取决于你机器上装了哪些求解器。如果没有商业求解器solver也可以直接写gurobi大不了改成cplex或者sedumiYalmip的可移植性就在这里。3.4 求解结果怎么提出来并验证求解完成后最关键的步骤是使用value()函数提取变量数值P_diesel_opt value(P_diesel); P_ch_opt value(P_ch); P_dis_opt value(P_dis); SOC_opt value(SOC);提取出来之后我一般会画两张图做验证第一张是24小时的功率平衡堆叠图柱状堆叠显示光伏、风电、柴油机、蓄电池放电各贡献多少同时叠加负荷曲线第二张是SOC变化曲线观察蓄电池是否在合理范围内来回波动。如果堆叠图各段加起来不等于负荷曲线说明某个约束写错了回查约束逻辑是排查问题的第一步。另外还有一个验证技巧值得分享把优化结果代回目标函数手动重新计算一次总成本看是不是和求解器的目标函数值一致。如果不一致大概率是某个公式里dt的系数有问题或者某个成本项的变量带错了。这个小检查只需要几行代码能避免非常多下游分析阶段的数据错误。4. 常见问题排查与调参实战模型跑通了那是一回事结果合理才是另一回事。这一章专门讲讲我在这个项目里实际遇到的几个典型问题哪些经验是可以复用的、哪些坑是必须避开的。4.1 模型不可行怎么定位问题Yalmip模型报infeasible不可行是我最常遇到的问题。整段模型几百条约束任何一条冲突都会导致整体无解。解决不可行问题的一个高效做法是把模型拆开来看——先把蓄电池约束全部注释掉只保留柴油机和功率平衡看模型是否可解。如果可以求解说明问题出在蓄电池约束内部如果还是不可行再看是不是功率平衡和预测数据之间存在矛盾。另外有一个很重要的思路负荷平衡约束的本质要求是所有可调出力之和要能覆盖负荷与风光预测的差额。如果某个时段负荷特别高而风光特别低柴油机最大出力加上电池放电最大出力仍然小于净负荷那模型一定会不可行。项目中出现这个情况时不应该去调约束参数而是要考虑是否应该增加柴油机容量或者削减高峰时段的部分负荷。模型不可行往往是在提醒你系统物理配置本身存在缺口而不是代码写错了。提示Yalmip提供了一个叫diagnostic的功能通过查optimize的输出信息能发现一些线索但真正高效的调试方式仍然是逐步注释法不要指望一次定位到某一条具体约束。就我的经验来说70%以上的不可行问题出在功率平衡约束和蓄电池SOC递推约束排查时优先看这两个。4.2 求解速度慢怎么优化这个问题在引入大量二进制变量之后会变得突出。微网调度本质上是混合整数线性规划整数变量越多求解时间越长这是数学上不可避免的特性。优化求解速度有几个方向第一个方向是减少整数变量的数目。比如蓄电池充放电状态变量很多情况下可以通过合理设置目标函数来避免使用二进制变量——如果充电和放电的成本系数都是正数目标函数最小化就会天然避免同时充放电这种荒谬行为。但要注意如果没有互斥约束某些特殊场景下求解器仍可能给出同时充放电的次优解还是要加上约束更稳妥。第二个方向是给求解器设置合理的容差。Gurobi和Cplex都有MIPGap参数MIP问题的相对最优间隙默认一般比较严格可以适当放宽ops sdpsettings(solver, gurobi, mipgap, 0.01);对于微网调度这种场景1%的间隙意味着目标函数与最优解的偏差最多为1%在实际工程中足够用了。但代价是求解时间可能减少60%到80%。我之前有一个96时段的模型默认参数求解要60秒以上把MIPGap调整到1%后只需要10秒左右运行结果差异肉眼根本看不出来。第三个方向是从数学建模层面优化。把某些双线性约束改写成线性等价形式或者用Big-M法替代逻辑判断。这些改写对求解器的友好度提升非常明显。4.3 运行结果不合理的检查方向有一次我跑出来的结果中蓄电池在某个时段又充电又放电但数值恰好都在很小的量级目标函数没有产生明显损失。仔细排查后发现问题出在我给电池维护成本设置的系数太小——蓄电池在两个方向上的成本变化对目标函数的影响微乎其微求解器选择了充一点也放一点的不合理方案。解决方法是直接加上互斥约束那组二进制变量或者把电池维护成本调大一些。还有一次我注意到模型里柴油机出力一直顶在上限即使风光都非常充足。原因是我在写爬坡约束的时候把R_up写成了R_down导致柴油机的减载能力被模型限制住了。这个错误在代码层面非常隐蔽因为完全符合格式逻辑上看着没有任何问题。从那次开始我养成了一个习惯每次修改模型后都要跑一个极端场景测试。怎么测把负荷设得全低、风光设得全高看系统是否自动减柴油机出力、给电池充电把负荷设得全高、风光设得全0看系统是否柴油机满发、电池放电。如果这两种极端的响应行为符合物理直觉模型的可靠性就基本有保障了。4.4 参数敏感性分析与结果稳健性微网优化调度模型有一个绕不开的现实问题风光预测数据不可能完全准确负荷预测同样会有偏差。这就导致模型算出来的最优调度方案在实际执行时不一定最优甚至可能不可行。为了解决这个问题我在项目里会做一个简单的敏感性分析——将风电、光伏的预测值按-20%到20%的比例扰动重新运行模型检查柴油机出力和蓄电池SOC的变化幅度。敏感性分析结果通常会呈现这样的规律风光预测误差对系统的影响主要取决于柴油机的调节能力和蓄电池的容量。如果柴油机爬坡快、容量裕度大那风光预测误差的影响就被消化掉了如果系统本身调节能力很弱一个小的预测偏差可能就导致切负荷。做敏感性分析的真正的价值不在于精确预测而在于给系统找出短板资源告诉工程团队需要在哪里加裕量。从成本和环保的角度来看这个模型本质上是在做经济学上的折中柴油机多发电成本高、碳排放高但系统稳定性好蓄电池多充放调度灵活度提升但电池寿命损耗不可避免风光满发边际成本最低但可能出现消纳不了的情况。目标函数里的各项成本系数其实就决定了模型在这些目标之间如何取舍。把燃料成本系数调高结果会自动偏向少用柴油机把电池损耗系数调高蓄电池的充放电次数会明显减少。调参的过程本身就是在做决策策略的选择。5. 经验总结与一些实际建议这个项目完整跑下来我最大的感受是微网优化调度模型真正的门槛不在数学建模也不在编程实现而在于你对物理系统的理解是否足够深。Yalmip只是工具它把从物理逻辑到数学表达的翻译过程变得顺畅但物理逻辑本身必须由你来把握——什么约束必须加、什么约束可以简化、哪些参数对结果影响大、哪些参数不过是细枝末节这些判断决定了模型的成品质量。几个实际建议可以分享给要上手类似项目的人第一先把单机模型跑通再做联合调度。先建一个只有柴油机负荷的模型确认它能正确求解然后加入蓄电池确认SOC递推和充放电逻辑正确最后再加入风光。一步到位往往导致出问题时根本不知道问题出在哪一层。第二在做调度之前先把数据校准做完。我在项目中有一次光伏预测数据和实际数据之间存在系统性偏差结果算出来的调度方案里蓄电池总是在午后提前充满等到傍晚负荷高峰时反而没有容量释放了。这个问题在模型里无解只能靠修正预测数据解决。第三时刻把结果可视化放在身边。Yalmip模型跑的是一次性优化不等于你就能理解结果。功率平衡图、SOC曲线、各电源的日出力曲线这三张图能覆盖大部分结果合理性判断。最后分享一个我在迭代中发现的细节这个模型后续可以扩展的方向很多——加入储能寿命衰减模型、需求侧响应、碳排放约束、多场景随机规划等。但所有扩展都建议在主流程跑通之后再逐项增加否则复杂度一上来调试的成本会远超预期。微网调度这个领域核心在于对系统平衡的理解和建模基本功的扎实工具终究只是辅助。从实践来看这个模型在运行中表现稳定求解速度快结果也符合预期。把一个优化调度模型从零搭到可以交付其实并没有太多神秘之处。把握住物理规律、整理好数据、选择顺手的建模工具、耐心调试最终的结果会足够可靠。
返回列表