Closed7

HoloLens2のハンドトラッキングで何か作る

FumiyaHrFumiyaHr

Simplified joint data access の Polling joint pose from HandJointUtils のコードを利用して、Jpint の Pose が取れた!

public class HandJointDetector : MonoBehaviour
{
    [SerializeField]
    private Handedness _handedness = Handedness.Both;

    private void Update()
    {
        if (HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, _handedness, out MixedRealityPose pose))
        {
            Debug.Log(pose);
        }
    }
}

しかし Update() で実行しているので検知する回数が多すぎますね。
Hand tracking events の Joint events も試してみよう。

FumiyaHrFumiyaHr

Hand tracking events の Joint events を試したところ、Update() の時と同じように検知する回数はかなり多いみたい。

public class HandJointDetector : MonoBehaviour, IMixedRealityHandJointHandler
{
    [SerializeField]
    private Handedness _handedness = Handedness.Both;

    private void OnEnable()
    {
        CoreServices.InputSystem.RegisterHandler<IMixedRealityHandJointHandler>(this);
    }

    private void OnDisable()
    {
        CoreServices.InputSystem.UnregisterHandler<IMixedRealityHandJointHandler>(this);
    }

    public void OnHandJointsUpdated(InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>> eventData)
    {
        if (eventData.Handedness == _handedness)
        {
            if (eventData.InputData.TryGetValue(TrackedHandJoint.IndexTip, out MixedRealityPose pose))
            {
                Debug.Log(pose);
            }
        }
    }
}

どちらでもよいのであれば、今回は Update() で Joint の Pose を取得する。
次は IndexTip と IndexKnuckle を取得して、人差し指を伸ばしているのを検知する。

FumiyaHrFumiyaHr

下記のサイトを参考に人差し指が真っ直ぐかの実装を追加してみる。

public class HandJointDetector : MonoBehaviour
{
    [SerializeField]
    private Handedness _handedness = Handedness.Both;

    private void Update()
    {
        if (IsStraight(0.8f, _handedness,
            TrackedHandJoint.IndexTip, TrackedHandJoint.IndexDistalJoint,
            TrackedHandJoint.IndexMiddleJoint, TrackedHandJoint.IndexKnuckle))
        {
            Debug.Log("Straight!!!");
        }
    }

    private bool IsStraight(float threshold, Handedness handedness, params TrackedHandJoint[] joints)
    {
        if (joints.Length < 3)
        {
            return false;
        }

        Vector3? oldVec = null;
        var dot = 1.0f;
        for (var index = 0; index < joints.Length - 1; index++)
        {
            if (HandJointUtils.TryGetJointPose(joints[index + 1], handedness, out MixedRealityPose pose1) &&
                HandJointUtils.TryGetJointPose(joints[index], handedness, out MixedRealityPose pose2))
            {
                var v = (pose1.Position - pose2.Position).normalized;
                if (oldVec.HasValue)
                {
                    dot *= Vector3.Dot(v, oldVec.Value);
                }
                oldVec = v;
            }
            else
            {
                return false;
            }
        }
        return dot >= threshold;
    }
}
  • 指を真っ直ぐにした時

  • 指を曲げた時

    ログなし

いい感じに人差し指を真っ直ぐにしたことを知ることができました。
次は指先に何か出します。

参考サイト

OculusQuest ハンドトラッキングSDKから、指Boneの情報を取得し分析する - Qiita

FumiyaHrFumiyaHr

HandJointUtils.TryGetJointPose()で判定だけだと、手を認識していないときも常に判定されていることに気付いた(真っ直ぐかばかり見ていて曲げている方を確認していなかった・・・)
手が認識されているかは IMixedRealitySourceStateHandler でできるらしい。
入力イベント - Mixed Reality Toolkit | Microsoft Docs
今度はこちらも含めて試してみる。

FumiyaHrFumiyaHr

Unityが落ちる、Windowsのブルーバックが連発してようやくサンプルで動作確認ができた。
ちゃんと右手を認識した時だけログが表示されることを確認。

public class HandJointDetector2 : MonoBehaviour,
    IMixedRealitySourceStateHandler,
    IMixedRealityHandJointHandler
{
    private Dictionary<Handedness, bool> _detectedHandedness = new Dictionary<Handedness, bool>()
    {
        {Handedness.Right, false },
        {Handedness.Left, false }
    };

    private void OnEnable()
    {
        CoreServices.InputSystem?.RegisterHandler<IMixedRealitySourceStateHandler>(this);
        CoreServices.InputSystem?.RegisterHandler<IMixedRealityHandJointHandler>(this);
    }

    private void OnDisable()
    {
        CoreServices.InputSystem?.UnregisterHandler<IMixedRealitySourceStateHandler>(this);
        CoreServices.InputSystem?.UnregisterHandler<IMixedRealityHandJointHandler>(this);
    }

    public void OnSourceDetected(SourceStateEventData eventData)
    {
        var hand = eventData.Controller as IMixedRealityHand;

        if (hand != null)
        {
            Debug.Log("Source detected: " + hand.ControllerHandedness);

            if (_detectedHandedness.ContainsKey(hand.ControllerHandedness))
            {
                _detectedHandedness[hand.ControllerHandedness] = true;
            }
        }
    }

    public void OnSourceLost(SourceStateEventData eventData)
    {
        var hand = eventData.Controller as IMixedRealityHand;

        if (hand != null)
        {
            Debug.Log("Source lost: " + hand.ControllerHandedness);

            if (_detectedHandedness.ContainsKey(hand.ControllerHandedness))
            {
                _detectedHandedness[hand.ControllerHandedness] = false;
            }
        }
    }

    public void OnHandJointsUpdated(InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>> eventData)
    {
        if (_detectedHandedness[Handedness.Right])
        {
            if (eventData.InputData.TryGetValue(TrackedHandJoint.Palm, out MixedRealityPose palmPose))
            {
                Debug.Log("Hand Joint Palm Updated: " + palmPose.Position);
            }
        }
    }
}

このスクラップは2022/05/18にクローズされました