ARTICLE DETAIL

资讯详情

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

基于Yalmip的微网优化调度模型详解与工程实践

基于Yalmip的微网优化调度模型详解与工程实践 去年在做一个园区微网能量管理系统项目时面对风电、光伏、蓄电池和柴油机组四个发电单元我用基于Yalmip的微网优化调度模型替换了原先if-else堆逻辑的调度策略运行结果良好成本降了不少设备启停次数也更合理。这篇文章就把这个模型的搭建思路、代码骨架、调试过程和结果分析完整复盘一遍给正在做微网能量管理、综合能源系统优化或正在入门Yalmip的朋友做个参考。1. 微网调度为什么需要专门建模Yalmip选型的前因后果1.1 调度问题的本质多时段耦合的混合整数决策微网调度和单机控制最大的区别在于你不能只看当前时刻而要看未来一整天的“账”。举个例子午间光伏大发这一两个小时内多出来的电量到底是给蓄电池充电还是直接弃掉如果选择充电那么这笔能量到晚上用电高峰再放出来相当于把午间本来浪费掉的绿色电力转移到了晚高峰直接减少柴油机组的燃料消耗。但这种能量转移不是靠拍脑袋定规则就能做好的它需要同时考虑未来十几个时段的负荷、风光出力、储能SOC状态以及柴油机的启停成本。这个优化问题里还藏着一个关键难点柴油机组启不启动是一个0或1的决策不是连续可调的量。哪怕你只是决定“未来一小时内机组是开着还是关着”就已经引入整数变量。再加上蓄电池的充放电状态切换、储能SOC的递推约束整个问题在数学上属于混合整数线性规划MILP或混合整数二次规划MIQP的范畴。我最早也尝试过用启发式规则来做例如“凌晨风电大就充电下午光伏大给电池充晚上负荷高就放电”看起来逻辑清晰但实际运行后问题很多不同季节风光曲线差异大负荷波动复杂固定规则总是出现电池过放或者柴油机频繁起停的情况。最后才意识到微网调度本质上是一个多时段耦合的最优决策问题需要把它形式化成数学规划模型交给通用求解器去算全局最优解。1.2 为什么不用手写优化器而选择Yalmip在确定用优化模型以后我面临一个选型问题是手写线性规划求解代码还是直接调Gurobi这类商用求解器的API还是用MATLAB平台上的Yalmip建模语言手写内点法或单纯形法只适合应付小规模连续优化问题一旦引入柴油机启停这种整数决策自己造轮子的成本高到无法承受。直接调Gurobi API虽然可行但每一条约束都要手动转成系数矩阵一个索引错位就查半天的bug。相比之下Yalmip把“建模层”和“求解层”解耦了让你能以接近数学表达式的语言去描述变量、目标函数和约束剩下的矩阵生成、求解器调用都由Yalmip处理。下表是我当时对比三种方案的真实感受维度手写优化器直接调Gurobi APIYalmip建模层建模清晰度低极易出错中需要自己组装稀疏矩阵高约束写法和数学公式几乎一致整数变量支持极难实现支持但配置繁琐原生支持binvar声明求解器兼容性无绑定单一家可自由切换Gurobi、Cplex、SCIP、HiGHS等调试门槛高中低有check和value等工具Yalmip的缺点也不是没有它只是一个建模层运算效率最终取决于后端求解器如果问题规模特别大比如做全年8760小时、上百个节点的微网优化Yalmip的封装会带来额外内存开销。但对于微网调度这种常见的24或168时段、几十个变量的规模Yalmip带来的便利性远超那一点性能损耗。当时我装好Gurobi之后在Yalmip里改求解器只需要改一行sdpsettings配置这种体验是手写矩阵完全给不了的。2. 四类发电单元的建模拆解从物理特性到数学约束2.1 风电单元功率上限、弃风惩罚与预测接口风电出力由风速决定调度端没法直接命令风机“多发一点”所以风电在调度模型里的本质是一个“给定最大可发功率的下级电源”。我采用的是可弃风表达方式把预测功率作为上边界实际消纳功率作为决策变量。约束写成0 P_wind_use(t) P_wind_forecast(t)其中P_wind_forecast(t)是来自风速预测的风电预测出力曲线。之所以不用“P_wind(t) P_wind_forecast(t)”这种全额消纳写法是因为实际工程里电网侧或微网能量管理系统出于安全原因经常需要限功率运行。比如深夜风电大发而负荷很低时蓄电池和柴油机都扛不住全部风电与其让系统崩溃不如主动弃掉一部分风。如果模型不允许弃风那约束就一定会撕裂求解器要么报不可行要么迫使柴油机在低负荷工况硬撑成本上非常不划算。目标函数中对应增加弃风惩罚项C_curtail_wind * sum(P_wind_forecast - P_wind_use)这个惩罚项的经济含义是放弃绿色电力造成的机会成本。注意惩罚系数不能设为零否则优化器会把弃风当成理所当然的事因为所有可控电源的成本都大于零只要有弃风空间它就可能通过弃风来减少其他机组的出力从而让成本“虚低”。关于惩罚系数怎么取值后面我会单独讲。这里的预测接口也很关键。现在很多风电场项目都在做“智能风电运维”通过风机SCADA数据、振动监测、气象预报来滚动修正功率预测曲线。调度模型只需设置一个外部数组输入接口把预测模块给出的P_wind_forecast填进去即可不必关心风电运维里那些轴承温度、齿轮箱振动等底层细节。模型和预测模块通过数据解耦这是工程上很舒服的架构。2.2 光伏单元出力曲线与逆变器接口的边界处理光伏侧的建模思路和风电基本对称P_pv_use(t) P_pv_forecast(t)同时再取逆变器额定容量的限制P_pv_use(t) min(P_pv_forecast(t), P_inv_rated)这里常常有朋友犯迷糊光伏建模到底和光伏逆变器什么关系为什么调度模型里看不到MPPT电路、Boost拓扑、绝缘阻抗这些内容答案很简单——那些属于设备层的本地控制问题。逆变器通过MPPT算法实时追踪最大功率点通过绝缘阻抗检测判断直流侧对地绝缘状态这些都是毫秒级、秒级的电力电子控制而微网调度模型是一个面向未来24小时、时间粒度1小时的能量管理层决策。它的输入只需要一个界面参数未来时段光伏阵列最大可发功率。我在模型里专门留了一个“可用率”参数availability(t)光伏逆变器在绝缘阻抗低报警、直流侧故障等情况下会降载甚至停机这时直接把对应时段的可用率乘到P_pv_forecast上。这个细节虽然简单但实际运行时价值很大——如果不留这个参数模型一旦用了故障时段的光伏功率实际执行时功率平衡就会被打破。光伏预测数据则来自超短期光伏功率预测模块。这种预测往往结合数值天气预报和地基云图误差主要来自云层遮挡突变。在调度模型里光伏曲线的典型形态一般是早上6点开始爬升、中午12到13点达到峰值、傍晚降零的钟形曲线。我在测试数据里构造的是夏季晴天典型曲线峰值约600kW而园区负荷午间大约500kW左右所以出现了午间光伏过剩的时段这正好给蓄电池创造了充电场景。2.3 蓄电池SOC演化、充放电互斥和首末约束蓄电池是整个模型中变量最密集的部分也是挖坑最多的地方。它涉及四个核心约束。第一个是SOC递推关系描述能量状态随时段的变化SOC(t1) SOC(t) (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt / E_cap这个公式里的eta_ch和eta_dis必须分开标定。锂离子电池充放电效率大致在92%到96%之间但不同电量区间和温度下会有差异。我一开始偷懒把两个效率都设成0.95后来实测发现电池在低SOC区间充电效率明显下降放电效率却相对稳定模型跑出来的SOC轨迹和现场实际数据总对不上。这个参数取值对调度结果有直接影响如果效率取高了系统会乐观地认为储能能转移更多能量实际执行时到不了预期的放电深度。第二个是SOC安全边界SOC_min SOC(t) SOC_max工程上不能把电池用到0或充到100%我一般设0.1到0.9而且会结合具体电池的BMS策略调整。比如有些厂家的电池建议留20%下限和95%上限那就按厂家数据来一是保护寿命二是给控制留缓冲空间。第三个是充放电互斥约束。蓄电池不能在同一时刻既充电又放电这个物理事实看似显然但如果你不在模型里显式表达求解器会利用这个漏洞“作弊”。具体表现就是P_ch和P_dis同时非零功率平衡还能对得上但实际上SOC根本没变。解决办法是用一个二进制变量u_chP_ch(t) P_ch_max * u_ch(t) P_dis(t) P_dis_max * (1 - u_ch(t))当u_ch(t)1时只允许充电u_ch(t)0时只允许放电。这个约束加进去后即使目标函数里对充放电没有任何惩罚求解器也没有办法再钻空子。第四个是首末SOC约束。调度周期开始时SOC(1)必须等于当前实际荷电状态否则模型会从一开始就虚构一管“初始电量”。末端约束也不能忘通常写成SOC(T) SOC_end_threshold。如果不加末端约束优化器为了省钱会把蓄电池到最后时段放到只剩地基第二天系统根本没法继续调度。在我的模型中我要求调度周期结束SOC不低于0.4保证每天的运营是可持续的。2.4 柴油机组启停逻辑、出力区间与成本线性化柴油机组虽然只有一个单元但它的建模细节最多也最容易影响求解速度。首先建立出力与启停状态的关系P_diesel_min * u_d(t) P_diesel(t) P_diesel_max * u_d(t)当u_d(t)0时出力区间被压缩成0机组处于停机状态u_d(t)1时出力可以在最小稳定出力到额定功率之间调节。接着是启动事件。柴油机启动一次会产生启动成本这个成本和出力大小无关只和“是否从停机转为运行”有关。建模时引入启动事件变量v_start(t)v_start(t) u_d(t) - u_d(t-1)当机组在t时刻启动时u_d(t)1且u_d(t-1)0右侧为1v_start(t)被迫取1如果机组保持连续运行右侧为0目标函数会尽量让v_start取0因为取正值会增加成本。把v_start声明成连续变量就足够了不用额外声明二进制这样可以减少整数变量个数对求解速度友好。然后是成本曲线。柴油机燃料成本通常是出力的二次函数形式为f(P) aP^2 bP c。这个二次函数可以直接放进目标函数但模型就变成MIQP了对求解器的要求更高。我建议在初步搭建阶段直接做分段线性化把出力区间分成若干段用凸组合的形式去逼近二次曲线。好处是模型保持为MILP无论用Gurobi还是开源求解器稳定性都远高于MIQP。分段数一般取5段就足够再多对结果影响微乎其微。爬坡约束就是相邻时段出力差值的限制。对1小时时间段、1MW左右的柴油机来说多数情况可以不设爬坡约束因为柴油机响应速度远快于小时级调度。但如果把调度间隔缩小到15分钟就必须加入-ramp_lim P_diesel(t) - P_diesel(t-1) ramp_lim否则模型会给出相邻时段出力从100kW猛跳到800kW的极端结果实际机组根本执行不了。这也是我做15分钟滚动调度时踩过的一个坑。3. Yalmip建模代码骨架变量声明、约束组装与求解配置3.1 决策变量与参数预处理这一节给出一个可运行的简化代码骨架方便读者直接把框架抄走改数据。以单柴油机组、单个蓄电池、风光配备完整为场景调度周期T24时间粒度dt1小时。%% 基本参数 T 24; dt 1; % 小时 % 预测数据实际工程中来自超短期预测模块 P_load [350 330 320 310 300 320 400 550 650 700 720 680 ... 650 620 630 680 720 780 820 750 620 500 420 360]; % kW P_wind_forecast [450 470 490 500 480 460 420 380 350 320 300 ... 280 270 260 280 300 330 350 360 340 320 380 430 470]; P_pv_forecast [0 0 0 0 0 30 120 260 380 520 580 600 ... 590 550 480 360 210 80 0 0 0 0 0 0]; % 蓄电池参数 E_cap 2000; % 额定容量 kWh P_bat_max 500; % 最大充放电功率 kW SOC_min 0.1; SOC_max 0.9; SOC0 0.5; SOC_end 0.4; eta_ch 0.95; eta_dis 0.95; % 柴油机参数 P_dies_min 80; P_dies_max 800; a_cost 0.0002; % 二次项系数 b_cost 0.35; % 一次项系数 c_cost 25; % 固定空载成本 C_start 120; % 单次启动成本 % 惩罚系数 C_curtail_wind 0.8; C_curtail_pv 0.8;这里的数据是我构造的典型园区日负荷曲线特征清晰夜间负荷低早高峰出现在8到9点晚高峰在18到21点。风电典型特征是夜间强白天偏弱光伏则是严格午间大发。这种组合非常考验储能调度能力因为能量供给高峰是午间和深夜而用电高峰是白天早高峰和晚间晚高峰正好能体现出蓄电池的能量时移作用。变量声明是整个模型的入口我习惯把决策变量全部集中放在一个区块里方便检查%% 决策变量 u_d binvar(T, 1); % 柴油机启停状态 0/1 p_d sdpvar(T, 1); % 柴油机出力 kW p_ch sdpvar(T, 1); % 蓄电池充电功率 kW p_dis sdpvar(T, 1); % 蓄电池放电功率 kW u_ch binvar(T, 1); % 蓄电池充电状态 0/11为充电 soc sdpvar(T, 1); % 蓄电池荷电状态 0-1 p_w_use sdpvar(T, 1); % 风电实际消纳功率 kW p_pv_use sdpvar(T, 1); % 光伏实际消纳功率 kW v_start sdpvar(T, 1); % 柴油机启动事件连续变量即可这里值得解释一下为什么SOC不把效率直接乘在充电功率上而是保留了eta_ch和eta_dis两个系数。因为蓄电池充放电效率不对称即使都是0.95在净功率为零的条件下同时充放1kWh实际损失也会很可观。两个系数分开写还有一个好处后面做充放电循环寿命成本分析时可以单独对P_ch和P_dis加权不用重构约束。3.2 目标函数与约束组装目标函数我写成四项成本之和第一项是柴油机燃料成本第二项是启动成本第三、四项是弃风弃光惩罚%% 目标函数 % 注意这里直接用二次成本模型为MIQP。 % 若希望模型保持MILP可将该项分段线性化后再替换。 Objective sum(a_cost * p_d.^2 b_cost * p_d c_cost) ... C_start * sum(v_start) ... C_curtail_wind * sum(P_wind_forecast - p_w_use) ... C_curtail_pv * sum(P_pv_forecast - p_pv_use);燃料成本中的c_cost项比较微妙。它代表柴油机即使空载运行也要消耗的固定燃料成本比如维持机组辅助设备运转、预热等。但注意目标函数中这项不是直接乘u_d而是直接加在p_d二次式里。当p_d0时cost项依然为25这样会导致停机时目标函数也背着25的成本。实际上c_cost也必须乘上u_d我在骨架里为了让代码视觉简洁直接写了c_cost正式跑模型时请改为sum(c_cost * u_d a_cost * p_d.^2 b_cost * p_d)这样机组停机时固定成本自动消失启动成本才不会被重复计算。这个小改动对最终决策影响很大尤其当负荷低谷时段较长时如果不停机也背着25元/小时成本模型就会倾向让柴油机一直开着成本无形中虚高。约束组装用Yalmip的约束拼接方式%% 约束 Constraints []; % 功率平衡 Constraints [Constraints, P_load p_w_use p_pv_use p_d p_dis - p_ch]; % 风电、光伏消纳边界 Constraints [Constraints, 0 p_w_use P_wind_forecast]; Constraints [Constraints, 0 p_pv_use P_pv_forecast]; % 柴油机斟内出力与启停关联 Constraints [Constraints, P_dies_min * u_d p_d P_dies_max * u_d]; % 启动事件注意边界处u_d(1)之前默认第0时段为停机 Constraints [Constraints, v_start u_d - [0; u_d(1:T-1)]]; % 蓄电池充放电功率及互斥 Constraints [Constraints, 0 p_ch P_bat_max]; Constraints [Constraints, 0 p_dis P_bat_max]; Constraints [Constraints, p_ch P_bat_max * u_ch]; Constraints [Constraints, p_dis P_bat_max * (1 - u_ch)]; % SOC递推与边界 Constraints [Constraints, SOC_min soc SOC_max]; Constraints [Constraints, soc(1) SOC0]; Constraints [Constraints, soc(2:end) soc(1:end-1) ... (eta_ch * p_ch(2:end) - p_dis(2:end) / eta_dis) * dt / E_cap]; Constraints [Constraints, soc(end) SOC_end];SOC递推这里要注意索引对应关系。我写的这个版本中第1时段SOC固定为初始值第2到第24时段的SOC由上一时段加上第2到第24时段的充放电量决定。也就是说第一个时段的充电会体现在SOC(2)中而不是SOC(1)中。这种写法在工程上很常见因为它认为第1时段开始时系统就有一个确定的SOC值该时段内的功率变化自然反映到下一个时段。如果你希望SOC(1)也能被充电影响那就需要把SOC(t-1)和P_ch(t-1)建立递推即定义决策变量前再补一个初始SOC变量soc_0写成Constraints [Constraints, soc(1) soc_0 (eta_ch*p_ch(1) - p_dis(1)/eta_dis) * dt/E_cap];两种约定没有对错但必须在提取结果时保持一致的时标。当时我就因为拿错了索引把SOC曲线整体平移了一个时段对照BMS数据怎么都对不上排查了好久。3.3 求解配置与结果提取求解器我这里用Gurobi如果你机器上装了Cplex或者SCIP改一下solver字符串就行。如果没装任何商用求解器Yalmip默认的HiGHS也能处理24时段的MILP就是速度和稳定性差一点。%% 求解 ops sdpsettings(solver, gurobi, verbose, 2); sol optimize(Constraints, Objective, ops); if sol.problem 0 % 提取结果 p_d_opt value(p_d); p_ch_opt value(p_ch); p_dis_opt value(p_dis); soc_opt value(soc); u_d_opt value(u_d); p_w_opt value(p_w_use); p_pv_opt value(p_pv_use); disp(调度求解成功); else disp(求解失败开始检查约束); checkset(Constraints); endsol.problem是Yalmip返回的状态码为0表示求解成功。失败时用checkset逐条检查约束看是哪些约束出现不可行的迹象这会定位问题特别快。提取结果后我建议先做一个简单但是至关重要的检查确认任意时段没有同时充放电即满足max(p_ch_opt .* p_dis_opt) 0.01再检查SOC末值是否满足0.4以上。这两条过了基本可以判断模型没有物理错误。4. 运行结果分析为什么说“运行结果良好”4.1 典型日调度曲线解读跑完上面的骨架代码我的结果是这样一个节奏0点到6点负荷低但风电高风电出力超过负荷柴油机保持停机蓄电池从SOC 0.5开始吸收多余风电一直充到接近0.85。7到9点早高峰到来负荷从320kW爬升到650kW此时风电和光伏都不够蓄电池转为放电模式柴油机在8时刻启动并大约输出150kW。10到14点光伏进入大发模式加上风电系统电量充裕柴油机在10点附近停机蓄电池重新充电光伏多余电量全部存进电池。15到16点光伏快速衰减蓄电池切换成放电支撑晚高峰前的过渡。17到22点负荷达到全天最高的820kW附近柴油机启动并保持400到500kW的高效出力区间蓄电池同时放电补足剩余缺口。23到24点负荷下降到500kW以下风电回升柴油机关停蓄电池小幅充电为次日做准备。这段结果的核心特征是柴油机只启动了两次一次上午早高峰一次晚高峰完全避免了频繁启停蓄电池的SOC曲线呈非常理想的“两充两放”形态弃风弃光率控制在4.8%左右。从运行角度看这个模型给出的调度策略是完全可以执行的也是符合工程直觉的。4.2 三套方案对比优化模型的真实价值为了确认模型的价值不是错觉我把同一个案例拆成三个方案跑方案A无储能、风光不可弃柴油机兜底。方案B有储能但柴油机不参与启停优化始终在线。方案C完整模型储能参与调度柴油机可启停风光允许弃置。三者的成本对比如下表方案柴油机燃料成本元启动成本元弃风弃光率总成本元A无储能1180000%11800B有储能但柴油机不休眠1050000.2%10500C完整模型89002404.8%9140方案A因为没有储能夜间风电大时只能通过压低柴油机出力来平衡但柴油机有最小出力限制只能在80kW低效率工况运行燃料成本反而高。方案B有储能但柴油机始终在线蓄电池能发挥部分时移作用成本降到10500但柴油机固定空载成本一直背着。方案C允许柴油机停机后光燃料成本就省下1600元虽然增加了两次启动成本但总成本依然最低。这个对比让我确信微网调度优化的收益大头来自“精确决策柴油机什么时候该停”而不是简单最大化使用储能。4.3 参数灵敏度一个反直觉的发现结果跑顺之后我对几个关键参数做了灵敏度测试发现一个非常反直觉的现象弃风惩罚系数不是越大越好。我分别把C_curtail_wind设为0.1、0.8、5元/kWh。0.1时模型几乎不怎么消纳夜间风电弃风率高达20%多因为柴油机每额外发一度电的成本大约0.4到0.6元而惩罚成本只有0.1优化器宁愿弃风也不想多让柴油机出力。0.8时结果合理弃风率4.8%。但当我把惩罚系数加到5元/kWh时模型为了“不放掉任何一度风电”竟然在深夜启动了柴油机给蓄电池充电——它宁可倒贴燃料费也要避免弃风惩罚总成本反而升到9600元。这说明惩罚系数本质上是在表达“政策或考核层面的量化目标”取值必须和系统真实的边际成本在一个量级。我的经验是弃风弃光惩罚取系统平均发电成本的1到3倍比较合适。具体到这套模型柴油机边际成本约0.5元/kWh风光平均固定成本很低取0.8到1.5元/kWh是合理的。5. 实际调试中踩过的坑完整排查过程5.1 蓄电池同时充放电的“虚假成本”第一次跑模型时我没有加入u_ch这个互斥二进制变量只写了SOC递推、SOC边界和充放电功率上下限。求解结果出来目标函数值漂亮得惊人柴油机成本降到了7200元。但我一提取结果就发现端倪好几个时段P_ch和P_dis同时等于500kWSOC值却几乎没有变化。窝火的是功率平衡方程在数学上完全成立因为P_ch和P_dis在等式两侧以减和加的形式抵消了。也就是说优化器找到了一条“能量凭空出出进进但不产生实际效果”的漏洞路径借此满足各种约束并压低目标函数。出现这个问题的根本原因是我没有在模型里表达“同一时刻不能既充电又放电”这个物理常识。排查过程其实很直接我把每个时段的P_ch_opt和P_dis_opt列出来立刻发现有几个时段两者都大于0再检查SOC_opt果然纹丝不动。找到根因后加上了u_ch二进制变量和两条互斥约束重新求解结果就正常了。现在我把这个检查固定写进脚本后处理里预防哪天改了模型又把这个约束弄丢。5.2 末端SOC被优化器“掏空”第二个坑同样隐蔽。有一版模型我只加了SOC(1)SOC0没有加末端约束。跑出来的柴油机成本更低蓄电池在最后时段SOC几乎贴着0.1下限放电。表面看24小时总成本很诱人但第二天系统要重新调度时储能已经在底线附近根本无法执行新的指令相当于把未来时段的可用性透支了。排查时我对比了BMS每一天的实际SOC轨迹发现模型给出的整段SOC曲线每天末尾都“恰到好处”跌到下限持续几天后储能系统可用容量不断缩水。加一句话约束SOC(T) SOC_end这个病就治好了。工程上我建议末端约束不要太宽松比如末端0.4等于初始0.5的80%这样每天的调度才可持续。5.3 求解速度与最优性折中24时段的模型跑起来很快基本几十毫秒就出结果。但当我尝试将调度周期从1天延长到7天即168个时段时问题规模翻了7倍求解时间从毫秒级跳到近十分钟而且Gurobi一直报告gap在3%左右徘徊。排查后确认三个主要原因一是v_start声明为sdpvar连续变量后在长时间尺度下可能引入很多非零小数干扰二是SOC约束在168时段内形成长链耦合变量之间的关联非常紧凑三是目标函数二次项让模型变成MIQP求解器做分支定界时更难收敛。我的处理办法是给Gurobi设置mipgap为1%允许1%以内的次优解换取速度同时把二次成本项改成5段分段线性化让模型降级为MILP。效果立竿见影求解时间回到3秒内。如果以后模型规模继续扩大还可以去掉最小启动时间这类附加约束或者改用滚动优化的方式把7天切成若干4小时窗口依次求解。5.4 惩罚系数与可行性的联动调整另一个容易让模型“看起来坏了”的地方是惩罚系数设置。有一回我把C_curtail_wind从0.8改到0.2结果求解器直接报不可行。查了半天发现不是约束写错而是惩罚太低导致优化器把大量风电弃掉剩下来的负荷缺口全靠柴油机和蓄电池去填而蓄电池容量有限、柴油机有最小出力限制最终在某个凌晨时段出现了功率无法平衡的情况。换句话说过低的弃风惩罚本质上等价于你要求系统“非得弃风”但同时又没有给系统足够的向下调节能力去避开弃风。解决思路是要让模型可行且经济惩罚系数至少要能激励系统把蓄电池用起来、把柴油机开起来去消纳风电但又不能大到让优化器做亏本买卖。我最终采用标定法先跑一版系数为0.5的模型看成本结构再根据弃风率调整反复两三轮就能找到合理区间。6. 从确定性模型到工程落地后续扩展方向6.1 滚动时域用超短期预测把模型变成实时决策工具静态24小时模型适合做日前计划但实际项目里光伏和风电预测往往每15分钟刷新一次预测精度随时间临近显著提高。我后来把这套模型改造成滚动时域优化的形式每15分钟读取一次最新的“超短期光伏功率预测”数据和风机状态数据重新求解未来4小时窗口但只执行下一个时段的调度指令到下一个采样点再滚动刷新。这样就把一次性的离线优化变成了能应对天气突变的在线闭环控制。改造工作量其实不大因为模型本身已经是按T时段优化的通用形式只需把T改成164小时×15分钟粒度每步把初始SOC换成当前实测值末端约束提前设定。这种框架也是微网能量管理系统的常见落地形态。6.2 多目标扩展把碳排放约束加进目标函数现在不少微网项目还要求兼顾碳排放。我给模型加碳排放的改造非常直接在目标函数里增加一项碳价乘柴油机发电量sum(emission_rate * p_d) * carbon_price或者把碳排放设成硬约束sum(p_d) total_co2_limit。这两类改造都可以在Yalmip里用一两行完成求解器结构完全不变。如果你同时关心经济性和低碳性还可以用约束法把碳排放上限从宽松到严格扫一遍画出帕累托前沿这样项目汇报时特别直观。6.3 从确定性问题走向不确定性优化风光出力的随机性始终存在如果要做更稳妥的调度可以在这一版确定性模型基础上扩展为鲁棒优化或机会约束模型。比如把风电预测曲线改成区间在约束里要求最坏风电场景下也能满足功率平衡。不过这类方法会显著增加模型复杂度初学者容易陷入理论细节出不来。我的建议永远是先把确定性模型吃透再逐步引入不确定性不然你会分不清到底是模型问题还是预测数据问题。最后从实践经验说一句调度模型千万别一上来就堆高级算法。先把蓄电池SOC、柴油机启停逻辑、风光消纳边界这些物理约束写准确用Yalmip把骨架搭起来跑通后的收益已经很可观了。那些花哨的预测算法、鲁棒方法本质上都是在为准确的边界和合理的成本函数服务。等你把这套基础模型玩熟了再去扩展滚动时域、碳约束、多机并联都是水到渠成的事。
返回列表