ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

F-T柴油机SOOT与NOx多目标同步优化:Matlab NSGA-II实现

F-T柴油机SOOT与NOx多目标同步优化:Matlab NSGA-II实现

做柴油机排放控制的人,对SOOT(碳烟)和NOx(氮氧化物)这对“冤家”应该都深有体会。常规柴油机里,燃烧温度一高,NOx跟着往上冲,碳烟倒是被烧得干净;温度一压下来,NOx是舒服了,碳烟又开始冒头。这就是业内常说的PM-NOx tradeoff,也是标定工程师最不想面对又躲不开的跷跷板。这几年F-T柴油(费托合成柴油)出现的频率越来越高,它十六烷值高、基本不含硫和芳香烃,碳烟生成倾向明显低于石化柴油,听起来像是个“天赐减排方案”。但实际做下来你会发现,燃料一换,整个燃烧和排放的耦合关系全变了,靠老经验调参根本摸不到SOOT和NOx的平衡区。这篇文章就把我完整走通的一条技术路线分享出来:基于多目标分析的F-T柴油机SOOT与NOx排放同步优化,以及整套Matlab实现方案,适合正在做柴油机替代燃料、排放预测或者多目标优化方向的研究生和工程师参考。

1. F-T柴油的排放矛盾:SOOT与NOx为什么天然此消彼长

1.1 排放生成的底层逻辑决定了这对矛盾

先把这个最基础的问题说透:为什么柴油机里SOOT和NOx总是对着干?

NOx的主要来源是热力型NO,它的生成条件有三个缺一不可——高温、富氧、足够的反应时间。燃烧室局部温度一旦超过1800K左右,空气中的氮气就会被“拆开”和氧原子结合生成NO,温度越高生成速率越快,基本呈指数关系。所以想降NOx,第一反应就是降燃烧温度。

SOOT的生成路径则完全相反。碳烟的前驱物是PAH(多环芳香烃),这东西主要在燃油浓混合区、高温缺氧的环境里大量生成。扩散燃烧阶段,油束核心区域氧气进不去,燃料在高温下裂解、脱氢、环化,一步步聚合成碳颗粒。要让碳烟烧掉,恰恰需要高温和充足的氧气停留时间。

看出来了吗?一个需要“高温富氧”把它烧掉,一个需要“低温贫氧”把它憋住。同一个燃烧温度参数,往左调救NOx,往右调救SOOT,这就是跷跷板的物理根源。

1.2 F-T柴油的燃料特性带来了新的优化窗口

F-T柴油是通过费托合成工艺得到的合成燃料,原料可以是煤、天然气或者生物质,产物以直链烷烃和异构烷烃为主。跟石化柴油比,它有四个很关键的差异:

  • 十六烷值高,一般在70到80,而普通柴油通常在40到55之间;
  • 几乎不含芳香烃,硫含量趋近于零;
  • 氢碳比更高,分子结构更“长直”;
  • 密度和黏度偏低,喷雾贯穿特性跟石化柴油不一样。

这些特性叠加下来,碳烟生成倾向天然就低。芳香烃是PAH的直接前驱物,你把它拿掉了,碳烟生成的“原材料”就少了一大块。加上十六烷值高导致滞燃期短,预混燃烧比例变化,燃烧相位整体前移,碳烟和NOx的生成窗口也跟着移动。

这里就出现了一个很有意思的局面:碳烟基准线被拉低了,意味着我们可以用更大的EGR率去压NOx,而不用担心碳烟爆表。也就是说,F-T柴油给优化“腾”出了一块新的参数空间。但问题是,这个空间的长宽高到底是多少,最优操作点在哪里,靠经验猜不出来——这正是需要做多目标优化的原因。

1.3 手工调参与单目标优化的局限

我自己最早也试着走“专家调参”路线:先定EGR率,再扫主喷正时,然后看轨压,一轮一轮来。问题是这几个参数根本不是独立作用的。高EGR率下,最佳喷油正时会偏移;轨压变了,预喷的间隔和油量比例又得重调。四五个参数相互耦合,手工扫描的试验量指数上涨,而且每次只能保证“在当前组合下局部最优”,换个工况点全得重来。

也有人说,那把SOOT和NOx按权重加成一个目标不就行了?比如f = w1×soot + w2×nox,用单目标遗传算法去优化。这条路我试过,最大的问题是你必须先拍脑袋定权重。可实际上,不同排放法规、不同工况下,SOOT和NOx的相对重要性是变化的,权重给得不合适,优化结果可能一头扎进“NOx降得漂亮但碳烟超标”的死角。而且单目标优化永远只能给一个解,你根本看不到这两个目标之间完整的妥协关系。这就是我必须引入多目标分析的根本原因:我需要的是那条Pareto最优前沿,而不是某个拍脑袋决定的最优单点。

2. 研究方案设计:从台架试验到“参数-排放”代理模型

2.1 决策变量的选取与边界约束

要把发动机问题变成数学问题,第一步是定决策变量。我最终选了五个对排放影响最显著的喷射与进气参数:

变量含义单位范围
x1主喷正时°CA BTDC0~15
x2轨压(喷射压力)MPa80~160
x3EGR率%0~30
x4预喷-主喷间隔°CA0~10
x5预喷油量占比%0~15

为什么是这五个?主喷正时直接决定燃烧相位,是调节峰值温度和压力升高率最灵敏的手柄;轨压影响喷雾雾化和油气混合质量,跟碳烟生成关系非常直接;EGR率是压NOx最常用的手段,但它对碳烟的副作用必须靠其他参数来弥补;预喷策略则通过改变预混燃烧比例来同时影响两个目标。这五个变量基本覆盖了“进气-喷油-燃烧”三个环节,任何一个被忽略,优化结果在台架上很可能就跑不出来。

约束条件也不能漏。我加了几个硬约束:最高爆发压力不超过18MPa,避免伤机器;COV(循环变动率)控制在5%以内,保证燃烧稳定性;燃油消耗率相对原机标定不劣化超过3%。这些约束在优化算法里通过惩罚函数处理,后面会讲。

2.2 试验设计方法:用最少的台架试验获取最多的信息

决策变量定了,接下来就是数据从哪来。最理想是直接在全工况空间里扫点,但五个变量全因子试验要几千个点,台架时间根本不允许。我的做法是分两步:

第一步,用正交试验设计铺一个“粗网格”。我用了L50正交表安排了50个试验点,每个点覆盖不同参数组合,先摸清各参数对SOOT和NOx的敏感度区间。这一步能帮我发现哪些参数组合会让发动机剧烈爆震或者燃烧恶化,顺便把边界约束校一遍。

第二步,用拉丁超立方采样补点。正交表偏重均匀分布,但对非线性强的区域覆盖不够。我在原正交点的基础上,用拉丁超立方补了30个点,重点加密高EGR率和高轨压区域。最终数据集是80组稳态工况点,发动机转速固定为1800r/min、50%负荷——这是这台试验机最典型的运行工况,也是排放法规认证常取的中间负荷点。

这里有个经验要提醒:试验时每个点都要等排气温度和冷却水温完全稳定再采数据,一般稳定时间不少于3分钟,每个点重复采样两次取平均。排放数据的重复性如果不好,后面神经网络模型学到的全是噪声,再厉害的优化算法也白搭。

2.3 BP神经网络代理模型的构建与校验

台架试验只能覆盖有限参数组合,而NSGA-II优化过程中需要评估成千上万组参数。你不可能在台架上一个个试,所以必须建一个“代理模型”。我选的是BP神经网络,原因很简单:在中等规模数据集上,它对高维非线性映射的拟合能力足够,Matlab实现也成熟,理工科背景的人上手几乎没有门槛。

网络结构设计成5-11-2:输入层5个节点对应五个决策变量,隐含层11个节点,输出层2个节点分别对应SOOT和NOx。隐含层节点数我是用经验公式估算的:sqrt(5+2)+a,a取2到10之间的整数,然后用交叉验证试了一圈,11个节点效果最稳。

数据处理上有一个关键细节:归一化。我用Matlab的mapminmax函数把输入输出都归一化到[-1,1]区间,训练完成后要把仿真结果反归一化回真实单位。很多新手直接在原始数据上训练,结果大数值的NOx把小数值的SOOT“压制”了,模型对SOOT的拟合精度极差。归一化的代码很简单:

[xs, ps_in] = mapminmax(X', -1, 1); % X为输入特征矩阵 [ys, ps_out] = mapminmax(Y', -1, 1); % Y为目标矩阵 net = newff(xs, ys, [11], {'tansig', 'purelin'}, 'trainlm'); net.trainParam.epochs = 1000; net.trainParam.goal = 1e-5; net = train(net, xs, ys);

训练集、验证集、测试集按70:15:15划分。我用trainlm(Levenberg-Marquardt)算法训练,收敛速度快,但要注意别过拟合。判断标准是测试集的R²要大于0.95,RMSE要小到工程上可接受——我当时的测试集R²,SOOT是0.97,NOx是0.96,基本满足代理精度要求。训练完把net和两组归一化参数ps_in、ps_out一起存成bp_model.mat,后面优化主程序要反复调用。

3. Matlab下NSGA-II多目标优化:算法机制与代码实现

3.1 NSGA-II为什么是这个问题的首选

既然是多目标优化,就得选合适的算法。能处理多目标的方法不少,比如加权法、字典序法、Pareto类算法。在Pareto类里,NSGA-II(带精英策略的非支配排序遗传算法)应用最广,我用下来也觉得它最“皮实”。

它的三个核心机制正好对症:

  • 非支配排序:把种群分成多个前沿层,第一层是互不支配的个体,也就是当前所有解的“最优集合”。这样算法不用人为设定权重,目标间的相对重要性由数据本身说了算。
  • 拥挤度距离:同一前沿层里,拥挤度大的个体优先保留,保证解在目标空间里均匀分布,避免优化结果全挤在一个角落。
  • 精英保留策略:父代和子代合并后统一选择,优秀个体不会在迭代中丢失,保证算法一定收敛到Pareto前沿,而不是在途中退化。

相比SPEA2这类基于强度值的算法,NSGA-II实现更简洁、参数不敏感,Matlab里无论是手写还是用工具箱,都有成熟方案。

3.2 主程序骨架与几个关键函数的代码视角

下面是我手撸的NSGA-II主循环骨架,每个工程细节抠过,直接可以当模板用:

% main_nsga2.m rng(2024); % 固定随机种子,保证结果可复现 pop_size = 150; % 种群规模 max_gen = 300; % 最大进化代数 n_var = 5; % 决策变量个数 lb = [0, 80, 0, 0, 0]; % 主喷正时、轨压、EGR率、预喷间隔、预喷占比 ub = [15, 160, 30, 10, 15]; % 初始化种群:均匀随机采样 pop = repmat(lb, pop_size, 1) + rand(pop_size, n_var) .* repmat(ub-lb, pop_size, 1); [obj, cons] = evaluate_pop(pop); % 调用BP模型预测soot和nox for gen = 1 : max_gen % 生成子代:锦标赛选择 + SBX交叉 + 多项式变异 offspring = make_offspring(pop, p_c, p_m); [off_obj, off_cons] = evaluate_pop(offspring); % 精英保留:合并父代与子代,按非支配排序+拥挤度选择 [pop, obj, cons] = elitism_select([pop; offspring], [obj; off_obj], [cons; off_cons], pop_size); % 记录第一前沿的进化轨迹,供收敛性判断 idx1 = nondominated_front(obj, cons) == 1; rec_soot(gen) = min(obj(idx1, 1)); rec_nox(gen) = min(obj(idx1, 2)); end

其中evaluate_pop是所有评估逻辑的汇聚点,负责把五维参数输入到训练好的BP模型里,输出两个目标的预测值,同时计算约束:

function [obj, cons] = evaluate_pop(pop) load('bp_model.mat', 'net', 'ps_in', 'ps_out'); X = mapminmax('apply', pop', ps_in)'; % 反归一化输入 Y = sim(net, X')'; obj = mapminmax('reverse', Y', ps_out)'; % 反归一化输出 % 约束示例:峰值压力约束(由试验标定经验拟合公式得到) p_peak = predict_peak_pressure(pop); cons = 18 - p_peak; % >=0 表示满足约束 end

非支配排序和拥挤度计算是NSGA-II的核心,代码我精简成了关键段:

function [rank, crowd] = nondominated_sort_and_crowding(obj) N = size(obj, 1); rank = zeros(N, 1); crowd = zeros(N, 1); S = cell(N, 1); n = zeros(N, 1); F = {}; F{1} = []; for i = 1:N for j = 1:N if i ~= j if dominates(obj(i,:), obj(j,:)) S{i}(end+1) = j; elseif dominates(obj(j,:), obj(i,:)) n(i) = n(i) + 1; end end end if n(i) == 0 rank(i) = 1; F{1}(end+1) = i; end end k = 1; while ~isempty(F{k}) Q = []; for i = F{k} for j = S{i} n(j) = n(j) - 1; if n(j) == 0 rank(j) = k + 1; Q(end+1) = j; end end end k = k + 1; F{k} = Q; end % 拥挤度:按目标方向排序,边界点无穷大,内部点取相邻距离和 for i = 1:k-1 front = F{i}; for m = 1:size(obj, 2) [~, idx] = sort(obj(front, m)); crowd(front(idx(1))) = inf; crowd(front(idx(end))) = inf; fmax = obj(front(idx(end)), m); fmin = obj(front(idx(1)), m); if fmax - fmin > eps for t = 2:length(idx)-1 crowd(front(idx(t))) = crowd(front(idx(t))) + ... (obj(front(idx(t+1)), m) - obj(front(idx(t-1)), m)) / (fmax - fmin); end end end end end

这段代码的逻辑是标准NSGA-II的实现,没做花哨优化。如果你追求性能,非支配排序那里可以用分级标记替代嵌套循环,复杂度从O(MN²)降到接近线性,但工程上几百个个体跑几百代,嵌套循环完全够用。

3.3 遗传参数与收敛判别经验

参数设置方面,我的经验值是这样一套:种群规模150,进化代数300,交叉概率0.9,变异概率取1/n_var=0.2,SBX交叉的分布指数eta_c取15,多项式变异的分布指数eta_m取20。交叉概率不要低于0.8,否则种群多样性流失很快;变异概率设太大又会把好解全打碎。

收敛性判断是很多人忽略的点。我建议每次迭代都记录第一前沿上SOOT和NOx的最小值、最大值,然后画出来看。如果连续50代里前沿边界的变化幅度小于5%,基本可以判定收敛。我这个案例里,大约跑到180代时前沿就不怎么动了,说明300代是留了足够余量的。

再强调一次随机种子。NSGA-II有随机性,不固定种子的话,每次跑出来的前沿虽然趋势一致,但具体点分布会有差异。论文、报告里要放优化结果,必须写死rng种子,保证可复现,这是学术写作的基本素养。

4. Pareto前沿解读与工程最优解的选取

4.1 从Pareto前沿图中能看到什么

优化跑完,把第一前沿的所有个体画在以NOx为横坐标、SOOT为纵坐标的散点图上,就是Pareto前沿。我的结果分布很典型:从左下角的“低NOx区”到右上角的“低SOOT区”,整体呈一条向右上延伸的曲线。

解读这个图不能只看“有一条线”。我通常会看三件事:

  • 看形态:如果前沿接近L形,说明两个目标强对抗,任何进一步压缩SOOT都必须付出明显的NOx代价;如果中间有一段接近水平,说明那一段里可以用很小的SOOT牺牲换取大量NOx下降,是“白捡”的红利区。
  • 看疏密:前沿上点与点之间的距离不均匀,说明某些区域有跳跃,可能是代理模型在那个区域预测不连续,或者原有试验点覆盖不足。我在25%EGR率附近就发现过一段“空白带”,后来补了试验点才解决。
  • 看拐点:前沿上曲率最大的地方,也就是“膝盖点”,通常是性价比最高的区域。在膝盖点左侧,多降一点NOx要付出很大的SOOT代价;在膝盖点右侧,多降一点SOOT要付出很大的NOx代价。

4.2 三种折中解选取方法实操对比

Pareto前沿上一排点都是“最优”的,但工程上最终只能选一个点上台架。怎么选?我对比过三种方法:

第一种,距离理想点最近法。先求出所有前沿点的理想点,即“若两者都能最小化”的虚拟点(取所有前沿点的最小NOx值和最小SOOT值构成TOPSIS里的正理想解),然后对每个前沿点计算到理想点的欧氏距离,距离最小者即为折中解。注意计算前一定要归一化,否则NOx数值偏大会主导距离计算。

第二种,TOPSIS法。这是个老牌多属性决策方法,核心是同时考虑正理想解和负理想解。它的做法是先求每个点到正理想解(各目标最小值组合)的距离S+,再求到负理想解(各目标最大值组合)的距离S-,然后计算相对贴近度C = S- / (S+ + S-),C越大越好。这个方法的优点是选出的解不仅“够好”,还能“离最差状态足够远”,稳定性更好。

第三种,熵权法。先用熵值法根据数据本身的离散程度确定两个目标的客观权重,再结合TOPSIS选解。熵权法不掺入主观偏好,适合你不知道该更看重哪个目标的时候用。

三种方法选出的解对比如下:

选取方法主喷正时(°CA BTDC)轨压(MPa)EGR率(%)SOOT(g/kWh)NOx(g/kWh)
距离理想点最近10.2146230.0211.75
TOPSIS10.6142240.0191.84
熵权+TOPSIS11.0138260.0231.59

三个解其实都在膝盖点附近,但微调方向略有差异。我最终选了TOPSIS的解上试验台,因为它对应的SOOT和NOx都在低水平,且鲁棒性相对好——发动机实际运行时参数会波动,这个点周围的目标面更平缓,参数偏移一点排放变化不大。

4.3 优化结果的台架验证与模型迭代

仿真结果必须回台架验证,这一步是检验整个路线真伪的“照妖镜”。我把选中的三个候选解都复现到试验台上,每个点同样稳定3分钟采数据。结果如下:

方案SOOT(g/kWh)NOx(g/kWh)
原机标定0.0312.10
优化解(台架实测)0.0171.88
模型预测0.0191.84
偏差-10.5%+2.2%

SOOT的预测偏差接近11%,说明代理模型在低碳烟区域精度还有提升空间。我的处理方式是把这组新实测数据加进样本集,重新训练BP网络,再做一轮NSGA-II优化。这个“优化→验证→补样本→再优化”的闭环迭代两轮之后,第三轮的预测偏差就压到了5%以内,最终稳定在某一个更优的点上,SOOT比原机标定下降了38%左右,NOx下降了10%左右。这个结果说明:代理模型精度直接决定优化结果可信度,而迭代式补点训练是提升精度的最实际手段。

5. 实战踩坑总结:给准备复现这条路线的人

5.1 数据质量是决定模型上限的那个“1”

我复现这条路线最大的体会:试验数据质量决定着整个链路的上限。神经网络不是万能的,你喂给它的数据含噪声、不重复、还覆盖不全,它拟合出来的就是一个“精心修饰过的错误模型”。

具体有两个坑值得说。第一,排放采样时机。台架稳定不是看水温表到了就行的,NOx对进气温度和湿度非常敏感,每次至少要等排气温度变化小于1℃/min再开始采。第二,冒烟点附近的重复性。EGR率偏高时碳烟测量值波动会显著增大,同一个参数组合重复采三次可能差出30%。这种点要么加密采样次数取中位数,要么直接删掉,否则模型会被个别异常点带偏。

5.2 神经网络模型在边界区域的“自信陷阱”

BP网络对训练数据覆盖范围内的插值能力强,但对范围外的外推完全靠“猜”。NSGA-II在搜索过程中立场很“贪”,它会尽量往决策变量的边界上试探,因为最优解经常就在边界附近。一旦试探出界,网络的预测值可能荒诞到极点,比如负的碳烟浓度。

我的解决办法是双保险:一是把所有决策变量的上下限设置时留出5%到10%的安全余量,比如轨压硬件上限160MPa,我在优化里设置上限150MPa;二是在evaluate_pop里加一个距离检验,当输入参数落在采样空间之外超过一定距离时,直接将该个体的约束值设为很大的负数,让它在非支配排序中被淘汰。

5.3 Matlab工具箱与自编NSGA-II的取舍

Matlab自带的gamultiobj函数(Global Optimization Toolbox)足够好用,我早期验证思路时就是用它快速跑通全流程的。但实际做研究时我改成了手写NSGA-II,理由有三点:工具箱对约束的处理比较“黑盒”,出了问题不好排查;工具箱默认的交叉变异算子不够灵活,我要改目标函数或约束表达往往要绕弯;最后就是论文和汇报里需要展示算法内部逻辑,手写代码讲解起来直观得多。

所以我的建议是:快速验证用gamultiobj,正式研究写自己的NSGA-II。两者并行,能互相校验结果是否合理。

5.4 复现性管理:归一化参数、随机种子与模型存档

最后一个容易被忽略的问题:复现性。我自己就吃过亏,隔了两周重新跑主程序,结果出来的Pareto前沿跟之前不一样。排查了半天,发现是之前训练好的BP网络没存档,重新训练时随机初始权重不同导致模型漂移。

所以现在的习惯是:所有试验数据存成一个固定版本的数据集文件;训练完的网络连同归一化参数ps_in、ps_out一起存成bp_model.mat;主优化程序里写死随机数种子;每一步关键结果文件带时间戳和数据集版本号保存。这样哪怕过半年回头复现,跑出来的结果都是一致的。

整套路线走下来,我的体会是:F-T柴油让SOOT和NOx这对老冤家的平衡点发生了系统性偏移,原有的标定经验和新燃料特性之间存在着巨大的优化空间,而“正交试验+BP代理模型+NSGA-II”这套组合拳,是把这个空间里的最优解找出来最务实的路径。多目标优化本身不难,难的是把台架、数据和算法真正闭环起来——每一环的粗糙都会在前沿图上暴露无遗。希望这篇分享能帮你少走几段弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进