Bug 609 - Euler Angles not Angle reversible, documentation example lacking, possible gimble lock issue resolveable
Euler Angles not Angle reversible, documentation example lacking, possible gi...
Status: RESOLVED FIXED
Product: Eigen
Classification: Unclassified
Component: Geometry
3.2
All All
: Normal Unknown
Assigned To: Nobody
:
Depends on:
Blocks: 3.3
  Show dependency treegraph
 
Reported: 2013-06-05 17:47 UTC by johnmichael.fischer
Modified: 2013-11-29 19:48 UTC (History)
5 users (show)



Attachments
Avoid if statement and improve consistency of eulerAngles method (6.80 KB, patch)
2013-06-06 22:44 UTC, Gael Guennebaud
no flags Details | Diff
self-contained test case (2.94 KB, application/octet-stream)
2013-06-27 18:44 UTC, Gael Guennebaud
no flags Details

Description johnmichael.fischer 2013-06-05 17:47:45 UTC
In testing the Eigen/EulerAngles implementation I have shown that I create a matrix and go
Matrix > Angles > Matrix
but I cannot successfully go
Angles > Matrix > Angles for any matrix other than the identity matrix.

Example code
// Hand-Craft, ANG > MAT > ANG
// first quad
inputAngles << 0, 0, M_PI/4;
outputMatrix.block<3,3>(0, 0) = (Eigen::AngleAxisf(inputAngles[0], Eigen::Vector3f::UnitZ()) 
                              * Eigen::AngleAxisf(inputAngles[1], Eigen::Vector3f::UnitX()) 
                              * Eigen::AngleAxisf(inputAngles[2], Eigen::Vector3f::UnitZ())).toRotationMatrix();
outputAngles = inputMat.block<3,3>(0,0).eulerAngles(2, 0, 2); // follows Z-X-Z

with output
inputAngles == [0.78539819, 0.78539819, 0.78539819]
pi/4, pi/4, pi/4
outputAngles == [-2.3561945, -0.78539824, -2.3561945] 
-3pi/4, -pi/4, -3pi/4

See this thread for more (http://forum.kde.org/viewtopic.php?f=74&t=111436&p=266230#p266230)

Obviously Euler angles have lots of different conventions -- if the Eigen documentation gave a very thorough walk through for one convention it would be very helpful. Also what the expected domain/range of the three euler angles is would be handy without having to dig through the source code.


Finally it is posited here (https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles.pdf) that according to the Graphics Gems IV method (which, according to the Eigen source code is in use) it is possible to avoid floating point instability by slightly modifying the generation method of the third coefficient
Comment 1 johnmichael.fischer 2013-06-05 17:51:53 UTC
Here is an updated code example including variable definitions.  The original code example got a little mushed in copy and paste.

Eigen::Matrix4f inputMat;
Eigen::Matrix4f outputMat;
Eigen::Array3d inputAngles;
Eigen::Array3d outputAngles;

inputMat = outputMat = Eigen::Matrix4f::Identity();
// Hand-Craft, ANG > MAT > ANG
// first quad
inputAngles << M_PI/4, M_PI/4, M_PI/4;
outputMatrix.block<3,3>(0, 0) = (Eigen::AngleAxisf(inputAngles[0],
Eigen::Vector3f::UnitZ()) 
                              * Eigen::AngleAxisf(inputAngles[1],
Eigen::Vector3f::UnitX()) 
                              * Eigen::AngleAxisf(inputAngles[2],
Eigen::Vector3f::UnitZ())).toRotationMatrix();
outputAngles = outputMatrix.block<3,3>(0,0).eulerAngles(2, 0, 2); // follows Z-X-Z
Comment 2 Gael Guennebaud 2013-06-06 22:44:15 UTC
Created attachment 342 [details]
Avoid if statement and improve consistency of eulerAngles method

Here is a patch implementing the approach of linked pdf. The return angles are guaranteed to be in the range [0:pi]x[0:pi]x[-pi:pi]. Implementation-wise this is much simpler and faster this way than starting with a wider [-pi:pi] range.
Comment 3 johnmichael.fischer 2013-06-09 19:46:48 UTC
This looks great.  Thanks a lot for your time on this wonderful project.
Comment 4 Gael Guennebaud 2013-06-09 23:16:45 UTC
https://bitbucket.org/eigen/eigen/commits/b12526cb05c9/
Changeset:   b12526cb05c9
User:        ggael
Date:        2013-06-09 23:14:45
Summary:     Fix bug 609: avoid if statement and improve consistency of eulerAngles method
Comment 5 johnmichael.fischer 2013-06-27 17:36:02 UTC
I need to re-open this.  Not having success with all test cases.

All of the hand constructed matricies are checked with the following code (written here for brevity).  Matricies are hand crafted for a right-hand system, although the determinant is still checked for safety.
The following test cases do not work and I've follow-copies the input/output for inspection.


Eigen::Matrix4f inputMat, outputMat;
Eigen::Array3d inputAngles;
inputMat  = Eigen::Matrix4f::Identity();
outputMat = inputMat;

// MAT > ANG > MAT
inputMat.block<3,1>(0,0) << 0,1,0;
inputMat.block<3,1>(0,1) << -1,0,0;
inputMat.block<3,1>(0,2) << 0,0,1;
if( inputMat.determinant() != 1 )
{
   // flip from LH to RH
   inputMat.block<3,1>(0,2) *= -1;
}
outputMat = inputMat;
inputAngles = inputMat.block<3,3>(0,0).cast<double>().eulerAngles(2, 0, 2); // follows Z-X-Z
outputMat.block<3,3>(0, 0) = (Eigen::AngleAxisd(inputAngles[0], Eigen::Vector3d::UnitZ()) 
				* Eigen::AngleAxisd(inputAngles[1], Eigen::Vector3d::UnitX()) 
	        		* Eigen::AngleAxisd(inputAngles[2], Eigen::Vector3d::UnitZ())).toRotationMatrix().cast<float>();
// Gives
 0 -1  0  0
 1  0  0  0
 0  0  1  0
 0  0  0  1
-0.846852 -0.531829         0         0
 0.107965 -0.171917  0.979177         0
-0.520755  0.829218  0.203007         0
        0         0         0         1

// Hand-Craft, MAT > ANG > MAT
inputMat.block<3,1>(0,0) << 0,-1,0;
inputMat.block<3,1>(0,1) << 1,0,0;
inputMat.block<3,1>(0,2) << 0,0,1;
// Gives
 0 1 0 0
-1 0 0 0
 0 0 1 0
 0 0 0 1
 2.22045e-016  1.57009e-016            -1             0
 1.57009e-016             1  1.57009e-016             0
            1 -1.57009e-016  2.22045e-016             0
            0             0             0             1

// Hand-Craft, MAT > ANG > MAT
inputMat.block<3,1>(0,0) << 0,-1,0;
inputMat.block<3,1>(0,1) << -1,0,0;
inputMat.block<3,1>(0,2) << 0,0,-1;
// Gives
 0 -1  0  0
-1  0  0  0
 0  0 -1  0
 0  0  0  1
-2.22045e-016 -1.57009e-016             1             0
 1.57009e-016            -1 -1.57009e-016             0
            1  1.57009e-016  2.22045e-016             0
            0             0             0             1


Here are two examples in reverse, checked with the following code, which also aren't working
inputMat = outputMat = Eigen::Matrix4f::Identity();
// Hand-Craft, ANG > MAT > ANG
// first quad
inputAngles << 0, 0, 0;
outputMat.block<3,3>(0, 0) = (Eigen::AngleAxisd(inputAngles[0], Eigen::Vector3d::UnitZ()) 
				* Eigen::AngleAxisd(inputAngles[1], Eigen::Vector3d::UnitX()) 
	        		* Eigen::AngleAxisd(inputAngles[2], Eigen::Vector3d::UnitZ())).toRotationMatrix().cast<float>();
outputAngles = outputMat.block<3,3>(0,0).cast<double>().eulerAngles(2, 0, 2); // follows Z-X-Z
// Gives
0
0
0
1.5708
1.5708
1.5708

// Hand-Craft, ANG > MAT > ANG
inputAngles << M_PI/4, 0, 0;
// Gives
0.785398
0
0
1.5708
1.5708
1.5708
Comment 6 Gael Guennebaud 2013-06-27 18:44:02 UTC
Created attachment 360 [details]
self-contained test case

Works for me with the attached self-contained example. It works both with double and float.

Remark that with the Z-X-Z convention, if the second angle (around X) is zero then the Euler angles are not uniquely defined. As I already explained, we always pick the one with the first angle equal to zero.
Comment 7 johnmichael.fischer 2013-07-03 18:03:54 UTC
I got everything working.  Thanks again.
Comment 8 johnmichael.fischer 2013-11-29 18:07:17 UTC
(In reply to comment #2)
> Created attachment 342 [details] [review]
> Avoid if statement and improve consistency of eulerAngles method
> 
> Here is a patch implementing the approach of linked pdf. The return angles are
> guaranteed to be in the range [0:pi]x[0:pi]x[-pi:pi]. Implementation-wise this
> is much simpler and faster this way than starting with a wider [-pi:pi] range.

Based on a specific example and studying the math again, it appears the second angle can be -pi:pi, not 0:pi as listed now in the docs.

EulerAngle.h lines 60 and 65 seem to allow for values 0:-pi and 0:pi respectively.

For example, this matrix evaluates to a second angle which is slightly less than -pi/2 (using Z-X-Z convention)

-0.0619354  -0.018203  -0.997914    0
  -0.99757 -0.0308364  0.0624764    0
-0.0319091   0.999359 -0.0162488    0
         0          0          0    1

Is this a bug in the docs or the angle code?

Thanks
Comment 9 Christoph Hertzberg 2013-11-29 19:48:38 UTC
This seems to have been a bug in the docs. I fixed the doc and extended the unit test (in default and 3.2):
http://bitbucket.org/eigen/eigen/commits/42e0115
http://bitbucket.org/eigen/eigen/commits/4a867dd

Note You need to log in before you can comment on or make changes to this bug.