
做混动能量管理策略或者整车经济性仿真的时候发动机最优工作曲线——也就是常说的最低燃油消耗率曲线——绝对是绕不开的硬骨头。我去年在做一个增程式混合动力的控制策略项目boost里跑了一圈下来发现发动机实际工作点离理论最优点差得很远。后来干脆用MATLAB写了一个发动机最优工作曲线计算程序把整个标定数据处理、最优曲线求解、可视化出图、数据导出的流程全部自动化。今天就把这个程序从需求拆解到代码实现再到应用落地的完整过程整理出来都是我自己踩过来的坑和实测有效的做法对于做发动机标定、混动控制、整车经济性仿真的人来说应该有直接的参考价值。1. 项目背景与需求拆解1.1 最优工作曲线到底是什么发动机万有特性BSFC Map是发动机性能最核心的表达方式横轴是转速rpm纵轴是扭矩Nm图上用等值线表示各工况下的燃油消耗率g/kWh。所谓发动机最优工作曲线指的是在每个转速下找到燃油消耗率最低的那个扭矩点把这些点连成一条线。发动机如果始终运行在这条曲线上理论上就能以最低的油耗输出当前转速下所允许的功率。打个比方这就像你爬楼梯的时候每一层都可以用不同的步幅和频率组合到达指定的高度但一定有某个步频区间让你的体力消耗最小。发动机也一样在相同转速下不同的扭矩负荷对应不同的比油耗总有一个工作点最省油。这条最省油的工作点连线就是混动策略里面最常说的最优工作曲线。1.2 这个程序解决的实际问题真实的发动机台架试验数据不会像教科书一样干净。我在实际项目里遇到过几个很现实的问题第一台架标定数据可能有上千个工况点人工在Excel里一个转速一个转速地找最低油耗点效率极低而且容易看走眼第二原始数据的测点分布不均低转速高负荷区域点很密高转速低负荷区域点很稀直接做min查找会把边界上的野值当最优点第三混动控制器要用这条曲线去查表控制发动机曲线必须平滑、单调、可外插否则Simulink查表模块会直接抖。所以我做这个程序的时候给自己定了三条需求第一输入原始万有特性测量数据输出平滑的最优工作曲线第二支持带功率约束的求解模式因为实际混动策略关心的是在目标功率下怎么分配转速扭矩最省油不是单纯每个转速下最省油第三出图要能直接用于标定报告和策略对比也就是说云图、等高线、最优曲线必须在一张图上清晰呈现。这里插一句工具选型的想法为什么用MATLAB而不用Python或者其他语言。核心原因是生态和交互。发动机的万有特性数据大多是台架系统的文本或Excel导出文件MATLAB的readtable和矩阵操作处理这类数据非常顺手插值和曲线平滑有现成的函数最关键的contourf绘图交互性极佳可以快速旋转视角、标注区域标定工程师一看就能理解。另外MATLAB和Simulink同源后续把最优曲线导出到2-D Lookup Table模块格式几乎无缝衔接。我自己也试过用Python的matplotlib画同样效果的图调整等值线间距和标注格式花的时间比画数据本身还长不值得。1.3 程序的整体功能定位这个程序不是简单的找最小值脚本而是一个轻量级的发动机工作点优化工具。它具备四个核心功能模块数据处理模块读入、清洗、网格化、最优曲线求解模块扫描法功率约束平滑后处理、可视化模块万有特性云图叠加最优曲线、数据导出模块输出Excel或CSV供策略模型查表。整个程序的核心逻辑围绕一个思想把试验数据变成策略可以直接用的控制规律。正因为定位是从数据到控制规律的工具它的边界我也划分得很清楚它不做发动机燃烧模型不做瞬态排放优化也不处理发动机和电机的联合效率寻优——那些是大系统级优化问题需要另外的工具链。我的目标就是把发动机本身的最优工作曲线这个零件做扎实后续不管接进增程器模型还是并联混动模型都能直接拿过去用。2. 程序架构与数据准备2.1 万有特性数据从哪里来发动机万有特性数据一般有三个来源最标准的是台架稳态工况测试按照不同的转速和负荷组合点在发动机热机稳定后测量燃油消耗率、扭矩、功率等参数其次是发动机仿真模型GT-Power、AVL Boost扫点导出的数据第三是厂家直接给的MAP表通常是Excel或者标定文件里的一段数据。不管哪个来源落到程序里都是同一类数据形式转速数组、扭矩数组、以及对应的BSFC矩阵。我建议把原始数据整理成一个标准CSV或者Excel表格格式如下转速(rpm)扭矩(Nm)功率(kW)BSFC(g/kWh)800806.7268.380012010.1252.780016013.4244.5实际项目中为了方便MATLAB处理我更喜欢把它读进来以后再转置成矩阵形式转速作为行方向或者列方向存储扭矩对应另一个轴BSFC构成grid矩阵。这里有个关键工作要做——去除非物理数据点。比如台架测试中怠速点、倒拖点、以及部分超出外特性的点直接参与寻优会把最优曲线拉到奇怪的位置。我的处理习惯是在读入阶段就加一个合法工况区的掩膜用外特性扭矩作为上限低于怠速扭矩的点直接排除。数据量方面一个典型的台架测试会有200到800个工况点测点的密度分布不均匀是常事。低转速区域扭矩分辨率可以到10Nm一个点高转速区域可能只有30Nm甚至50Nm一个点。直接使用这种不均匀网格计算最优曲线画出来的曲线会带着台阶感所以预处理阶段必须做网格插值加密。我个人推荐的做法是先把原始数据用scatteredInterpolant插值到规则的转速×扭矩网格上网格间距按需求和后期使用精度设定。比如后续要用于Simulink查表转速步长取100rpm扭矩步长取10Nm就够用如果要做得更精细可以加密到转速步长50rpm。2.2 数据清洗与插值加密的代码实现读取数据我用readtable因为项目现场给的原始文件经常是Excel列名可能还不统一readtable对这种情况容忍度最高。读进来以后先做去重同一转速扭矩组合出现多条记录按BSFC取最小或平均值。然后剔除异常值这里我不是简单粗暴地删掉偏离均值3倍的数据因为BSFC的分布本来就不对称等油耗线上有局部起伏是正常现象。我的做法是先用filloutliers基于movmedian方法把明显的野值替换掉再看替换后的变化幅度是否超过物理常识比如超过周边点30%以上才认定为野值。% 数据读取与清洗 data readtable(engine_fuel_map_raw.xlsx); n_raw data.RPM; T_raw data.Torque; B_raw data.BSFC; % 剔除重复工况点同一转速,扭矩取最小油耗 key round(n_raw/10)*1000 round(T_raw/5); % 按转速10rpm、扭矩5Nm分桶 [~, idx_unique] unique(key, first); % 分组取最小BSFC B_min accumarray(key - min(key) 1, B_raw, [], min);插值加密用的是scatteredInterpolant插值方法我建议选linear而不是natural因为natural在数据稀疏区域的曲面曲率变化太突兀画出来不像是等油耗面。插值以后网格范围必须严格限制在原始测点覆盖范围内任何超出外特性的外推点都要置为NaN否则后边的寻优会把非物理区间的数据当宝。% 网格插值 F scatteredInterpolant(n_raw, T_raw, B_raw, linear, none); n_grid 800:50:5000; T_grid 40:10:320; [n_mesh, T_mesh] meshgrid(n_grid, T_grid); B_gird_interp F(n_mesh, T_mesh);2.3 数据格式的规范化建议程序跑通以后我才意识到数据格式规范化的重要性。台架数据导出的时候列名可能是EX_001、EX_002这种可读性极差。建议在预处理阶段就把单位列名规范化统一命名为RPM、NM、KW、BSFC。同时功率列自己重新计算一遍因为实测功率和用扭矩转速换算出来的功率经常有微小偏差原因在于台架系统的测量通道存在细微的时间偏差和传感器迟滞。我自己在数据处理阶段就统一用公式 P T * n / 9549 重新生成功率列这样可以保证后续功率约束求最优的工作点功率始终保持自洽。这里多提一句数据清洗是程序里最容易被跳过但影响最大的环节。我第一版程序偷懒没有做清洗直接用原始数据寻优结果最优工作曲线在1200rpm附近莫名其妙地出现了一个凹陷后来排查发现是因为该转速附近有一个测点BSFC值被台架系统异常记录为320g/kWh是周边工况点的两倍以上单点野值直接带偏了整条曲线。从那以后我再也不敢跳过清洗环节。3. 核心算法实现与参数处理3.1 基于扫描法求解最优工作点最直接的最优工作曲线求解方法就是对每一个转速节点沿着扭矩方向搜索BSFC的最小值点。这个方法的物理意义非常清晰发动机运行在某一固定转速时控制策略需要决定让它输出多大扭矩而最小BSFC的扭矩值就是当前转速下的最优设定点。数学上可以写成BSFC_opt(n_i) min_j BSFC(n_i, T_j) 且 T_j ∈ [T_idle, T_max(n_i)]实现代码非常简短n_opt n_grid; % 转速沿用网格值 T_opt zeros(size(n_opt)); B_opt_line zeros(size(n_opt)); for k 1:length(n_opt) bsfc_col B_gird_interp(:, k); % 排除NaN区域 valid ~isnan(bsfc_col); if sum(valid) 10 T_opt(k) NaN; B_opt_line(k) NaN; continue; end [B_opt_line(k), idx_min] min(bsfc_col(valid)); T_opt(k) T_grid(idx_min); end这里有一个特别需要注意的点单纯按转速方向搜索出来的最优工作点在低转速区域往往落在外特性线附近因为低转速负荷越大单位功率的油耗越低。但这并不意味着策略应该让发动机在低转速极限负荷工作——实际发动机在接近外特性区域时爆压、热负荷、排气温度都很难控制台架数据也未必覆盖到极限区域。所以扫描法得到的原始曲线必须与外特性允许范围做交集约束同时要设定一个最低留出边界比如外特性扭矩的90%作为上限防止策略持续压榨极限。扫描法的优点是简单、稳健、无需求解优化问题计算速度在任何一台普通电脑上都是一眨眼的功夫。缺点也明显就是它对局部野值敏感而且把发动机看成每个转速独立寻优忽略了工作点切换时的过渡约束。所以这个结果一般作为基础曲线后边还要做平滑和约束处理。3.2 等功率约束的改进算法实际混动策略中发动机的控制逻辑很少单纯地说我今天要固定转速1800rpm请你输出最省油的那个扭矩。更多时候控制器收到的是一个功率需求比如整车的加速需求或者给电池充电的功率需求它需要决定的是这个功率P下用多少转速、多少扭矩的组合让发动机最省油。这就引出了功率约束的最优曲线算法。等功率约束的思路是在万有特性图中功率等于扭矩乘以转速即 P T × n / 9549所以每一条等功率线在转速-扭矩坐标系中是一条双曲线。算法要做的是在每条等功率曲线上找到BSFC最小的那个工作点。把这些点连接起来就得到了不同功率需求下发动机的最优工作组合曲线这比单纯按转速寻优更贴近策略的实际需求。我实现的思路是这样的先设定一个目标功率序列比如从10kW到80kW每5kW一个点。然后对每个目标功率 P_target在插值后的BSFC网格上找到满足功率近似等于 P_target 的所有点代入BSFC值取最小值。% 等功率约束寻优 P_target 10:5:80; T_power_opt zeros(size(P_target)); n_power_opt zeros(size(P_target)); B_power_opt zeros(size(P_target)); for k 1:length(P_target) P_map T_mesh .* n_mesh / 9549; % 找到功率接近目标值的点 mask_power abs(P_map - P_target(k)) 1.0; % 1kW窗口 bsfc_candidate B_gird_interp(mask_power); t_candidate T_mesh(mask_power); n_candidate n_mesh(mask_power); if isempty(bsfc_candidate) continue; end [B_power_opt(k), idx] min(bsfc_candidate); T_power_opt(k) t_candidate(idx); n_power_opt(k) n_candidate(idx); end功率窗口取多宽这里有个权衡。取得太窄候选点太少寻优结果容易跳变取得太宽候选点跨了很多工况最优值可能会落在距离目标功率很远的位置。我实测下来功率窗口按目标值的2%到3%取比较合适比如目标20kW就取±0.5kW。另外这个算法要求插值后的数据网格足够密否则等功率线穿过的网格太少候选点稀疏结果不稳定。我一般是先把原始数据插值加密到转速步长50rpm、扭矩步长5Nm再做功率约束寻优效果会好很多。3.3 边界处理与曲线平滑算法跑出来的原始最优曲线无论用扫描法还是功率约束法通常都带着毛刺和锯齿。原因有物理层面的台架测量波动也有数值层面的插值网格离散、等功率线穿过网格的位置跳跃。我最早的时候直接用这条锯齿曲线去生成控制策略查表结果发动机在台架上实测的时候转速小幅波动就导致扭矩指令来回跳明显不正常。解决办法分两步。第一步是边界约束把最优工作点限制在安全运行区间内对于转速方向最低转速不能低于台架标定的最小稳定转速我项目里的发动机是800rpm最高转速不能超过额定转速的95%对于扭矩方向任何最优工作点的扭矩不能超过对应转速下外特性扭矩的92%同时不能低于最大扭矩的30%因为太低扭矩的点或者说接近怠速的点一般在经济性上意义不大反而容易让发动机工作不稳定。第二步是平滑处理。我尝试过三种方案移动平均滤波、Savitzky-Golay滤波、以及分段多项式拟合。移动平均实现最简单但会降低峰值响应而且如果窗口取的过大最优曲线在高低转速的过渡区域会被拉得太平丢失真实特性。Savitzky-Golay滤波效果最好能保留数据趋势又抑制高频抖动MATLAB里一句smoothdata就能搞定。分段多项式拟合我试过一次需要手动选择分段点发动机不同转速区间的最优扭矩走向差异很大分段点位置对结果影响太敏感不适合做通用化程序。平滑以后有一个校验步骤必须做平滑后的曲线逐点计算BSFC看和未平滑前的差值是否在可接受范围内。如果某个转速点平滑前后的BSFC差异超过了8g/kWh基本可以判断平滑窗口过大或者该点本身就是野值点需要回到数据清洗阶段重新处理。这个校验在项目交付评审时尤其有用标定工程师一看这个表格就能判断曲线质量是不是可信。4. 可视化与结果输出4.1 万有特性云图与最优曲线叠加绘制程序的可视化模块我花了心思打磨原因是这个图要直接用进技术评审报告。万有特性图的绘制核心是contourf函数但直接用默认参数画出来的效果很糟糕颜色分界不明显等值线光滑度不够低油耗区不够突出。我的实际配置如下等值线层级c 220:10:340因为常见柴油机的BSFC范围在200到340g/kWh之间10为一个间隔可以平衡美观度和信息量。颜色映射用turbo或jet低油耗区用冷色调高油耗区用暖色调这样技术报告打印成黑白稿时通过灰度也能看出大致趋势。% 绘制万有特性图并叠加最优曲线 figure(Color, w, Position, [100 100 860 560]); contourf(n_mesh, T_mesh, B_gird_interp, 220:10:340, LineWidth, 0.5); hold on; % 绘制外特性曲线扭矩上限 plot(n_outer, T_outer, k-, LineWidth, 2.5); % 绘制最优工作曲线 plot(n_opt, T_opt, r--, LineWidth, 2.5); plot(n_power_opt, T_power_opt, g-., LineWidth, 2.5); colorbar; xlabel(Engine Speed (rpm)); ylabel(Torque (Nm)); legend({BSFC (g/kWh), External Characteristic, Optimal Curve (Speed Scan), Optimal Curve (Power Constraint)}, Location, northeast); title(Engine Fuel Consumption Map with Optimal Operating Curves);有一个绘图细节值得强调最优工作曲线一定要和BSFC的等高线图分图层清晰区分。我用红色虚线表示按转速扫描法得到的基础最优曲线用绿色点划线表示按功率约束法得到的改进最优曲线外特性用粗黑实线。这样审阅报告的人一眼就能看出两条曲线的差异和权衡关系尤其是功率约束曲线在低功率区会明显向低转速方向弯曲而转速扫描曲线则稳定地贴在外特性以下的某个区域。另一个实用技巧是标注特定工作点。比如全局最低BSFC点用实心五角星标记额定功率点用实心圆圈标记。这些关键点标注对控制策略的讨论很有价值评审会上工程师可以直接在图上指认发动机最经济点在2300rpm、240Nm附近策略应该尽量让发动机工作在这附近而不是盲目追高转速。4.2 结果导出与Simulink查表模块衔接计算出的最优工作曲线最终要导出给策略模型使用。导出格式我做了两种一是Excel/CSV方便标定工程师人工查看和对比二是MAT格式方便直接load到Simulink仿真环境里。Excel导出用writetable很直接关键是导出的列顺序要和后续查表工具的预期一致。我的习惯是第一列转速第二列最优扭矩第三列最优功率第四列最优BSFC列名全部用英文字段避免中文列名在部分查表工具中产生编码问题。% 导出数据 resultTable table(n_opt, T_opt, T_opt.*n_opt/9549, B_opt_line, ... VariableNames, {RPM, Torque_Nm, Power_kW, BSFC_g_per_kWh}); writetable(resultTable, optimal_curve.xlsx); save(optimal_curve.mat, n_opt, T_opt, B_opt_line, n_power_opt, T_power_opt);如果要把这条曲线喂给Simulink的2-D Lookup Table模块还需要做一步格式转换。因为Lookup Table要求以转速和扭矩或功率为输入查得对应的最优设定而最优曲线本质上是一个一维关系转速查扭矩。我建议生成一张二维表转速作为第一维输入分别对应最优扭矩、最优功率和最低BSFC值。这样在Simulink里只需要一个1-D Lookup Table加一个饱和度限制模块就可以实现最优工作控制。我在增程器模型中实测查表延迟可以忽略不计仿真稳定比直接在MATLAB Function里写插值逻辑要省心得多。5. 应用场景拓展与避坑指南5.1 在混动能量管理策略中的落地用法我在增程式混动项目里最优工作曲线主要用在了两处。第一处是增程器功率跟随控制。当电池SOC低于阈值时增程器需要按整车需求功率发电这时候程序输出的功率-最优转速-最优扭矩曲线就派上了用场。控制逻辑简化为整车控制器发出目标功率P_req增程器控制器查表得到最优转速n_opt和最优扭矩T_opt然后以这两个值为目标用转速PID和扭矩限制来闭环控制发动机工作点。实测下来发动机可以稳定地运行在最优曲线附近油耗比固定转速发电方案低了将近7%。第二处是并联混动的换挡策略参考在这个场景中最优工作曲线则用来评估发动机直驱模式下应该选哪个挡位。挡位决定发动机转速车速决定车速与转速比通过最优曲线可以查出当前车速下不同挡位对应的发动机BSFC然后选择BSFC最低的挡位。这个用法在DHT混动变速箱的策略开发中尤其常见。我把最优曲线直接换算成车速-挡位-最佳油耗表输入到换挡规律标定工具中省去了大量的人工实车标定迭代。使用场景上还有一个通用技巧最优工作曲线不能直接用于所有状态。在冷启动阶段、催化剂加热阶段、高海拔低气压环境发动机的最优工作点会发生迁移这时候必须通过修正因子对最优曲线进行偏移。我在程序里预留了一个修正量输入接口可以输入温度修正MAP和大气压力修正MAP程序会自动叠加修正量后输出最终控制目标曲线。这个设计在项目交付后被标定工程师反复表扬过。5.2 踩坑记录边界数据、插值陷阱与曲线跳变写这个程序的过程中我遇到过的几个典型问题值得单独整理成避坑清单。第一个坑是原始数据网格过疏导致最优曲线在高速区飞掉。我的发动机最长测试转速是4800rpm但台架数据在4000rpm以上的测点数量严重不足插值以后这几个转速的BSFC面形状很诡异扫描法找出来的最优点频繁落在扭矩边界上。后来我把插值范围限制在实际测点包络内所有超过包络的网格点一律置NaN问题立刻消失。这就是前面提到的数据掩膜的重要性宁可让高速区曲线短一截也不能让外推数据污染寻优结果。第二个坑是BSFC矩阵中存在局部极小点导致的误判。发动机万有特性在某些转速区域比如1800rpm附近由于涡流比和燃烧组织的差异BSFC会出现一个经济岛其最低点明显低于周边区域。按转速扫描法算出来的曲线在这个点会突然下跳然后回到正常位置。从纯数学角度看这是真实的极小值但是从控制策略角度这个点附近的工作窗口太窄转速偏差超过50rpm油耗就恶化很多实际不值得作为最优工作点。解决方法是给寻优加一个坡度限制条件某个转速下的候选最优扭矩与前一个转速的最优扭矩变化率不能超过设定阈值超过就用前一点的扭矩加上限制梯度来替代。第三个坑是可视化时contourf的等值线级别设置不当导致图像信息丢失。最开始我用默认的自动等级画出来全是一片蓝色低油耗区域完全无法识别。调整等级间距到10g/kWh以后经济岛的轮廓才能清晰地展现出来。如果报告需要打印成灰度等值线等级还要进一步微调确保最优曲线叠加在图上后不被色块淹没。第四个坑是单位混用。台架数据有的用kW有的用PS有台架的扭矩用kgf·m而不是Nm不统一换算就寻优结果完全不对。我的程序在读入阶段强制检查单位信息如果发现功率列最大值小于50且扭矩列最大值大于300大概率是功率是kW、扭矩是kgf·m会自动按换算系数修正。这看起来是个土办法但确实帮我避免过一次低级错误。5.3 后续扩展思路这个程序做完基础版以后后续扩展的空间其实挺大。我目前已经在做的一个方向是把排放约束加进寻优目标。发动机低油耗区域和低NOx排放区域并不完全重合单纯按BSFC寻优可能会把工作点推向高排放区。如果你面临的是国六乃至更严苛的排放标准建议把原始寻优目标从单一BSFC改成 BSFC λ×NOx 的加权组合λ可以按排放法规的要求和催化剂转化效率动态调整。这个扩展在算法层面只增加了两个输入矩阵和一个加权项改动成本很小但对策略可落地性的提升非常明显。另一个扩展方向是把最优工作曲线从稳定工况拓展到动态工况。发动机在转速和扭矩快速变化时最优工作点会因为进排气动态、涡轮迟滞等因素发生瞬态偏移柴油机尤其明显。如果要考虑动态寻优可以在当前最优曲线的基础上叠加一个动态修正系数系数可以通过瞬态台架试验标定也可以通过仿真模型的动态工况扫描生成。我当时给程序加了一个动态修正表的输入接口底层逻辑是转速变化率乘以一个修正系数加到目标扭矩上实测效果可以有效抑制动态过程油耗和排温的波动。多目标寻优和大数据处理相结合是更前沿的方向。如果发动机数据量达到数万甚至数十万点例如从虚拟仿真平台批量扫描生成的合成数据可以用MATLAB的Optimization Toolbox做整体优化而不是简单逐点扫描。我曾经用fmincon写过一个基于BSFC曲面的连续寻优版本在约束外特性曲线上限的前提下同时优化转速和扭矩两个变量得到的曲线比逐点扫描更加平滑只是需要填写一个合理的初始猜测值。初始值直接用扫描法的结果就好收敛速度非常快。这两个版本程序我都保留了代码路径换一个开关参数就能切换运行模式实测下来扫描法适合快速检查数据质量优化法适合生成最终的策略控制表各有各的用武之地。