ARTICLE DETAIL

资讯详情

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

风光储联合优化:用Matlab+Gurobi破解微电网容量配置难题

风光储联合优化:用Matlab+Gurobi破解微电网容量配置难题 前阵子帮一个园区做微电网规划一开始按老思路把风电、光伏、储能分开算容量结果报告刚出来就被领导问住了风光装机翻了一倍弃风弃光反而涨了三成储能利用率却低得吓人。这个问题憋了我好几天最后把所有设备放进一个模型里用MatlabGurobi做风光储联合优化才算把账理清楚。这篇博文就围绕这条线展开先讲清楚独立运营为什么不行再拆解联合优化的数学建模思路最后给出可参考的代码框架、求解验证和避坑经验。无论你是正在做数学建模竞赛、研究微电网经济调度还是实际项目里要写规划方案这套“从独立到联合”的建模方法都能直接用。1. 园区微电网规划的两种视角独立运营模型为什么不是最优解1.1 园区微电网里到底有哪些设备和决策园区微电网通常包含风机、光伏、储能、负荷以及和外部电网的联络线。规划阶段的核心问题很简单风机装多少千瓦、光伏装多少千瓦、储能功率和容量配多大才能让整个系统在满足用电需求的同时综合成本最低。很多人习惯把这个问题拆成几个独立的子问题先算风电装机再算光伏装机最后按负荷低谷和尖峰差来配储能。这种方法看上去清晰实际上忽略了三个设备之间的强耦合。举个例子光伏午间出力大如果负荷午间小多余电量要么卖给电网要么存进储能储能配多了光伏弃光可以减少但储能成本上升储能配少了尖峰时段还得靠买电网电。风机晚上出力高如果负荷夜间低储能能不能存住这部分风电又会影响风电装机是否划算。独立运营时每个设备只盯着自己的发电或充放电特性做规划互相之间没有反馈最后得到的“局部最优”合在一起往往不是系统最优。1.2 独立运营的典型做法和三个典型问题独立运营在项目里通常表现为三种做法。第一种是“按比例分摊”比如负荷年用电量是1000万kWh让风电承担30%、光伏承担30%剩下40%买电然后根据当地等效利用小时数反推装机容量。第二种是“单设备投资回收期”分别计算风电、光伏、储能在各自电价和补贴下的投资回收期凡是回收期小于N年的设备就装到资源上限。第三种是“储能为新能源让路”先定风光容量再把储能的功率按新能源装机的10%~20%估算容量按2小时设置。这三种做法都会带来典型问题。由于没有把“什么时候发电、什么时候用电、电价怎么波动”放到同一个时间轴上考虑很容易出现新能源装机越多、弃电率越高的情况储能容量则往往偏保守或偏激进偏保守时高峰买电成本盖不住储能投资偏激进时储能大部分时间闲置投资回报极差。还有一类问题更隐蔽独立运营下的“最优”可能互相冲突比如风电为了多发电要求储能容量大光伏为了降低弃光也需要储能但两者的出力高峰时间不同对储能的充放电需求是互相竞争的单独算出来的储能配置完全没法用。1.3 联合优化到底“联合”了什么联合优化的本质是把容量层决策和运行层决策放进同一个优化问题。容量层决定风机、光伏、储能的装机规模和投资成本运行层决定每个时段每台设备该怎么出力、储能该充还是该放、买电和卖电怎么安排。这两层原本就是嵌套的容量配置决定运行可行域运行策略又决定容量投资的收益。只有把它们同时求解才能得到真正意义上的最优规划方案。从数学上看联合优化是一个带连续变量和0-1变量的混合整数优化问题规模比独立运营大好几倍。这也是为什么很多团队用Matlab优化工具箱跑不动、最后换Gurobi的原因。Gurobi在求解大规模线性规划、混合整数规划上效率很高尤其是处理储能充放电状态、买卖电状态这类0-1变量时明显比通用优化工具箱更适合。2. 风光储协同规划的数学建模变量、约束与目标函数的设计逻辑2.1 决策变量怎么定容量层与运行层分开列建模的第一步是明确决策变量。容量层变量只有4个风电装机容量、光伏装机容量、储能额定功率、储能额定容量。运行层变量跟时间步长和场景数有关如果采用典型日缩聚每个典型日有24个时段每个时段都需要风电实际出力、光伏实际出力、储能充电功率、储能放电功率、与电网购电功率、向电网售电功率、弃风功率、弃光功率、荷电状态SOC以及两个描述储能状态的0-1变量。至于要不要加“是否买电”和“是否卖电”的0-1变量取决于联络线模型怎么建立。有人会问为什么需要0-1变量因为储能不能同时充电和放电购电和售电也不能同时发生。如果不加0-1变量模型可能会利用同时充放电制造“伪能量流”导致目标函数虚低但实际工程里根本做不到。这类约束在数学上属于“互补性约束”需要借助二进制变量和大M法线性化。2.2 目标函数投资成本与运行成本要放在同一张表上规划期的目标是年化总成本最小包括设备投资年值、年运行维护成本、年购电成本减去年售电收益。设备投资成本不能直接把设备总价放进目标函数因为风电寿命20年、储能寿命可能只有10年需要用年值法折算成年均投资成本。简单做法是把静态投资乘以资本回收因子资本回收因子CRF r×(1r)^n / ((1r)^n − 1)其中r是折现率n是设备寿命。目标函数可以写成最小化 f 投资成本(风光储) 运维成本 购电成本 − 售电收益 弃电惩罚弃电惩罚不是必须的因为弃风弃光本身已经通过“实际出力小于可用出力”体现在功率平衡约束里但如果在实际项目中希望优先消纳新能源可以加一个很小的惩罚系数避免模型因为经济性原因无故弃电。2.3 约束条件功率平衡、储能SOC、联络线一个都不能少核心约束包括四类。第一类是逐时功率平衡风电实际出力光伏实际出力储能放电功率购电功率 负荷功率储能充电功率售电功率弃风功率弃光功率。第二类是新能源出力上限每个时段风电实际出力和弃风功率之和等于风电装机容量乘以该时段归一化出力系数光伏同理。第三类是储能运行约束SOC动态方程、SOC上下限、充放电功率上限、充放电互斥约束、初末SOC一致约束。第四类是电网联络线约束购电功率和售电功率都必须小于联络线最大传输功率并且不能同时为正。注意功率平衡里的单位必须统一。装机容量常用kW电量常用kWh时间步长如果是1小时功率(kW)乘时间(h)就是电量(kWh)但如果时间步长是15分钟功率乘0.25h才是电量建模时特别容易漏掉这个系数。2.4 为什么这是个MILP问题为什么选择Gurobi因为存在储能充放电互斥、购售电互斥这两类0-1逻辑约束整个问题属于混合整数线性规划MILP。此外如果目标函数或约束里有储能损耗项、电压潮流等二次项还会升级为MIQP或MINLP。微电网容量规划常见模型还是MILP因为线性化之后求解稳定、全局最优性有保证。选Gurobi而不是其他求解器的原因很简单一是0-1变量规模大时Gurobi的割平面和启发式算法成熟几十万个变量也能在可接受时间内收敛二是学术版License申请方便Matlab调用接口顺手三是和Yalmip配合建模效率比自己写约束矩阵高很多。实际项目里我一般用Yalmip搭模型框架再用Gurobi做后端求解这样代码可读性和求解速度都能兼顾。3. MatlabGurobi建模环境准备安装、验证与接口选型3.1 安装Gurobi与申请学术LicenseGurobi对在校学生和教师提供免费的学术License申请入口在官网直接注册学校邮箱。重点提醒申请下来的License一般有机器限制必须安装后先绑定当前电脑换机器需要重新申请或更新。安装时选择与操作系统匹配的版本解压到固定目录比如Windows下的C:\gurobi\win64。安装完不建议把整个文件夹塞进Matlab路径只需要在Matlab里运行gurobi_setup.m让Matlab能找到gurobi.mexw64等接口文件。有些同学安装后报“Unable to load gurobi”大概率是License没有放到正确位置或者系统环境变量GRB_LICENSE_FILE没有指向gurobi.lic。Windows下可以把License文件放到用户目录并在系统环境变量里手动指定重启Matlab再验证。3.2 Matlab调用Gurobi的三种方式第一种是直接用Gurobi官方提供的Matlab接口构建model结构体调用gurobi(model, params)。第二种是使用Yalmip工具包用sdpvar和binvar声明变量最后用optimize(Constraints, Objective, sdpsettings(solver,gurobi))求解。第三种是通过MATLAB优化工具箱的intlinprog再指定求解器但不推荐。我个人的建议是写规划模型用Yalmip做对比测试时用官方接口。Yalmip的符号建模方式非常贴近数学表达方便维护和修改尤其适合从独立运营模型改造成联合优化模型。官方接口更适合性能调优和嵌入到生产系统里因为省掉了Yalmip的变量解析开销。如果只是做研究或者比赛Yalmip Gurobi足够用。3.3 最小验证Demo先跑通一个简单的LP/MILP安装完成后建议先做最小验证。Yalmip版测试代码x sdpvar(2,1); Constraints [x(1) x(2) 1, x 0]; Objective 2*x(1) 3*x(2); ops sdpsettings(solver,gurobi,verbose,0); optimize(Constraints, Objective, ops); value(x)如果顺利得到[0;1]说明Yalmip Gurobi链路是通的。官方接口版测试可以用结构体写一个类似问题重点看result.status是否返回OPTIMAL。这一步看起来简单但能排查掉80%的环境问题。4. 从独立到联合优化Gurobi模型代码的逐步实现与关键API解读4.1 数据准备典型日曲线、负荷与电价参数建模之前必须准备好输入数据。最少需要三类园区24小时负荷曲线风机和光伏的归一化出力曲线以及分时购售电价。归一化出力曲线的含义是“每kW装机容量在某个时段的实际出力”比如光伏在中午出力系数为0.85意味着1kW光伏在此时可发0.85kW电。如果有多组典型日数据还要加上“典型日权重”用于把单个典型日成本加权折算到全年。经济参数包括设备单位投资成本、运维成本、储能充放电效率、折现率、设备寿命、最大联络线功率、SOC上下限等。这些参数在建模阶段不要写死在代码里最好放在一个结构体变量Param里后面对比方案时改参数非常方便。4.2 独立运营模型的代码骨架先看它缺了什么独立运营模型可以拆成三个独立子问题。风电子问题只优化风电容量光伏子问题只优化光伏容量储能子问题则在一组预设的风光容量下优化储能功率和容量。典型的储能独立优化代码如下% 储能独立优化示例 cap_p sdpvar(1); cap_e sdpvar(1); P_ch sdpvar(1,T); P_dis sdpvar(1,T); SOC sdpvar(1,T1); u_ch binvar(1,T); u_dis binvar(1,T); Constraints []; for t 1:T Constraints [Constraints, P_ch(t) 0, P_ch(t) cap_p*u_ch(t)]; Constraints [Constraints, P_dis(t) 0, P_dis(t) cap_p*u_dis(t)]; Constraints [Constraints, u_ch(t) u_dis(t) 1]; Constraints [Constraints, SOC(t1) SOC(t) eta_ch*P_ch(t)*dt - P_dis(t)/eta_dis*dt]; end Constraints [Constraints, SOC(1) 0.2*cap_e, SOC(T1) 0.2*cap_e]; Constraints [Constraints, 0.2*cap_e SOC, SOC cap_e]; Objective c_p*cap_p c_e*cap_e sum(om_ess*(P_ch P_dis)*dt);这段代码的问题在于储能不知道风电和光伏的具体出力曲线也不知道负荷和电价的全貌它只能在自己“削峰填谷”的独立目标下选择容量逻辑上天然缺失了“多存一单位风电可以少弃多少电”这类协同价值。4.3 联合优化模型的构建把容量层和运行层一起声明联合优化模型只需要一个优化问题把所有容量变量和运行变量放进去。核心代码框架如下T 24; dt 1; cap_wt sdpvar(1); cap_pv sdpvar(1); cap_p sdpvar(1); cap_e sdpvar(1); P_wt sdpvar(1,T); P_pv sdpvar(1,T); curtail_wt sdpvar(1,T); curtail_pv sdpvar(1,T); P_ch sdpvar(1,T); P_dis sdpvar(1,T); P_buy sdpvar(1,T); P_sell sdpvar(1,T); SOC sdpvar(1,T1); u_ch binvar(1,T); u_dis binvar(1,T); y_import binvar(1,T); Constraints []; % 功率平衡 for t 1:T Constraints [Constraints, P_wt(t)P_pv(t)P_dis(t)P_buy(t) ... P_load(t)P_ch(t)P_sell(t)curtail_wt(t)curtail_pv(t)]; % 新能源实际出力上限 Constraints [Constraints, P_wt(t)curtail_wt(t) cap_wt*wind_cf(t)]; Constraints [Constraints, P_pv(t)curtail_pv(t) cap_pv*pv_cf(t)]; % 储能充放电互斥 Constraints [Constraints, P_ch(t) 0, P_ch(t) cap_p*u_ch(t)]; Constraints [Constraints, P_dis(t) 0, P_dis(t) cap_p*u_dis(t)]; Constraints [Constraints, u_ch(t)u_dis(t) 1]; % 储能SOC Constraints [Constraints, SOC(t1) SOC(t) eta_ch*P_ch(t)*dt - P_dis(t)/eta_dis*dt]; % 购售电互斥和联络线限制 Constraints [Constraints, P_buy(t) 0, P_buy(t) P_line_max*y_import(t)]; Constraints [Constraints, P_sell(t) 0, P_sell(t) P_line_max*(1-y_import(t))]; end % SOC边界和初末约束 Constraints [Constraints, 0.2*cap_e SOC(1:T), SOC(1:T) cap_e]; Constraints [Constraints, SOC(1) 0.2*cap_e, SOC(T1) 0.2*cap_e]; % 目标函数 Objective c_wt*cap_wt c_pv*cap_pv c_p*cap_p c_e*cap_e ... sum(om_wt*P_wt om_pv*P_pv om_ess*(P_chP_dis))*dt ... sum(buy_price.*P_buy - sell_price.*P_sell)*dt; ops sdpsettings(solver,gurobi,verbose,2,gurobi.MIPGap,0.01); optimize(Constraints, Objective, ops);这段代码有几个关键点。第一P_wt和P_pv必须是连续变量但它们的上限由cap_wt和wind_cf的乘积决定这样就把容量变量和运行变量耦合成整体。第二y_import是购售电互斥的0-1标志变量比引入两个互斥约束更直观。第三SOC(1)和SOC(T1)的初末约束是必须的否则模型会在一个调度周期内把储能能量“透支”光配置结果会失真。第四目标函数里买电和售电使用分时价格向量直接用矩阵乘法代替循环在Matlab里更快。4.4 调用Gurobi求解与结果后处理求解完用value()取出变量值然后做后处理。后处理至少要输出装机容量表、典型日运行曲线、年化成本分项、弃风弃光率、储能循环次数、最大购电功率等。如果是报告我还会专门生成一张“储能SOC曲线叠加负荷曲线”的图评审专家最喜欢看这种图直观说明储能有没有干活。Gurobi求解器本身会输出大量日志包括节点数、上下界、MIP Gap。在sdpsettings里设置gurobi.MIPGap,0.01可以让求解器在相对最优间隙低于1%时提前停止显著减少求解时间。如果模型很大还可以设置gurobi.TimeLimit,300。5. 结果对比与灵敏度分析联合优化收益到底从哪里来5.1 独立结果与联合结果会差多少我拿一个典型园区数据进行过对比。负荷峰值2MW日用电量2.8万kWh工业分时电价峰平谷价差约0.8元/kWh储能单位投资成本按1200元/kWh计算。独立运营模型给出的配置大约为风电800kW、光伏600kW、储能300kW/600kWh年化总成本约320万元弃风弃光率约18%。联合优化模型给出的配置大约为风电650kW、光伏750kW、储能400kW/800kWh年化总成本约275万元弃风弃光率降到7%。这不是说联合优化一定会同时减少风电、增加光伏趋势取决于是晚间还是午间缺电、哪个地方光照资源更好。关键在于独立运营里风电和光伏都在争抢“负荷满足率”这个指标储能则是事后补救联合优化里储能既帮风电消纳也帮光伏消纳它会在充放策略上自动平衡两边的边际价值。最终结果是风光总容量更低但发电利用率更高。5.2 把收益拆开看储能消纳了多少弃电又转移了多少负荷联合优化收益可以拆成三个渠道。第一是峰谷套利收益储能低谷买电、高峰放电直接降低购电成本。第二是新能源消纳收益储能把午间低价甚至负价格的光伏电存起来晚上释放减少了弃电损失。第三是容量替代收益因为储能优化了负荷曲线风电和光伏的实际利用率提高等量新能源装机提供了更多有效电量因此不需要装那么多风机或光伏也能满足负荷。在报告里做敏感性分析时我喜欢把储能目标函数中的“边际价值”单独输出也就是储能每增加1kWh容量对系统年化成本的降低值。如果这个值接近储能边际投资成本说明储能配置到了经济平衡点如果远小于投资成本说明储能可能配多了。5.3 灵敏度分析峰谷价差、储能成本、新能源渗透率三个参数最值得做灵敏度分析。第一个是峰谷价差价差从0.5元/kWh拉大到1.2元/kWh时储能最优容量往往成倍增长但增长不是线性的因为储能容量越增加边际套利小时数越少。第二个是储能单位成本成本从1500元/kWh降到800元/kWh时不仅储能容量上升风电和光伏的配比也会变化可能允许更多弃电但总成本更低。第三个是可再生能源渗透率约束如果规定园区可再生能源电量占比不能低于40%联合优化会自动增加新能源装机并同步增加储能用来平抑新能源波动但渗透率超过某个阈值后边际成本会快速上升。灵敏度分析还有一个作用检验模型的鲁棒性。如果改变某个参数后容量配置结果出现剧烈跳变往往说明模型少了约束或者目标函数里某些惩罚项系数不合理需要回头检查。6. 建模过程中容易踩的坑与调优经验6.1 时间粒度和典型日缩聚怎么选才不会被骂很多新手一上来就用全年8760小时数据建模结果变量爆炸、求解时间变成几个小时最后还要为报告圆场。更合理的做法是用K-means聚类把全年缩聚成4~8个典型日并为每个典型日赋予权重。但聚类时要注意不能只看负荷和新能源出力的平均值要把极端场景单独保留比如全年最大负荷日、新能源出力最低的连续阴雨天、以及电价尖峰出现最多的那一天。如果不保留极端场景规划出来的容量很可能在峰荷时力不从心。6.2 储能SOC初末约束最容易被忽略、又最容易导致结果虚构有些代码不加SOC初末一致约束结果优化器会让储能从0开始在一个周期结束时刚好放完甚至实现“凭空发电”。这相当于给了系统一笔免费初始电量储能投资回报会虚高。我的习惯是令SOC(1)SOC(T1)0.2*cap_e这样即使模型有误差也不会影响储能的有效容量。SOC下限设在0.1~0.2而不是0是为了保护电池寿命同时也让模型更贴近实际运行策略。6.3 大M法和二进制变量有关的数值陷阱购售电互斥约束P_buy ≤ P_line_maxy和P_sell ≤ P_line_max(1-y)这里P_line_max就是大M。如果最大联络线功率是10MW而其他变量单位是kW会出现数量级差10^3Gurobi求解时容易警告数值问题。解决方案是统一单位或者把大M尽量缩小到“该变量在可行域内能被观测到的最大合理值”。另一个常见错误是给0-1变量和连续变量乘上超大系数比如投资成本是百万元运行成本是元目标函数里数值量级差10^6求解器精度会退化最好都统一到万元。6.4 Gurobi求解参数别一上来就默认设置默认参数能解小规模问题但对规模较大的微电网规划模型建议调整几个参数。MIPGap是最常用的设置0.01或0.005代表允许1%或0.5%的相对最优性间隙节省大量时间。TimeLimit设置300秒或600秒防止求解器陷入无穷节点。还有方法的调整如果模型主要是MILP用默认的branch-and-cut即可如果线性松弛很少可以尝试设置MIPFocus为2。不过这些参数都依赖具体模型我在每个项目里都会做一个小实验先在3个典型日上测试不同参数组合再放到完整模型上跑。6.5 独立运营和联合优化代码之间怎么复用从独立模型改成联合模型尽量把参数、约束函数、求解后处理拆成三个独立函数。我会写一个getData()读入所有参数一个buildConstraints()通过开关控制是否启用某个模块比如flagIndependent1时只生成风电子问题的SOC约束和功率平衡flagIndependent0时生成完整联合约束。这样对比方案时只需要切换开关不需要复制粘贴大段代码也能减少因为两套代码不一致导致的低级错误。6.6 汇报结果时最重要的两张图规划项目交付时数据表格再多都不如图直观。我通常只画两张图一张是联合优化后的典型日调度曲线横轴是24小时图上包含负荷曲线、风电出力、光伏出力、储能SOC、购电功率另一张是成本分摊堆叠柱状图柱状图按典型日权重展开能清楚看到投资成本、购电成本、售电收益、运维成本各占多少。这两张图一放评审基本不会再追问“设备配置是否合理”因为运行层面的电力平衡已经一目了然。踩过几次坑之后我现在做园区微电网规划都会坚持一个习惯先搭联合优化模型的骨架用最粗的时间粒度和最小的典型日集合跑通再逐步加约束、缩聚典型日。每加一个约束就重新对比一次独立运营与联合优化的结果差异。如果差异突然变大不是模型出bug就是遗漏了重要边界条件。规划模型不怕复杂怕的是每个模块单独看都对放在一起却总给不出一个能落地的容量配置。把“独立运营”和“联合优化”放进同一个代码框架里反复对比是让我快速发现问题、又能在截止日期前交付可靠方案的最好办法。
返回列表