尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

混合动力系统Simulink建模:能量管理与功率分配实战解析

混合动力系统Simulink建模:能量管理与功率分配实战解析 混合动力系统的Simulink模型说白了就是在MATLAB环境里把发动机、电机、电池、变速箱和整车动力学串联成一条能跑的仿真链路然后在上面跑能量管理策略和功率分配算法。这个方向我折腾了三年多从最开始打开Demo模型改参数都要查半天文档到现在可以独立从零搭一套并联混动整车模型踩过的坑确实不少。这篇内容想把这套模型的完整搭建思路捋一遍重点放在能量管理和功率分配两条主线上适合已经会用Simulink基本操作、想往新能源整车控制方向深入的人也适合刚接手混动项目还不太清楚从哪儿下手的工程师。1. 混合动力系统模型先把整车架构搭对1.1 拓扑方案怎么选串联、并联与混联的建模差异做混动Simulink模型第一件事不是拖模块而是把拓扑确定下来。这个决定会影响后面所有建模工作发动机和电机是机械连接还是电气连接直接决定了功率流动路径和解耦程度。串联拓扑里发动机不直接驱动车轮只带动发电机发电电能进入电池或者直接供驱动电机使用。建模时机械路径很干净但电气路径复杂发电机、驱动电机、直流母线电压控制、电池充放电电流波动这些问题都会冒出来。并联拓扑则是发动机和电机通过离合器或者扭矩耦合装置共同驱动车轮整车可以纯电行驶、纯发动机行驶也可以联合驱动。它的结构上多了一套耦合机构模型但不用处理母线电压动态功率分配逻辑反而更直观。混联拓扑是前两者的组合类似行星排结构建模时还要额外处理行星排的转速转矩关系发动机和电机的转速通过速比互相约束模型精度和复杂度同时上了一个台阶。我手里的项目是P2并联构型也就是在变速箱输入端布置一个电机离合器放在发动机和电机之间。选它原因很现实传统燃油车平台上改动最小电机一旦出故障车辆还能靠发动机继续跑控制策略也能按模块独立调试。要是你只是做教学或者预研验证我也建议从并联构型入手先把能量管理的核心逻辑跑通再往串联和混联延伸。1.2 Simulink顶层架构从物理域到控制域的模块划分拓扑定了以后我会把整个模型分成四层来搭每一层都能独立调试出问题时不至于把整个模型拖垮。第一层是环境和驾驶员层。这里读取标准驾驶循环工况比如NEDC、WLTC或者CLTC的车速曲线然后用一个PI控制器模拟驾驶员踩油门和刹车的行为输出加速踏板百分比和制动踏板百分比。很多人嫌这个步骤多余直接拿需求车速当作扭矩需求输入模型做能量管理时影响不大但一旦要评估驾驶性或者把控制策略部署到快速原型上这种简化就会坑人。这个驾驶员模型本质上就是Simulink里最常见的PID闭环应用比例项跟踪车速误差积分项消除稳态偏差参数调好后输出曲线非常接近真实踏板动作。第二层是物理域包括发动机模型、驱动电机模型、电池模型、传动系模型和整车纵向动力学模型。入门阶段不用追求高精度发动机用MAP查表加一阶惯性近似电机扭矩响应可以用一阶惯性传递函数替代效率MAP电池用Rint等效电路模型整车动力学用坡度阻力、滚动阻力和空气阻力的平衡方程来计算。够用就好后面做标定验证时再逐步加精度一上来就堆高保真模型只会让仿真速度慢得没法迭代。第三层是控制域也就是能量管理的核心。整车控制器接收驾驶员需求、车速、SOC、发动机转速、电机转速这些信号输出发动机目标扭矩、电机目标扭矩、发动机启停指令和离合器状态。对于并联混动这一层要完成的核心任务是把一条总需求扭矩拆分成发动机和电机两条扭矩路径同时保证电池SOC保持在合理区间这就是能量管理和功率分配要解决的核心问题。第四层是数据层包括各种Scope、To Workspace、数据字典和代价函数记录模块。我习惯把所有关键信号通过Bus对象组织起来统一送到一个信号记录子系统里后处理非常方便。这里有个经验必须说出来各层之间的信号交换尽量用非虚拟总线也就是定义好Bus Object的那一种而不是一根根散线到处拉。只要总线对象在模型初始化回调里预先加载接口就会很清晰后面无论是做MIL测试还是生成C代码都不用担心信号被Simulink悄悄改了维度。2. 能量管理策略规则、优化和学习式三种思路2.1 规则式策略状态机与查表能量管理策略是整个混动模型里最核心的部分。最常见的入门方案是规则式策略核心思想就是用一组阈值和查表把车辆状态划分成不同模式然后在每个模式里按固定比例分配功率。我在模型里用Stateflow的Chart搭了一个状态机定义了五个模式纯电模式、发动机单独驱动、联合驱动、制动能量回收、停车充电。模式切换条件尽量写得简洁比如SOC高于某一阈值且需求扭矩在电机能力范围内就进入纯电模式SOC降到阈值以下或者需求扭矩超过发动机经济区下限就请求启动发动机。状态机里最难的其实不是状态定义而是切换条件的标定。发动机启动阈值如果设太低发动机频繁启停油耗和NVH都受影响设得太高SOC会长期偏高电池寿命又成问题。我标定时把阈值做成模型参数跑完整WLTC循环观察SOC轨迹、发动机启停次数和油耗三个指标来回迭代很多轮才稳定下来。规则式策略的优点是实时性好、逻辑透明生成代码后可以直接部署到原型控制器上。缺点是整体效率离全局最优还有差距因为它本质上是把人的经验固化成了规则。但工程前期用规则策略建立一条基准线非常关键后面不管上LQR、MPC还是强化学习都要拿规则式策略的结果做对照否则你很难判断花大力气做的优化策略到底值不值。2.2 优化式策略LQR/MPC在功率分配中的应用想把能量管理做得更细就要引入优化算法。混动能量管理本质上是一个带约束的动态优化问题在满足驾驶员需求功率的前提下决定发动机和电机各自输出多少功率使整段行程的燃油消耗和电耗加权总和最小。LQR是比较容易上手的一类优化方法。基本思路是把功率分配问题线性化状态量取SOC偏差控制量取电机功率修正量目标函数里同时包含SOC偏差罚项和控制量罚项通过求解Riccati方程得到状态反馈增益矩阵K。Simulink里实现不需要额外工具箱矩阵运算用Gain模块直接乘上去就行计算量很小适合做实时控制器的底层算法。但LQR只能处理线性化后的系统发动机效率MAP和电池内阻这些强非线性因素会限制它的精度所以实际使用时往往要配一套前馈查表先把工作点拉到经济区附近再用LQR做动态修正。MPC是更完整的方案每个控制周期在线求解一个有限时域优化问题能显式处理SOC约束、电机功率约束和发动机外特性约束。Simulink里有现成的MPC Toolbox模块把被控对象模型填进去设定预测时域和控制时域就能跑起来。MPC效果确实好但计算量明显更大在快速原型上部署时一定要检查每个步长的求解时间能不能压进控制周期里。另外做MPC之前我特别建议先用动态规划离线算出全局最优解用这个解去校验MPC目标函数里的权值和约束边界是否合理否则MPC很容易在局部最优里跑偏仿了半天结果反而不如规则式策略。2.3 学习式策略Simulink与强化学习的结合路径这两年用强化学习做混动能量管理的热度很高很多团队都在试。Simulink和强化学习的结合主要有两条路我两条都走过。一条是纯MATLAB路线用Reinforcement Learning Toolbox创建RL环境把Simulink模型当作环境里的被控对象。具体操作是写脚本调用rlSimulinkEnv把模型里的观测信号、动作信号和奖励信号连出来再定义观测规格和动作规格用DDPG、PPO或者SAC这些算法训练智能体。这条路搭建方便模型内部物理细节不用额外暴露给训练框架但训练速度受Simulink仿真速度限制一个episode就是一次完整工况仿真跑几百上千轮确实需要耐心。另一条路是Python和Simulink联合这也是被问得比较多的一种方式。可以先把Simulink模型封装成DLL或者可执行文件然后在Python里用类似gym的接口去调用由Python控制工况加载、读取观测、下发动作。这条路灵活度高能无缝使用PyTorch这些框架但工程复杂度也上去了数据交换、同步、内存生命周期都要自己处理。我的建议是团队以MATLAB为主就走第一条路算法中台在Python就走第二条路不要两边来回折腾。不管选哪条路都要记住一点强化学习策略上车前必须做充分验证和对比。它本质上是黑箱策略训练时没覆盖到的工况一旦出现输出可能完全超出预期。至少要和规则式策略在同一组工况下对比油耗、SOC维持效果和约束满足情况确认可靠后再谈部署。3. 功率分配的实现细节从需求扭矩到SOC闭环3.1 驾驶员需求处理与扭矩路径分配能量管理策略最终要落到功率分配的数学实现上。并联混动模型里功率分配可以简化成一句话给定总需求扭矩决定电机扭矩和发动机扭矩各占多少。需求扭矩的来源要理清楚。驾驶循环给的是目标车速通过驾驶员模型的PI控制器输出加速踏板开度加速踏板开度再经一个整车扭矩需求表换算成驱动扭矩同时结合制动踏板开度生成总需求扭矩。这套换算在Simulink里用一维查表和一阶惯性模块就能搭出来但要注意加扭矩限制电机有峰值扭矩和持续扭矩限制发动机有外特性曲线限制所以需求扭矩还要经过一个扭矩仲裁环节把超出执行器能力的部分削掉或转移给另一个动力源。这个问题在仿真里很容易被忽略因为查表模块本身不会告诉你扭矩超限了。分配环节我习惯用一个分配系数k来实现电机扭矩等于k乘以总需求扭矩发动机扭矩等于一减k再乘以总需求扭矩。纯电模式下k等于1发动机不工作联合驱动模式下k在0到1之间电池需要充电时k为负值电机处于发电状态发动机要多输出一部分扭矩来给电池充电。这里有个容易出问题的地方发动机和电机的扭矩响应速度不一样。发动机从扭矩请求到实际响应会有几百毫秒的延迟电机响应则快得多。如果功率分配策略直接给发动机下发阶跃扭矩指令实际车速会和驾驶员需求产生明显波动。所以我会在分配模块输出端各加一个扭矩速率限制器电机的速率限制设大一些发动机的速率限制设小一些同时用一套优先级逻辑保证动态过程中的总扭矩仍然满足需求。这部分调起来最有意思也最花时间。3.2 SOC反馈与奖惩机制功率分配不能做到哪算哪一定要把电池SOC放进闭环里。电池模型我用Rint等效电路开路电压和内阻都做成SOC的函数查表获得。SOC的计算在Simulink里常见的做法有两种一种直接用Integrator模块对电池电流积分另一种是手写离散化的安时积分法。安时积分法的公式很简单SOC下一时刻的值等于当前SOC减去电池电流乘采样周期再除以电池额定容量。我在模型里一般直接搭一个离散子系统用Unit Delay保存上一时刻的SOC再把当前步的电流折算成SOC增量累加进去。这里有个细节在代码生成阶段特别重要SOC估算尽量别用连续积分器因为连续积分器在嵌入式环境里需要额外的求解器配置固定步长下还容易积分漂移离散安时积分生成代码后的行为和仿真完全一致省去一堆麻烦。说起来这套电池模型和功率闭环稍作扩展就能复用到双向储能控制仿真模型里本质上都是控制功率在源和储能之间的双向流动SOC估算算法和电池保护逻辑是共通的。有了SOC反馈接下来就是奖惩机制。最简单的做法是设计一个SOC惩罚项加入目标函数SOC靠近上限时惩罚电机使用鼓励发动机直接驱动并给电池小功率充电SOC靠近下限时惩罚发动机过度充电优先让电机参与驱动。这样策略不会把电池长期压在极值附近。实际调试中惩罚增益不能太大否则SOC在中间区域时策略会频繁改变分配系数造成功率分配抖动NVH和油耗都不好。正确做法是把惩罚项做成SOC滞回区间中间区域保持原有分配逻辑只在边界区域加大惩罚力度。如果要做更精细的优化可以用等效燃油消耗最小策略也就是ECMS。核心思想是把电池消耗的电能按一定折算系数换算成等效油耗与发动机实际油耗相加然后求解总等效油耗最小的电机功率。Simulink里实现不复杂每个步长枚举若干候选分配点选成本最小的那个。难点在于等效因子的在线调整SOC偏高时等效因子要减小SOC偏低时要增大这本质上又回到了SOC反馈闭环。3.3 关键参数标定过程模型框架搭好之后真正的功夫在标定。我把常用的标定参数整理成一个表方便按顺序检查。参数初值范围标定方法影响指标SOC工作窗口上下限30%~80%按项目电池寿命要求定SOC轨迹、充电能力发动机启动SOC阈值30%~50%跑WLTC观察启停次数油耗、启停次数电机助力分配系数上限0.3~0.7参数扫描仿真对比油耗、电耗SOC惩罚增益0.1~1.0从零逐步增大分配稳定性发动机扭矩速率限制50~200 Nm/s按响应实测或估计驾驶性、动态偏差制动回收扭矩分配比例0~0.5按电机能力和制动稳定性定回收能量、踏板感标定的基本流程是先在单一标准工况下用规则策略跑到稳定记录SOC初终值一致、发动机启停次数合理再换其他工况验证鲁棒性。每改一组参数我同时记录三样东西SOC轨迹是否在窗口内、发动机启停次数、等效百公里油耗。参数扫描不建议手工一遍遍仿真用脚本把参数设成向量跑批处理效率高很多。安时积分法这里再提醒一句SOC初值务必在初始化脚本里明确赋值否则模型默认从零开始整个能量管理策略一开始就偏了。4. 从模型到产品的关键一跳状态机、S-function与代码生成4.1 用Stateflow chart实现模式切换规则式策略和部分优化式策略都离不开模式切换逻辑Simulink里做这个的标准工具是Stateflow的Chart模块。混动控制的状态机设计有一些基本原则状态互斥、转移条件完备、状态内不能有残留输出。我通常把模式切换放在一个独立Chart里输入是SOC、车速、踏板开度、电机和发动机故障状态输出是当前模式编号和发动机启动请求。Chart里每个状态对应一组输出赋值比如纯电模式下发动机请求扭矩等于0电机请求扭矩等于需求扭矩离合器状态为分离。转移条件写在状态之间的边上可以是事件触发也可以是条件持续满足。这里需要特别提一下Simulink的Chart完全是学习状态机的好帮手状态图可以直接在编辑器里可视化比手写一堆if else直观太多。有个容易犯的错误把转移条件直接写成SOC小于30%然后在边界附近因为信号噪声反复切换模式在两条边之间来回抖。我的解决办法是加滞回进入发动机模式的下限设为SOC等于30%退出发动机模式的上限设为SOC等于35%留出5%的滞回带。同时还可以在转移边上加计时条件比如发动机启动请求必须持续200毫秒以上才真正触发过滤瞬态干扰。这条规则在Simulink里也有现成的滞回模块可以用但放在状态机里逻辑更清晰。Stateflow的状态覆盖率在模型测试阶段也要看。Simulink Coverage报告里可以看到状态是否全部访问、转移是否全部执行如果某条转移在标准工况下没跑到说明存在工况死角需要补测试用例。4.2 S-function Builder的正确用法与踩坑当控制算法里有不方便用普通模块搭建的逻辑时比如ECMS里需要调用外部求解器或者查一张很复杂的离线表很多人会选择写S-function。现代Simulink用S-function Builder可以直接生成C代码不用手写整套mex函数模板确实省事。我踩过最深的坑是多个S-function Builder模块之间的编译相互影响。这个问题的典型场景是模型里放了好几个S-function Builder每次编译其中一个其他模块的输出就变得不对。根源通常是编译目录和生成文件名称冲突。解决办法有三种给每个S-function Builder设置独立的目标名称在模型配置里把中间目录和最终目录分开如果情况允许优先用共享库的方式而不是每个Builder单独编译。还有一个土办法如果只是做仿真不做代码生成可以考虑直接用MATLAB Function模块用MATLAB语言写逻辑执行效率略低但开发和调试速度快很多。S-function Builder的另一个坑是数据类型隐式转换。默认情况下输入输出都是double但当你把某个端口改成单精度或定点数时C代码里如果没有显式转换就容易出现精度丢失。我的习惯是在S-function内部所有接口变量都显式声明类型不依赖Simulink自动推断这样生成代码后和仿真结果才能对齐。4.3 C代码生成与联合仿真的准备能量管理模型最终要落地躲不开代码生成。在Simulink里做C代码生成之前我会先做三件事把求解器改成固定步长离散求解器、把模型参数全部定义成parameter对象而不是工作区临时变量、检查所有子系统是否都支持代码生成。代码生成目标配置选ert.tlc一般会生成模块化的C代码。生成完代码后直接丢到真实控制器上还不算完真实环境里还有一堆传感器和执行器这时候就需要快速原型或者硬件在环验证。如果整车动力学部分不想自己搭比较常见的做法是联合仿真比如CarSim和Simulink联合仿真用CarSim的高保真车辆模型做被控对象Simulink里的控制策略模型做控制器通过联合仿真接口交换车速、踏板、扭矩这些信号。联合仿真的接口配置有几个技巧要掌握。第一数据类型要严格一致CarSim送出的车速是doubleSimulink接收端口也要设成double。第二采样时间要匹配CarSim通常有自己的一套仿真步长Simulink侧尽量用等步长避免变步长导致时序错乱。第三联合仿真模型里绝对不要用连续积分器做状态估计能离散的地方全离散否则仿真速度会慢到让人怀疑人生。5. 仿真调试中常踩的坑与排查思路5.1 代数环、积分器选择与隐含时序问题混动模型是多域耦合系统控制信号和物理信号经常在同一个步长内形成闭环所以代数环是几乎所有人都遇到的问题。Simulink里出现代数环最明显的症状是仿真速度突然变慢或者结果高频振荡原因就是信号在同一个采样时刻需要迭代求解才能从输出回推到输入。排查代数环的方法是打开诊断设置里的代数环报告或者用Model Advisor扫描模型会高亮显示代数环路径。修复办法优先在环路上加一个Unit Delay把闭环打断如果这个环节不允许延迟一步就重新梳理计算顺序把需要即时反馈的计算放进一个子系统里利用状态变量做隐式解耦。我的经验是能量管理策略里的扭矩仲裁和功率分配目标函数计算最容易产生代数环因为分配结果反过来影响发动机转速发动机转速又反馈到发动机扭矩能力查询表。正确做法是用上一时刻的转速来计算当前扭矩别在当前时刻做同步反算。积分器选择也是Simulink建模里常被问的问题。有人习惯用连续积分器有人喜欢手写Forward Euler积分器。手写离散积分器在特定场景反而更好比如SOC计算、电池热模型因为它的数值稳定和生成代码后行为可控。但用Forward Euler要记住它的稳定性条件步长不能大于系统最小时间常数的两倍否则数值会发散。5.2 Bus Selector没有可选信号与结构体参数管理这个报错我遇到过很多次报错信息大概是没有可选信号或者总线上没有列出的信号。出现这个问题绝大多数原因是总线信号对应的Bus Object没有正确加载到当前模型工作区。举一个真实场景别人发来一个模型里面有一个Bus Selector双击想选某个信号信号列表却是空的。打开模型资源管理器一看关联的Bus对象根本没有定义或者定义了但名字跟Bus Creator里设置的Bus Object差一个字母。解决办法是找到模型目录下的Bus定义脚本或数据字典重新加载Bus Object定义然后在Bus Selector里刷新信号列表。如果模型里信号比较多更推荐的方式是让Bus Selector根据Bus Object的端口维数自动推断不要手动输入信号名称否则总线内部信号改名后Selector不会自动更新。这里顺便说一下虚拟总线和非虚拟总线的区别。虚拟总线只是逻辑上把信号打包在一起物理上仍然是独立走线非虚拟总线会真正创建一个结构体类型的信号。Bus Selector两种都能选但To Workspace记录非虚拟总线时数据是结构体嵌套的后处理反而有意思。跟结构体相关的还有模型输入参数用结构体形式的问题可以把一组标定参数打包成一个结构体传给模型在InitFcn里用Bus to Struct或者Signal Conversion做转换整体接口会清爽很多。我之前做过一个项目所有标定参数都放在一个结构体里模型侧用Bus Creator重建信号无论仿真还是生成代码参数传递都是透明的再也不用担心工作区变量被覆盖。5.3 外部模式、实时仿真与覆盖率报告做快速原型时经常用到Simulink外部模式也就是模型在目标硬件上运行时可以通过Simulink界面在线修改参数、观察信号。配置外部模式时最常遇到的问题是一段时间后报出过载错误原因多半是仿真步长太小CPU算力不够。解决办法是先简化模型把不参与控制的物理细节降低采样率或者用模型引用方式把低频部分和高频部分放到不同任务里。我个人建议不要一上来就实时观测所有信号选几个关键量比如SOC、发动机扭矩、电机扭矩、等效油耗就够了在线调参时这些量已经能反映策略状态。覆盖率报告也是模型验证绕不开的内容。做功能安全相关的项目模型测试要用Simulink Coverage生成MC/DC报告说明测试用例对逻辑分支的覆盖程度。MC/DC要求每个条件的每个取值都能独立影响判定结果这对混动模式切换逻辑来说意味着工作量不小因为条件往往是SOC低于阈值且车速低于某值且无故障这种组合。实际操作时我一般先按标准工况跑一圈拿到基础覆盖率然后看报告里哪些Condition和True/False分支没覆盖到再有针对性地补驾驶循环片段比如单独跑一个大脚油门片段把联合驱动模式的条件覆盖到。Simulink Coverage报告里除了MC/DC还有行覆盖率、状态覆盖率、转移覆盖率。MC/DC报告重点看Modified Condition/Decision Coverage的百分比要求高的时候要达到100%达不到就继续补测试用例这个环节最花时间。调试时可以打开覆盖率高亮模型上会直接标出未覆盖的模块和分支排查效率比纯看报告快很多。最后再分享一个我个人特别受用的经验。功率分配与能量管理模型做得越久越觉得模型里最值钱的不是哪个模块用得高级而是参数管理工作做得好不好。我现在的做法是让所有模型参数都收敛到一个初始化脚本里统一管理脚本用结构体按子系统分门别类存放模型里再用命令加载到工作区。这样不管是一个月后自己重新打开模型还是同事接手维护都能从参数初始化那层把逻辑看清。之前有一次项目汇报前临时改了个参数发现工作区变量被覆盖模型行为完全异常排查了大半天才发现是这个低级问题。从那以后我再也不允许模型里出现没有来源说明的裸参数了。这个混动Simulink模型后续如果继续打磨我倾向于把规则式策略和优化式策略做成嵌套结构让模型大多数工况跑规则策略保证实时性和稳定性只在预测到的高效区间触发优化算法做到效率和可解释性兼顾。目前这套模型已经支撑了两个预研项目的可行性验证能量管理和功率分配的核心逻辑完整可复用。想往底层算法方向深入的朋友可以沿着文章里状态机、LQR、ECMS和强化学习这条路逐步替换控制模块每换一层都能看到明显的效果差异。
返回列表