2017-03-01 30 views
2

我嘗試將Unity中的簡單模擬環境與基於神經網絡的python控制器連接起來。模擬Unity環境中的主要角色應該根據它的經驗學習一種行爲:爲了試點研究的目的,通過TCP連接Unity的模擬與python控制器

  1. 主要字符掃描環境並將其存儲在數據結構中;
  2. 此數據結構通過TCP發送到python中的正在運行的服務器;
  3. 一個複雜的計算(基於從Unity接收到的數據)是在python中執行的,它產生一個結果(action);
  4. 結果(動作)通過TCP發送回Unity。角色執行與結果相對應的動作。
  5. 重複步驟1-4直到無限(除非客戶端或服務器停止)。

這個過程可以抽象爲通過TCP發送的乒乓信息,其中Unity發送一個「Ping」,Python進行一些計算併產生一個「Pong」,它將被髮送回Unity,從而更新模擬並再次產生一個「平」,並將其發送到python ...

我目前在一個玩具項目中測試這個過程。我有連接到主攝像頭的團結場景腳本,看起來像這樣:

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; 
    } 
} 

並用下面的代碼蟒蛇服務器:

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 

如果我啓動服務器,然後運行團結項目我得到奇怪的事情。也就是說,python服務器似乎並不理解Unity客戶端發送的「ping」字符串。此外,我希望Unity模擬中的幀在它從python服務器收到「Pong」消息之前不要更新。

我已經閱讀過有關使用協程或線程的類似問題的解決方案,但我擔心它會建立另一個「時鐘」,並導致沒有真正的乒乓更新幀乒乓更新過程。

任何想法?

在此先感謝!

P.S.請記住,真正的消息不會是「Ping」和「Pong」,而是適當的Json對象。這只是一個簡化。

編輯: 我首先啓動python服務器,然後啓動Unity模擬(在編輯器中)。 蟒控制檯我得到:

Client connected. 
Unity Sent Something Else: ping 

...這證明我的要求是 「蟒蛇不明白通過Unity(C#)發送字符串」

在統一的控制檯,我得到:

enter image description here

+0

這是一個以上的問題。對於第一個:python服務器是否收到任何東西?你的程序打印什麼? – Dschoni

+0

你能告訴我們當前的代碼會發生什麼嗎? – Programmer

+0

我剛剛用python打印和Unity控制檯日誌編輯了原始問題。並且...感謝回覆的人。 – thecritter

回答

0

我剛剛發現我的第一個問題的解決方案,即「python不理解從Unity發送的字符串」。看來換行序列引起了這個問題。

我只是刪除了:原代碼

line = line + "\r\n"; 

線。這樣,沒有額外的轉義字符被添加到字符串。添加\r\n導致python的socket.recv()pingping\r\n進行比較,並且它總是觸發switch語句中的默認情況。

其他解決方法(從python)我發送pong\n(注意轉義字符)以便Unity(C#)streamWriter.ReadLine()能夠接收清除pong

現在互信理解起作用了。但是,它並沒有解決我的第二個問題 - 達到乒乓更新幀的過程。

0

您應該使響應異步。你可以使用回調。你的錯誤在於你正在進行連續輪詢,並且套接字可用的調用將始終爲假。

嘗試在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); 
} 

}