Raspberry Pi と Unity



Raspberry Pi4 を買ってちょっと遊んでる。
いったん簡単なサンプルができたので自分メモ。
作ったのはタクトスイッチを押したら Python から LED を光らせつつ OSC を Mac に送り、Mac 側の Max で受信。それをそのまま Unity で再び OSC で localhost に渡してキューブに AddForce() とかをさせる。
Max を使わず Unity で直接 OSC で受けてもいいけど単純に使ってみたかったのだけ。


Python3 で python-osc を使うのでインストール。

pip3 install python-osc

タクトスイッチの回線にはプルダウン抵抗を On。
LED を一瞬光らせつつ OSCで Mac 側へメッセージを投げる。

import socket
import random
import RPi.GPIO as GPIO
from pythonosc import udp_client
from pythonosc.osc_message_builder import OscMessageBuilder
from time import sleep

LED_GPIO = 4
TACT_GPIO = 17
IP = socket.gethostbyname("ホスト名"); # もしくは IP 直
PORT = 6700

GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_GPIO, GPIO.OUT)
GPIO.setup(TACT_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # プルダウン抵抗有効

client = udp_client.UDPClient(IP, PORT)

try:
    while True:
        if GPIO.input(TACT_GPIO) == GPIO.HIGH:
            # OSC 送信
            print("Send")
            msg = OscMessageBuilder(address='/testmessage')
            msg.add_arg("Hello World")
            msg.add_arg(random.randrange(-10, 10))

            client.send(msg.build())
            
            # LED を一瞬光らせる
            GPIO.output(LED_GPIO, GPIO.HIGH)
            sleep(0.1)
            GPIO.output(LED_GPIO, GPIO.LOW)            
except KeyboardInterrupt:
    pass
finally:
    GPIO.cleanup()
    print('--- stop program ---');

続いて Max。今回始めて使うのでまだ良くわかってない。OSC での通信をどうすればいいのか分からなかったけど、単純に UDP での受け取り送信は用意されているので簡単。今回はきたものをそのまま違うポート番号で Send するだけ。(OSC 自体は UDP でも TCP でもできる企画っぽい)
あとは試しに音を鳴らしてみた。オーディオのデバイスの設定がどこか分からなかったけど(スピーカーから出したかった) [Option] > [AudioStatus] にあった。

続いて Unity 側。こっちはなれたもんなので平気。と思ってたけど テストで Python 側で OSC のサーバーを立ててたせいで Unity 側で受け取れず少しハマった。ライブラリは keijiro さんの OscJack。サンプルコードが Timeline 1.5 とかだとエラーがあったので UPM からバージョンを下げた。あとはイベントが来た際のスレッドがメインではないので、Unity 周りの処理をメインスレッドでやるように変えた。

using System.Threading;
using OscJack;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class AddForceFromOSC : MonoBehaviour
{
    private OscServer server;

    private Rigidbody rigidbody;

    void Start()
    {
        SynchronizationContext synchronizationContext = SynchronizationContext.Current;

        this.rigidbody = this.GetComponent<Rigidbody>();

        this.server = new OscServer(6701);
        this.server.MessageDispatcher.AddCallback("/testmessage",
            (address, data) =>
            {
                Debug.Log($"Message : {data.GetElementAsString(0)} : {data.GetElementAsInt(1)}");
                synchronizationContext.Post(this.AddRandomForce, default);
            });
    }

    private void OnDestroy()
    {
        this.server.Dispose();
    }

    private void AddRandomForce(object o)
    {
        this.rigidbody.AddForce(new Vector3(Random.Range(-2f, 2f), Random.Range(0f, 10f), Random.Range(-2f, 2f)), ForceMode.Impulse);
        this.rigidbody.AddTorque(new Vector3(Random.Range(-5f, -5f), Random.Range(-5f, 5f), Random.Range(-5f, 5f)), ForceMode.Impulse);
    }
}

あとは通信が来ているのかどうかを見るために tcpdump というコマンドを使った。

sudo tcpdump -A -n udp port 6700

こんな感じで監視して、ここが来てなければそもそも送られていない。

Unity の OSCJack で 再生したり OSCMonitor を立ち上げるとサーバーが生成されるのでそのあとビルドなんかをしてしまうとそっち側で確立ができなく動かなかったいするので注意が必要。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です