简介:面向电力系统与微网优化领域的研究者,这份MATLAB资源包聚焦分布式能源能量调度问题,利用粒子群优化(PSO)算法协调光伏、风电等分布式电源出力,以兼顾配电网稳定和经济运行,同时可为相关课题提供算法参考。资源共13个文件,包含9个m脚本和4个xlsx数据表;m脚本涵盖PSO主程序、适应度函数、算法测试脚本等,xlsx表则提供光伏气温、风速及适应度变化数据,压缩包仅941KB,整体轻量精悍,适合用于算法验证和教学演示。目前已有378人学习下载。通过该资源,读者可掌握微网能量调度问题的建模思路,体验从数据输入、PSO寻优到结果分析的完整流程,理解多约束条件下功率分配的处理方法;配套的测试函数与数据文件也便于二次开发,可直接迁移至课程设计或相关科研场景,是学习智能优化算法在能源领域应用的实用参考。
1. 分布式能源能量调度为什么绕不开 MATLAB
分布式能源系统的典型特征是高比例间歇性电源、多能互补耦合和强不确定性,光伏与风电出力波动、负荷曲线随机变化,再加上储能、电动汽车和可调负荷的时序耦合,让调度问题从传统单目标经济调度膨胀成了一个典型的高维、非线性、含时序约束的优化问题。MATLAB 在这个领域流行不是因为它是最快的,而是因为它具备三条别处难凑齐的能力:数值计算与矩阵运算是母语级、优化工具箱与 YALMIP 等建模语言直接衔接主流求解器、Simulink 可以与优化代码联动验证闭环。从学术论文到工程预研,MATLAB 几乎是分布式能源调度建模的默认起跑线。
很多工程师一上来就直奔遗传算法或粒子群,却发现约束处理一团乱麻、结果不可复现。问题恰恰出在跳过了运筹学建模这一步。本文从数学建模、求解器选型、代码实现到多目标与不确定性处理,给出一个可以直接照搬再改的 MATLAB 调度方案骨架,适合正在做微电网、虚拟电厂或园区综合能源系统课题的研究生与工程师。
2. 分布式能源能量调度的数学建模:先定义目标与约束
2.1 调度时间尺度与决策变量的确定
常见做法是把调度周期设为 24 小时,时间步长取 1 小时,这是行业里最通用的设置,既兼容光伏、负荷的典型预测粒度,又不会被日内分钟级波动拖垮求解规模。决策变量按设备类型分组:
- 分布式光伏与风电的出力计划,通常按预测值给定或允许一定比例的弃光弃风率
- 储能系统的充放电功率与 SOC(荷电状态)时序,含充放电状态互斥约束
- 燃气轮机或柴油机的启停状态与出力水平
- 可转移负荷的投切时段
- 与大电网交互的购售电功率
在 MATLAB 中,决策变量统一整理成一个向量x。以 24 小时、储能加微型燃气轮机加联络线功率为例,x是一个长度为 96 的列向量,前 24 维是储能充电功率,第 25 到 48 维是放电功率,第 49 到 72 维是燃气轮机出力,最后 24 维是联络线交换功率。这种「按设备按时段展平」的编码方式是 MATLAB 求解器的通用输入格式。
2.1.1 目标函数:从纯经济成本到综合运行成本
纯经济调度目标函数通常是系统总运行成本最小化,包括燃料成本、购电成本、设备启停成本与弃光弃风惩罚。YALMIP 建模时目标函数可写作:
% 决策变量定义:P_ch/P_dis 为储能充放电功率,P_gt 为燃气轮机出力,P_grid 为联络线功率 % x = [P_ch; P_dis; P_gt; P_grid],均为 24 维向量 P_ch = sdpvar(24, 1); P_dis = sdpvar(24, 1); P_gt = sdpvar(24, 1); P_grid = sdpvar(24, 1); % 成本参数 c_gas = 0.62; % 燃气轮机发电边际成本,元/kWh c_buy = 0.85; % 购电分时电价,元/kWh,峰时 c_sell = 0.35; % 售电上网电价,元/kWh c_penalty = 2.0; % 弃光弃风惩罚系数 Objective = sum(c_gas * P_gt) + sum(c_buy * max(P_grid, 0)) ... - sum(c_sell * min(P_grid, 0)) + c_penalty * sum(P_curtail);max与min作用于待优化变量时是非线性算子,YALMIP 会将其自动转为辅助变量与不等式约束,但这种写法在求解器内部会引入二进制变量,增加求解难度。更讲究的做法是把购电和售电拆成两个非负变量P_buy与P_sell,用互斥约束限制二者不同时为正,这样问题就保持了线性结构。
2.2 核心约束:功率平衡、储能动态与联络线限值
功率平衡约束是能量调度的骨架,数学形式为:
P_pv + P_wt + P_gt + P_dis + P_buy = P_load + P_ch + P_sell这里P_pv与P_wt是预测的可再生出力,P_load是负荷需求。该约束在每个时段都必须严格满足,属于等式约束,YALMIP 中直接用Constraints = [Constraints, P_pv + P_wt + P_gt + P_dis + P_buy == P_load + P_ch + P_sell]表达。
储能约束是让调度结果可落地的关键,包括三组:
- SOC 递推方程:
SOC(t+1) = SOC(t) + (P_ch * eta_ch - P_dis / eta_dis) * dt / E_cap - 充放电功率上下限:
0 <= P_ch <= P_ch_max,0 <= P_dis <= P_dis_max - 荷电状态安全区间:
SOC_min <= SOC(t) <= SOC_max
充放电互斥约束引入二进制变量b_ch与b_dis,保证同一时刻不能既充又放:
% 储能参数 E_cap = 500; % 容量 500 kWh eta_ch = 0.95; % 充电效率 eta_dis = 0.92; % 放电效率 SOC_init = 0.4; % 初始荷电状态 40% SOC_min = 0.2; SOC_max = 0.9; P_ch_max = 100; P_dis_max = 100; % 最大充/放电功率 kW % 电池二进制变量互斥约束 b_ch = binvar(24, 1); b_dis = binvar(24, 1); Constraints = [Constraints, b_ch + b_dis <= 1]; Constraints = [Constraints, 0 <= P_ch <= P_ch_max .* b_ch]; Constraints = [Constraints, 0 <= P_dis <= P_dis_max .* b_dis]; % SOC 递推 SOC = sdpvar(24, 1); Constraints = [Constraints, SOC(1) == SOC_init + (P_ch(1) * eta_ch - P_dis(1) / eta_dis) / E_cap]; for t = 2:24 Constraints = [Constraints, SOC(t) == SOC(t-1) + (P_ch(t) * eta_ch - P_dis(t) / eta_dis) / E_cap]; end Constraints = [Constraints, SOC_min <= SOC <= SOC_max, SOC(end) >= 0.3];YALMIP 里. *是逐元素乘法,用于把二进制变量广播到功率约束上。注意 SOC 递推采用了逐时段循环写法,24 小时规模下完全够用,不必刻意向量化,后者反而容易在索引上出错。
3. 从 YALMIP 到求解器:分布式能源调度问题的求解链路
3.1 混合整数线性规划为什么是首选框架
把燃气轮机启停、储能充放电互斥、购售电互斥这些逻辑关系都建模为整数变量后,问题自然落入混合整数线性规划(MILP)框架。MILP 的优势在于全局最优性有保障,商用求解器在数千变量规模下求解时间通常在秒级。与之对比,如果把非线性约束原样丢给遗传算法,既无法保证收敛到全局最优,每次运行结果还可能不同,这在工程预研阶段是致命的——你无法向评审解释为什么昨天和今天跑出的最优成本差了 5%。
MATLAB 生态里有几条求解路径:一是 Optimization Toolbox 自带的intlinprog,免费但性能一般;二是 YALMIP 或 CVX 建模后调用 Gurobi、CPLEX、MOSEK 等商用求解器,性能强但需要额外安装和配置许可证;三是用 MATLAB 自带的ga或particleswarm做启发式求解,适合非凸非线性问题但不适合标准 MILP。我的建议非常简单:如果你的约束都是线性的,那就走 YALMIP + Gurobi 路线;如果装不上 Gurobi,就用intlinprog代替,YALMIP 的代码完全不用改,只需切换求解器名称。
3.2 求解器参数设置与可行解调试
YALMIP 调用求解器的标准方式是optimize(Constraints, Objective, options),options用sdpsettings构造。工程中需要关注的三个关键参数:
| 参数 | 作用 | 建议值 |
|---|---|---|
solver | 指定求解器名称 | 'gurobi'或'intlinprog' |
verbose | 控制求解日志输出 | 调试期设为 2,生产期设为 0 |
solver.options.mipgap | MILP 的次优间隙阈值 | 0.01 或 0.001,越小越慢 |
options = sdpsettings('solver', 'gurobi', 'verbose', 2); options.gurobi.MIPGap = 0.01; % 允许 1% 的次优间隙,大幅缩短求解时间 options.gurobi.TimeLimit = 300; % 求解时间上限 300 秒,防止小规模问题也卡死 sol = optimize(Constraints, Objective, options); if sol.problem == 0 fprintf('求解成功,最小运行成本 = %.2f 元\n', value(Objective)); else disp('求解失败,错误信息:'); disp(sol.info); endMIPGap是实际工程中回报最高的参数。24 时段的小规模问题默认间隙求到 1e-4 通常也就几秒,但如果扩展到 96 时段、多储能多机组,间隙从 1e-4 放宽到 1e-2 可能把求解时间从十几分钟压到几十秒,而目标函数值差异往往不到 0.5%。这在预研和方案比选阶段完全可接受;只有出最终报告时再把间隙收紧到 1e-3 左右跑一次终版。
sol.problem返回 0 表示成功,返回非零值时先看sol.info里求解器给出的原始错误码。Gurobi 返回INFEASIBLE时,第一步检查功率平衡约束的维度是否匹配——这是最常犯的错误,P_pv + P_wt + P_gt的向量长度可能因为某个变量定义成了24*1而另一个是1*24,YALMIP 有时会静默广播而不是报错,导致约束形状错误。第二步才是检查数据里是否有 NaN 或 Inf。第三步用Constraints中逐步注释掉储能动态约束的方式定位不可行来源,这种排查思路在处理大模型时几乎是必用的。
4. 完整可运行的 MATLAB 分布式能源能量调度代码
4.1 基础数据准备:光伏、负荷与分时电价
写一段可以直接复制到.m脚本的完整代码。数据规模故意做小,便于验证行为后再替换成自己的数据。
%% 基础数据 T = 24; dt = 1; % 调度周期 24h,步长 1h % 光伏归一化出力曲线(标幺值,基于装机容量 400 kW) pv_profile = [0 0 0 0 0 0.02 0.08 0.16 0.35 0.55 0.72 0.85 ... 0.9 0.82 0.68 0.5 0.3 0.15 0.05 0 0 0 0 0]'; P_pv_max = 400; P_pv = pv_profile * P_pv_max; % 负荷曲线(单位 kW,峰谷形态) load_profile = [320 300 290 280 270 320 450 580 620 600 580 590 ... 610 640 660 670 650 630 610 590 560 520 480 400]'; % 分时电价:峰 8-11, 18-21;平 6-7, 12-17, 22-23;谷 0-5 price_buy = 0.45 * ones(24, 1); price_buy(9:12) = 0.85; price_buy(19:22) = 0.85; price_buy(7:8) = 0.62; price_buy(13:18) = 0.62; price_buy(23:24) = 0.62; price_sell = price_buy * 0.4; % 上网电价约为购电的 40%这里时段索引按 MATLAB 习惯从 1 开始,1 对应 0 点到 1 点。price_buy(9:12)表示 8 点到 11 点的峰段,与国内多数地区峰段划分错开一个索引位,这是新手最容易搞混的地方,建议在脚本头部加注释写明索引与时刻的偏移关系。
4.2 主模型:完整调度脚本与结果输出
%% 定义变量 P_ch = sdpvar(T, 1); P_dis = sdpvar(T, 1); P_gt = sdpvar(T, 1); P_buy = sdpvar(T, 1); P_sell = sdpvar(T, 1); SOC = sdpvar(T, 1); b_ch = binvar(T, 1); b_dis = binvar(T, 1); %% 参数 P_gt_max = 300; P_gt_min = 50; % 燃气轮机出力上下限 P_line_max = 250; % 联络线功率限值 P_pv_curtail = sdpvar(T, 1); % 弃光功率,非负 %% 约束 C = []; % 功率平衡:光伏 + 气机 + 放电 + 购电 = 负荷 + 充电 + 售电 + 弃光 C = [C, P_pv - P_pv_curtail + P_gt + P_dis + P_buy == load_profile + P_ch + P_sell]; C = [C, 0 <= P_pv_curtail <= P_pv]; % 弃光不超过光伏出力 C = [C, P_gt_min <= P_gt <= P_gt_max]; % 气机出力范围 C = [C, 0 <= P_buy <= P_line_max]; % 购电限值 C = [C, 0 <= P_sell <= P_line_max]; % 售电限值 C = [C, b_ch + b_dis <= 1]; % 充放电互斥 C = [C, 0 <= P_ch <= 100 .* b_ch]; C = [C, 0 <= P_dis <= 100 .* b_dis]; % SOC 递推与边界 C = [C, SOC(1) == 0.4 + (P_ch(1) * 0.95 - P_dis(1) / 0.92) / 500]; for t = 2:T C = [C, SOC(t) == SOC(t-1) + (P_ch(t) * 0.95 - P_dis(t) / 0.92) / 500]; end C = [C, 0.2 <= SOC <= 0.9, SOC(T) >= 0.3]; % SOC 末端留裕量 %% 目标:购电成本 + 燃气成本 + 弃光惩罚 - 售电收益 Objective = sum(price_buy .* P_buy) + sum(0.62 * P_gt) + 2.0 * sum(P_pv_curtail) ... - sum(price_sell .* P_sell); %% 求解 options = sdpsettings('solver', 'intlinprog', 'verbose', 2); sol = optimize(C, Objective, options); %% 结果提取 if sol.problem == 0 P_ch_opt = value(P_ch); P_dis_opt = value(P_dis); P_gt_opt = value(P_gt); SOC_opt = value(SOC); P_buy_opt = value(P_buy); P_sell_opt = value(P_sell); fprintf('总成本: %.2f 元\n', value(Objective)); fprintf('燃气轮机发电量: %.2f kWh\n', sum(P_gt_opt)); fprintf('储能放电量: %.2f kWh\n', sum(P_dis_opt)); % 绘制调度曲线 figure; t = 1:T; area(t, [P_pv, P_gt_opt, P_dis_opt, P_buy_opt]); % 供给堆叠图 hold on; plot(t, load_profile + value(P_ch), 'k-', 'LineWidth', 2); legend('光伏', '燃气轮机', '储能放电', '购电', '总负荷+充电', 'Location', 'best'); xlabel('时间/h'); ylabel('功率/kW'); title('分布式能源系统日前调度结果'); figure; plot(t, SOC_opt, 'r-o', 'LineWidth', 1.5); yline(0.2, '--', 'SOC_min'); yline(0.9, '--', 'SOC_max'); xlabel('时间/h'); ylabel('SOC / 1'); title('储能荷电状态变化曲线'); else disp(sol.info); end代码逻辑分四步:先定义所有决策变量并区分连续量与二进制量;再写约束,从最核心的功率平衡开始,逐步叠加设备限值与储能动态;然后构造目标函数,注意price_buy .* P_buy的逐元素乘法;最后调用求解器并绘制结果。绘制供给堆叠图时,area的输入顺序决定了堆叠次序,依次为光伏、气机、储能放电、购电,对应供给侧的装机优先级。
这里有个细节值得注意:目标函数里我显式加了弃光惩罚系数2.0。如果没有这项,求解器会优先通过切光伏来满足功率平衡——因为光伏边际成本为零,但约束里允许弃光存在,等于给了求解器一个免费的泄压阀。惩罚项的数值至少应大于购电峰价,否则求解器可能为了省 0.1 元购电费而弃掉本可消纳的光伏电量。
5. 多目标调度与不确定性场景的进阶处理
5.1 多目标:用加权和与e约束处理运行成本与碳排放
分布式能源调度很少只看经济性,碳排放、新能源消纳率、负荷峰谷差往往同时出现在考核指标中。两个常用方案:一是线性加权和法,把碳排放量乘以碳价折算进目标函数;二是e约束法,把一个目标转成约束,例如限定全天碳排放不超过某阈值,再最小化运行成本。加权和法代码改动最小:
% 碳排放参数 e_grid = 0.581; % 电网购电碳排放因子 kg/kWh e_gt = 0.724; % 燃气轮机碳排放因子 kg/kWh carbon_price = 0.05; % 碳价 元/kg % 在原目标基础上追加碳排放成本项 Objective_dual = Objective + carbon_price * sum(e_grid * P_buy + e_gt * P_gt);碳价取 0.05 元/kg 在当前的全国碳市场配额价格区间内是合理估计,你可以根据所在地区碳配额实际行情调整。多目标加权的本质困难在于确定权重——碳价本身就是一种社会化的权重,用它做换算比凭感觉设w1=0.7, w2=0.3更有依据。
e约束法的做法是先把单目标最优解跑出来,再逐步收紧约束边界,得出的帕累托前沿更完整。比如先求出最小成本对应的碳排放量E_min,然后设定Carbon_total <= E_min * 1.1、E_min * 1.05等递减阈值重新求解,记录每个阈值下的最优成本,即可画出一条成本-碳排放的帕累托曲线,这是很多论文的标准展示方式。
5.2 鲁棒调度:用区间预测处理光伏与负荷的不确定性
确定性日前调度最大的隐患是光伏预测误差。简单做法是把预测值下修 15% 作为保守估计,但这会牺牲经济性且缺乏理论保障。更讲究的是鲁棒优化里的盒式不确定集:
% 不确定集:光伏实际出力在 [P_pv_lb, P_pv_ub] 区间内波动 P_pv_nom = P_pv; % 预测值 delta_pv = 0.15 * P_pv_nom; % 预测偏差带宽 P_pv_ub = P_pv_nom + delta_pv; P_pv_lb = max(0, P_pv_nom - delta_pv); % 调度模型中,功率平衡约束改为应对最劣场景 % 即光伏取区间下界时,系统仍能平衡 C_robust = [C, P_pv_lb - P_pv_curtail + P_gt + P_dis + P_buy == load_profile + P_ch + P_sell];保守调度取区间下界,激进调度取期望值,折中方案是引入鲁棒调节参数Gamma,限制最多有多少个时段的光伏同时偏离预测值——这就是著名的预算不确定集。Gamma = 0退化为确定性模型,Gamma = T变成最保守模型。实际工程中Gamma取 6 到 12(即 T 的一半左右)通常能在经济性与鲁棒性之间取得较好平衡。
与鲁棒优化并列的还有一个实用思路:日内滚动修正。把调度周期切成 4 小时一个窗口,每 15 分钟用当前实测数据重新求解一次未来 4 小时的调度计划,只执行第一个时段的结果,这就是模型预测控制思想在能量管理中的标准落地方式。MATLAB 自带mpc工具箱可以搭这类控制器,但如果只是做调度仿真,手写滚动循环完全足够。
6. 调度结果验证与参数调优的实战技巧
验证调度结果是否可行不能只看目标函数收敛,必须独立检查约束是否被满足。三个必做检查项:逐时段功率平衡误差是否在 1e-6 内、SOC 是否始终位于安全区间、充放电是否像约束里设计的那样没有同时发生。这些检查写成一个独立函数,每次求解完自动执行:
function ok = validate_schedule(P_pv, P_gt, P_ch, P_dis, P_buy, P_sell, SOC, load_profile) tol = 1e-4; balance_err = max(abs(P_pv + P_gt + P_dis + P_buy - load_profile - P_ch - P_sell)); soc_viol = any(SOC < 0.2 - tol) || any(SOC > 0.9 + tol); cd_viol = any(P_ch > tol & P_dis > tol); % 充放电同时为正即为违规 ok = balance_err < tol && ~soc_viol && ~cd_viol; if ok fprintf('验证通过: 平衡误差=%.2e, SOC与互斥约束均满足\n', balance_err); else warning('验证失败: 平衡误差=%.2e, SOC越限=%d, 互斥违例=%d', ... balance_err, soc_viol, cd_viol); end end参数调优顺序按影响力排序:最先调储能容量与充放电功率上限,它们决定系统削峰填谷的天花板;其次调 SOC 下限与末端约束,这影响储能全天的可用策略空间;然后才是燃气轮机的出力上下限和电价参数。一个非常实用的诊断技巧是:画出不同储能容量下的成本下降曲线,当容量从 300 加到 500 kWh 时成本下降明显,从 500 加到 700 时变化趋缓,这个拐点就是储能配置的经济边界。
另一个隐藏较深的坑是 YALMIP 在生成.m脚本时如果将P_ch初始化为0的标量后再与binvar相乘,维度会隐式扩展成列向量,某些旧版本会报维度不匹配或产生不可预期的广播。规避方法是所有 sdpvar 变量创建时显式指定维度。遇到求解器抛出"Solver not found"这类错误时,运行yalmiptest检查求解器注册状态,再确认sdpsettings里填写的求解器名称与yalmiptest输出完全一致——大小写和空格都可能造成调用失败。
本文还有配套的精品资源,点击获取