kinecalc.cpp
Go to the documentation of this file.
00001 /*
00002  *  P2OS for ROS
00003  *  Copyright (C) 2015  David Feil-Seifer, Brian Gerkey, Kasper Stoy,
00004  *     Richard Vaughan, & Andrew Howard
00005  *  Copyright (C) 2018  Hunter L. Allen
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  */
00022 #include <stdio.h>
00023 #include <math.h>
00024 
00025 #include <p2os_driver/kinecalc.hpp>
00026 
00027 KineCalc::KineCalc(void)
00028 {
00029   link1 = 0.06875f;
00030   link2 = 0.16f;
00031   link3 = 0.0f;
00032   link4 = 0.13775f;
00033   link5 = 0.11321f;
00034 
00035   endEffector.p.x = 0.0f; endEffector.p.y = 0.0f; endEffector.p.z = 0.0f;
00036   endEffector.n.x = 0.0f; endEffector.n.y = 0.0f; endEffector.n.z = 0.0f;
00037   endEffector.o.x = 0.0f; endEffector.o.y = -1.0f; endEffector.o.z = 1.0f;
00038   endEffector.a.x = 1.0f; endEffector.a.y = 0.0f; endEffector.a.z = 0.0f;
00039 
00040   for (int ii = 0; ii < 5; ii++) {
00041     joints[ii] = 0.0f;
00042     jointOffsets[ii] = 0.0f;
00043     jointMin[ii] = 0.0f;
00044     jointMax[ii] = 0.0f;
00045   }
00046 }
00047 
00048 
00050 //  Accessor functions
00052 
00053 void KineCalc::SetP(double newPX, double newPY, double newPZ)
00054 {
00055   endEffector.p.x = newPX;
00056   endEffector.p.y = newPY;
00057   endEffector.p.z = newPZ;
00058 }
00059 
00060 void KineCalc::SetN(double newNX, double newNY, double newNZ)
00061 {
00062   endEffector.n.x = newNX;
00063   endEffector.n.y = newNY;
00064   endEffector.n.z = newNZ;
00065 }
00066 
00067 void KineCalc::SetO(double newOX, double newOY, double newOZ)
00068 {
00069   endEffector.o.x = newOX;
00070   endEffector.o.y = newOY;
00071   endEffector.o.z = newOZ;
00072 }
00073 
00074 void KineCalc::SetA(double newAX, double newAY, double newAZ)
00075 {
00076   endEffector.a.x = newAX;
00077   endEffector.a.y = newAY;
00078   endEffector.a.z = newAZ;
00079 }
00080 
00081 double KineCalc::GetTheta(unsigned int index)
00082 {
00083   return joints[index];
00084 }
00085 
00086 void KineCalc::SetTheta(unsigned int index, double newVal)
00087 {
00088   joints[index] = newVal;
00089 }
00090 
00091 void KineCalc::SetLinkLengths(
00092   double newLink1, double newLink2, double newLink3, double newLink4,
00093   double newLink5)
00094 {
00095   link1 = newLink1;
00096   link2 = newLink2;
00097   link3 = newLink3;
00098   link4 = newLink4;
00099   link5 = newLink5;
00100 }
00101 
00102 void KineCalc::SetOffset(unsigned int joint, double newOffset)
00103 {
00104   jointOffsets[joint] = newOffset;
00105 }
00106 
00107 void KineCalc::SetJointRange(unsigned int joint, double min, double max)
00108 {
00109   jointMin[joint] = fmin(min, max);         // So that if min > max we reverse them
00110   jointMax[joint] = fmax(min, max);
00111 }
00112 
00113 
00115 //  Utility helper functions
00117 
00118 KineVector KineCalc::Normalise(const KineVector & vector)
00119 {
00120   KineVector result;
00121   double length = sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
00122   if (length != 0) {
00123     result.x = vector.x / length;
00124     result.y = vector.y / length;
00125     result.z = vector.z / length;
00126   } else {
00127     printf("P2OS: Tried to normalise a vector of zero length.\n");
00128     result.x = 0;
00129     result.y = 0;
00130     result.z = 0;
00131   }
00132   return result;
00133 }
00134 
00135 KineVector KineCalc::CalculateN(const EndEffector & pose)
00136 {
00137   KineVector result;
00138   result.x = pose.o.y * pose.a.z - pose.a.y * pose.o.z;
00139   result.y = pose.o.z * pose.a.x - pose.a.z * pose.o.x;
00140   result.z = pose.o.x * pose.a.y - pose.a.x * pose.o.y;
00141   if (result.x == 0 && result.y == 0 && result.z == 0) {
00142     printf(
00143       "P2OS: Approach and orientation cannot be the same vector"
00144       "- their cross product cannot be zero.\n");
00145     // Ensures that a different orientation vector is created
00146     KineVector orient;
00147     if (pose.a.y == 0 && pose.a.z == 0) {
00148       orient.x = 0;
00149       orient.y = 1;
00150       orient.z = 0;
00151     } else {
00152       orient.x = 1;
00153       orient.y = 0;
00154       orient.z = 0;
00155     }
00156     result.x = orient.y * pose.a.z - pose.a.y * orient.z;
00157     result.y = orient.z * pose.a.x - pose.a.z * orient.x;
00158     result.z = orient.x * pose.a.y - pose.a.x * orient.y;
00159   }
00160   return Normalise(result);
00161 }
00162 
00163 void KineCalc::PrintEndEffector(const EndEffector & endEffector)
00164 {
00165   printf("P: (%f, %f, %f)\tA: (%f, %f, %f)\tO: (%f, %f, %f)\tN: (%f, %f, %f)\n",
00166     endEffector.p.x, endEffector.p.y, endEffector.p.z,
00167     endEffector.a.x, endEffector.a.y, endEffector.a.z,
00168     endEffector.o.x, endEffector.o.y, endEffector.o.z,
00169     endEffector.n.x, endEffector.n.y, endEffector.n.z);
00170 }
00171 
00172 
00174 //  The important functions
00176 
00177 //  Calculate the forward kinematics
00178 //  The result is stored in endEffector
00179 //  fromJoints[]:   An array of 5 joint values
00180 void KineCalc::CalculateFK(const double fromJoints[])
00181 {
00182   double adjustedJoints[5];
00183 
00184   adjustedJoints[0] = (fromJoints[0] - jointOffsets[0]) * -1;
00185   adjustedJoints[1] = fromJoints[1] - jointOffsets[1];
00186   adjustedJoints[2] = fromJoints[2] - jointOffsets[2];
00187   adjustedJoints[3] = (fromJoints[3] - jointOffsets[3]) * -1;
00188   adjustedJoints[4] = (fromJoints[4] - jointOffsets[4]) * -1;
00189 
00190   endEffector = CalcFKForJoints(adjustedJoints);
00191 //  printf ("Result of FK:\n");
00192 //  PrintEndEffector (endEffector);
00193 }
00194 
00195 //  Calculate the inverse kinematics
00196 //  The result is stored in joints
00197 //  fromPosition:   An EndEffector structure describing the pose
00198 //                  of the end effector
00199 bool KineCalc::CalculateIK(const EndEffector & fromPosition)
00200 {
00201   // Some references to make the code neater
00202   const KineVector & p = fromPosition.p;
00203   const KineVector & a = fromPosition.a;
00204   // These are the four possible solutions to the IK
00205   // solution1 = {1a, 2a, 3a, 4a, 5a}
00206   // solution2 = {1a, 2b, 3b, 4b, 5b}
00207   // solution3 = {1b, 2c, 3c, 4c, 5c}
00208   // solution4 = {1b, 2d, 3d, 4d, 5d}
00209   double solutions[4][5];
00210   double temp = 0.0f;
00211 
00212   // First calculate the two possible values for theta1, theta1a and theta1b
00213   temp = atan2(p.y - link5 * a.y, p.x - link5 * a.x);
00214   solutions[0][0] = solutions[1][0] = temp;
00215   temp = atan2(link5 * a.y - p.y, link5 * a.x - p.x);
00216   solutions[2][0] = solutions[3][0] = temp;
00217 
00218   // Next, using theta1_a, calculate thetas 2 and 3 (a and b)
00219   // First up is calculating r and rz
00220   double r = 0.0f, rz = 0.0f;
00221   if (sin(solutions[0][0]) < 0.1f && sin(solutions[0][0]) > -0.1f) {
00222     r = ((p.x - (link5 * a.x)) / cos(solutions[0][0])) - link1;
00223   } else {
00224     r = ((p.y - (link5 * a.y)) / sin(solutions[0][0])) - link1;
00225   }
00226   rz = p.z - (link5 * a.z);
00227   // Then calculate theta2a and 3a
00228   temp = (r * r + rz * rz + link2 * link2 - link4 * link4) / (2 * link2 * sqrt(r * r + rz * rz));
00229   temp = fmin(fmax(temp, -1.0f), 1.0f);
00230   temp = atan2(rz, r) - acos(temp);
00231   int m1 = -1;
00232   do {
00233     if (m1 > 1) {
00234       printf("m1 > 1!\n");
00235       break;
00236     }
00237     solutions[0][1] = temp + 2 * m1 * M_PI;
00238     m1 += 1;  // So that within the 3 iterations we get m1 = -1, 0, 1
00239   } while ((solutions[0][1] < -(M_PI) || solutions[0][1] > M_PI));  // && m1 < 1);
00240   // Put a catchall here to prevent infinite loops by checking if m1 has gone past 1
00241   temp = (link2 * link2 + link4 * link4 - r * r - rz * rz) / (2 * link2 * link4);
00242   temp = fmin(fmax(temp, -1.0f), 1.0f);
00243   solutions[0][2] = M_PI - acos(temp);
00244   // Followed by theta2b and 3b
00245   temp = (r * r + rz * rz + link2 * link2 - link4 * link4) / (2 * link2 * sqrt(r * r + rz * rz));
00246   temp = fmin(fmax(temp, -1.0f), 1.0f);
00247   temp = atan2(rz, r) + acos(temp);
00248   m1 = -1;
00249   do {
00250     if (m1 > 1) {
00251       break;
00252     }
00253     solutions[1][1] = temp + 2 * m1 * M_PI;
00254     m1 += 1;              // So that within the 3 iterations we get m1 = -1, 0, 1
00255   } while ((solutions[1][1] < -(M_PI) || solutions[1][1] > M_PI));    // && m1 < 1);
00256   temp = (link2 * link2 + link4 * link4 - r * r - rz * rz) / (2 * link2 * link4);
00257   temp = fmin(fmax(temp, -1.0f), 1.0f);
00258   solutions[1][2] = -(M_PI) + acos(temp);
00259 
00260   // Using theta2a and 3a, calculate 4a and 5a to complete solution1
00261   CalcTheta4and5(solutions[0], fromPosition);
00262   // Using theta2b and 3b, calculate 4b and 5b to complete solution2
00263   CalcTheta4and5(solutions[1], fromPosition);
00264 
00265   // That's two of the possible solutions. To get the other two, repeat with theta1b
00266   // First up is calculating r and rz
00267   r = 0.0f;
00268   rz = 0.0f;
00269   if (sin(solutions[2][0]) < 0.1f && sin(solutions[2][0]) > -0.1f) {
00270     r = (p.x - link5 * a.x) / cos(solutions[2][0]) - link1;
00271   } else {
00272     r = (p.y - link5 * a.y) / sin(solutions[2][0]) - link1;
00273   }
00274   rz = p.z - (link5 * a.z);
00275   // Then calculate theta2c and 3c
00276   temp = (r * r + rz * rz + link2 * link2 - link4 * link4) / (2 * link2 * sqrt(r * r + rz * rz));
00277   temp = fmin(fmax(temp, -1.0f), 1.0f);
00278   temp = atan2(rz, r) - acos(temp);
00279   m1 = -1;
00280   do {
00281     if (m1 > 1) {
00282       break;
00283     }
00284     solutions[2][1] = temp + 2 * m1 * M_PI;
00285     m1 += 1;              // So that within the 3 iterations we get m1 = -1, 0, 1
00286   } while ((solutions[2][1] < -(M_PI) || solutions[2][1] > M_PI));  // && m1 < 1);
00287   // Put a catchall here to prevent infinite loops by checking if m1 has gone past 1
00288   temp = (link2 * link2 + link4 * link4 - r * r - rz * rz) / (2 * link2 * link4);
00289   temp = fmin(fmax(temp, -1.0f), 1.0f);
00290   solutions[2][2] = M_PI - acos(temp);
00291   // Followed by theta2d and 3d
00292   temp = (r * r + rz * rz + link2 * link2 - link4 * link4) / (2 * link2 * sqrt(r * r + rz * rz));
00293   temp = fmin(fmax(temp, -1.0f), 1.0f);
00294   temp = atan2(rz, r) + acos(temp);
00295   m1 = -1;
00296   do {
00297     if (m1 > 1) {
00298       break;
00299     }
00300     solutions[3][1] = temp + 2 * m1 * M_PI;
00301     m1 += 1;              // So that within the 3 iterations we get m1 = -1, 0, 1
00302   } while ((solutions[3][1] < -(M_PI) || solutions[3][1] > M_PI));    // && m1 < 1);
00303   temp = (link2 * link2 + link4 * link4 - r * r - rz * rz) / (2 * link2 * link4);
00304   temp = fmin(fmax(temp, -1.0f), 1.0f);
00305   solutions[3][2] = -(M_PI) + acos(temp);
00306 
00307   // Using theta2c and 3c, calculate 4c and 5c to complete solution1
00308   CalcTheta4and5(solutions[2], fromPosition);
00309   // Using theta2d and 3d, calculate 4d and 5d to complete solution2
00310   CalcTheta4and5(solutions[3], fromPosition);
00311 
00312   // Choose the best of the four solutions
00313   int chosenSolution = ChooseSolution(fromPosition, solutions);
00314   if (chosenSolution == -1) {
00315     // Couldn't find a valid solution
00316     return false;
00317   }
00318 
00319   // Offsets and so forth
00320   joints[0] = (solutions[chosenSolution][0] * -1) + jointOffsets[0];
00321   joints[1] = solutions[chosenSolution][1] + jointOffsets[1];
00322   joints[2] = solutions[chosenSolution][2] + jointOffsets[2];
00323   joints[3] = (solutions[chosenSolution][3] * -1) + jointOffsets[3];
00324   joints[4] = (solutions[chosenSolution][4] * -1) + jointOffsets[4];
00325 
00326   return true;
00327 }
00328 
00329 //  Calculates thetas 4 and 5 based on supplied thetas 1, 2 and 3 and the desired end effector pose
00330 //  angles[]:       A 5-element array, of which elements 0, 1 and 2 should be filled already
00331 //  fromPosition:   The desired end effector pose
00332 void KineCalc::CalcTheta4and5(double angles[], const EndEffector & fromPosition)
00333 {
00334   const KineVector & n = fromPosition.n;
00335   const KineVector & o = fromPosition.o;
00336   const KineVector & a = fromPosition.a;
00337 
00338   double cos1 = cos(angles[0]);
00339   double cos23 = cos(angles[1] + angles[2]);
00340   double sin1 = sin(angles[0]);
00341   double sin23 = sin(angles[1] + angles[2]);
00342 
00343   if (cos23 != 0.0f) {
00344     if (sin1 < -0.1f || sin1 > 0.1f) {
00345       angles[3] = atan2(n.z / cos23, -(n.x + ((n.z * cos1 * sin23) / cos23)) / sin1);
00346     } else {
00347       angles[3] = atan2(n.z / cos23, (n.y + ((n.z * sin1 * sin23) / cos23)) / cos1);
00348     }
00349 
00350     double cos4 = cos(angles[3]);
00351     double sin4 = sin(angles[3]);
00352     if (cos4 != 0 || sin23 != 0) {
00353       angles[4] = atan2(a.z * cos23 * cos4 - o.z * sin23, o.z * cos23 * cos4 + a.z * sin23);
00354     } else {
00355       angles[4] = atan2(-(o.x * cos1 + o.y * sin1) / cos23, (o.x * sin1 - o.y * cos1) / sin4);
00356     }
00357   } else {
00358     angles[4] = atan2(-o.z / sin23, a.z / sin23);
00359 
00360     double cos5 = cos(angles[4]);
00361     double sin5 = sin(angles[4]);
00362     if (cos5 > -0.1f || cos5 < 0.1f) {
00363       angles[3] = atan2((a.x * sin1 - a.y * cos1) / sin5, -(n.x * sin1) + n.y * cos1);
00364     } else {
00365       angles[3] = atan2((o.x * sin1 - o.y * cos1) / cos5, -(n.x * sin1) + n.y * cos1);
00366     }
00367   }
00368 }
00369 
00370 //  Choose the best solution from the 4 available based on error and reachability
00371 //  fromPosition:   The desired end effector pose
00372 //  solutions[][]:  The four solutions (each with 5 angles) in an array
00373 int KineCalc::ChooseSolution(const EndEffector & fromPosition, const double solutions[][5])
00374 {
00375   double errors[4];
00376   int order[4], jj;
00377 
00378   // We have 4 solutions, calculate the error for each one
00379   errors[0] = CalcSolutionError(solutions[0], fromPosition);
00380   errors[1] = CalcSolutionError(solutions[1], fromPosition);
00381   errors[2] = CalcSolutionError(solutions[2], fromPosition);
00382   errors[3] = CalcSolutionError(solutions[3], fromPosition);
00383 
00384   for (int ii = 0; ii < 4; ii++) {
00385     double min = fmin(errors[0], fmin(errors[1], fmin(errors[2], errors[3])));
00386     for (jj = 0; min != errors[jj]; jj++) {             // Find the index at which the min is at
00387     }
00388     errors[jj] = 999999;
00389     order[ii] = jj;
00390   }
00391 
00392   for (int ii = 0; ii < 4; ii++) {
00393     if (SolutionInRange(solutions[order[ii]])) {
00394       return order[ii];
00395     }
00396   }
00397 
00398   return -1;
00399 }
00400 
00401 //  Calculate the error for a solution from the desired pose
00402 //  solution[]:       An array of 5 angles
00403 //  fromPosition[]:   The end effector pose
00404 double KineCalc::CalcSolutionError(const double solution[], const EndEffector & fromPosition)
00405 {
00406   EndEffector solutionPos;
00407   double error = 0.0f;
00408 
00409   // Calculate the position of the end effector this solution gives using FK
00410   solutionPos = CalcFKForJoints(solution);
00411   // Calculate the distance from this to the desired position
00412   double xOffset = solutionPos.p.x - fromPosition.p.x;
00413   double yOffset = solutionPos.p.y - fromPosition.p.y;
00414   double zOffset = solutionPos.p.z - fromPosition.p.z;
00415 
00416   error = sqrt(xOffset * xOffset + yOffset * yOffset + zOffset * zOffset);
00417   if (isnan(error)) {
00418     error = 9999;
00419   }
00420 
00421   return error;
00422 }
00423 
00424 //  Calculates the forward kinematics of a set of joint angles
00425 //  angles[]:   The 5 angles to calculate from
00426 EndEffector KineCalc::CalcFKForJoints(const double angles[])
00427 {
00428   EndEffector result;
00429 
00430   double cos1 = cos(angles[0]);
00431   double cos2 = cos(angles[1]);
00432   double cos23 = cos(angles[1] + angles[2]);
00433   double cos4 = cos(angles[3]);
00434   double cos5 = cos(angles[4]);
00435   double sin1 = sin(angles[0]);
00436   double sin2 = sin(angles[1]);
00437   double sin23 = sin(angles[1] + angles[2]);
00438   double sin4 = sin(angles[3]);
00439   double sin5 = sin(angles[4]);
00440 
00441   result.p.x = link5 *
00442     ((cos1 * cos23 * cos5) + (sin1 * sin4 * sin5) - (cos1 * sin23 * cos4 * sin5)) +
00443     cos1 * ((link4 * cos23) + (link2 * cos2) + link1);
00444   result.p.y = link5 *
00445     ((sin1 * cos23 * cos5) + (cos1 * sin4 * sin5) - (sin1 * sin23 * cos4 * sin5)) +
00446     sin1 * ((link4 * cos23) + (link2 * cos2) + link1);
00447   result.p.z = link5 * ((sin23 * cos5) + (cos23 * cos4 * sin5)) + (link4 * sin23) + (link2 * sin2);
00448 
00449   result.n.x = -(sin1 * cos4) - (cos1 * sin23 * sin4);
00450   result.n.y = (cos1 * cos4) - (sin1 * sin23 * sin4);
00451   result.n.z = (cos23 * sin4);
00452 
00453   result.o.x = -(cos1 * cos23 * sin5) + (sin1 * sin4 * cos5) - (cos1 * sin23 * cos4 * cos5);
00454   result.o.y = -(sin1 * cos23 * sin5) - (cos1 * sin4 * cos5) - (sin1 * sin23 * cos4 * cos5);
00455   result.o.z = -(sin23 * sin5) + (cos23 * cos4 * cos5);
00456 
00457   result.a.x = (cos1 * cos23 * cos5) + (sin1 * sin4 * sin5) - (cos1 * sin23 * cos4 * sin5);
00458   result.a.y = (sin1 * cos23 * cos5) + (cos1 * sin4 * sin5) - (sin1 * sin23 * cos4 * sin5);
00459   result.a.z = (sin23 * cos5) + (cos23 * cos4 * sin5);
00460 
00461   return result;
00462 }
00463 
00464 //  Checks if the angles for a solution are reachable by the arm
00465 //  angles[]:   The 5 angles to check
00466 bool KineCalc::SolutionInRange(const double angles[])
00467 {
00468   for (int ii = 0; ii < 5; ii++) {
00469     if (angles[ii] < jointMin[ii] || angles[ii] > jointMax[ii] || isnan(angles[ii])) {
00470       return false;
00471     }
00472   }
00473 
00474   return true;
00475 }


p2os_driver
Author(s): Hunter L. Allen , David Feil-Seifer , Aris Synodinos , Brian Gerkey, Kasper Stoy, Richard Vaughan, Andrew Howard, Tucker Hermans, ActivMedia Robotics LLC, MobileRobots Inc.
autogenerated on Wed Mar 27 2019 02:34:56