为了进行试验研究,我尝试将Unity中的简单模拟环境与基于神经网络的python控制器连接起来。模拟Unity环境中的主角应该根据它的经验学习行为:
这个过程可以被抽象为通过TCP发送的Ping-Pong消息,其中Unity发送" Ping",Python进行一些计算并生成一个" Pong"正被送回Unity更新模拟并再次生成一个" Ping"并将其发送到python ...
我目前正在玩具项目中测试这个过程。我有一个脚本附加到Unity场景中的主摄像头,如下所示:
using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Net.Sockets;
using Newtonsoft.Json;
using System.Threading;
public class networkSocketSO : MonoBehaviour
{
public String host = "localhost";
public Int32 port = 50000;
internal Boolean socket_ready = false;
internal String input_buffer = "";
TcpClient tcp_socket;
NetworkStream net_stream;
StreamWriter socket_writer;
StreamReader socket_reader;
private void Start()
{
setupSocket();
}
void Update()
{
string received_data = readSocket();
switch (received_data)
{
case "pong":
Debug.Log("Python controller sent: " + (string)received_data);
writeSocket("ping");
break;
default:
Debug.Log("Nothing received");
break;
}
}
void OnApplicationQuit()
{
closeSocket();
}
// Helper methods for:
//...setting up the communication
public void setupSocket()
{
try
{
tcp_socket = new TcpClient(host, port);
net_stream = tcp_socket.GetStream();
socket_writer = new StreamWriter(net_stream);
socket_reader = new StreamReader(net_stream);
socket_ready = true;
}
catch (Exception e)
{
// Something went wrong
Debug.Log("Socket error: " + e);
}
}
//... writing to a socket...
public void writeSocket(string line)
{
if (!socket_ready)
return;
line = line + "\r\n";
socket_writer.Write(line);
socket_writer.Flush();
}
//... reading from a socket...
public String readSocket()
{
if (!socket_ready)
return "";
if (net_stream.DataAvailable)
return socket_reader.ReadLine();
return "";
}
//... closing a socket...
public void closeSocket()
{
if (!socket_ready)
return;
socket_writer.Close();
socket_reader.Close();
tcp_socket.Close();
socket_ready = false;
}
}
带有以下代码的python服务器:
import socket
import json
host = 'localhost'
port = 50000
backlog = 5
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(backlog)
while 1:
client, address = s.accept()
print "Client connected."
while 1:
data = client.recv(size)
if data == "ping":
print ("Unity Sent: " + str(data))
client.send("pong")
else:
client.send("Bye!")
print ("Unity Sent Something Else: " + str(data))
client.close()
break
如果我启动服务器然后运行Unity项目,我会发生奇怪的事情。也就是说,python服务器似乎不理解" ping" Unity客户端发送的字符串。此外,我希望Unity模拟中的帧在收到" Pong"之前不要更新。来自python服务器的消息。
我已经阅读了有关使用协同程序或线程的类似问题的解决方案,但我担心它会建立另一个" clock"结果没有真正的ping-pong-updateframe-ping-pong-updateframe ...进程。
有什么想法吗?
提前致谢!!!
P.S。请记住,真正的信息不会是" Ping"和" Pong"但适当的Json对象。这只是一种简化。
编辑: 我首先启动python服务器,然后启动Unity模拟(在编辑器中)。 在python控制台上我得到:
Client connected.
Unity Sent Something Else: ping
...这证明我声称" python不理解通过Unity发送的字符串(C#)"
在Unity控制台中,我得到:
答案 0 :(得分:0)
我刚刚找到了第一个问题的解决方案,即" python不理解Unity发送的字符串"。似乎换行序列导致了这个问题。
我刚删除了:
line = line + "\r\n";
原始代码中的行。这样,字符串中不会添加其他转义字符。添加\r\n
导致python的socket.recv()
将ping
与ping\r\n
进行比较,并且它始终在switch语句中触发默认大小写。
其他方式(来自python)我发送pong\n
(注意转义字符),以便Unity(C#)streamWriter.ReadLine()
能够接收清晰的pong
。
现在相互的消息理解起作用了。但是,它无法解决我的第二个问题 - 达到ping-pong - 更新帧过程。
答案 1 :(得分:0)
您应该将响应设为异步。您可以使用回调。您的错误是您正在进行连续轮询,并且套接字可用调用将始终为false。
在Unity中尝试此代码:
using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
public class Client : MonoBehaviour {
private Socket _clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
private byte[] _recieveBuffer = new byte[8142];
void Start() {
SetupServer ();
}
void Update() {
}
void OnApplicationQuit()
{
_clientSocket.Close ();
}
private void SetupServer()
{
try
{
_clientSocket.Connect(new IPEndPoint(IPAddress.Loopback,50003));
}
catch(SocketException ex)
{
Debug.Log(ex.Message);
}
_clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
}
private void ReceiveCallback(IAsyncResult AR)
{
//Check how much bytes are recieved and call EndRecieve to finalize handshake
int recieved = _clientSocket.EndReceive(AR);
if(recieved <= 0)
return;
//Copy the recieved data into new buffer , to avoid null bytes
byte[] recData = new byte[recieved];
Buffer.BlockCopy(_recieveBuffer,0,recData,0,recieved);
//Process data here the way you want , all your bytes will be stored in recData
Debug.Log(System.Text.Encoding.Default.GetString(_recieveBuffer));
SendData (System.Text.Encoding.Default.GetBytes ("ping"));
//Start receiving again
_clientSocket.BeginReceive(_recieveBuffer,0,_recieveBuffer.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
}
private void SendData(byte[] data)
{
SocketAsyncEventArgs socketAsyncData = new SocketAsyncEventArgs();
socketAsyncData.SetBuffer(data,0,data.Length);
_clientSocket.SendAsync(socketAsyncData);
}
}