EAIK 0.0.1__tar.gz → 1.1.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 (66) hide show
  1. {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/sp.cpp +124 -65
  2. {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/sp.h +1 -1
  3. {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/src/CMakeLists.txt +3 -3
  4. eaik-1.1.0/CPP/src/EAIK.cpp +258 -0
  5. eaik-1.1.0/CPP/src/EAIK.h +58 -0
  6. eaik-1.1.0/CPP/src/IK/3R_IK.cpp +148 -0
  7. eaik-1.1.0/CPP/src/IK/6R_IK.cpp +646 -0
  8. {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/src/IK/CMakeLists.txt +2 -2
  9. eaik-1.1.0/CPP/src/IK/IKS.h +101 -0
  10. eaik-1.1.0/CPP/src/IK/utils/kinematic_utils.cpp +64 -0
  11. eaik-1.1.0/CPP/src/IK/utils/kinematic_utils.h +13 -0
  12. eaik-1.1.0/CPP/src/utils/kinematic_remodeling.cpp +165 -0
  13. eaik-1.1.0/CPP/src/utils/kinematic_remodeling.h +20 -0
  14. eaik-1.1.0/MANIFEST.in +6 -0
  15. eaik-1.1.0/PKG-INFO +811 -0
  16. eaik-1.1.0/README.md +116 -0
  17. {eaik-0.0.1 → eaik-1.1.0}/pyproject.toml +5 -5
  18. eaik-1.1.0/setup.py +22 -0
  19. eaik-1.1.0/src/EAIK.egg-info/PKG-INFO +811 -0
  20. eaik-1.1.0/src/EAIK.egg-info/SOURCES.txt +30 -0
  21. eaik-1.1.0/src/eaik/IK_DH.py +26 -0
  22. eaik-1.1.0/src/eaik/IK_Homogeneous.py +42 -0
  23. eaik-1.1.0/src/eaik/IK_Robot.py +79 -0
  24. eaik-1.1.0/src/eaik/IK_URDF.py +40 -0
  25. eaik-1.1.0/src/eaik/pybindings/eaik_pybindings.cpp +86 -0
  26. eaik-0.0.1/MANIFEST.in +0 -1
  27. eaik-0.0.1/PKG-INFO +0 -108
  28. eaik-0.0.1/README.md +0 -89
  29. eaik-0.0.1/external/EAIK/.git +0 -1
  30. eaik-0.0.1/external/EAIK/.gitignore +0 -8
  31. eaik-0.0.1/external/EAIK/.gitmodules +0 -3
  32. eaik-0.0.1/external/EAIK/README.md +0 -2
  33. eaik-0.0.1/external/EAIK/Tests/CMakeLists.txt +0 -24
  34. eaik-0.0.1/external/EAIK/Tests/IK_system_tests.cpp +0 -252
  35. eaik-0.0.1/external/EAIK/Tests/IK_unit_tests.cpp +0 -369
  36. eaik-0.0.1/external/EAIK/external/ik-geo/.git +0 -1
  37. eaik-0.0.1/external/EAIK/external/ik-geo/.gitignore +0 -43
  38. eaik-0.0.1/external/EAIK/external/ik-geo/LICENSE +0 -29
  39. eaik-0.0.1/external/EAIK/external/ik-geo/README.md +0 -60
  40. eaik-0.0.1/external/EAIK/src/EAIK.cpp +0 -50
  41. eaik-0.0.1/external/EAIK/src/EAIK.h +0 -28
  42. eaik-0.0.1/external/EAIK/src/IK/General_IK.cpp +0 -155
  43. eaik-0.0.1/external/EAIK/src/IK/IKS.h +0 -41
  44. eaik-0.0.1/external/EAIK/src/IK/Spherical_IK.cpp +0 -261
  45. eaik-0.0.1/external/EAIK/src/utils/kinematic_remodelling.cpp +0 -105
  46. eaik-0.0.1/external/EAIK/src/utils/kinematic_remodelling.h +0 -16
  47. eaik-0.0.1/setup.py +0 -20
  48. eaik-0.0.1/src/EAIK.egg-info/PKG-INFO +0 -108
  49. eaik-0.0.1/src/EAIK.egg-info/SOURCES.txt +0 -44
  50. eaik-0.0.1/src/eaik/IK_CSV.py +0 -97
  51. eaik-0.0.1/src/eaik/IK_URDF.py +0 -47
  52. eaik-0.0.1/src/eaik/cpp/eaik_pybindings.cpp +0 -56
  53. eaik-0.0.1/src/eaik/examples/evaluate_ik.py +0 -49
  54. eaik-0.0.1/src/eaik/examples/load_urdf.py +0 -52
  55. eaik-0.0.1/src/eaik/examples/robot_csv.py +0 -79
  56. eaik-0.0.1/tests/test_axis_convention_trafo.py +0 -115
  57. eaik-0.0.1/tests/test_sphericalwrist_first_intersection.py +0 -123
  58. eaik-0.0.1/tests/test_sphericalwrist_generation.py +0 -28
  59. eaik-0.0.1/tests/test_sphericalwrist_second_intersection.py +0 -199
  60. {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/CMakeLists.txt +0 -0
  61. {eaik-0.0.1/external/EAIK → eaik-1.1.0}/LICENSE +0 -0
  62. {eaik-0.0.1 → eaik-1.1.0}/setup.cfg +0 -0
  63. {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/dependency_links.txt +0 -0
  64. {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/requires.txt +0 -0
  65. {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/top_level.txt +0 -0
  66. {eaik-0.0.1 → eaik-1.1.0}/src/eaik/__init__.py +0 -0
@@ -1,8 +1,3 @@
1
- //---------------------------------------------------------------//
2
- // Name: sp_1.cpp
3
- // Author: Runbin Chen and Amar Maksumic
4
- // Purpose: Port of the subproblem files functionality
5
- //---------------------------------------------------------------//
6
1
  #include "sp.h"
7
2
  #include <math.h>
8
3
  #include <vector>
@@ -56,50 +51,16 @@ namespace IKS
56
51
 
57
52
  std::vector<std::complex<double>> quartic_roots(const Eigen::Matrix<double, 1, 5> &poly)
58
53
  {
54
+ Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;
55
+ solver.compute(poly);
56
+ const Eigen::PolynomialSolver<double, Eigen::Dynamic>::RootsType &roots = solver.roots();
59
57
 
60
- const std::complex<double> i = std::complex<double>(0, 0);
61
-
62
- std::vector<std::complex<double>> roots;
63
-
64
- double A = poly(0, 0), B = poly(0, 1), C = poly(0, 2), D = poly(0, 3), E = poly(0, 4);
65
-
66
- std::complex<double> alpha = -0.375 * B * B / (A * A) + C / A;
67
- std::complex<double> beta = 0.125 * B * B * B / (A * A * A) - 0.5 * B * C / (A * A) + D / A;
68
- std::complex<double> gamma = -B * B * B * B * 3. / (A * A * A * A * 256.) + C * B * B / (A * A * A * 16.) - B * D / (A * A * 4.) + E / A;
69
-
70
- if (fabs(beta.real()) < ZERO_THRESH && fabs(beta.imag()) < ZERO_THRESH)
71
- {
72
- std::complex<double> tmp = sqrt(alpha * alpha - gamma * 4. + i);
73
- roots.push_back(-B / (A * 4.) + sqrt((-alpha + tmp) / 2. + i));
74
- roots.push_back(-B / (A * 4.) - sqrt((-alpha + tmp) / 2. + i));
75
- roots.push_back(-B / (A * 4.) + sqrt((-alpha - tmp) / 2. + i));
76
- roots.push_back(-B / (A * 4.) - sqrt((-alpha - tmp) / 2. + i));
77
- return roots;
58
+ std::vector<std::complex<double>> result(roots.size());
59
+ for (int i = 0; i < roots.size(); ++i) {
60
+ result[i] = roots[i];
78
61
  }
79
62
 
80
- std::complex<double> P = -alpha * alpha / 12. - gamma;
81
- std::complex<double> Q = -alpha * alpha * alpha / 108. + alpha * gamma / 3. - beta * beta * 0.125;
82
- std::complex<double> R = -Q * 0.5 + sqrt(Q * Q * 0.25 + P * P * P / 27. + i);
83
- std::complex<double> U = pow(R, 1. / 3);
84
-
85
- std::complex<double> y;
86
- if (fabs(U.real()) < ZERO_THRESH && fabs(U.imag()) < ZERO_THRESH)
87
- {
88
- y = -alpha * 5. / 6. - pow(Q, 1. / 3);
89
- }
90
- else
91
- {
92
- y = -alpha * 5. / 6. + U - P / (3. * U);
93
- }
94
-
95
- std::complex<double> W = sqrt(alpha + 2. * y + i);
96
-
97
- roots.push_back(-B / (A * 4.) + (W + sqrt(-(alpha * 3. + 2. * y + beta * 2. / W))) / 2.);
98
- roots.push_back(-B / (A * 4.) + (W - sqrt(-(alpha * 3. + 2. * y + beta * 2. / W))) / 2.);
99
- roots.push_back(-B / (A * 4.) - (W + sqrt(-(alpha * 3. + 2. * y - beta * 2. / W))) / 2.);
100
- roots.push_back(-B / (A * 4.) - (W - sqrt(-(alpha * 3. + 2. * y - beta * 2. / W))) / 2.);
101
-
102
- return roots;
63
+ return result;
103
64
  }
104
65
 
105
66
  std::vector<std::pair<double, double>> solve_2_ellipse_numeric(const Eigen::Vector2d &xm1, const Eigen::Matrix<double, 2, 2> &xn1,
@@ -110,7 +71,7 @@ namespace IKS
110
71
  xm1'*xm1 + xi'*xn1'*xn1*xi + xm1'*xn1*xi == 1
111
72
  xm2'*xm2 + xi'*xn2'*xn2*xi + xm2'*xn2*xi == 1
112
73
  Where xi = [xi_1; xi_2] */
113
- const double EPSILON = ZERO_THRESH;
74
+ const double EPSILON = 1e-5;
114
75
  Eigen::Matrix<double, 2, 2> A_1 = xn1.transpose() * xn1;
115
76
  double a = A_1.coeffRef(0, 0);
116
77
  double b = 2 * A_1.coeffRef(1, 0);
@@ -148,7 +109,7 @@ namespace IKS
148
109
  double z4 = a*a*c1*c1 - 2.0*a*c1*a1*c + a1*a1*c*c - b*a*b1*c1 - b*b1*a1*c + b*b*a1*c1 +
149
110
  c*a*b1*b1;
150
111
  Eigen::Matrix<double, 1, 5> z;
151
- z << z4, z3, z2, z1, z0;
112
+ z << z0,z1, z2, z3, z4;
152
113
  std::vector<std::complex<double>> roots = quartic_roots(z);
153
114
 
154
115
  std::vector<std::pair<double, double>> xi;
@@ -237,10 +198,19 @@ namespace IKS
237
198
  a.row(1) = -k.cross(kxp);
238
199
 
239
200
  const Eigen::Vector2d x = a * p2;
240
- theta = std::atan2(x.x(), x.y());
241
201
 
242
- _solution_is_ls = std::fabs(p1.norm() - p2.norm()) > ZERO_THRESH ||
243
- std::fabs(k.dot(p1) - k.dot(p2)) > ZERO_THRESH;
202
+ if(x.norm() > ZERO_THRESH)
203
+ {
204
+ theta = std::atan2(x.x(), x.y());
205
+ _solution_is_ls = std::fabs(p1.norm() - p2.norm()) > ZERO_THRESH ||
206
+ std::fabs(k.dot(p1) - k.dot(p2)) > ZERO_THRESH;
207
+ }
208
+ else
209
+ {
210
+ theta = 0;
211
+ _solution_is_ls = true;
212
+ }
213
+
244
214
 
245
215
  is_calculated = true;
246
216
  }
@@ -401,12 +371,13 @@ namespace IKS
401
371
  {
402
372
  const Eigen::Vector3d kxp = k.cross(p1);
403
373
  Eigen::Matrix<double, 3, 2> a_1;
404
- if(kxp.isZero(ZERO_THRESH))
374
+ if(kxp.isZero(ZERO_THRESH) || k.cross(p2).isZero(ZERO_THRESH))
405
375
  {
406
376
  // Minimization is independent of theta -> probably redundant!
407
377
  //std::cout<<"Warning! - minimization of subproblem 3 is indipendent of theta. This may indicate redundancy"<<std::endl;
408
378
  is_calculated = true;
409
379
  theta.push_back(0); // Default angle
380
+ _solution_is_ls = true;
410
381
  return;
411
382
  }
412
383
 
@@ -420,7 +391,7 @@ namespace IKS
420
391
 
421
392
  const Eigen::Vector2d x_ls = a_1.transpose() * (-2.0 * p2 * b / norm_a_sq);
422
393
 
423
- if (x_ls.squaredNorm() > 1.0)
394
+ if (x_ls.squaredNorm() > 1.0 - ZERO_THRESH || 1.0 - b * b / norm_a_sq < 0)
424
395
  {
425
396
  theta.push_back(std::atan2(x_ls.x(), x_ls.y()));
426
397
  _solution_is_ls = true;
@@ -436,6 +407,7 @@ namespace IKS
436
407
 
437
408
  theta.push_back(std::atan2(sc_1.x(), sc_1.y()));
438
409
  theta.push_back(std::atan2(sc_2.x(), sc_2.y()));
410
+
439
411
  _solution_is_ls = false;
440
412
  }
441
413
 
@@ -494,25 +466,32 @@ namespace IKS
494
466
  const Eigen::Vector2d a = h.transpose() * a_1;
495
467
 
496
468
  const double b = d - (h.transpose() * k * k.transpose() * p).x();
497
-
498
469
  const double norm_a_sq = a.squaredNorm();
499
470
  const Eigen::Vector2d x_ls = a_1.transpose() * h * b;
500
471
 
501
- if (norm_a_sq > b * b)
472
+ if (norm_a_sq - b * b > 0)
502
473
  {
503
- const double xi = std::sqrt((norm_a_sq - b * b));
474
+ const double xi = std::sqrt(norm_a_sq - b * b );
504
475
  const Eigen::Vector2d a_perp_tilde(a.y(), -a.x());
505
476
 
506
477
  const Eigen::Vector2d sc_1 = x_ls + xi * a_perp_tilde;
507
478
  const Eigen::Vector2d sc_2 = x_ls - xi * a_perp_tilde;
508
479
 
509
480
  theta.push_back(std::atan2(sc_1.x(), sc_1.y()));
510
- theta.push_back(std::atan2(sc_2.x(), sc_2.y()));
481
+ theta.push_back(std::atan2(sc_2.x(), sc_2.y()));
511
482
  _solution_is_ls = false;
512
483
  }
513
484
  else
514
485
  {
515
- theta.push_back(std::atan2(x_ls.x(), x_ls.y()));
486
+ if (x_ls.norm() < ZERO_THRESH)
487
+ {
488
+ theta.push_back(0);
489
+ }
490
+ else
491
+ {
492
+ theta.push_back(std::atan2(x_ls.x(), x_ls.y()));
493
+ }
494
+
516
495
  _solution_is_ls = true;
517
496
  }
518
497
 
@@ -587,7 +566,7 @@ namespace IKS
587
566
 
588
567
  const Eigen::Matrix<double, 1, 5> eqn_real = convolution_3(rhs, rhs) - 4.0 * convolution_3(p_13_sq, r_1);
589
568
 
590
- const std::vector<std::complex<double>> all_roots = quartic_roots(eqn_real);
569
+ std::vector<std::complex<double>> all_roots = quartic_roots(eqn_real.row(0).reverse());
591
570
 
592
571
  std::vector<double> h_vec;
593
572
  for (const auto &root : all_roots)
@@ -598,6 +577,16 @@ namespace IKS
598
577
  }
599
578
  }
600
579
 
580
+ // No analytical solution found - use all roots and decide later for valid solutions
581
+ if (h_vec.size() == 0)
582
+ {
583
+ for (const auto &root : all_roots)
584
+ {
585
+ h_vec.push_back(root.real());
586
+ }
587
+ _solution_is_ls = true;
588
+ }
589
+
601
590
  const Eigen::Vector3d kxp1 = k1.cross(p1);
602
591
  const Eigen::Vector3d kxp3 = k3.cross(p3);
603
592
 
@@ -616,6 +605,10 @@ namespace IKS
616
605
  j << 0, 1,
617
606
  -1, 0;
618
607
 
608
+ // We save angle combinations w.r.t. their error in std::fabs((v1 - h * k2).norm() - (v3 - h * k2).norm())
609
+ // If no combination reaches our threshold we can then still output the best approximate solution
610
+ std::vector<std::tuple<double, double, double, double>> norm_errors_angles;
611
+
619
612
  for (const auto &h : h_vec)
620
613
  {
621
614
  Eigen::Vector2d const_1 = a_1.transpose() * k2 * (h - delta_1);
@@ -653,19 +646,76 @@ namespace IKS
653
646
  Eigen::Vector3d v1 = a_1 * sc_1 + p1_s;
654
647
  Eigen::Vector3d v3 = a_3 * sc_3 + p3_s;
655
648
 
656
- if (std::fabs((v1 - h * k2).norm() - (v3 - h * k2).norm()) < EPSILON)
649
+ const double norm_equation_error = std::fabs((v1 - h * k2).norm() - (v3 - h * k2).norm());
650
+ if (norm_equation_error < EPSILON)
657
651
  {
658
652
  SP1 sp(v3, v1, k2);
659
653
  sp.solve();
654
+ _solution_is_ls |= sp.solution_is_ls();
655
+
656
+ if (sc_1.norm() > ZERO_THRESH)
657
+ {
658
+ theta_1.push_back(std::atan2(sc_1.x(), sc_1.y()));
659
+ }
660
+ else
661
+ {
662
+ theta_1.push_back(0);
663
+ _solution_is_ls = true;
664
+ }
665
+
666
+ if (sc_3.norm() > ZERO_THRESH)
667
+ {
668
+ theta_3.push_back(std::atan2(sc_3.x(), sc_3.y()));
669
+ }
670
+ else
671
+ {
672
+ theta_3.push_back(0);
673
+ _solution_is_ls = true;
674
+ }
660
675
 
661
- theta_1.push_back(std::atan2(sc_1.x(), sc_1.y()));
662
676
  theta_2.push_back(sp.get_theta());
663
- theta_3.push_back(std::atan2(sc_3.x(), sc_3.y()));
677
+ }
678
+ // Only save approximate solutions if no analytical one is given yet
679
+ else if (theta_1.size()==0)
680
+ {
681
+ SP1 sp(v3, v1, k2);
682
+ sp.solve();
683
+
684
+ double approx_t1 = 0;
685
+ double approx_t2 = 0;
686
+ double approx_t3 = 0;
687
+
688
+ if (sc_1.norm() > ZERO_THRESH)
689
+ {
690
+ approx_t1 = std::atan2(sc_1.x(), sc_1.y());
691
+ }
692
+
693
+ if (sc_3.norm() > ZERO_THRESH)
694
+ {
695
+ approx_t3 = std::atan2(sc_3.x(), sc_3.y());
696
+ }
697
+
698
+ approx_t2 = sp.get_theta();
699
+
700
+ norm_errors_angles.push_back({norm_equation_error, approx_t1, approx_t2, approx_t3});
664
701
  }
665
702
  }
666
703
  }
667
704
 
668
- reduce_solutionset();
705
+ // Check if any valid angles were found - if not, use best approximate solution
706
+ if (theta_1.size() == 0)
707
+ {
708
+ // Sort by error
709
+ std::sort(norm_errors_angles.begin(), norm_errors_angles.end(), [](const std::tuple<double, double, double, double>& a, const std::tuple<double, double, double, double>& b) {
710
+ return std::get<0>(a) < std::get<0>(b);
711
+ });
712
+ theta_1.push_back(std::get<1>(norm_errors_angles.at(0)));
713
+ theta_2.push_back(std::get<2>(norm_errors_angles.at(0)));
714
+ theta_3.push_back(std::get<3>(norm_errors_angles.at(0)));
715
+ _solution_is_ls = true;
716
+ }
717
+
718
+ //reduce_solutionset();
669
719
  is_calculated = true;
670
720
  }
671
721
 
@@ -893,8 +943,17 @@ namespace IKS
893
943
  for (const auto[xi_1, xi_2] : xi)
894
944
  {
895
945
  Eigen::Matrix<double, 4, 1> x = x_min + x_null_1 * xi_1 + x_null_2 * xi_2;
896
- theta_1.push_back(atan2(x(0, 0), x(1, 0)));
897
- theta_2.push_back(atan2(x(2, 0), x(3, 0)));
946
+
947
+ if(x.norm() < ZERO_THRESH)
948
+ {
949
+ theta_1.push_back(0);
950
+ theta_2.push_back(0);
951
+ }
952
+ else
953
+ {
954
+ theta_1.push_back(atan2(x(0, 0), x(1, 0)));
955
+ theta_2.push_back(atan2(x(2, 0), x(3, 0)));
956
+ }
898
957
  }
899
958
 
900
959
  is_calculated = true;
@@ -8,7 +8,7 @@ namespace IKS
8
8
  {
9
9
 
10
10
  #ifndef ZERO_THRESH
11
- #define ZERO_THRESH 1e-8
11
+ #define ZERO_THRESH 1e-12
12
12
  #endif
13
13
 
14
14
  std::pair<Eigen::Vector2d, Eigen::Vector3d> cone_polynomials(const Eigen::Vector3d &p0_i, const Eigen::Vector3d &k_i, const Eigen::Vector3d &p_i, const Eigen::Vector3d &p_i_s, const Eigen::Vector3d &k2);
@@ -1,7 +1,7 @@
1
1
  cmake_minimum_required(VERSION 3.9)
2
2
  project(EAIK_Cpp)
3
3
 
4
- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../src/IK build_IK)
4
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/IK build_IK)
5
5
 
6
6
  if(NOT CMAKE_BUILD_TYPE)
7
7
  set(CMAKE_BUILD_TYPE Release)
@@ -9,8 +9,8 @@ endif()
9
9
  set(CMAKE_CXX_STANDARD 17)
10
10
  set(CMAKE_CXX_FLAGS_RELEASE "-O3")
11
11
 
12
- set(sources EAIK.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/kinematic_remodelling.cpp)
12
+ set(sources EAIK.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/kinematic_remodeling.cpp ${CMAKE_CURRENT_SOURCE_DIR}/IK/utils/kinematic_utils.cpp)
13
13
 
14
14
  add_library(EAIK ${sources})
15
- target_include_directories(EAIK PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/IK)
15
+ target_include_directories(EAIK PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/IK ${CMAKE_CURRENT_SOURCE_DIR}/IK/utils)
16
16
  target_link_libraries(EAIK EAIK_Kinematics)
@@ -0,0 +1,258 @@
1
+ #include <iostream>
2
+ #include <eigen3/Eigen/Dense>
3
+ #include <thread>
4
+ #include <mutex>
5
+
6
+ #include "kinematic_remodeling.h"
7
+ #include "kinematic_utils.h"
8
+ #include "EAIK.h"
9
+
10
+ namespace EAIK
11
+ {
12
+ Robot::Robot(const Eigen::VectorXd& dh_alpha, const Eigen::VectorXd& dh_a, const Eigen::VectorXd& dh_d, const Eigen::Matrix<double, 3, 3> &R6T, const std::vector<std::pair<int, double>>& fixed_axes, bool is_double_precision)
13
+ {
14
+ const auto&[H, P, R6T_dh] = IKS::dh_to_H_P(dh_alpha, dh_a, dh_d);
15
+ this->R6T = R6T*R6T_dh;
16
+ init(H, P, fixed_axes, is_double_precision);
17
+ }
18
+
19
+ Robot::Robot(const Eigen::MatrixXd &H, const Eigen::MatrixXd &P, const Eigen::Matrix<double, 3, 3> &R6T, const std::vector<std::pair<int, double>>& fixed_axes, bool is_double_precision) : R6T(R6T)
20
+ {
21
+ init(H, P, fixed_axes, is_double_precision);
22
+ }
23
+
24
+ void Robot::init(const Eigen::MatrixXd &H, const Eigen::MatrixXd &P, const std::vector<std::pair<int, double>>& fixed_axes, bool is_double_precision)
25
+ {
26
+ if(is_double_precision)
27
+ {
28
+ ZERO_THRESHOLD = 1e-13;
29
+ AXIS_INTERSECT_THRESHOLD = 1e-9;
30
+ }
31
+
32
+ if(P.cols() != H.cols() + 1)
33
+ {
34
+ throw std::runtime_error("Wrong input dimensions for H and P. Note that #P = #H+1.");
35
+ }
36
+
37
+ Eigen::MatrixXd P_remodeled;
38
+ Eigen::MatrixXd H_remodeled;
39
+
40
+ // Insert fixed axes (e.g., to cope with >6DOF manipulators) into the kinematic chain
41
+ if(fixed_axes.size()>0)
42
+ {
43
+ this->fixed_axes = fixed_axes;
44
+ std::sort(this->fixed_axes.begin(), this->fixed_axes.end(), [](const std::pair<int, double>& a, const std::pair<int, double>& b) {return a.first < b.first;});
45
+
46
+ const auto&[H_part, P_part, R6T_part] = partial_joint_parametrization(H, P, fixed_axes, R6T);
47
+ P_remodeled = remodel_kinematics(H_part, P_part, ZERO_THRESHOLD, AXIS_INTERSECT_THRESHOLD);
48
+ H_remodeled = H_part;
49
+ this->R6T_partial = R6T_part;
50
+ }
51
+ else
52
+ {
53
+ P_remodeled = remodel_kinematics(H, P, ZERO_THRESHOLD, AXIS_INTERSECT_THRESHOLD);
54
+ H_remodeled = H;
55
+ this->R6T_partial = R6T;
56
+ }
57
+ switch (H_remodeled.cols())
58
+ {
59
+ case 6:
60
+ bot_kinematics = std::make_unique<IKS::General_6R>(H_remodeled, P_remodeled);
61
+ original_kinematics = std::make_unique<IKS::General_Robot>(H, P);
62
+ break;
63
+
64
+ 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);
67
+ break;
68
+ default:
69
+ throw std::runtime_error("Currently, only 6R and 3R Robots are solvable with EAIK.");
70
+ break;
71
+ }
72
+ }
73
+
74
+
75
+ Eigen::MatrixXd Robot::get_remodeled_H() const
76
+ {
77
+ return bot_kinematics->get_H();
78
+ }
79
+
80
+ Eigen::MatrixXd Robot::get_remodeled_P() const
81
+ {
82
+ return bot_kinematics->get_P();
83
+ }
84
+
85
+ Eigen::MatrixXd Robot::get_original_H() const
86
+ {
87
+ return original_kinematics->get_H();
88
+ }
89
+
90
+ Eigen::MatrixXd Robot::get_original_P() const
91
+ {
92
+ return original_kinematics->get_P();
93
+ }
94
+
95
+ std::string Robot::get_kinematic_family() const
96
+ {
97
+ return bot_kinematics->get_kinematic_family();
98
+ }
99
+
100
+ IKS::IK_Solution Robot::calculate_IK(const IKS::Homogeneous_T &ee_position_orientation) const
101
+ {
102
+ if (!bot_kinematics->has_known_decomposition())
103
+ {
104
+ throw std::runtime_error(decomposition_unknown_exception_info);
105
+ }
106
+
107
+ IKS::Homogeneous_T ee_06_rot = ee_position_orientation;
108
+ ee_06_rot.block<3,3>(0,0) *= R6T_partial.transpose();
109
+ IKS::IK_Solution solution = bot_kinematics->calculate_IK(ee_06_rot);
110
+
111
+ // TODO: Find alternative to this costly insert operation
112
+ for(const auto&[joint_index, value] : fixed_axes)
113
+ {
114
+ for(auto& Qs : solution.Q)
115
+ {
116
+ Qs.insert(Qs.begin()+joint_index, value);
117
+ }
118
+ }
119
+
120
+ return solution;
121
+ }
122
+
123
+ std::vector<IKS::IK_Solution> Robot::calculate_IK_batched(std::vector<IKS::Homogeneous_T> EE_pose_batch, const unsigned worker_threads) const
124
+ {
125
+ if (!bot_kinematics->has_known_decomposition())
126
+ {
127
+ throw std::runtime_error(decomposition_unknown_exception_info);
128
+ }
129
+
130
+ std::vector<IKS::IK_Solution> solutions(EE_pose_batch.size());
131
+ std::vector<std::thread> thread_pool;
132
+ thread_pool.reserve(worker_threads);
133
+
134
+ std::mutex pose_mutex;
135
+
136
+ auto kernel =
137
+ [this, &pose_mutex, &solutions, &EE_pose_batch]{
138
+ pose_mutex.lock();
139
+ int solution_index = EE_pose_batch.size()-1;
140
+
141
+ while(solution_index >= 0)
142
+ {
143
+ const IKS::Homogeneous_T& pose = EE_pose_batch.back();
144
+ EE_pose_batch.pop_back();
145
+ pose_mutex.unlock();
146
+
147
+ solutions.at(solution_index) = calculate_IK(pose);
148
+
149
+ pose_mutex.lock();
150
+ solution_index = EE_pose_batch.size()-1;
151
+ }
152
+ pose_mutex.unlock();
153
+ };
154
+
155
+ for(unsigned i = 0; i < worker_threads; i++)
156
+ {
157
+ thread_pool.push_back(std::thread(kernel));
158
+ }
159
+
160
+ for(unsigned i = 0; i < worker_threads; i++)
161
+ {
162
+ thread_pool.at(i).join();
163
+ }
164
+
165
+ return solutions;
166
+ }
167
+
168
+ // Eigen matrices as solutions for the pybindings
169
+ IKS::IK_Eigen_Solution Robot::calculate_Eigen_IK(const IKS::Homogeneous_T &ee_position_orientation) const
170
+ {
171
+ IKS::IK_Solution vector_solution = calculate_IK(ee_position_orientation);
172
+ IKS::IK_Eigen_Solution eigen_solution;
173
+
174
+ if(vector_solution.Q.size() > 0)
175
+ {
176
+ eigen_solution.is_LS_vec.resize(vector_solution.is_LS_vec.size());
177
+
178
+ // TODO: Find a way to avoid slow loop in future releases
179
+ for(unsigned i = 0; i < vector_solution.is_LS_vec.size(); ++i)
180
+ {
181
+ eigen_solution.is_LS_vec(i) = vector_solution.is_LS_vec.at(i);
182
+ }
183
+
184
+ eigen_solution.Q = Eigen::MatrixXd(vector_solution.Q.size(), vector_solution.Q.at(0).size());
185
+ for (unsigned i = 0; i < vector_solution.Q.size(); i++)
186
+ {
187
+ eigen_solution.Q.row(i) = Eigen::VectorXd::Map(vector_solution.Q.at(i).data(),vector_solution.Q.at(0).size());
188
+ }
189
+ }
190
+ return eigen_solution;
191
+ }
192
+
193
+ std::vector<IKS::IK_Eigen_Solution> Robot::calculate_Eigen_IK_batched(std::vector<IKS::Homogeneous_T> EE_pose_batch, const unsigned worker_threads) const
194
+ {
195
+ std::vector<IKS::IK_Eigen_Solution> solutions(EE_pose_batch.size());
196
+ std::vector<std::thread> thread_pool;
197
+ thread_pool.reserve(worker_threads);
198
+
199
+ std::mutex pose_mutex;
200
+
201
+ auto kernel =
202
+ [this, &pose_mutex, &solutions, &EE_pose_batch]{
203
+ pose_mutex.lock();
204
+ int solution_index = EE_pose_batch.size()-1;
205
+
206
+ while(solution_index >= 0)
207
+ {
208
+ const IKS::Homogeneous_T& pose = EE_pose_batch.back();
209
+ EE_pose_batch.pop_back();
210
+ pose_mutex.unlock();
211
+
212
+ solutions.at(solution_index) = calculate_Eigen_IK(pose);
213
+
214
+ pose_mutex.lock();
215
+ solution_index = EE_pose_batch.size()-1;
216
+ }
217
+ pose_mutex.unlock();
218
+ };
219
+
220
+ for(unsigned i = 0; i < worker_threads; i++)
221
+ {
222
+ thread_pool.push_back(std::thread(kernel));
223
+ }
224
+
225
+ for(unsigned i = 0; i < worker_threads; i++)
226
+ {
227
+ thread_pool.at(i).join();
228
+ }
229
+
230
+ return solutions;
231
+ }
232
+
233
+ IKS::Homogeneous_T Robot::fwdkin(const std::vector<double> &Q) const
234
+ {
235
+ IKS::Homogeneous_T sol_r06 = original_kinematics->fwdkin(Q);
236
+ sol_r06.block<3,3>(0,0) *= R6T;
237
+ return sol_r06;
238
+ }
239
+
240
+ // Function for use with pybindings and the corresponding numpy arrays
241
+ IKS::Homogeneous_T Robot::fwdkin_Eigen(const Eigen::VectorXd &Q) const
242
+ {
243
+ std::vector<double> Q_stdVector(Q.data(), Q.data() + Q.size());
244
+ IKS::Homogeneous_T sol_r06 = original_kinematics->fwdkin(Q_stdVector);
245
+ sol_r06.block<3,3>(0,0) *= R6T;
246
+ return sol_r06;
247
+ }
248
+
249
+ bool Robot::is_spherical() const
250
+ {
251
+ return this->bot_kinematics->is_spherical();
252
+ }
253
+
254
+ bool Robot::has_known_decomposition() const
255
+ {
256
+ return this->bot_kinematics->has_known_decomposition();
257
+ }
258
+ } // namespace EAIK
@@ -0,0 +1,58 @@
1
+ #ifndef EAIK_IMPL
2
+ #define EAIK_IMPL
3
+
4
+ #include <eigen3/Eigen/Dense>
5
+ #include <memory>
6
+ #include <tuple>
7
+
8
+ #include "IKS.h"
9
+
10
+ namespace EAIK
11
+ {
12
+ class Robot
13
+ {
14
+ public:
15
+ Robot(const Eigen::MatrixXd &H, const Eigen::MatrixXd &P, const Eigen::Matrix<double, 3, 3> &R6T=Eigen::Matrix<double, 3, 3>::Identity(), const std::vector<std::pair<int, double>>& fixed_axes={}, bool is_double_precision=true);
16
+ Robot(const Eigen::VectorXd& dh_alpha, const Eigen::VectorXd& dh_a, const Eigen::VectorXd& dh_d, const Eigen::Matrix<double, 3, 3> &R6T=Eigen::Matrix<double, 3, 3>::Identity(), const std::vector<std::pair<int, double>>& fixed_axes={}, bool is_double_precision=true);
17
+
18
+ IKS::IK_Solution calculate_IK(const IKS::Homogeneous_T &ee_position_orientation) const;
19
+ std::vector<IKS::IK_Solution> calculate_IK_batched(std::vector<IKS::Homogeneous_T> EE_pose_batch, const unsigned worker_threads) const;
20
+
21
+ IKS::IK_Eigen_Solution calculate_Eigen_IK(const IKS::Homogeneous_T &ee_position_orientation) const;
22
+ std::vector<IKS::IK_Eigen_Solution> calculate_Eigen_IK_batched(std::vector<IKS::Homogeneous_T> EE_pose_batch, const unsigned worker_threads) const;
23
+
24
+ IKS::Homogeneous_T fwdkin(const std::vector<double> &Q) const;
25
+ IKS::Homogeneous_T fwdkin_Eigen(const Eigen::VectorXd &Q) const;
26
+
27
+ bool is_spherical() const;
28
+ bool has_known_decomposition() const;
29
+
30
+ Eigen::MatrixXd get_remodeled_H() const;
31
+ Eigen::MatrixXd get_remodeled_P() const;
32
+
33
+ Eigen::MatrixXd get_original_H() const;
34
+ Eigen::MatrixXd get_original_P() const;
35
+
36
+ std::string get_kinematic_family() const;
37
+
38
+ private:
39
+ // Init function to allow nice constructor overloading
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);
41
+ std::unique_ptr<IKS::General_Robot> bot_kinematics;
42
+ std::unique_ptr<IKS::General_Robot> original_kinematics; // Forward Kinematics calculated by original kinematics
43
+
44
+ bool spherical_wrist{false};
45
+
46
+ // single precision default thresholds
47
+ double ZERO_THRESHOLD = 1e-7;
48
+ double AXIS_INTERSECT_THRESHOLD = 1e-6;
49
+
50
+ Eigen::Matrix<double, 3, 3> R6T;
51
+ Eigen::Matrix<double, 3, 3> R6T_partial;
52
+ std::vector<std::pair<int, double>> fixed_axes; // Locked axes sorted by rising indices (!)
53
+
54
+ const std::string decomposition_unknown_exception_info{"No Subproblem-Decomposition for the given manipulator is currently known! Analytical IK computation is not possible."};
55
+
56
+ };
57
+ } // namespace EAIK
58
+ #endif