【问题标题】:Connecting Unity simulation with python controller over TCP通过 TCP 将 Unity 模拟与 python 控制器连接起来
【发布时间】:2017-07-20 18:40:32
【问题描述】:

出于试点研究的目的,我尝试将 Unity 中的简单模拟环境与基于神经网络的 python 控制器连接起来。模拟 Unity 环境中的主角应根据其经验学习行为:

  1. 主角扫描环境并将其存储在数据结构中;
  2. 此数据结构通过 TCP 发送到 Python 中正在运行的服务器;
  3. 在 python 中执行复杂的计算(基于从 Unity 接收的数据)并产生结果(动作);
  4. 结果(操作)通过 TCP 发送回 Unity。角色执行与结果对应的动作。
  5. 重复步骤 1-4 直到无穷大(除非客户端或服务器停止)。

这个过程可以抽象为通过 TCP 发送 Ping - Pong 消息,其中 Unity 发送“Ping”,Python 进行一些计算并产生“Pong”,该“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 服务器似乎不理解 Unity 客户端发送的“ping”字符串。此外,我希望 Unity 模拟中的帧在从 python 服务器接收到“Pong”消息之前不要更新。

我已经阅读了有关使用协程或线程的类似问题的解决方案,但我担心它会建立另一个“时钟”并导致没有真正的 ping-pong-updateframe-ping-pong-updateframe... 进程。

有什么想法吗?

提前致谢!!!

附:请记住,真正的消息不会是“Ping”和“Pong”,而是正确的 Json 对象。这只是一个简化。

编辑: 我首先启动 python 服务器,然后启动 Unity 模拟(在编辑器中)。 在 python 控制台上,我得到:

Client connected.
Unity Sent Something Else: ping

...这证明了我的说法是“python 不理解通过 Unity (C#) 发送的字符串”

在 Unity 控制台中我得到:

【问题讨论】:

  • 这个问题不止一个。对于第一个:python 服务器是否收到任何东西?你的程序打印什么?
  • 你能告诉我们当前代码发生了什么吗?
  • 我刚刚使用 python 打印和 Unity 控制台日志编辑了原始问题。还有...感谢大家的回复。
  • 你弄清楚如何以帧为基础进行通信了吗?

标签: python unity3d tcp simulation


【解决方案1】:

我刚刚找到了第一个问题的解决方案,即“python 不理解从 Unity 发送的字符串”。似乎是换行符导致了问题。

我刚刚删除了:

line = line + "\r\n";

原始代码中的行。这样,不会向字符串添加额外的转义字符。添加\r\n 导致python 的socket.recv()pingping\r\n 进行比较,它总是触发switch 语句中的默认情况。

其他方式(来自python)我发送pong\n(注意转义字符)以便Unity(C#)streamWriter.ReadLine()能够接收清晰的pong

现在相互信息理解起作用了。但是,它并没有解决我的第二个问题 - 到达 ping - pong - update frame 过程。

【讨论】:

    【解决方案2】:

    您应该使响应异步。您可以使用回调。您的错误是您正在进行连续轮询,并且套接字可用调用将始终为 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);
    }
    

    }

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 2020-10-13
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      • 2019-09-01
      • 2014-10-09
      • 2017-06-03
      • 2023-03-25
      相关资源
      最近更新 更多