WitMotionをUnityで使う

Unity Hubで新規プロジェクトを作成します.

  • 3D
  • version: 2020.3.21f1

オブジェクトを配置

二つのオブジェクトをHierarchyで作成し,Main CameraをVReye下に移動させます.

  1. VReye
  2. SerialHandler

以下,階層構造

スクリプトを作成

Assets内で以下二つのc#スクリプトを新規作成します.

  1. Rotate
  2. SerialHandler

それぞれの内容は以下をコピペしてください.

Rotate

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotate : MonoBehaviour
{
    public SerialHandler serialHandler;

    void Start(){
        // 
    }

    void Update(){
        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準に、回転を取得
        Vector3 worldAngle = myTransform.eulerAngles;
        worldAngle.y = (float)serialHandler.roll;
        worldAngle.x = (float)serialHandler.pitch;
        myTransform.eulerAngles = worldAngle;
    }
}
    
SerialHander

using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
using System;


public class SerialHandler : MonoBehaviour
{
    public delegate void SerialDataReceivedEventHandler(string message);
    public event SerialDataReceivedEventHandler OnDataReceived;

    //ポート名
    public string portName = "COM1";// 自分の使用したいセンサを確認し,適切なCOM番号に変更してください
    public int baudRate    = 115200;

    private SerialPort serialPort_;
    private Thread thread_;
    private bool isRunning_ = false;

    public Queue cmds = new Queue();


    private string message_;
    private bool isNewMessageReceived_ = false;

    private string msg = "";
    public double roll=0.0, pitch=0.0, yaw=0.0;

    byte[] buffer = new byte[100];
    int cnt=0;

    void Awake()
    {
        Open();
    }

    void Update()
    {
        // nothing
    }

    void OnDestroy()
    {
        Close();
    }

    private void Open()
    {
        serialPort_ = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
        serialPort_.Open();
        serialPort_.ReadTimeout = 50;
        
        isRunning_ = true;

        thread_ = new Thread(Read);
        thread_.Start();
    }

    private void Close()
    {
        isNewMessageReceived_ = false;
        isRunning_ = false;

        if (thread_ != null && thread_.IsAlive) {
            thread_.Join();
        }

        if (serialPort_ != null && serialPort_.IsOpen) {
            serialPort_.Close();
            serialPort_.Dispose();
        }

    }

    private void Read()
    {
        int plus       = 0;
        int minus      = 0;
        double preRoll = 0;

        // COMPort確認
        // string[] ports = SerialPort.GetPortNames();
        // foreach(string port in ports){
        //     // Debug.Log(port);
        // }

        while (isRunning_ && serialPort_ != null && serialPort_.IsOpen) {
            try {
                serialPort_.Read(buffer, 0, buffer.Length);
                isNewMessageReceived_ = true;
            } catch (System.Exception e) {
                Debug.LogWarning(e.Message);
            }

            for(int i=0;i<buffer.Length;i++){
                if(buffer[i]==0x55 && buffer[i+1]==0x53){
                    int start=i+2;
                    this.roll  = (buffer[start+1]*Math.Pow(2,8)+buffer[start+0])/32768.0*180;
                    this.pitch = (buffer[start+3]*Math.Pow(2,8)+buffer[start+2])/32768.0*180;
                    this.yaw   = (buffer[start+5]*Math.Pow(2,8)+buffer[start+4])/32768.0*180;
                    
                    // センサ値を[-180,180]に変換する
                    if(this.roll > 180){
                        this.roll -= 360;
                    }
                    // センサ値を[0→90→0],[0→-90→0]に変換する
                    if (this.pitch > 180) {
                        this.pitch -= 360;
                    }
                    // センサ値を[-180,180]に変換する
                    // if (yaw > 180) {
                    //     yaw -= 360;
                    // }

                    if(preRoll*this.roll <= 0){
                        if(Mathf.Abs((float)this.roll) > 150){
                            if(preRoll < 0){
                                minus++;
                            }else if(preRoll > 0){
                                plus++;
                            }
                        }
                        preRoll=this.roll;
                    }
                    this.roll += (plus-minus)*360;

                    // Check
                    // Debug.Log($"roll:{roll}");
                    // Debug.Log($"pitch:{pitch}");
                    // Debug.Log($"yaw:{yaw}");
                }
            }
        }
    }

    public void Write(string message)
    {
        try {
            serialPort_.Write(message);
        } catch (System.Exception e) {
            Debug.LogWarning(e.Message);
        }
    }
}

 

設定

Unity上でAssets内にあるSerialHanderをドラッグしHierarchyにあるSerialHanderの上でドロップし,スクリプトをアタッチします.

次に,Rotateも同様にMain Cameraにスクリプトをアタッチします.

その後,MainCameraをクリックし右側にあるInspectorタブ内のSerialHandlerの右のボックスにHierarchy内のSerialHanderオブジェクトをドラッグ&ドロップします.

 

※重要!

最後にUnity左上のEditタブ→Project Settings...→Player→Configuration→Api Compatibility Level*の部分を.NET standard 2.0から.NET 4.xに変更してください.

 

以上で終わりです!

画面上部中央の再生ボタンをクリックしてみてちゃんとセンサとUnity上のカメラが同期しているか確認してみてください.

 

追記(2023.9.22)

コード改良版は以下.

challenge-think.hatenablog.com