-
-
Notifications
You must be signed in to change notification settings - Fork 56.2k
Add to/from Euler Angles in quaternion #19098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
6e9de09
to
4785bea
Compare
4785bea
to
77d94e1
Compare
{ | ||
Quat<T> q1 = Quat<T>::createFromXRot(angles(0) / 2.); | ||
Quat<T> q2 = Quat<T>::createFromYRot(angles(1) / 2.); | ||
Quat<T> q3 = Quat<T>::createFromZRot(angles(2) / 2.); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/ 2.
* 0.5f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! Thanks for your reviewing code for us.
Quat<T> q1 = Quat<T>::createFromXRot(angles(0) / 2.); | ||
Quat<T> q2 = Quat<T>::createFromYRot(angles(1) / 2.); | ||
Quat<T> q3 = Quat<T>::createFromZRot(angles(2) / 2.); | ||
q = q1 * q2 * q3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets reduce amount of generated binary code.
Make private function (detail
namespace should be fine)
template <typename T>
Quat<T> createQuatFromRot(int axis, T angle)
{
if (i == 0)
return Quat<T>::createFromXRot(angle * 0.5f);
if (i == 1)
return Quat<T>::createFromXRot(angle * 0.5f);
if (i == 2)
return Quat<T>::createFromXRot(angle * 0.5f);
CV_Assert(0);
}
Fill axis1,axis2,axis3 (preferable from static const int rotationAxis[][3] = { { i1, i2, i3 }, { i1, i2, i3}, ... };
table):
int axis1, axis2, axis3; // values are 0/1/2 for X/Y/Z
enum EulerAnglesType
{
...
#ifndef CV_DOXYGEN
EULER_ANGLES_MAX_VALUE
#endif
};
+ static assert for table size using EULER_ANGLES_MAX_VALUE
+ runtime assert: eulerAnglesType < EULER_ANGLES_MAX_VALUE
Finally, use this common code in epilog:
Quat<T> q1 = Quat<T>::createQuatFromRot(axis1, angles(0));
Quat<T> q2 = Quat<T>::createQuatFromRot(axis2, angles(1));
Quat<T> q3 = Quat<T>::createQuatFromRot(axis3, angles(2));
q = q1 * q2 * q3;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I got it and I will update the implementation of toEulerAngle()
in the same way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alalek The table size of rotationAxis is fixed (24,4). Is it necessary to add static_assert
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Static assert is a compilation guard if someone would change/update corresponding enum values.
if (abs(R(0, 2) - 1) < CV_QUAT_CONVERT_THRESHOLD) | ||
{ | ||
std::cout << "WARNING: Gimbal Lock will occur. Euler angles are non-unique." << std::endl; | ||
std::cout << "For intrinsic rotations, we set the third angle to zero, and for external rotation, we set the first angle to zero." << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::cout
Avoid that. There is CV_LOG_WARNING()
. However messages probably should be reduced to INFO level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
* | ||
* The three elemental rotations may be [extrinsic](https://en.wikipedia.org/wiki/Euler_angles#Definition_by_extrinsic_rotations) | ||
* (rotations about the axes *xyz* of the original coordinate system, which is assumed to remain motionless), | ||
* or [intrinsic](https://en.wikipedia.org/wiki/Euler_angles#Definition_by_intrinsic_rotations)(rotations about the axes of the rotating coordinate system *XYZ*, solidary with the moving body, which changes its orientation after each elemental rotation). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix the links to paragraphs in that wiki article
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we will update doc.
* Using intrinsic rotations with Euler angles type XYZ as an example, | ||
* \f$\theta_1 \f$, \f$\theta_2 \f$, \f$\theta_3 \f$ are three angles for Euler angles, the rotation matrix R can be calculated by:\f[R =X(\theta_1)Y(\theta_2)Z(\theta_3) | ||
* ={\begin{bmatrix}\cos\theta_{2}\cos\theta_{3}&-\cos\theta_{2}\sin\theta_{3}&\sin\theta_{2}\\\cos\theta_{1}\sin\theta_{3}+\cos\theta_{3}\sin\theta_{1}\sin\theta_{2}&\cos\theta_{1}\cos\theta_{3}-\sin\theta_{1}\sin\theta_{2}\sin\theta_{3}&-\cos\theta_{2}\sin\theta_{1}\\\sin\theta_{1}\sin\theta_{3}-\cos\theta_{1}\cos\theta_{3}\sin\theta_{2}&\cos\theta_{3}\sin\theta_{1}+\cos\theta_{1}\sin\theta_{2}\sin\theta_{3}&\cos\theta_{1}\cos_{2}\end{bmatrix}}\f] | ||
* Rotation matrix M and R are equal. As long as \f$ s_{2} \neq 1 \f$, by comparing each element of two matrices ,the solution is\f$\begin{cases} \theta_1 = \arctan2(-m_{23},m_{33})\\\theta_2 = arcsin(m_{13}) \\\theta_3 = \arctan2(-m_{12},m_{11}) \end{cases}\f$. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please clarify what is s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thanks! s_2
mean sin\theta_2
, I will fix the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add commits with code optimizations from here: https://github.com/alalek/opencv/commits/pr19098_r
@@ -845,8 +953,8 @@ class Quat | |||
/** | |||
* @brief transform a quaternion to a 4x4 rotation matrix. | |||
* @param assumeUnit if QUAT_ASSUME_UNIT, this quaternion assume to be a unit quaternion and | |||
* this function will save some computations. Otherwise, this function will normalized this | |||
* quaternion at first then to do the transformation. | |||
* this function will save some computations. Otherwise, this function will normaliz this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thanks for your commits! We have added these codes and removed the typo.
namespace | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace detail {
should be better
namespace | ||
{ | ||
template <typename T> | ||
Quat<T> createFromAxisRot(int axis, const T theta) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It make sense to add static
to keep this local:
template <typename T> static
Quat<T> createFromAxisRot(int axis, const T theta)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I got it!
Quat<T> q3 = createFromAxisRot(rotationAxis[eulerAnglesType][2], angles(2)); | ||
return q1 * q2 * q3; | ||
} | ||
else if (rotationAxis[eulerAnglesType][3]){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}
else // (rotationAxis[eulerAnglesType][3])
{
and remove CV_Assert(0)
.
{R(2, 2), 0, R(1, 0), R(1, 1), CV_PI, R(1, 0), R(0, 0),R(2, 0), R(2, 1), R(2, 2),R(0, 2), -R(1, 2), 1}, //EXT_ZXZ | ||
{R(2, 2), 0, R(1, 0), R(0, 0), CV_PI, R(1, 0), R(0, 0),R(2, 1), -R(2, 0), R(2, 2),R(1, 2), R(0, 2), 1} //EXT_ZYZ | ||
}; | ||
if(!rotationR[eulerAnglesType][12]){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before this if
build dynamic table:
T rotationR[12]
through decoding in a loop using the indexes above.
And apply replacement rotationR[eulerAnglesType]
=> rotationR
for code below (it should not have large changes).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! We really appreciate your help.
* For Proper Euler angles,the valid range of \f$\theta_2\f$ is in [0, π]. The solutions of Euler angles are unique in condition of \f$ \theta_2 \in (0, π)\f$ . If \f$\theta_2 =0 \f$ or \f$\theta_2 =π \f$, | ||
* there are infinite solutions and gimbal lock will occur. | ||
*/ | ||
enum EulerAnglesType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to avoid placing enums in template classes.
I see these options:
- base non-template class with such constants (
QuatEnum
?) - external enum with proper prefixes (like
QUAT_ASSUME_NOT_UNIT
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we will create class QuatEnum
to place enum EulerAnglesType
.
Quatd qEuler1 = Quatd::createFromEulerAngles(test_angle, Quatd::INT_XYZ); | ||
Quatd qEuler2 = Quatd::createFromEulerAngles(test_angle, Quatd::INT_XZY); | ||
Quatd qEuler3 = Quatd::createFromEulerAngles(test_angle, Quatd::INT_XZX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using arrays and for loops to process/check values. Like qEuler[24]
This should reduce amount of code.
Use error messages to help with problem investigations. See proposed commits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we use for loops and array qEuler[24]
in test codes to reduce duplicate code.
d0bbc0a
to
3699fa2
Compare
|
||
TEST_F(QuatTest, EulerAngles){ | ||
Vec3d test_angle={0.523598,0.78539,1.04719}; | ||
Quatd qEuler0 = Quatd(0, 0, 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not transform these huge lists (here and below) to loops?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thanks, I solved this problem by using the for loop method and array qEuler
.
0851141
to
22a0dd4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good progress!
…test_quaternion.cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me 👍
* add to/from Euler Angles * restruct codes * quat: optimize implementation * cleanup debug code * correct spelling errors * create QuatEnum for enum EulerAnglesType * use for loop for test_quaternion * drop template from isIntAngleType & add minimal error information in test_quaternion.cpp Co-authored-by: ShanChenqi <shanchenqi@huawei.com> Co-authored-by: Alexander Alekhin <alexander.a.alekhin@gmail.com>
We finally support all types of Euler angles to/from the quaternion.
Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
Patch to opencv_extra has the same branch name.