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