PythonからVMC Protocolでばもきゃに情報を送信してみよう
きっかけ
OAK-D-Liteを買った。
Just Danceで踊っている模様をトラッカーなしでばもきゃに反映するためだ。
OAK-D-Liteをいい感じに固定することに成功した。 pic.twitter.com/rplcLaih8c
— takeda_san (@takedasan593) 2021年12月11日
つまり、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)があるので、ありがたく使いましょう。
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でモーション受信を有効にする」にチェックでプログラム実行。
PythonからVMC Protocol送れたわいわい。でもトラッカーの姿勢の送り方が謎。 pic.twitter.com/JmRngneZNY
— takeda_san (@takedasan593) 2021年12月11日
疎通確認はこれで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世界の座標に変換すればよさそう・・・だけど簡単にはいかなそうだなぁ…