前段时间我把一台纯电车型的制动能量回收策略从一整套离散公式,真正搬到了CRUISE整车模型里跑联合仿真。这个工作看着简单,实际卡了我不少时间:策略本身不算复杂,麻烦的是CRUISE和Simulink之间那个DLL接口要处理干净,不然策略算得再精细,整车模型里乱输出一样白搭。整车模型涉及车辆、电池、电机、传动、制动器多个模块,控制策略则要同时处理踏板信号、SOC、电池温度、ABS标志这些输入,最终给电机一个再生负扭矩指令、给液压制动一个修正系数。这篇就把整个CRUISE纯电动车仿真模型的搭建过程、电制动优先能量回收策略的写法、以及Simulink DLL联合仿真的对接细节完整拆给大家。做整车控制策略、制动系统仿真、能量流分析的人,尤其是一开始想把“策略公式”变成“能跑的联合仿真”的工程师,应该能从这里省下不少弯路。
1. 为什么是CRUISE加Simulink DLL:这套组合解决了我什么问题
1.1 内建能量回收模块的局限
CRUISE本身提供了制动能量回收相关的内建函数,可以在制动器、电机的数据里定义回收系数、回收启用条件。对简单项目,比如“低速不回收、高SOC不回收、固定回收比例”,内建模块完全够用。但我要做的“电制动优先”不是一条查表规则,它要同时受制动踏板需求、当前车速、电机转速、电池SOC、电池最大充电功率、电池温度、ABS激活状态多个变量约束,而且每个采样周期都要重新计算一次分配比例。用内建函数做这种逻辑,要么把条件拆成无数个查表和开关模块,要么每改一次条件就重启仿真,开发效率太低,还容易出错。更麻烦的是,内建策略不太方便做单元测试,你很难单独验证“某条边界条件下控制策略是不是按预期输出”。
1.2 DLL联合仿真的核心价值
后来我换成Simulink DLL联合仿真,思路就顺多了。Simulink里用纯模型搭控制策略,每个输入输出都看得见,逻辑用模块、状态机、MATLAB Function都可以实现。调好后把模型编译成一个独立的DLL文件,然后在CRUISE的DLL模块里加载它。CRUISE在每个步长调用这个DLL,把车速、踏板、SOC、转速等信号送进去,再从DLL拿回电机扭矩指令和液压修正系数。这种方式的优势是整车模型和控制策略彻底解耦:改策略只动Simulink模型,重新编译DLL,CRUISE整车模型完全不用重建。对于多轮迭代的控制策略开发,省下的时间非常可观。
1.3 为什么不是FMU或其他外部接口
团队里也有人问,为什么不直接做FMU或者外部软件在环连接。FMU是标准的联合仿真方式,核心问题反而是标准太开放,很多场景需要额外验证数据交换的成功率。外部软件在环则需要每步网络通信,延迟比DLL高。DLL是最“重”但也最可控的方案,策略和整车循环频率完全一致,最适合制动能量回收这种需要快速响应的控制。我在这个项目里最终选了DLL,主要原因是调试直观、依赖少。
| 方案 | 灵活性 | 调试效率 | 实时性 | 适合场景 |
|---|---|---|---|---|
| 内建函数模块 | 低 | 低 | 高 | 固定规则、简单逻辑 |
| FMU/FMI | 中 | 中 | 中 | 跨工具协同建模 |
| Simulink DLL | 高 | 高 | 高 | 复杂策略、多轮迭代 |
2. 纯电动整车模型搭建:参数、子系统和制动结构怎么落位
2.1 整车参数与动力总成选型
建CRUISE模型第一步是整理整车参数。这步不能省,因为后面所有能量回收效果都建立在整车阻力、质量、轮胎尺寸这些基础数据上。我这版项目用的是一台前驱纯电车:整备质量1520kg,满载1850kg,迎风面积2.28平方米,风阻系数0.28,滚阻系数0.012,轮胎滚动半径0.317m。电机峰值功率120kW,峰值扭矩280牛米,最高转速12000rpm,减速比8.28,单挡减速器。动力电池容量52kWh,额定电压355V。这些参数直接填到CRUISE各模块里,如果后续要研究不同车型,只需要改参数表,模型结构不用动。
提示:参数里最容易被低估的是轮胎滚动半径。这个值直接参与轮端扭矩和车速的换算,差一厘米,回收扭矩指令可能就差出好几个百分点。
2.2 关键子系统配置
电池模块里要填OCV-SOC曲线、充放电内阻、容量、温度修正系数。能量回收策略的准确度很大程度取决于电池充电功率是否被正确建模。如果电池最大允许充电功率设得过于理想,DLL给出的电制动扭矩上限就会偏大,仿真出来的回收能量会比实际乐观很多。电机模块填效率map和外特性曲线,外特性曲线决定了不同转速下能产生的最大驱动和再生扭矩。
这里特别提醒一下:CRUISE里电机输出正扭矩就是驱动,输出负扭矩就是发电。制动能量回收本质上是让电机工作在发电象限,所以效率map在第二象限的数据必须完整,不然回收能量计算会失真。我见过有人电机效率map只有驱动象限数据,跑回收工况时模型直接按默认效率来算,结果回收能量偏大,拿这个数据去做续航预测完全不靠谱。
2.3 制动系统模块的建模方式
CRUISE的制动器模块有几种输入方式:按压力输入、按扭矩输入、按信号输入。做联合仿真时我建议用信号控制模式:Simulink DLL给出前轴电制动扭矩和液压修正系数,CRUISE里的制动器接收这些值,直接转换为机械制动力。
具体到制动结构,我采用的是前轴电制动叠加液压、后轴纯液压的构型。电机在前轴,所以电制动扭矩通过传动系统作用在前轮;后轴只提供液压制动,负责保持车辆稳定。DLL输出的液压修正系数会作用在前后轴主缸压力上,修正后的液压制动力加上电机负扭矩,共同满足驾驶员的制动需求。这种构型在策略上比较干净,因为电制动只作用于前轴,不用处理复杂的四轮扭矩分配。
2.4 CRUISE模型里的信号连接
在CRUISE中,DLL模块需要连接的关键信号有:车速、制动踏板行程、挡位、电机转速、电池SOC、电池允许充电功率、电池温度、ABS触发标志。输出信号是电机负扭矩指令和液压制动修正系数。连接好后,先跑一个空载滑行验证模型阻力是否正确,再跑制动工况。CRUISE的监视器可以直接看信号,联合仿真调试时很好用。
3. 电制动优先策略在Simulink里怎么写
3.1 这个概念不等于全电制动
“电制动优先”很多人理解成“只要踩刹车,电机就把车刹停”。不是的。真实的电制动优先策略是在满足总制动力需求的前提下,先把电机能提供的再生制动力用满,剩余部分由液压制动补齐。简单说,驾驶员要10份制动力,电机制动能给6份,那就出6份电制动,液压只出4份。这样做的目的很直接:电机发电回收的动能直接进电池,液压制动只把能量变成刹车盘上的热,白白浪费掉。所以这个策略也被称为串联式再生制动。
3.2 策略输入输出定义
| 方向 | 信号 | 单位 |
|---|---|---|
| 输入 | 制动踏板行程 | 0-100% |
| 输入 | 车速 | km/h |
| 输入 | 电机转速 | rpm |
| 输入 | 电池SOC | 0-100% |
| 输入 | 电池允许充电功率 | kW |
| 输入 | 电池温度 | ℃ |
| 输入 | 挡位 | / |
| 输入 | ABS触发标志 | 0/1 |
| 输出 | 电机负扭矩指令 | N·m |
| 输出 | 液压修正系数 | 0-1 |
这些信号定义了策略的所有边界条件。其中电池允许充电功率是关键的输入,它已经包含了SOC、温度、电压限制的影响,策略里直接使用可以少写很多逻辑。
3.3 总制动力需求解析
制动踏板行程要先换算成需求减速度,再换算成轮端总制动力矩。这个项目里我用的是线性映射:踏板行程0-100%对应减速度0到0.3g。换算公式为:
- (a_{des} = pedal_pos \times 0.3g)
- (F_{brake} = m_{tot} \times a_{des})
- (T_{req} = F_{brake} \times R_{wheel})
这里的 (m_{tot}) 是当前整车质量,(R_{wheel}) 是轮胎滚动半径。如果踏板行程超过某个阈值,比如对应减速度超过0.3g,视为紧急制动,策略不做能量回收,先撤电制动再加液压,优先保证制动距离。
3.4 电制动上限包络
策略最核心的部分是计算“当前条件下电制动最大能用多少”,这个上限由几个边界同时约束,取最小:
- 电机外特性限制:根据电机转速查外特性,得到该转速下电机允许的最大再生扭矩。
- 电池充电功率限制:电池允许充电功率 (P_{chg}) 换算成电机轴端扭矩,再乘减速比得到轮端电制动扭矩。
- SOC限制:SOC高于95%时充电功率直接降到0,避免过充。
- 车速下限:车速低于5km/h时退出电制动,防止低速停车瞬间抖动。
- ABS状态:ABS激活时电制动快速回退。
最终电制动上限取以上各值的最小值。这套包络逻辑写起来不复杂,真正考验标定的是每个边界怎么平滑过渡,后面我会专门说。
3.5 分配逻辑
用一个简化脚本描述整个决策流程:
function [T_regen_cmd, brake_fade_f] = regen_strategy(...) % 输入基础状态 T_req = 0; % 需求轮端制动力矩 T_regen_max = 0; % 电制动上限 % 1. 计算需求轮端制动力矩 T_req = m_tot * a_des * R_wheel; % 2. 计算当前电制动上限 T_regen_max = min(T_motor_limit, T_bat_limit, T_soc_limit, T_speed_limit); % 3. 电制动优先 if ABS_active T_regen_cmd = 0; brake_fade_f = 1.0; elseif T_req <= T_regen_max T_regen_cmd = T_req; brake_fade_f = 0.0; else T_regen_cmd = T_regen_max; brake_fade_f = min(1.0, (T_req - T_regen_max) / T_req); end end这里 (T_{req}) 和 (T_{regen_max}) 都定义在轮端,更方便比较。电机扭矩指令要从轮端值除以减速比和当前传动效率换算回去,千万不能直接在电机端和轮端混着写。
3.6 边界保护与舒适性处理
能量回收策略不能用简单的开关去控制,否则驾驶员会明显感觉到制动力的阶跃。我加了几个边界保护:
- 再生扭矩斜率限制:指令变化速率不超过设定值,避免突兀介入。
- 退出斜坡:遇到ABS激活或SOC过高时,再生扭矩按斜坡退出,不是瞬间归零。
- 电池温度修正:低温时电池允许充电功率下降,策略自然受限。
- 档位切换确认:换挡瞬间暂停再生扭矩,避免传动系统冲击。
这些保护逻辑越多,仿真模型越接近真实表现。做联合仿真的价值就在这:可以在CRUISE里看到每一条保护逻辑对整车减速度的影响,比单看模型内部波形直观得多。
4. 从Simulink到CRUISE的DLL对接:接口定义、编译配置和调试方法
4.1 模型编译前的准备
为了保证生成的DLL每次调用都有一致的行为,Simulink模型必须设置固定步长,我用的是10ms,和CRUISE里的DLL调用周期保持一致。求解器选离散求解器,避免连续求解器带来的额外状态。编译前要确认目标编译器可用,配置为生成动态库而不是独立可执行程序。这里我把模型名定义为regen_lib,编译生成regen_lib.dll。
需要明确一点点:Simulink编译生成DLL的机制依赖目标配置,常见的是用实时目标或者嵌入式目标。我的建议是尽量让模型里不出现连续积分模块,状态全部用离散变量,这样生成的DLL就是个纯粹的步进函数,被CRUISE按固定频率调用时行为最可预测。
4.2 在CRUISE里配置DLL模块
CRUISE里新建DLL模块,选择生成好的dll文件。然后进入接口映射,把输入输出信号一一对应。需要注意,CRUISE里的信号命名和Simulink里的端口命名完全一致映射容易,但更常见的是手工匹配,必须逐项检查单位。我第一次做的时候以为名字一样就自动映射好了,结果发现信号压根没接上,CRUISE直接给默认值,策略完全没有生效。
4.3 数据总线连接
在CRUISE数据总线中,为DLL模块添加连接:车速、制动踏板、SOC、电机转速、电池允许充电功率、电池温度、挡位、ABS标志。输出连接电机控制器、制动器液压。调用周期这一栏要设置成10ms,同时CRUISE主求解步长也设成10ms,保证两边完全同步。如果CRUISE主步长比DLL调用周期小,比如主步长1ms、DLL调用10ms,CRUISE会在DLL两次调用之间用上一次输出值,这没问题;反过来,DLL调用周期比主步长还短,就会在一步内多次调用,容易出现逻辑混乱。
4.4 接口单位与缩放因子
这是最容易被忽略的坑。CRUISE里制动踏板信号范围通常是0到100,Simulink接口里如果按0到1算,需求制动力会直接放大100倍。SOC也一样,有的地方是0到1,有的地方是0到100,必须统一。电机转速rpm直接传没问题,但有些版本会传rad/s,要仔细看信号说明。我建议大家记住一句话:接口映射表里所有信号都要带上单位,并且标注原始范围,不要只写信号名。编译前最好在Simulink里做一个信号缩放检查函数,把输入范围都映射到策略期望的范围内。
4.5 分步调试方法
DLL对接最容易犯的错就是“一步到位,出问题不知道是哪边的”。我的做法是分三层验证:
- 直通测试:Simulink模型什么都不算,输入什么就输出什么,验证DLL加载和信号通路。
- 固定值测试:输出一个固定的扭矩和液压修正系数,看CRUISE里整车是否按预期响应。
- 完整策略测试:接上全部逻辑,再对比中间变量。
还有一个技巧:把策略中间变量,比如电制动上限的各个分量,暂时作为额外输出连接到CRUISE监视器。这样能看到是电机外特性限住了,还是电池充电功率限住了,调起来思路非常清楚。
5. 仿真验证与结果:能量回收率、安全边界与续航提升
5.1 工况怎么选
我用了两个工况:一个自定义的低速市区循环,速度在0到40km/h之间,有频繁起停;一个WLTC全循环,覆盖低速、中速、高速段。刹车能量主要集中在中低速起停段,所以市区循环回收率一定比高速段高。高速段风阻占主导,制动频率低,回收空间小。如果你的目标是研究续航,重点看WLTC;如果目标是研究能量回收策略的标定逻辑,自定义市区循环更有针对性。
5.2 能量回收率的口径
不要只说“回收了多少能量”,要说清分母是什么。我的口径是:回收率 = 制动过程实际进入电池的电能 / 制动前汽车动能。动能通过 (\frac{1}{2}mv^2) 计算,进入电池的电能通过电池输入功率积分。这个口径下,低速工况回收率达到20%左右已经很不错,因为传动损耗、电机效率、电池充电损耗都在里面。如果把分母换成“理论上可回收动能”,数字会高一些,反而会掩盖损耗。建议大家统一按进入电池的电能除以整车动能来算,行业对比才有意义。
5.3 三组策略的对比结果
我做了三组仿真:无回收、并联回收、电制动优先。并联回收是我在这个项目中另外写的一个简化策略,按固定比例把制动需求的一部分分给电机制动。结果如下:
| 工况 | 无回收 | 并联回收 | 电制动优先 |
|---|---|---|---|
| 低速市区电耗 | 17.2 kWh/100km | 15.6 kWh/100km | 14.5 kWh/100km |
| WLTC电耗 | 16.4 kWh/100km | 15.7 kWh/100km | 15.1 kWh/100km |
| WLTC续航提升 | 基准 | 约4.5% | 约8.6% |
这个表说明:并联回收虽然也能回收一部分能量,但因为踏板深踩时电机制动比例不够灵活,很多可回收动能还是被液压制动吃掉了。电制动优先在频繁起停的市区工况优势更明显,高速段优势相对小。
5.4 安全边界验证
除了省能量,还要看策略在边界条件是否安全。我分别设置了高SOC 97%起步、低速停车前、ABS触发三个场景。
高SOC时电池允许充电功率为0,策略自动切换到纯液压制动,车辆仍有完全制动能力。低速段再生扭矩按斜坡退出,停车瞬间没有扭矩跳变。ABS触发时,再生扭矩快速回退,液压接管,制动距离没有异常增加。这几个场景验证完,策略才算真正可用。仿真里发现的很多问题,比如退出斜坡太慢导致ABS介入延迟,都是在这一步暴露出来的。
6. 联合仿真踩坑实录:频率、数据对齐和策略振荡
6.1 DLL调用周期与模型步长不一致,导致输出抖动
我最早把Simulink模型步长设为1ms,CRUISE的DLL调用周期设为10ms。跑出来的扭矩曲线在部分工况下出现明显的高频抖动,刹车减速度忽大忽小。原因是模型内部1ms计算一次,输出却被CRUISE每隔10ms采样一次,采样间隔里的多次计算结果没有意义,反而让输出信号出现混叠。解决办法是把模型固定步长和DLL调用周期都统一成10ms,抖动立刻消失。
6.2 信号缩放系数搞错,制动力翻倍
还有一次,制动踏板信号在CRUISE里输出0到100,我却在策略里按0到1处理,需求减速度直接放大100倍,整车第一个制动工况直接一头怼停。排查方法:在策略里加了一个调试输出,把踏板原始值和换算后的需求扭矩送到CRUISE监视器,和整车曲线对比,立刻发现数值差两个数量级。所以做接口映射时,一定要把“信号原始范围”和“策略计算范围”分开记,最好在Simulink模型里用Gain做一次显式单位换算。
6.3 再生扭矩响应太快,液压响应跟不上,形成振荡
电制动优先策略的核心矛盾在于:电制动扭矩可以毫秒级响应,而液压制动系统有建压时间,两者响应速度不匹配。当策略在“电制动满额加少量液压补充”和“电制动降低加液压增加”之间频繁切换时,总制动力会产生振荡。解决方法是给再生扭矩指令加斜率限制,同时液压修正系数做一拍延迟。斜率限制的起始值我设在2000牛米每秒,再根据减速度波动微调。这个参数标定过程比较耗时,但也说明了为什么联合仿真比纯逻辑验证更有价值。
6.4 DLL位数和编译环境不匹配
这个坑比较基础,但特别容易在换电脑后遇到。Simulink默认生成32位还是64位DLL,取决于目标编译器和安装版本,而CRUISE加载DLL也分位数。有一次我换了台新机器,Simulink生成的是32位DLL,CRUISE是64位版本,加载时直接报错。最后把目标编译器和CRUISE都统一成64位才解决。换环境后第一件事就是确认这两个位数一致,不要等报错了再查。
6.5 模型初始化状态没清零,首次调用输出异常
DLL在CRUISE仿真启动时会被调用一次初始化。如果Simulink模型里的离散状态在首次调用时是NaN或未定义,第一次输出的扭矩指令可能是一个很大的值,踩下刹车时车辆会瞬间抖动。解决办法是在模型里加一个Init标志,或者在使用状态前先做一次显式赋值。CRUISE的DLL模块里有初始化调用选项,要把初始化入口函数处理干净,这一步很多人会忽略。
我个人实际做完这个项目的体会是:能量回收策略的算法本身并不难,难在让策略和整车模型在同一个频率、同一套单位、同一种边界条件下好好协作。DLL联合仿真是目前我觉得最稳妥的方式,调试起来也直观。后面如果继续做,我可能会把策略里的状态机扩展,把启停、换挡、电子稳定系统交互都纳入进来。这盘的核心是“电制动优先”,但整体思路——先建整车模型、再写策略、再对接DLL、最后验证边界——完全可以复制到其他控制策略上,剩下的事情就是多踩坑,多把这些坑总结成表,下次做新项目就能直接绕开。