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


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 Sat Aug 5 2017 02:20:17