4、模型搭建与部署:从Simulink模型到实时C代码、代码生成工具链、部署到实时目标机
好,咱们这一章聊点硬核的。模型搭好了,仿真跑通了,然后呢?
然后就得把它变成能在硬件上跑的代码。这一步,我称之为“从图纸到实物”的关键一跃。很多人在算法开发上花了大把时间,结果卡在代码生成和部署上,那可就太冤了。
4.1 为什么不能手写代码?
你可能会问:“我C语言写得溜,干嘛非要用自动代码生成?”
嗯,我刚开始做项目时也这么想。有一次,我手动移植了一个卡尔曼滤波算法,花了整整三天。结果一跑硬件在环测试,发现有个数据类型定义错了,导致整个状态估计偏差了10%。
从那以后,我学乖了。自动代码生成的好处,说白了就三点:
- 一致性:模型里怎么算,代码就怎么算。不会出现“模型里是浮点,代码里写成整型”这种低级错误。
- 效率:你想想看,一个复杂的SOC算法,状态机加查表加滤波,手写要多久?自动生成,点一下按钮,几分钟搞定。
- 可追溯:生成的代码和模型一一对应。出了问题,能快速定位到模型里的哪个模块。
核心原则:模型是“源”,代码是“影”。永远只修改模型,不要直接改生成的代码。
4.2 代码生成工具链:你需要准备什么?
工具链这事儿,其实没那么玄乎。我习惯把它分成三块:
| 环节 | 工具/组件 | 我的经验 |
|---|---|---|
| 模型准备 | Simulink + Stateflow | 确保模型是“可配置”的,别写死参数 |
| 代码生成 | Embedded Coder | 这是核心,别用普通的Simulink Coder |
| 编译部署 | 目标机编译器 + 下载工具 | 比如TI的CCS,或者NI的VeriStand |
我个人习惯,在模型里就把数据类型、采样时间、存储类都定义好。这样生成代码时,几乎不用额外配置。
4.3 从模型到代码:三步走
这一步,我把它拆成三个动作。每一步都有坑,我一个个说。
4.3.1 第一步:模型配置
打开你的Simulink模型,按 Ctrl+E 进入配置参数。这里有几个关键项:
- Solver:选固定步长,离散求解器。别用变步长,那是给仿真用的。
- Hardware Implementation:设置目标芯片的字长、字节序。比如ARM Cortex-M4,就选“ARM Compatible”。
- Code Generation:系统目标文件选
ert.tlc(Embedded Real-Time)。
注意:我曾经因为忘了改“Hardware Implementation”里的字节序,导致生成的结构体在目标机上解析错误。排查了整整一个下午。嗯,这种低级错误,犯一次就够了。
4.3.2 第二步:生成代码
配置好了,点一下 Ctrl+B。你会看到Simulink开始“编译”模型,然后生成一堆 .c 和 .h 文件。
生成的代码结构大概是这样的:
model.c // 主算法逻辑
model.h // 接口声明
model_private.h // 内部变量
ert_main.c // 主函数(通常需要自己重写)
这里有个小技巧:我一般会生成一个“代码报告”,里面会列出所有生成的函数和变量。方便后续集成时查阅。
4.3.3 第三步:集成与编译
生成的代码,不能直接扔到目标机上跑。你得把它和你的硬件驱动、操作系统(如果有)集成在一起。
我通常的做法是:
- 把生成的
.c/.h文件复制到我的工程目录。 - 写一个“包装函数”,把模型里的
step()函数放到定时中断里调用。 - 用目标机的编译器(比如GCC或者CCS)编译整个工程。
避坑指南:我曾经直接把 ert_main.c 拿过来用,结果发现它默认用了 printf 打印数据。我的目标机根本没有串口驱动,一运行就卡死。所以,主函数一定要自己写,别偷懒。
4.4 部署到实时目标机
代码编译通过了,接下来就是部署。这一步,说白了就是把可执行文件下载到目标机上,然后让它跑起来。
不同的目标机,部署方式不一样。我拿两种常见的来说:
4.4.1 基于NI PXI/VeriStand的部署
如果你用的是NI的硬件,流程会简单很多:
- 生成代码后,用NI的
VeriStand Model Framework打包成.dll。 - 在VeriStand里导入这个
.dll,配置好I/O映射。 - 点击“Deploy”,代码就下载到PXI的实时控制器上了。
我做过一个项目,用PXI跑电池模型在环测试。从生成代码到部署运行,前后不到半小时。嗯,这种“一键部署”的感觉,确实爽。
4.4.2 基于TI DSP的部署
如果是嵌入式芯片,比如TI的C2000系列,步骤会多一些:
- 用Embedded Coder生成针对C2000的优化代码。
- 在CCS(Code Composer Studio)里导入工程。
- 编译、链接,生成
.out文件。 - 通过仿真器(比如XDS100)下载到DSP的Flash或RAM里。
关键点:部署到Flash和部署到RAM,启动时间和运行速度都不一样。调试阶段我建议先下载到RAM,跑通了再固化到Flash。
4.5 验证:代码跑起来之后
代码部署上去,不代表万事大吉。你还需要验证:
- 时序:模型的一个步长,在目标机上实际跑了多久?有没有超时?
- 数值:目标机上算出来的结果,和Simulink仿真结果是否一致?
- 稳定性:连续跑几个小时,会不会有内存泄漏或者堆栈溢出?
我习惯在代码里加一些“监视点”,把关键变量的值通过串口或者DAC输出出来。然后和Simulink里的仿真数据做对比。误差在1%以内,就算通过。
好了,这一章的内容就这些。从模型到代码,再到部署,每一步都有它的门道。你想想看,如果这些坑都提前避开了,后面的硬件在环测试是不是就顺畅多了?