LCOV - code coverage report
Current view: top level - src/Kinematics - Kinematics.cpp (source / functions) Hit Total Coverage
Test: HLR Lines: 121 122 99.2 %
Date: 2018-01-09 14:49:59 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "Kinematics.hpp"
       2             : 
       3             : #include <iostream>
       4             : 
       5             : namespace HLR
       6             : {
       7             : namespace Kinematics
       8             : {
       9             : using IKSolution = std::pair<std::optional<Matrix<double, 4, 1>>,
      10             :                              std::optional<Matrix<double, 4, 1>>>;
      11             : 
      12      330314 : std::optional<Matrix<double, 3, 4>> Kinematics::forward(
      13             :     Matrix<double, 4, 1> angles)
      14             : {
      15             :     // Set theta 3 always be horizontal.
      16      330314 :     angles[3][0] = 90 - angles[1][0] - angles[2][0];
      17             : 
      18      330314 :     if (!Kinematics::angles_within_limits(angles))
      19             :     {
      20       12316 :         return std::nullopt;
      21             :     }
      22             : 
      23             :     // Convert angles to radians.
      24      317998 :     angles *= (pi / 180);
      25             : 
      26             :     /**
      27             :      * keep names short because the formules below are unreadable with long
      28             :      * variable names.
      29             :      * p are the end positions of each link.
      30             :      * a are the thetas in radians
      31             :      * l are the joint lengths
      32             :      */
      33      317998 :     Matrix<double, 3, 4> p;
      34      317998 :     const auto& a = angles;
      35      317998 :     const auto& l = joint_lengths;
      36             : 
      37      317998 :     p[0][0] = 0.0;
      38      317998 :     p[1][0] = 0.0;
      39      317998 :     p[2][0] = 0.0;
      40             : 
      41      317998 :     p[0][1] = p[0][0] + (l[0][0] * std::sin(a[1][0]) * std::cos(a[0][0]));
      42      317998 :     p[1][1] = p[1][0] + (l[0][0] * std::cos(a[1][0]));
      43      317998 :     p[2][1] = p[2][0] + (l[0][0] * std::sin(a[1][0]) * std::sin(a[0][0]));
      44             : 
      45      317998 :     p[0][2] =
      46      317998 :         p[0][1] + (l[1][0] * std::sin(a[1][0] + a[2][0]) * std::cos(a[0][0]));
      47      317998 :     p[1][2] = p[1][1] + (l[1][0] * std::cos(a[1][0] + a[2][0]));
      48      317998 :     p[2][2] =
      49      317998 :         p[2][1] + (l[1][0] * std::sin(a[1][0] + a[2][0]) * std::sin(a[0][0]));
      50             : 
      51      317998 :     p[0][3] = p[0][2] + (l[2][0] * std::cos(a[0][0]));
      52      317998 :     p[1][3] = p[1][2];
      53      317998 :     p[2][3] = p[2][2] + (l[2][0] * std::sin(a[0][0]));
      54             : 
      55      317998 :     return p;
      56             : }
      57             : 
      58           9 : IKSolution Kinematics::jacobian_inverse(Matrix<double, 4, 1> current_angles,
      59             :                                         const Matrix<double, 3, 1>& goal)
      60             : {
      61           9 :     IKSolution solutions;
      62             : 
      63             :     // alias variables to the same names they have in the documentation.
      64           9 :     const auto& g = goal;
      65           9 :     auto new_angles = current_angles;
      66           9 :     double beta = 0.1;
      67             : 
      68           9 :     std::size_t i = 0;
      69           9 :     auto e_opt = forward(new_angles);
      70           9 :     auto e_opt_new = e_opt;
      71           9 :     if (!e_opt.has_value())
      72             :     {
      73           1 :         return solutions;
      74             :     }
      75           8 :     auto e = e_opt.value().template slice<0, 3, 3, 1>();
      76           8 :     auto e_new = e;
      77             : 
      78      660594 :     while ((g - e).get_magnitude() > precision && ++i <= max_iterations)
      79             :     {
      80      330293 :         const auto j_i_opt = compute_jacobian(current_angles).inverse();
      81      330293 :         if (!j_i_opt.has_value())
      82             :         {
      83           0 :             break;
      84             :         }
      85             : 
      86      330293 :         const auto& j_i = j_i_opt.value();
      87      330293 :         const auto d_e = beta * (g - e);
      88      330293 :         const auto d_angle = j_i * d_e;
      89      330293 :         new_angles = current_angles + d_angle.template slice<0, 0, 4, 1>();
      90      330293 :         new_angles[3][0] = (pi / 2.0) - new_angles[1][0] - new_angles[2][0];
      91      330293 :         e_opt_new = forward(new_angles);
      92             : 
      93      342605 :         if (!e_opt_new.has_value())
      94             :         {
      95       12312 :             beta /= 2.0;
      96       12312 :             continue;
      97             :         }
      98             : 
      99      317981 :         e_new = e_opt_new.value().template slice<0, 3, 3, 1>();
     100             : 
     101      317981 :         if ((e_new - e + d_e).get_magnitude() > deviation)
     102             :         {
     103       56555 :             beta /= 2.0;
     104             :         }
     105             :         else
     106             :         {
     107      261426 :             beta *= 1.2;
     108      261426 :             e = e_new;
     109      261426 :             current_angles = new_angles;
     110             :         }
     111             :     }
     112             : 
     113           8 :     if ((g - e).get_magnitude() <= precision)
     114             :     {
     115           3 :         solutions.first = new_angles;
     116             :     }
     117             : 
     118           8 :     return solutions;
     119             : }
     120             : 
     121           8 : IKSolution Kinematics::analytical_inverse(const Matrix<double, 3, 1>& goal)
     122             : {
     123           8 :     IKSolution solutions;
     124             : 
     125             :     // Check if solution is in worksapce.
     126             :     static const double max_arm_length =
     127             :         joint_lengths[0][0] + joint_lengths[1][0] + joint_lengths[2][0];
     128           8 :     if (max_arm_length - goal.get_magnitude() <= 0)
     129             :     {
     130           3 :         return solutions;
     131             :     }
     132             : 
     133           5 :     solutions.first = Matrix<double, 4, 1>();
     134           5 :     solutions.second = Matrix<double, 4, 1>();
     135             : 
     136           5 :     const auto& l = joint_lengths;
     137             : 
     138           5 :     auto& s_1 = solutions.first.value();
     139           5 :     auto& s_2 = solutions.second.value();
     140             : 
     141           5 :     double theta_3 = std::atan2(goal[2][0], goal[0][0]);
     142           5 :     double x = std::hypot(goal[2][0], goal[0][0]) - l[2][0];
     143           5 :     double y = goal[1][0];
     144             : 
     145             :     double cos_theta_2 =
     146           5 :         ((x * x) + (y * y) - (l[0][0] * l[0][0]) - (l[1][0] * l[1][0]))
     147           5 :         / (2 * l[0][0] * l[1][0]);
     148             : 
     149           5 :     s_1[2][0] =
     150           5 :         std::atan2(std::sqrt(1 - cos_theta_2 * cos_theta_2), cos_theta_2);
     151           5 :     s_2[2][0] =
     152           5 :         std::atan2(-std::sqrt(1 - cos_theta_2 * cos_theta_2), cos_theta_2);
     153             : 
     154          10 :     s_1[1][0] = std::atan2(x, y)
     155          10 :                 - std::atan2(l[1][0] * std::sin(s_1[2][0]),
     156          10 :                              l[0][0] + l[1][0] * std::cos(s_1[2][0]));
     157          10 :     s_2[1][0] = std::atan2(x, y)
     158          10 :                 - std::atan2(l[1][0] * std::sin(s_2[2][0]),
     159          10 :                              l[0][0] + l[1][0] * std::cos(s_2[2][0]));
     160             : 
     161           5 :     s_1[3][0] = (pi / 2.0) - s_1[1][0] - s_1[2][0];
     162           5 :     s_2[3][0] = (pi / 2.0) - s_2[1][0] - s_2[2][0];
     163             : 
     164           5 :     s_1[0][0] = theta_3;
     165           5 :     s_2[0][0] = theta_3;
     166             : 
     167           5 :     s_1 *= (180.0 / pi);
     168           5 :     s_2 *= (180.0 / pi);
     169             : 
     170           5 :     if (!angles_within_limits(solutions.first.value()))
     171             :     {
     172           2 :         solutions.first = std::nullopt;
     173             :     }
     174             : 
     175           5 :     if (!angles_within_limits(solutions.second.value()))
     176             :     {
     177           5 :         solutions.second = std::nullopt;
     178             :     }
     179             : 
     180           5 :     return solutions;
     181             : }
     182             : 
     183      330293 : Matrix<double, 3, 3> Kinematics::compute_jacobian(
     184             :     const Matrix<double, 4, 1>& current_angles)
     185             : {
     186             :     // alias current angles to a and joint lengths l.
     187      330293 :     auto a = current_angles * (pi / 180);
     188      330293 :     const auto& l = joint_lengths;
     189      330293 :     Matrix<double, 3, 3> jacobian;
     190             : 
     191      330293 :     jacobian[0][0] =
     192      330293 :         (l[0][0] * std::sin(a[1][0]) + l[1][0] * std::sin(a[1][0] + a[2][0]))
     193      330293 :         * -std::sin(a[0][0]);
     194      330293 :     jacobian[0][1] =
     195      330293 :         (l[0][0] * std::cos(a[1][0]) + l[1][0] * std::cos(a[1][0] + a[2][0]))
     196      330293 :         * std::cos(a[0][0]);
     197      330293 :     jacobian[0][2] = l[1][0] * std::cos(a[1][0] + a[2][0]) * std::cos(a[0][0]);
     198             : 
     199      330293 :     jacobian[1][0] = 0;
     200      330293 :     jacobian[1][1] =
     201      330293 :         -l[0][0] * std::sin(a[1][0]) - l[1][0] * std::sin(a[1][0] + a[2][0]);
     202      330293 :     jacobian[1][2] = -l[1][0] * std::sin(a[1][0] + a[2][0]);
     203             : 
     204      330293 :     jacobian[2][0] =
     205      330293 :         (l[0][0] * std::sin(a[1][0]) + l[1][0] * std::sin(a[1][0] + a[2][0]))
     206      330293 :         * std::cos(a[0][0]);
     207      330293 :     jacobian[2][1] =
     208      330293 :         (l[0][0] * std::cos(a[1][0]) + l[1][0] * std::cos(a[1][0] + a[2][0]))
     209      330293 :         * std::sin(a[0][0]);
     210      330293 :     jacobian[2][2] = l[1][0] * std::cos(a[1][0] + a[2][0]) * std::sin(a[0][0]);
     211             : 
     212      330293 :     return jacobian;
     213             : }
     214             : 
     215      330324 : bool Kinematics::angles_within_limits(const Matrix<double, 4, 1>& angles)
     216             : {
     217     1627112 :     for (std::size_t i = 0; i < angles.get_m(); ++i)
     218             :     {
     219     2618222 :         if (servo_limits[i][0] > angles[i][0]
     220     1309111 :             || servo_limits[i][1] < angles[i][0])
     221             :         {
     222       12323 :             return false;
     223             :         }
     224             :     }
     225             : 
     226      318001 :     return true;
     227             : }
     228             : 
     229             : } // namespace Kinematics
     230           3 : } // namespace HLR

Generated by: LCOV version 1.12