Magic Leap2 の ハンドトラッキング
はじめに
この記事は、Magic Leap2 Advent Calendar 2022 の18日目です。
Magic Leap2 トラッキング について説明します。
Magic Leap 1 と同様にMagic Leap2 にもハンドトラッキングの機能が搭載しています。Magic Leap 1 の時は位置のみ取得することができ、回転情報はMagic Leap Toolkit(MLTK)を導入する必要がありましたが、Magic Leap2では標準のAPIで回転の情報も取得することができます。
OnePlanet XR について
このブログ記事は OnePlanet XR によるものです。
OnePlanet XR は、AR/MR/VPS技術に専門特化したコンサルティングサービスです。豊富な実績を元に、AR/MR技術を活用した新たな事業の立ち上げ支援や、社内業務のデジタル化/DX推進など、貴社の必要とするイノベーションを実現いたします。
ご相談から受け付けております。ご興味ございましたらお問い合わせください。
Magic Leap2 の ハンドトラッキングについて
ハンドトラッキングの各キーポイントにCubeを配置してトラッキングするデモアプリケーションか開発します。
開発環境 / 動作環境
Unity Editor 2022.2.0b8.3023
Magic Leap SDK 1.1.0-dev1
Magic Leap XR Plugin 7.0.0.pre.1
Magic Leap2 OS 1.1.0-dev1 (B3E.221020.13-R.039_40)
取得できるキーポイント
計28個のキーポイントの取得が可能です。
親指 4個
親指以外 5個
手の真ん中 1個
手首 3個(Wrist UlnarとWrist Radialは計算しないため実質1個。)
ヒエラルキー
Hand Tracking Example
シーンを新規作成します。Main Cameraは削除し、XR Rigのプレファブをヒエラルキーに配置します。
Game Objectを作成し、名前を Hand Tracking Example にします。
キーポイント用の Game Object 群
親指(4個のCube)、親指以外の4本の指(5個のCube)、中央(1個のCube)、手首(中央しかキーポイントは取得できないが、ここでは3個のCube)を左右分生成してヒエラルキー上に配置します。Cubeのスケールは0.05とします。
HandTrackingExample
Hand Tracking Example の Game Object にアタッチするHandTrackingExampleというスクリプトを作成します。
スクリプトは以下になります。
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.MagicLeap;
using InputDevice = UnityEngine.XR.InputDevice;
using HandTracking = UnityEngine.XR.MagicLeap.InputSubsystem.Extensions.MLHandTracking;
using System.Collections.Generic;
using System.Collections;
public class HandTrackingExample : MonoBehaviour
{
private float highConfidence = 0.6f;
private InputDevice leftHandDevice;
private InputDevice rightHandDevice;
private List<Bone> leftPinkyFingerBones = new List<Bone>();
private List<Bone> leftRingFingerBones = new List<Bone>();
private List<Bone> leftMiddleFingerBones = new List<Bone>();
private List<Bone> leftIndexFingerBones = new List<Bone>();
private List<Bone> leftThumbFingerBones = new List<Bone>();
private List<Bone> leftWristBones = new List<Bone>();
private List<Bone> rightPinkyFingerBones = new List<Bone>();
private List<Bone> rightRingFingerBones = new List<Bone>();
private List<Bone> rightMiddleFingerBones = new List<Bone>();
private List<Bone> rightIndexFingerBones = new List<Bone>();
private List<Bone> rightThumbFingerBones = new List<Bone>();
private List<Bone> rightWristBones = new List<Bone>();
[SerializeField]
private GameObject[] leftPinkyFinger;
[SerializeField]
private GameObject[] leftRingFinger;
[SerializeField]
private GameObject[] leftMiddleFinger;
[SerializeField]
private GameObject[] leftIndexFinger;
[SerializeField]
private GameObject[] leftThumbFinger;
[SerializeField]
private GameObject[] leftWrist;
[SerializeField]
private GameObject leftCenter;
[SerializeField]
private GameObject[] rightPinkyFinger;
[SerializeField]
private GameObject[] rightRingFinger;
[SerializeField]
private GameObject[] rightMiddleFinger;
[SerializeField]
private GameObject[] rightIndexFinger;
[SerializeField]
private GameObject[] rightThumbFinger;
[SerializeField]
private GameObject[] rightWrist;
[SerializeField]
private GameObject rightCenter;
// Start is called before the first frame update
void Start()
{
if (MLSegmentedDimmer.Exists)
{
MLSegmentedDimmer.Activate();
MLSegmentedDimmer.SetEnabled(true);
}
if (MLPermissions.CheckPermission(MLPermission.HandTracking).IsOk)
{
InputSubsystem.Extensions.MLHandTracking.StartTracking();
}
}
// Update is called once per frame
void Update()
{
TrackingLeftHand();
TrackingRightHand();
}
private void TrackingLeftHand()
{
if (!leftHandDevice.isValid)
{
leftHandDevice = InputSubsystem.Utils.FindMagicLeapDevice(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.Left);
return;
}
leftHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.Confidence, out float handConfidence);
if (handConfidence < highConfidence)
{
return;
}
if (leftHandDevice.TryGetFeatureValue(CommonUsages.handData, out UnityEngine.XR.Hand leftHand))
{
leftHand.TryGetFingerBones(UnityEngine.XR.HandFinger.Index, leftIndexFingerBones);
leftHand.TryGetFingerBones(UnityEngine.XR.HandFinger.Middle, leftMiddleFingerBones);
leftHand.TryGetFingerBones(UnityEngine.XR.HandFinger.Ring, leftRingFingerBones);
leftHand.TryGetFingerBones(UnityEngine.XR.HandFinger.Pinky, leftPinkyFingerBones);
leftHand.TryGetFingerBones(UnityEngine.XR.HandFinger.Thumb, leftThumbFingerBones);
leftWristBones.Clear();
leftHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristCenter, out Bone wrist);
leftWristBones.Add(wrist);
leftHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristRadial, out wrist);
leftWristBones.Add(wrist);
leftHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristUlnar, out wrist);
leftWristBones.Add(wrist);
}
for (int i = 0; i < leftPinkyFingerBones.Count; i++)
{
leftPinkyFingerBones[i].TryGetPosition(out Vector3 leftPinkyBonePosition);
leftPinkyFingerBones[i].TryGetRotation(out Quaternion leftPinkyBoneRotation);
leftPinkyFinger[i].transform.position = leftPinkyBonePosition;
leftPinkyFinger[i].transform.rotation = leftPinkyBoneRotation;
}
for (int i = 0; i < leftMiddleFingerBones.Count; i++)
{
leftMiddleFingerBones[i].TryGetPosition(out Vector3 leftMiddleBonePosition);
leftMiddleFingerBones[i].TryGetRotation(out Quaternion leftMiddleBoneRotation);
leftMiddleFinger[i].transform.position = leftMiddleBonePosition;
leftMiddleFinger[i].transform.rotation = leftMiddleBoneRotation;
}
for (int i = 0; i < leftRingFingerBones.Count; i++)
{
leftRingFingerBones[i].TryGetPosition(out Vector3 leftRingBonePosition);
leftRingFingerBones[i].TryGetRotation(out Quaternion leftRingBoneRotation);
leftRingFinger[i].transform.position = leftRingBonePosition;
leftRingFinger[i].transform.rotation = leftRingBoneRotation;
}
for (int i = 0; i < leftIndexFingerBones.Count; i++)
{
leftIndexFingerBones[i].TryGetPosition(out Vector3 leftIndexBonePosition);
leftIndexFingerBones[i].TryGetRotation(out Quaternion leftIndexBoneRotation);
leftIndexFinger[i].transform.position = leftIndexBonePosition;
leftIndexFinger[i].transform.rotation = leftIndexBoneRotation;
}
for (int i = 0; i < leftThumbFingerBones.Count; i++)
{
leftThumbFingerBones[i].TryGetPosition(out Vector3 leftThumbBonePosition);
leftThumbFingerBones[i].TryGetRotation(out Quaternion leftThumbBoneRotation);
leftThumbFinger[i].transform.position = leftThumbBonePosition;
leftThumbFinger[i].transform.rotation = leftThumbBoneRotation;
}
for (int i = 0; i < leftWristBones.Count; i++)
{
leftWristBones[i].TryGetPosition(out Vector3 leftWristBonePosition);
leftWristBones[i].TryGetRotation(out Quaternion leftWristBoneRotation);
leftWrist[i].transform.position = leftWristBonePosition;
leftWrist[i].transform.rotation = leftWristBoneRotation;
}
leftHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 leftHandCenterPosition);
leftHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion leftHandCenterRotation);
leftCenter.transform.position = leftHandCenterPosition;
leftCenter.transform.rotation = leftHandCenterRotation;
}
private void TrackingRightHand()
{
if (!rightHandDevice.isValid)
{
rightHandDevice = InputSubsystem.Utils.FindMagicLeapDevice(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.Right);
return;
}
rightHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.Confidence, out float handConfidence);
if (handConfidence < highConfidence)
{
return;
}
if (rightHandDevice.TryGetFeatureValue(CommonUsages.handData, out UnityEngine.XR.Hand hand))
{
hand.TryGetFingerBones(UnityEngine.XR.HandFinger.Index, rightIndexFingerBones);
hand.TryGetFingerBones(UnityEngine.XR.HandFinger.Middle, rightMiddleFingerBones);
hand.TryGetFingerBones(UnityEngine.XR.HandFinger.Ring, rightRingFingerBones);
hand.TryGetFingerBones(UnityEngine.XR.HandFinger.Pinky, rightPinkyFingerBones);
hand.TryGetFingerBones(UnityEngine.XR.HandFinger.Thumb, rightThumbFingerBones);
rightWristBones.Clear();
rightHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristCenter, out Bone wrist);
rightWristBones.Add(wrist);
rightHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristRadial, out wrist);
rightWristBones.Add(wrist);
rightHandDevice.TryGetFeatureValue(InputSubsystem.Extensions.DeviceFeatureUsages.Hand.WristUlnar, out wrist);
rightWristBones.Add(wrist);
}
for (int i = 0; i < rightPinkyFingerBones.Count; i++)
{
rightPinkyFingerBones[i].TryGetPosition(out Vector3 rightPinkyBonePosition);
rightPinkyFingerBones[i].TryGetRotation(out Quaternion rightPinkyBoneRotation);
rightPinkyFinger[i].transform.position = rightPinkyBonePosition;
}
for (int i = 0; i < rightMiddleFingerBones.Count; i++)
{
rightMiddleFingerBones[i].TryGetPosition(out Vector3 rightMiddleBonePosition);
rightMiddleFingerBones[i].TryGetRotation(out Quaternion rightMiddleBoneRotation);
rightMiddleFinger[i].transform.position = rightMiddleBonePosition;
rightMiddleFinger[i].transform.rotation = rightMiddleBoneRotation;
}
for (int i = 0; i < rightRingFingerBones.Count; i++)
{
rightRingFingerBones[i].TryGetPosition(out Vector3 rightRingBonePosition);
rightRingFingerBones[i].TryGetRotation(out Quaternion rightRingBoneRotation);
rightRingFinger[i].transform.position = rightRingBonePosition;
}
for (int i = 0; i < rightIndexFingerBones.Count; i++)
{
rightIndexFingerBones[i].TryGetPosition(out Vector3 rightIndexBonePosition);
rightIndexFingerBones[i].TryGetRotation(out Quaternion rightIndexBoneRotation);
rightIndexFinger[i].transform.position = rightIndexBonePosition;
rightIndexFinger[i].transform.rotation = rightIndexBoneRotation;
}
for (int i = 0; i < rightThumbFingerBones.Count; i++)
{
rightThumbFingerBones[i].TryGetPosition(out Vector3 rightThumbBonePosition);
rightThumbFingerBones[i].TryGetRotation(out Quaternion rightThumbBoneRotation);
rightThumbFinger[i].transform.position = rightThumbBonePosition;
rightThumbFinger[i].transform.rotation = rightThumbBoneRotation;
}
for (int i = 0; i < rightWristBones.Count; i++)
{
rightWristBones[i].TryGetPosition(out Vector3 rightWristBonePosition);
rightWristBones[i].TryGetRotation(out Quaternion rightWristBoneRotation);
rightWrist[i].transform.position = rightWristBonePosition;
rightWrist[i].transform.rotation = rightWristBoneRotation;
}
rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 rightHandCenterPosition);
rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion rightHandCenterRotation);
rightCenter.transform.position = rightHandCenterPosition;
rightCenter.transform.rotation = rightHandCenterRotation;
}
}
HandTrackingExampleにキーポイント用のCubeを設定
Hand Tracking Example の キーポイント用のフィールドにヒエラルキー上で生成した、キーポイント用の Game Object 群を全て設定します。
実行
左右の手を目の前にかざすとハンドトラッキング機能が働き、Cubeが手の動きに追従します。
OnePlanet XR
AR/MR/VPS技術に専門特化したコンサルティングサービス
Magic Leap2 を使ったソリューションのご検討の方からのお問い合わせ、お待ちしております。
お問い合わせ先
https://1planet.co.jp/xrconsulting.html#op_form
OnePlanet Tech Magazine
Magic Leap 1、Magic Leap2、スマホAR(Niantic Lightship ARDK、WebAR、VPSなど)といったAR技術全般をブログマガジンを連載しています。