Skip to main content

In one of my previous blog posts, I showed you how to measure joint angles using Kinect and C#. Today, we’ll dive into a more complex topic: in this article, you are going to learn how to measure the orientation of a joint around each axis (X, Y, Z).

Measuring the orientation values is not trivial because it requires some good knowledge of Mathematics. Do not be afraid, though! After reading this article, you’ll be able to calculate the orientation of each joint using one line of C# code!

Sounds good? Let’s get started.

Prerequisites

To run the code and samples provided in this guide, you’ll need the following:

Let’s do the Math…

Kinect is reading the joint orientation values as a quaternion. A quaternion is a set of 4 values: X, Y, Z, and W.

The Kinect SDK is encapsulating the quaternion into a structure called Vector4. We need to transform this quaternion (Vector4) into a set of 3 numeric values.

Using the Orientation quaternion, we can calculate the rotation of the joint around the X, Y, and Z axis.

Kinect Joint Rotation

Pitch: rotating around the X-axis

The rotation around the X axis is called Pitch. Here is how to measure it:

public static double Pitch(this Vector4 quaternion)
{
    double value1 = 2.0 * (quaternion.W * quaternion.X + quaternion.Y * quaternion.Z);
    double value2 = 1.0 - 2.0 * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
    double roll = Math.Atan2(value1, value2);
    return roll * (180.0 / Math.PI);
}

Yaw: rotating around the Y-axis

The rotation around the Y axis is called Yaw. Here is how to measure it:

public static double Yaw(this Vector4 quaternion)
{
    double value = 2.0 * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X);
    value = value > 1.0 ? 1.0 : value;
    value = value < -1.0 ? -1.0 : value;
    double pitch = Math.Asin(value);
    return pitch * (180.0 / Math.PI);
}

Roll: rotating around the Z-axis

The rotation around the Z axis is called Roll. Here is how to measure it:

public static double Roll(this Vector4 quaternion)
{
    double value1 = 2.0 * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y);
    double value2 = 1.0 - 2.0 * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
    double yaw = Math.Atan2(value1, value2);
    return yaw * (180.0 / Math.PI);
}

Using the code

Here is the complete code. All you have to do is import the following C# file into your Kinect project.

using System;
using Microsoft.Kinect;
namespace LightBuzz.Vitruvius
{
    /// <summary>
    /// Provides extension methods for transforming quaternions to rotations.
    /// </summary>
    public static class JointOrientationExtensions
    {
        /// <summary>
        /// Rotates the specified quaternion around the X axis.
        /// </summary>
        /// <param name="quaternion">The orientation quaternion.</param>
        /// <returns>The rotation in degrees.</returns>
        public static double Pitch(this Vector4 quaternion)
        {
            double value1 = 2.0 * (quaternion.W * quaternion.X + quaternion.Y * quaternion.Z);
            double value2 = 1.0 - 2.0 * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
            double roll = Math.Atan2(value1, value2);
            return roll * (180.0 / Math.PI);
        }
        /// <summary>
        /// Rotates the specified quaternion around the Y axis.
        /// </summary>
        /// <param name="quaternion">The orientation quaternion.</param>
        /// <returns>The rotation in degrees.</returns>
        public static double Yaw(this Vector4 quaternion)
        {
            double value = 2.0 * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X);
            value = value > 1.0 ? 1.0 : value;
            value = value < -1.0 ? -1.0 : value;
            double pitch = Math.Asin(value);
            return pitch * (180.0 / Math.PI);
        }
        /// <summary>
        /// Rotates the specified quaternion around the Z axis.
        /// </summary>
        /// <param name="quaternion">The orientation quaternion.</param>
        /// <returns>The rotation in degrees.</returns>
        public static double Roll(this Vector4 quaternion)
        {
            double value1 = 2.0 * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y);
            double value2 = 1.0 - 2.0 * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
            double yaw = Math.Atan2(value1, value2);
            return yaw * (180.0 / Math.PI);
        }
    }
}

Then, in your main C# file, import the following namespace:

using LightBuzz.Vitruvius;

And, finally, specify the joint you would like to measure the orientation for and call the Roll, Pitch, and Yaw methods:

var orientation = body.JointOrientations[JointType.ElbowLeft].Orientation;
var rotationX = orientation.Pitch();
var rotationY = orientation.Yaw();
var rotationZ = orientation.Roll();
Download the code on GitHub

 

Supported joints

Measuring the rotation of the human body joints is not a trivial job. Unfortunately, Kinect does not provide us with Orientation values for the Head, Hands, and Feet. These joints do not have a “parent” joint, so it’s extremely difficult to accurately measure their orientation. The orientation accuracy regarding the rest of the joints is pretty good. The supported joints are the following:

  • Neck
  • SpineShoulder
  • SpineBase
  • ShoulderLeft/ShoulderRight
  • ElbowLeft/ElbowRight
  • WristLeft/WristRight
  • HipLeft/HipRight
  • KneeLeft/KneeRight

The method described in this post will help you in most use-case scenarios, such as simple games. In case you would like to have increased accuracy (e.g. healthcare apps), you’ll need to use more complex algorithms, which involve a lot of custom coding. LightBuzz has a lot of experience with this kind of algorithms and could help you with your project.

Summary

In this blog post, you’ve learnt how to easily measure the rotation of the human body joints around the X, Y, and Z axis.

PS: Vitruvius

If you liked this post, then you’ll love Vitruvius. Vitruvius is the result of my Kinect research during the past 5 years. Vitruvius will help you minimize the development time and create tough applications with just a few lines of code! Includes advanced Mathematics, Avateering, Video Recording, Face Tracking, and more.

Download Vitruvius

‘Til the next time, keep Kinecting!

Vangos Pterneas

Vangos Pterneas is a software engineer, book author, and award-winning Microsoft Most Valuable Professional (2014-2019). Since 2012, Vangos has been helping Fortune-500 companies and ambitious startups create demanding motion-tracking applications. He's obsessed with analyzing and modeling every aspect of human motion using AI and Maths. Vangos shares his passion by regularly publishing articles and open-source projects to help and inspire fellow developers.

13 Comments

  • mggsc says:

    Hi, I found many interpretations about joint orientation on MSDN forum. But thers is no definitely explainations. Some people says the joint orientation is relative to its parent. However some others think it is absolute. It makes me confused! And what is the orientation of X/Y/Z axis of the joint coordinate? Someone puts it as follows:
    Binormal (X) – perpendicular to bone and normal
    Bone direction (Y) – always matches skeleton
    Normal (Z) – joint roll, perpendicular to the bone
    The explanationis ambiguous too. I think nobody has clearly explaned how the orientation information works so far.

  • Karla Trejo says:

    Hello Vangos! Thank you for this great tutorial 🙂

    I have a question regarding the “Yaw” formulation:

    double value = 2.0 * (quaternion.W * quaternion.Y – quaternion.Z * quaternion.X);
    value = value > 1.0 ? 1.0 : value;
    value = value < -1.0 ? -1.0 : value;

    I understand the last two lines are to handle the "gimbal lock" singularity, but why don't the yaw, pitch and roll values change accordingly?
    The theoretical formulation state that the special case of [value≈1.0] can be solved as:

    roll= ±2 *atan *(quaternion.X / quaternion.W)
    yaw = ± Math.Pi / 2
    pitch = ± 0

    And also… now I'm noticing the values returning from each function are called "different" from what they actually are, like:

    public static double Pitch
    return roll

    public static double Yaw
    return pitch

    public static double Roll
    return yaw

    I know this doesn't affect the result as they are just names for variables, but it could be confusing in some way… (?)

    Hope you can resolve my doubts and sorry for any inconveniences this may cause!

    Best regards,
    Karla

  • Harry says:

    Hi, Vangos. I was able to get the Yaw, Roll, and Pitch of a joint using your JointOrientationExtensions.cs code.
    It seems to give each angles correctly, but I was wondering what exactly are the reference axes for these angles.
    Quaternion math is too complicated for me to understand, and based on the answers at Microsoft Community Forum, it is stated that
    – Bone direction(Y green) – always matches the skeleton.
    – Normal(Z blue) – joint roll, perpendicular to the bone
    – Binormal(X orange) – perpendicular to the bone and normal

    As the description above, if Z axis(normal) is perpendicular to the bone and X axis is perpendicular to the bone and normal, how am I supposed to find them since it’s not specified which direction out of infinite lines that are perpendicular to the bone direction? What exactly are the X, Y, and Z axis?
    Thank you.

  • eya says:

    Hi Vangos Pterneas, I am trying to calculate the neck and the trunk yow , roll and pitch but I am getting weird results example :
    >> neck roll = 169, neck twist -101
    >> trunk roll -150, trunk twist -105
    please help me if you have any idea

  • N/A says:

    Hi! I am trying to use this code for the sdk 1.8, I can’t seem to get it too work, it keeps saying body class does not exist in this context, i’ve tried replacing it with skeleton among other things but it still doesn’t work. I don’t know whether you will reply or not.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.