【问题标题】:Unity3D Leap Motion - Put hand in static pose (Pose done just can't rotate)Unity3D Leap Motion - 将手置于静态姿势(完成的姿势无法旋转)
【发布时间】:2018-01-25 09:36:31
【问题描述】:

我正在尝试将手设置为一系列姿势中的一个。我创建了一个脚本,它捕获一个帧,序列化左手,然后将其存储为 xml 以供加载。我目前让系统通过计算实时数据中手掌位置与存储姿势之间的偏移量来将手设置为正确的姿势。我遇到的问题是让手旋转。

'hand' 参数是来自实时数据的当前手,'pose' 参数是从 xml 加载的手。

这是我创建的方法:

public static Hand SetHandInPose(Hand hand, Hand pose)
{
    if(hand == null)
    {
        Debug.Log("Hand is null, so returning that");
        return hand;
    }
    if(pose == null)
    {
        Debug.Log("The loaded pose is null, so let's just return the original hand");
        return hand;
    }
    Hand h = pose;

    Quaternion handRotOffset = pose.Rotation.ToQuaternion() * Quaternion.Inverse(hand.Rotation.ToQuaternion());
    //Debug.Log("The rotational offset is: " + handRotOffset.eulerAngles);
    Vector offset = hand.PalmPosition - pose.PalmPosition;

    h.Rotation = hand.Rotation;//(h.Rotation.ToQuaternion() * handRotOffset).ToLeapQuaternion();
    h.PalmPosition += (offset.ToVector3()).ToVector();
    h.WristPosition += (offset.ToVector3()).ToVector();

    for(int f = 0; f< h.Fingers.Count; f++)
    {
        for(int i = 0; i < h.Fingers[f].bones.Length; i++)
        {
            //offset = hand.Fingers[f].bones[i].Center - pose.Fingers[f].bones[i].Center;     

            //if (h.Fingers[f].bones[i].Type == Bone.BoneType.TYPE_METACARPAL) continue;
            h.Fingers[f].bones[i].Center += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].NextJoint += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].PrevJoint += (offset.ToVector3()).ToVector();
            h.Fingers[f].bones[i].Rotation = hand.Fingers[f].bones[i].Rotation;
            //h.Fingers[f].bones[i].Rotation = (h.Fingers[f].bones[i].Rotation.ToQuaternion() * handRotOffset).ToLeapQuaternion();
            h.Fingers[f].bones[i].Direction = (h.Fingers[f].bones[i].NextJoint - h.Fingers[f].bones[i].PrevJoint);
            h.Fingers[f].bones[i].Center = (h.Fingers[f].bones[i].PrevJoint + h.Fingers[f].bones[i].NextJoint) / 2f;
        }
        h.Fingers[f].Direction = h.Fingers[f].GetBone(Bone.BoneType.TYPE_INTERMEDIATE).Direction;
    }
    return h;
}

任何建议/帮助将不胜感激。

更新:

这是根据收到的建议的新代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Leap.Unity;

 public class MimicHandModelDriver : MonoBehaviour
 {

    public Chirality whichHand = Chirality.Left;

    public enum RotationMode { Inherit, Overwrite }
    public RotationMode rotationMode = RotationMode.Inherit;

    public MultiLeap_CapsuleHand handModelToDrive;
    private bool _handModelInitialized = false;
    private bool _handModelBegun = false;

    private Hand mimicHand = null;

    Hand sourceHand; //The current hand from the live data
    Hand poseHand; //Reference to the hand we load from XML that is in the pose we want

    public Hand SourceHand
    {
        set
        {
            sourceHand = value;
        }
    }

    public Hand PoseHand
    {
        set
        {
            poseHand = value;
        }
    }

    private void Update()
    {

        if (sourceHand != null)
        {
            // Copy data from the tracked hand into the mimic hand.
            if (mimicHand == null) { mimicHand = new Hand(); }

            mimicHand.CopyFrom(poseHand); //copy the stored pose in the mimic hand
            mimicHand.Arm.CopyFrom(poseHand.Arm); // copy the stored pose's arm into the mimic hand

            // Use the rotation from the live data
            var handRotation = sourceHand.Rotation.ToQuaternion();

            // Transform the copied hand so that it's centered on the current hands position and matches it's rotation.
            mimicHand.SetTransform(sourceHand.PalmPosition.ToVector3(), handRotation);
        }

        // Drive the attached HandModel.
        if (mimicHand != null && handModelToDrive != null)
        {
            // Initialize the handModel if it hasn't already been initialized.
            if (!_handModelInitialized)
            {
                handModelToDrive.SetLeapHand(mimicHand); //Prevents an error with null reference exception when creating the spheres from
                //the init hand call
                handModelToDrive.InitHand();
                _handModelInitialized = true;
            }

            // Set the HandModel's hand data.
            handModelToDrive.SetLeapHand(mimicHand);

            // "Begin" the HandModel to represent a 'newly tracked' hand.
            if (!_handModelBegun)
            {
                handModelToDrive.BeginHand();
                _handModelBegun = true;
            }

            Debug.Log("Updating the mimic hand");
            handModelToDrive.UpdateTheHand(); //This method contains the update code, with update code commented out
            //so i control when a hand is updated. I have used this throughout the rest of my project so i know this works. 
        }
    }

}

【问题讨论】:

  • 看起来这个问题得到了答案,但几天后,这个问题根据一个新问题进行了实质性修改。我不知道这会在多大程度上使您在下面已经收到的帮助无效,但是由于您在新问题中提出了新问题,所以我认为这个更好地回到原来的状态。请记住,如果您的问题变化如此之大,以至于下面的答案是错误的或令人困惑的,那么他们可能会因此而被否决 - 尽量确保问答保持良好的秩序。
  • 给出的答案并不适用。
  • 它发生了。不过,如果您将此问题修改为当前问题,您将获得三份而不是两份。我不知道原来的情况与你的新情况有多大不同,但总的来说,即使答案没有帮助,我也不会修改它,因为对于一个问题和一个不同意的答案来说,它仍然令人困惑。我建议您关注第二个问题,因为 +2 可能有助于获得新观点。

标签: unity3d vector rotation quaternions leap-motion


【解决方案1】:

我们在 UnityModules 中内置了一些便利/扩展方法,可以更轻松地复制手部数据:

hand.Transform(LeapTransform transform) -- 这将应用手的变换。 hand.SetTransform(Vector3 position, Quaternion rotation) -- 这将转换一只手以使 PalmPosition 以位置为中心,并将整个手部转换为与rotation 对齐。 hand.CopyFrom(Hand other) -- 这个将处理将数据从一只手复制到另一只手的所有血腥细节(阅读:姿势)。

在这种情况下,CopyFrom 和 SetTransform 将为您提供您正在寻找的数据。

编辑:因此,您的问题的新方面涉及使用这些数据来驱动 HandModel——在本例中为 CapsuleHand。对于我们当前版本的 Unity 资产,手模型管道非常关闭。您要么使用标准的 Leap 装备来驱动真正的履带式手,要么您遇到了一些困难。

幸运的是,您可以手动驱动 HandModel,但您必须小心以正确的顺序调用正确的方法。查看此示例脚本,该脚本可以驱动它所引用的 HandModel。 重要提示:因为您独立于普通的 Provider/HandPool/HandGroup 管道来驱动 HandModel,所以您需要创建一个新的 HandModel - 例如一个新的、重复的 CapsuleHand 对象——它不在在您的 Leap Rig 中,也没有被该 Rig 的 HandPool 驱动。然后这个脚本可以驱动它:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Leap.Unity;

public class MimicHandModelDriver : MonoBehaviour {

  public Chirality whichHand = Chirality.Left;

  public enum RotationMode { Inherit, Overwrite }
  public RotationMode rotationMode = RotationMode.Inherit;

  public HandModelBase handModelToDrive;
  private bool _handModelInitialized = false;
  private bool _handModelBegun = false;

  [Header("Debug")]
  public bool drawEditorGizmos = false;

  private Hand mimicHand = null;

  private void Update() {
    // Get a hand from the standard tracking pipeline.
    var sourceHand = Hands.Get(whichHand);

    if (sourceHand != null) {
      // Copy data from the tracked hand into the mimic hand.
      if (mimicHand == null) { mimicHand = new Hand(); }
      mimicHand.CopyFrom(sourceHand);
      mimicHand.Arm.CopyFrom(sourceHand.Arm); // Capsule Hands like to have Arm data too.

      // Figure out what rotation to use for the mimic hand.
      var handRotation = this.transform.rotation;
      if (rotationMode == RotationMode.Inherit) {
        handRotation = mimicHand.Rotation.ToQuaternion();
      }

      // Transform the copied hand so that it's centered on this object's transform.
      mimicHand.SetTransform(this.transform.position, handRotation);
    }

    // Drive the attached HandModel.
    if (mimicHand != null && handModelToDrive != null) {
      // Initialize the handModel if it hasn't already been initialized.
      if (!_handModelInitialized) {
        handModelToDrive.InitHand();
        _handModelInitialized = true;
      }

      // Set the HandModel's hand data.
      handModelToDrive.SetLeapHand(mimicHand);

      // "Begin" the HandModel to represent a 'newly tracked' hand.
      if (!_handModelBegun) {
        handModelToDrive.BeginHand();
        _handModelBegun = true;
      }

      // Every Update, we call UpdateHand. It's necessary to call this every update
      // specifically for CapsuleHands, which uses manual GL calls to render rather than
      // a standard MeshRenderer.
      handModelToDrive.UpdateHand();
    }
  }

  // Draw some gizmos in case there's no HandModel attached.
  private void OnDrawGizmos() {
    if (!drawEditorGizmos) return;

    Gizmos.color = Color.red;

    if (mimicHand != null) {
      draw(mimicHand.PalmPosition.ToVector3());

      for (int f = 0; f < 5; f++) {
        for (int b = 0; b < 4; b++) {
          draw(mimicHand.Fingers[f].bones[b].NextJoint.ToVector3());
        }
      }
    }
  }

  private void draw(Vector3 pos) {
    Gizmos.DrawWireSphere(pos, 0.01f);
  }

}

【讨论】:

  • 我之前尝试过使用 hand.transform 和 hand.setTransform,但是虽然它确实将手放在正确的位置,但它会导致手像疯了一样旋转。
  • 嗨,你还在吗?
  • 我无法重现您使用 SetTransform 描述的问题。在导入最新的 UnityModules 核心资产后,我刚刚在新项目中重新测试了上述脚本,在此处找到:developer.leapmotion.com/unity -- 传递给 SetTransform 的旋转参数是绝对的;我没有观察到它们在手上堆积。您正在运行最新版本的 Leap Motion 核心资产吗?您是否确认您传递给 SetTransform() 的旋转参数实际上是您希望手部具有的旋转?
  • 或者,如果您可以创建一个简单的示例脚本来重现您在使用 SetTransform 时看到的错误,我可以为您提供修复。
  • 我已根据您的示例添加了代码,显示了我想要的内容。它适用于这个 Gizmos 示例,但由于某种原因,当我将它与胶囊手一起使用时,它会像疯了一样旋转。
猜你喜欢
  • 2020-11-16
  • 2013-02-15
  • 1970-01-01
  • 2021-08-28
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多