00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <robot/KinematicChain>
00026
00027 #include <base/MD5>
00028 #include <base/Externalizer>
00029 #include <base/externalization_error>
00030 #include <robot/JFKengine>
00031
00032 #include <cstdio>
00033
00034 using robot::KinematicChain;
00035
00036 using base::Byte;
00037 using base::Matrix;
00038 using base::Vector;
00039 using base::ExpressionMatrix;
00040 using base::Externalizer;
00041 using base::externalization_error;
00042 using base::dom::DOMNode;
00043 using base::dom::DOMElement;
00044 using base::MD5;
00045 using robot::KinematicEvaluator;
00046 using robot::JFKengine;
00047
00048
00049
00050 const Real KinematicChain::unboundedMinAngleLimit = -consts::Pi - consts::epsilon;
00051 const Real KinematicChain::unboundedMaxAngleLimit = +consts::Pi + consts::epsilon;
00052 const Real KinematicChain::unboundedMinDistLimit = consts::maxReal;
00053 const Real KinematicChain::unboundedMaxDistLimit = -consts::maxReal;
00054
00055
00056
00057 KinematicChain::Link& KinematicChain::Link::operator=(const KinematicChain::Link& l)
00058 {
00059 linkType=l.linkType;
00060 switch (linkType) {
00061 case Translating:
00062 direction=l.direction;
00063 t=l.t;
00064 break;
00065 case FixedTransform:
00066 transform=l.transform;
00067 break;
00068 case Prismatic:
00069 case Revolute:
00070 alpha=l.alpha; a=l.a; d=l.d; theta=l.theta;
00071 break;
00072 default: Assertm(false,"known joint type");
00073 }
00074 jminLimit=l.jminLimit; jmaxLimit=l.jmaxLimit;
00075 jminAccel=l.jminAccel; jmaxAccel=l.jmaxAccel;
00076 active=l.active;
00077 dirtyHash();
00078 return *this;
00079 }
00080
00081
00082 bool KinematicChain::Link::operator==(const Link& l) const
00083 {
00084 if (linkType != l.linkType) return false;
00085 if (this == &l) return true;
00086
00087 switch (linkType) {
00088 case Prismatic:
00089 case Revolute:
00090 return Math::equals(alpha,l.alpha) && Math::equals(theta,l.theta)
00091 && Math::equals(d,l.d) && Math::equals(a,l.a) && (active==l.active);
00092 case Translating:
00093 return direction.equals(l.direction) && (active==l.active);
00094 case FixedTransform:
00095 return transform.equals(l.transform);
00096 default:
00097 throw std::runtime_error(Exception("unhandled KinematicChain::Link type"));
00098 }
00099
00100 return false;
00101 }
00102
00103
00104 base::Matrix KinematicChain::Link::kinematicTransform(const base::Vector& q) const
00105 {
00106 Assert(q.size() == dof());
00107 Matrix A(4,4);
00108
00109 if (isDHType()) {
00110 Real d = ((linkType == Prismatic) && active)?q[0]:this->d;
00111 Real theta = ((linkType == Revolute) && active)?q[0]:this->theta;
00112
00113
00114
00115
00116 Real sinAlpha = Math::sin(alpha);
00117 Real cosAlpha = Math::cos(alpha);
00118 Real sinTheta = Math::sin(theta);
00119 Real cosTheta = Math::cos(theta);
00120
00121 A(0,0) = cosTheta; A(0,1) = -sinTheta*cosAlpha; A(0,2) = sinTheta*sinAlpha; A(0,3) = a*cosTheta;
00122 A(1,0) = sinTheta; A(1,1) = cosTheta*cosAlpha; A(1,2) = -cosTheta*sinAlpha; A(1,3) = a*sinTheta;
00123 A(2,0) = 0; A(2,1) = sinAlpha; A(2,2) = cosAlpha; A(2,3) = d;
00124 A(3,0) = 0; A(3,1) = 0; A(3,2) = 0; A(3,3) = 1;
00125 }
00126 else if (linkType == Translating) {
00127 A = base::identityMatrix(4,4);
00128 Assert(direction.length() > 0);
00129 Vector3 dir(direction);
00130 dir.normalize();
00131 Real v = active?q[0]:t;
00132 A(0,3) = dir.x * v;
00133 A(1,3) = dir.y * v;
00134 A(2,3) = dir.z * v;
00135 }
00136 else if (linkType == FixedTransform) {
00137 A = base::fromMatrix4(transform);
00138 }
00139 else
00140 throw std::runtime_error(Exception("unhandled KinematicChain::Link type"));
00141
00142 return A;
00143 }
00144
00145
00146 base::array<base::Byte> KinematicChain::Link::hashCode() const
00147 {
00148 if (!hashDirty) return hash;
00149
00150
00151 MD5 md5;
00152 md5.update( (unsigned char *)(&linkType), sizeof(LinkType) );
00153 md5.update( (unsigned char *)(&active), sizeof(bool) );
00154
00155 switch (linkType) {
00156 case Translating:
00157 md5.update( (unsigned char *)(&direction.x), sizeof(Real) );
00158 md5.update( (unsigned char *)(&direction.y), sizeof(Real) );
00159 md5.update( (unsigned char *)(&direction.z), sizeof(Real) );
00160 md5.update( (unsigned char *)(&t), sizeof(Real) );
00161 break;
00162 case FixedTransform:
00163 for (Int r=1; r<=4; r++)
00164 for(Int c=1; c<=4; c++) {
00165 Real e = transform(r,c);
00166 md5.update( (unsigned char *)(&e), sizeof(Real) );
00167 }
00168 break;
00169 case Prismatic:
00170 case Revolute:
00171 md5.update( (unsigned char *)(&alpha), sizeof(Real) );
00172 md5.update( (unsigned char *)(&a), sizeof(Real) );
00173 md5.update( (unsigned char *)(&d), sizeof(Real) );
00174 md5.update( (unsigned char *)(&theta), sizeof(Real) );
00175 break;
00176 default: Assertm(false,"known joint type");
00177 }
00178
00179 md5.finalize();
00180 hash.resize(16);
00181 unsigned char *digest = md5.raw_digest();
00182 for(Int b=0; b<16; b++) hash[b] = digest[b];
00183
00184 return hash;
00185 }
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 bool KinematicChain::isDHType() const
00196 {
00197 for(Int i=0; i<links.size(); i++)
00198 if (!links[i].isDHType()) return false;
00199 return true;
00200 }
00201
00202
00203 KinematicChain KinematicChain::subChain(Int first, Int count) const
00204 {
00205 Assert( first < size() );
00206 Assert( first+count <= size() );
00207 LinkArray la(count);
00208 for(Int i=0; i<count; i++) la[i]=links[first+i];
00209 return KinematicChain(la);
00210 }
00211
00212
00213 KinematicChain& KinematicChain::operator+=(const KinematicChain& kc)
00214 {
00215 if (kc.size() > 0) {
00216 for(Int i=0; i<kc.size(); i++)
00217 links.push_back(kc[i]);
00218 computeVariables();
00219 }
00220 dirtyHash();
00221 return *this;
00222 }
00223
00224
00225 KinematicChain& KinematicChain::insert(Int pos, const KinematicChain& kc)
00226 {
00227 Assert( pos <= size() );
00228 KinematicChain newkc( subChain(0,pos) );
00229 newkc += kc;
00230 newkc += subChain(pos,size()-pos);
00231 *this = newkc;
00232 computeVariables();
00233 dirtyHash();
00234 return *this;
00235 }
00236
00237
00238 KinematicChain& KinematicChain::insert(Int pos, const Link& l)
00239 {
00240 Assert( pos <= size() );
00241 KinematicChain newkc( subChain(0,pos) );
00242 newkc += l;
00243 newkc += subChain(pos,size()-pos);
00244 *this = newkc;
00245 computeVariables();
00246 dirtyHash();
00247 return *this;
00248 }
00249
00250
00251 KinematicChain& KinematicChain::erase(Int pos)
00252 {
00253 Assert( pos < size() );
00254 KinematicChain newkc( size()-1);
00255 for(Int i=0; i<size(); i++)
00256 if (i!=pos) newkc.push_back(links[i]);
00257 *this = newkc;
00258 computeVariables();
00259 dirtyHash();
00260 return *this;
00261 }
00262
00263
00264 KinematicChain& KinematicChain::erase(Int first, Int last)
00265 {
00266 Assert( first <= last );
00267 Assert( first < size() );
00268 Assert( last <= size() );
00269 KinematicChain newkc( size()-(last-first) );
00270 for(Int i=0; i<size(); i++)
00271 if (!((i>=first) && (i<last))) newkc.push_back(links[i]);
00272 *this = newkc;
00273 computeVariables();
00274 dirtyHash();
00275 return *this;
00276 }
00277
00278
00279
00280 void KinematicChain::computeVariables()
00281 {
00282 variables.clear();
00283 for(Int l=0; l<size(); l++) {
00284 const Link& link(links[l]);
00285
00286 for(Int d=0; d<link.dof(); d++)
00287 variables.push_back( std::make_pair<Int,Int>(l,d) );
00288 }
00289 }
00290
00291
00292
00293
00294
00295 bool KinematicChain::KinematicEvaluatorInitialized = false;
00296 ref<KinematicEvaluator> KinematicChain::defaultKinematicEvaluator;
00297
00298
00299 void KinematicChain::initKinematicEvaluator()
00300 {
00301 if (!KinematicEvaluatorInitialized) {
00302 KinematicEvaluatorInitialized = true;
00303 defaultKinematicEvaluator = ref<JFKengine>(NewObj JFKengine());
00304 }
00305 kinematicEvaluator = defaultKinematicEvaluator;
00306 }
00307
00308
00309
00310 Matrix KinematicChain::getForwardKinematics(const Vector& q) const
00311 {
00312 return kinematicEvaluator->getForwardKinematics(*this,q);
00313 }
00314
00315
00316 Matrix KinematicChain::getJacobian(const Vector& q, bool includeOrientation) const
00317 {
00318 return kinematicEvaluator->getJacobian(*this, q, includeOrientation);
00319 }
00320
00321 array<Vector> KinematicChain::getJointOrigins(const Vector& q) const
00322 {
00323 return kinematicEvaluator->getJointOrigins(*this, q);
00324 }
00325
00326
00327 array<Vector> KinematicChain::getLinkOrigins(const Vector& q) const
00328 {
00329 return kinematicEvaluator->getLinkOrigins(*this, q);
00330 }
00331
00332
00333
00334
00335 Vector KinematicChain::transform(Int fromLink, Int toLink, const Vector& v, const Vector& q) const
00336 {
00337 if (fromLink == toLink) return v;
00338
00339 Int from = fromLink;
00340 Int to = toLink;
00341
00342 if (fromLink < toLink) base::swap(from, to);
00343
00344 KinematicChain subTo_From(subChain(toLink, fromLink-toLink));
00345 KinematicChain sub0_To(subChain(0, toLink));
00346 Vector subq( vectorRange(q, Range(sub0_To.dof(), subTo_From.dof())) );
00347
00348 Matrix4 T( base::toMatrix4(subTo_From.getForwardKinematics(subq)) );
00349
00350 if (fromLink < toLink) T.invert();
00351
00352 return base::fromVector3(T*base::toVector3(v));
00353 }
00354
00355
00356 Vector KinematicChain::transform(const String& fromFrame, const String& toFrame, const Vector& v, const Vector& q) const
00357 {
00358 const SInt NotFound = -2;
00359 const SInt InitialFrame = -1;
00360
00361
00362 SInt from = NotFound;
00363 if (fromFrame == initialFrame)
00364 from=InitialFrame;
00365 else {
00366 for (Int fl=0; fl<size(); fl++)
00367 if (links[fl].frameName == fromFrame) {
00368 from=fl;
00369 break;
00370 }
00371 }
00372
00373
00374 SInt to = NotFound;
00375 if (toFrame == initialFrame)
00376 to=InitialFrame;
00377 else {
00378 for (Int tl=0; tl<size(); tl++)
00379 if (links[tl].frameName == toFrame) {
00380 to=tl;
00381 break;
00382 }
00383 }
00384
00385 if (from==NotFound)
00386 throw std::invalid_argument(Exception("unknown frame name:"+fromFrame));
00387 if (to==NotFound)
00388 throw std::invalid_argument(Exception("unknown frame name:"+toFrame));
00389
00390 if (from == to) return v;
00391
00392
00393 if ((from != InitialFrame) && (to != InitialFrame))
00394 return transform(from, to, v, q);
00395
00396
00397 Unimplemented;
00398
00399 }
00400
00401
00402
00403
00404 void KinematicChain::externalize(base::Externalizer& e, String format, Real version)
00405 {
00406 if (format == "") format = "xml";
00407
00408 if (!formatSupported(format,version,e.ioType()))
00409 throw std::invalid_argument(Exception(String("format ")+format+" v"+base::realToString(version)+" unsupported"));
00410
00411 if (e.isOutput()) {
00412
00413 DOMElement* chainElem = e.createElement("kinematicchain");
00414 bool allDHLinks = isDHType();
00415 e.setElementAttribute(chainElem,"type",allDHLinks?"DH":"mixed" );
00416
00417 e.appendComment(chainElem,"Parameters describing the serial Kinematic Chain:");
00418 e.appendComment(chainElem," revolute , {alpha} , {a} , {d} , {theta} ,{minlimit},{maxlimit} ");
00419 e.appendComment(chainElem," prismatic, {alpha} , {a} , {d} , {theta} ,{minlimit},{maxlimit} ");
00420 if (!allDHLinks) {
00421 e.appendComment(chainElem," translating, {dir.x} , {dir.y} , {dir.z} ,{minlimit},{maxlimit} ");
00422
00423 }
00424
00425 e.pushContext(chainElem);
00426 for(Int l=0; l<links.size(); l++)
00427 links[l].externalize(e, format, version);
00428 e.popContext();
00429
00430 e.appendElement(chainElem);
00431
00432 } else {
00433
00434 DOMNode* context = e.context();
00435
00436 DOMElement* chainElem = e.getFirstElement(context, "kinematicchain");
00437
00438 String units = e.getDefaultedElementAttribute(chainElem, "units", "meters");
00439 if ((units != "meters") && (units != "inches"))
00440 throw externalization_error(Exception("unknown/unsupported length unit:"+units));
00441
00442 String type = e.getDefaultedElementAttribute(chainElem, "type", "mixed");
00443 if ( (type == "mixed") || (type == "DH")) {
00444
00445 links.clear();
00446 e.pushContext(chainElem);
00447
00448 while (e.getFirstChildElement(chainElem, "link",false) != 0) {
00449 Link l;
00450 l.externalize(e, format, version);
00451 if ( (!l.isDHType()) && (type == "DH"))
00452 throw externalization_error(Exception("a non D-H type joint was specified for a kinematic chain of type 'DH' (try type 'mixed')"));
00453
00454
00455 if (units != "meters") {
00456 Real scale = 1.0;
00457 if (units == "inches") scale = 0.0254;
00458
00459 if (l.isDHType()) {
00460 l.setA( l.getA()*scale );
00461 l.setD( l.getD()*scale );
00462 }
00463 else if (l.type() == KinematicChain::Link::FixedTransform) {
00464 Logln("Warning: conversion from "+units+" to meters unsupported for fixed transform links");
00465 }
00466
00467 }
00468
00469 links.push_back(l);
00470 }
00471
00472 e.popContext();
00473
00474 if (links.size() < 1)
00475 throw externalization_error(Exception("a KinematicChain must contain at least one link (<link>...</link>)."));
00476
00477 }
00478 else
00479 throw externalization_error(Exception(String("unknown KinematicChain type ")+type));
00480
00481 computeVariables();
00482 dirtyHash();
00483 }
00484 }
00485
00486
00487
00488 void KinematicChain::Link::externalize(base::Externalizer& e, String format, Real version)
00489 {
00490 if (format == "") format = "xml";
00491
00492 if (!formatSupported(format,version,e.ioType()))
00493 throw std::invalid_argument(Exception(String("format ")+format+" v"+base::realToString(version)+" unsupported"));
00494
00495 if (e.isOutput()) {
00496 DOMElement* linkElem = e.createElement("link",false);
00497 char buf[1024];
00498
00499 if (type() == Revolute) {
00500 if ( (Math::equals(jminLimit, KinematicChain::unboundedMinAngleLimit))
00501 ||(Math::equals(jmaxLimit, KinematicChain::unboundedMaxAngleLimit)) )
00502 sprintf(buf, "revolute , %8.4f, %8.4f, %8.4f, %8.4f, -inf , +inf ",
00503 Math::radToDeg(alpha), a, d, Math::radToDeg(theta));
00504 else
00505 sprintf(buf, "revolute , %8.4f, %8.4f, %8.4f, %8.4f, %8.4f, %8.4f ",
00506 Math::radToDeg(alpha), a, d, Math::radToDeg(theta),
00507 Math::radToDeg(jminLimit), Math::radToDeg(jmaxLimit));
00508 }
00509 else if (type() == Prismatic) {
00510 char minBuf[32], maxBuf[32];
00511 if (Math::equals(jminLimit, KinematicChain::unboundedMinDistLimit,999))
00512 sprintf(minBuf, "%8.4f", jminLimit);
00513 else
00514 sprintf(minBuf, "%s", "-inf");
00515
00516 if (Math::equals(jmaxLimit, KinematicChain::unboundedMaxDistLimit,999))
00517 sprintf(maxBuf, "%8.4f", jmaxLimit);
00518 else
00519 sprintf(maxBuf, "%s", "+inf");
00520
00521 sprintf(buf, "prismatic, %8.4f, %8.4f, %8.4f, %8.4f, %s, %s ",
00522 Math::radToDeg(alpha), a, d, Math::radToDeg(theta),
00523 minBuf, maxBuf);
00524 }
00525 else if (type() == Translating) {
00526 char minBuf[32], maxBuf[32];
00527 if (Math::equals(jminLimit, KinematicChain::unboundedMinDistLimit,999))
00528 sprintf(minBuf, "%8.4f", jminLimit);
00529 else
00530 sprintf(minBuf, "%s", "-inf");
00531
00532 if (Math::equals(jmaxLimit, KinematicChain::unboundedMaxDistLimit,999))
00533 sprintf(maxBuf, "%8.4f", jmaxLimit);
00534 else
00535 sprintf(maxBuf, "%s", "+inf");
00536
00537 sprintf(buf, "translating, %8.4f, %8.4f, %8.4f, %s, %s ",
00538 direction.x, direction.y, direction.z,
00539 minBuf, maxBuf);
00540 }
00541 else if (type() == FixedTransform) {
00542 Unimplemented;
00543 }
00544 else
00545 throw externalization_error(Exception(String("unknown link type")));
00546
00547 e.appendText(linkElem,String(buf));
00548 e.appendElement(linkElem);
00549 e.appendBreak();
00550 }
00551 else {
00552 DOMNode* context = e.context();
00553
00554 DOMElement* linkElem = e.getFirstChildElement(context, "link");
00555
00556 String linkText = e.getContainedText(linkElem);
00557 array<String> elts = e.splitAtDelimiter(linkText,',');
00558 elts[0] = e.removeChar(elts[0],' ');
00559 if (elts[0] == "revolute")
00560 linkType = Revolute;
00561 else if (elts[0] == "prismatic")
00562 linkType = Prismatic;
00563 else if (elts[0] == "translating")
00564 linkType = Translating;
00565 else if (elts[0] == "transform")
00566 linkType = FixedTransform;
00567 else
00568 throw externalization_error(Exception(String("link type should be either 'revolute', 'prismatic', 'translating' or 'transform'; not '")+elts[0]+"'."));
00569
00570 Vector vals( e.stringsToReals(elts) );
00571 jminLimit=jmaxLimit = 0.0;
00572 if (isDHType()) {
00573 alpha = Math::degToRad( vals[1] );
00574 a = vals[2];
00575 d = vals[3];
00576 theta = Math::degToRad( vals[4] );
00577 if (type() == Revolute) {
00578 if (vals.size() > 6) {
00579 jminLimit = Math::degToRad( vals[5] );
00580 jmaxLimit = Math::degToRad( vals[6] );
00581 }
00582 if (Math::equals(jminLimit,0) && Math::equals(jmaxLimit,0)) {
00583 jminLimit = KinematicChain::unboundedMinAngleLimit;
00584 jmaxLimit = KinematicChain::unboundedMaxAngleLimit;
00585 }
00586
00587 }
00588 else {
00589 if (vals.size() > 6) {
00590 jminLimit = vals[5];
00591 jmaxLimit = vals[6];
00592 }
00593 if (Math::equals(jminLimit,0) && Math::equals(jmaxLimit,0)) {
00594 jminLimit = unboundedMinDistLimit;
00595 jmaxLimit = unboundedMaxDistLimit;
00596 }
00597 }
00598 }
00599 else if (type() == Translating) {
00600 direction.x = vals[1];
00601 direction.y = vals[2];
00602 direction.z = vals[3];
00603 if (vals.size() > 5) {
00604 jminLimit = vals[4];
00605 jmaxLimit = vals[5];
00606 }
00607 if (Math::equals(jminLimit,0) && Math::equals(jmaxLimit,0)) {
00608 jminLimit = unboundedMinDistLimit;
00609 jmaxLimit = unboundedMaxDistLimit;
00610 }
00611 }
00612 else if (type() == FixedTransform) {
00613 Unimplemented;
00614 }
00615 else
00616 Assertm(false,"link type not implemented");
00617
00618 e.removeElement(linkElem);
00619
00620 dirtyHash();
00621 }
00622
00623 }
00624
00625
00626 base::array<base::Byte> KinematicChain::hashCode() const
00627 {
00628 if (!hashDirty) return hash;
00629
00630
00631 MD5 md5;
00632 Int chainSize = size();
00633 md5.update( (unsigned char *)(&chainSize), sizeof(Int));
00634 Int chainDOF = dof();
00635 md5.update( (unsigned char *)(&chainDOF), sizeof(Int));
00636 for(Int l=0; l<size(); l++) {
00637 array<Byte> hashCode( links[l].hashCode() );
00638 md5.update( (unsigned char *)(&(hashCode[0])), hashCode.size());
00639 }
00640 md5.finalize();
00641
00642 hash.resize(16);
00643 unsigned char *digest = md5.raw_digest();
00644 for(Int b=0; b<16; b++) hash[b] = digest[b];
00645
00646 return hash;
00647 }
00648
00649
00650
00651 std::ostream& robot::operator<<(std::ostream& out, const robot::KinematicChain::Link& l)
00652 {
00653
00654 switch(l.type()){
00655 case KinematicChain::Link::Revolute:
00656 if (l.isActive())
00657 out << "DH Revolute :" << " alpha:" << Math::radToDeg(l.getAlpha()) << ", a:" << l.getA() << ", d:" << l.getD() << ", {theta}:" << Math::radToDeg(l.getTheta()) << std::endl;
00658 else
00659 out << "DH Revolute :" << " alpha:" << Math::radToDeg(l.getAlpha()) << ", a:" << l.getA() << ", d:" << l.getD() << ", theta:" << Math::radToDeg(l.getTheta()) << std::endl;
00660 break;
00661 case KinematicChain::Link::Prismatic:
00662 if (l.isActive())
00663 out << "DH Prismatic:" << " alpha:" << Math::radToDeg(l.getAlpha()) << ", a:" << l.getA() << ", {d}:" << l.getD() << ", theta:" << Math::radToDeg(l.getTheta()) << std::endl;
00664 else
00665 out << "DH Prismatic:" << " alpha:" << Math::radToDeg(l.getAlpha()) << ", a:" << l.getA() << ", d:" << l.getD() << ", theta:" << Math::radToDeg(l.getTheta()) << std::endl;
00666 break;
00667 case KinematicChain::Link::Translating:
00668 if (l.isActive())
00669 out << "Translating : direction {t}:" << l.getT() << "*" << l.getDirection() << std::endl;
00670 else
00671 out << "Translating : direction t:" << l.getT() << "*" << l.getDirection() << std::endl;
00672 break;
00673 case KinematicChain::Link::FixedTransform:
00674 out << "Transform : translation:" << l.getTransform().column(4).toVector3() << " rotation:" << base::Orient(Matrix3(l.getTransform())).getQuat4() << std::endl;
00675 break;
00676 default: out << "unknown joint type\n";
00677 }
00678 return out;
00679 }
00680
00681
00682