Appearance
Simulink MATLAB Function 使用
Simulink 中可以使用 MATLAB 函数,相关模块称为 MATLAB Function。基础部分可以先看Implement MATLAB Functions in Simulink with MATLAB Function Blocks。
When you simulate a model that contains a MATLAB Function block, the software generates binary code or C/C++ MATLAB executable (MEX) code from the block and integrates this code with the model.
TIP
因为涉及到 C/C++ 代码生成,所以 MATLAB Function 中直接使用的函数必须支持代码生成
代码生成存在一些限制(支持代码生成的函数列表)。但是可以使用 coder.extrinsic 声明外部调用的函数,这样执行时将不生成代码而直接由外部执行。
数据限制
- MATLAB Function 中使用到的变量,因为代码生成限制,必须是已知类型(已知大小)
- 声明其他变量时,需要在声明时给定一个初始值以确定大小
- 对
coder.extrinsic声明的函数,函数的返回值是mxArray类型。此类型也存在限制,但是 MATLAB 支持的mxArray操作包括:- 存储
mxArray到变量 - 将
mxArray传递给coder.extrinsic外部函数 - 将
mxArray从函数返回到 MATLAB - 在运行时将
mxArray转换为已知类型。将mxArray分配给类型已由先前赋值定义的变量
- 存储
我们的例子:TCP 通信
MATLAB 库提供 tcpclient 创建客户端(我个人觉得在 Python 侧创建服务器会更好操作,服务器任务更重)。tcpclient 是一个不支持代码生成的函数。
在这里我们做一个向服务器(地址 127.0.0.1:23466)不停发送一系列 double 数据的客户端。整体结构大概是这样:

这个函数不需要任何的外部输入输出,因此基础代码如下:
matlab
function client1
为了使用不受代码生成支持的 tcpclient 函数,同时,为了在反复调用 MATLAB Function 的过程中一直维持一个 tcpclient 对象,需要使用 coder.extrinsic 包含对应函数,并使用 persistent 变量(persistent)存储这个对象。代码中还需要包含相关初始化逻辑,只有当这个永久对象为空时,才进行初始化赋值(具体可以看最后的代码)。
matlab
function client
coder.extrinsic("tcpclient");
persistent client;
if isempty(client)
client = tcpclient("127.0.0.1", 23466);
end
disp(client);
end1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
注意第4行虽然 tcpclient 在这里返回了 mxArray,不过 disp 打印对象信息依然能看到 tcpclient 对象信息(实际是外部代码)。
tcpclient - 属性:
Address: '127.0.0.1'
Port: 23466
Tag: ""
NumBytesAvailable: 01
2
3
4
5
2
3
4
5
根据文档,tcpclient 提供了观察已经写入了多少字节的字段 NumBytesWritten。不过,因为客户端在内部表现为 mxArray 对象,因此原始对象中的所有成员信息都丢失了。但是 mxArray 可以传递给其他外部函数, 因此,若想获得 NumBytesWritten 信息,虽然不能直接在 MATLAB Function 中使用 client.NumBytesWritten,但是可以借助运行在 MATLAB 中的外部函数,返回有关信息。因为 NumBytesWritten 是一个 double 数据,对 MATLAB Function 来说是已知大小的,因此可以使用。
继续修改相关代码,尝试获取可得字节数量信息。
首先创建 numBytes.m 文件,写一个 numBytes 函数,得到相关信息:
matlab
function num = numBytes(client)
num = client.NumBytesWritten;
end1
2
3
2
3
随后,在 Simulink MATLAB Function 中,声明 numBytes 外部函数,并将 client 传递给它,拿到对应的信息。
- 注意声明
num时要先赋值,这样大小才是已知的符合相关代码生成要求,后面才能继续使用。 - client 要先检查是否为空,如果是空,说明是第一次使用它,则给它初始化;否则就直接使用,不能再创建新的 tcpclient,不然会丢失对象信息,每次都创建了一个新的 tcpclient,后续检查已发送字节数的结果将一直是 0。
matlab
function myClient()
coder.extrinsic("tcpclient");
coder.extrinsic("write");
coder.extrinsic("numBytes");
persistent client;
if isempty(client)
client = tcpclient("127.0.0.1", 23466, "EnableTransferDelay", true);
return
end
num = 1;
num = numBytes(client);
if num >= 80
disp("Already sent 80 bytes, hanging...");
else
write(client, 1, 'double');
end
end1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上述代码中,规定当 num 超过80也就是发送10个数据之后就返回而不继续发送数据。同时前5个数据是1,后5个数据是2。接下来检验效果。Python 服务端代码如下:
python
import socket
import struct
# 创建 TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
host = '127.0.0.1'
port = 23466
server_socket.bind((host, port))
# 监听连接
server_socket.listen()
print("服务器正在监听...")
num = 0
while True:
conn, addr = server_socket.accept()
print('连接地址:', addr)
while True:
data = conn.recv(8) # 一次读取 8 个字节
if not data:
break # 没有数据表示连接关闭
# 处理接收到的数据
num += 8
value = struct.unpack('d', data)[0]
print(f"收到数据: {value}")
if num == 80:
print("已经收到 80 个字节,程序结束")
break
conn.close()
break1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
上述 Python 代码中一定要设计好 conn.recv 的大小,建议与收到的单个数据大小吻合。
实验结果如下图所示,可以看到程序完整取得了全部的数据。
