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.
- {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/sp.cpp +124 -65
- {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/sp.h +1 -1
- {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/src/CMakeLists.txt +3 -3
- eaik-1.1.0/CPP/src/EAIK.cpp +258 -0
- eaik-1.1.0/CPP/src/EAIK.h +58 -0
- eaik-1.1.0/CPP/src/IK/3R_IK.cpp +148 -0
- eaik-1.1.0/CPP/src/IK/6R_IK.cpp +646 -0
- {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/src/IK/CMakeLists.txt +2 -2
- eaik-1.1.0/CPP/src/IK/IKS.h +101 -0
- eaik-1.1.0/CPP/src/IK/utils/kinematic_utils.cpp +64 -0
- eaik-1.1.0/CPP/src/IK/utils/kinematic_utils.h +13 -0
- eaik-1.1.0/CPP/src/utils/kinematic_remodeling.cpp +165 -0
- eaik-1.1.0/CPP/src/utils/kinematic_remodeling.h +20 -0
- eaik-1.1.0/MANIFEST.in +6 -0
- eaik-1.1.0/PKG-INFO +811 -0
- eaik-1.1.0/README.md +116 -0
- {eaik-0.0.1 → eaik-1.1.0}/pyproject.toml +5 -5
- eaik-1.1.0/setup.py +22 -0
- eaik-1.1.0/src/EAIK.egg-info/PKG-INFO +811 -0
- eaik-1.1.0/src/EAIK.egg-info/SOURCES.txt +30 -0
- eaik-1.1.0/src/eaik/IK_DH.py +26 -0
- eaik-1.1.0/src/eaik/IK_Homogeneous.py +42 -0
- eaik-1.1.0/src/eaik/IK_Robot.py +79 -0
- eaik-1.1.0/src/eaik/IK_URDF.py +40 -0
- eaik-1.1.0/src/eaik/pybindings/eaik_pybindings.cpp +86 -0
- eaik-0.0.1/MANIFEST.in +0 -1
- eaik-0.0.1/PKG-INFO +0 -108
- eaik-0.0.1/README.md +0 -89
- eaik-0.0.1/external/EAIK/.git +0 -1
- eaik-0.0.1/external/EAIK/.gitignore +0 -8
- eaik-0.0.1/external/EAIK/.gitmodules +0 -3
- eaik-0.0.1/external/EAIK/README.md +0 -2
- eaik-0.0.1/external/EAIK/Tests/CMakeLists.txt +0 -24
- eaik-0.0.1/external/EAIK/Tests/IK_system_tests.cpp +0 -252
- eaik-0.0.1/external/EAIK/Tests/IK_unit_tests.cpp +0 -369
- eaik-0.0.1/external/EAIK/external/ik-geo/.git +0 -1
- eaik-0.0.1/external/EAIK/external/ik-geo/.gitignore +0 -43
- eaik-0.0.1/external/EAIK/external/ik-geo/LICENSE +0 -29
- eaik-0.0.1/external/EAIK/external/ik-geo/README.md +0 -60
- eaik-0.0.1/external/EAIK/src/EAIK.cpp +0 -50
- eaik-0.0.1/external/EAIK/src/EAIK.h +0 -28
- eaik-0.0.1/external/EAIK/src/IK/General_IK.cpp +0 -155
- eaik-0.0.1/external/EAIK/src/IK/IKS.h +0 -41
- eaik-0.0.1/external/EAIK/src/IK/Spherical_IK.cpp +0 -261
- eaik-0.0.1/external/EAIK/src/utils/kinematic_remodelling.cpp +0 -105
- eaik-0.0.1/external/EAIK/src/utils/kinematic_remodelling.h +0 -16
- eaik-0.0.1/setup.py +0 -20
- eaik-0.0.1/src/EAIK.egg-info/PKG-INFO +0 -108
- eaik-0.0.1/src/EAIK.egg-info/SOURCES.txt +0 -44
- eaik-0.0.1/src/eaik/IK_CSV.py +0 -97
- eaik-0.0.1/src/eaik/IK_URDF.py +0 -47
- eaik-0.0.1/src/eaik/cpp/eaik_pybindings.cpp +0 -56
- eaik-0.0.1/src/eaik/examples/evaluate_ik.py +0 -49
- eaik-0.0.1/src/eaik/examples/load_urdf.py +0 -52
- eaik-0.0.1/src/eaik/examples/robot_csv.py +0 -79
- eaik-0.0.1/tests/test_axis_convention_trafo.py +0 -115
- eaik-0.0.1/tests/test_sphericalwrist_first_intersection.py +0 -123
- eaik-0.0.1/tests/test_sphericalwrist_generation.py +0 -28
- eaik-0.0.1/tests/test_sphericalwrist_second_intersection.py +0 -199
- {eaik-0.0.1/external/EAIK → eaik-1.1.0/CPP}/external/ik-geo/cpp/subproblems/CMakeLists.txt +0 -0
- {eaik-0.0.1/external/EAIK → eaik-1.1.0}/LICENSE +0 -0
- {eaik-0.0.1 → eaik-1.1.0}/setup.cfg +0 -0
- {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/dependency_links.txt +0 -0
- {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/requires.txt +0 -0
- {eaik-0.0.1 → eaik-1.1.0}/src/EAIK.egg-info/top_level.txt +0 -0
- {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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
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 =
|
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 <<
|
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
|
-
|
243
|
-
|
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
|
472
|
+
if (norm_a_sq - b * b > 0)
|
502
473
|
{
|
503
|
-
const double xi = std::sqrt(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
897
|
-
|
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-
|
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}
|
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/
|
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
|