2
votes

I am using the Leap Motion device to get usable data on the position and orientation of my hand. At the moment I am having trouble with the orientation segment of the coding. The Leap API only has code for frame by frame rotation, however, it also provides a normal vector (normal to the palm of the hand) and a pointer vector (pointing in the direction from the palm outwards towards the fingers). These two vectors are perpendicular.

The vectors:

Leap.Vector normal = current.PalmNormal;
Leap.Vector pointer = current.Direction; 

More information can be found on the Leap Hand API: https://developer.leapmotion.com/documentation/Languages/CSharpandUnity/API/class_leap_1_1_hand.html

Converting to Unity Vector3 class:

Vector3 normalU = new Vector3();
normalU.x = normal.x;
normalU.y = normal.y;
normalU.z = normal.z;
Vector3 pointerU = new Vector3();
pointerU.x = pointer.x;
pointerU.y = pointer.y;
pointerU.z = pointer.z;

I use these to vectors to calculate the Euler Angles orientation of my hand (a rotation of theta_x degrees about the x-axis, theta_y degrees about the y-axis, and theta_z degrees about the z-axis) using the code:

float rXn = Mathf.Atan (normalU.y/normalU.z);
rXn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normalU.z < 0) || (normalU.y > 0 && normalU.z < 0))
{
    rXn += 180f;
}
float rZn = Mathf.Atan (normalU.y/normalU.x);
rZn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normal.x > 0) || (normalU.y > 0 && normalU.x > 0))
{
    rZn += 180f;
}
float rYn = Mathf.Atan (pointerU.x/pointerU.z);
rYn *= (180f/Mathf.PI);
if ((pointerU.x > 0 && pointerU.z > 0) || (pointerU.x < 0 && pointerU.z > 0))
{
    rYn += 180f;
}

The Euler Angles are then converted to a Quaternion and implemented using the code:

Quaternion rotation = Quaternion.Euler (-rZn+90, -rYn, rXn+90);
rigidbody.MoveRotation (rotation);

More information on the Unity Quaternion class can be found here: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.html

As I coded this, I tested each axis of rotation individually, commenting out the others (setting them to 0), and they worked properly. However, when I implemented all three at once, the behaviors of rotations around an individual axis changed, which confuses me. Why would including recognition of rotation about the y-axis change the way rotation about the x-axis occurs?

As each individual axis of rotation worked when the others were commented out (and set to 0), I think the problem lies in the way the Euler Angles are converted to a Quaternion. I do not have a great understanding of the way Quaternions are used to represent rotations, however I am confused as to why changing the value of the angle of rotation about the y-axis would change the angle of rotation about the x-axis.

Thanks for your help.

2
Are you sure you meant to say Quaternion.Euler (-rZn+90, -rYn, rXn+90); and not Quaternion.Euler (-rXn+90, -rYn, rZn+90); or does -rZn+90 properly correlate to degrees of rotation about the x-axis, and -rXn+90 properly correlates to degrees of rotation about z-axis? - AndyG
The Unity API for Quaternion.Euler specifies the order of the arguments and it is z, y, x. - Tristan Hull
Correct me if I'm wrong, but this documentation seems to say otherwise: docs.unity3d.com/Documentation/ScriptReference/… - AndyG
@AndyG I think the key is the last bit "in that order", after defining rotations around Z, then X, then Y. Also, I recommend switching from atan to atan2. If normalU.z or normalyU.x are 0 you'll have division by 0. - Jerdak
@Jerdak: Thank you, but I still disagree. The parameter list gives x,y,z order. The description is most likely talking about the order the rotations would be applied (rotation about z applied first, rotation about y applied second, rotation about x applied last). See PMF's answer. - AndyG

2 Answers

2
votes

The order of rotation is relevant, and this might be what causes your confusion. Imagine a point on the x-axis at (1, 0, 0). When we now do a rotation of 90° around the x axis, nothing happens. Then we do a rotation of 90° around the y axis, which makes the point lie on the positive z-axis. If we change the order of rotation, the point will end on the y axis. Depending on the way your functions are implemented, they require a certain order of rotation to get the expected results.

0
votes

It's not perfect, but I am getting pretty good results with:

private void UpdateHandNormal( Hand hand, Transform marker )
{

    float y = Mathf.Atan( hand.Direction.x / hand.Direction.z );
    if( float.IsNaN( y ) ) y = 0;
    marker.localRotation = new Quaternion( hand.PalmNormal.z, -y, hand.PalmNormal.x, 1 ) ;  

}

Where hand is the Hand instance from the leap controller and marker is a simple rectangle representing the hand rotation.

I was getting NaN for y so I added the set to 0 check.

ath

J.