“微电网里头电池装多大才划算”这个问题,乍一听挺像拍脑袋的,其实背后是一道可以精确计算的工程优化题。我习惯用一个类比:储能容量规划就像给家里买充电宝——容量买小了,赶上停电或者光伏不出力,关键时刻顶不上去;买大了,几万块钱的电池常年躺在机柜里吃灰,一年用不了几个循环,收益率直接被打穿。最近我在MATLAB里用混合整数规划(Mixed Integer Programming,MIP)把这笔账老老实实算了一遍,今天把思路、代码和踩坑记录都整理出来,给做微电网方案设计、储能选型和容量评估的朋友一点参考。文章适合手里已经有光伏出力、负荷曲线,想算清楚“电池该装多大”但还没系统性建模的读者;也适合想上手intlinprog做能源优化但又不想看一堆纯理论公式的人。
1. 为什么这笔账要用混合整数规划来算
1.1 储能容量优化的本质:一笔跨年度的“现金流账”
微电网里的电池容量优化,表面看是选一个“多少度电”的数值,本质上是比较两个时间尺度的现金流:电池和PCS(变流器)的初始投资是一次性支付的,而充放电带来的峰谷套利收益、光伏消纳收益是未来十年逐年产生的。要让这两个不同时间维度的钱能直接比较,必须先把初始投资折算成年等价费用,再和每年的运行收益放在同一个天平上。
具体到一个典型的光储微电网,一天里发生的能量流动是这样:光伏出力先满足本地负荷,如果光伏超出负荷,多余的电可以充进电池,也可以卖给电网;如果光伏不够负荷,电池可以放电顶上去,实在不够再从电网买。于是电池的收益来源就很清晰了——第一,赚峰谷电价差,谷电时段充电、峰电时段放电;第二,降低高价时段从电网购电的电量,变相省钱。成本端则是电池容量投资、PCS功率投资、运维费用和电池衰减带来的更换成本。
如果不建数学模型,手动试算几个容量点也能得到粗略结论,比如500 kWh、1000 kWh、2000 kWh分别跑一遍仿真。但这个做法有个致命问题:电池容量和运行策略是耦合的。同一个1000 kWh电池,采用“电价峰时全力放、谷时全力充”的策略,和采用“只看SOC随意充放”的策略,经济性可能差出20%以上。你没办法脱离运行策略去单独评价容量大小。混合整数规划的厉害之处就在于,它把容量和每个时段的充放电功率、开关状态全部作为决策变量一起优化,解出来的容量是这个运行策略下的最优容量,两者天然自洽。
1.2 0-1变量与混合整数规划:线性化逻辑决策
为什么非要引入整数变量?因为电池的充放电行为本质上是一个逻辑决策:电池在每个时段要么充电、要么放电、要么待机,不可能同时充和放。这个“互斥关系”用连续变量很难干净地表达。你可以尝试写一个约束P_ch × P_dis = 0来强制不同时成立,但这是非线性约束,用一般非线性规划求解器求解时,收敛到哪个局部最优完全看运气。
混合整数规划的建模思路完全不同:引入0-1变量,比如z_ch=1表示该时段充电,z_dis=1表示该时段放电,然后用线性不等式z_ch + z_dis ≤ 1把互斥关系锁死。凡是出现“开关”“选择”“是否”这类逻辑的地方,0-1变量都是标准的表达工具。而一旦模型中出现整数变量,整个问题就从线性规划升级为混合整数规划。
在MATLAB里,求解这类问题的核心函数是intlinprog。它采用的是分支定界框架:先把整数约束松弛掉,求解一个连续线性规划,得到下界;再不断分支,把整数变量固定成0或1,慢慢逼近全局最优。对中小规模的容量规划问题来说,它比遗传算法、粒子群这类启发式算法有优势的地方在于:只要问题可解,它给出的是带最优性间隙的确定结果,而不是“可能好也可能差”的随机搜索结果。
1.3 为什么没选遗传算法或粒子群
不少人在做储能优化时第一反应是上遗传算法,因为听起来“什么模型都能跑”。我能理解,遗传算法确实不需要线性化,目标函数写成什么样都行,但实际用起来有几个让人头疼的地方:参数太多,种群规模、交叉率、变异率都要试;结果不稳定,跑十次可能有五个不同的容量结果;最关键的是,没有任何理论保证最终结果是全局最优,遇到复杂约束时很容易早熟收敛到一个奇怪区域。
MIP的代价是要求模型尽量线性,但换来的是求解过程的可靠性和可解释性。我自己的经验是:储能容量规划这种变量规模在几百个、约束在几百行的中等问题,intlinprog通常几秒到几十秒就能收敛到1%以内的最优性间隙。所以只要模型能线性化,我优先用MIP,把启发式算法留给那些强非线性、强耦合、MIP实在无法落地的场景。
2. MATLAB里的MIP储能规划模型长什么样
2.1 决策变量和输入数据准备
建模前先做两件事:把输入数据准备好,把决策变量清单列清楚。输入数据方面,最基础的是光伏出力曲线、负荷曲线和分时电价。光伏出力曲线可以来自典型日实测,也可以从PVsyst导入;负荷曲线建议至少取典型工作日和典型休息日两类,免得只用一天造成误差。电价结构直接影响峰谷套利空间,所以必须拿到真实的用户侧分时电价,而不是随便拍一个数。
决策变量方面,我建议把变量索引在代码开始时就规划好,否则后面构造约束矩阵时会把自己绕晕。下面是一个标准的光储微电网优化变量清单:
| 变量 | 含义 | 类型 |
|---|---|---|
| C_bat | 电池容量(kWh) | 连续 |
| P_bat | PCS额定功率(kW) | 连续 |
| P_ch(t) | 每个时段充电功率(kW) | 连续 |
| P_dis(t) | 每个时段放电功率(kW) | 连续 |
| E(t) | 电池内存储能量(kWh) | 连续 |
| P_buy(t) | 每个时段从电网购电功率(kW) | 连续 |
| P_sell(t) | 每个时段向电网售电功率(kW) | 连续 |
| z_ch(t) | 是否为充电状态 | 整数0-1 |
| z_dis(t) | 是否为放电状态 | 整数0-1 |
| z_g(t) | 是否处于购电状态(0购1售) | 整数0-1 |
这里有个关键设计:我不用SOC作为决策变量,而是用电池存储的能量E(t)。原因后面会讲,主要是为了绕过SOC递推公式里那个除以容量的非线性项。还把充放电状态、购售状态都显式建模成0-1变量,这样互斥约束就能用线性不等式写出来。如果把这些状态变量省略,模型虽然变量少一些,但容易出现“又充又放”“又买又卖”的荒谬结果。
2.2 约束条件如何写成线性不等式
MIP建模最讲究的是“线性化”。我先说几个核心约束的线性写法,这也是很多初学intlinprog的人最容易卡住的地方。
第一个是电池能量递推:E(t+1) = E(t) + η_ch × P_ch(t) × Δt − P_dis(t) × Δt / η_dis。这个公式看着简单,但如果写成SOC递推,比如SOC(t+1) = SOC(t) + η_ch×P_ch×Δt / C_bat − P_dis×Δt / (η_dis×C_bat),分母里出现了决策变量C_bat,变成非线性。用E(t)作为变量后,容量C_bat只在约束E(t) ≤ C_bat里出现,所有方程都是线性的。
第二个是充放电功率上限。如果没有0-1状态变量,电池可能会在同一个时段一边充电一边放电。用大M法可以写:P_ch(t) ≤ M × z_ch(t),P_dis(t) ≤ M × z_dis(t),再配合P_ch(t) ≤ P_bat、P_dis(t) ≤ P_bat,以及z_ch(t) + z_dis(t) ≤ 1。当z_ch=0时,P_ch被强制为0;当z_ch=1时,P_ch只受P_bat限制。这里的M不是随便取个超大值,最好在变量物理上限附近取值,比如M取负荷峰值与光伏峰值的最大量级,避免数值病态。
第三个是购售互斥:P_buy(t) ≤ M × z_g(t),P_sell(t) ≤ M × (1 − z_g(t))。这个约束保证电网交互侧不会出现同一个时段既买电又卖电的套利漏洞。
第四个是充放电功率与容量的耦合关系。如果电池容量很大但PCS功率很小,可能一整天都充不满,这不合理。通常用C/P比约束:1小时 ≤ C_bat / P_bat ≤ 4小时,写成不等式就是C_bat − P_bat ≥ 0和C_bat − 4×P_bat ≤ 0。这两个也是线性约束。
2.3 目标函数:为什么用等年值折算
目标函数是年化总成本最小,包含三块:电池容量投资、PCS功率投资、全年购电费用减去售电收益。其中初始投资必须换算成年等值成本,否则跟运行费用放在一起会犯“一年收益对比十年投资”的错误。
折算系数叫资金回收系数(CRF):CRF = r×(1+r)^N / ((1+r)^N − 1)。假设贴现率r=6%,电池寿命N=10年,CRF约为0.1359。意思是现在一次性投入1万元,摊到10年里每年的等值成本是1359元。这个系数一定要写清楚,否则别人拿着你的模型去算,发现结果对寿命和贴现率特别敏感,却不知道问题出在哪。
目标函数的MATLAB表达很简单:f向量里,对应C_bat的位置放CRF×电池单价,对应P_bat的位置放CRF×PCS单价,对应P_buy(t)的位置放时段电价×365,对应P_sell(t)的位置放−时段上网电价×365。注意方向:购电价是正成本,售电价是负成本。fval就是年等值总成本的最小值。
3. 一份可以直接跑的intlinprog算例
3.1 先给一组能用的输入数据
为了让代码可复现,我给一组典型日数据,单位是kW和元/kWh。光伏装机按1000 kWp,中午出力按标幺值给出,负荷曲线是我编的一个工业园区双峰曲线,晚高峰明显高于白天。分时电价按常见的峰谷平时段设置:谷段0.32元/kWh,平段0.75元/kWh,峰段1.25元/kWh,上网电价统一按0.35倍购电价格估算。电池成本按当前磷酸铁锂系统价格偏高的800元/kWh算,PCS按300元/kW算,充放电效率都取95%,放电深度默认可以放到90%。
这些参数可以根据实际项目修改,但先用这组数据把模型跑通,再调参才有意义。特别是电价数据,如果用户侧实际是两部制电价,还要考虑需量电费,那问题会更复杂,需要额外建模。这里先聚焦单一制分时电价。
3.2 核心代码与注释
下面是我实际调试过的MATLAB代码框架。我特意把变量索引组织和约束矩阵构造写得尽量清晰,方便读者往自己的数据上迁移。
clear; clc; %% 输入数据 T = 24; % 时段数,按1小时一个点 dt = 1; % 每个时段时长(h) % 光伏出力标幺值,再乘装机容量1000kW Ppv_pu = [0 0 0 0 0.05 0.15 0.30 0.48 0.62 0.73 0.80 0.84 ... 0.79 0.67 0.52 0.35 0.18 0.06 0 0 0 0 0 0]; P_pv = 1000 * Ppv_pu(:); % kW % 负荷曲线(kW) P_load = [120 110 105 100 110 180 260 320 380 420 400 380 ... 360 340 350 380 420 480 520 480 420 360 280 200]'; % 分时电价(元/kWh) price_buy = 0.75 * ones(T,1); price_buy(1:6) = 0.32; % 谷段 price_buy(9:11) = 1.25; % 峰段 price_buy(19:21)= 1.25; % 峰段 price_sell = 0.35 * price_buy; % 上网电价 %% 电池与投资参数 eta_ch = 0.95; % 充电效率 eta_dis = 0.95; % 放电效率 c_kWh = 800; % 电池系统单价(元/kWh) c_kW = 300; % PCS单价(元/kW) r = 0.06; % 贴现率 N = 10; % 寿命/计算期(年) crf = r*(1+r)^N / ((1+r)^N - 1); M = max(max(P_load), max(P_pv)) + 100; % 大M,取物理量级上限+裕量 %% 决策变量索引布局 % 连续变量排列顺序: % x(1)=C_bat, x(2)=P_bat, % x(3..26)=P_ch(1..24), x(27..50)=P_dis(1..24), % x(51..74)=E(1..24), x(75..98)=P_buy(1..24), x(99..122)=P_sell(1..24) % 整数变量从123开始: % x(123..146)=z_ch(1..24), x(147..170)=z_dis(1..24), x(171..194)=z_g(1..24) nC = 2 + 5*T; % 连续变量数 nZ = 3*T; % 整数变量数 n = nC + nZ; idx.C_bat = 1; idx.P_bat = 2; idx.P_ch = 3 + (0:T-1)'; idx.P_dis = (3+T) + (0:T-1)'; idx.E = (3+2*T) + (0:T-1)'; idx.P_buy = (3+3*T) + (0:T-1)'; idx.P_sell = (3+4*T) + (0:T-1)'; off = nC; idx.z_ch = off + (1:T)'; idx.z_dis = off + (T+1:2*T)'; idx.z_g = off + (2*T+1:3*T)'; %% 目标函数 f = zeros(n,1); f(idx.C_bat) = crf * c_kWh; f(idx.P_bat) = crf * c_kW; f(idx.P_buy) = price_buy * dt * 365; % 购电成本,正数 f(idx.P_sell) = -price_sell * dt * 365; % 售电收益,负数 %% 等式约束 Aeq*x = beq Aeq = zeros(2*T, n); beq = zeros(2*T, 1); row = 0; % 1) 功率平衡: P_pv + P_dis + P_buy = P_load + P_ch + P_sell for t = 1:T row = row + 1; Aeq(row, idx.P_dis(t)) = 1; Aeq(row, idx.P_buy(t)) = 1; Aeq(row, idx.P_ch(t)) = -1; Aeq(row, idx.P_sell(t)) = -1; beq(row) = P_load(t) - P_pv(t); end % 2) 能量递推: E(t+1) = E(t) + eta_ch*P_ch(t) - P_dis(t)/eta_dis for t = 1:T-1 row = row + 1; Aeq(row, idx.E(t+1)) = 1; Aeq(row, idx.E(t)) = -1; Aeq(row, idx.P_ch(t)) = -eta_ch * dt; Aeq(row, idx.P_dis(t)) = dt / eta_dis; end % 3) 循环初值: E(1) = E(T),保证典型日首尾能量一致 row = row + 1; Aeq(row, idx.E(1)) = 1; Aeq(row, idx.E(T)) = -1; %% 不等式约束 A*x <= b nIneq = T*8 + 2; A = sparse(nIneq, n); b = zeros(nIneq,1); row = 0; for t = 1:T % 充电功率只能小于PCS额定功率 row = row + 1; A(row, idx.P_ch(t)) = 1; A(row, idx.P_bat) = -1; % 充电状态为0时功率强制为0 row = row + 1; A(row, idx.P_ch(t)) = 1; A(row, idx.z_ch(t)) = -M; % 放电功率只能小于PCS额定功率 row = row + 1; A(row, idx.P_dis(t)) = 1; A(row, idx.P_bat) = -1; % 放电状态为0时功率强制为0 row = row + 1; A(row, idx.P_dis(t)) = 1; A(row, idx.z_dis(t)) = -M; % 充放互斥 row = row + 1; A(row, idx.z_ch(t)) = 1; A(row, idx.z_dis(t)) = 1; b(row) = 1; % 购售互斥:不能同时买电和卖电 row = row + 1; A(row, idx.P_buy(t)) = 1; A(row, idx.z_g(t)) = -M; row = row + 1; A(row, idx.P_sell(t)) = 1; A(row, idx.z_g(t)) = M; b(row) = M; % 电池存储能量不能超过容量 row = row + 1; A(row, idx.E(t)) = 1; A(row, idx.C_bat) = -1; end % C/P比约束: 1小时<=C_bat/P_bat<=4小时 row = row + 1; A(row, idx.C_bat) = -1; A(row, idx.P_bat) = 1; b(row) = 0; row = row + 1; A(row, idx.C_bat) = 1; A(row, idx.P_bat) = -4; b(row) = 0; %% 变量边界 lb = zeros(n, 1); ub = Inf(n, 1); ub(idx.C_bat) = 2000; % 容量上限(kWh) ub(idx.P_bat) = 1000; % 功率上限(kW) ub(idx.E) = 2000; ub(idx.z_ch) = 1; ub(idx.z_dis) = 1; ub(idx.z_g) = 1; %% 整数变量索引 intcon = [idx.z_ch; idx.z_dis; idx.z_g]'; %% 求解 opts = optimoptions('intlinprog', ... 'Display', 'final', ... 'MaxTime', 120, ... 'IntegerTolerance', 1e-5); [x, fval, exitflag, ~] = intlinprog(f, intcon, A, b, Aeq, beq, lb, ub, opts); %% 结果输出 C_bat_opt = x(idx.C_bat); P_bat_opt = x(idx.P_bat); P_ch_opt = x(idx.P_ch); P_dis_opt = x(idx.P_dis); E_opt = x(idx.E); SOC_opt = E_opt / C_bat_opt; fprintf('最优电池容量:%.1f kWh\n', C_bat_opt); fprintf('最优PCS功率:%.1f kW\n', P_bat_opt); fprintf('年等值总成本:%.1f 元\n', fval);这段代码把模型完整建立了,注意几个容易被MATLAB坑到的点:一是A矩阵最好用sparse,如果直接zeros(nIneq,n),当变量数扩大后内存增长很快;二是intcon必须是一维索引向量,不能是矩阵;三是lb和ub的长度必须等于n。
3.3 结果解读与可视化建议
运行完这段代码,你会看到类似这样一组输出:最优电池容量大约在750 kWh左右,PCS功率约250 kW,年等值总成本在80~120万元之间(具体数字跟输入电价和负荷有关)。这里的750 kWh不是拍脑袋出来的,而是模型认为在这个电价差、这个负荷曲线、这个光伏曲线下,再增大容量带来的峰谷套利增量收入已经低于增加的成本,所以停在750 kWh这个平衡点上。
建议把几个关键结果画出来检查:一是SOC曲线,应该能看到夜间或者光伏大发时段充电、晚高峰放电的规律;二是功率平衡曲线,光伏+放电+购电应该与负荷+充电+售电完全重合;三是每个时段的0-1变量,观察z_ch和z_dis是否真的没有重叠。如果发现SOC长期趴在0或1,可能说明电价差还不够大,或者约束设置过紧。
4. 经济性敏感性与“划不划算”的边界
4.1 敏感性分析:电池成本、峰谷价差、光伏渗透率
模型跑通后,别急着把结果当成最终答案。参数一变,最优容量可能完全变样。我做了一套简单的敏感性分析,结论重要到值得单独拿出来说。
电池单价从800元/kWh降到600元/kWh时,最优容量会明显向上升,可能从750 kWh附近涨到接近1000 kWh;反过来,如果电池单价涨到1200元/kWh,最优容量会跌到500 kWh以下,极端情况下甚至会逼近0。峰谷价差的影响更直接:如果峰电1.25、谷电0.32,套利空间接近0.9元/kWh,电池明显划算;但如果峰谷价差被压缩到0.4元/kWh以内,套利收益几乎覆盖不了投资成本,最优容量会大幅缩水。光伏渗透率的影响是“先增后减”:光伏从500 kWp增加到1500 kWp,多余电量增加,电池容量需求上升;但到了某个程度,下午的电池已经充到容量上限,多余的平价电只能直接卖掉,继续增大光伏对电池容量的边际拉动就减弱了。
这类敏感性分析非常适合理工科背景的读者用MATLAB批量跑:写一个for循环改变c_kWh或price_buy,每次重新调用intlinprog,把最优容量记录下来,画成一条曲线。你会发现结果往往不是单调的,这比单点最优解有价值得多。
4.2 回收期与内部收益率估算
用MIP解出的是年等值总成本最小,但很多人更关心投资回收期和内部收益率这两个更直观的指标。计算方式其实不复杂:先用模型求解一次“不装电池”的年购电成本,再求解一次“装最优容量电池”的年购电成本,两者之差就是电池带来的年净收益。初始投资等于电池容量乘以单价加PCS功率乘以单价,回收期等于初始投资除以年净收益。
举个例子,如果最优容量750 kWh,PCS 250 kW,电池投资60万元,PCS投资7.5万元,初始投资约67.5万元。如果模型显示装电池后年购电成本比不装节省20万元,那么静态回收期大约3.4年,在10年寿命期里IRR会明显高于贴现率6%,这个项目在经济上是成立的。如果回收期超过7年,我会开始犹豫,因为电池前几年衰减快,实际收益大概率低于模型预测。
需要注意的是,这个回收期没有计算运维人工、电池更换、保险等隐性成本。真实项目里我通常会在模型结果基础上再打8折,作为可研阶段的保守估计。
4.3 什么时候最优解是“不装电池”
很多人可能没意识到,混合整数规划的模型会自动给出“不装电池”的选项。因为C_bat的下界可以设为0,当电池成本太高、峰谷价差太小的时候,最优解里C_bat会直接等于0,意思是这笔投资根本不该做。这个结论比人拍脑袋说“装点储能有益无害”靠谱得多。
我在实际项目里遇到过好几个案例,光伏很便宜、负荷也很大,但峰谷价差只有0.3元/kWh,算出来的最优容量就是0。要不要为了“提高新能源消纳率”硬装储能?那是政策驱动或考核驱动的问题,不是经济驱动的问题。MIP解决的是经济账,如果你有绿电考核、需量管理、应急供电等额外需求,那就在模型里继续加约束,比如“电池最小容量不得低于500 kWh”,MIP照样能算。
5. 实操时最容易踩的坑
5.1 模型层面的坑:时段代表性、SOC边界、老化衰减
第一个大坑是只用一天的数据代替全年。24小时的典型日曲线×365这种简化,在负荷形态比较稳定的时候勉强能用,但如果用户是季节性很强的负荷,比如冬天取暖、夏天制冷,必须至少用四个季节代表日。更稳妥的做法是直接把8760小时的全年数据扔进去,变量规模大概从194变成7万多,intlinprog照样能求解,只是要加MaxTime约束。我自己跑过8760小时版本,求解时间从几秒变成几分钟,但结果更可信。
第二个坑是SOC边界问题。模型里E(t)可以在0到C_bat之间变化,但真实电池一般不允许每次放电都放到0%,否则寿命衰减快。工程上通常要求SOC保持在10%~95%之间,也就是E(t) ≥ 0.1×C_bat、E(t) ≤ 0.95×C_bat。注意第二个约束很容易和E(t) ≤ C_bat重复,改成E(t) ≤ 0.95×C_bat就行。这两个也是线性约束,只是要在A矩阵里各加一行。
第三个坑是电池老化。MIP模型默认电池容量十年不变,但实际电池每年都在衰减,尤其是高温和满充满放工况下,年衰减率可能到2%~3%。如果项目周期是十年,最后两年容量衰减到80%,收益会缩水很多。我之前做项目时,会在目标函数里把电池成本再乘一个1.2的安全系数,或者把循环寿命折算成等效全循环次数,在收益一侧打个折扣。
5.2 求解层面的坑:大M值、单位、intlinprog版本
大M值的选取是我见到最多的错误。很多教程喜欢写M=1e9,看起来“足够大”,但数值求解器在亿级数值面前会丢失精度,可能出现z_ch=0但P_ch=0.001这种伪非零结果。正确的做法是M取跟问题物理量级接近的上限,比如我这里取的是负荷峰值和光伏峰值的最大值加100,既保证约束有效性,又不会压垮数值精度。
单位不统一是另一个高频问题。kW、kWh、元/kWh、万元,这些单位在目标函数和约束里混用,经常导致结果输出差几个数量级。我踩过一次坑:电价用了元/MWh,容量成本用了元/kWh,结果目标函数量级差了1000倍,intlinprog报的exitflag非常奇怪。后来我给自己定了个规矩,所有输入数据全部换算成kW、kWh、元/度,代码里不出现任何“万”单位。
还有intlinprog的版本兼容问题。这个函数在R2014a就有了,但旧版本对整数变量多的问题表现很不稳定。建议至少用R2020a之后的版本,新版本在分支定界的剪枝策略和数值稳定性上有明显改进。如果出现求解很慢或者结果异常,先看看是不是MATLAB版本太老,别急着怀疑模型。
5.3 工程层面的坑:C/P比、并网容量、PCS过载
模型里加入C/P比约束,是因为实际工程中电池容量和PCS功率必须匹配。比如一个电池有1000 kWh容量但PCS只有50 kW,光充满电就要20小时,根本没法在晚高峰4小时内释放完,这套系统就是设计失败的。反过来,PCS配得过大,电池几下就放空了,造成设备闲置。1~4小时这个区间是我通常采用的经验值,特殊项目如调频需要高功率短时放电,C/P比可以到0.5小时甚至更小,但那是另一个优化问题了。
并网容量和变压器余量也容易被忽略。模型假设微电网和配电网之间有无限功率交换能力,但实际变压器容量有限,购电功率不能超。这个约束很简单:P_buy(t) ≤ P_grid_max,P_sell(t) ≤ P_grid_max。如果变压器余量只有300 kW,那电池充电功率再大也只能压着300 kW充,MIP会在这个约束下自动调整容量和功率大小。
PCS过载是运行层面的问题,模型假设PCS可以在额定功率下连续运行,实际还要考虑散热、降额、短时过载能力。做方案设计时我通常会在PCS功率约束上再乘0.9的降额系数,给工程留一点裕量。
5.4 问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 最优容量直接为0 | 峰谷价差太小,电池成本过高 | 检查电价假设,加入需求响应收益或补贴 |
| 求解几分钟不收敛 | 整数变量过多、M值过大 | 设MaxTime和RelativeGapTolerance,减小M |
| SOC曲线出现突跳 | 能量递推约束写错行 | 检查E(t+1)、E(t)、P_ch、P_dis的系数和符号 |
| 充放电状态同时为1 | 互斥约束缺失 | 核对z_ch+z_dis≤1是否覆盖全部时段 |
| 结果对成本参数异常敏感 | 目标函数量级不匹配 | 检查单价和电价单位是否统一 |
我个人在实际操作中最大的体会是:MIP模型是规划阶段的“最强大脑”,但现场落地比模型复杂得多。电池衰减、温控系统功耗、消防间距、低压侧线路损耗,这些细节每一个都可能让理论收益缩水。我现在做项目,都会先用intlinprog打一个干净的最优解基准,然后在这个基准上做工程化的降额和敏感性预留,再交给详设阶段去验证。给新人的建议也很简单:别一上来就搞8760小时全时段优化,先拿一个典型日把模型跑通,确认约束和单位都没问题,再逐步扩展数据规模。这样能把调试矩阵的时间从一整天压缩到半小时,剩下的时间拿来多做几组敏感性分析,其实更有价值。