2. MATLAB/Simulink基础:Simulink环境介绍、基本模块库、模型创建与仿真

好,咱们开始聊Simulink。说实话,很多做嵌入式的人一开始都把它当成一个画图工具,觉得不就是拖几个方块连几根线嘛。嗯,我当年刚入行时也这么想,直到第一次在项目里因为一个采样时间设置错误,导致生成的代码在ECU上跑飞了……从那以后,我再也不敢小看这个环境了。

2.1 Simulink环境介绍

打开Simulink,你首先看到的是启动界面。我个人习惯直接点工具栏上的Simulink图标,或者命令行敲simulink回车。你会看到几个关键区域:

  • 库浏览器(Library Browser):所有模块的仓库,左边是分类树,右边是具体模块
  • 模型编辑器(Model Editor):你拖模块、画连线的主战场
  • 仿真工具栏:运行、停止、设置仿真时间,都在这里

这里有个小细节——很多人一上来就找模块,其实不如先熟悉一下模型资源管理器(Model Explorer)。我在做AUTOSAR项目时,经常用它来批量管理信号和参数,比在画布上一个一个点快多了。

我的习惯: 每次新建模型,第一件事就是打开Model Explorer,把工作空间的数据字典先挂载好。这样后面所有模块都能直接引用变量,不用手动填数字。

2.2 基本模块库

Simulink的模块库看着眼花缭乱,但做ECU软件,你真正高频使用的其实就那几类。我按使用频率给你排个序:

模块库 常用模块 我的使用场景
Sources(信号源) Inport, Constant, Step, Pulse Generator 给模型注入外部输入,比如传感器信号
Sinks(信号接收) Outport, Scope, To Workspace 把结果输出到ECU端口或查看波形
Continuous(连续) Integrator, Transfer Fcn 做PID控制器时常用,但生成代码时要注意离散化
Discrete(离散) Unit Delay, Discrete PID Controller 这才是嵌入式的主力,所有采样时间都要显式设置
Math Operations Add, Product, Gain, Saturation 加减乘除限幅,天天用
Logic and Bit Operations Logical Operator, Relational Operator 做状态机条件判断时离不开

你可能会问,为什么没有Stateflow?别急,那是专门做状态机的,后面章节我会单独讲。这里先打好基础。

避坑指南: 我曾经在项目里直接用Continuous模块做积分,结果生成的代码里出现了连续时间求解器,ECU根本跑不了。记住:嵌入式模型必须全部使用离散模块,采样时间要显式设置,别用默认的-1(继承)。

2.3 模型创建与仿真

好,咱们动手建一个最简单的模型。假设你要实现一个功能:输入一个车速信号,经过一个低通滤波器,再输出给CAN总线

步骤其实很简单:

  1. 从Sources库拖一个Inport模块,命名VehSpd_Input
  2. 从Discrete库拖一个Discrete Filter模块,双击设置分子分母系数
  3. 从Sinks库拖一个Outport模块,命名VehSpd_Filtered
  4. 用鼠标连线,从Inport连到Filter,再从Filter连到Outport

代码层面,你不需要写一行。但Simulink背后其实在生成一个差分方程。比如一阶低通滤波器的离散形式:

y(n) = a * x(n) + (1-a) * y(n-1)

其中a是滤波系数,由采样时间和截止频率决定。你在Discrete Filter模块里填的分子分母,就是对应这个方程的系数。

设置好仿真时间(比如10秒),点一下运行按钮。双击Scope模块,你就能看到输入信号和滤波后的输出波形。嗯,这里要注意——Scope的显示刷新频率可能跟不上仿真步长,如果波形卡顿,可以试试把仿真步长改大一点,或者用To Workspace把数据导到MATLAB里画图。

核心要点: 仿真通过不代表代码生成没问题。我建议每次仿真后,都打开代码生成报告看一眼,确认生成的C代码里没有浮点运算溢出或者死循环。有一次我发现生成的代码里有个while(1)循环,就是因为模型里有个代数环没处理。

2.4 仿真参数配置

仿真不是随便点一下运行就完事的。你需要配置几个关键参数:

  • 求解器(Solver):嵌入式模型选discrete (no continuous states),别选ode45那些连续求解器
  • 步长(Step size):固定步长,比如0.01秒(对应100Hz任务周期)
  • 仿真时间(Stop time):根据你的测试场景来,一般设10-100秒足够

为什么强调固定步长?因为ECU的任务调度是周期性的,你想想看,如果仿真步长不固定,生成的代码里就会出现变步长逻辑,这在嵌入式里几乎不可接受。我见过一个同事,仿真时用变步长跑得好好的,生成代码后一部署就报看门狗超时,查了半天才发现是步长不一致导致的。

好了,这一章的内容就这些。记住:Simulink不是画图工具,它是代码生成的前端。你拖的每一个模块,连的每一条线,最终都会变成ECU里跑的C代码。所以从一开始,就要用嵌入式工程师的思维去建模。