S-Function 控制元件
本文档主要介绍自定义 S-Function 元件的创建和实现方法。为实现该功能,用户需提前准备封装好的二进制模型 .so 文件。本文档也将从白盒模型封装和黑盒模型封装两种实现方式介绍生成 .so 的具体步骤,并通过一个白盒模型实现的案例介绍 S-Function 元件的构建、调试方法。
功能定义
该功能支持将封装的二进制模型导入平台,构建自定义 S-Function 电磁暂态仿真元件。
功能说明
自定义 S-Function 元件具有以下几个功能特点:
- 
控制模型构建方便、高效,可降低模型搭建和参数校验的时间成本。 
- 
控制策略与真实控制器的保持高度一致,仿真结果更接近实际情况。 
- 
控制器开发者不需要公开具体的控制策略和核心算法,可满足保密需求。 
适用模型
需提前准备 Linux 64 位系统环境下编译生成的 .so 文件。
编译来源可以有以下两种:
- 
MATLAB/Simulink 子系统编译生成 S-Function 时自动生成的代码文件。 
- 
用户自定义函数和接口的代码文件。 
整体流程

用户可将 Simulink 白箱模型封装成 S-Function 子系统,利用 MATLAB 的自动代码生成功能,得到 *.c 和 *.h 文件,在 Linux 环境中编译生成 .so 文件;也可以将包含自定义函数和接口的 .c 或 .cpp、.h 文件,或 .a 二进制文件,在 Linux 环境中编译生成 .so 文件。
用户需要获取 Simulink 子系统或自定义代码文件的全局参数、输入/输出接口信息,并根据信息定义 S-Function 元件的参数和引脚,在平台导入 .so 文件,完成自定义 S-Function 元件的构建。
具体步骤
本文档根据白盒模型封装和黑盒模型封装两种方式介绍自定义 S-Function 元件实现的具体步骤。
若用户需要构建白盒模型的 S-Function 元件,.so 的编译来源为 MATLAB/Simulink 自动生成的代码,请参见白盒模型 S-Function 实现标签。
若用户需要构建黑盒模型的 S-Function 元件,.so 的编译来源为用户自定义的函数和接口代码,请参见黑盒模型 S-Function 实现标签。
- 白盒模型 S-Function 实现
- 黑盒模型 S-Function 实现
步骤 1. 将 Simulink 模型封装为 S-Function 并自动生成代码
- 
a. 解算器设置 模型设置为固定步长,解算器设置为 discrete,确保模型在当前设置下能正常运行。 Simulink 模型参数配置 CloudPSS 中的系统均为离散系统,因此建议 S-Function 也使用离散解算器。 注意若 S-Function 子系统中存在难以离散化的连续状态,也可选用连续积分解算器,目前支持的有 ode1、ode2、ode3、ode4、ode5 和 ode8,不同的积分方式会影响仿真结果准确度和仿真计算时间。 
- 
b. 子系统封装 将控制模型封装为一个子系统。子系统中可以引用全局参数,若子系统的顶层有参数封装,并且希望这些参数在生成 S-Function 后可调,请将这些参数也通过全局参数定义,否则将被视为固定值而不可调。 子系统参数封装 例如上图中,Parameter1 与 Parameter2 分别用全局参数 a 和 b 定义,则在生成 S-Function 后,这两个参数仍可在子系统顶层设置,而 Parameter3 为固定值 0,在生成 S-Function 时将以固定值写入。 注意S-Function 参数必须以字母开头(不能是数字或下划线),目前仅支持 double 型,若是一个矩阵,在 CloudPSS 中对应表格型参数。 
- 
c. 生成 S-Function 右键子系统,依次选择 C/C++ Code、Generate S-Function 选项。  子系统的 S-Function 编译 
- 
d. 设置 S-Function 参数 勾选列出的全局参数中需要可调的,未勾选的参数将以固定值写入。  勾选可调参数 
- 
e. S-Function 自动代码生成 自动代码生成,sfcn_rtw 文件夹中包含 *.c与*.h文件。 S-Function 自动代码生成 提示若提示无编译器,则需下载并安装一个 MingGW64 编译器,流程可参考:https://zhuanlan.zhihu.com/p/359962279?ivk_sa=1024320u  MATLAB 编译器安装与配置 
步骤 2. 在 Linux 环境编译生成 .so 文件
- 
a. 添加接口定义 在步骤 1 生成的 sfcn_rtw 文件夹中找到以 S-Function 名称命名的 .c文件,在 #include 之后添加如下接口定义并保存:#define ssSetInputPortVectorDimension(S, port, d) \
 (ssSetInputPortWidth(S, port, d))
 #define ssSetOutputPortVectorDimension(S, port, d) \
 (ssSetOutputPortWidth(S, port, d))注意此函数定义是为识别 S-Function 的输入输出引脚,若每个输入输出引脚维数均为 1,则不用添加;若有引脚维数大于 1,则必须添加。 
- 
b. 运行编译指令 将 sfcn_rtw 文件夹复制到 Linux 环境,在其路径下输入以下编译命令: gcc -Wall -DRT -DRT_MALLOC -fPIC -lm -shared *.c -o xxxx.so其中, xxxx为用户自定义的.so文件名,与内部函数名(S-Function 名)无关。Linux 环境必须是 64 位系统。若出现缺少 *.h头文件的报错,需要手动添加相应文件。不同版本的 MATLAB 所调用的头文件不同,获取路径可参考:安装路径\MATLAB\Rxxxxx\simulink\include
 安装路径\MATLAB\Rxxxxx\extern\include
 安装路径\MATLAB\Rxxxxx\rtw\c\src其中, xxxxx为 MATLAB 版本。
步骤 3:根据全局参数和输入输出接口信息构建 S-Function 实现元件
- 
a. 新建空白 S-Function 实现元件 在工作台点击新建按钮,新建一个空白普通电力系统模型。  新建项目 选择总览标签页,将模型类型切换为元件。  切换为元件类型 此时,在实现标签页中将出现拓扑、电磁暂态等子标签页。选中电磁暂态子标签页,点击创建电磁暂态 - S-Function 实现按钮,即可创建自定义 S-Function 元件。  创建电磁暂态 - S-Function 实现 
- 
b. 参数引脚列表定义 选择接口标签页,在参数列表添加参数,在引脚列表添加引脚,并绘制元件图形。参数键名必须与 S-Function 引用的全局参数名一致;引脚键名不作要求,但顺序必须分别与 S-Function 的输入输出引脚对应。  添加元件引脚并绘制图形 
- 
c. 导入 .so 文件 在实现标签页导入步骤 2 生成的 .so文件,并填写入口函数名。 导入 .so 文件并填写入口函数 注意入口函数名是 S-Function 名称,不是 .so的文件名。
- 
d. 元件名称与权限设置 切换到总览标签页,输入元件的名称,并设置元件的权限和元件标签。  元件名称与权限设置 
步骤 1. 在 Linux 环境编译生成 .so 文件
- 
a. 运行编译指令 将代码文件 *.cpp及其头文件*.h复制到 Linux 环境,使用以下指令编译:gcc -Wall -fPIC -s -lm -shared -DRT xx1.cpp xx2.cpp -o xx.so注意Linux 环境必须是 64 位系统 对于已经编译为 .a的文件,使用以下指令编译:gcc -Wall -fPIC -s -lm -shared xx.a -o xx.so -u <entry_name>其中, <entry_name>是入口函数名。
步骤 2:根据全局参数和输入输出接口信息构建 S-Function 实现元件
- 
a. 新建空白 S-Function 实现元件 在工作台点击新建按钮,新建一个空白普通电力系统模型。  新建项目 选择总览标签页,将模型类型切换为元件。  切换为元件类型 此时,在实现标签页中将出现拓扑、电磁暂态等子标签页。选中电磁暂态子标签页,点击创建电磁暂态 - S-Function 实现按钮,即可创建自定义 S-Function 元件。  创建电磁暂态 - S-Function 实现 
- 
b. 参数引脚列表定义 选择接口标签页,在参数列表添加参数,在引脚列表添加引脚,并绘制元件图形。参数键名必须与 S-Function 引用的全局参数名一致;引脚键名不作要求,但顺序必须分别与 S-Function 的输入输出引脚对应。  添加元件引脚并绘制图形 
- 
c. 导入 .so 文件 在实现标签页导入步骤 2 生成的 .so文件,并填写入口函数名。 导入 .so 文件并填写入口函数 注意入口函数名是 S-Function 名称,不是 .so的文件名。
- 
d. 元件名称与权限设置 切换到总览标签页,输入元件的名称,并设置元件的权限和元件标签。  元件名称与权限设置 
调试方法
在完成自定义 S-Function 元件的构建后,可以使用开环测试的方式,调试并验证元件的正确性。
步骤 1:获取 S-Function 正常运行时的输入数据
- 
a. 添加示波器 在 Simulink 中闭环运行原模型,使用示波器观测 S-Function 所有输入引脚。 输入引脚处添加示波器 
- 
b. 保存数据 设置示波器保存数据到 MATLAB 工作区。  示波器设置录波 
步骤 2:数据处理并导出为 .csv 格式文件
- 
a. 数据处理 在 MATLAB 中处理示波器保存的数据,将每一路引脚的数据按照第一列时间、第二列数据的方式另存。 注意需要在每组数据的第一行添加一个索引号,第一列是 0,第二列是 1。将数据导入 CloudPSS 时,会根据索引号判断数据内容。  数据格式处理 
- 
b. 导出 .csv 使用 dlmwrite命令将每路输入引脚的数据导出为.csv格式,命令参考如下:dlmwrite('input1.csv', input1, 'delimiter', ',', 'precision', 9);注意为保证导出的 .csv文件中数据的精度,请使用dlmwrite而不使用csvwrite。
步骤 3:利用自定义曲线元件导入 .csv 格式文件
- 
a. 自定义曲线元件导入 .csv 使用 CloudPSS 控制-非线性函数库中的自定义曲线元件,在元件参数面板的编辑数据中直接导入刚才生成的 .csv文件。 csv 数据导入 
- 
b. 数据输入 自定义曲线的输入是控制-基础库中的时间输入元件,输出连到 S-Function 元件的对应引脚。 开环数据输入 
- 
c. 对比开环测试结果 将 S-Function 元件的所有输入引脚都以 .csv 导入的形式输入数据。 在运行标签页中,配置电磁暂态仿真方案。  电磁暂态仿真方案配置 设置与生成 S-Function 时固定步长相同的积分步长,并根据输入数据的长度,设置合适的仿真结束时间。 运行仿真得到开环测试结果。 
案例
本文档提供一个白盒模型 S-Function 实现的案例,展示自定义 S-Function 元件的创建和使用方法。
- 三相整流器控制的 S-Function 实现
- 
a. 封装白盒模型 在 Simulink 中构建一个三相整流器模型,如下图所示。  三相整流器 Simulink 模型 将控制部分封装成一个子系统,并命名为 Control 。子系统有 3 个输入,维数分别是 3、3、1,和 1 个输出,维数是 3。  模型控制部分封装 将子系统中的锁相环比例系数 Kp_PLL、锁相环积分系数 Ki_PLL、电压环比例系数 Kp_V、电压环积分系数 Ki_V、电流环比例系数 Kp_I、电流环积分系数 Ki_I、直流电压参考值 Vdc_ref 和交流滤波电感值 Lf 定义为全局参数,并在模型初始化页面赋值。  定义全局参数 也可以对子系统进行参数封装后再引用全局参数。若封装后的参数未使用全局参数,将作为固定值写入 S-Function,不可调。  子系统参数封装后引用全局参数 本案例中的解算器设置如下图所示,固定步长 50 us,采用 ode1 积分方式(欧拉积分)。 运行仿真,确认当前设置下模型无报错。  模型参数配置 
- 
b. 自动代码生成 右键子系统,选择 C/C++ Code,点击 Generate S-Function,在弹出的对话框中勾选可调参数,点击 Build,开始自动生成代码并编译 S-Function。  生成S-Function 编译成功后,会弹出封装好的 S-Function 模块,以及自动生成的包含 *.c、*.h文件的 sfcn_rtw 文件夹。 S-Function 编译成功 
- 
c. 编译生成 .so 打开 sfcn_rtw 文件夹中的 Control_sf.c文件,添加接口函数定义。由于引脚维数不为 1,此步骤不可省略。 添加接口函数定义 在 MATLAB 安装路径的 simulink\include 和 extern\include 文件夹中,将所需 *.h头文件复制到 sfcn_rtw 文件夹。 添加头文件 将整个 sfcn_rtw 文件夹复制到 64 位 Linux 系统。  进入 Linux 编译环境 在路径下输入命令: gcc -Wall -DRT -DRT_MALLOC -fPIC -lm -shared *.c -o Control.so完成编译。  编译代码 编译成功后,会在路径下生成 .so文件,将.so文件复制到本地。 编译成功 
- 
d. 构建自定义 S-Function 元件 在 CloudPSS 平台新建自定义 S-Function 元件。在工作台点击新建按钮,新建一个空白普通电力系统模型。  新建项目 选择总览标签页,将模型类型切换为元件。  切换为元件类型 切换到实现标签页,选中电磁暂态子标签页,点击创建电磁暂态 - S-Function 实现按钮。  创建电磁暂态 - S-Function 实现 在接口标签页的参数列表定义栏,新建参数组,并添加参数,参数键名与 S-Function 的可调参数名一致,参数类型设为实数,输入类型为常量,默认值可与 Simulink 模型初始化页面的赋值一致。 在引脚列表定义栏,添加 input1、input2、input3 和 output1 引脚,按照对应的数据维数和连接类型设置。 在参数和引脚定义好后,可点击元件图形设计窗口右上角的直接导入按钮,根据引脚类型和数量生成一个默认图形,在默认图形基础上自定义元件图形。  添加元件参数和引脚并绘制图形 在实现标签页,导入 .so文件,填写入口函数名。注意,入口函数名不是.so文件的名称,而是 S-Function 的名称,本案例中是 Control_sf。 导入 .so 文件并填写入口函数 
- 
e. 开环测试 在 Simulink 模型的 S-Function 输入引脚处添加示波器,并设置录波。  闭环数据录波 运行仿真,整理录波数据,将每一路输入引脚处理成第一列时间、第二列数据,并在第一行添加索引。  数据整理 使用 dlmwrite命令将整理后的数据导出为.csv文件。 导出csv文件 在 CloudPSS 平台利用自定义曲线元件导入每一路输入引脚的数据,导入成功后如下图所示。  导入数据 完成所有输入引脚的数据导入后,开环测试模型如下图所示。  开环测试模型 运行仿真,对比 CloudPSS 与 Simulink 的开环测试结果,验证所构建的 S-Function 元件的正确性。 开环测试结果对比(output1 A 相) 
- 
f. 构建电气系统模型,完成闭环测试 搭建三相整流器的电气系统模型,并添加构建好的 S-Function 控制元件,S-Function 元件的输入引脚连接量测信号,运行仿真完成闭环测试。  三相整流器 S-Function 闭环模型 闭环测试结果如下图所示。 闭环测试结果对比(直流电压) 闭环测试结果对比(交流电流 A 相) 
三相整流器控制的 S-Function 实现 算例文件下载:model_admin_MicroGrid_Rectifier_S_Function_withpara.zip
常见问题
- 如何设置控制器的采样步长?
- 
S-Function 封装的采样步长固定为生成 S-Function 时控制器子系统的采样步长。在某些应用场景,控制器的采样步长可能与仿真步长不一致。Simulink 中,要求控制器采样步长为仿真步长的整数倍,如仿真步长为 10 us,而控制器以 50 us(仿真步长的 5 倍)进行采样和计算。 CloudPSS 中内置了采样倍率参数来应对这种情况。用户可根据控制器需求选择是否添加这个参数,当不添加这个参数时,其值默认为 1,即控制器采样步长与仿真步长一致。 该参数键名为:__SFUNC_STEP,添加方法如下:  添加控制器采样倍率参数 注意参数以两个下划线开头。为避免系统识别错误,用户自定义的其它全局参数名只能以字母开头,不能以数字或下划线开头。 
- 如何选择控制器求解的积分方式?
- 
针对 Simulink 中不同阶数的连续积分解算器,CloudPSS 进行了适配,目前支持的有 ode1、ode2、ode3、ode4、ode5 和 ode8。  支持的 Simulink 连续积分解算器 在 CloudPSS 中,内置了定义解算器类型的参数,参数键名为:__SFUNC_SOLVER,参数值为与目标解算器类型同名的字符串。例如,可将该参数定义为选择型参数,其键值分别为 ode1、ode2、ode3、ode4、ode5 和 ode8。  添加解算器类型参数 当模型中不存在连续积分时,解算器默认为离散解算器 discrete,该参数的设置无效;当模型中存在连续积分时,该参数值默认为 ode3。 
- 如何输入矩阵类型的参数?
- 
若 S-Function 的全局参数是一个矩阵,在 CloudPSS 中,需要用表格型参数与之对应。 例如,全局参数 a 是一个 1×3 的矩阵,其值为 [1, 2, 3]。在 CloudPSS 中构建自定义 S-Function 元件时,添加一个表格型参数,在列定义选项中新建 1 个列定义,在默认值选项中添加 3 行,并分别设置为 1、2、3,如下图所示。  表格型参数定义矩阵 在元件参数面板设置或修改表格型参数时,也可以点击参数框右侧的 (x) 按钮切换为表达式输入形式,再输入 [1, 2, 3],完成数据编辑。 表达式输入形式编辑参数数据 若全局参数 a 是一个 3×3 的矩阵,其值为 [1, 2, 3; 4, 5, 6; 7, 8, 9],则在 CloudPSS 中,按照先行后列的顺序,填入所有矩阵元素。例如,将表格型参数切换为表达式输入形式,再输入 [1, 2, 3, 4, 5, 6, 7, 8, 9]。