这两年做配电系统规划项目的人应该都有同一个体会:如果方案只盯着经济指标,评审会上一定会被追问一句“那可靠性怎么保证”;反过来,如果只把可靠率往上堆,预算又死活扛不住。这个项目就是在这样的拉扯下出来的——我把经济性和可靠性同时放进规划目标里,用Python实现了从网架方案生成、潮流校验、可靠性评估到双目标寻优的一整条闭环。研究对象是含分布式光伏、风电、储能、柴发等多类电源的混合配电系统,这也是目前配电网规划里最贴近实际工程的一类场景。
这份内容适合几类人:一是电气工程专业做配电网方向的研究生,需要复现“规划-评估-优化”这类论文代码;二是电力设计院或供电公司里做网架规划、可研论证的工程师,想用脚本替代手算表;三是对电力系统优化感兴趣、想看看Python在传统工科里能怎么落地的同学。我会按我的实际开发顺序来讲,把目标函数怎么搭、可靠性怎么算、NSGA-II怎么和评估模块耦合、以及那些“文档里不会写但你一定会遇到”的坑,全部摊开讲。
1. 项目整体思路与双目标拆解
1.1 为什么经济性和可靠性必须“同时优化”
先说我踩过的一个真实教训。早期我做过一个只以年综合费用最小为目标的DG定容方案,优化结果是把光伏集中在负荷最重的馈线段,网损确实降下来了,年度费用也好看。但后来单独做可靠性校核时发现,这条馈线一旦检修,后端一大片负荷直接失电,停电损失算进去之后,总体费用反而比另一个“看起来不够优”的方案高出一截。
原因不复杂:经济性追求的是投资、运行、损耗这些显性成本最小化,而可靠性追求的其实是“少停电、短停电、少漏停”。你少装一台联络开关,投资省了,但故障后的负荷转带能力变差,停电时户数上去了;你把线路截面全部升级,可靠率上去了,但折旧和建设成本压得你喘不过气。这两个目标在数学上是冲突的,不存在一个方案让两个指标同时达到最好,只存在一组“帕累托最优解”。
所以这个项目的核心想法,不是给两个目标硬捏一个加权系数,而是用一个多目标进化算法同时搜索两个目标,最后给出一整条帕累托前沿。决策者可以根据自己的预算和可靠率要求,在前沿上挑方案。这个思路在工程上有一个非常大的好处:你不用在算之前就拍脑袋定“可靠性占60%还是70%”,因为权重一旦定错,结果很容易被某一个目标绑架。
1.2 混合配电系统的建模边界怎么划
“混合配电系统”在不同文献里有几种解读,一种是交直流混合配电,另一种是含高比例分布式电源和多元负荷的混合供能系统。我这次按工程里最常见的场景去做:中压配电网为主干,接入光伏、风电、储能、柴发,同时保留上级变电站的供电能力,也就是一种“多源混合”的主动配电网。建模时要做几个必要的简化,否则问题规模会大到解不动:
- 网络拓扑用节点-支路模型,负荷按时序曲线处理,而不是只用一个年最大负荷;
- 分布式电源按时序出力曲线建模,光伏按光照、风电按风速序列简化处理,储能按充放电状态变量建模;
- 柴发作为可调度电源,在故障后可以孤岛运行,承担一部分停电负荷;
- 规划决策变量简化为四类:DG安装位置与容量、储能接入位置与容量、线路升级或新增联络线方案、关键节点加装分段/联络开关。
这里要特别说一句:决策变量不是越多越好。最开始我把“储能每个小时的充放电功率”也放进了优化变量,结果搜索空间爆炸,程序跑了一个通宵,帕累托前沿还是稀稀拉拉。后来改成“储能容量和位置由规划层决定,运行策略由下层确定性规则决定”,两周工作量换来了几十倍的求解效率提升。这也是我建议所有做同类项目的朋友先想清楚的事:规划问题里能解析的部分尽量解析化,不要把运行策略全部扔给优化器去搜。
1.3 经济性目标函数:全生命周期成本怎么拆
经济性我用的是“年值化全生命周期成本”,把一次性建设投资折算到每一年,再和运行费用相加,公式可以写成:
C_total = C_inv_annual + C_om + C_loss + C_interruption
四个分项分别是:
- C_inv_annual:DG、储能、线路改造、开关设备的投资等年值。需要注意等年值转换,用资本回收系数CRF把初始投资P拆到n年:CRF = i(1+i)^n / ((1+i)^n - 1),i是折现率,n是设备寿命。光伏和储能寿命不一样,要分开折算。
- C_om:运行维护费用,可以按总投资的比例估,也可以按设备类型给单位容量运维成本。
- C_loss:网损费用,等于全年各时段网损电量乘以购电价。这一项要靠潮流计算才能拿到,所以优化迭代里每评估一个方案都要跑潮流。
- C_interruption:停电损失费用。把可靠性指标ENS(期望缺供电量)乘上单位停电损失费用,就得到了停电经济损失。这里把可靠性的经济后果直接放进了经济目标,同时ENS本身又是第二个目标的分量,两个目标之间就有了明确的经济学联系。
这个公式里最容易被忽略的是C_interruption。很多初版方案没考虑停电损失,结果优化器专门挑那些投资少的方案,因为省下的投资比多出来的停电损失还要大,但工程上这种方案根本交不了差。把停电损失纳入之后,优化结果明显回归到合理区间。
1.4 可靠性指标体系:SAIDI、SAIFI、ENS
可靠性目标我选了三指标:系统平均停电频率SAIFI、系统平均停电持续时间SAIDI、期望缺供电量ENS。公式定义如下:
SAIFI = 用户停电总次数 / 用户总数,单位次/(户·年); SAIDI = 用户停电总时长 / 用户总数,单位小时/(户·年); ENS = 各次停电事件中缺失的电量之和,单位kWh/年。
实际代码里,这三个指标是可靠性评估模块的输出。SAIFI和SAIDI用于做帕累托前沿的纵坐标展示,ENS用于算经济目标里的停电损失。为什么不直接只优化ENS?因为ENS和SAIFI/SAIDI在信息上有重叠但不完全等价——一次短时停电可能ENS很小但SAIFI很难看,对用户感知和考核指标都不友好。所以现在项目里最终展示的可靠度指标会保留三个,决策层挑方案时看的是完整的可靠性画像。
2. 可靠性评估方法选型与实现
2.1 解析法和序贯蒙特卡洛,到底选哪个
配电网可靠性评估传统上有两大类方法:解析法和蒙特卡洛模拟法。
解析法的代表是故障模式后果分析法FMEA和最小割集法,思路是把所有可能的故障事件枚举出来,查表统计影响。优点是计算快、结果稳定,小系统上精度也够;缺点是枚举组合数随设备数量指数增长,而且很难精细模拟时序负荷、DG出力变化、储能状态、故障后孤岛运行这些时间相关行为。
序贯蒙特卡洛法更贴近物理过程:按设备可靠性参数抽样,模拟一整年的故障发生、修复、开关操作、负荷转移过程,然后统计用户停电情况。它天然支持时序负荷和DG时序出力,缺点是计算量大、结果有抽样误差,需要控制抽样次数和收敛判据。
我这个项目最终采用了“序贯蒙特卡洛为主、解析法校验为辅”的方案。原因是混合配电系统里有光伏、储能、柴发,这些设备的行为强依赖时间,解析法很难把这些“时序耦合”刻进去。比如同一个线路故障发生在白天光伏大发时段和夜间负荷高峰时段,影响完全不同,只有时序模拟才能区分开。
2.2 序贯蒙特卡洛评估的基本流程
实现的时候我按六步走:
- 输入网络拓扑、负荷时序曲线、DG出力时序曲线、设备可靠性参数;
- 对每个可修复元件,根据故障率抽样出全年的故障时刻序列,用指数分布描述无故障工作时间;
- 把所有元件的故障时刻合并成一条按时间排序的故障事件序列;
- 从故障时刻开始,模拟故障隔离、开关操作、负荷转带、DG孤岛运行、修复完成这一完整过程,逐时段记录失电负荷;
- 遍历完整年后,统计SAIFI、SAIDI、ENS;
- 重复多轮模拟,用置信区间判断收敛,然后取平均作为评估结果。
这个流程里最容易出错的地方在第3步。多个元件可能在同一年内多次故障,合并排序时一定要保证时间顺序正确,而且同一时段不能重复计停电。我代码里用了一个事件堆来处理,而不是单纯嵌套循环,效率差很多。
2.3 设备可靠性参数怎么给
可靠性评估的参数取不好,后面全是白算。网上很多文献直接把IEEE标准算例的可靠性参数搬过来,但实际工程里不同地区、不同设备类型的参数差距很大。我的建议是先搭一套参考参数跑通流程,后面用自己单位的历史数据替换。下面是一组常见参考值表格:
| 设备类型 | 故障率 | 平均修复时间 | 备注 |
|---|---|---|---|
| 架空线路 | 0.08~0.25次/(km·年) | 4~8小时 | 农网和城网差异大 |
| 电缆线路 | 0.02~0.06次/(km·年) | 8~16小时 | 故障定位和修复更慢 |
| 配电变压器 | 0.005~0.02次/台·年 | 5~10小时 | 油变和干变有差异 |
| 断路器 | 0.002~0.01次/台·年 | 4~8小时 | 含拒动概率则单独建模 |
| 隔离开关操作 | 0.5~1小时/次 | - | 故障隔离和恢复送电时间 |
| 联络开关操作 | 0.5~2小时/次 | - | 手动和自动差别大 |
| 光伏逆变器 | 0.1~0.2次/年 | 10~24小时 | 强迫停运率约2%~4% |
| 储能系统 | 0.02~0.05次/年 | 8~16小时 | 电池簇 + PCS 合并简化 |
注意线路故障率是单位长度值,输入前一定要把线路长度乘进去。这个单位换算坑我踩过两次,后面在章节4里还会细说。
2.4 时序负荷曲线和DG出力模型
可靠性评估要想贴近真实,不能只用“年最大负荷”这种单点数据。我在项目里用了8760小时的分区负荷曲线,分成居民、商业、工业三类典型曲线。光伏出力和光照强度近似线性,用典型日照小时数乘以峰值出力得到;风电出力用典型风速序列换算。
这里有个取舍:如果系统规模大、场景多,8760小时的序列会让蒙特卡洛慢到不可接受。我最后的处理办法是先做典型日缩减法,把一年缩成“春夏秋冬 × 工作日/休息日”8个典型日,每个典型日24点,也就是一年用192个时段近似。实测下来评估速度和8760完整曲线差了一个数量级,但SAIFI/SAIDI误差控制在5%以内,对规划阶段完全够用。
3. Python实现:代码框架与关键模块
3.1 工程文件结构建议
这个项目代码量不算小,如果全部堆在一个.py文件里,后面调参想死的心都有。我建议按下面的结构组织:
hybrid_dn_planning/ ├── data/ │ ├── network.csv # 节点-支路拓扑 │ ├── load_profile.csv # 典型日负荷曲线 │ ├── dg_profile.csv # DG出力曲线 │ └── device_param.csv # 设备可靠性参数 ├── src/ │ ├── power_flow.py # 前推回代潮流 │ ├── reliability.py # 序贯蒙特卡洛评估 │ ├── cost.py # 经济目标计算 │ ├── optimization.py # NSGA-II优化 │ └── main.py # 主流程 └── results/ └── pareto_front.csv把数据、算法、入口分开,最大的好处是换算例的时候只改data目录,不用动代码。后面我调试33节点算例时深有体会:矩阵维数对不上、单位换算不对,都是因为数据和代码耦合太紧才查了半天。
3.2 配电网潮流:前推回代法核心实现
经济目标里的网损费用需要潮流结果。配电网是辐射状结构,前推回代法是最稳的选择,不需要求雅可比矩阵,迭代简单。核心思路是:先假设各节点电压为额定值,从末端往首端推支路电流,再从首端往末端推节点电压,反复迭代直到收敛。
下面是核心代码,简化到只保留主干逻辑:
import numpy as np def backward_forward(nodes, branches, load_p, load_q, v0=1.0, tol=1e-6, max_iter=50): """ nodes: 节点编号列表 branches: [(from, to, r, x), ...] load_p, load_q: 各节点有功、无功负荷 v0: 根节点电压标幺值 """ n = len(nodes) v = np.full(n, v0, dtype=float) # 先构建父节点关系和层级 parent = {} children = {node: [] for node in nodes} for f, t, r, x in branches: parent[t] = (f, r, x) children[f].append(t) for _ in range(max_iter): v_old = v.copy() # 回代:从叶子往根推电流 i_br = {} s = load_p + 1j * load_q for node in reversed(nodes): # 该节点流出的电流 = 自身负荷 + 所有子支路电流 cur = np.conj(s[node] / (v[node] + 1e-12)) for child in children[node]: cur = cur + i_br[(node, child)] if node in parent: # 支路电流注入 i_br[(parent[node][0], node)] = cur # 前推:从根往叶子算电压 v = np.full(n, v0, dtype=float) for node in nodes: if node in parent: pf, pr, px = parent[node] v[node] = v[pf] - (pr + 1j * px) * i_br[(pf, node)] if np.max(np.abs(v - v_old)) < tol: break # 网损计算 loss = 0.0 for f, t, r, x in branches: i_mag = np.abs(i_br[(f, t)]) loss += 3 * r * i_mag**2 return v, loss这段代码里我加了“1e-12”来防止负荷为零的节点电压为零导致除零错误。前推回代法的收敛性对配网几乎没问题,但注意如果网络里带了PV节点(比如柴发恒压控制),就不能直接用上面的纯前推回代,得做PV节点处理或改用牛拉法。我自己项目里的柴发处理为了省事,采用“PQ节点+出力限制”,可靠性模块里柴发视作可调度电源,潮流计算把它当成输出功率给定的负负荷,这样简化在规划阶段已经够用。
3.3 经济目标计算的代码实现
经济目标函数计算的关键是把投资、运维、损耗、停电损失四个部分都折算到同一时间尺度。折现率、设备寿命、单位电价这些参数都放在一个字典里,方便批量修改:
def capital_recovery_factor(rate, years): """等年值系数 CRF""" if rate == 0: return 1.0 / years return rate * (1 + rate) ** years / ((1 + rate) ** years - 1) def annual_cost(capex, opex_rate, loss_energy, ens, params): """ capex: 总投资 opex_rate: 年运维费率 loss_energy: 年网损电量 ens: 期望缺供电量 kWh params: {'discount': 0.08, 'life': 20, 'loss_price': 0.6, 'outage_price': 12.0} """ crf = capital_recovery_factor(params['discount'], params['life']) c_inv = capex * crf c_om = capex * opex_rate c_loss = loss_energy * params['loss_price'] c_outage = ens * params['outage_price'] return c_inv + c_om + c_loss + c_outage停电损失单价params['outage_price']对结果影响非常大。不同地区、不同用户类型差别很大,居民用户可能几块钱一度,商业用户可能几十上百块。我建议至少按居民、商业、工业三类分开设置,在可靠性评估阶段分别统计各类用户的ENS再乘以对应单价。最开始我图省事用统一单价,结果优化出来的DG选址全部偏向商业负荷中心,后来改成分类单价之后结果才合理。
3.4 可靠性评估模块:蒙特卡洛模拟核心代码
蒙特卡洛可靠性评估是整个项目最耗时的部分。核心是利用numpy的向量化能力,一次性抽样多个元件的年故障序列,减少Python循环次数:
import numpy as np def simulate_one_year(line_params, load_curves, dg_curves, switch_time=1.0): """ 简化版:一年时序模拟 line_params: [{'lambda': 0.1, 'r': 5.0, 'length': 2.0, 'nodes_affected': [...]}, ...] load_curves: dict, 节点号 -> 8760小时或192时段负荷序列 """ total_outage_time = 0.0 total_outage_ens = 0.0 outage_count = 0 for lp in line_params: # 泊松过程抽样故障次数 lam = lp['lambda'] * lp['length'] n_fault = np.random.poisson(lam) if n_fault == 0: continue # 抽样故障时间(均匀分布在一年内,简化处理) fault_times = np.sort(np.random.uniform(0, 8760, n_fault)) for ft in fault_times: # 故障隔离时间 + 修复时间 iso_time = switch_time repair_time = lp['r'] + iso_time # 统计受影响节点在该时段内的停电时长和缺供电量 for node_id in lp['nodes_affected']: # 简化:故障时段内所有受影响负荷都停电 ens = np.sum(load_curves[node_id][int(ft):int(ft + repair_time)]) / 4.0 total_outage_time += repair_time total_outage_ens += ens outage_count += 1 return outage_count, total_outage_time, total_outage_ens上面这段是简化演示,实际项目里要做的事情更多:要考虑联络开关转带负荷后的恢复供电,要考虑DG孤岛运行能带起多少负荷,还要区分“故障修复前就恢复的用户”和“修复后才恢复的用户”。但这些扩展逻辑都是在同一个事件模拟框架上加判断条件,核心的故障抽样和统计逻辑不变。
蒙特卡洛评估一定要控制随机数种子。我在main.py里对所有随机过程设了固定seed,否则调参时你会困惑为什么同样的参数结果一次一个样,没法定位是代码问题还是随机波动。
3.5 双目标优化与帕累托前沿:NSGA-II实现
双目标优化我选了NSGA-II,这是多目标进化算法里的“万金油”,实现成熟、不用调太多参数,非常适合配电网规划这种离散/连续变量混合的问题。它可以分成三层理解:
第一层,编码和解码。我的染色体由一个混合向量组成,前一段表示每个候选DG节点的装机容量,后一段表示储能配置,再后面是线路升级的0/1选择。解码时把染色体翻译成具体的网络参数,交给潮流和可靠性模块评估。
第二层,非支配排序。把所有个体按“经济成本”和“可靠度指标”两个目标进行比较,找出谁支配谁,然后按支配层级分组。同一层级的个体用拥挤距离排序,保证前沿上的解尽量均匀分布。
第三层,进化操作。SBX交叉和多项式变异用于连续变量,二进制交叉用于离散变量。然后选择、交叉、变异、环境选择,迭代一代代推进。
完整NSGA-II手写太长,我用pymoo框架实现比较方便,只需要把评估函数写清楚:
from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.core.problem import Problem from pymoo.optimize import minimize class DnPlanningProblem(Problem): def __init__(self, n_var): # 2个目标:经济成本最小,ENS/可靠指标最小 super().__init__(n_var=n_var, n_obj=2, xl=0, xu=1) def _evaluate(self, x, out, *args, **kwargs): cost = [] ens_obj = [] for individual in x: # 解码 -> 潮流 -> 可靠性评估 -> 目标值 c, ens = evaluate_individual(individual) cost.append(c) ens_obj.append(ens) out["F"] = np.column_stack([cost, ens_obj]) algorithm = NSGA2(pop_size=100, sampling=real_random) res = minimize(DnPlanningProblem(n_var=20), algorithm, termination=('n_gen', 100))用pymoo最大的好处是它内置了非支配排序、拥挤距离、锦标赛选择这些算子的高效实现,不用自己写还能保证正确性。但要注意pymoo里的目标默认是最小化,如果你的ENS想取负数,记得在评估函数里做符号转换。
4. 常见问题与排查技巧实录
4.1 可靠性评估太慢,怎么加速
蒙特卡洛模拟是时间黑洞。我最早用纯Python逐台设备、逐小时循环,40节点的算例跑一次评估要好几秒;放进NSGA-II里,种群100个个体迭代100代,就是上百万次评估,根本跑不完。
后来做了三个优化,速度提升一个数量级:
一是向量化故障抽样,用numpy一次性生成所有元件的年故障序列,不要一台一台循环; 二是典型日缩减法,把8760小时压到192时段,代价是精度稍微损失,但速度提升明显; 三是评估结果缓存,相同拓扑结构下如果只有负荷曲线变化,可以复用可靠性评估的中间结果,避免重复计算。
实际落地后,40节点系统一次蒙特卡洛评估大概从4秒降到0.3秒左右,5000次收敛模拟不到半小时,整个NSGA-II流程能在一晚上跑完。
4.2 帕累托前沿分布不均匀
如果你跑出来的帕累托前沿总是挤成一团,或者两端缺失,首先要检查拥挤距离的计算。在NSGA-II里,拥挤距离决定了同支配层个体的保留优先度,如果实现有bug,极端个体会被过早淘汰。其次是变异概率,连续变量变异概率太小,种群会过早收敛到一个区域;建议SBX的eta设为20左右,多项式变异的eta设为20,变异概率设为1/n_var。
还有一个经常被忽略的点:随机数种子。我发现不同种子之间的帕累托前沿形态差异很大,尤其是种群规模小于50的时候。解决办法是固定seed,或者用多次独立运行取并集的方式生成最终前沿。我在项目里跑了5个不同种子,把结果合并后删掉重复解,前沿覆盖率明显改善。
4.3 潮流迭代不收敛怎么排查
前推回代法在正常辐射状配网里几乎不会出问题,如果出现不收敛,九成是数据错误。最常见的是支路参数r和x的单位不一致,或者节点编号在拓扑表里出现了孤立节点。代码里我会在潮流计算前加一个校验环节,检查每个节点是否只有一个父节点、是否所有节点都能从根节点遍历到。
另一个容易出问题的是PV节点。如果柴发采用恒压控制,前推回代法需要在回代阶段做PV节点的无功修正,最简单的做法是用“无功迭代修正法”,每一轮算完电压后根据PV节点电压偏差修正无功注入,再继续迭代。我的项目为了方便,把柴发处理成PQ节点,因为规划阶段重点是容量配置而非稳态电压精确控制,这个简化是合理的。
4.4 数据输入的几个典型坑
数据坑我在前面提过两个,这里系统性罗列一下:
- 线路故障率单位:有的资料是“次/(百km·年)”,有的是“次/(km·年)”,差100倍,改错的话SAIFI会飘到离谱;
- 负荷功率单位:有些原始数据给的是视在功率kVA,潮流计算需要有功kW和无功kvar,别忘了乘功率因数;
- DG容量和实际出力曲线不匹配:光伏峰值容量10MW,但典型日出力序列峰值可能是0.8,别把两者搞混;
- 联络线容量约束:新增联络线之后,故障转带负荷时联络线可能过载,评估模块里必须加转带容量校验,否则恢复方案是假的;
- 孤岛校验:DG孤岛运行时要校验频率和电压能不能撑住,至少要做有功平衡校验——孤岛内DG出力必须大于孤岛内负荷,否则孤岛直接失败。
4.5 问题排查速查表
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
| SAIFI大得离谱 | 线路故障率单位填错或长度未乘 | 抽查单条线路年故障次数 |
| ENS偏小 | 没算联络开关转带时间或DG孤岛 | 对比故障时段负荷曲线 |
| 帕累托前沿挤在一端 | 拥挤距离失效或变异率过低 | 打印每代前沿范围,调高变异率 |
| 经济成本恒为常量 | 停电损失没纳入目标函数 | 检查C_interruption是否被覆盖 |
| 优化循环里评估结果波动大 | 蒙特卡洛未固定随机种子 | main.py里加np.random.seed |
| 潮流迭代次数超限 | 支路单位不一致或存在孤立节点 | 校验网络连通性 |
4.6 几个实操心得
最后分享几个我在项目里验证过、觉得特别有价值的小技巧。
第一个是“先单目标后双目标”。我在跑NSGA-II之前,先用加权单目标的方式把大致合理区间摸了一遍,把变量取值范围压缩到一个小区域,再放开成双目标跑。这样既减少无效搜索,又能提前发现评估函数的bug。
第二个是“可靠性评估函数尽量保持无状态”。把网络拓扑、参数、负荷曲线全部做成传入参数,不要在评估函数内部读文件和修改全局变量。这个习惯让评估模块可以被多进程、多线程复用,也方便pymoo并行评估。NSGA-II的并行化可以简单地把pop_size改成进程数倍数,然后使用pymoo的run_in_thread或run_in_process,速度还能再上一档。
第三个是“结果可视化用网上网下两张图”。网上图是帕累托前沿,横轴经济成本,纵轴SAIDI或ENS;网下图是选的方案对应的DG位置和容量分布,画在坐标平面上。这样不仅自己检查结果方便,汇报的时候给非技术领导看也直观得多。绘图用matplotlib就够了,不需要其他花哨库。
我在这个项目里最深的体会是:配电网规划问题真正的难点不在优化算法,而在“评估模型是否可靠”。NSGA-II本身是成熟工具,但如果你前面的潮流和可靠性评估有任何一个环节失真,优化器输出的帕累托前沿再漂亮,也都是空中楼阁。所以先把评估模块用标准算例校核准确,再谈优化,这个顺序不要颠倒。项目后续还可以往两个方向扩展:一是把负荷和DG出力的不确定性显式建模,做成区间优化或鲁棒优化;二是加入碳减排目标和绿电消纳约束,变成三目标问题。架构上只要评估函数接口清晰,这些扩展都不会伤筋动骨。