01 介绍
因为在pytorch中训练的模型需要使用carsim与matlab进行联合仿真验证,所以设计了如下程序进行TCP数据传输。
02 服务器端代码设计
导入的相关库如下:
import socket
import struct
import numpy as np
import time
import gc
创建了TCP服务器端的类,代码如下:
class TCPServer:
def __init__(self, ip, port, backlog=5, buffer_size=1024 * 8):
self.ip = ip
self.port = port
self.backlog = backlog
self.buffer_size = buffer_size
self.server_socket = None
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.ip, self.port))
self.server_socket.listen(self.backlog)
self.connection, self.address = self.server_socket.accept()
def start_send(self, data):
# connection, address = self.server_socket.accept()
# 将数据发送到服务器
data_bytes = selected_data.tobytes() # 将数据转换为字节流
self.connection.sendall(data_bytes)
print("发送成功")
time.sleep(0.00001)
# print(data)
return True
def start_read(self):
#connection, address = self.server_socket.accept()
while True:
time.sleep(0.001)
# 接收来自客户端的数据
data_bytes = self.connection.recv(24320) # 假设数据大小是40x(76)*8
if len(data_bytes) == 24320:
# 计算字节流中包含的双精度浮点数数量
num_double_values = len(data_bytes) // 8
# 解析字节流为双精度浮点数
double_values = []
for i in range(num_double_values):
start_index = i * 8
end_index = start_index + 8
double_bytes = data_bytes[start_index:end_index]
double_value = struct.unpack('d', double_bytes)[0]
double_values.append(double_value)
# 使用列表推导来将每个元素除以1000,并将结果存储为NumPy数组
received_data = np.array(double_values) / 1000
# 将数组重塑为40x76的形状
received_data = received_data.reshape(76, 40)
received_data = received_data.T
#self.server_socket.close()
# 在此处可以处理接收到的数据,例如打印或其他操作
print(received_data, "正在读取")
break
return received_data
def stop(self):
if self.server_socket:
self.server_socket.close()
主程序代码设置了while循环进行接收和发送。代码如下:
if __name__ == "__main__":
server1 = TCPServer('127.0.0.1', 22)
num = 1
while True:
print(num)
msg = server1.start_read()
server1.stop()
server2 = TCPServer('127.0.0.1', 23)
time.sleep(0.1)
# 读取CSV文件
data = np.genfromtxt('vehcile_features.csv', delimiter=',')
# 提取第二列到倒数第二列的前40行数据
selected_data = data[0:40, 1:-1]
selected_data = selected_data.T
# 显示提取的数据
print(selected_data)
# 将数据乘以1000
selected_data = selected_data * 1000
server2.start_send(selected_data)
num = num + 1
time.sleep(0.005)
server2.stop()
server1 = TCPServer('127.0.0.1', 22)
gc.collect()
03 Simulink端S-function模块设计
因为只是个demo文件,所以将Simulink设置如下所示:
以上使用零阶保持器将采样频率限制在10Hz,设置一级S-Function进行TCP通讯。设计代码如下:
function [sys, x0, str, ts, simStateCompliance] = TCPSimulink(t, x, u, flag)
%STORECARSIMDATASFUNCTION Custom MATLAB S-Function for storing Carsim data
% This S-Function takes input 'u' containing Carsim model output parameters
% and performs some calculations to store the data in Trajectory_data.
switch flag
case 0
[sys, x0, str, ts, simStateCompliance] = mdlInitializeSizes();
case 3
sys = mdlOutputs(t, x, u);
case 9
sys = mdlTerminate(t, x, u);
otherwise
sys = [];
end
function [sys, x0, str, ts, simStateCompliance] = mdlInitializeSizes()
% Initialize sizes, initial conditions, and sample times
%import java.net.Socket;
%import java.io.OutputStream;
%import java.io.DataOutputStream;
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 0;
sizes.NumOutputs = 1;
sizes.NumInputs = 1; % One input, which is the Carsim data
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
x0 = [];% 有状态的要初始化
str = [];
ts = [0 0]; % 一步为零或者-1
clc
% Specify the block simStateCompliance
simStateCompliance = 'UnknownSimState';
function sys = mdlOutputs(t, x, u)%模型输出
% Calculate and store the data
persistent Order historyData lastTimeStep currentTimeStep Trajectory_data last_historyData
if ~isempty(Trajectory_data)
Order = 0;
end
%% 远程主机为localhost,即本地主机,要连接的目的端口为22,作为客户机使用
client1=tcpip('127.0.0.1',22,'NetworkRole','client');
client2=tcpip('127.0.0.1',23,'NetworkRole','client');
%% 设置接收和发送缓存区的最大容量,这里设置的是1000*1000*8,也就是一个1000*1000的double类型的数组大小
client1.InputBuffersize=27000;
client1.OutputBuffersize=27000;
client2.InputBuffersize=27000;
client2.OutputBuffersize=27000;
%% 向服务器发送数据
%% 打开连接,寻找目的服务器,如果未找到,报错
% 读取CSV文件
data = readmatrix('vehcile_features.csv');
% 提取第二列到倒数第二列的前40行数据
Trajectory_data = data(1:40, 2:end-1);
% 显示提取的数据
% disp(Trajectory_data);
Trajectory_data = Trajectory_data * 1000;
client1.timeout = 2; % 增加超时时间
client2.timeout = 2; % 增加超时时间
fopen(client1);
% 将数据发送到服务器
data_bytes = typecast(Trajectory_data(:), 'uint8'); % 将数据转换为字节流
fwrite(client1, data_bytes);
disp('发送数据成功!!');
fclose(client1);
fopen(client2);
condition = 1;
while(condition)
pause(0.01)
received_bytes = fread(client2, 24320, 'uint8'); % 假设数据类型是uint8 , 24640 = 40*77*8
if ~isempty(received_bytes)
% 将字节流转换为双精度浮点数
received_data = typecast(uint8(received_bytes), 'double');
disp('接收数据成功!!');
% 将数据重新形状为40x76
received_data = reshape(received_data, 40,76 );
% 如果需要,将数据缩小1000倍
Predict_data = received_data / 1000;
Order = Order + 1;
condition = 0;
fclose(client2);
% pause(0.005)
break
else
%加一段等待时间
pause(0.025)
end
end
sys = [condition];
function sys=mdlGetTimeOfNextVarHit(t,x,u)%计算下一个采样时间点
sampleTime = 0.1; % Example, set the next hit to be one second later.
sys = t + sampleTime;
function sys = mdlTerminate(t, x, u)
% Perform end of simulation tasks
sys = [];
04 通讯程序测试结果展示
服务器端和客户端部分打印结果:
发送成功
98
[[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
...
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]] 正在读取
[[ nan 3.88000011 3.88000011 ... 3.88000011 3.88000011 3.88000011]
[ nan 3. 3. ... 3. 3. 3. ]
[ nan 1.82000005 1.82000005 ... 1.82000005 1.82000005 1.82000005]
...
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 3. 3. ... 3. 3. 3. ]]
发送成功
99
[[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
...
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]] 正在读取
[[ nan 3.88000011 3.88000011 ... 3.88000011 3.88000011 3.88000011]
[ nan 3. 3. ... 3. 3. 3. ]
[ nan 1.82000005 1.82000005 ... 1.82000005 1.82000005 1.82000005]
...
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 3. 3. ... 3. 3. 3. ]]
发送成功
100
[[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
...
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]
[3.88000011 3. 1.82000005 ... 0. 0. 3. ]] 正在读取
[[ nan 3.88000011 3.88000011 ... 3.88000011 3.88000011 3.88000011]
[ nan 3. 3. ... 3. 3. 3. ]
[ nan 1.82000005 1.82000005 ... 1.82000005 1.82000005 1.82000005]
...
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 0. 0. ... 0. 0. 0. ]
[ nan 3. 3. ... 3. 3. 3. ]]
发送数据成功!!
接收数据成功!!
发送数据成功!!
接收数据成功!!
发送数据成功!!
接收数据成功!!
发送数据成功!!
接收数据成功!!
05 注意事项
- 本TCP通讯程序使用了两个通道进行数据传输;
- 传输的数据是40*76的浮点型数据,其中matlab和python的行列之间需要转置才能保证数据相同;
- 在程序中的延时都是为了能够将通讯连接准确开闭;
- 在传输数据中,都是将浮点型转化为字节流传输的。