00001 #include "Shared/RobotInfo.h"
00002 #if (defined(TGT_HAS_LEGS) && !defined(TGT_IS_AIBO)) || defined(TGT_HAS_HEAD)
00003
00004 #include "IKThreeLink.h"
00005 #include "Shared/debuget.h"
00006 #include "Shared/fmatSpatial.h"
00007 #include <limits>
00008
00009 using namespace std;
00010
00011 const float IKThreeLink::EPSILON=1e-3f;
00012 const std::string IKThreeLink::autoRegisterIKThreeLink = IKSolver::getRegistry().registerType<IKThreeLink>("IKThreeLink");
00013
00014 bool IKThreeLink::solve(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float posPri, const Orientation& oriTgt, float oriPri) const {
00015 bool converges[3] = { true, true, true };
00016 KinematicJoint* links[3] = { NULL, NULL, NULL };
00017 float origKnee;
00018 setLinks(j,links,3);
00019
00020
00021
00022 fmat::Transform Tj = j.getFullT();
00023 fmat::Column<3> pEffBase(Tj*pEff);
00024 fmat::Quaternion qj = j.getWorldQuaternion();
00025 Rotation oEffBase = qj*oriEff;
00026 fmat::Column<3> de;
00027 pTgt.computeErrorGradient(pEffBase,oEffBase,de);
00028
00029 if(links[0]==NULL || posPri<=0) {
00030
00031 std::cerr << "ERROR: IKThreeLink trying to solve without any mobile joints (" << j.outputOffset.get() << ")" << std::endl;
00032 return de.norm()<=EPSILON;
00033 }
00034
00035 origKnee = (links[2]!=NULL) ? links[2]->getQ() : 0;
00036 hasInverseSolution=false;
00037 fmat::Column<3> pTgtV = pEffBase + de;
00038
00039
00040
00041
00042
00043
00044
00045 if(links[1]!=NULL) {
00046 if(links[2]!=NULL)
00047 computeThirdLink(*links[2], pTgtV, j, pEff, invertThird, converges[2]);
00048 computeSecondLink(*links[1], pTgtV, j, pEff, false, converges[1]);
00049 }
00050 computeFirstLink(*links[0], pTgtV, j, pEff, converges[0]);
00051
00052 if(hasInverseSolution && (!converges[0] || !converges[1])) {
00053
00054
00055 float defaultKnee = links[2]->getQ();
00056 links[2]->tryQ(inverseKnee);
00057 if(links[1]!=NULL)
00058 computeSecondLink(*links[1], pTgtV, j, pEff, false, converges[1]);
00059 computeFirstLink(*links[0], pTgtV, j, pEff, converges[0]);
00060
00061
00062
00063
00064
00065
00066
00067
00068 if((!converges[0] || !converges[1]) && std::abs(origKnee-inverseKnee) > std::abs(origKnee-defaultKnee)) {
00069
00070
00071 links[2]->tryQ(defaultKnee);
00072 if(links[1]!=NULL)
00073 computeSecondLink(*links[1], pTgtV, j, pEff, false, converges[1]);
00074 computeFirstLink(*links[0], pTgtV, j, pEff, converges[0]);
00075 }
00076 }
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086 if(!converges[0]) {
00087
00088 if(links[1]!=NULL) {
00089 hasInverseSolution=false;
00090 if(links[2]!=NULL) {
00091 computeSecondLink(*links[2], pTgtV, j, pEff, invertThird, converges[2]);
00092 }
00093 computeFirstLink(*links[1], pTgtV, j, pEff, converges[1]);
00094 if(hasInverseSolution && !converges[1]) {
00095 float defaultKnee = links[2]->getQ();
00096 links[2]->tryQ(inverseKnee);
00097 computeFirstLink(*links[1], pTgtV, j, pEff, converges[1]);
00098 if(!converges[1] && std::abs(origKnee-inverseKnee) > std::abs(origKnee-defaultKnee)) {
00099
00100 links[2]->tryQ(defaultKnee);
00101 computeFirstLink(*links[1], pTgtV, j, pEff, converges[1]);
00102 }
00103 }
00104 }
00105 }
00106
00107
00108 if(!converges[1] && links[2]!=NULL) {
00109
00110 computeFirstLink(*links[2], pTgtV, j, pEff, converges[2]);
00111 }
00112
00113 de = j.getFullT()*pEff - pTgtV;
00114 return de.norm()<=EPSILON;
00115 }
00116
00117 IKSolver::StepResult_t IKThreeLink::step(const Point& pEff, const Rotation& oriEff, KinematicJoint& j, const Position& pTgt, float pDist, float posPri, const Orientation& oriTgt, float oriDist, float oriPri) const {
00118
00119 fmat::Transform Tj = j.getFullT();
00120 fmat::Column<3> pEffBase(Tj*pEff);
00121 fmat::Quaternion qj = j.getWorldQuaternion();
00122 Rotation oEffBase = qj*oriEff;
00123 Point de;
00124 pTgt.computeErrorGradient(pEffBase,oEffBase,de);
00125
00126 StepResult_t res=SUCCESS;
00127 if(de.norm()>pDist) {
00128 de*=pDist/de.norm();
00129 res = PROGRESS;
00130 }
00131 Point pTgtP = pEffBase+de;
00132 if(solve(pEff,oriEff,j,pTgtP,posPri,oriTgt,oriPri))
00133 return res;
00134 return LIMITS;
00135 }
00136
00137 unsigned int IKThreeLink::setLinks(KinematicJoint& eff, KinematicJoint* links[], unsigned int remain) {
00138 if(remain==0)
00139 return 0;
00140 if(eff.isMobile())
00141 --remain;
00142 unsigned int unused=remain;
00143 if(eff.getParent()!=NULL)
00144 unused=setLinks(*eff.getParent(),links,remain);
00145 if(eff.isMobile()) {
00146 links[remain-unused]=&eff;
00147
00148 }
00149 return unused;
00150 }
00151
00152 void IKThreeLink::computeFirstLinkRevolute(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>& Plink, bool& valid) const {
00153
00154 fmat::Transform tr = (curlink.getParent()==NULL) ? curlink.getTo().inverse() : (curlink.getParent()->getFullT() * curlink.getTo()).inverse();
00155 fmat::Column<3> cObj = tr*Pobj;
00156 fmat::Column<3> cEff = endlink.getT(curlink)*Plink;
00157
00158
00159
00160 if(std::abs(cEff[0])<EPSILON && std::abs(cEff[1])<EPSILON) {
00161
00162 valid=true;
00163 return;
00164 }
00165 if(std::abs(cObj[0])<EPSILON && std::abs(cObj[1])<EPSILON) {
00166
00167 valid=true;
00168 return;
00169 }
00170 float ao=atan2(cObj[1],cObj[0]);
00171 float ae=atan2(cEff[1],cEff[0]);
00172 valid=curlink.tryQ(normalize_angle(ao-ae-curlink.qOffset));
00173 }
00174
00175 void IKThreeLink::computeFirstLinkPrismatic(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>& Plink, bool& valid) const {
00176
00177 fmat::Transform tr = (curlink.getParent()==NULL) ? curlink.getTo().inverse() : (curlink.getParent()->getFullT() * curlink.getTo()).inverse();
00178 fmat::Column<3> cObj = tr*Pobj;
00179
00180 valid=curlink.tryQ(cObj[2] - curlink.qOffset);
00181 }
00182
00183
00184
00185
00186 void IKThreeLink::computeSecondLinkRevolute(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>& Plink, bool invert, bool& valid) const {
00187
00188 fmat::Column<3> cEff = endlink.getT(curlink)*Plink;
00189 if(std::abs(cEff[0])<EPSILON && std::abs(cEff[1])<EPSILON) {
00190
00191 valid=true;
00192 return;
00193 }
00194
00195
00196
00197
00198 fmat::Column<3> pObj = curlink.getParent()->getFullInvT()*Pobj;
00199
00200 if(std::abs(curlink.getRotation()(2,2)) >= (1-EPSILON)) {
00201
00202
00203
00204
00205 float adj1 = curlink.r;
00206 float adj2 = hypotf(cEff[0],cEff[1]);
00207 float opp = hypotf(pObj[0],pObj[1]);
00208 float a;
00209 if(adj1 + opp <= adj2 || adj2 + opp <= adj1) {
00210
00211
00212 a = 0;
00213 } else if(adj1 + adj2 <= opp) {
00214
00215
00216 a = (float)M_PI;
00217 } else {
00218 float ca = ( adj1*adj1 + adj2*adj2 - opp*opp ) / ( 2*adj1*adj2);
00219
00220 a = std::acos(ca);
00221 }
00222
00223 float offset = -curlink.qOffset - std::atan2(cEff[1],cEff[0]);
00224 if(curlink.r>0)
00225 offset+=(float)M_PI;
00226
00227
00228 float q1,q2;
00229 if(invert) {
00230 q1 = offset-a;
00231 q2 = offset+a;
00232 } else {
00233 q1 = offset+a;
00234 q2 = offset-a;
00235 }
00236
00237
00238 valid = curlink.tryQ( normalize_angle(q1) );
00239 if(!valid) {
00240
00241 valid = curlink.tryQ( normalize_angle(q2) );
00242 if(!valid) {
00243
00244 valid = curlink.tryQ( normalize_angle(q1) );
00245 }
00246 } else if(std::abs(q1-q2)>EPSILON && curlink.qmin<=q2 && q2<=curlink.qmax) {
00247
00248 hasInverseSolution=true;
00249 inverseKnee=q2;
00250 }
00251
00252 } else {
00253
00254
00255
00256
00257 float cr = std::abs((float)curlink.r);
00258
00259 float lz = std::abs(cEff[2]);
00260 float coh = pObj[2] - curlink.d;
00261 if(curlink.alpha<0)
00262 coh=-coh;
00263 float por = hypotf(pObj[0],pObj[1]);
00264
00265
00266 float x;
00267 if(por<lz) {
00268
00269
00270
00271 valid=false;
00272 return;
00273 } else if(false ) {
00274
00275
00276
00277 float b = 2*cr;
00278 float c = cr*cr + lz*lz - por*por;
00279 x = ( -b + std::sqrt(b*b - 4*c) ) / 2;
00280
00281 } else {
00282
00283
00284
00285 float b = 2*cr;
00286 float c = cr*cr + lz*lz - por*por;
00287 x = ( -b + std::sqrt(b*b - 4*c) ) / 2;
00288
00289 }
00290
00291
00292
00293
00294 float q = std::atan2(coh, x);
00295
00296 q -= curlink.qOffset + std::atan2(cEff[1],cEff[0]);
00297 valid = curlink.tryQ( normalize_angle(q) );
00298 }
00299 }
00300
00301
00302
00303
00304
00305 void IKThreeLink::computeSecondLinkPrismatic(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>& Plink, bool invert, bool& valid) const {
00306 KinematicJoint* tilt = curlink.getParent();
00307 while(!tilt->isMobile())
00308 tilt=tilt->getParent();
00309
00310
00311 fmat::Column<3> tObj = tilt->getFullInvT()*Pobj;
00312
00313
00314 fmat::Transform curToTilt = curlink.getParent()->getT(*tilt) * curlink.getTo();
00315
00316 const fmat::Column<3>& neck = curToTilt.translation();
00317 float neckD2 = neck.sumSq();
00318
00319 const fmat::Column<3>& tcz = curToTilt.column(2);
00320 float inner = fmat::dotProduct(tcz,-neck);
00321
00322
00323 float tod2 = tObj[0]*tObj[0] + tObj[1]*tObj[1];
00324
00325
00326 valid = curlink.tryQ(computePrismaticQ(tod2,neckD2,inner) - curlink.qOffset);
00327 }
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341 void IKThreeLink::computeThirdLinkRevolute(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>& Plink, bool invert, bool& valid) const {
00342
00343 fmat::Column<3> cEff = endlink.getT(curlink)*Plink;
00344 if(std::abs(cEff[0])<EPSILON && std::abs(cEff[1])<EPSILON) {
00345
00346 valid=true;
00347 return;
00348 }
00349
00350
00351 KinematicJoint& parLink = *curlink.getParent();
00352
00353 KinematicJoint& gparLink = *parLink.getParent();
00354
00355
00356
00357 fmat::Column<3> gpObj = gparLink.getFullInvT()*Pobj;
00358 gpObj[2]-=parLink.d;
00359
00360
00361 float thigh, thigh2;
00362 float ringObjD2;
00363
00364
00365 const bool KNEE_PERP = std::abs(std::abs((float)curlink.alpha) - (float)M_PI_2) < EPSILON;
00366 if(KNEE_PERP) {
00367
00368 ASSERT(parLink.r==0,"IKThreeLink can't handle perpendicular knees with non-zero hip radius");
00369
00370 thigh2 = curlink.r*curlink.r + curlink.d*curlink.d;
00371 thigh = std::sqrt(thigh2);
00372
00373 float lz = cEff[2];
00374 float rd = std::sqrt(gpObj[0]*gpObj[0] + gpObj[1]*gpObj[1]) - std::abs((float)parLink.r);
00375 ringObjD2 = rd*rd + gpObj[2]*gpObj[2] - lz*lz;
00376 if(ringObjD2<0)
00377 ringObjD2=0;
00378
00379 } else {
00380
00381 thigh = std::abs((float)curlink.r);
00382 thigh2=thigh*thigh;
00383
00384 float lz = cEff[2] - curlink.d;
00385
00386
00387
00388 float d2 = gpObj[0]*gpObj[0] + gpObj[1]*gpObj[1] - lz*lz;
00389 if(d2<0)
00390 d2=0;
00391 float rd = std::sqrt(d2) - std::abs((float)parLink.r);
00392
00393 ringObjD2 = rd*rd + gpObj[2]*gpObj[2];
00394
00395
00396
00397
00398
00399
00400
00401
00402 }
00403 float ringObjD = std::sqrt(ringObjD2);
00404
00405
00406 float ln2 = cEff[0]*cEff[0] + cEff[1]*cEff[1];
00407 float ln = std::sqrt(ln2);
00408
00409
00410
00411
00412 float a;
00413 if( (ln + ringObjD <= thigh) || (ringObjD + thigh <= ln)) {
00414
00415 a = 0;
00416
00417 } else if(ln + thigh <= ringObjD) {
00418
00419 a = (float)M_PI;
00420
00421 } else {
00422
00423 a = std::acos( (ln2 + thigh2 - ringObjD2) / (2*ln*thigh) );
00424 }
00425 float offset = -curlink.qOffset - std::atan2(cEff[1],cEff[0]);
00426
00427 if(KNEE_PERP) {
00428 if(curlink.alpha<0)
00429 offset -= std::atan2((float)curlink.d,(float)curlink.r);
00430 else
00431 offset += std::atan2((float)curlink.d,(float)curlink.r);
00432 a = (float)M_PI-a;
00433 } else if(curlink.r>0) {
00434 a = (float)M_PI-a;
00435 }
00436
00437
00438 float q1,q2;
00439 if(invert) {
00440 q1 = offset-a;
00441 q2 = offset+a;
00442 } else {
00443 q1 = offset+a;
00444 q2 = offset-a;
00445 }
00446
00447
00448 valid = curlink.tryQ( normalize_angle(q1) );
00449 if(!valid) {
00450
00451 valid = curlink.tryQ( normalize_angle(q2) );
00452 if(!valid) {
00453
00454 valid = curlink.tryQ( normalize_angle(q1) );
00455 }
00456 } else if(std::abs(q1-q2)>EPSILON && curlink.qmin<=q2 && q2<=curlink.qmax) {
00457
00458 hasInverseSolution=true;
00459 inverseKnee=q2;
00460 }
00461 }
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 void IKThreeLink::computeThirdLinkPrismatic(KinematicJoint& curlink, const fmat::Column<3>& Pobj, KinematicJoint& endlink, const fmat::Column<3>&, bool invert, bool& valid) const {
00477
00478
00479 KinematicJoint* tilt = curlink.getParent();
00480 while(!tilt->isMobile())
00481 tilt=tilt->getParent();
00482 KinematicJoint* pan = tilt->getParent();
00483
00484
00485 fmat::Column<3> gpObj = pan->getFullInvT()*Pobj;
00486 gpObj[2]-=tilt->d;
00487
00488
00489 fmat::Transform curToTilt = curlink.getParent()->getT(*tilt) * curlink.getTo();
00490
00491 const fmat::Column<3>& neck = curToTilt.translation();
00492 float neckD2 = neck.sumSq();
00493
00494 const fmat::Column<3>& tcz = curToTilt.column(2);
00495 float inner = fmat::dotProduct(tcz,-neck);
00496
00497
00498 float d2 = gpObj[0]*gpObj[0] + gpObj[1]*gpObj[1];
00499 float rd = std::sqrt(d2) - std::abs((float)tilt->r);
00500 float ringObjD2 = rd*rd + gpObj[2]*gpObj[2];
00501
00502
00503 valid = curlink.tryQ(computePrismaticQ(ringObjD2,neckD2,inner) - curlink.qOffset);
00504 }
00505
00506 fmat::fmatReal IKThreeLink::computePrismaticQ(fmat::fmatReal objD2, fmat::fmatReal neckD2, fmat::fmatReal inner) {
00507
00508
00509
00510
00511
00512
00513 float b = -2*inner;
00514 float c = neckD2 - objD2;
00515 float r = b*b - 4*c;
00516 if(r<0) {
00517
00518
00519 float q2 = neckD2 - inner*inner;
00520 if(q2<0) {
00521
00522 cerr << "IKThreeLink::computePrismaticQ numeric error, q² < 0 : " << q2 << " from " << neckD2 << ' ' << inner << ' ' << objD2 << endl;
00523 return 0;
00524 } else {
00525 return std::sqrt(q2);
00526 }
00527 } else {
00528 return (-b + std::sqrt(r))/2;
00529 }
00530 }
00531
00532
00533
00534
00535
00536
00537
00538
00539 #endif