Git Product home page Git Product logo

unity3d-python-communication's Introduction

Unity3D-Python-Communication

⚡️ A very fast, simple, and general inter-process communication example between Unity3D C# and Python, using ZeroMQ.

unity-cmd-play-example.gif PS. It looks slow in the GIF above because I put a delay of one second between each message so that you can see it working.

Core Pillars

  • very fast — ZeroMQ is a networking library that allows you to send huge amount of data from server to client in a short period of time. I'm talking about casually sending/receiving 10,000 requests per second.
  • simple — You don't have to explicitly open and bind a socket or know anything about low-level networking.
  • general — You can use this to send/receive any kind of data request. You can send image, video, text, JSON, file, or whatever you want. In this example, we are sending text.
  • inter-process — The communication is done inside the same machine. Which means very low-latency.

Introduction

  • Have you ever tried to communicate C# code in Unity3D with Python before but could not find a satisfying solution?
  • Have you ever tried implementing communication protocol using file read/write and found out that it's a stupid approach?
  • Have you ever tried communicating using Web HTTP request and found out that it's stupidly slow and high latency?
  • Have you ever tried communicating using socket/TCP/UDP stuff, but it feels like you are reinventing the wheel and you are becoming a network engineer?
  • Have you ever tried to communicate by emulating a serial port, and found out that it's not how cool guys do work?
  • Have you ever tried to send Unity input to python and do some scientific work (maybe even machine learning task) and return the output to Unity?
  • Have you ever tried to build a .dll from python or even rewrite everything in C# because you don't know how to communicate between python and C# processes?
  • Have you ever tried to embed IronPython or Python.NET inside Unity but it doesn't allow you to install your amazing external python libraries? (And its minimal power is pretty ridiculous compared to your external python)
  • Have you ever tried to export a TensorFlow Protobuf Graph (Deep learning model) and use TensorFlowSharp or OpenCVForUnity to import the graph inside Unity because you want to use the model to predict stuff in Unity, but it doesn't allow you to use/utilize your new NVIDIA GeForce RTX 2080Ti, and it's also hard to code?
  • Tried MLAgents, anyone?

If you answer Yes to any of these questions but it seems you have found no solutions, then this repository is definitely for you! (If you answered Yes to all questions, you and me are brothers! 😏)

I've tried a lot. With a lot of searching on the internet, I've found no solutions that is simple, fast, and general enough that I can apply to any kind of communication between Python and Unity3D. All I've done in the past were simply a hack to either get my scientific computation work in Unity instead of python, or communicate between the processes painfully.

Until I found ZeroMQ approach from this repository (and some head scratching).

Solution Explanation

I've built a request-reply pattern of ZeroMQ where Python (server) replies whenever Unity (client) requests a service from Python.

The idea is to create a separate thread inside Unity that will send a request to python, receive a reply and log the reply to the console.

Getting Started

  1. Clone this repository using git clone https://github.com/offchan42/Unity3D-Python-Communication.git command.
  2. Open UnityProject (its dll files are targeting .NET 4.x version) and run Assets/NetMQExample/Scenes/SampleScene.
  3. Run python file PythonFiles/server.py using command python server.py on a command prompt.
  4. You should start seeing messages being logged inside Unity and the command prompt.

Specifically, Unity will send request with a message Hello 10 times, and Python will simply reply World 10 times. There is a one second sleep between each reply on the server (to simulate long processing time of the request).

Please read the comments inside PythonFiles/server.py and UnityProject/Assets/NetMQExample/Scripts/ and you will understand everything more deeply.

The most important thing is that you should follow the 4 getting started steps first. Don't skip it! ❣️

After you've understood most of the stuff but it's not advanced enough, you should consult the official ØMQ - The Guide.

Requirements

  • PyZMQ is the Python bindings for ZeroMQ. You can install it using pip install pyzmq command or see more installation options here or here.
  • NetMQ is a native C# port of ZeroMQ. Normally you need to install this using NuGet package manager inside Visual Studio when you want to build a .NET application, or you could install using .NET CLI. But for this repository here, you don't need to do any of the installation because we've already included AsyncIO.dll and NetMQ.dll for you inside UnityProject/Assets/NetMQExample/Plugins/ directory. If you want to build your own dll files, please take a look at this issue.

Known Issues

Based on this issue, the NetMQ implementation is not working nicely with Unity. If you create more than one ZeroMQ client in Unity, the Unity editor will freeze.

Troubleshooting

  • While both server and client are running and communicating fine, I kill the server process, restart the server, then both server and client seem to not be communicating anymore. Why don't they continue communicating? Is this a bug?

    No, this is the expected behavior of ZeroMQ because of the simplicity of the code. It's mentioned in the guide here. If you want to make the code better, which is not the focus of this example, you can learn more about ZeroMQ as suggested in the screenshot below. troubleshooting-1.PNG

    The problem is that when you restart the server, the server won't reconnect to the old client anymore. You have to restart the client also.

Disclaimer

This repository is designed to be a minimal learning resource for getting started. It's not a fully working high-level package. After you understand the example, my job is done.

Most of the code are just copies from the official ZeroMQ tutorial. I try to make this as simple to grasp as possible, so I only log the message to the console and nothing fancy. This is to minimize the unnecessary learning curve.

TODO

  • Add a complicated example of how to use it for real
  • Show how to do this with SocketIO. SocketIO is another approach I found very viable and stable. I use BestHTTP package in Unity for SocketIO client and use python-socketio as SocketIO server. And it does not have the issue of making Unity editor freezes.

unity3d-python-communication's People

Contributors

imgbotapp avatar offchan42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unity3d-python-communication's Issues

Can it send different info to different clients?

I have such scenario, in the python server, it uses class to update and calculate the instance' s state, then sends such state to corresponding client; in the client side, more than one clients need to communication with the server to get its one result and not interfere by others. Can your tool distinguish with different clients?

About send pictures

Hello!
1、Should messages be sent only in child threads in order not to block the Unity main thread?
2、I need to send pictures,so I used client.SendFrame(Convert.ToBase64String(CameraHelper.CaptureFrame(Camera)));
But I got an error from unity:UnityException: get_targetTexture can only be called from the main thread

So How to send pictures by your plugin?Thank you!

Can this be used for communication between 2 pc?

Hi,
Thank you for this great repo.
I am a beginner so my question might seem very trivial.

My project requires me to run a Unity project on one computer and run a python script in another computer. The two computers will be on the same network. We need to send data from the python script (pc-2) to Unity (pc-1). Can this be done using your repo?

The python script will output a matrix of 6 numbers. So we are thinking of using UDP. It is a PyTorch inferencing script. As the Unity pc's GPU will already be under stress we decided to run the deep learning inferencing on another pc.

[Question] Can this be run in game?

We are looking for a in-game Python interpreter, so player can code Python in game.

We came across in this great repo and would like to know if this can be used in game, instead of using an external terminal to communicate...?

Or in this case IronPython is enough for me? (I havn't dig deep into IronPython yet...)

Can be used for communication web browser and local machine?

Thanks for the response to #16 ! I think the question was not clearly deliver to you (#16). The issue was not about type of data. The issue is that i need to communicate between web browser (the python server is running in the web browser (using Jupyter Notebook)) and unity (running on local machine). But i am not able to send data from browser to unity. Please give some guidance on that.

server.py stuck on thread

Hi, I've been looking for something like this (I really felt the second to last question on your readme lol) I am trying to run a modified version of R-CNN mask from python to unity, the script is from this repository and just gets the image saved inside the unity project so I can display it later with opencv for unity.

so on server.py, I added some stuff (I am pretty new to python so it may be ugly)

import time
import zmq
import os
import threading

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")

#run on thread
def runMask(image):
    os.chdir("path/to/rcnn/mask")
    #I know there must be a better way to just call that function, I'm open to suggestions 
    cmd = "python ColorPopAndPotraitMode.py ColorPop --image "+image.decode()
    os.system(cmd)

while True:
    #  Wait for next request from client
    message = socket.recv()

    #message will be the name of the picture (path)
    print("Received request: %s" % message)
    print(os.getcwd())
    print("starting mask script")

    #starting thread to run mask (remember to put the ',' on args, else it would be treated as a tuple)
    thread = threading.Thread(target=runMask, args=(message,))
    thread.start()
    print("running mask")
    socket.send(b"... running mask ...")
    thread.join()

    #  Send reply back to client
    #  In the real world usage, after you finish your work, send your output here
    response = "done, "+os.getcwd()
    socket.send(response.encode())

I wrapped in a thread because is quite heavy and I wanted to make sure it would wait for the process to finish, however it gets stuck on this part forever until I close the window

2020-08-01 21:42:51.503401: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library cublas64_10.dll
2020-08-01 21:42:51.757888: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library cudnn64_7.dll
2020-08-01 21:42:52.802719: W tensorflow/stream_executor/gpu/redzone_allocator.cc:314] Internal: Invoking GPU asm compilation is supported on Cuda non-Windows platforms only
Relying on driver to perform ptx compilation.
Modify $PATH to customize ptxas location.
This message will be only logged once.

I think it may have to do with tensorflow but if I run the exact same code in a different file without the server part, it works all right.
So, is there like a timeout or something that may provoke that from server.py?

btw the reason I am not using the Mask rcnn integrated on Opencv for unity is because it is using just a checkpoint (that much lighter .pb file) from the original model and the accuracy is not enough for our project, where we need to take body measurements, although we need that to run on android, and we are using GPU here, but that's for another day.

I running on windows 10, Nvidia GeForce GTX 1650 SUPER, python 3.8.3, tensorflow 2.2, keras 2.4, unity 2019.3.15

I know it is kind of unrelated, but if you have any questions, please let me know.

Thank you

Sending Images from Unity to Python

Can you please share a short example of sending images from Unity to Python over the socket? The type of image is not of great importance, all that is required is for each image to come in sequentially with latency as low as possible. Thanks.

about broadcast message

This is a good solution to my project.
But I have another question. I have multiple clients(in unity) and How to write a broadcast message from one client to all other clients.I look forward to your reply.

Client just stops receiving stuff after ~30 seconds

I am working on a screen capture using a bridge between Python and Unity with this library.

This is my client code:

public class HelloRequester : RunAbleThread
{
    /// <summary>
    ///     Request Hello message to server and receive message back. Do it 10 times.
    ///     Stop requesting when Running=false.
    /// </summary>
    protected override void Run()
    {
        ForceDotNet.Force(); // this line is needed to prevent unity freeze after one use, not sure why yet
        using (RequestSocket client = new RequestSocket())
        {
            client.Connect("tcp://localhost:6789");

            for (int i = 0; i < 100000 && Running; i++)
            {
                client.SendFrame("Hello");
                // ReceiveFrameString() blocks the thread until you receive the string, but TryReceiveFrameString()
                // do not block the thread, you can try commenting one and see what the other does, try to reason why
                // unity freezes when you use ReceiveFrameString() and play and stop the scene without running the server
//                string message = client.ReceiveFrameString();
//                Debug.Log("Received: " + message);
                byte[] bytes = null;
                bool gotMessage = false;
                while (Running)
                {
                    gotMessage = client.TryReceiveFrameBytes(out bytes); // this returns true if it's successful
                    if (gotMessage) break;
                }

                if (gotMessage)
                {
                    var colors = new List<Color>();
                    for (var p = 0; p < bytes.Length; p += 4)
                        colors.Add(new Color(bytes[p + 2] / 255f, bytes[p + 1] / 255f, bytes[p] / 255f));

                    HelloClient.pixels = colors;
                    HelloClient.update = true;
                }
            }
        }

        NetMQConfig.Cleanup(); // this line is needed to prevent unity freeze after one use, not sure why yet
    }
}

This is my server code:

import time
import zmq
from mss import mss

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:6789")

while True:
    #  Wait for next request from client
    message = socket.recv()

    #  Do some 'work'.
    #  Try reducing sleep time to 0.01 to see how blazingly fast it communicates
    #  In the real world usage, you just need to replace time.sleep() with
    #  whatever work you want python to do, maybe a machine learning task?
    time.sleep(0.1)

    #  Send reply back to client
    #  In the real world usage, after you finish your work, send your output here
    with mss() as sct:
        # Use the 1st monitor
        monitor = sct.monitors[0]

        # Grab the picture
        im = sct.grab(monitor)

        # Get the entire PNG raw bytes
        socket.send(im.raw)

(I tried to keep it as similar as possible as the hello world example)

If I set the time.sleep() to 2 seconds, I can get exactly 14 requests through until the client just stops getting any messages from the python server (it just gets stuck in the while gotMessage loop with no errors). If I set the time.sleep() to lower I can get more requests through but it seems like the connection just stops working after around ~30 seconds. What am I doing wrong?

Thanks!

Python to Unity

Hi @off99555 thanks for this cool stuff.
I just test your example and it is working fine. I want to ask that, I'm running this github project which is in Python. My teacher asked me to transfer all the python code to C++ to run it in Unity.
So my question is that using your stuff can I directly run python code to Unity and how we can build dll files directly from Python?

trolling users

The troubleshooting section says that killing the process using CTRL + C will break the server, and the example file "server.py" has a while True statement making the only way to stop the scrip (CTRL + C) crash the server.

Communication stops

I have downloaded and opened the Unity project in Unity 2020.3.27f1 and run server.py. It works as is and stops at 10 message sent and received. When I modify the code HelloRequester.cs to run continuously the process stops at 30. Every time. It stops here:

gotMessage = client.TryReceiveFrameString(out message);
if (gotMessage) break; // gotMessage is always false after 30 messages.

The strange thing is I can stop and play Unity again and the messages start sending/receiving again - unit 30. I have tired reducing the delay in the python code and the number of messages goes up but they still stop consistently at the same time each time.

Any ideas on how to get around this?

Sending message based on button press

First of all, thanks for sharing this with the community.
Having a problem simply sending a message after the initial handshake, say from some UI input like a button press.
Most likely because of my lack of understanding on how threads work in Unity.

Any chance you might post a quick example?
If not a button then a simple message sent from HelloClient a few seconds after launch?

Thanks in advance.

connect SUMO to Unity3D

Hello, how can I connect SUMO to Unity3D? I have cloned your projects and simulation is running but unfortunately connection between Unity3D with SUMO is not working.

Sending Frames from Unity to Python

Hello,

I have an object classification model on Python which is trained using Keras. I want to get frames from Unity and analyze the frames on Python. Is it possible? If it is, can you explain?

Trying to understand the reason for using both threading and non-blocking TryReceiveFrameString

Hi,
Thank you, again for the repo. I am trying to learn from your codes. I see that you created a new thread and commented here that we need a new thread so that unity can do other stuff. I assume the new thread will be running the client in "parallel".
Then in here you said that if we used ReceiveFrameString() we would block unity from doing other stuff.

My first question is that, if we have already started a new thread to deal with blocking receive function, then why do we need to use TryReceiveFrameString(). Even if this new thread gets blocked for using ReceiveFrameString(), why would it hamper unity?

If we used ReceiveFrameString() and the new thread gets blocked until it received a response from the server isn't that good for the processing power? I see in your ToDo (in the readme.md) that, the current implementation is calling TryReceiveFrameString() in a loop without any delay which is bad for processing power.

Again, thank you for your time.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.