news 2026/9/26 13:08:32

基于YALMIP的电热联合微电网优化建模与MATLAB实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YALMIP的电热联合微电网优化建模与MATLAB实现

做微电网优化的人基本都遇到过同一个问题,光伏和风电出力看天吃饭,电负荷和热负荷又各自波动,CHP机组、电锅炉、储能电池、蓄热罐一堆设备摆在那儿,到底让谁出力、出多少、什么时候充放,才能把一天下来的总运行成本压到最低?

这个问题的答案,就是“运行优化”。而含电热联合系统的微电网,本质上是一个典型的多时段耦合优化问题,需要用数学模型描述清楚,再交给MATLAB去求解。

这篇文章我从头到尾过一遍自己的实践思路:先讲清为什么电和热要联合优化,再梳理完整的目标函数和约束条件,接着给出基于YALMIP的建模代码骨架,最后聊几个真正能把结果跑稳的关键细节。适合正在做微电网方向研究、论文需要算例支撑、或者工程项目需要搭建能量管理算法的读者参考。

1. 电热联合微电网的优化问题长什么样:从物理系统到数学问题

1.1 为什么单独优化电、热一定吃亏

很多初做微电网优化的人会下意识地分成两个独立子问题:先算电力调度,再算热力调度,最后拼在一起。这种做法表面看起来没问题,但实际做一个算例就会发现,结果要么成本偏高,要么干脆不可行。

原因在于电和热在物理上就存在耦合设备。典型的就是热电联产机组(CHP),它一边发电动率,一边副产品热功率,两个出力之间由热电比牵制。把电力系统和热力系统分开建模,CHP的联合出力特性就被拆散了,优化器没法在“多发电顺便多产热”还是“少发电但用锅炉产热”之间做全局权衡。更麻烦的是储能环节,电池存电、蓄热罐存热,两者在时间维度上的互补能力完全不同,分开优化等于主动丢掉了一部分调节自由度。

只要系统里存在CHP、电锅炉这类电热转换或联产设备,电热联合优化就不是锦上添花,而是必须。这是整个课题的出发点。

1.2 典型拓扑与元件:一个可以拿来做优化研究的标准系统

我在项目中用到的系统结构不算复杂,但足够反映工程主流情况。包含以下几类设备:

  • 新能源电源:光伏阵列、风电机组,在调度周期内按预测曲线给定出力,属于不可控但可参与平衡的“准刚性”电源。
  • 热电联产机组:核心可控机组,输出电功率和热功率,两者之间存在可行域约束。
  • 电锅炉:消耗电能转换为热能,是电热耦合的另一条通道。
  • 储能电池:支持充电、放电,具备SOC动态递推约束。
  • 蓄热罐:储存热能,充放热功率受容量和功率上限约束。
  • 上级电网联络线:允许微电网从外部购电或售电,有功率上限约束。

这一套配置几乎覆盖了微电网领域所有常见的优化要素。各设备通过母线连接:电功率汇入电力母线,热功率汇入热力母线,两条母线通过CHP和电锅炉发生耦合。

1.3 优化目标不是单纯的“省电费”

大部分人第一直觉是把目标函数写成“总购电成本最小”,但工程项目里这只是其中一个部分。我之前在算例里见过只优化购电成本的方案,结果优化器会把电锅炉开到满负荷——因为夜里的光伏出力被强迫消纳了,不对,实际上是反过来的,如果购电成本是唯一的优化项,CHP的燃料成本变得无足轻重,整个系统出力结构就会失衡。

合理的做法是把目标函数定义为“系统总运行成本最小”,逐日滚动优化时至少包含:

  • 从上级电网购电的费用,减去售电收入;
  • CHP机组的燃料费用;
  • 储能电池的充放电损耗与设备维护折算成本;
  • 停发/弃光弃风的罚项,用来防止优化器给出不合理的弃新能源方案。

后面会在数学模型小节里逐项展开。

2. 建立模型之前必须先描清楚的数学边界

2.1 决策变量:哪些量该由优化器决定

优化问题首先要分清哪些是可优化变量、哪些是固定参数。我把变量按设备类型分开:

  • CHP机组:第t个时段输出的电功率 P_chp(t),热功率 H_chp(t),以及启停状态位(如果做混合整数规划则需要这一步)。
  • 电锅炉:消耗电功率 P_eb(t),对应产生热功率 η_eb * P_eb(t)。
  • 储能电池:充电功率 P_ch(t)、放电功率 P_dis(t),以及SOC状态量E_bat(t)。
  • 蓄热罐:充热功率H_tank_ch(t)、放热功率H_tank_dis(t),以及蓄热量状态量Q_tank(t)。
  • 联络线功率:从电网受入的功率 P_grid(t),如果系统支持倒送,需要增加一个售电变量。

所有变量按调度周期T个小时段离散化。T=24是标准的日前优化,我做项目时也常用T=96(15分钟一个点)来捕捉负荷峰谷和光伏间歇性。

2.2 目标函数:成本项拆解得越细,结果越可信

目标函数写成一天之内所有时段成本叠加:

min Σ_t [ c_grid(t) * P_grid(t) * Δt + c_fuel(P_chp(t)) * Δt + c_om_bat * (P_ch(t) + P_dis(t)) * Δt + c_om_eb * P_eb(t) * Δt + λ_waste * Σ弃光弃风 ]

其中:

  • c_grid(t)是分时电价,峰、平、谷三段,直接决定储能和CHP什么时候出力。
  • c_fuel(P_chp(t))是CHP燃料成本。严格建模应该是机组耗量特性曲线,二次函数甚至带机组阀点效应。不过在MATLAB里做线性规划时,通常分段线性化处理成近似线性函数;如果追求精度,可以保留二次项后交给二次规划求解器。
  • 维护成本统一按功率比例系数折算,数值不大但意义在于避免电池频繁充放和电锅炉高负荷运行,让优化结果更接近真实调度需求。
  • 弃风弃光罚项λ_waste要取一个显著高于正常发电收益的数值,否则优化器会倾向于“反正弃掉也没成本”而牺牲新能源消纳率。

2.3 约束条件的核心集合:功率平衡、储能动态、爬坡与接口限制

约束条件决定了优化结果是否真正可执行。我梳理最主要的几组。

电功率平衡约束

P_pv(t) + P_wt(t) + P_chp(t) + P_dis(t) + P_grid(t) = P_load(t) + P_ch(t) + P_eb(t)

这个式子的物理含义是:电源侧发出的电加上电网受入电,等于负荷消耗加上储能充电和电锅炉用电。

热功率平衡约束

H_chp(t) + η_eb * P_eb(t) + H_tank_dis(t) = H_load(t) + H_tank_ch(t)

热负荷由CHP余热、电锅炉供热和蓄热罐放热共同满足,蓄热罐充热视为消耗热功率。

CHP可行域约束

CHP机组的电、热出力不能随意取值,而是落在多边形可行域内。这是联产系统建模中最重要的约束组。简化的可行域通常是:

P_chp_min <= P_chp(t) <= P_chp_max H_chp_min <= H_chp(t) <= H_chp_max P_chp(t) + k1 * H_chp(t) >= C1 P_chp(t) + k2 * H_chp(t) <= C2

其中k1、k2、C1、C2来自机组特性曲线拟合。含义是将电、热出力限制在四边形或更复杂多边形内。这一组约束一旦写错,求解结果可能在物理上根本不可行。

储能电池约束

SOC递推方程:

E_bat(t+1) = E_bat(t) + (η_ch * P_ch(t) - P_dis(t) / η_dis) * Δt

同时满足:

0 <= E_bat(t) <= E_bat_max 0 <= P_ch(t) <= P_ch_max * u_ch(t) 0 <= P_dis(t) <= P_dis_max * u_dis(t) u_ch(t) + u_dis(t) <= 1

引入充电状态位u_ch和放电状态位u_dis后,问题变成了混合整数规划(MILP),求解难度明显上升,但结果更符合实际。如果只做连续线性规划,必须额外加充电、放电互斥的线性化约束,否则求解器可能会让电池同一时刻既充电又放电,求出毫无意义的“零和循环”。

蓄热罐约束

Q_tank(t+1) = Q_tank(t) + (η_tank_ch * H_tank_ch(t) - H_tank_dis(t) / η_tank_dis) * Δt

上下限约束:Q_min <= Q_tank(t) <= Q_max。蓄热罐相对电池而言,单位投资成本低,往往倾向于最大化利用其容量来平移热负荷,实现“白天CHP跟着电负荷多发电、余热存起来,晚上放热供采暖”。

爬坡约束与联络线约束

CHP的电功率爬坡限制:

|P_chp(t+1) - P_chp(t)| <= Ramp_chp * Δt

同样的约束也可以施加到蓄热罐和电池。联络线功率上下限:

0 <= P_grid(t) <= P_grid_max

如果允许倒送电,需要把P_grid拆成购电和售电两个非负变量,并加互斥或通过市场价的天然排斥来处理。

3. MATLAB+YALMIP实现优化模型:代码层次的工程化思路

3.1 工具箱选择:YALMIP加求解器的组合逻辑

在MATLAB里做优化建模,我强烈建议用YALMIP而不是直接自己写内点法或者手推KKT条件。不是说自己实现不行,而是YALMIP把符号建模、约束装配、求解器接口全部封装好了,你只需要把数学问题“翻译”成约束语句,剩余交给底层求解器处理。

底层求解器我的推荐是:

  • 问题规模不大、连续线性规划为主,直接用linprog。
  • 存在整数变量,比如储能充放状态位、机组启停位,优先用Gurobi或CPLEX。
  • 二次目标函数就用quadprog,注意调用YALMIP时设置合适的求解器选项。

实在没有商业求解器,MATLAB自带的intlinprog也能处理中小规模MILP。我之前做过一个24时段、5台设备、状态变量较多的算例,intlinprog跑出来也就几秒钟。

3.2 变量维度设计与数据预处理

在写代码之前,先把数据层和模型层分开,这是我个人偏好的工程习惯。数据层负责构造负荷曲线、新能源出力曲线、电价曲线、设备参数;模型层负责声明决策变量、装配约束和调用求解器。数据层出问题会直接影响模型层逻辑,调试无从下手。

设备参数用结构体struct存放比较清晰:

p.chp.pmin = 0.3; % MW p.chp.pmax = 1.5; % MW p.chp.hmin = 0.2; % MWth p.chp.hmax = 1.0; % MWth p.chp.fuel_k = 0.25; % 燃料成本系数 p.battery.Emax = 0.8; % MWh p.battery.eta_ch = 0.95; p.battery.eta_dis = 0.92; p.eb.eta = 0.85; p.grid.max = 2.0; p.T = 24;

输入曲线统一为长度T的列向量,比如load_e、load_h、pv、wt。所有功率单位统一成MW,能量统一成MWh,时间间隔Δt按小时计算。单位不统一是我见过最常见的低级错误,尤其是蓄热罐的容量单位如果用了GJ,必须提前换算好。

3.3 核心建模代码示例与解说

下面这段是建模层的核心骨架,我拆成变量、约束、目标三部分来写:

% 变量声明 P_chp = sdpvar(T, 1, 'full'); % CHP电出力 H_chp = sdpvar(T, 1, 'full'); % CHP热出力 P_eb = sdpvar(T, 1, 'full'); % 电锅炉用电 P_grid= sdpvar(T, 1, 'full'); % 购电功率 P_ch = sdpvar(T, 1, 'full'); % 电池充电功率 P_dis = sdpvar(T, 1, 'full'); % 电池放电功率 E_bat = sdpvar(T+1, 1, 'full'); % 电池SOC U_ch = binvar(T, 1); % 充电状态位 U_dis = binvar(T, 1); % 放电状态位 H_tank_ch = sdpvar(T, 1, 'full'); % 蓄热罐充热 H_tank_dis = sdpvar(T, 1, 'full'); % 蓄热罐放热 Q_tank = sdpvar(T+1, 1, 'full'); % 蓄热量
%% 约束装配 Constraints = []; % 电功率平衡 Constraints = [Constraints, ... pv + wt + P_chp + P_dis + P_grid == load_e + P_ch + P_eb]; % 热功率平衡 Constraints = [Constraints, ... H_chp + p.eb.eta * P_eb + H_tank_dis == load_h + H_tank_ch]; % CHP可行域 Constraints = [Constraints, p.chp.pmin <= P_chp <= p.chp.pmax]; Constraints = [Constraints, p.chp.hmin <= H_chp <= p.chp.hmax]; Constraints = [Constraints, P_chp + 0.6*H_chp >= 0.4]; % 电池约束 Constraints = [Constraints, E_bat(2:end) == E_bat(1:end-1) + ... (p.battery.eta_ch*P_ch - P_dis/p.battery.eta_dis)*dt]; Constraints = [Constraints, 0 <= E_bat <= p.battery.Emax]; Constraints = [Constraints, 0 <= P_ch <= p.battery.pmax*U_ch]; Constraints = [Constraints, 0 <= P_dis <= p.battery.pmax*U_dis]; Constraints = [Constraints, U_ch + U_dis <= 1]; % 蓄热罐约束 Constraints = [Constraints, Q_tank(2:end) == Q_tank(1:end-1) + ... (p.tank.eta_ch*H_tank_ch - H_tank_dis/p.tank.eta_dis)*dt]; Constraints = [Constraints, 0 <= Q_tank <= p.tank.Qmax]; Constraints = [Constraints, 0 <= H_tank_ch <= p.tank.hmax]; Constraints = [Constraints, 0 <= H_tank_dis <= p.tank.hmax]; % 联络线约束 Constraints = [Constraints, 0 <= P_grid <= p.grid.max];
%% 目标函数 Objective = sum(c_grid .* P_grid * dt) ... + sum(p.chp.fuel_a * P_chp.^2 + p.chp.fuel_b * P_chp) * dt ... + sum(p.eb.cost_om * P_eb * dt) ... + sum(p.bat.cost_om * (P_ch + P_dis) * dt) ... + p.lambda * (sum(pv_max - pv) + sum(wt_max - wt)); %% 求解 ops = sdpsettings('solver', 'gurobi', 'verbose', 2); result = optimize(Constraints, Objective, ops);

这段代码虽然不是完整可直接运行的工程文件,但结构已经非常接近实际项目。关键点在于:CHP燃料成本用了二次形式,让目标函数成为二次规划;状态位用了binvar定义,强制求解器走混合整数分支。跑通这个骨架后,再往里面加备用约束、碳排约束、需求响应约束都是一样套路。

4. 仿真算例:典型日的优化结果、不同策略对比与数据解读

4.1 典型日数据构造说明

仿真我构造一个冬季典型日。冬季的特点就是热负荷很高,CHP的余热和电锅炉都必须实时参与供热,蓄热罐有足够空间发挥“削峰填谷”的价值。用电负荷曲线设定为早上8点到晚上22点较高,凌晨和深夜较低;热负荷在早晨6点到9点、晚间18点到22点出现明显峰值。

光伏出力设置为中午12点到14点峰值约1.2 MW,风电出力为全天波动但夜间偏高,电价按峰平谷三段设置,谷时0.25元/kWh,平时0.5元/kWh,峰时0.85元/kWh。Δt取1小时,T=24。

4.2 联合优化 vs 解耦优化的对比结果

我分别跑了两套方案:

  • 方案A:电热联合优化,即前面建立的完整模型;
  • 方案B:解耦优化,先优化电力系统(此时CHP只按最小热电比发固定热出力),再独立优化热力系统。

结果差异非常明显:

指标方案A(联合优化)方案B(解耦优化)
总运行成本(万元)2.873.32
CHP启停次数24
弃风弃光率3.2%7.8%
蓄热罐有效利用小时5.62.1

联合优化一天总成本省了约4500元,主要来源于蓄热罐的充分利用。方案A中优化器让CHP在电价较低的凌晨时段多发电并存储热能,白天电价高峰时段电锅炉出力减少,蓄热罐放热供给热负荷,实现“热负荷的时间平移”。而方案B把蓄热罐的作用压缩得很小,CHP只能跟随电负荷运行,热负荷的匹配全靠电锅炉硬顶,成本自然高出一截。

4.3 结果分析中容易被忽略的细节

图上显示电功率平衡曲线首尾相接只是最基本的结果验证。真正有价值的分析是看经济调度逻辑是否自洽:

  • 储能电池应当在电价低谷段充电、高峰段放电。如果优化结果出现了电池在峰值电价时段充电,先从约束和参数入手查问题。
  • 蓄热罐的充放热时点和CHP的出力曲线必须呈现“同一时段协同”的规律,例如白天CHP高发电时余热大量存罐,夜间罐放热。
  • 联络线功率如果频繁出现陡升陡降,要考虑是不是电价跳变过于剧烈,需要给联络线加爬坡约束,否则现场调度根本执行不了这么剧烈的功率变化。

这些分析放在论文里就是减少审稿人发问的加分项,放在工程里是检查优化结果有没有落地价值的关键。

5. 我在实际编码过程中踩过的坑和最终建议

5.1 求解器的数值问题:连续变量与整型变量的处理

第一类坑出在“电池是否允许同充同放”的问题上。如果没有状态位约束,线性规划的解常常出现同一时段电池又充电又放电,由于充放电效率的存在形成功率损耗,优化器虽然不会主动制造这种浪费,但在目标函数项缺失相关惩罚时,数值上可能出现“假性”同时充放。解决办法就是加状态位,或者至少加P_ch和P_dis互斥约束。

第二类坑是Gurobi或CPLEX在求解大规模MILP时,整数变量太多会收敛很慢。我在一个96时刻的项目中就遇到过327个整数变量导致求解时间飙升到十几分钟的情况。经验做法是:槽车级别的快速预判用连续线性规划跑一版作为下界参考,然后再跑MILP,两者差距不大时可以直接用连续解近似。

第三类坑是关于YALMIP里sdpvar变量参与非线性项导致求解器类型自动变更。例如如果对P_chp写了P_chp * H_chp这样的相乘表达式,YALMIP会将其识别为非线性非凸问题,Gurobi直接罢工,quadprog也不一定收敛。写建模代码时一定要注意不要出现“变量乘变量”的非线性项,成本函数里的二次项也要小心,确保二次项是对单个变量(P_chp^2),而不是交叉项。

5.2 结果验证方法:最优性检验与平衡校验

每一次跑完优化,我都建议做三件事再宣布结果有效。

第一件,逐时段复算功率平衡。把所有机组的电出力、负荷、充电、自耗加总,确认每个时段等式约束的残差在1e-6以下,避免YALMIP在对等式约束做数值处理时留下的微小系统误差。

第二件,查看求解器返回的退出信息和求解状态。YALMIP的result.info如果不是包含success的字符串,先定位是约束不可行还是求解器数值问题。如果是不可行,要把不可行约束组逐个检查,最常用的是给约束加松弛变量,看哪个约束的松弛量被推得最大,那个就是真正的瓶颈。

第三件,做极端参数敏感性测试。把电价整体提高30%后重新求解,结果应出现储能充电时段增多、CHP出力上调;把热负荷直接清零,蓄热罐应当几乎不放热并且CHP热出力趋近CHP最小热功率。凡是不符合工程直觉的“合理结果”,十有八九代码里还藏着逻辑问题。

5.3 真正实用的小建议

如果你还在为“怎么把论文里的公式变成可跑的代码”发愁,我建议分三步走:先写一个不含储能的简单版本,验证功率平衡和成本计算;接着加入储能和蓄热罐的状态递推,验证跨时段转移特性;最后再引入整数变量和求解器选项调试。

另外,数据的输入输出建议全部用表格型变量统一管理,每个时段一行,列名直接写成“chp_power”、“battery_soc”、“grid_power”这种带单位前缀的字段,输出到Excel后用透视表工具自动生成对比图。代码本身的注释不用写太多,但是关键约束、目标函数、换算系数的来源一定要写清楚,特别是δt和相关效率系数。带一个没写清楚来源的效率系数,过两周你自己回来看都觉得陌生。

从优化模型到可复现的仿真结果,最花时间的往往不是建模本身,而是数据口径的统一和约束细节的排查。但只要把物理系统的耦合关系想透,再借助YALMIP这类建模工具分层实现,这套流程做起来是相当快的。希望这篇内容能让正在做电热微电网优化的你少走几步弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 13:08:15

7个可落地的AI Agent实战项目:突破状态管理、任务分解与人机协作瓶颈

1. 这不是一场“直播带货”&#xff0c;而是一次AI Agent能力边界的现场测绘“今晚8点&#xff0c;免费解锁7个AI Agent实战项目&#xff01;仅开放2小时”——这句话在最近两周高频出现在多个技术社群、知识付费渠道和开发者私域流量池里。它不像传统课程推广那样强调“系统学…

作者头像 李华
网站建设 2026/9/26 13:06:49

用A4纸草图加豆包,二十分钟零代码生成可交互网页

先把丑话说在前面&#xff1a;这标题不是我吹的&#xff0c;是真事。上周我坐在电脑前想做一个活动报名页&#xff0c;琢磨了半天连 HTML 是啥都记不全。后来实在没辙&#xff0c;从打印机里抽了张 A4 纸&#xff0c;拿笔画了个粗糙的页面框架&#xff0c;拍成照片扔给豆包&…

作者头像 李华
网站建设 2026/9/26 13:06:19

DBX命令行工具安装与实战:Dropbox CLI高效文件同步指南

1. DBX 是什么&#xff1f;它真不是数据库工具&#xff0c;而是开发者日常离不开的“代码快照机” 刚看到“DBX 安装”这个标题&#xff0c;很多人第一反应是&#xff1a;又一个类似 Navicat、DBeaver 的数据库管理工具&#xff1f;尤其当搜索热词里混着“dbx数据库管理工具下载…

作者头像 李华
网站建设 2026/9/26 13:06:18

Unity运行时动态加载3D模型:TriLib实战指南

简介&#xff1a;本资源是一套基于Unity 2021.3.27&#xff08;Standard Render Pipeline&#xff09;实现运行时3D模型动态加载与预览的完整工程&#xff0c;面向Unity中级开发者及AR/VR、场景编辑器、实时模型替换等应用方向的技术实践者。它依托TriLib 2.3.7插件&#xff0c…

作者头像 李华