EulerAngles.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2015 Tal Hadad <tal_hd@hotmail.com>
  5. //
  6. // This Source Code Form is subject to the terms of the Mozilla
  7. // Public License v. 2.0. If a copy of the MPL was not distributed
  8. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. #include "main.h"
  10. #include <unsupported/Eigen/EulerAngles>
  11. using namespace Eigen;
  12. // Unfortunately, we need to specialize it in order to work. (We could add it in main.h test framework)
  13. template <typename Scalar, class System>
  14. bool verifyIsApprox(const Eigen::EulerAngles<Scalar, System>& a, const Eigen::EulerAngles<Scalar, System>& b)
  15. {
  16. return verifyIsApprox(a.angles(), b.angles());
  17. }
  18. // Verify that x is in the approxed range [a, b]
  19. #define VERIFY_APPROXED_RANGE(a, x, b) \
  20. do { \
  21. VERIFY_IS_APPROX_OR_LESS_THAN(a, x); \
  22. VERIFY_IS_APPROX_OR_LESS_THAN(x, b); \
  23. } while(0)
  24. const char X = EULER_X;
  25. const char Y = EULER_Y;
  26. const char Z = EULER_Z;
  27. template<typename Scalar, class EulerSystem>
  28. void verify_euler(const EulerAngles<Scalar, EulerSystem>& e)
  29. {
  30. typedef EulerAngles<Scalar, EulerSystem> EulerAnglesType;
  31. typedef Matrix<Scalar,3,3> Matrix3;
  32. typedef Matrix<Scalar,3,1> Vector3;
  33. typedef Quaternion<Scalar> QuaternionType;
  34. typedef AngleAxis<Scalar> AngleAxisType;
  35. const Scalar ONE = Scalar(1);
  36. const Scalar HALF_PI = Scalar(EIGEN_PI / 2);
  37. const Scalar PI = Scalar(EIGEN_PI);
  38. // It's very important calc the acceptable precision depending on the distance from the pole.
  39. const Scalar longitudeRadius = std::abs(
  40. EulerSystem::IsTaitBryan ?
  41. std::cos(e.beta()) :
  42. std::sin(e.beta())
  43. );
  44. Scalar precision = test_precision<Scalar>() / longitudeRadius;
  45. Scalar betaRangeStart, betaRangeEnd;
  46. if (EulerSystem::IsTaitBryan)
  47. {
  48. betaRangeStart = -HALF_PI;
  49. betaRangeEnd = HALF_PI;
  50. }
  51. else
  52. {
  53. if (!EulerSystem::IsBetaOpposite)
  54. {
  55. betaRangeStart = 0;
  56. betaRangeEnd = PI;
  57. }
  58. else
  59. {
  60. betaRangeStart = -PI;
  61. betaRangeEnd = 0;
  62. }
  63. }
  64. const Vector3 I_ = EulerAnglesType::AlphaAxisVector();
  65. const Vector3 J_ = EulerAnglesType::BetaAxisVector();
  66. const Vector3 K_ = EulerAnglesType::GammaAxisVector();
  67. // Is approx checks
  68. VERIFY(e.isApprox(e));
  69. VERIFY_IS_APPROX(e, e);
  70. VERIFY_IS_NOT_APPROX(e, EulerAnglesType(e.alpha() + ONE, e.beta() + ONE, e.gamma() + ONE));
  71. const Matrix3 m(e);
  72. VERIFY_IS_APPROX(Scalar(m.determinant()), ONE);
  73. EulerAnglesType ebis(m);
  74. // When no roll(acting like polar representation), we have the best precision.
  75. // One of those cases is when the Euler angles are on the pole, and because it's singular case,
  76. // the computation returns no roll.
  77. if (ebis.beta() == 0)
  78. precision = test_precision<Scalar>();
  79. // Check that eabis in range
  80. VERIFY_APPROXED_RANGE(-PI, ebis.alpha(), PI);
  81. VERIFY_APPROXED_RANGE(betaRangeStart, ebis.beta(), betaRangeEnd);
  82. VERIFY_APPROXED_RANGE(-PI, ebis.gamma(), PI);
  83. const Matrix3 mbis(AngleAxisType(ebis.alpha(), I_) * AngleAxisType(ebis.beta(), J_) * AngleAxisType(ebis.gamma(), K_));
  84. VERIFY_IS_APPROX(Scalar(mbis.determinant()), ONE);
  85. VERIFY_IS_APPROX(mbis, ebis.toRotationMatrix());
  86. /*std::cout << "===================\n" <<
  87. "e: " << e << std::endl <<
  88. "eabis: " << eabis.transpose() << std::endl <<
  89. "m: " << m << std::endl <<
  90. "mbis: " << mbis << std::endl <<
  91. "X: " << (m * Vector3::UnitX()).transpose() << std::endl <<
  92. "X: " << (mbis * Vector3::UnitX()).transpose() << std::endl;*/
  93. VERIFY(m.isApprox(mbis, precision));
  94. // Test if ea and eabis are the same
  95. // Need to check both singular and non-singular cases
  96. // There are two singular cases.
  97. // 1. When I==K and sin(ea(1)) == 0
  98. // 2. When I!=K and cos(ea(1)) == 0
  99. // TODO: Make this test work well, and use range saturation function.
  100. /*// If I==K, and ea[1]==0, then there no unique solution.
  101. // The remark apply in the case where I!=K, and |ea[1]| is close to +-pi/2.
  102. if( (i!=k || ea[1]!=0) && (i==k || !internal::isApprox(abs(ea[1]),Scalar(EIGEN_PI/2),test_precision<Scalar>())) )
  103. VERIFY_IS_APPROX(ea, eabis);*/
  104. // Quaternions
  105. const QuaternionType q(e);
  106. ebis = q;
  107. const QuaternionType qbis(ebis);
  108. VERIFY(internal::isApprox<Scalar>(std::abs(q.dot(qbis)), ONE, precision));
  109. //VERIFY_IS_APPROX(eabis, eabis2);// Verify that the euler angles are still the same
  110. // A suggestion for simple product test when will be supported.
  111. /*EulerAnglesType e2(PI/2, PI/2, PI/2);
  112. Matrix3 m2(e2);
  113. VERIFY_IS_APPROX(e*e2, m*m2);*/
  114. }
  115. template<signed char A, signed char B, signed char C, typename Scalar>
  116. void verify_euler_vec(const Matrix<Scalar,3,1>& ea)
  117. {
  118. verify_euler(EulerAngles<Scalar, EulerSystem<A, B, C> >(ea[0], ea[1], ea[2]));
  119. }
  120. template<signed char A, signed char B, signed char C, typename Scalar>
  121. void verify_euler_all_neg(const Matrix<Scalar,3,1>& ea)
  122. {
  123. verify_euler_vec<+A,+B,+C>(ea);
  124. verify_euler_vec<+A,+B,-C>(ea);
  125. verify_euler_vec<+A,-B,+C>(ea);
  126. verify_euler_vec<+A,-B,-C>(ea);
  127. verify_euler_vec<-A,+B,+C>(ea);
  128. verify_euler_vec<-A,+B,-C>(ea);
  129. verify_euler_vec<-A,-B,+C>(ea);
  130. verify_euler_vec<-A,-B,-C>(ea);
  131. }
  132. template<typename Scalar> void check_all_var(const Matrix<Scalar,3,1>& ea)
  133. {
  134. verify_euler_all_neg<X,Y,Z>(ea);
  135. verify_euler_all_neg<X,Y,X>(ea);
  136. verify_euler_all_neg<X,Z,Y>(ea);
  137. verify_euler_all_neg<X,Z,X>(ea);
  138. verify_euler_all_neg<Y,Z,X>(ea);
  139. verify_euler_all_neg<Y,Z,Y>(ea);
  140. verify_euler_all_neg<Y,X,Z>(ea);
  141. verify_euler_all_neg<Y,X,Y>(ea);
  142. verify_euler_all_neg<Z,X,Y>(ea);
  143. verify_euler_all_neg<Z,X,Z>(ea);
  144. verify_euler_all_neg<Z,Y,X>(ea);
  145. verify_euler_all_neg<Z,Y,Z>(ea);
  146. }
  147. template<typename Scalar> void check_singular_cases(const Scalar& singularBeta)
  148. {
  149. typedef Matrix<Scalar,3,1> Vector3;
  150. const Scalar PI = Scalar(EIGEN_PI);
  151. for (Scalar epsilon = NumTraits<Scalar>::epsilon(); epsilon < 1; epsilon *= Scalar(1.2))
  152. {
  153. check_all_var(Vector3(PI/4, singularBeta, PI/3));
  154. check_all_var(Vector3(PI/4, singularBeta - epsilon, PI/3));
  155. check_all_var(Vector3(PI/4, singularBeta - Scalar(1.5)*epsilon, PI/3));
  156. check_all_var(Vector3(PI/4, singularBeta - 2*epsilon, PI/3));
  157. check_all_var(Vector3(PI*Scalar(0.8), singularBeta - epsilon, Scalar(0.9)*PI));
  158. check_all_var(Vector3(PI*Scalar(-0.9), singularBeta + epsilon, PI*Scalar(0.3)));
  159. check_all_var(Vector3(PI*Scalar(-0.6), singularBeta + Scalar(1.5)*epsilon, PI*Scalar(0.3)));
  160. check_all_var(Vector3(PI*Scalar(-0.5), singularBeta + 2*epsilon, PI*Scalar(0.4)));
  161. check_all_var(Vector3(PI*Scalar(0.9), singularBeta + epsilon, Scalar(0.8)*PI));
  162. }
  163. // This one for sanity, it had a problem with near pole cases in float scalar.
  164. check_all_var(Vector3(PI*Scalar(0.8), singularBeta - Scalar(1E-6), Scalar(0.9)*PI));
  165. }
  166. template<typename Scalar> void eulerangles_manual()
  167. {
  168. typedef Matrix<Scalar,3,1> Vector3;
  169. typedef Matrix<Scalar,Dynamic,1> VectorX;
  170. const Vector3 Zero = Vector3::Zero();
  171. const Scalar PI = Scalar(EIGEN_PI);
  172. check_all_var(Zero);
  173. // singular cases
  174. check_singular_cases(PI/2);
  175. check_singular_cases(-PI/2);
  176. check_singular_cases(Scalar(0));
  177. check_singular_cases(Scalar(-0));
  178. check_singular_cases(PI);
  179. check_singular_cases(-PI);
  180. // non-singular cases
  181. VectorX alpha = VectorX::LinSpaced(20, Scalar(-0.99) * PI, PI);
  182. VectorX beta = VectorX::LinSpaced(20, Scalar(-0.49) * PI, Scalar(0.49) * PI);
  183. VectorX gamma = VectorX::LinSpaced(20, Scalar(-0.99) * PI, PI);
  184. for (int i = 0; i < alpha.size(); ++i) {
  185. for (int j = 0; j < beta.size(); ++j) {
  186. for (int k = 0; k < gamma.size(); ++k) {
  187. check_all_var(Vector3(alpha(i), beta(j), gamma(k)));
  188. }
  189. }
  190. }
  191. }
  192. template<typename Scalar> void eulerangles_rand()
  193. {
  194. typedef Matrix<Scalar,3,3> Matrix3;
  195. typedef Matrix<Scalar,3,1> Vector3;
  196. typedef Array<Scalar,3,1> Array3;
  197. typedef Quaternion<Scalar> Quaternionx;
  198. typedef AngleAxis<Scalar> AngleAxisType;
  199. Scalar a = internal::random<Scalar>(-Scalar(EIGEN_PI), Scalar(EIGEN_PI));
  200. Quaternionx q1;
  201. q1 = AngleAxisType(a, Vector3::Random().normalized());
  202. Matrix3 m;
  203. m = q1;
  204. Vector3 ea = m.eulerAngles(0,1,2);
  205. check_all_var(ea);
  206. ea = m.eulerAngles(0,1,0);
  207. check_all_var(ea);
  208. // Check with purely random Quaternion:
  209. q1.coeffs() = Quaternionx::Coefficients::Random().normalized();
  210. m = q1;
  211. ea = m.eulerAngles(0,1,2);
  212. check_all_var(ea);
  213. ea = m.eulerAngles(0,1,0);
  214. check_all_var(ea);
  215. // Check with random angles in range [0:pi]x[-pi:pi]x[-pi:pi].
  216. ea = (Array3::Random() + Array3(1,0,0))*Scalar(EIGEN_PI)*Array3(0.5,1,1);
  217. check_all_var(ea);
  218. ea[2] = ea[0] = internal::random<Scalar>(0,Scalar(EIGEN_PI));
  219. check_all_var(ea);
  220. ea[0] = ea[1] = internal::random<Scalar>(0,Scalar(EIGEN_PI));
  221. check_all_var(ea);
  222. ea[1] = 0;
  223. check_all_var(ea);
  224. ea.head(2).setZero();
  225. check_all_var(ea);
  226. ea.setZero();
  227. check_all_var(ea);
  228. }
  229. EIGEN_DECLARE_TEST(EulerAngles)
  230. {
  231. // Simple cast test
  232. EulerAnglesXYZd onesEd(1, 1, 1);
  233. EulerAnglesXYZf onesEf = onesEd.cast<float>();
  234. VERIFY_IS_APPROX(onesEd, onesEf.cast<double>());
  235. // Simple Construction from Vector3 test
  236. VERIFY_IS_APPROX(onesEd, EulerAnglesXYZd(Vector3d::Ones()));
  237. CALL_SUBTEST_1( eulerangles_manual<float>() );
  238. CALL_SUBTEST_2( eulerangles_manual<double>() );
  239. for(int i = 0; i < g_repeat; i++) {
  240. CALL_SUBTEST_3( eulerangles_rand<float>() );
  241. CALL_SUBTEST_4( eulerangles_rand<double>() );
  242. }
  243. // TODO: Add tests for auto diff
  244. // TODO: Add tests for complex numbers
  245. }