2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MagicLeapのハンドトラッキング実装

Last updated at Posted at 2020-07-30

MagicLeapのハンドトラッキング実装の備忘録

基本的にはここの通りに進めていけば問題ない

前提となるもの

  • MagicLeapのmpkをビルドできる段階までのセッティングが完了している
  • The LabでのZeroIterationが可能になっている

開発環境
Windows10
Unity 2019.3.7f1
MagicLeapUnityPackage 0.24.1

出来上がり

こんな感じに各関節
手首、親指 ~ 小指までの座標の取得
ジェスチャの取得( サンプルの中では利用していない )

handtracking.gif

実装方法

下準備

シーンに配置するGameObject
大体のVRデバイスとかで主流な構成の配置にしてます
image.png

CameraRig
ただのルートオブジェクトとして利用しています

Head
MagicLeapパッケージ内 Core > Assets > Prefabs > MainCamera
のプレハブをCameraRigの子オブジェクトにしてHeadと名称を変えたものです、この中にDirectionalLightを子オブジェクトとして配置していますが配置するか否かはお好みで
image.png

Controller
MagicLeapパッケージ内 Examples > Assets > Prefabs > Controller
のプレハブをCameraRigの子オブジェクトとして配置しています
こちらの記事で紹介したコントローラの入力でホームボタンでアプリを閉じるために利用してます
image.png

LHand, RHand
今回のメイン
CameraRigの子オブジェクトとしてGameObjectを作成( Emptyとして )
Thumb( 親指 ), Index( 人差し指 ), Middle( 中指 ), Ring( 薬指 ), Pinky( 小指 )をそれぞれEmptyのGameObjectで作成し、
各指の子オブジェクトThumb, Index, Middleは3個、Ring, Pinkyは2個、SphereObjectを生成、スケールは0.01くらいに設定
image.png

スクリプト

このスクリプトはMagicLeap公式サンプルのものに手を加えたものです
以下のスクリプトをLHand, RHandにアタッチ

using UnityEngine;
using UnityEngine.XR.MagicLeap;



/// <summary>
/// ハンドトラッキング.
/// </summary>
public class HandController : MonoBehaviour
{

    [System.Serializable]
    public class HandJointData
    {
        [SerializeField] GameObject wrist;
        [SerializeField] GameObject[] thumb;
        [SerializeField] GameObject[] index;
        [SerializeField] GameObject[] middle;
        [SerializeField] GameObject[] ring;
        [SerializeField] GameObject[] pinky;
        [SerializeField] Material handMaterial;
        [SerializeField] Color color;
        
        public Vector3 Wrist { get; private set; }
        public Vector3[] Thumb { get; private set; }
        public Vector3[] Index { get; private set; }
        public Vector3[] Middle { get; private set; }
        public Vector3[] Ring { get; private set; }
        public Vector3[] Pinky { get; private set; }
        MLHandTracking.Hand hand;
        LineRenderer[] lines;

        public void Initialize(
            MLHandTracking.Hand _hand)
        {
            hand = _hand;
            Wrist = Vector3.zero;
            
            // 各関節 + 手首の分, 配列をとる.
            Thumb = new Vector3[thumb.Length + 1];
            Index = new Vector3[index.Length + 1];
            Middle = new Vector3[middle.Length + 1];
            Ring = new Vector3[ring.Length + 1];
            Pinky = new Vector3[pinky.Length + 1];
            
            lines = new LineRenderer[5];
            lines[0] = thumb[0].transform.parent.gameObject.AddComponent<LineRenderer>();
            lines[0].positionCount = 4;
            
            lines[1] = index[0].transform.parent.gameObject.AddComponent<LineRenderer>();
            lines[1].positionCount = 4;

            lines[2] = middle[0].transform.parent.gameObject.AddComponent<LineRenderer>();
            lines[2].positionCount = 4;
            
            lines[3] = ring[0].transform.parent.gameObject.AddComponent<LineRenderer>();
            lines[3].positionCount = 3;

            lines[4] = pinky[0].transform.parent.gameObject.AddComponent<LineRenderer>();
            lines[4].positionCount = 3;

            // LineRendererの初期セッティング.
            foreach (var line in lines)
            {
                line.material = handMaterial;
                line.startColor = color;
                line.endColor = color;
                line.startWidth = 0.01f;
                line.endWidth = 0.01f;
            }
        }


        public void UpdatePositions()
        {
            if (hand == null) return;
            
            Thumb[0] = hand.Wrist.KeyPoints[0].Position;
            Thumb[1] = hand.Thumb.KeyPoints[0].Position;
            Thumb[2] = hand.Thumb.KeyPoints[1].Position;
            Thumb[3] = hand.Thumb.KeyPoints[2].Position;
            for (var i = 1; i < Thumb.Length; ++i)
            {
                thumb[i - 1].transform.position = Thumb[i];
            }
            lines[0].SetPositions(Thumb);

            Index[0] = hand.Wrist.KeyPoints[0].Position;
            Index[1] = hand.Index.KeyPoints[0].Position;
            Index[2] = hand.Index.KeyPoints[1].Position;
            Index[3] = hand.Index.KeyPoints[2].Position;
            for (var i = 1; i < Index.Length; ++i)
            {
                index[i - 1].transform.position = Index[i];
            }
            lines[1].SetPositions(Index);

            Middle[0] = hand.Wrist.KeyPoints[0].Position;
            Middle[1] = hand.Middle.KeyPoints[0].Position;
            Middle[2] = hand.Middle.KeyPoints[1].Position;
            Middle[3] = hand.Middle.KeyPoints[2].Position;
            for (var i = 1; i < Middle.Length; ++i)
            {
                middle[i - 1].transform.position = Middle[i];
            }
            lines[2].SetPositions(Middle);
            
            Ring[0] = hand.Wrist.KeyPoints[0].Position;
            Ring[1] = hand.Ring.KeyPoints[0].Position;
            Ring[2] = hand.Ring.KeyPoints[1].Position;
            for (var i = 1; i < Ring.Length; ++i)
            {
                ring[i - 1].transform.position = Ring[i];
            }
            lines[3].SetPositions(Ring);

            Pinky[0] = hand.Wrist.KeyPoints[0].Position;
            Pinky[1] = hand.Pinky.KeyPoints[0].Position;
            Pinky[2] = hand.Pinky.KeyPoints[1].Position;
            for (var i = 1; i < Pinky.Length; ++i)
            {
                pinky[i - 1].transform.position = Pinky[i];
            }
            lines[4].SetPositions(Pinky);
            
        }
    }
    

    // ジェスチャ.
    public enum HandPoses
    {
        Ok,
        Finger,
        Thumb,
        OpenHand,
        Fist,
        NoPose,
        NoHand,
    }

    public enum HandId
    {
        RightHand,
        LeftHand
    }


    [SerializeField] HandPoses handPose = HandPoses.NoPose;
    [SerializeField] HandJointData handData;
    [SerializeField] HandId handId;
    MLHandTracking.HandKeyPose[] gestures;
    MLHandTracking.Hand hand;

    
    void Start()
    {
        // HandTrackingを開始する.
        MLHandTracking.Start();
        
        hand = handId == HandId.LeftHand ? MLHandTracking.Left : MLHandTracking.Right;
        handData.Initialize(hand);

        gestures = new MLHandTracking.HandKeyPose[6];

        // 各ジェスチャを登録.
        gestures[0] = MLHandTracking.HandKeyPose.Ok;
        gestures[1] = MLHandTracking.HandKeyPose.Finger;
        gestures[2] = MLHandTracking.HandKeyPose.OpenHand;
        gestures[3] = MLHandTracking.HandKeyPose.Fist;
        gestures[4] = MLHandTracking.HandKeyPose.Thumb;
        gestures[5] = MLHandTracking.HandKeyPose.NoHand;

        MLHandTracking.KeyPoseManager.EnableKeyPoses(gestures, true, false);

    }


    void OnDestroy()
    {
        MLHandTracking.Stop();
    }

    
    void Update()
    {
        handData.UpdatePositions();
        
        if (GetGesture(hand, MLHandTracking.HandKeyPose.Ok))
        {
            handPose = HandPoses.Ok;
        }
        else if (GetGesture(hand, MLHandTracking.HandKeyPose.Finger))
        {
            handPose = HandPoses.Finger;
        }
        else if (GetGesture(hand, MLHandTracking.HandKeyPose.OpenHand))
        {
            handPose = HandPoses.OpenHand;
        }
        else if (GetGesture(hand, MLHandTracking.HandKeyPose.Fist))
        {
            handPose = HandPoses.Fist;
        }
        else if (GetGesture(hand, MLHandTracking.HandKeyPose.Thumb))
        {
            handPose = HandPoses.Thumb;
        }
        else
        {
            handPose = HandPoses.NoPose;
        }
    }


    /// <summary>
    /// ジェスチャの取得.
    /// </summary>
    /// <param name="hand"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    private bool GetGesture(
        MLHandTracking.Hand hand, 
        MLHandTracking.HandKeyPose type)
    {
        if (hand == null) return false;

        return 0.9f < hand.HandKeyPoseConfidence && hand.KeyPose == type;
    }

}


アタッチしたら以下の画像のように各関節のオブジェクトをセット、配列の添え字が若い方が根元に来るように設定
image.png

各パラメータの説明
HandPose : ジェスチャのポーズ名( 今回はInspectorに表示しているだけです )
HandData : 各関節のオブジェクトを保持するクラス、HandCenterは利用していません( うまくトラッキングできなかったので外しました )
HandMaterial : 各関節オブジェクトの球をつなぐ線の描画用マテリアルです、今回はMagicLeapパッケージ内のUIBeamを利用しました
Color : 各関節オブジェクトの球をつなぐ線の色です、今回は左は赤、右は緑で設定しています
HandId : 手の左右を決定する識別子

Unity の設定

この状態でTheLabでMagicLeapと接続してPlayModeに入るとハンドトラッキングされたオブジェクトの様子が確認できると思います
ただしmpkファイルとして出力する際は以下の設定を行わないと実機ではエラーが出てハンドトラッキング及びジェスチャの取得はできません
公式のチュートリアル通りにやれば設定の仕方まで説明されてたけど必要なメソッドとか確認したらすぐ実行したくなっちゃうのよね

image.png

Edit > ProjectSettigs > MagicLeap > ManifestSettings の項目を開き
GestureConfig, GestureSubscribeにチェックを入れる ( 公式チュートリアルだと明示的にLowLatencyLightwearにもチェックを入れるように説明されているが現バージョンでは自動で入ってる? )

これでmpkを出力して実機でテストするとハンドトラッキングが実装できているはずです

あとがき

これはTheLabからDLしてきたMagicLeapUnityPackageでのサンプルです、MagicLeapToolKitでのハンドトラッキングはまだ触ったことがないので後日記事にできればと思います

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?