(控制)把SIMULINK转成C语言并在VS中调用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010185894/article/details/52490509

背景

最近发现SIMULINK其实是可以转成C语言的(再次感叹MATLAB的神奇),这个功能大大地减少了我的工作量。由于时间不允许,在转换过程中我没有对这里的一些细节进行研究,而且网络上关于这个方法把这个方法的文章不多(主要是本人懒得看英文资料……),所以把它写成博客,既方便以后回来看也希望能和大家交流。

本人编程基础差且没有对这个方法进行深究,这篇文章仅作为本人笔记和交流知识的作用,文中很可能有大量错误,也希望大家能指出

平台

  1. VS 2013
  2. MATLAB 2015a
  3. Windows 10

方法

下面以建立一个PID控制器为例说明这个方法的过程。

首先在SIMULINK中搭建一个模型,注意给模型添加起码一个输入端口和一个输出端口。在这里,因为我需要搭一个PID控制器,所以另外分别加入P、I、D三个输入端口,最终得到的模型如下图所示,注意这里端口的命名,转成C代码后输入输出点的命名将和他们相对应。因为我最后是用这个控制器来控制云台摄像头的转动的,其实这里应该设计成离散控制系统,由于本人偷懒,随便做了个连续的PID控制器就用了起来,最后调整好PID后效果也还行,就不管了。
PID控制器

然后在当前的模型窗口中依次打开Simulation->Model Configuration Parametersta弹出以下窗口。
设置

一定要把Stop time设为inf,把Solver options下的Type设为Fixed-step编译才能正常进行,否则会报错。这里设置的好像是系统状态更新的方式, 我没有仔细研究,如果大家希望详细了解这些选项的意思可以把鼠标放在相关选项上然后右击,会出来一个What’s this选项,点进去就可以(感叹MATLAB的友好度)。

点击右侧窗口的Code Generation按键可以选择生成C代码(默认)或者C++代码,以及选定IDE。

另外对于要在单片机中实现控制的同学可以在右侧窗口点击Hardware Implementation,会出来一个选择设备的下拉选框,这个可能需要设置一下(没有经过测试),但是因为本文只要在VS中实现控制,所以就不需要选了。

设置好之后关闭Configuration窗口,依次点击Code->C/C++ Code->Build Model。此时SIMULINK已经开始编译了,等到MATLAB的Command Window窗口进入就绪状态,这时就已经编译成功了。打开MATLAB当前的工作目录你会发现多了一个文件夹,文件夹名称为“你的模型名称”+_grt_rtw。例如我这里的模型为PIDController.slx,那么生成的文件夹名称为PIDController_grt_rtw。为了便于说明,MATLAB自动生成的文件和函数等的命名一并以这个例子为例,具体情况请根据相应的模型名替换。在资源管理器中打开这个文件夹你会发现里面有一个批处理文件,虽然不知道有什么用,我一般都会运行一下(-.- !)。

下一步就是把这些文件导入到VS里面去了。先建立一个VS的项目。可以选择把这一整个文件夹的文件拷进VS的工程文件里,或者在VS工程里添加这个文件夹作为头文件目录和源码目录。在头文件中右击,点击添加->现有项…,然后把PIDController_grt_rtw里面所有的头文件和源文件添加进工程。

在这些文件中我们只要关心PIDController.h这个文件就行了。在工程的入口函数所在的源文件处加上

include “PIDController.h”

这一行。接下来Build这个工程,你会发现有很多找不到文件的错误,这是因为PIDController_grt_rtw这个文件夹中的源码都引用了MATLAB安装目录底下的一些源码。因为找不到更好的方法,我只能按照报错的源头一个一个文件地从MATLAB安装目录中找出来并且添加进工程。我这个应该是很笨的方法,如果有简单的方法请告诉我。

把所有应该添加的文件都添加进来之后就可以在程序中调用这个PID控制器了。阅读PIDController.h这个文件你会发现有以下代码


//...
/* External inputs (root inport signals with auto storage) */
extern ExtU_PIDController_T PIDController_U;

/* External outputs (root outports fed by signals with auto storage) */
extern ExtY_PIDController_T PIDController_Y;
/* Model entry point functions */
extern void PIDController_initialize(void);
extern void PIDController_step(void);
extern void PIDController_terminate(void);

从注释我们可以看出这PIDController_U和PIDController_Y分别是模型的输入和输出;PIDController_initialize(void)、PIDController_step(void)和PIDController_terminate(void)三个函数是模型的入口函数。

进一步研究PIDController_U和PIDController_Y的定义,我们可以发现它们其实是两个结构体,分别对应着模型中的输入端口和输出端口。

/* External inputs (root inport signals with auto storage) */
typedef struct {
  real_T In1;                          /* '<Root>/In1' */
  real_T I;                            /* '<Root>/I' */
  real_T D;                            /* '<Root>/D' */
  real_T P;                            /* '<Root>/P' */
} ExtU_PIDController_T;
/* External outputs (root outports fed by signals with auto storage) */
typedef struct {
  real_T Out1;                         /* '<Root>/Out1' */
} ExtY_PIDController_T;

PIDController_initialize(void)、PIDController_step(void)和PIDController_terminate(void)三个函数的功能分别如下:
- PIDController_initialize(void): 初始化模型
- PIDController_step(void):对模型的输出端口进行一次更新,有点像PLC中的一个周期
- PIDController_terminate(void):结束模型

实际使用如下

PIDController_initialize();
PIDController_U.I = 0.0193;
PIDController_U.P = 1.625;
PIDController_U.D = 2.67;
while (!quit_flag){

    //TODO 检测被控量,更新模型的输入newInput

    PIDController_U.In1 = newInput;
    PIDController_step();
    newOutput = PIDController_Y.Out1;

    //TODO 把新的输出newOutput作为被控对象的输入

}
PIDController_terminate(void);
阅读更多
换一批

没有更多推荐了,返回首页