Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

IKCalliope.cc

Go to the documentation of this file.
00001 #if defined(TGT_IS_CALLIOPE) && defined(TGT_HAS_ARMS)
00002 
00003 #include "IKCalliope.h"
00004 #include "Kinematics.h"
00005 
00006 const float IKCalliope::EPSILON = std::numeric_limits<float>::min();
00007 const std::string IKCalliope::autoRegisterIKCalliope = IKSolver::getRegistry().registerType<IKCalliope>("IKCalliope");
00008 
00009 #  if defined(TGT_IS_CALLIOPE5)
00010 
00011 IKCalliope::IKCalliope() : L1(), L2(), jointLimits() {
00012   const KinematicJoint* j = kine->getKinematicJoint(ArmWristOffset);
00013   if ( j == NULL )
00014     throw std::runtime_error("IKCalliope can't find GripperFrameOffset or ARM:wristrot");
00015   
00016   L1 = j->getParent()->r;
00017   L2 = j->r;
00018   jointLimits[0][0] = j->getParent()->getParent()->qmin;
00019   jointLimits[0][1] = j->getParent()->getParent()->qmax;
00020   jointLimits[1][0] = j->getParent()->qmin; 
00021   jointLimits[1][1] = j->getParent()->qmax;
00022   jointLimits[2][0] = j->qmin;
00023   jointLimits[2][1] = j->qmax;
00024 }
00025 
00026 bool IKCalliope::solve(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float posPri, const Orientation& oriTgt, float oriPri) const {
00027   // translate target point from BaseFrame to the frame of the ARM:base.
00028   const Point& pTgtPoint = dynamic_cast<const Point&>(pTgt);
00029   Point pTgtBase(kine->getKinematicJoint(ArmBaseOffset)->getTo().inverse() * pTgtPoint);
00030   
00031   // use std::atan2 to solve for base link
00032   float qBase = std::atan2(pTgtBase.y, pTgtBase.x);
00033   float qOffset = kine->getKinematicJoint(ArmBaseOffset)->qOffset;
00034   fmat::SubVector<2> gripperToBase(kine->linkToLink(GripperFrameOffset, ArmBaseOffset).translation());
00035   fmat::SubVector<2> wristToBase(kine->linkToLink(ArmWristOffset, ArmBaseOffset).translation());
00036   float qGripper = fmat::atan(gripperToBase) - fmat::atan(wristToBase);
00037   j.getParent()->getParent()->getParent()->getParent()->getParent()->setQ(qBase-qOffset-qGripper);
00038   
00039   // convert target point to shoulder reference frame
00040   fmat::Column<2> pTgtV;
00041   if (std::abs(std::cos(qBase)) < EPSILON)
00042     pTgtV[0] = pTgtBase.y;
00043   else
00044     pTgtV[0] = pTgtBase.x / std::cos(qBase);
00045   pTgtV[1] = pTgtBase.z;
00046   // shift from ARM:base -> ARM:shoulder for new origin for IK
00047   float baseToShoulder = kine->linkToLink(ArmShoulderOffset, ArmBaseOffset).translation()[0];
00048   pTgtV[0] -= baseToShoulder;
00049   
00050   const Rotation& oriTgtV = dynamic_cast<const Rotation&>(oriTgt);
00051   
00052   // perform IK with rotation around the Y axis for orientation
00053   IKCalliope::Solutions sols = closestThreeLinkIK(pTgtV, pEff, oriTgtV.axisComponent(fmat::UNIT3_Y));
00054   
00055   // set wristRot angle on the basis of how the x-y plane is rotated about the z axis
00056   j.getParent()->setQ(oriTgtV.axisComponent(fmat::UNIT3_Z));
00057   
00058   // set the rest based on the three link IK solution
00059   j.getParent()->getParent()->tryQ(sols.angles(0,2));
00060   j.getParent()->getParent()->getParent()->tryQ(sols.angles(0,1));
00061   j.getParent()->getParent()->getParent()->getParent()->tryQ(sols.angles(0,0));
00062   
00063   return sols.valid;
00064 }
00065 
00066 IKCalliope::Solutions IKCalliope::closestThreeLinkIK(const fmat::Column<2>& t, const fmat::Column<3>& pEff, float prefPhi) const {
00067   fmat::Column<2> target = updatePosition(t, pEff, prefPhi);
00068   IKCalliope::Solutions solutions = threeLinkIK(target, prefPhi); 
00069   if( solutions.valid )
00070     return solutions;
00071   
00072   /* If solution cannot be found with specified phi,
00073    try ±{1,2,3,...,180} degrees until achievable angles are found */
00074   else {
00075     float pstep = M_PI/180;
00076     for(float deltaPhi = pstep; deltaPhi <= M_PI; deltaPhi += pstep) {
00077       target = updatePosition(t, pEff, AngSignPi(prefPhi + deltaPhi));
00078       solutions = threeLinkIK(target, AngSignPi(prefPhi + deltaPhi));
00079       if( solutions.valid )
00080         return solutions;
00081       
00082       target = updatePosition(t, pEff, AngSignPi(prefPhi - deltaPhi));
00083       solutions = threeLinkIK(target, AngSignPi(prefPhi - deltaPhi));
00084       if( solutions.valid )
00085         return solutions;
00086     }
00087   }
00088   return punt(t);
00089 }
00090 
00091 fmat::Column<2> IKCalliope::updatePosition(const fmat::Column<2>& t, const fmat::Column<3>& pEff, float oriThreeLink) const {
00092   // shift from the gripper to the end of the wrist for three-link IK
00093   fmat::Column<2> gripperToWrist(kine->linkToLink(GripperFrameOffset, ArmWristOffset).translation());
00094   gripperToWrist = fmat::rotation2D(oriThreeLink) * gripperToWrist;
00095   fmat::Column<2> target = t - gripperToWrist;
00096   
00097   // factor in the effector point pEff
00098   return target + fmat::rotation2D(oriThreeLink) * fmat::Column<2>(pEff);
00099 }
00100 
00101 IKCalliope::Solutions IKCalliope::threeLinkIK(fmat::Column<2> t, float phi) const {
00102   IKCalliope::Solutions solutions;
00103   fmat::Matrix<2,2> sols_2link;
00104   fmat::Column<2> q3;
00105   float c2 = (t.sumSq() - L1*L1 - L2*L2) / (2*L1*L2);
00106   if(c2*c2 > 1) {
00107     solutions.noSols = 0;
00108     solutions.valid = false;
00109     return solutions;
00110   }
00111   else {
00112     sols_2link = twoLinkIK(t);
00113     q3[0] = phi - sols_2link(0,0) - sols_2link(0,1);
00114     q3[1] = phi - sols_2link(1,0) - sols_2link(1,1);
00115     solutions.angles(0,0) = sols_2link(0,0); solutions.angles(0,1) = sols_2link(0,1); solutions.angles(0,2) = q3[0];
00116     solutions.angles(1,0) = sols_2link(1,0); solutions.angles(1,1) = sols_2link(1,1); solutions.angles(1,2) = q3[1];
00117     solutions.noSols = 2;
00118     solutions = validAngles(solutions);
00119     return solutions;
00120   }
00121 }
00122 
00123 IKCalliope::Solutions IKCalliope::punt(const fmat::Column<2>& target) const {
00124   IKCalliope::Solutions solutions;
00125   float q1 = fmat::atan(target), q2 = 0.0f, q3 = 0.0f;
00126   q1 = std::min(std::max(q1,jointLimits[0][0]),jointLimits[0][1]);
00127   solutions.angles(0,0) = q1; solutions.angles(0,1) = q2; solutions.angles(0,2) = q3;
00128   solutions.noSols = 1;
00129   solutions.valid = false;
00130   return solutions;
00131 }
00132 
00133 fmat::Matrix<2,2> IKCalliope::twoLinkIK(fmat::Column<2> t) const {
00134   fmat::Matrix<2,2> ret;
00135   float c2 = (t.sumSq() - L1*L1 - L2*L2) / (2*L1*L2);
00136   float s2 = std::sqrt(1 - c2*c2);
00137   float q2 = std::atan2(s2, c2);
00138   float kQ = std::atan2(L2*s2,L1 + L2*c2);
00139   float q1plus = fmat::atan(t) - kQ;
00140   float q1minus = fmat::atan(t) + kQ;
00141   ret(0,0) = q1plus; ret(0,1) = q2;
00142   ret(1,0) = q1minus; ret(1,1) = -q2;
00143   return ret;
00144 }
00145 
00146 IKCalliope::Solutions IKCalliope::validAngles(IKCalliope::Solutions solutions) const {
00147   int valid_solutions[2] = {0, 0}; // assume both solutions are invalid
00148   for (int sol_no = 0; sol_no < solutions.noSols; sol_no++) {
00149     // Check for joint limits, any solution with a joint outside the limits is an invalid solution
00150     int val_angles = 0;
00151     for (int joint_no = 0; joint_no < 3; joint_no++) {
00152       if (solutions.angles(sol_no,joint_no) > jointLimits[joint_no][0] && solutions.angles(sol_no,joint_no) < jointLimits[joint_no][1])
00153         val_angles++;
00154     }
00155     if (val_angles == 3)
00156       valid_solutions[sol_no] = 1;
00157   } 
00158   if (valid_solutions[0]+valid_solutions[1] == 2) {      // both solutions were valid
00159     solutions.noSols = 2;
00160     solutions.valid = true;
00161     return solutions;
00162   }
00163   else if (valid_solutions[0]+valid_solutions[1] == 0) { // both solutions were invalid
00164     solutions.noSols = 0;
00165     solutions.valid = false;
00166     return solutions;
00167   }
00168   else {                                                  // one solution was invalid
00169     if (valid_solutions[0] == 0)
00170       solutions.angles.row(0) = solutions.angles.row(1);
00171     solutions.noSols = 1;
00172     solutions.valid = true;
00173     return solutions;
00174   }
00175 }
00176 
00177 IKSolver::StepResult_t IKCalliope::step(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float pDist, float posPri, const Orientation& oriTgt, float oriDist, float oriPri) const {
00178   // find the closest target point
00179   fmat::Transform Tj = j.getFullT();
00180   fmat::Column<3> pEffBase(Tj*pEff);
00181   fmat::Quaternion qj = j.getWorldQuaternion();
00182   Rotation oEffBase = qj*oriEff;
00183   Point de;
00184   pTgt.computeErrorGradient(pEffBase,oEffBase,de);
00185   StepResult_t res = SUCCESS;
00186   if(de.norm() > pDist) {
00187     de *= pDist/de.norm();
00188     res = PROGRESS;
00189   }
00190   Point pEffBaseP(pEffBase[1],pEffBase[2],pEffBase[3]);
00191   Point pTgtP = pEffBaseP+de;
00192   if(solve(pEff,oriEff,j,pTgtP,posPri,oriTgt,oriPri))
00193     return res;
00194   return LIMITS;
00195 }
00196 
00197 #  elif defined(TGT_IS_CALLIOPE2) // TGT_IS_CALLIOPE5
00198 
00199 IKCalliope::IKCalliope() : baseToArm(), qOffset(), forearmLength() {
00200   const KinematicJoint* j = kine->getKinematicJoint(GripperFrameOffset);
00201   
00202   forearmLength = (j->getWorldPosition() - j->getParent()->getParent()->getWorldPosition()).norm();
00203   qOffset = kine->getKinematicJoint(ArmBaseOffset)->qOffset;
00204   baseToArm = kine->getKinematicJoint(ArmBaseOffset)->getTo().inverse();
00205 }
00206 
00207 bool IKCalliope::solve(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float posPri, const Orientation& oriTgt, float oriPri) const {
00208   // translate target point from BaseFrame to the frame of the ARM:base.
00209   const Point& pTgtPoint = dynamic_cast<const Point&>(pTgt);
00210   Point pTgtBase(baseToArm * pTgtPoint);
00211   
00212   // use std::atan2 to solve for base link
00213   /* note that in reality, the GripperFrame is shifted 1.283cm to the left of the
00214    axis pointed to by the arm, and that this solver does not take into account
00215    that shift due to its insignificance. */
00216   float qBase = std::atan2(pTgtBase.y, pTgtBase.x);
00217   j.getParent()->getParent()->getParent()->tryQ(qBase-qOffset);
00218   
00219   /* we want the Z component of the specified target point to line up with the Gripper
00220    so that perhaps from there, the robot can move forward to touch the point */
00221   float opposite = pTgtBase[2];
00222   float qElbow;
00223   if (std::abs(opposite) < forearmLength)
00224     qElbow = std::asin(opposite / forearmLength);
00225   else
00226     qElbow = opposite > 0 ? M_PI_2 : -M_PI_2;
00227   j.getParent()->getParent()->tryQ(qElbow);
00228   
00229   if ( (j.getWorldPosition() - pTgtBase).norm() < 5.0f )
00230     return true;
00231   
00232   return false;
00233 }
00234 
00235 IKSolver::StepResult_t IKCalliope::step(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float pDist, float posPri, const Orientation& oriTgt, float oriDist, float oriPri) const {
00236   // find the closest target point
00237   fmat::Transform Tj = j.getFullT();
00238   fmat::Column<3> pEffBase(Tj*pEff);
00239   fmat::Quaternion qj = j.getWorldQuaternion();
00240   Rotation oEffBase = qj*oriEff;
00241   Point de;
00242   pTgt.computeErrorGradient(pEffBase,oEffBase,de);
00243   StepResult_t res=SUCCESS;
00244   if(de.norm()>pDist) {
00245     de*=pDist/de.norm();
00246     res = PROGRESS;
00247   }
00248   Point pEffBaseP(pEffBase[1],pEffBase[2],pEffBase[3]);
00249   Point pTgtP = pEffBaseP+de;
00250   if(solve(pEff,oriEff,j,pTgtP,posPri,oriTgt,oriPri))
00251     return res;
00252   return LIMITS;
00253 }
00254 
00255 #  endif
00256 
00257 #endif // TGT_HAS_ARMS

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:41 2012 by Doxygen 1.6.3