EAIK 1.1.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {eaik-1.1.0 → eaik-1.2.0}/CPP/external/ik-geo/cpp/subproblems/sp.cpp +5 -5
  2. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/EAIK.cpp +19 -4
  3. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/EAIK.h +1 -1
  4. eaik-1.2.0/CPP/src/IK/1R_IK.cpp +48 -0
  5. eaik-1.2.0/CPP/src/IK/2R_IK.cpp +89 -0
  6. eaik-1.2.0/CPP/src/IK/4R_IK.cpp +391 -0
  7. eaik-1.2.0/CPP/src/IK/5R_IK.cpp +714 -0
  8. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/IK/6R_IK.cpp +63 -2
  9. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/IK/CMakeLists.txt +1 -1
  10. eaik-1.2.0/CPP/src/IK/IKS.h +218 -0
  11. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/IK/utils/kinematic_utils.h +1 -0
  12. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/utils/kinematic_remodeling.cpp +20 -20
  13. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/utils/kinematic_remodeling.h +1 -1
  14. eaik-1.2.0/LICENSE +28 -0
  15. eaik-1.2.0/PKG-INFO +155 -0
  16. eaik-1.2.0/README.md +132 -0
  17. {eaik-1.1.0 → eaik-1.2.0}/pyproject.toml +6 -4
  18. {eaik-1.1.0 → eaik-1.2.0}/setup.py +5 -2
  19. eaik-1.2.0/src/EAIK.egg-info/PKG-INFO +155 -0
  20. {eaik-1.1.0 → eaik-1.2.0}/src/EAIK.egg-info/SOURCES.txt +6 -0
  21. eaik-1.2.0/src/eaik/IK_HP.py +24 -0
  22. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/IK_Robot.py +10 -10
  23. eaik-1.2.0/src/eaik/pybindings/__init__.py +3 -0
  24. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/pybindings/eaik_pybindings.cpp +5 -5
  25. eaik-1.1.0/CPP/src/IK/IKS.h +0 -101
  26. eaik-1.1.0/LICENSE +0 -674
  27. eaik-1.1.0/PKG-INFO +0 -811
  28. eaik-1.1.0/README.md +0 -116
  29. eaik-1.1.0/src/EAIK.egg-info/PKG-INFO +0 -811
  30. {eaik-1.1.0 → eaik-1.2.0}/CPP/external/ik-geo/cpp/subproblems/CMakeLists.txt +0 -0
  31. {eaik-1.1.0 → eaik-1.2.0}/CPP/external/ik-geo/cpp/subproblems/sp.h +0 -0
  32. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/CMakeLists.txt +0 -0
  33. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/IK/3R_IK.cpp +0 -0
  34. {eaik-1.1.0 → eaik-1.2.0}/CPP/src/IK/utils/kinematic_utils.cpp +0 -0
  35. {eaik-1.1.0 → eaik-1.2.0}/MANIFEST.in +0 -0
  36. {eaik-1.1.0 → eaik-1.2.0}/setup.cfg +0 -0
  37. {eaik-1.1.0 → eaik-1.2.0}/src/EAIK.egg-info/dependency_links.txt +0 -0
  38. {eaik-1.1.0 → eaik-1.2.0}/src/EAIK.egg-info/requires.txt +0 -0
  39. {eaik-1.1.0 → eaik-1.2.0}/src/EAIK.egg-info/top_level.txt +0 -0
  40. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/IK_DH.py +0 -0
  41. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/IK_Homogeneous.py +0 -0
  42. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/IK_URDF.py +0 -0
  43. {eaik-1.1.0 → eaik-1.2.0}/src/eaik/__init__.py +0 -0
@@ -546,7 +546,7 @@ namespace IKS
546
546
 
547
547
  void SP5::solve()
548
548
  {
549
- const double EPSILON = ZERO_THRESH; // Should this be different?
549
+ const double EPSILON = 1e-6; // Should this be different?
550
550
  std::vector<double> theta;
551
551
  theta.reserve(8);
552
552
 
@@ -646,8 +646,8 @@ namespace IKS
646
646
  Eigen::Vector3d v1 = a_1 * sc_1 + p1_s;
647
647
  Eigen::Vector3d v3 = a_3 * sc_3 + p3_s;
648
648
 
649
- const double norm_equation_error = std::fabs((v1 - h * k2).norm() - (v3 - h * k2).norm());
650
- if (norm_equation_error < EPSILON)
649
+ const double norm_equation_error = std::fabs(v1.norm() - v3.norm());
650
+ if (norm_equation_error < 1e-3)
651
651
  {
652
652
  SP1 sp(v3, v1, k2);
653
653
  sp.solve();
@@ -675,8 +675,7 @@ namespace IKS
675
675
 
676
676
  theta_2.push_back(sp.get_theta());
677
677
  }
678
- // Only save approximate solutions if no analytical one is given yet
679
- else if (theta_1.size()==0)
678
+ else
680
679
  {
681
680
  SP1 sp(v3, v1, k2);
682
681
  sp.solve();
@@ -698,6 +697,7 @@ namespace IKS
698
697
  approx_t2 = sp.get_theta();
699
698
 
700
699
  norm_errors_angles.push_back({norm_equation_error, approx_t1, approx_t2, approx_t3});
700
+ _solution_is_ls = true;
701
701
  }
702
702
  }
703
703
  }
@@ -60,13 +60,28 @@ namespace EAIK
60
60
  bot_kinematics = std::make_unique<IKS::General_6R>(H_remodeled, P_remodeled);
61
61
  original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
62
62
  break;
63
-
63
+ case 5:
64
+ bot_kinematics = std::make_unique<IKS::General_5R>(H_remodeled, P_remodeled);
65
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
66
+ break;
67
+ case 4:
68
+ bot_kinematics = std::make_unique<IKS::General_4R>(H_remodeled, P_remodeled);
69
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
70
+ break;
64
71
  case 3:
65
- bot_kinematics = std::make_unique<IKS::General_3R>(H_remodeled, P_remodeled);
66
- original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
72
+ bot_kinematics = std::make_unique<IKS::General_3R>(H_remodeled, P_remodeled);
73
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
74
+ break;
75
+ case 2:
76
+ bot_kinematics = std::make_unique<IKS::General_2R>(H_remodeled, P_remodeled);
77
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
78
+ break;
79
+ case 1:
80
+ bot_kinematics = std::make_unique<IKS::General_1R>(H_remodeled, P_remodeled);
81
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
67
82
  break;
68
83
  default:
69
- throw std::runtime_error("Currently, only 6R and 3R Robots are solvable with EAIK.");
84
+ throw std::runtime_error("Currently, only 1-6R robots are solvable with EAIK. Consider locking redundant joints.");
70
85
  break;
71
86
  }
72
87
  }
@@ -34,7 +34,7 @@ namespace EAIK
34
34
  Eigen::MatrixXd get_original_P() const;
35
35
 
36
36
  std::string get_kinematic_family() const;
37
-
37
+
38
38
  private:
39
39
  // Init function to allow nice constructor overloading
40
40
  void init(const Eigen::MatrixXd &H, const Eigen::MatrixXd &P, const std::vector<std::pair<int, double>>& fixed_axes={}, bool is_double_precision=true);
@@ -0,0 +1,48 @@
1
+ #include <eigen3/Eigen/Dense>
2
+ #include <vector>
3
+ #include <iostream>
4
+ #include <math.h>
5
+
6
+ #include "sp.h"
7
+ #include "IKS.h"
8
+ #include "utils/kinematic_utils.h"
9
+
10
+ namespace IKS
11
+ {
12
+ General_1R::General_1R(const Eigen::Matrix<double, 3, 1> &H, const Eigen::Matrix<double, 3, 2> &P)
13
+ : General_Robot(H,P), H(H), P(P)
14
+ {
15
+ }
16
+
17
+ IK_Solution General_1R::calculate_IK(const Homogeneous_T &ee_position_orientation) const
18
+ {
19
+ IK_Solution solution = calculate_position_IK(ee_position_orientation.block<3, 1>(0, 3));
20
+
21
+ // 6DOF Problem for 1R is overconstrained -> "Throw away" extraneous orientation solutions
22
+ return enforce_solution_consistency(solution, ee_position_orientation);
23
+ }
24
+
25
+ IK_Solution General_1R::calculate_position_IK(const Eigen::Vector3d &ee_position) const
26
+ {
27
+ IK_Solution sol;
28
+ const Eigen::Vector3d p_1t = ee_position - this->P.col(0);
29
+ SP1 sp1(this->P.col(1), p_1t, this->H.col(0));
30
+ sp1.solve();
31
+ sol.Q.push_back({sp1.get_theta()});
32
+ sol.is_LS_vec.push_back(sp1.solution_is_ls());
33
+
34
+ return sol;
35
+ }
36
+
37
+ IK_Solution General_1R::calculate_orientation_IK(const Eigen::Matrix3d &ee_orientation) const
38
+ {
39
+ IK_Solution sol;
40
+ const Eigen::Vector3d h_n = create_normal_vector(this->H.col(0));
41
+ SP1 sp1(h_n, ee_orientation*h_n, this->H.col(0));
42
+ sp1.solve();
43
+ sol.Q.push_back({sp1.get_theta()});
44
+ sol.is_LS_vec.push_back(sp1.solution_is_ls());
45
+
46
+ return sol;
47
+ }
48
+ };
@@ -0,0 +1,89 @@
1
+ #include <eigen3/Eigen/Dense>
2
+ #include <vector>
3
+ #include <iostream>
4
+ #include <math.h>
5
+
6
+ #include "sp.h"
7
+ #include "IKS.h"
8
+ #include "utils/kinematic_utils.h"
9
+ #include "kinematic_remodeling.h"
10
+
11
+ namespace IKS
12
+ {
13
+ General_2R::General_2R(const Eigen::Matrix<double, 3, 2> &H, const Eigen::Matrix<double, 3, 3> &P)
14
+ : General_Robot(H, P), H(H), P(P)
15
+ {
16
+ }
17
+
18
+ IK_Solution General_2R::calculate_IK(const Homogeneous_T &ee_position_orientation) const
19
+ {
20
+ IK_Solution solution = calculate_position_IK(ee_position_orientation.block<3, 1>(0, 3));
21
+
22
+ // 6DOF Problem for 2R is overconstrained -> "Throw away" extraneous orientation solutions
23
+ return enforce_solution_consistency(solution, ee_position_orientation);
24
+ }
25
+
26
+ IK_Solution General_2R::calculate_position_IK(const Eigen::Vector3d &ee_position) const
27
+ {
28
+ IK_Solution solution;
29
+ const Eigen::Vector3d p_1ee = ee_position - this->P.col(0);
30
+
31
+ if (EAIK::do_axes_intersect(this->H.col(0), this->H.col(1), this->P.col(1), ZERO_THRESH, ZERO_THRESH))
32
+ {
33
+ // (h1 x h2)(p12)==0) -> h1 intersects h2
34
+ SP2 sp2(p_1ee, this->P.col(2), -this->H.col(0), this->H.col(1));
35
+ sp2.solve();
36
+ const std::vector<double>& theta_1 = sp2.get_theta_1();
37
+ const std::vector<double>& theta_2 = sp2.get_theta_2();
38
+
39
+ for(unsigned i = 0; i < theta_1.size(); i++)
40
+ {
41
+ solution.Q.push_back({theta_1.at(i), theta_2.at(i)});
42
+ solution.is_LS_vec.push_back(sp2.solution_is_ls());
43
+ }
44
+ }
45
+ else
46
+ {
47
+ SP3 sp3(this->P.col(2), -this->P.col(1), this->H.col(1), p_1ee.norm());
48
+ sp3.solve();
49
+
50
+ for (const auto &q2 : sp3.get_theta())
51
+ {
52
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(q2, this->H.col(1).normalized()).toRotationMatrix();
53
+ SP1 sp1(this->P.col(1) + r_12 * this->P.col(2), p_1ee, this->H.col(0));
54
+ sp1.solve();
55
+ solution.Q.push_back({sp1.get_theta(), q2});
56
+ solution.is_LS_vec.push_back(sp1.solution_is_ls() || sp3.solution_is_ls());
57
+ }
58
+ }
59
+
60
+ return enforce_solution_consistency(solution, ee_position);
61
+ }
62
+
63
+ IK_Solution General_2R::calculate_orientation_IK(const Eigen::Matrix3d &ee_orientation) const
64
+ {
65
+ IK_Solution solution;
66
+ if (this->H.col(0).cross(this->H.col(1)).norm() > ZERO_THRESH)
67
+ {
68
+ const Eigen::Vector3d h_n = this->H.col(0).cross(this->H.col(1));
69
+ SP2 sp2(ee_orientation*h_n, h_n, -this->H.col(0), this->H.col(1));
70
+ sp2.solve();
71
+
72
+ const std::vector<double> &theta_1 = sp2.get_theta_1();
73
+ const std::vector<double> &theta_2 = sp2.get_theta_2();
74
+
75
+ for (unsigned i = 0; i < theta_1.size(); i++)
76
+ {
77
+ solution.Q.push_back({theta_1.at(i), theta_2.at(i)});
78
+ solution.is_LS_vec.push_back(sp2.solution_is_ls());
79
+ }
80
+ }
81
+ else
82
+ {
83
+ // Implicit redundancy
84
+ throw new std::runtime_error("Can not calculate orientation IK for implicitly redundant manipulator");
85
+ }
86
+
87
+ return enforce_solution_consistency(solution, ee_orientation);
88
+ }
89
+ };
@@ -0,0 +1,391 @@
1
+ #include <eigen3/Eigen/Dense>
2
+ #include <vector>
3
+ #include <iostream>
4
+ #include <math.h>
5
+
6
+ #include "sp.h"
7
+ #include "IKS.h"
8
+ #include "utils/kinematic_utils.h"
9
+ #include "kinematic_remodeling.h"
10
+
11
+ namespace IKS
12
+ {
13
+ General_4R::General_4R(const Eigen::Matrix<double, 3, 4> &H, const Eigen::Matrix<double, 3, 5> &P)
14
+ : General_Robot(H,P), H(H), P(P)
15
+ {
16
+ kinematicClass = determine_Kinematic_Class();
17
+ }
18
+
19
+ General_4R::KinematicClass General_4R::determine_Kinematic_Class()
20
+ {
21
+ if (this->H.col(0).cross(this->H.col(1)).norm() >= ZERO_THRESH &&
22
+ std::fabs(this->H.col(0).cross(this->H.col(1)).transpose() * this->P.col(1)) < ZERO_THRESH)
23
+ {
24
+ // (h1 x h2)(p12)==0) -> h1 intersects h2
25
+ // And 1 not parallel to 2 (h1 x h2 =/= 0)
26
+ const Eigen::Vector3d intersection = EAIK::calc_intersection(H.col(0), H.col(1), P.col(0), P.col(1), ZERO_THRESH);
27
+
28
+ if(EAIK::is_point_on_Axis(H.col(2), P.col(0)+P.col(1)+P.col(2), intersection, ZERO_THRESH))
29
+ {
30
+ const auto&[H_reversed, P_reversed] = reverse_kinematic_chain(this->H, this->P);
31
+ const Eigen::MatrixXd P_reversed_remodeled = EAIK::remodel_kinematics(H_reversed, P_reversed, ZERO_THRESH, ZERO_THRESH);
32
+
33
+ this->reversed_Robot_ptr = std::make_unique<General_4R>(H_reversed, P_reversed_remodeled);
34
+ // Spherical Wrist at the base of the robot
35
+ return KinematicClass::REVERSED;
36
+ }
37
+ else if(this->H.col(2).cross(this->H.col(3)).norm() >= ZERO_THRESH &&
38
+ std::fabs(this->H.col(2).cross(this->H.col(3)).transpose() * this->P.col(3)) < ZERO_THRESH)
39
+ {
40
+ // (h3 x h4)(p34)==0) -> h3 intersects h4
41
+ // And 3 not parallel to 4 (h3 x h4 =/= 0)
42
+ return KinematicClass::FIRST_TWO_LAST_TWO_INTERSECTING;
43
+ }
44
+
45
+ // Same as if h3 intersects with h4 -> Use reversed kinematics
46
+ const auto&[H_reversed, P_reversed] = reverse_kinematic_chain(this->H, this->P);
47
+ const Eigen::MatrixXd P_reversed_remodeled = EAIK::remodel_kinematics(H_reversed, P_reversed, ZERO_THRESH, ZERO_THRESH);
48
+
49
+ this->reversed_Robot_ptr = std::make_unique<General_4R>(H_reversed, P_reversed_remodeled);
50
+ return KinematicClass::REVERSED;
51
+ }
52
+
53
+ // Spherical wrist at end of manipulator
54
+ if (this->H.col(1).cross(this->H.col(2)).norm() >= ZERO_THRESH &&
55
+ std::fabs(this->H.col(1).cross(this->H.col(2)).transpose() * this->P.col(2)) < ZERO_THRESH)
56
+ {
57
+ const Eigen::Vector3d p02 = P.block<3,2>(0,0).rowwise().sum();
58
+ const Eigen::Vector3d intersection = EAIK::calc_intersection(H.col(1), H.col(2), p02,P.col(2), ZERO_THRESH);
59
+
60
+ if(EAIK::is_point_on_Axis(H.col(3), p02+P.col(2)+P.col(3), intersection, ZERO_THRESH))
61
+ {
62
+ return KinematicClass::SPHERICAL_WRIST;
63
+ }
64
+
65
+ return KinematicClass::SECOND_THIRD_INTERSECTING;
66
+ }
67
+
68
+ if(this->H.col(2).cross(this->H.col(3)).norm() >= ZERO_THRESH &&
69
+ std::fabs(this->H.col(2).cross(this->H.col(3)).transpose() * this->P.col(3)) < ZERO_THRESH)
70
+ {
71
+ // (h3 x h4)(p34)==0) -> h3 intersects h4
72
+ // And 3 not parallel to 4 (h3 x h4 =/= 0)
73
+ return KinematicClass::THIRD_FOURTH_INTERSECTING;
74
+ }
75
+
76
+ if (this->H.col(0).cross(this->H.col(1)).norm() > ZERO_THRESH)
77
+ {
78
+ // h1 =/= h2
79
+ if (this->H.col(1).cross(this->H.col(2)).norm() > ZERO_THRESH)
80
+ {
81
+ // h2 =/= h3
82
+ if (this->H.col(2).cross(this->H.col(3)).norm() < ZERO_THRESH)
83
+ {
84
+ // h3 == h4
85
+ const auto&[H_reversed, P_reversed] = reverse_kinematic_chain(this->H, this->P);
86
+ const Eigen::MatrixXd P_reversed_remodeled = EAIK::remodel_kinematics(H_reversed, P_reversed, ZERO_THRESH, ZERO_THRESH);
87
+
88
+ this->reversed_Robot_ptr = std::make_unique<General_4R>(H_reversed, P_reversed_remodeled);
89
+ return KinematicClass::REVERSED;
90
+ }
91
+ return KinematicClass::NONE_PARALLEL_NONE_INTERSECTING;
92
+ }
93
+ // h2 == h3
94
+ return KinematicClass::SECOND_THIRD_PARALLEL;
95
+ }
96
+ else if(this->H.col(1).cross(this->H.col(2)).norm() > ZERO_THRESH)
97
+ {
98
+ // h1 == h2 , h2 =/= h3
99
+ return KinematicClass::FIRST_SECOND_PARALLEL;
100
+ }
101
+ else
102
+ {
103
+ // h1 == h2 , h2 == h3 -> h3 =/= h4 for non-redundant manipulator -> reverse
104
+ const auto&[H_reversed, P_reversed] = reverse_kinematic_chain(this->H, this->P);
105
+ const Eigen::MatrixXd P_reversed_remodeled = EAIK::remodel_kinematics(H_reversed, P_reversed, ZERO_THRESH, ZERO_THRESH);
106
+
107
+ this->reversed_Robot_ptr = std::make_unique<General_4R>(H_reversed, P_reversed_remodeled);
108
+ return KinematicClass::REVERSED;
109
+ }
110
+
111
+ return KinematicClass::UNKNOWN;
112
+ }
113
+
114
+ IK_Solution General_4R::calculate_IK(const Homogeneous_T &ee_position_orientation) const
115
+ {
116
+ IK_Solution solution;
117
+ const Eigen::Vector3d p_0t = ee_position_orientation.block<3, 1>(0, 3);
118
+ const Eigen::Matrix3d r_04 = ee_position_orientation.block<3, 3>(0, 0);
119
+ const Eigen::Vector3d p_14 = p_0t - this->P.col(0) - r_04*this->P.col(4);
120
+
121
+ switch(this->kinematicClass)
122
+ {
123
+ case KinematicClass::THIRD_FOURTH_INTERSECTING:
124
+ {
125
+ // (h3 x h4)(p34)==0) -> h3 intersects h4
126
+ // And 3 not parallel to 4 (h3 x h4 =/= 0)
127
+ SP3 sp3_q1(p_14, this->P.col(1), -this->H.col(0), this->P.col(2).norm());
128
+ sp3_q1.solve();
129
+
130
+ for(const auto& q1 : sp3_q1.get_theta())
131
+ {
132
+ const Eigen::Matrix3d r_10 = Eigen::AngleAxisd(q1, -this->H.col(0).normalized()).toRotationMatrix();
133
+ SP3 sp3_q2(this->P.col(2), -this->P.col(1), this->H.col(1), p_14.norm());
134
+ sp3_q2.solve();
135
+
136
+ for(const auto& q2 : sp3_q2.get_theta())
137
+ {
138
+ const Eigen::Matrix3d r_21 = Eigen::AngleAxisd(q2, -this->H.col(1).normalized()).toRotationMatrix();
139
+
140
+ const Eigen::Vector3d hn = create_normal_vector(this->H.col(2));
141
+ SP2 sp2(r_21*r_10*r_04*hn, hn, -this->H.col(2), this->H.col(3));
142
+ sp2.solve();
143
+
144
+ const std::vector<double>& theta_3 = sp2.get_theta_1();
145
+ const std::vector<double>& theta_4 = sp2.get_theta_2();
146
+
147
+ for(unsigned i = 0; i < theta_3.size(); ++i)
148
+ {
149
+ solution.Q.push_back({q1, q2, theta_3.at(i), theta_4.at(i)});
150
+ solution.is_LS_vec.push_back(sp3_q1.solution_is_ls() || sp3_q2.solution_is_ls() || sp2.solution_is_ls());
151
+ }
152
+ }
153
+ }
154
+ break;
155
+ };
156
+ case KinematicClass::SECOND_THIRD_INTERSECTING:
157
+ {
158
+ SP3 sp3(this->P.col(1), p_14, this->H.col(0), this->P.col(3).norm());
159
+ sp3.solve();
160
+
161
+ for(const auto& q1 : sp3.get_theta())
162
+ {
163
+ const Eigen::Matrix3d r_10 = Eigen::AngleAxisd(q1, -this->H.col(0).normalized()).toRotationMatrix();
164
+ SP2 sp2(r_10*p_14 - this->P.col(1), this->P.col(3), -this->H.col(1), this->H.col(2));
165
+ sp2.solve();
166
+
167
+ const std::vector<double>& theta_2 = sp2.get_theta_1();
168
+ const std::vector<double>& theta_3 = sp2.get_theta_2();
169
+
170
+ const Eigen::Vector3d h_n = create_normal_vector(this->H.col(3));
171
+ for(unsigned i = 0; i < theta_2.size(); ++i)
172
+ {
173
+ const double& q2 = theta_2.at(i);
174
+ const double& q3 = theta_3.at(i);
175
+
176
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(q2, this->H.col(1).normalized()).toRotationMatrix();
177
+ const Eigen::Matrix3d r_23 = Eigen::AngleAxisd(q3, this->H.col(2).normalized()).toRotationMatrix();
178
+
179
+ SP1 sp1(h_n, r_04.transpose()*r_10.transpose()*r_12*r_23*h_n, -this->H.col(3));
180
+ sp1.solve();
181
+
182
+ solution.Q.push_back({q1, q2, q3, sp1.get_theta()});
183
+ solution.is_LS_vec.push_back(sp3.solution_is_ls() || sp2.solution_is_ls() || sp1.solution_is_ls());
184
+ }
185
+ }
186
+
187
+ break;
188
+ };
189
+ case KinematicClass::FIRST_SECOND_PARALLEL:
190
+ {
191
+ // h1 = h2; h2=/=h3
192
+ SP4 sp4_q3(this->H.col(0), this->P.col(3), this->H.col(2), this->H.col(0).transpose()*(p_14 - this->P.col(1) - this->P.col(2)));
193
+ sp4_q3.solve();
194
+
195
+ for(const auto& q3 : sp4_q3.get_theta())
196
+ {
197
+ const Eigen::Matrix3d r_23 = Eigen::AngleAxisd(q3, this->H.col(2).normalized()).toRotationMatrix();
198
+ SP3 sp3_q1(p_14, this->P.col(1), -this->H.col(0), (this->P.col(2)+r_23*this->P.col(3)).norm());
199
+ sp3_q1.solve();
200
+
201
+ for(const auto& q1 : sp3_q1.get_theta())
202
+ {
203
+ const Eigen::Matrix3d r_01 = Eigen::AngleAxisd(q1, this->H.col(0).normalized()).toRotationMatrix();
204
+ SP1 sp1_q2(this->P.col(2)+r_23*this->P.col(3), r_01.transpose()*p_14-this->P.col(1), this->H.col(1));
205
+ sp1_q2.solve();
206
+
207
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(sp1_q2.get_theta(), this->H.col(1).normalized()).toRotationMatrix();
208
+ const Eigen::Vector3d hn = create_normal_vector(this->H.col(3));
209
+ SP1 sp1_q4(hn, r_04.transpose()*r_01*r_12*r_23*hn, -this->H.col(3));
210
+ sp1_q4.solve();
211
+
212
+ solution.Q.push_back({q1, sp1_q2.get_theta(), q3, sp1_q4.get_theta()});
213
+ solution.is_LS_vec.push_back(sp4_q3.solution_is_ls() || sp3_q1.solution_is_ls() || sp1_q2.solution_is_ls() || sp1_q4.solution_is_ls());
214
+ }
215
+ }
216
+ break;
217
+ };
218
+ case KinematicClass::SECOND_THIRD_PARALLEL:
219
+ {
220
+ // h1 =/= h2; h2=h3
221
+ SP4 sp4(this->H.col(1), p_14, -this->H.col(0), this->H.col(1).transpose()*(this->P.col(1) + this->P.col(2) + this->P.col(3)));
222
+ sp4.solve();
223
+
224
+ for(const auto& q1 : sp4.get_theta())
225
+ {
226
+ const Eigen::Matrix3d r_10 = Eigen::AngleAxisd(q1, -this->H.col(0).normalized()).toRotationMatrix();
227
+ SP3 sp3(this->P.col(3), -this->P.col(2), this->H.col(2), (r_10*p_14-this->P.col(1)).norm());
228
+ sp3.solve();
229
+
230
+ for(const auto& q3 : sp3.get_theta())
231
+ {
232
+ const Eigen::Matrix3d r_23 = Eigen::AngleAxisd(q3, this->H.col(2).normalized()).toRotationMatrix();
233
+ SP1 sp1_q2(this->P.col(2) + r_23*this->P.col(3), r_10*p_14-this->P.col(1), this->H.col(1));
234
+ sp1_q2.solve();
235
+
236
+ const double q2 = sp1_q2.get_theta();
237
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(q2, this->H.col(1).normalized()).toRotationMatrix();
238
+
239
+ const Eigen::Vector3d hn = create_normal_vector(this->H.col(3));
240
+ SP1 sp1_q4(hn, r_04.transpose()*r_10.transpose()*r_12*r_23*hn, -this->H.col(3));
241
+ sp1_q4.solve();
242
+
243
+ solution.Q.push_back({q1, q2, q3, sp1_q4.get_theta()});
244
+ solution.is_LS_vec.push_back(sp4.solution_is_ls() || sp3.solution_is_ls() || sp1_q2.solution_is_ls() || sp1_q4.solution_is_ls());
245
+ }
246
+ }
247
+ break;
248
+ };
249
+ case KinematicClass::NONE_PARALLEL_NONE_INTERSECTING:
250
+ {
251
+ // h1 =/= h2; h2=/=h3
252
+ SP5 sp5(-this->P.col(1), p_14, this->P.col(2), this->P.col(3), -this->H.col(0), this->H.col(1), this->H.col(2));
253
+ sp5.solve();
254
+
255
+ const std::vector<double> &theta_1 = sp5.get_theta_1();
256
+ const std::vector<double> &theta_2 = sp5.get_theta_2();
257
+ const std::vector<double> &theta_3 = sp5.get_theta_3();
258
+
259
+ const Eigen::Vector3d h_n = create_normal_vector(this->H.col(3));
260
+ for(unsigned i = 0; i < theta_1.size(); i++)
261
+ {
262
+ const double& q1 = theta_1.at(i);
263
+ const double& q2 = theta_2.at(i);
264
+ const double& q3 = theta_3.at(i);
265
+ const Eigen::Matrix3d r_01 = Eigen::AngleAxisd(q1, this->H.col(0).normalized()).toRotationMatrix();
266
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(q2, this->H.col(1).normalized()).toRotationMatrix();
267
+ const Eigen::Matrix3d r_23 = Eigen::AngleAxisd(q3, this->H.col(2).normalized()).toRotationMatrix();
268
+
269
+ SP1 sp1(h_n, r_23.transpose()*r_12.transpose()*r_01.transpose()*r_04*h_n, this->H.col(3));
270
+ sp1.solve();
271
+
272
+ solution.Q.push_back({q1, q2, q3, sp1.get_theta()});
273
+ solution.is_LS_vec.push_back(sp5.solution_is_ls() || sp1.solution_is_ls());
274
+ }
275
+ break;
276
+ };
277
+ case KinematicClass::FIRST_TWO_LAST_TWO_INTERSECTING:
278
+ {
279
+ SP2 sp2_q12(p_14, this->P.col(2), -this->H.col(0), this->H.col(1));
280
+ sp2_q12.solve();
281
+
282
+ const std::vector<double> &theta_1 = sp2_q12.get_theta_1();
283
+ const std::vector<double> &theta_2 = sp2_q12.get_theta_2();
284
+
285
+ for(unsigned i = 0; i < theta_1.size(); ++i)
286
+ {
287
+ const double& q1 = theta_1.at(i);
288
+ const double& q2 = theta_2.at(i);
289
+ const Eigen::Matrix3d r_01 = Eigen::AngleAxisd(q1, this->H.col(0).normalized()).toRotationMatrix();
290
+ const Eigen::Matrix3d r_12 = Eigen::AngleAxisd(q2, this->H.col(1).normalized()).toRotationMatrix();
291
+
292
+ const Eigen::Vector3d h_n = this->H.col(3).cross(r_04.transpose()*r_01*r_12*this->H.col(2));
293
+ SP2 sp2_q34(r_12.transpose()*r_01.transpose()*r_04*h_n, h_n, -this->H.col(2), this->H.col(3));
294
+ sp2_q34.solve();
295
+
296
+ const std::vector<double> &theta_3 = sp2_q34.get_theta_1();
297
+ const std::vector<double> &theta_4 = sp2_q34.get_theta_2();
298
+
299
+ for(unsigned j = 0; j < theta_3.size(); ++j)
300
+ {
301
+ const double& q3 = theta_3.at(j);
302
+ const double& q4 = theta_4.at(j);
303
+
304
+ solution.Q.push_back({q1, q2, q3, q4});
305
+ solution.is_LS_vec.push_back(sp2_q12.solution_is_ls() || sp2_q34.solution_is_ls());
306
+ }
307
+ }
308
+
309
+ break;
310
+ };
311
+ case KinematicClass::SPHERICAL_WRIST:
312
+ {
313
+ SP1 sp1_q1(this->P.col(1), p_14, this->H.col(0));
314
+ sp1_q1.solve();
315
+
316
+ const double& q1 = sp1_q1.get_theta();
317
+ const Eigen::Matrix3d r_01 = Eigen::AngleAxisd(q1, this->H.col(0).normalized()).toRotationMatrix();
318
+
319
+ SP2 sp2(r_01.transpose()*r_04*this->H.col(3), this->H.col(3), -this->H.col(1), this->H.col(2));
320
+ sp2.solve();
321
+
322
+ const std::vector<double> &theta_2 = sp2.get_theta_1();
323
+ const std::vector<double> &theta_3 = sp2.get_theta_2();
324
+
325
+ const Eigen::Vector3d& h_n = create_normal_vector(this->H.col(3));
326
+ for(unsigned i = 0; i < theta_2.size(); ++i)
327
+ {
328
+ const double& q2 = theta_2.at(i);
329
+ const double& q3 = theta_3.at(i);
330
+
331
+ const Eigen::Matrix3d r_21 = Eigen::AngleAxisd(q2, -this->H.col(1).normalized()).toRotationMatrix();
332
+ const Eigen::Matrix3d r_32 = Eigen::AngleAxisd(q3, -this->H.col(2).normalized()).toRotationMatrix();
333
+
334
+ SP1 sp1_q4(h_n, r_32*r_21*r_01.transpose()*r_04*h_n, this->H.col(3));
335
+ sp1_q4.solve();
336
+
337
+ solution.Q.push_back({q1, q2, q3, sp1_q4.get_theta()});
338
+ solution.is_LS_vec.push_back(sp1_q1.solution_is_ls() || sp2.solution_is_ls() || sp1_q4.solution_is_ls());
339
+ }
340
+ break;
341
+ };
342
+ case KinematicClass::REVERSED:
343
+ {
344
+ if(reversed_Robot_ptr)
345
+ {
346
+ // Use reversed kinematic chain
347
+ solution = this->reversed_Robot_ptr->calculate_IK(inverse_homogeneous_T(ee_position_orientation));
348
+ reverse_vector_second_dimension(solution.Q);
349
+ }
350
+ else
351
+ {
352
+ throw std::runtime_error("Reversed kinematic chain was not initialized! Please open an issue on our GitHub repository.");
353
+ }
354
+
355
+ break;
356
+ };
357
+
358
+ default:
359
+ std::cerr<< "The choosen manipulator has no known subproblem decomposition! The resulting solutions will be empty."<<std::endl;
360
+ }
361
+
362
+ return enforce_solution_consistency(solution, ee_position_orientation);
363
+ }
364
+
365
+
366
+ std::string General_4R::get_kinematic_family() const
367
+ {
368
+ switch (kinematicClass)
369
+ {
370
+ case KinematicClass::THIRD_FOURTH_INTERSECTING:
371
+ return std::string("4R-THIRD_FOURTH_INTERSECTING");
372
+ case KinematicClass::SECOND_THIRD_INTERSECTING:
373
+ return std::string("4R-SECOND_THIRD_INTERSECTING");
374
+ case KinematicClass::FIRST_SECOND_PARALLEL:
375
+ return std::string("4R-FIRST_SECOND_PARALLEL");
376
+ case KinematicClass::SECOND_THIRD_PARALLEL:
377
+ return std::string("4R-SECOND_THIRD_PARALLEL");
378
+ case KinematicClass::NONE_PARALLEL_NONE_INTERSECTING:
379
+ return std::string("4R-NONE_PARALLEL_NONE_INTERSECTING");
380
+ case KinematicClass::FIRST_TWO_LAST_TWO_INTERSECTING:
381
+ return std::string("4R-FIRST_TWO_LAST_TWO_INTERSECTING");
382
+ case KinematicClass::SPHERICAL_WRIST:
383
+ return std::string("4R-SPHERICAL_WRIST");
384
+ case KinematicClass::REVERSED:
385
+ return reversed_Robot_ptr ? reversed_Robot_ptr->get_kinematic_family() : "4R-Unknown Kinematic Class";
386
+ default:
387
+ return std::string("4R-Unknown Kinematic Class");
388
+ }
389
+ }
390
+
391
+ };