ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CANoe与Simulink联合仿真:5个接地气技巧,告别CAPL硬撸

CANoe与Simulink联合仿真:5个接地气技巧,告别CAPL硬撸 别急着撸CAPL先把CANoe和Simulink的联合仿真思路捋清楚搞汽车电子测试的兄弟应该都有这种体会CAPL脚本写多了尤其是在做复杂ECU仿真、多节点交互和故障注入的时候那种字符串拼接、数组操作、状态机管理写着写着就想摔键盘。CAPL有它的学习门槛C语法阉割版加一堆事件函数调试起来也不够直观。再加上现在ECU功能越来越复杂光是整车的网关逻辑、域控制器策略、传感器融合算法用CAPL硬撸代码量上去之后维护简直是灾难。这也是为什么业内越来越多人开始把Matlab/Simulink引入CANoe的测试流程里。Simulink做控制算法和复杂逻辑建模是强项CANoe做总线仿真和诊断测试是强项两者一配合正好把各自的优势发挥出来。结合Vector官方提供的一些接口方式Simulink模型可以跑在CANoe里也可以跑在外部实时环境里通过CANoe把总线报文喂给模型再让模型把计算结果发回总线上这套闭环基本能覆盖从功能验证到故障模拟的大部分场景。这篇文章不打算讲什么高大上的理论就基于我自己实际跑过的项目聊聊用Simulink配合CANoe做ECU仿真的5个接地气的技巧。这些技巧解决的就是环境配置、模型集成、实时通信、数据交互和排查问题这类日常工作里最头疼的事情。适合刚接触CANoe和Simulink联调的测试工程师也适合在CAPL里挣扎了很久想换个思路的兄弟。1. 环境搭建与总线基础配置1.1 CANoe工程创建与DBC文件导入很多人的困惑其实是卡在最开始的环境搭建上。Vector家的CANoe版本众多从10.x到现在的16、17界面布局大同小异核心配置逻辑基本没变过。安装的时候记住一个原则既然要做Simulink联合仿真尽量选一个足够新的版本因为Vector对Matlab版本的兼容列表是持续更新的老版本的CANoe加上太新的MATLAB经常会在编译模型那步报出一堆莫名其妙的问题。工程创建完成之后第一件事就是把DBC文件加进工程里。DBC是CANoe的灵魂里面定义了报文、信号、节点和网络节点的拓扑关系。在CANoe的Configuration界面里右击Networks下的CAN Network选择Import Database把DBC导进去。这里有个小细节要注意导入DBC之后一定要在Simulation Setup里确认每个ECU节点绑定的DBC是否正确尤其是你计划用Simulink替代的那个节点绑错文件会导致后面所有信号名称对不上号。注意DBC文件里的信号名和Simulink模型里的端口名、信号名不会自动映射。你需要在Simulink端要么用同一个命名规则要么在后续的映射配置中手动一一对应前期命名有规律后面省事非常多。1.2 虚拟CAN通道的配置方法如果你的开发环境里没有真实的CAN盒硬件比如只有一台笔记本想先在软件层面跑通联调流程那就得靠VN7560这类VN系列硬件卡的虚拟通道功能或者直接依赖一般的仿真模式。Vector的驱动安装好之后硬件自带的虚拟CAN通道就会被系统识别为额外的CAN通道可以在CANoe里直接当作普通通道使用。如果你完全没有Vector的硬件那也别慌。CANoe自带的纯软件仿真模式没有硬件的license也能做基本的Simulink联合仿真只要电脑上装了MatlabCANoe里选择Simulation Mode通道类型选择无硬件依赖的虚拟通道照样能把模型跑起来。实测下来纯软件模式跑一般的控制逻辑和报文收发完全没问题就是实时性稍微差点不能严格满足时间精度要求极高的硬实时场景但做功能验证和思路验证是够用的。1.3 仿真节点与总线通道的拓扑搭建CANoe的Simulation Setup里左侧是网络拓扑右侧是节点列表。要把Simulink仿真集成进来常见的做法是新建一个Simulink Node然后在节点属性里关联外部的模型文件或编译生成的DLL。这个节点就会被当作总线上的一个普通ECU来对待其他节点的报文它都能收到它发出去的报文也会按DBC定义出现在总线上。拓扑搭建这里最容易踩的坑是通道和节点的对应关系搞错。举个例子你有两个ECU节点在做互相通信的测试CAN1上挂了ECU_A和ECU_BCAN2上挂了ECU_C如果Simulink模型这个节点被默认分配到了CAN2而模型里又是按CAN1的信号来写的那自然会收不到任何报文。所以每次搭建拓扑之后务必先在CANoe里用Trace窗口发几帧测试报文确认通道通断再开始拉模型。2. 模型架构设计与信号映射2.1 从CAPL逻辑到Simulink模型的迁移思路CAPL里写逻辑的时候大家的习惯往往是按照事件来组织代码比如on message、on key、on timer这三个关键字就能撑起一大半的逻辑。这种事件驱动的方式在CANoe原生的仿真环境里非常好用但一旦逻辑复杂起来比如涉及到多层状态机嵌套、算法运算、参数标定CAPL的表达能力就显得吃力了。换到Simulink之后推荐的思路是把原先CAPL里的逻辑拆成两个层面一个是数据流层面即接收报文、解析信号、计算、输出信号、发送报文这一整条链路另一个是事件/状态层面即原来的timer、key事件对应的状态切换逻辑。数据流层面可以直接用Simulink的标准模块搭而状态机层面的东西建议用Stateflow做Stateflow的状态图和CAPL里的on message timer事件的思路其实是对应的但可读性要好得多。2.2 信号映射的推荐实现方式模型内部搭好了接下来就是最关键的信号对接环节。CANoe与Simulink之间的信号传递有两种主流方式一种是直接把Simulink模型用C代码生成工具编译成DLL然后在CANoe的Simulink Node里加载这个DLL通过配置映射文件把模型输入输出端口映射到CANoe内部的系统变量或总线信号上另一种是在Simulink环境里直接通过CANoe提供的Matlab接口库建立通道在Simulink模型里以S-Function的形式接收和发送报文。我的建议是如果模型规模不大逻辑不算特别复杂走DLL编译这条路最稳。把模型编译成DLL之后CANoe加载的稳定性好不受Matlab运行时的影响部署到别的机器上也方便。编译配置在Simulink的Code Generation界面里设置系统目标文件选择CANoe提供的那个配置即可步骤并不复杂关键是编译之后DLL文件的路径不能被移动否则CANoe加载时会报找不到模块的错误。2.3 命名规范与可维护性规划信号命名的规范程度往往决定了一个模型后期好不好维护。我做过的项目里凡是模型端口名、Simulink内部信号名和DBC里信号名能对上的后期排查问题都很快反之命名混乱的项目基本上每次改动都要花半天去捋线。建议维持一套固定的前缀规则输入信号全部以CAN_前缀开头输出信号用CTRL_前缀开头内部的中间变量用TMP_开头状态机里的状态用ST_开头。这样在模型里看到信号名就能大致判断它的作用域配合Simulink的Signal Label功能整个模型的数据流走向一目了然。另外Simulink里每个子系统的名字尽量和CANoe仿真界面里的节点名对应起来这样后期查看Trace的时候可以快速定位是模型里哪个子系统在处理这条报文。3. 外部模式与实时仿真的应用3.1 Simulink外部模式的工作原理Simulink的外部模式External Mode是一个特别适合联调的运行方式。在这种模式下Simulink的模型并不是以独立的可执行文件方式运行的而是由一个实时的目标环境CANoe就是它的宿主来调度执行。模型信号可以在线地用Simulink的Scope来观测参数可以在Simulink界面里直接调这不就是很多做标定的人梦寐以求的功能吗原理上其实不复杂CANoe通过共享内存或者以太网接口把总线上收到的报文数据推送给Simulink模型的输入端口模型计算完成之后把输出结果推回到CANoe里打包成报文发出去。Simulink的External Mode在Run的时候会建立一个通信链路一方面实时传递数据另一方面也传输观测变量的值。我用这个模式调过一段电机控制逻辑电机转速信号和扭矩信号直接在Simulink的Scope里看曲线比在CANoe Trace里扒数据再画图爽太多了。3.2 External Mode的配置步骤Simulink的外部模式配置其实不难核心就是三步选对硬件接口、设置好通信周期、正确配置Build选项。第一步在Simulink的Configuration Parameters里选择Solver定步长模式仿真步长设置成和CANoe总线的报文基础周期一致。举个例子如果总线上最关键的周期性报文是10ms一帧那你这个模型的采样步长最好也设成0.01秒这样模型的计算节奏和总线报文节奏是同步的不会出现数据错拍的问题。第二步在Hardware Implementation里选择对应的目标硬件这里要选择CANoe对应的目标类型。第三步在模型里添加一个SCANeR或CANoe提供的CAN Communication模块作为CANoe与Simulink数据交换的通道。这个模块在Simulink的Library Browser里能找到安装CANoe之后Vector的库会自动注册到Matlab里。配置完成之后点一下Build Model生成的代码会通过编译器编译链接然后在CANoe里启动仿真再回到Simulink里点Connect to Target模型就开始在外部模式下运行了。注意外部模式下模型的在线调参能力是有点局限的。只有标记为Tunable的参数才能在外部模式下直接改普通的常量模块参数改完不一定生效所以建模的时候想好哪些参数是需要在线调整的把它们设置成Tunable或者用Simulink的Parameter对象定义。3.3 采样点与步长匹配采样点这个参数在CANoe里平时不太起眼但做联合仿真的时候它非常关键。CAN总线的采样点定义了节点在位流中对每个bit进行采样的时间位置一般建议设置在75%到85%之间这个范围内的采样是相对可靠的。做仿真的时候如果你发现报文隔三差五出现错误帧或者信号值在某个速度下突然跳变多半是采样点设置的问题。Simulink模型的步长和CAN总线的采样点看似无关实际上影响很大。模型步长短了计算量上去了实时性可能跟不上步长了信号波形会被拉平看瞬态响应就看不出细节。我的经验是先根据总线上最重要的那几帧报文的周期来选择步长然后在此基础上往下细化四到五倍保证采样精度又不至于计算量爆炸。比如总线报文周期是10ms模型步长用2ms是比较合理的。设置采样点的时候保证采样点落在位时间的78%左右这个值在高速CAN500kbps下测试是比较稳妥的。4. 复杂数据处理与算法集成的关键技巧4.1 用MATLAB Function块处理字节序与信号解析做ECU仿真最烦的事情就是报文解析特别是涉及多字节信号的时候字节序、符号位、缩放因子、偏移量一个没弄对解析出来的数值就完全不对。CAPL里你得自己写函数去做这些转换费时费力还容易错。在Simulink里这个问题简单多了。新建一个MATLAB Function块在编辑器里直接用MATLAB语法处理信号解析逻辑即可。比如一个16位的无符号信号在高位在前的情况下转成有符号数只需要调用typecast和swapbytes配合处理或直接按位运算。需要注意的是在MATLAB Function块里代码会被编译成C代码所以不能用到一些运行时特性比如eval这种动态执行的函数但在数组、矩阵、位运算这些层面基本都能满足需求。4.2 与神经网络、滑模控制等算法的集成方式ECU仿真做到一定程度就不会满足于简单的报文转发和常规逻辑了很多兄弟会想把自己训练好的BP神经网络或者滑模控制算法塞到仿真模型里做一个软ECU来跑。这里头难点不在于算法本身而在于算法和总线通信的对接。Simulink的好处在于它的生态非常开放。神经网络用Deep Learning Toolbox训练好之后可以直接用exportNetworkToSimulink或者通过MATLAB Function块调用训练好的模型。训练的权重矩阵在这个块里就是一个普通的参数数组不会因为代码生成而丢失。滑模控制这类基于数学表达式的控制律就更适合了直接在Simulink里用模块搭或者写进MATLAB Function中间层都行。4.3 16进制与有符号数转换的坑关于16进制转有符号数这个问题我在CAPL和Simulink里都踩过坑。CAN总线上传输的都是原始值真正有物理意义的数值需要根据信号的定义来转换。DBC文件里的信号定义包含了字节序、起始位、长度、缩放因子和偏移量。咱们拿到一帧报文里的原始值的时候必须按照这个定义反算。很多兄弟刚上手的容易直接把原始十六进制值扔给MATLAB代码去转有符号结果发现正负数全反了原因就是没有考虑到字节序和符号扩展。以常见的Intel格式16位有符号数为例要先按小端序拼出16位数值然后用bitshift把最高位为1的数扩展到32位再转有符号或者直接用typecast(uint16(raw),int16)一步到位。注意CANoe的DBC文件定义了信号的范围和物理值但Simulink模型在解析的时候并不会自动读取DBC所以信号的缩放因子和偏移量必须你在模型里手动配置。建议把缩放因子和偏移量做成模型的参数这样在标定不同ECU的时候可以直接在参数界面调整不用改模型本身。5. 调试排查与性能优化实录5.1 Trace窗口和在线观测数据不显示的排查Trace窗口是CANoe里最有价值的调试窗口没有之一。不过使用中经常出现一个让人抓狂的问题Trace窗口里什么都没有或者只有ID没有Name列一行空白。我排查过几次绝大多数原因出在以下两个地方。第一个是滤波器设置。Trace窗口有一层过滤机制可以按通道、报文类型、ID范围、错误类型来过滤。如果误开了某个过滤器比如把ErrorFrame类型的显示关掉了或者选中了只显示某个特定通道的数据那其他报文自然不显示了。检查Trace窗口下方的Filter区域把所有过滤条件恢复默认。第二个是数据库绑定缺失。如果总线上跑着的报文没有在DBC里定义或者虽然是已定义报文但这条报文的ID和DBC里的定义不一致Trace窗口也不会显示对应的Name只会显示一行空白。这种情况去Configuration界面里检查一下当前网络拓扑的DBC文件到底有没有加对加对了还是空白的话在Trace里右键Data Source看是不是通道选错了。另外做Simulink联合仿真的时还要检查模型的仿真是否真的处于运行状态模型没跑起来总线空空如也Trace当然也空。5.2 延迟函数、周期控制在Simulink端的实现对比CAPL里写延迟函数是挺常见的事比如等待一个响应超时5秒之后执行某个动作。在CAPL里用Timer或者定时器回调来解决。在Simulink里对应的做法常用的是Delay模块和Clock模块配合实现。比如你要实现收到某个信号之后延迟10ms再置位另一个信号那就用Delay模块延迟时间设置为10ms除以模型步长对应的采样周期个数。这里有个很关键的细节Simulink模型跑在实时环境里的时候时间是由仿真步长决定的不是由现实时间决定的。也就是说如果模型步长设置不正确或者模型计算量太大导致实际运行跟不上那延迟真实的时间可能远大于你设置的延时。我在项目里就遇到过这种情况模型里设的100ms延时实际用秒表一掐已经过了333毫秒。解决的办法是合理设计步长并检查实时运行的CPU占用率不能让模型超负荷跑还指望时序准确。5.3 采样点问题导致的通信异常排查做多节点CAN通信仿真时采样点设置不合理会导致一种特别蹊跷的故障单独测每个节点的时候一切正常两个节点一通信就出现周期性错误帧而且错误位置总是固定在数据场的某个字节附近。这种问题十有八九是总线上的两个节点采样点设置相差太大。CAN总线规范要求所有网络节点的采样点必须落在位时间的一个特定区间内通常是70%-85%这样各个节点的采样时刻可以容忍一定的时钟漂移和信号传播延迟。如果ECU_A的采样点设置在80%而ECU_B的采样点设置在60%那么ECU_B采样的时刻明显偏早重复读到前一位的电平值位错误率就上来了。排查方法很直接在CANoe里打开CAN Stats窗口看错误帧的分布情况然后到每个节点的属性页里统一设置采样点为推荐值78%。做联合仿真的时候Simulink节点也遵循同样的规则需要你在建模参数里显式设置。5.4 模型编译时间过长与外部模式连接不上的处理模型编译时间一直是让人头疼的事情。大模型加上复杂的子系统层次每次Build Model动辄半个小时迭代一次测试要等半天。我的建议是把模型拆成若干独立子系统分别用Model Reference的方式管理。这样修改了某个子系统的内部逻辑后Simulink只编译被修改的部分配合增量编译的选项编译时间可以缩短一个数量级。外部模式连接不上是另一个常见的掉链子问题。现象就是你点了Connect to Target结果显示connection failed。排查思路按顺序来第一确认CANoe工程已经启动仿真第二确认Simulink目标环境选择正确第三确认防火墙没有拦截CANoe或Matlab进程的跨进程通信第四检查模型里是否有不支持代码生成的模块。最后一种情况在实际中发生率最高因为外部模式要求模型必须能够生成C代码Simulink里一堆看起来人畜无害的模块比如Display、Scope这些在Normal仿真模式没问题但代码生成模式下会被自动忽略或者报错。模型里如果有这类纯可视化模块最好在外部模式运行前先清理掉或者用注释的方式暂时禁用。写在最后的几句经验之谈做CANoe和Simulink联合仿真这块我陆陆续续折腾了大半年踩过的坑算是比较典型。最大的体会是这套方案并不是要完全取代CAPL而是给了一个更适合做复杂逻辑的方式。CAPL在处理简单报文转发、触发式诊断响应这些场景时依然是最快的工具但当你的ECU仿真模型涉及到算法、状态机、参数标定这些内容的时候Simulink的建模能力会节省大量的时间。最后分享一个小技巧在Simulink模型里跑MCU外设级的仿真时比如定时器中断服务程序建议用Simulink的Function-Call Subsystem来模拟中断服务函数的调用频率用底层的定时器模块精确控制调用周期这样模型的时序结构和底层代码的映射关系会非常清晰后续要做硬件在环测试的时候直接复用这套模型也不用大改架构。希望这5个技巧能帮你少走一些弯路。不同项目的需求和环境可能稍有差异但整体的思路和排查方法是可以迁移的。做汽车电子这行工具链千变万化底层逻辑却始终相通把数据流和时序关系想明白不管用什么工具心里都有底。
返回列表