简介:面向电力系统运行优化领域的MATLAB程序包,聚焦经典经济调度问题,通过3节点算例考虑网损分摊,帮助读者掌握机组出力分配与成本最小化的建模思路。压缩包共7个文件,包括4个.m主程序与核心函数、2个.asv自动备份文件及1个txt说明文档,整体仅7KB,结构紧凑。程序采用经典算例,实现按比例分摊网损的功率分配,涉及发电机模型、电网损耗与约束条件处理,适合教学演示、课程设计或入门研究。目前已有158人学习,读者可直接运行MATLAB代码查看结果,并通过源码注释理解优化调度流程,进一步扩展至多机组或新能源场景。整体而言,这份小体积但完整的代码包是理解经济调度核心原理的实用工具。
1. 用 MATLAB 写的经典电力系统经济调度程序:它到底解决什么问题
拿到这个标题里的 zip 压缩包,很多人下意识以为里面是什么高深算法,解压之后大概率发现是几十行最朴素的 MATLAB 脚本:给定几台火电机组的成本曲线和系统负荷,算出每台机组该发多少电,让总煤耗(总成本)最低。这件事在电力系统里叫经济调度(Economic Dispatch),属于最经典也最容易被低估的问题——它看着只是一个二次规划,但真正动手在 MATLAB 里复现一遍,你会发现求解的难点压根不在数学推导,而在迭代收敛、机组上下限、网损反馈这些细节上。
这套程序适合三类人:做电力系统分析课程设计的学生,需要一份能读懂、能改的参考实现;刚入职调度或规划岗位的工程师,想弄明白经典算法和智能算法到底差在哪;还有准备把经济调度嵌进自己仿真项目里的研究者。如果你只想点一下运行看个结果,它十几秒就出数;如果你想把它改造成计及网损、多时段甚至机组组合的版本,就得从模型和参数两个层面吃透它——这也是这篇笔记想带你走完的路。
2. 经济调度的数学模型:目标函数、等微增率与三种经典解法的选择
2.1 目标函数与约束条件:教材里那个二次规划到底在写什么
经典经济调度的目标函数写成下面这个样子,所有机组的发电成本之和最小:
min F = Σ( a_i · P_i² + b_i · P_i + c_i )
其中 P_i 是第 i 台机组的有功出力,a_i、b_i、c_i 是它的成本系数。这里的单位要特别注意:a_i 是二次项系数,单位是 $/MW²·h;b_i 是一次项系数,单位是 $/MWh;c_i 是常数项,单位是 $/h。为什么标准形式都用二次曲线拟合成本?因为线性成本函数的最优解必然跑到机组边界上,多台机组之间没有折中空间;三次以上成本函数又可能引入多个局部极值,对求解器不友好。二次函数是工程精度和求解难度之间的折中,这也是经典程序几乎全部采用它的原因。
约束条件分两类。等式约束是功率平衡:
Σ P_i = P_D + P_loss
P_D 是系统总负荷,P_loss 是网损。忽略网损时 P_loss = 0,这就是很多入门版本的样子;计及网损时 P_loss 用 B 系数矩阵表示,后面会细说。不等式约束是每台机组的出力上下限:
P_i_min ≤ P_i ≤ P_i_max
这个约束看着简单,却是程序里最容易「翻车」的地方。很多初版程序算出来的 P_i 不是负的就是超上限,原因就是迭代过程中根本没做钳位处理。等你把上述模型写进 MATLAB,会发现真正的迭代算法反而是整段代码里最短的部分。
2.2 协调方程式法:等微增率为什么是经济调度的地基
忽略网损和不等式约束,只保留功率平衡约束时,可以用拉格朗日乘数法直接推出最优性条件。构造拉格朗日函数:
L = Σ( a_i·P_i² + b_i·P_i + c_i ) − λ · ( Σ P_i − P_D )
对每个 P_i 求偏导并令其为零:
∂L / ∂P_i = 2·a_i·P_i + b_i − λ = 0
于是得到:
2·a_i·P_i + b_i = λ
这就是所谓的「等微增率准则」:最优状态下,每台机组的边际成本(增量成本)必须相等,都等于系统边际成本 λ。物理直觉也说得通:如果机组 1 的边际成本比机组 2 高,那把出力从机组 1 往机组 2 挪一点,总成本就会下降,直到两边边际成本拉平为止。由上式可以直接解出各机组出力:
P_i = ( λ − b_i ) / ( 2·a_i )
所以整个求解过程就变成:找一个 λ,让算出来的所有 P_i 加起来正好等于 P_D。这就是协调方程式法,也是绝大多数 MATLAB 经典经济调度程序的核心逻辑。别小看这一步推导,后面所有程序调试都要回到这个公式来核对结果。
2.3 从解析解到程序:梯度法、牛顿法与二分法的选型差异
如果忽略所有约束,λ 其实有闭式解。把 P_i 的表达式代入功率平衡方程,整理得:
λ = ( P_D + Σ( b_i / (2·a_i) ) ) / Σ( 1 / (2·a_i) )
这个公式在验证程序正确性时非常有用,后面避坑章节会用到。但一旦引入机组上下限和网损,闭式解失效,必须走迭代。经典 MATLAB 程序里常见三种迭代思路,我按实际工程经验给你排个序:
| 迭代方法 | 收敛速度 | 稳定性 | 实现难度 | 适用场景 |
|---|---|---|---|---|
| 梯度法 | 中等 | 差,步长敏感 | 低 | 入门教学,步长拍脑袋 |
| 牛顿法 | 快 | 中等,易震荡 | 中 | 无约束或约束简单时 |
| 二分法 | 中等 | 最好,几乎不翻车 | 低 | 有上下限约束,推荐 |
梯度法的更新公式是 λ_new = λ_old + α·(ΣP − P_D),步长 α 全靠经验。α 取大了 λ 震荡甚至发散,取小了要迭代几百次,是我当年踩得最惨的坑。牛顿法引入二阶导数信息,收敛快,但在边界约束处容易来回跳。二分法不需要梯度,只要给定 λ 的上下搜索区间,每次取中点算 P,看 ΣP 与 P_D 的偏差来收缩区间,稳定到令人感动。我的建议是:要是你自己写程序,优先二分法;经典程序里如果原生是梯度法,也可以直接改成二分法,结果只会更稳。
3. 把模型写成 MATLAB 程序:可复现的协调方程式法与 B 矩阵接入
3.1 解压后先看什么:程序结构的最小三件套
拿到 zip 包解压之后,经典的 MATLAB 经济调度程序通常会包含三个文件。一个是主脚本(一般是main.m或economic_dispatch.m),里面写数据输入、迭代循环和结果打印;一个是数据文件(可能直接写在主脚本开头,也可能是.m或.mat格式),存放机组成本系数、上下限和负荷;还有一个或多个函数文件,封装网损计算或机组出力分配逻辑。这种结构的好处是数据与算法分离,调整算例时不用在几十行里翻找改数。
我一般习惯把数据结构化地写在主脚本开头,方便别人一眼看到。典型布局如下:
% 主脚本开头统一放数据区块 a = [0.001562; 0.001940; 0.004820]; % 二次成本系数 b = [7.92; 7.85; 7.97]; % 一次成本系数 c = [561; 310; 78]; % 常数项 Pmin = [150; 75; 50]; % 出力下限 Pmax = [600; 400; 200]; % 出力上限 PD = 850; % 负荷这段代码不需要任何第三方工具箱,基础 MATLAB 环境就能运行。注意所有向量都是列向量,a.*P.^2这种写法依赖.*和.^,如果你在别的程序里看到没加点号的写法,多半是循环逐元素算的——功能一样,但向量化写法更简洁且不会有行列维度坑。
3.2 等微增率法的核心迭代代码:二分法实现
下面是完整的无网损经济调度主循环,用二分法搜索系统边际成本 λ。这段代码可以直接复制运行,也是我推荐你对照阅读经典程序的最短参考:
% 经典经济调度:协调方程式法(二分版),无网损 % 机组成本函数 Ci(Pi) = ai*Pi^2 + bi*Pi + ci a = [0.001562; 0.001940; 0.004820]; b = [7.92; 7.85; 7.97]; c = [561; 310; 78]; Pmin = [150; 75; 50]; Pmax = [600; 400; 200]; PD = 850; tol = 1e-6; % 功率平衡容差,单位 MW % λ 搜索区间:下界取所有机组最小边际成本,上界取最大边际成本 lamLow = min(2*a.*Pmin + b); lamHigh = max(2*a.*Pmax + b); for iter = 1:500 lam = (lamLow + lamHigh) / 2; % 二分取中点 P = (lam - b) ./ (2*a); % 等微增率准则 P = max(P, Pmin); % 越下限则钳位 P = min(P, Pmax); % 越上限则钳位 deltaP = sum(P) - PD; % 功率不平衡量 if abs(deltaP) < tol break; % 已满足精度,跳出 elseif deltaP > 0 lamHigh = lam; % 出力过大,λ 要降 else lamLow = lam; % 出力过小,λ 要升 end end fprintf('迭代次数:%d\n', iter); fprintf('系统边际成本 lambda = %.4f $/MWh\n', lam); for i = 1:length(P) fprintf('机组%d:出力 %.2f MW,增量成本 %.4f $/MWh\n', ... i, P(i), 2*a(i)*P(i) + b(i)); end fprintf('总出力 %.2f MW,总成本 %.2f $/h\n', ... sum(P), sum(a.*P.^2 + b.*P + c));逻辑说明:每次迭代取 λ 区间中点,由协调方程式算出各机组出力,然后检查总出力与负荷的偏差。偏差为正说明当前 λ 偏高、出力过多,把上边界拉到中点;偏差为负则把下边界拉到中点。这样每轮把搜索区间缩短一半,最多 500 次也能跑到机器精度。钳位操作最关键:一旦某台机组触及上下限,它在后续迭代里就像被「钉住」,虽然不再参与边际成本均衡,但仍然计入总出力。
参数说明:tol取 1e-6 MW 属于论文级精度;课程设计取 1e-3 到 1e-4 就足够。λ 搜索区间必须覆盖所有机组的边际成本范围,否则二分会收敛到边界值。for iter = 1:500是硬性保险,防止区间压缩速度过慢导致死循环,实际迭代次数通常在 30 到 60 次之间,运行时间可以忽略。
3.3 加上网损:B 系数矩阵的接入与代码改动
经济调度里让人头疼的是计及网损。经典做法用 B 系数矩阵把网损写成二次型:
P_loss = P' · B · P + B0' · P + B00
其中 P 是出力列向量,B 是网损系数矩阵,B0 是一次项系数向量,B00 是常数。多数教学程序把 B0 和 B00 省略了,只保留 P'·B·P 这一项,对教学案例来说够用。接入主循环时,功率平衡条件变成 ΣP = PD + Ploss,网损本身又依赖当前出力,所以每一轮二分都要用本轮 P 刷新一下 Ploss:
% 计及网损的协调方程式法 B = [3.0e-5 0 0; ... 0 9.0e-5 0; ... 0 0 1.2e-4]; % 简化对角网损矩阵,单位 1/MW B0 = zeros(3,1); % 一次项向量,教学案例设为 0 B00 = 0; % 常数项 Ploss = 0; % 网损初值 for iter = 1:500 lam = (lamLow + lamHigh) / 2; P = (lam - b) ./ (2*a); P = max(P, Pmin); P = min(P, Pmax); Ploss = P' * B * P + B0' * P + B00; % 用本轮出力刷新网损 deltaP = sum(P) - PD - Ploss; if abs(deltaP) < tol break; elseif deltaP > 0 lamHigh = lam; else lamLow = lam; end end逻辑说明:网损被当作「额外的负荷」加进功率平衡方程,因此每轮算完出力后必须重新计算 Ploss,否则功率永远对不上。这里的对角 B 矩阵只反映每台机组自身出力对网损的影响,忽略了机组之间的交叉耦合项,对 3 机小型系统够用;工程上真实 B 矩阵通常是稠密的,接入方式完全一样,只是矩阵规模变大。
参数说明:B 矩阵元素量级对收敛影响很大。典型 3 机系统的 B 对角元在 1e-5 到 1e-4 之间,系统容量越大,B 元素越小。如果你把 B 设成 0.01 量级,网损会占掉负荷的几分之一,迭代极易发散。遇到不收敛,先检查 B 的量级,再检查是否忘记刷新 Ploss。
4. 机组数据与迭代参数:成本系数、网损 B 矩阵和收敛判据怎么设
4.1 成本系数 a、b、c:单位陷阱和那个「除以 2」的玄学
经济调度程序的第一个大坑藏在成本系数里。很多教材和论文把成本函数写成:
C_i = (a_i / 2) · P_i² + b_i · P_i + c_i
这么写的时候,边际成本是 a_i·P_i + b_i,而不是 2·a_i·P_i + b_i。两份参考资料用同一组数据,一个把二次项系数当「已折半」,另一个当「未折半」,算出来的最优出力能差出十几 MW。怎么鉴别?拿到数据后先算一下某台机组在额定出力附近的成本,再和燃料消耗量对照,看量级合不合理。或者干脆先跑一遍:如果算出来的总成本和论文给的答案对不上,优先怀疑这个「除以 2」。
另一个常见问题是标幺换算。有些程序内部把功率都换成标幺值,成本系数随之缩放;而在主脚本里用户输入的是有名值。你要是把标幺系数直接灌进有名值程序,结果会错得离谱。我的习惯是:统一用有名值,功率用 MW,成本用 $/h,所有系数标注好单位,绝不在程序内部隐式换单位。数据注释里写清楚$/MW^2h这样的量纲,能省掉后面一大半排错时间。
4.2 网损 B 矩阵怎么来:两种途径与适用边界
计及网损需要 B 矩阵,这个矩阵从哪来是新手最容易卡住的地方。两条路:第一条是用电力系统分析教材附录给出的 B 系数公式,从潮流数据直接计算。做法是先对目标系统做一次牛顿-拉夫逊潮流,得到基准运行点的电压幅值和相角,再对潮流方程做一阶泰勒展开求灵敏度项,最后按教材公式组装出 B、B0、B00。这条路对几十个节点的系统可行,但节点多了以后矩阵维度爆炸,计算量和精度都不划算。第二条路更常用——直接问调度机构要,或者从系统的经济调度历史记录里反推。对教学和科研验证来说,把 B 简化为对角阵、忽略互项,是性价比最高的做法。
如果你非要自己从潮流反推,给一段伪代码示意完整的流程,方便对着教材步骤填充公式:
% 反推 B 系数的伪代码流程 % J = load_flow_jacobian(V, theta, Ybus); % 牛拉法雅可比矩阵 % S = inv(J); % 灵敏度矩阵 % B = ...; B0 = ...; B00 = ...; % 按教材公式组装 % 真实工程系统建议直接用调度机构提供的 B 系数注意这段不是完整可运行代码,真正的组装公式在每本电力系统分析教材附录里写法略有差异。程序里留好接口,把 B、B0、B00 作为外部输入传入,比写死在函数里灵活得多——这也是我反复强调的:数据区和算法区一定要分离。
4.3 收敛判据、初值与迭代参数推荐区间
经典程序里迭代参数设置得当,结果和运行时间都会很舒服。下面这组参数来自我多年跑这类程序的实战经验,可以直接照抄当起点:
| 参数 | 推荐取值 | 说明 |
|---|---|---|
| λ 搜索区间 | [min(2aPmin+b), max(2aPmax+b)] | 必须覆盖所有机组可行边际成本 |
| 功率平衡容差 tol | 1e-4 ~ 1e-6 MW | 课程设计取下界,论文级取上界 |
| 梯度法步长 α | 0.001 ~ 0.01 | 与系统功率量级相关,越界就分母发散 |
| 最大迭代次数 | 200 ~ 500 | 二分法通常 50 次内收敛 |
| 网损 B 对角元素 | 1e-5 ~ 1e-4 (1/MW) | 超过 1e-3 基本必发散 |
以我自己的经验,二分法把 tol 设为 1e-4 就足够了,因为负荷数据本身都有 ±1 MW 的预测误差,追求 1e-6 的功率平衡意义不大,但跑论文仿真时为了美观我会收紧到 1e-6。梯度法的 α 需要根据算例规模手动调,这是它最令人烦躁的缺陷。
4.4 负荷变化与多时段调度的参数批量设置
经济调度不是只跑一次单点负荷。实际工程里做日前计划要跑 24 个时段,每个时段一个负荷值。程序如果只能改主脚本里的 PD 变量,那 24 次手动改数就太痛苦了。经典程序的进阶用法是把主循环包成函数,让 PD 变成输入参数。负荷序列用向量或者 Excel 读入,循环调用调度函数就能批量出结果。这里唯一的坑是:多时段之间机组出力的爬坡约束没有建模,经典程序默认每台机组可以从任何出力跳到任何出力——这在长期机组组合里是不可接受的,但对经济调度这个层面的教学和基础研究没问题。你在改造时心里要清楚这层边界,别把经济调度程序的结果直接拿去做需要爬坡约束的结论。
5. 排查与避坑:经济调度程序在 MATLAB 里的 5 个典型问题
5.1 现象:λ 越迭代越大,出力秒变天文数字
现象:程序跑起来后,机组出力一路涨到 Pmax 附近甚至超过 Pmax,λ 数值冲到几百,迭代几百次不收敛。
原因:最常见的两个原因——梯度法步长 α 设置过大,导致 λ 更新量每次都越过最优点;或者 λ 搜索区间下界/上界取得不覆盖真实最优值,二分法把区间压缩到错误的边界。另一个隐蔽原因是成本系数单位搞混,a 的数量级差几个数量级,λ 的合理范围完全变了。
解决:先把迭代法改成二分法,用lamLow = min(2*a.*Pmin + b)和lamHigh = max(2*a.*Pmax + b)锁死搜索区间。如果二分法还发散,在迭代循环里打印每一轮的 λ、P 和 deltaP,看哪一步开始异常。我十次里有八次是数据单位问题,剩下两次是梯度步长太大——所以别急着怀疑算法,先查数据。
5.2 现象:机组出力算成负值或超上限,结果不敢用
现象:某台机组出力是-37.5 MW或者823 MW,明显超出物理可行域。
原因:迭代过程中没有做钳位,或者做了钳位但钳位后直接跳出循环。没有钳位时,λ 小于某台机组的最小边际成本,该机组出力按公式自然变负;钳位后直接跳出,则破坏功率平衡约束,因为被卡住的机组没有继续调整,其他机组也没分担它的缺额。
解决:每轮算完P = (lam - b) ./ (2*a)之后强制加两行:P = max(P, Pmin); P = min(P, Pmax);。钳位之后不能立即结束迭代,要继续二分,让未越界的机组多发电来补上越界机组的缺口。如果多台机组同时越界,迭代次数会稍微增加,但二分法最终会收敛到一个合规解。特别注意:钳位改变了 ΣP 与 λ 的关系,所以验证结果时不要用闭式解公式,要用实际的 ΣP 和 PD 对比。
5.3 现象:计及网损后结果来回震荡,功率偏差正负交替
现象:无网损版本跑得好好的,一加 B 矩阵就出现 deltaP 在正负之间来回跳,永远小于不了 tol。
原因:核心原因是网损随出力变化,而每一轮的 λ 调整和网损刷新之间存在「反馈延迟」或「反馈过强」。尤其当 B 矩阵元素偏大时,Ploss 对 P 的变化非常敏感,每轮刷新后目标值 PD+Ploss 本身也在变,二分区间被带着来回摆动。另一个原因是把 Ploss 写成了本轮 P 的函数后就立即判断收敛,却忘了 Ploss 这一轮已经变了。
解决:做法一是用上一轮迭代算出的 Ploss 来构造功率平衡条件,即把 Ploss 当作常数滞后一拍,这样二分区间是稳定的;做法二是在更新 Ploss 后不立即做收敛判断,而是先多跑两次迭代等 Ploss 稳定。我一般用后者:先正常迭代 20 次,再开始判断收敛,相当于给反馈回路一个稳定期。同时缩小 B 矩阵元素量级试试,如果量级在 1e-3 以上,基本必震荡。
5.4 现象:和教材或论文参考答案对不上,出力差十几 MW
现象:同样的机组参数和负荷,程序输出和资料给的答案不一致,总成本差异在可观测范围。
原因:第一嫌疑是成本系数的「折半」定义;第二是负荷数据口径不同——参考答案把厂用电、网损都含在 PD 里,你只填了外部负荷;第三是机组上下限设置的取法不同,有人把 Pmin 设成技术最小出力,有人设成可调出力下限。经验判断:出力差个 5% 以内先别慌,检查数据口径;差到 20% 以上再往算法上怀疑。
解决:先用第 2 章给出的闭式解公式算一个无约束、无网损的 λ 和 P,和程序跑的结果对比。这个步骤能排除 80% 的算法问题。再打开参考资料的原始数据,逐一核对 a、b、c 是否经过折半、负荷是否是净负荷、Pmin/Pmax 是否一致。我踩过一次最冤的坑:参考答案用的负荷是 850 MW,但那是包括厂用电的毛负荷,程序输入 800 MW,两边当然对不上。
5.5 现象:老脚本在新版 MATLAB 里报错或结果异常
现象:从前辈那边拷来的经典程序,在自己电脑上跑直接红字报错,或者跑出来缺胳膊少腿。
原因:MATLAB 版本迭代删掉了一批老函数。比如strmatch早几年就被建议用strncmp或contains替代;老程序里warning的语法也变过。另外很多老脚本不写.*、.^,矩阵全是*,遇到逐元素乘法就会报维度错误或者算错。
解决:先看报错提示定位到具体行,大部分是点乘缺失。批量搜索把*和^替换成.*、.^时注意别误伤真正的矩阵乘法。其次把老式的disp输出换成fprintf,格式可控还不会有兼容问题。新版本 MATLAB 对脚本语言的核心语法保持得很好,R2016a 之后的版本跑这种教学级程序基本无障碍,遇到问题九成是你的点乘问题,不是软件问题。
6. 从一次运行到批量仿真:改造、验证与进阶的几个实用技巧
先聊验证。我拿到任何经济调度程序的第一件事,不是看运行结果,而是对一个手算案例。用闭式解lam = (PD + sum(b./(2*a))) / sum(1./(2*a))先算不含约束的出力,再手动核对含约束的版本。这一步能把数据问题、公式问题全部挡在门外。算完再跑程序,结果一致才继续往下走。这个习惯帮我省了无数查错的时间,比任何调试工具都管用。
然后是改造。把主循环包成函数[P, lam, cost] = economic_dispatch(a, b, c, Pmin, Pmax, PD, B)之后,你就可以用循环批量跑 24 小时负荷曲线,或者作为子模块嵌进更大的仿真项目。我做过最实用的小工具,是从 Excel 读负荷序列,逐时段调用调度函数,把每天的出力计划写回 Excel——全程不需要打开 MATLAB 界面手动改数。
再进一步,想研究机组组合(Unit Commitment)的话,经济调度程序是天然的起步点:给每台机组增加一个 0/1 启停变量,把功率平衡等式约束保留,再用intlinprog或者 YALMIP 求解混合整数二次规划。经典程序里的成本函数和约束代码可以原样搬进去,只需要把迭代求解器换掉。如果你对智能算法感兴趣,也可以把粒子群或遗传算法接到同一个目标函数和约束上,对比一下迭代法和元启发式到底差多少——这个对比本身就能写一篇不错的课程设计。
最后说一个我自己的血泪教训:早年迷信梯度法,步长全靠拍脑袋,数据换一套就翻一次车。后来全部改用二分法,世界清净了。经典算法之所以「经典」,不是因为它最先进,而是因为它稳。希望这篇笔记能帮你绕开我踩过的这些坑,稳定复现属于你自己的经济调度程序。希望帮到你。
本文还有配套的精品资源,点击获取