How do I convert from a rotation matrix to Heading, Pitch, and Roll?

4176
1
Jump to solution
09-17-2015 07:32 PM
meme2
by
New Contributor II

Starting with a 3D rotation matrix, how do I compute the Heading, Pitch, and Roll values that the various 3D symbols desire?

More Detail:

So, given a rotation matrix that looks like this:

[ M00, M01, M02 ]

[ M10, M11, M12 ]

[ M20, M21, M22 ]

And I'm trying to create a ConeMarkerSymbol.

First I tried this computation (source😞

Heading = Math.Atan2(M10, M00) * (180 / Math.PI)

Pitch = Math.Atan2(-M20, Math.Sqrt(M21 * M21 + M22 * M22)) * (180 / Math.PI)

Roll = Math.Atan2(M21, M22) * (180 / Math.PI)

But that didn't yield the correct results.  Then I tried this computation (source😞

Heading = Math.Atan2(M20, M21) * (180 / Math.PI)

Pitch = Math.Acos(M22) * (180 / Math.PI)

Roll = -Math.Atan2(M02, M12) * (180 / Math.PI)

But that didn't get it either.  What's the correct calculation?

Even More Details:

I'm doing all of my business logic using a state planar projection and then converting it to WGS84 to display it in a SceneView.  The GraphicsLayer is using a SurfacePlacement of Absolute and I'm able to place all of the objects correctly, but getting them oriented properly is a challenge.  I am able to render the objects in an DirectX window with the correct orientation, so I think the rotation matrix is correct,

I'm using C# and the .NET runtime SDK.  Thanks in advance!

0 Kudos
1 Solution

Accepted Solutions
meme2
by
New Contributor II

Calculation:

    public class TaitBryanAngles

    {

        public static TaitBryanAngles FromMatrix(Matrix3D matrix)

        {

            double psi, theta, phi;

            if (!(matrix.M02 == 1 || matrix.M02 == -1))

            {

                theta = -Math.Asin(matrix.M02);

                psi = Math.Atan2(matrix.M12, matrix.M22);

                phi = Math.Atan2(matrix.M01, matrix.M00);

            }

            else

            {

                phi = 0.0;

                if (matrix.M02 == -1)

                {

                    theta = Math.PI / 2;

                    psi = phi + Math.Atan2(matrix.M10, matrix.M20);

                }

                else

                {

                    theta = -Math.PI / 2;

                    psi = -phi + Math.Atan2(-matrix.M10, -matrix.M20);

                }

            }

            return new TaitBryanAngles(phi * 180 / Math.PI, psi * 180 / Math.PI, theta * 180 / Math.PI);

        }

        public TaitBryanAngles(double heading, double roll, double pitch)

        {

            Heading = heading;

            Roll = roll;

            Pitch = pitch;

        }

        public double Heading { get; }

        public double Roll { get; }

        public double Pitch { get; }

    }

Explanation:

The values that are expected are the Tait-Bryan XYZ angles.  In order to derive these angles, I followed this paper here:

http://staff.city.ac.uk/~sbbh653/publications/euler.pdf

In that paper, they derive the ZYX angles.  However, the API expects the XYZ angles.  So, I followed the same procedure with a different order of the rotation matrix multiplication and arrived at the answer given above.  Hopefully this is helpful for someone else!

View solution in original post

1 Reply
meme2
by
New Contributor II

Calculation:

    public class TaitBryanAngles

    {

        public static TaitBryanAngles FromMatrix(Matrix3D matrix)

        {

            double psi, theta, phi;

            if (!(matrix.M02 == 1 || matrix.M02 == -1))

            {

                theta = -Math.Asin(matrix.M02);

                psi = Math.Atan2(matrix.M12, matrix.M22);

                phi = Math.Atan2(matrix.M01, matrix.M00);

            }

            else

            {

                phi = 0.0;

                if (matrix.M02 == -1)

                {

                    theta = Math.PI / 2;

                    psi = phi + Math.Atan2(matrix.M10, matrix.M20);

                }

                else

                {

                    theta = -Math.PI / 2;

                    psi = -phi + Math.Atan2(-matrix.M10, -matrix.M20);

                }

            }

            return new TaitBryanAngles(phi * 180 / Math.PI, psi * 180 / Math.PI, theta * 180 / Math.PI);

        }

        public TaitBryanAngles(double heading, double roll, double pitch)

        {

            Heading = heading;

            Roll = roll;

            Pitch = pitch;

        }

        public double Heading { get; }

        public double Roll { get; }

        public double Pitch { get; }

    }

Explanation:

The values that are expected are the Tait-Bryan XYZ angles.  In order to derive these angles, I followed this paper here:

http://staff.city.ac.uk/~sbbh653/publications/euler.pdf

In that paper, they derive the ZYX angles.  However, the API expects the XYZ angles.  So, I followed the same procedure with a different order of the rotation matrix multiplication and arrived at the answer given above.  Hopefully this is helpful for someone else!