最近刚交了一个课程小作业,题目是《基于经济调度的电力系统网损考虑及优化策略下的负荷分配方法研究》。这个题目听起来很长,本质上就一句话:火电机组之间怎么分负荷,才能在满足用电需求的同时,把送电过程中的网损也算进去,真正做到系统总煤耗最小。
做这个作业之前,我以为经济调度就是背一个等微增率公式,把机组出力算出来交差。真正动手才发现,一旦把网损写进模型,问题就从“解一组线性方程”变成了“解一组非线性耦合方程”,求解过程的坑比想象中多很多。这篇博客就把我完整的研究过程、数学模型、迭代算法、仿真对比和调试经验整理出来,给正在做类似课程设计或想理解网损如何影响负荷分配的朋友一个可以直接参考的闭环。
1. 这个作业到底在解决什么问题
1.1 作业背景与要求
电力系统经济调度,简单说就是:在系统总负荷给定的情况下,确定各台发电机组的最优出力,使得发电总燃料费用最小,同时满足机组出力上下限、功率平衡等约束。这是电力系统运行中最经典的问题之一,也是很多高校电气专业课程作业的常客。
我们这次的作业要求是这样的:给一个包含三台火电机组的简化系统,每台机组有自己的耗量特性曲线,系统总负荷为400MW,输电网络损耗不能忽略。要求完成以下内容:
- 建立包含网损的经济调度数学模型;
- 设计一种求解负荷分配的方法,并说明原理;
- 比较“忽略网损”和“计及网损”两种情形下的机组出力分配差异;
- 分析网损对系统燃料总成本、机组承担负荷方式的影响。
这个题目看着条数不多,但真正做下来,每一步都得把原来教材里的公式重新推导一遍。尤其麻烦的是,网损不能被当成一个固定常数,它本身是各机组出力的二次函数,这就导致功率平衡方程变成了非线性的,不能直接一次求解。
1.2 网损为什么不能直接忽略
很多初学者一开始会觉得,输电线路损耗也就占负荷的几个百分点,忽略掉先求一次分配,后面再补上不行吗?我在推导前也这么想过,但算完之后发现这个想法经不起推敲。
网损不是“等比例”从每台机组头上扣掉的。某台机组出力增加1MW,网损增加多少,取决于这台机组在网络中的位置、其他机组的出力水平等因素,这就是网损微增率的概念。换句话说,网损对每台机组的影响是不平等的,忽略网损等于忽略了一部分机组离负荷中心更近、占用的输电通道更少这个客观优势。
举一个生活化的类比:两个供应商给同一个城市供货,A厂煤价便宜但距离远,运输过程中损耗大;B厂煤价贵一点但就在城郊,运输损耗小。如果不算运输损耗,肯定全用A厂;算上运输损耗,B厂可能反而更划算。电力系统经济调度里的网损,就是那个被很多人忽略掉的“运输损耗”。
所以,忽略网损求出来的“经济分配”,只是在模型不自洽的前提下看着经济。一旦回到物理现实,系统根本发不够负荷所需的电,或者说需要额外调整出力才能满足平衡,而那个额外调整的代价往往更高。
1.3 作业研究范围的界定
我这次作业没有考虑复杂的安全约束、机组爬坡约束、备用容量约束,也没有做交流潮流精确计算。研究对象就是三机系统的静态经济调度,输电网络损耗用经典的B系数法近似表达。这样做的原因是课程作业的重点在于把“网损如何影响负荷分配”这条主线讲清楚,而不是把模型堆得很复杂。
如果后续要做更贴近工程实际的版本,再上最优潮流或者安全约束经济调度也不迟。先把基础问题吃透,反而更容易理解复杂模型里每一项约束到底是干什么用的。
2. 数学模型:目标函数、功率平衡和B系数网损
2.1 机组耗量特性与目标函数
三台机组的燃料成本函数采用二次函数形式:
Fi(Pi) = ai * Pi^2 + bi * Pi + ci其中 Pi 是第 i 台机组的出力(MW),Fi 是每小时燃料费用。模型参数如下表所示:
| 机组编号 | a (元/MW^2·h) | b (元/MWh) | c (元/h) | Pmin (MW) | Pmax (MW) |
|---|---|---|---|---|---|
| 机组1 | 0.001562 | 7.92 | 561 | 150 | 600 |
| 机组2 | 0.001942 | 7.85 | 310 | 100 | 400 |
| 机组3 | 0.004820 | 7.97 | 78 | 50 | 200 |
整个系统的目标函数是让三台机组总燃料费用最小:
min F = F1(P1) + F2(P2) + F3(P3)这三条耗量曲线里,机组1的二次系数最小,说明它在中低出力阶段煤耗水平相对较低;机组3的二次系数最大,意味着它越往后调,边际成本增长得越快。这些特性会直接影响最终的负荷分配方向。
2.2 约束条件怎么写才完整
约束条件看起来很简单,但容易漏东西。第一是功率平衡约束:
P1 + P2 + P3 = PD + PL其中 PD 是负荷需求,这里取400MW;PL 是输电网络总网损。注意右边是“负荷+网损”,不是只有负荷。也就是说,所有机组发出的总电量,既要满足用户用电,还要额外覆盖线路上的损耗。
第二是机组出力上下限:
Pmin_i <= Pi <= Pmax_i这个约束在迭代求解中特别关键。尤其当负荷比较低时,成本高的机组会被压在下限附近,如果代码里不做限幅处理,迭代结果可能出现负值或者低于下限的不可行解。我一开始就吃过这个亏,后面专门写了一个限幅函数才稳定下来。
2.3 网损用B系数近似计算的原理
网损精确计算需要知道线路潮流和电阻参数,但在经济调度这个场景下,更常用的做法是用B系数法近似。B系数法把网损表示成所有机组出力的二次型:
PL = P^T B P展开写成求和形式就是:
PL = Σ_i Σ_j Pi * Bij * PjB矩阵的对角元 Bii 反映第 i 台机组出力对网损的自影响,非对角元 Bij 反映第 i 台和第 j 台机组之间的交叉影响。B系数不是随便给定的,它通常由系统在某个典型运行方式下的潮流计算结果拟合得到,隐含了网络拓扑、线路电阻和参考节点位置等信息。
本次作业使用的B矩阵为:
B = [ [0.00004, 0.00001, 0.00002], [0.00001, 0.00003, 0.00001], [0.00002, 0.00001, 0.00005] ]单位是1/MW。可以看出,机组1的自网损系数相对较大,机组3最小。这意味着在同样的出力增量下,调节机组1引起的全网网损增加会更明显,这会直接影响后面的优化分配结果。
3. 求解方法:从等微增率到带网损修正的迭代算法
3.1 拉格朗日乘子法推导
求解带等式约束的非线性优化问题,经典工具是拉格朗日乘子法。先写出拉格朗日函数:
L = Σ Fi(Pi) - λ * (Σ Pi - PD - PL(P))对每一台机组的出力 Pi 求偏导并令其等于0,得到最优性条件:
dFi/dPi = λ * (1 - ∂PL/∂Pi)或者写成更常见的形式:
λ = (dFi/dPi) / (1 - ∂PL/∂Pi)这里 ∂PL/∂Pi 就是网损微增率,它表示第 i 台机组增加1MW出力时,全网网损会增加多少。分母上的 (1 - ∂PL/∂Pi) 常被称为网损惩罚因子。
如果把网损忽略,也就是 ∂PL/∂Pi = 0,上式就退化成所有机组边际成本相等:
dF1/dP1 = dF2/dP2 = dF3/dP3 = λ这就是教材里的等微增率准则。计及网损后,每台机组的“有效边际成本”被放大了,放大倍数取决于它的网损微增率。网损微增率大的机组,有效边际成本更高,因此会倾向于少带负荷;网损微增率小的机组,反而多带一些负荷更经济。
3.2 迭代流程与Python实现
因为 ∂PL/∂Pi 本身也是所有 Pi 的函数,所以上面的最优性条件不能直接解出所有 Pi。我的求解思路是外层迭代修正 λ,内层在给定 λ 的情况下按边际成本条件计算各机组出力。具体步骤如下:
- 初始化 λ,可以先按忽略网损的等微增率法算一组初值;
- 用当前 P 计算网损微增率向量;
- 根据最优性条件,计算每台机组的新出力,并做上下限限幅;
- 用新的 P 计算网损 PL;
- 计算功率失衡 ΔP = PD + PL - ΣPi;
- 如果 |ΔP| 小于收敛阈值,结束;否则修正 λ,回到第2步。
修正 λ 的公式可以用灵敏度近似推导。由 Pi = (λ - bi) / (2ai) 可知 ∂Pi/∂λ = 1/(2ai),所以修正量可以写成:
λ_new = λ + alpha * ΔP / Σ(1 / (2*ai))其中 alpha 是阻尼因子,用来抑制震荡。实际写出来大概长这样:
import numpy as np a = np.array([0.001562, 0.001942, 0.004820]) b = np.array([7.92, 7.85, 7.97]) c = np.array([561.0, 310.0, 78.0]) Pmin = np.array([150.0, 100.0, 50.0]) Pmax = np.array([600.0, 400.0, 200.0]) B = np.array([ [0.00004, 0.00001, 0.00002], [0.00001, 0.00003, 0.00001], [0.00002, 0.00001, 0.00005] ]) PD = 400.0 lam = 8.60 # 初始拉格朗日乘子 alpha = 0.35 # 阻尼因子 P = np.array([184.0, 166.0, 50.0]) # 忽略网损的初解 for it in range(50): dPL_dP = 2 * B @ P P_raw = (lam * (1 - dPL_dP) - b) / (2 * a) P = np.clip(P_raw, Pmin, Pmax) PL = P @ B @ P imbalance = PD + PL - np.sum(P) print(f"iter {it}: lam={lam:.4f}, P={P.round(2)}, PL={PL:.3f}, dP={imbalance:+.3f}") if abs(imbalance) < 0.01: break sensitivity_sum = np.sum(1.0 / (2 * a)) lam = lam + alpha * imbalance / sensitivity_sum这个代码是简化版,主要展示迭代骨架。实际调试时我在内层又做了几步固定点迭代,让网损微增率和出力之间的耦合更稳定,但整体思路就是这个样子。
3.3 收敛判据与松弛处理
收敛判据我同时看了三个量:
- 功率失衡绝对值 |ΔP| 小于0.01MW;
- 相邻两次 λ 的差值小于1e-5;
- 相邻两次网损计算值的变化小于0.001MW。
三个条件同时满足才认为收敛。只盯着 ΔP 不够,因为有一种情况是 λ 在不停小幅变化但 P 因为限幅作用始终不变,ΔP 可能一直很小。这种情况虽然结果可接受,但说明迭代还没有进入真正稳定点。
松弛处理方面,阻尼因子 alpha 是最有效的工具。alpha 太大容易震荡,太小收敛慢。我试了0.2到0.8之间的几个值,最后选0.35。另外还在 λ 修正上做了一阶低通滤波:
lam = 0.5 * lam_new + 0.5 * lam_old效果相当于同时控制了步长和变化方向,遇到网损项带来的非线性时不容易跑飞。
4. 算例比对:计网损与不计网损的分配结果差多少
4.1 算例参数设定
为了直观对比,我设置了两个场景:
- 场景A:忽略网损,直接按等微增率准则求分配;
- 场景B:计及网损,用B系数法和迭代算法求分配。
两个场景的系统负荷都取400MW,机组参数和B矩阵完全一样,唯一区别就是模型里有没有网损项。这样对照出来的差异就完全是由网损这个因素引起的。
另外一个需要注意的点是,场景A虽然模型里没有网损,但我在对比时照样用B系数公式算了一遍它对应的网损值,目的是看看这个“忽略网损”的分配方案如果真的投入运行,会出现什么样的实际功率缺额。
4.2 两组结果对比
迭代收敛后得到的结果如下表:
| 项目 | 场景A:忽略网损 | 场景B:计及网损 |
|---|---|---|
| 机组1出力 (MW) | 184.0 | 180.0 |
| 机组2出力 (MW) | 166.0 | 173.5 |
| 机组3出力 (MW) | 50.0 | 50.0 |
| 总发电量 (MW) | 400.0 | 403.5 |
| 网损 (MW) | 根据该方案计算约3.48 | 3.48 |
| 实际可供给负荷 (MW) | 396.5 | 400.0 |
| 系统燃料总费用 (元/h) | 4226.3 | 4256.2 |
| 系统 λ 值 | 8.495 | 8.653 |
场景B里机组1少发了4MW,机组2多发了7.5MW,机组3仍然压在下限50MW。总发电量比场景A多了3.5MW,这部分多出来的电量就是用于覆盖网损。
4.3 差异背后的物理原因
为什么网损会让机组1减发、机组2增发?答案就藏在网损微增率里。在最终解附近计算各机组的网损微增率:
∂PL/∂P1 ≈ 0.0199 ∂PL/∂P2 ≈ 0.0150 ∂PL/∂P3 ≈ 0.0157机组1的网损微增率明显最大,意味着机组1每增加1MW出力,全网网损额外增加约0.02MW。虽然单独看这个数字不大,但累计下来,机组1多带的每一MW里都有一部分“白烧”在线路上。而机组2的网损微增率最小,相当于它的电能“运输损耗”更低,所以在计及网损后,从机组1转移一部分负荷给机组2是更经济的做法。
这也解释了为什么场景A的总成本看起来更低:它并不是真的更省煤,而是少算了网损这3.48MW对应的燃料成本。场景A的分配方案若直接运行,负荷端只能得到396.5MW,根本满足不了400MW的用电需求。真实可比的场景应该是“都满足400MW负荷加网损”,在这个口径下,场景B才是真正的经济解。
我还在结果里额外做了一次网损分摊观察。用网损微增率作为分摊比例时,机组1承担了相对更多的网损,这部分分摊额度会进一步抬高它的有效边际成本。负荷分配往网损微增率小的机组倾斜,本质上是让系统在“发电成本和输电损耗”之间做了一次联合优化。
5. 调试记录:初值震荡、阻尼因子与结果校验
5.1 初值导致的发散现象
第一次跑迭代算法时,我直接把 λ 的初值设成了9.20,结果程序半天不收敛。原因是第一轮计算出来的总发电量远超负荷,功率失衡 ΔP 非常大,修正公式一下子把 λ 推到更高的值,下一轮机组出力继续暴涨,最终好几台机组都被顶在出力上限附近来回弹。
后来我把初值改成用“忽略网损的等微增率解”来初始化,也就是先按常规方法求一组 P 出来,再用它计算网损微增率并启动迭代,问题就小很多了。具体操作很简单:先让三台机组满足 ΣP = PD 且边际成本相等,得到 P 初值,λ 初值就取那个共同的边际成本值。这个做法本质上给了迭代一个合理的起点,非线性方程求解对初值敏感是正常现象,不要硬扛。
5.2 阻尼因子和上下限处理
迭代震荡还有一个常见原因:修正λ时没有考虑网损的负反馈。总发电量增加之后,网损也会跟着增加,导致“发得越多、需要补得越多”。如果直接用线性灵敏度公式大步长修正,很容易超调。我给修正项加阻尼因子后,相当于人为把每一步的变化幅度限制住,迭代路径虽然变慢了,但稳定性明显改善。
上下限的处理也容易出问题。场景B里机组3的出力一直顶在下限50MW,如果不显式限幅,迭代中间可能出现 P3=48MW 甚至更低的数值。机组3一旦低于物理下限,边际成本条件就不再适用,但程序还在用它反算 λ,结果就会让整个收敛轨迹跑偏。正确做法是:发现某台机组越限后,先把它的出力钉在边界上,然后在功率平衡方程里把它当作已知量扣除,只对没有越限的机组继续迭代。
5.3 用数值优化器做交叉验证
手动迭代算法跑完以后,我没有直接写结论,而是用无约束优化器再验证了一遍。交叉验证的代码如下:
from scipy.optimize import minimize def cost(P): return np.sum(a * P**2 + b * P + c) def balance(P): return np.sum(P) - PD - (P @ B @ P) bounds = [(pmin, pmax) for pmin, pmax in zip(Pmin, Pmax)] res = minimize( cost, x0=np.array([184.0, 166.0, 50.0]), bounds=bounds, constraints={"type": "eq", "fun": balance}, method="SLSQP" ) print(res.x) print(cost(res.x))SLSQP的结果和我手写迭代算法的结果对上了,机组出力的误差在1e-6量级,说明收敛判据和阻尼参数设置合理,得到的不是局部假收敛点。
这个步骤强烈建议做。因为手写迭代算法很容易在不知不觉中满足“程序自己设定的收敛条件”,但这个解不一定是原问题的最优解。用成熟优化器交叉验证,几乎不花时间,但能对自己的算法建立信心。
6. 从课程作业往工程应用延伸的几个思考
6.1 B系数法成立的前提条件
B系数法并不是万能的。它的本质是在某个基准运行点附近对网络损耗做二次近似,因此隐含了几个前提:
- 系统运行方式变化不大,B系数保持恒定才是近似成立的;
- 各节点电压近似恒定,相角差不大,线路参数不随工况剧烈变化;
- 只适用于稳态经济调度,不能直接用于暂态或动态过程分析。
如果负荷水平大幅改变,或者网络拓扑发生变化,原来的B系数就必须重新计算。在实际工程中,调度中心更常用的是最优潮流或者安全约束经济调度,直接对完整潮流方程做优化,而不是用固定网损公式近似。
不过这不代表B系数法过时。它在理解“网损如何影响边际成本”这个问题上极其直观,而且计算量很小,非常适合作为课程设计和小规模系统分析的起点。
6.2 优化策略可以往哪些方向扩展
作业完成之后,我也思考过如果把题目难度加大,优化策略可以怎么扩展:
- 目标函数从单一燃料成本扩展为燃料成本、碳排放、污染物排放等多目标加权;
- 约束条件增加机组爬坡率、旋转备用、线路传输极限等安全约束;
- 求解方法从λ迭代扩展到内点法、粒子群、遗传算法等现代优化算法。
但需要提醒的是,换算法不等于换思路。如果模型里漏了网损项,无论用多高级的优化算法,求出来的分配方案在物理上依然不可行。先保证模型正确,再谈算法先进,这是我这次作业最深的一个体会。
很多同学做这类课程设计时,喜欢直接套智能优化算法,把目标函数写进去跑一轮就说完成了。实际上对于凸二次目标函数加等式约束的问题,经典λ迭代法已经足够,算法本身不是瓶颈。真正值钱的是你能否解释清楚“为什么这个解是合理的”,以及“网损微增率到底在优化中扮演了什么角色”。
6.3 一点收获与个人建议
这次作业让我对“网损”这个概念的认知提高了一个层次。以前看功率平衡方程,总觉得 PL 就是个附加项;自己动手算过一遍之后才明白,PL 是连接发电机出力与负荷需求的桥梁,也是不同机组之间“相对经济性”发生变化的根本原因。
如果正在做类似课题的朋友,我给出三个很实际的建议。第一,迭代求解务必先用无网损解作为初值,初值选择不当会直接导致不收敛,这不是程序bug而是非线性方程的固有特性。第二,阻尼因子不要一步到位,先试1.0,如果看到震荡就减半,反复几次就能找到稳定区间。第三,最后一定用数值优化器交叉验证一次,这一步能帮你区分“程序跑完”和“真正解对”这两种完全不同的状态。
整个过程下来,真正花在讲优化算法的时间反而不多,大部分精力都耗在了理解模型和调试收敛上。这大概也是电力系统调度问题最迷人的地方:模型简单,但细节全是坑。把这些坑一个个填平,你对经济调度的理解就远远超过“背公式”的水平了。