takeda_san’s blog

KotlinとVRを頑張っていく方向。

PythonからVMC Protocolでばもきゃに情報を送信してみよう

きっかけ

OAK-D-Liteを買った。
Just Danceで踊っている模様をトラッカーなしでばもきゃに反映するためだ。

つまり、OAK-D-Liteで取得したLandmarksをうまいことばもきゃに転送する必要がある。
たしかVMC Protocolという素敵な仕組みがあったような・・・?

VMC Protocol

VMC Protocolとは、ばもきゃとの情報送受信のプロトコル
Open Sound Controlという規格を使っているらしい。

Virtual Motion Capture Protocol (VMCProtocol 公式ページ) | VirtualMotionCaptureProtocol

Unityで使うことを想定したC#のツールとか参考になる実装は色々あるんだけど、今回はPythonを使っているので、ばもきゃとの通信用のプログラムもPythonで書きたいところ。
最低限のプログラムを書いて動作を確認したのでメモ書きしておきます。

とりあえず目線を動かそう

先ほど書いた通り、VMC ProtocolはOSCを使って送信します。
Python用のライブラリ(python-osc)があるので、ありがたく使いましょう。

python-osc · PyPI

pip install python-osc

プログラムはこんな感じ。
ほぼほぼサンプルのまま

import argparse
import random
import time

from pythonosc import udp_client

if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  parser.add_argument("--ip", default="127.0.0.1",
      help="The ip of the OSC server")
  parser.add_argument("--port", type=int, default=39539,
      help="The port the OSC server is listening on")
  args = parser.parse_args()

  client = udp_client.SimpleUDPClient("127.0.0.1", 39540)

  for x in range(10):
    client.send_message("/VMC/Ext/Set/Eye", [1, random.uniform(-1,1), random.uniform(-1,1), random.uniform(-1,1)])
    time.sleep(1)

大切なのはここ。
↓の仕様の通りで、/VMC/Ext/Set/Eye に瞳の座標データをランダムで送る。
本文?の部分は配列で送る必要があるっぽい。
(座標はすごい適当)
VMC Protocol Performerプロトコル仕様 | VirtualMotionCaptureProtocol

client.send_message("/VMC/Ext/Set/Eye", [1, random.uniform(-1,1), random.uniform(-1,1), random.uniform(-1,1)])

あとはばもきゃを起動して、設定から「OSCでモーション受信を有効にする」にチェックでプログラム実行。

疎通確認はこれでOK。

トラッカーの姿勢

トラッカーの姿勢を送るには、普通にHMDとトラッカーを使うときのようにキャリブレーションをする必要がある。 事前にheadとtra1とtra2を頭と手に設定しておくと、よしなキャリブレーションして姿勢を受け取ってくれる。 (またしても座標は適当なので、このままだとカメラ外に勢いよくぶっ飛んでいく)

  client.send_message("/VMC/Ext/Set/Calib/Ready", "")
  time.sleep(3)
  client.send_message("/VMC/Ext/Set/Calib/Exec", 0)
  for x in range(10):
    client.send_message("/VMC/Ext/Hmd/Pos", ["head", 0.5, 0.5, 0.8, 0.0, 0.0, 0.0, 0.0])
    client.send_message("/VMC/Ext/Tra/Pos", ["tra1", random.uniform(-0.1,0.1), random.uniform(-0.1,0.1), random.uniform(-0.1,0.3), 0.0, 0.0, 0.0, 0.0])
    client.send_message("/VMC/Ext/Tra/Pos", ["tra2", random.uniform(-0.1,0.1), random.uniform(-0.1,0.1), random.uniform(-0.1,0.3), 0.0, 0.0, 0.0, 0.0])
    time.sleep(1)

あとは、Landmarksの座標をUnity世界の座標に変換すればよさそう・・・だけど簡単にはいかなそうだなぁ…