双电机电动车扭矩分配:NMPC控制与Simulink仿真实践 简介面向双电机电池电动汽车的扭矩分配优化这里提供一套完整的非线性模型预测控制器Simulink实现方案。资源内容从整车与双电机动力学建模出发覆盖预测模型构建、滚动优化求解、控制律分配与闭环验证等核心环节同时附带项目报告、运行说明、系统架构图和仿真结果图便于读者对照复现和修改调试适合车辆工程、控制科学等方向的研究生、工程师以及对预测控制感兴趣的初学者学习使用。压缩包共包含27个文件其中有7个MATLAB脚本文件、3个Simulink模型文件整车虚拟模型、电气系统架构模型、可配置被控对象模型另有2份PDF文档、11张图片以及配置说明与许可文档资源总大小仅为4.44MB小巧但结构清晰便于下载和本地运行。当前已有33人学习浏览读者可依据模型与代码快速搭建双电机纯电动汽车的能量管理仿真环境结合文档理解非线性模型预测控制在扭矩分配中的设计思路、优化目标与调试方法从而有效缩短前期研究与开发时间。1. 双电机电动汽车的扭矩分配为什么偏偏用 NMPC双电机电池电动汽车的扭矩分配在实车上就是一场毫秒级的拉锯战前轴电机和后轴电机各有独立的效率区间SOC 不同、车速不同最优分配点随时在变。查表法能稳住低速匀速工况一到爬坡、急加速和能量回收叠加出现的场景规则之间就开始互相冲突。非线性模型预测控制NMPC的价值在于把未来一段时间的整车功率需求、电机效率边界、电池放电约束都卷进滚动优化每步重新算一次前后轴扭矩指令。这套工程包正好把整车建模、电气架构和 NMPC 控制器拆成三个可以独立打开的 Simulink 模型适合整车控制方向的工程师当基准实现也适合做策略对比时参考。2. 先拆工程三份 slx 文件在 Simulink 里各管什么2.1 虚拟车辆模型和整车被控对象的边界压缩包里除去源代码和文档核心内容是三份.slx模型ConfiguredVirtualVehicleModel.slx、ConfiguredSimulinkPlantModel.slx、ElectricalSystemArchitecture.slx。文件名里都带 Configured说明作者交付的是参数已经配好、加载即可仿真的工程不是只有框架的空模型。这一点很重要因为 NMPC 这类控制器对模型参数的一致性极度敏感哪怕效率曲面差两个百分点优化结果都会偏。ConfiguredVirtualVehicleModel.slx是整车级被控对象里面包含纵向车速动力学、轴荷转移、车轮和坡度阻力负责把前后轴扭矩指令换算成整车响应。ConfiguredSimulinkPlantModel.slx则更偏向控制器视角通常会把电机、电池、传动效率折进去NMPC 做内部预测时用的简化模型也经常从这一层抽。两者的关系可以类比台架上的实车和试验用的标定模型前者产生仿真真值后者给控制器当「内模」。调试时如果只改其中一个很容易出现控制器的预测和虚拟车辆的真值对不上。2.2 电气系统架构与数据流ElectricalSystemArchitecture.slx描述的是电池包、DC-DC、两台电机逆变器之间的功率链路。这一层决定了代价函数里的能耗项从哪来如果只看电机轴端效率而忽略电池内阻损耗大功率工况下分配结果会偏向某一台电机实际电池电流反而变大。打开这个文件时重点看电池模型的 Thevenin 等效内阻和电压外特性曲线它们是能量管理优化里最容易被忽略的约束。三个模型之间的信号流通常是控制器把需求扭矩指令发给虚拟车辆模型虚拟车辆模型回传车速、SOC 和当前挡位状态电气系统架构层输出电池电流和母线电压。实际操作时我一般会用Simulink.Signal给总线里的Demand_Nm、Actual_Nm、SOC_pct显式命名并标注单位不然换个人接手前后轴的扭矩命令很容易接反控制器以为在优化能效实际上在做反向充放电。2.3 用脚本把模型结构先打印出来再动手拿到别人的仿真工程最忌讳直接按 Run。模型里有多少 Goto/From 块、哪些子系统藏了硬编码扭矩限制双击一层层翻很难看全。我习惯先用一段 MATLAB 脚本把模型结构和关键参数扫一遍再决定从哪里下手改。mdl ConfiguredVirtualVehicleModel; % 模型名要和 slx 文件名一致 load_system(mdl); % 只加载模型不弹界面 % 列出顶层所有模块确认接口命名 topBlocks find_system(mdl, SearchDepth, 1, Type, Block); disp(topBlocks); % 抓出所有 Gain 模块排查硬编码的扭矩系数和传动比 params find_system(mdl, FollowLinks, on, LookUnderMasks, all, ... BlockType, Gain); for i 1:length(params) gainVal get_param(params{i}, Gain); fprintf(%s - %s\n, params{i}, gainVal); end这段脚本里load_system只加载不打开界面适合批量处理find_system配合FollowLinks,on和LookUnderMasks,all能把封装子系统内部的 Gain 参数也挖出来。对于那些藏在 Mask 里的峰值扭矩限制、传动比、扭矩滤波系数用这个办法能在跑仿真之前就暴露出来避免后面改了半天发现限制条件根本没生效。下表把三份模型的定位和检查顺序整理清楚建议按这个顺序打开核对文件在系统中的角色打开后重点看检查顺序ElectricalSystemArchitecture.slx电池-逆变器-电机功率链路电池内阻、电压外特性1ConfiguredVirtualVehicleModel.slx整车纵向动力学被控对象车速计算、轴荷转移2ConfiguredSimulinkPlantModel.slx控制器内部使用的预测模型需求扭矩到轴扭矩的折算3先核对电气架构再核对整车模型最后看控制器的内模能避开一种典型错误在 Plant Model 里改了电机效率却忘了同步 Virtual Vehicle Model仿真曲线看起来优化有效实际两套模型眼里的根本不是同一台车。3. NMPC 的预测模型、代价函数和约束怎么落到代码上3.1 预测模型从整车纵向动力学到双电机效率 MapNMPC 的预测模型不是越精细越好而是在保留关键非线性和保证每步优化能算完之间取折中。扭矩分配问题里必须保留的非线性有两处一是电机在不同转速和扭矩点下的效率曲面二是电池内阻随 SOC 和温度的变化。前者用二维 Lookup Table 表达后者用一维查表或多项式拟合都可以。常见做法是把整车纵向动力学缩成一个离散状态方程状态量取车速v和电池 SOC控制量只取前轴扭矩T_f后轴扭矩由需求扭矩减掉T_f得到。这样双输入问题被压成单输入预测时域的维度和约束数量同时下降求解难度明显降低。我在实际搭建时用的预测函数如下。function x_next predictDualMotor(x, u, p) % x [v; SOC], u [T_f] v x(1); soc x(2); T_d p.T_demand; % 当前驾驶员需求扭矩 T_r T_d - u(1); % 后轴扭矩由差值决定 % 按转速限制前后轴扭矩边界防止优化器把控制量推到物理极限外 T_f_max interp1(p.speedAxis, p.frontTqMax, v, linear, extrap); T_r_max interp1(p.speedAxis, p.rearTqMax, v, linear, extrap); u(1) max(min(u(1), T_f_max), -T_f_max); T_r max(min(T_r, T_r_max), -T_r_max); % 查电机效率 Map注意单位扭矩 Nm转速 rpm eff_f interp2(p.tqAxis, p.speedAxis, p.frontEffMap, u(1), v); eff_r interp2(p.tqAxis, p.speedAxis, p.rearEffMap, T_r, v); % 电池功率两台电机的机械功率分别除以各自效率 Pb u(1)*v/p.wheelR/eff_f T_r*v/p.wheelR/eff_r; x_next zeros(2,1); x_next(1) v p.dt/p.m * (T_d/p.wheelR - p.F_res(v)); % 纵向动力学 x_next(2) soc - p.dt*p.Uoc/(3600*p.Qbatt) * Pb; % SOC 更新 end这段代码里interp2用的效率数组必须和 Simulink 模型里的 Lookup Table 保持一致这是工程里最容易出偏差的地方。x_next(1)的更新默认轮胎处在附着极限内如果工况包含低附着路面需要在约束里再加轴荷利用率项否则预测的加速能力比实车乐观。x_next(2)用恒定开路电压近似内阻损耗精度不足时把它替换成 Thevenin 模型的端电压表达式代价是多一个状态和一个非线性方程。3.2 代价函数扭矩偏差、能耗与电机损耗的加权优化问题写成标准形式就是滚动求解一个带约束的最小化问题。目标函数一般取三项扭矩跟踪性、能量经济性、扭矩变化平滑性。三项权重的量纲完全不同不能直接比较数值大小要先各自归一化再调参否则调w1时根本不知道它和能耗项的量级差。代价项表达式默认权重调参方向扭矩跟踪(T_f T_r - T_d)^2w1 1起步加速阶段可适度减小能量消耗Pb * dtw2 50续航测试工况下加大扭矩变化率(u_k - u_{k-1})^2w3 2出现抖动时加大w2给到 50 不是随意写的电池功率的量级通常是几十千瓦不放大它在平方项里根本压不过百牛米级的扭矩偏差。实车标定时我一般固定w1把w2从 1 开始十倍往上试观察 SOC 下降曲线和扭矩跟随误差的拐点找到「能耗明显改善但跟踪误差还能接受」的区间再回头细调w3抑制抖动。三个权重之间不是独立关系调完w2后w3的效果会被放大需要回头复验一轮。提示代价函数里的每项都要除以对应的归一化系数比如扭矩项除以T_d^2能耗项除以Pb_nominal否则不同工况下权重比例会漂移。3.3 求解器选型与实时性约束NMPC 每步都要解一次非线性优化实时性是这个工程落地时最大的坎。小规模问题两三个状态、单个控制量用fmincon的内点法就能跑纯离线仿真完全够用。如果要上硬件在环就得换 CasADi 配 IPOPT或者退一步把预测模型在当前工作点线性化退化成基于 LTV 的 MPC用二次规划求解器把单步求解时间压到 10 ms 以内。Simulink 里最常见的接法是用 MATLAB Function 模块包住优化求解函数内部用coder.extrinsic声明fmincon这样普通仿真能跑将来做 Embedded Coder 代码生成时也留了替换入口。但要注意fmincon每次调用都要传函数句柄内部会反复触发效率查表仿真步长设得太小时这一步会成为整环瓶颈。我的做法是把预测模型写成独立的predictDualMotor函数优化器只负责迭代调用这样既能单独调试预测模块也方便后续把效率表换成 Response Surface 或者神经网络回归来加速查表。4. 冷启动与排错把仿真跑通并让控制器稳定工作4.1 按顺序加载模型再运行工程包里有How to run simulation.pdf但在按文档操作之前我建议先在脚本里把三份模型按依赖顺序加载一遍。电气架构模型定义总线类型和物理参数虚拟车辆模型依赖它控制器模型又依赖车辆模型的接口顺序错了会报「未定义的总线对象」。models {ElectricalSystemArchitecture, ... ConfiguredVirtualVehicleModel, ... ConfiguredSimulinkPlantModel}; for i 1:numel(models) if bdIsLoaded(models{i}) close_system(models{i}, 0); % 不保存关闭防止动到原工程 end load_system(models{i}); end set_param(ConfiguredVirtualVehicleModel, StopTime, 50); simOut sim(ConfiguredVirtualVehicleModel);close_system(models{i}, 0)的第二个参数0表示关闭时不保存改动这一点很关键调试过程中改的采样时间、求解器设置都会被丢弃原工程文件保持干净。跑完后simOut里能拿到logsout和yout等默认输出具体变量名要看模型里的 Signal Logging 配置下一步验证时直接引用就行。4.2 求解器配置和采样时间双电机整车模型横跨机械和电气两个时间尺度车速动态是秒级电池电流和逆变器开关动态是毫秒级。仿真步长选不好就会出现「车速没变化、电池电流高频振荡」的怪现象。这类模型我一般不用默认的ode45而是按下面的表来配。配置项推荐值说明Solverode15s 或 ode23t模型含刚性环节时比 ode45 稳Max Step Size0.01 s限制电气振荡对数值积分的影响Filter Time Constant0.05 s给扭矩指令加一阶滤波减少抖动NMPC 采样周期0.02 ~ 0.05 s决定滚动优化的执行频率如果模型刚性不强用ode45配合Max Step Size也能跑但在急加速或重启动变工况时容易报「步长在 t.. 处小于最小步长」。看到这个提示先别急着改最小步长限制把Max Step Size从auto改成0.01通常能消掉大部分报错。NMPC 采样周期和求解器步长之间没有强制关系但建议 NMPC 的更新周期是求解器最大步长的整数倍否则每次优化结果在两次采样之间会被零阶保持器切得很难看。4.3 常见报警代数环、S 函数版本和效率 Map 越界第一次跑这类工程三个报错出现频率最高。第一个是代数环扭矩指令经过效率计算反查电池电流电流影响电压电压又回算功率Simulink 检测到直接求值回路就会报警。处理办法是在反馈路径上放一个 Memory 块或者把电气模型改成用上一时刻的电压代价是损失一点精度但能有效切断代数环。第二个是 S 函数和代码生成版本问题。如果 MATLAB Function 里用了coder.extrinsic老版本编译器在做代码生成时报「不支持该函数」很常见。先确认仿真模式能跑通再考虑生成代码不要一上来就奔着生成目标去。第三个是效率 Map 越界电机转速或扭矩超过查表区间上界时interp2默认返回 NaNNaN 一路传播进优化器fmincon直接失败。给查表加linear, extrap只是权宜之计正规做法是在模型里加饱和模块把预测用的转速和扭矩钳在表格范围内同时在代价函数里对越界量加惩罚项双保险才稳。5. 从仿真数据反推控制效果一个可复用的验证技巧5.1 把 logsout 信号整理成可对比的表格仿真跑通只是开始真正判断 NMPC 有没有优于规则分配要用同一段工况分别跑 NMPC 和 rule-based 策略再对比 SOC 下降量和扭矩跟随误差。前提是信号记录足够规范。目标信号建议在 Signal Logging 里统一命名为v_meas、T_front、T_rear、SOC_pct、Pb_w后处理脚本可以直接复用。logsout simOut.get(logsout); v logsout.getElement(v_meas).Values.Data; Tf logsout.getElement(T_front).Values.Data; Tr logsout.getElement(T_rear).Values.Data; soc logsout.getElement(SOC_pct).Values.Data; Pb logsout.getElement(Pb_w).Values.Data; splitRatio Tf ./ (Tf Tr eps); % 前轴扭矩占比 totalEnergy trapz(Pb) / 3600; % 积分得到总能耗单位 kWhsplitRatio是判断控制器行为最直观的指标规则分配在恒扭矩区间是平坦的NMPC 的曲线会随车速和 SOC 动态起伏起伏的幅值和频率反映优化器对效率边界的响应。trapz对功率采样点做数值积分算出的总能耗要和 SOC 变化量交叉验证如果两者方向矛盾说明电池模型的内阻损耗设置有问题查完模型再回来谈策略优劣。5.2 用前轴占比和能耗做横向对比把不同权重配置下的结果叠在同一张图里比逐条看曲线高效得多。对比时固定三个指标平均跟踪误差mean(abs(T_d - Tf - Tr))、总能耗、扭矩波动率也就是前后轴指令变化量的标准差。这三个指标正好映射代价函数里的跟踪项、能耗项和平滑项权重改没改到位一眼就能看出来。滚动优化里一个常被忽略的细节是初始控制序列的选择。如果每步都用上一时刻的解当初始点急加速工况下优化器容易困在局部解表现为扭矩分配突然跳到边界值再弹回来。我一般会把「零控制量」和「上一时刻解」各算一次代价取较小者作为当前步的起点计算量翻倍但稳定性明显改善。这个技巧在 MATLAB Function 里改动只有几行却是仿真曲线从锯齿状变平滑的分水岭。权重、采样周期、初始序列三点调完再回头看splitRatio曲线是否保留清晰的物理含义——前轴占比应该在高 SOC 时偏向高效区低 SOC 时偏向内阻损耗更小的电机而不是随工况无规律乱跳。本文还有配套的精品资源点击获取