Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Kinematics.cc

Go to the documentation of this file.
00001 #include "Kinematics.h"
00002 #include "Shared/Config.h"
00003 #include "Shared/WorldState.h"
00004 #include "Events/VisionObjectEvent.h"
00005 #include "Shared/string_util.h"
00006 #include <sstream>
00007 #include <iostream>
00008 #include <limits>
00009 #include <cmath>
00010 
00011 #ifdef PLATFORM_APERIOS
00012 //! this is normally defined in <math.h>, but the OPEN-R cross-compiler isn't configured right
00013 template<typename T> static inline int signbit(T x) { return x<0; }
00014 #endif
00015 
00016 using namespace std;
00017 
00018 Kinematics * kine = NULL;
00019 Kinematics::InterestPointMap Kinematics::ips;
00020 bool Kinematics::staticsInited=false;
00021 
00022 void
00023 Kinematics::init() {
00024   checkStatics();
00025   for(unsigned int i=0; i<NumReferenceFrames; ++i)
00026     jointMaps[i]=NULL;
00027   if(!root.loadFile(config->makePath(config->motion.kinematics).c_str())) {
00028     std::cerr << "Could not load " << config->motion.makePath(config->motion.kinematics) << ", forward and inverse kinematics unavailable." << std::endl;
00029     return;
00030   }
00031   root.buildChildMap(jointMaps,0,NumReferenceFrames);
00032 }
00033 
00034 void Kinematics::initStatics() {
00035   if(!staticsInited) {
00036     staticsInited=true;
00037   }
00038 }
00039 
00040 Kinematics::~Kinematics() {
00041 }
00042 
00043 fmat::Transform
00044 Kinematics::baseToLocal(const PlaneEquation& plane) {
00045   std::cout << "plane " << plane.getDirection() << ' ' << plane.getDisplacement() << std::endl;
00046   fmat::Column<3> p = plane.getDirection() / plane.getDirection().norm();
00047   float s = hypotf(p[0],p[1]); // |cross(p,[0 0 -1])| = sin(an)
00048   if(std::abs(s)<std::numeric_limits<float>::epsilon()*100) 
00049     return fmat::Transform::IDENTITY;
00050   
00051   // cross(p,[0 0 -1]) : 
00052   float x = -p[1]/s;
00053   float y = p[0]/s;
00054   // float z=0
00055   
00056   float d = -plane.getDisplacement();
00057   
00058   float c = -p[2]; // dot(p,[0 0 -1]) = cos(an) = p[2]
00059   float t = 1 - c;
00060   std::cout << "axis " << x << ' ' << y << " angle " << std::asin(s) << ' ' << std::acos(c) << std::endl;
00061   float v[fmat::Transform::CAP] = { t*x*x + c, t*x*y, -y*s, t*x*y, t*y*y + c, x*s, y*s, -x*s, c, p[0]*d, p[1]*d, p[2]*d };
00062   std::cout << fmat::Transform(v) << std::endl;
00063   return fmat::Transform(v);
00064 }
00065 
00066 void 
00067 Kinematics::getInterestPoint(const std::string& name, unsigned int& link, fmat::Column<3>& ip, bool convertToJoint/*=false*/) {
00068   link=-1U;
00069   ip=fmat::Column<3>();
00070   for(unsigned int i=0; i<NumReferenceFrames; ++i) {
00071     if(strcmp(outputNames[i],name.c_str())==0) {
00072       link = i;
00073       ip=pack(0,0,0);
00074     }
00075   }
00076   if(convertToJoint && NumOutputs<=link && link<NumReferenceFrames) {
00077     KinematicJoint * kj = jointMaps[link];
00078     while(kj!=NULL) {
00079       if(kj->outputOffset < NumOutputs) {
00080         ip = jointMaps[link]->getT(*kj) * ip;
00081         link = kj->outputOffset;
00082         return;
00083       }
00084     }
00085   }
00086 }
00087 
00088 fmat::Column<3>
00089 Kinematics::getInterestPoint(unsigned int link, const std::string& name) {
00090   vector<string> ipNames = string_util::tokenize(name,",");
00091   fmat::Column<3> ans = pack(0,0,0);
00092   unsigned int cnt=0;
00093   for(vector<string>::const_iterator it=ipNames.begin(); it!=ipNames.end(); ++it) {
00094     string srcName = string_util::trim(*it);
00095     if(srcName.size()==0)
00096       continue;
00097     unsigned int srcLink=-1U;
00098     fmat::Column<3> srcPos;
00099     getInterestPoint(srcName,srcLink,srcPos);
00100     if(srcLink==-1U)
00101       throw std::runtime_error(std::string("Kinematics::getLinkInterestPoint: interest point unknown ").append(name));
00102     if(link!=srcLink)
00103       srcPos = linkToLink(srcLink,link)*srcPos;
00104     ans+=srcPos;
00105     ++cnt;
00106   }
00107   if(cnt==0)
00108     throw std::runtime_error(std::string("Kinematics::getLinkInterestPoint called with empty string"));
00109   return ans/cnt;
00110 }
00111 
00112 
00113 #ifdef TGT_HAS_LEGS
00114 void
00115 Kinematics::calcLegHeights(const fmat::Column<3>& down, float heights[NumLegs]) {
00116   update();
00117   //initialize to the height of the ball of the foot
00118   for(unsigned int i=0; i<NumLegs; i++) {
00119     if(jointMaps[FootFrameOffset+i]==NULL) {
00120       heights[i]=std::numeric_limits<typeof(heights[0])>::max();
00121     } else {
00122       fmat::Column<3> ip_b=jointMaps[FootFrameOffset+i]->getWorldPosition();
00123       float h = -fmat::dotProduct(ip_b,down);
00124       h-=BallOfFootRadius; //add the ball's radius
00125       heights[i]=h;
00126     }
00127   }
00128   //see if any interest points are lower
00129   for(InterestPointMap::const_iterator it=ips.begin(); it!=ips.end(); ++it) {
00130     unsigned int leg;
00131     if(jointMaps[it->second.output]==NULL)
00132       continue;
00133     else if(it->second.output>=LegOffset || it->second.output<LegOffset+NumLegJoints)
00134       leg = (it->second.output - LegOffset) / JointsPerLeg;
00135     else if(it->second.output>=FootFrameOffset || it->second.output<FootFrameOffset+NumLegs)
00136       leg = (it->second.output - FootFrameOffset);
00137     else
00138       continue;
00139     fmat::Column<3> ip_b=jointMaps[it->second.output]->getFullT() * it->second.p;
00140     float h = -fmat::dotProduct(ip_b,down);
00141     if(h<heights[leg])
00142       heights[leg]=h;
00143   }
00144 }
00145 
00146 LegOrder_t
00147 Kinematics::findUnusedLeg(const fmat::Column<3>& down) {
00148   float heights[NumLegs];
00149   calcLegHeights(down,heights);
00150   //Find the highest foot
00151   unsigned int highleg=0;
00152   for(unsigned int i=1; i<NumLegs; i++)
00153     if(heights[i]>heights[highleg])
00154       highleg=i;
00155   //cout << "High: " << highleg << endl;
00156   return static_cast<LegOrder_t>(highleg);
00157 }
00158 
00159 PlaneEquation Kinematics::calculateGroundPlane() {
00160 #ifdef TGT_HAS_ACCELEROMETERS
00161   fmat::Column<3> down=fmat::pack(state->sensors[BAccelOffset],-state->sensors[LAccelOffset],state->sensors[DAccelOffset]);
00162   if(down.sumSq()<0.01)
00163     down=fmat::pack(0,0,-1); //default to a down vector if sensors don't give a significant indication of gravity
00164   return calculateGroundPlane(down);
00165 #else
00166   return calculateGroundPlane(fmat::pack(0,0,-1));
00167 #endif
00168 }
00169 
00170 // TODO comment out name tracking (and this following #include) once we're sure it's working
00171 #include "PostureEngine.h"
00172 PlaneEquation
00173 Kinematics::calculateGroundPlane(const fmat::Column<3>& down) {
00174   update();
00175   fmat::Matrix<3,3> lowip; //3 points define a plane
00176   float heights[3];
00177   unsigned int legs[3];
00178   std::string names[3];
00179   //initialize to max float
00180   for(unsigned int i=0; i<3; i++) {
00181     heights[i]=std::numeric_limits<typeof(heights[0])>::max();;
00182     legs[i]=-1U;
00183   }
00184   //Check the balls of the foot
00185   for(unsigned int i=0; i<NumLegs; i++) {
00186     if(jointMaps[FootFrameOffset+i]==NULL)
00187       continue;
00188     fmat::Column<3> ip_b=jointMaps[FootFrameOffset+i]->getWorldPosition();
00189     float h = -fmat::dotProduct(ip_b,down);
00190     h-=BallOfFootRadius; //add the ball's radius
00191     if(h<heights[0]) {
00192       if(h<heights[1]) {
00193         heights[0]=heights[1];
00194         lowip.column(0)=lowip.column(1);
00195         legs[0]=legs[1];
00196         names[0]=names[1];
00197         if(h<heights[2]) {
00198           heights[1]=heights[2];
00199           lowip.column(1)=lowip.column(2);
00200           legs[1]=legs[2];
00201           names[1]=names[2];
00202           
00203           heights[2]=h;
00204           lowip.column(2)=ip_b;
00205           legs[2]=i;
00206           names[2]="paw"; names[2]+=(char)('0'+i);
00207         } else {
00208           heights[1]=h;
00209           lowip.column(1)=ip_b;
00210           legs[1]=i;
00211           names[1]="paw"; names[1]+=(char)('0'+i);
00212         }
00213       } else {
00214         heights[0]=h;
00215         lowip.column(0)=ip_b;
00216         legs[0]=i;
00217         names[0]="paw"; names[0]+=(char)('0'+i);
00218       }
00219     }
00220   }
00221   //cout << "Ground plane initial: " << names[0] <<" ("<<heights[0]<<") " << names[1] << " ("<<heights[1]<<") " << names[2] << " ("<<heights[2]<<")"<< endl;
00222   
00223   //now check interest points
00224   for(InterestPointMap::const_iterator it=ips.begin(); it!=ips.end(); ++it) {
00225 #ifdef TGT_IS_AIBO
00226     if(it->first.substr(0,3)=="Toe")
00227       continue;
00228 #endif
00229     if(jointMaps[it->second.output]==NULL)
00230       continue;
00231     fmat::Column<3> ip_b=jointMaps[it->second.output]->getFullT() * it->second.p;
00232     float h = -fmat::dotProduct(ip_b,down);
00233     if(h<heights[0]) {
00234       unsigned int leg;
00235       if(it->second.output>=LegOffset && it->second.output<LegOffset+NumLegJoints)
00236         leg = (it->second.output - LegOffset) / JointsPerLeg;
00237       else if(it->second.output>=FootFrameOffset && it->second.output<FootFrameOffset+NumLegs)
00238         leg = (it->second.output - FootFrameOffset);
00239       else
00240         leg=-1U;
00241       
00242       if(h<heights[1]) {
00243         if(h<heights[2]) {
00244           if(leg==-1U || (legs[1]!=leg && legs[2]!=leg)) {
00245             heights[0]=heights[1];
00246             lowip.column(0)=lowip.column(1);
00247             legs[0]=legs[1];
00248             names[0]=names[1];
00249           }
00250           if(leg==-1U || legs[2]!=leg) {
00251             heights[1]=heights[2];
00252             lowip.column(1)=lowip.column(2);
00253             if(legs[2]!=leg)
00254               legs[1]=legs[2];
00255             names[1]=names[2];
00256           }
00257           
00258           heights[2]=h;
00259           lowip.column(2)=ip_b;
00260           legs[2]=leg;
00261           names[2]=(*it).first;
00262         } else {
00263           if(leg!=-1U && legs[2]==leg)
00264             continue;
00265           if(leg==-1U || legs[1]!=leg) {
00266             heights[0]=heights[1];
00267             lowip.column(0)=lowip.column(1);
00268             legs[0]=legs[1];
00269             names[0]=names[1];
00270           }
00271           heights[1]=h;
00272           lowip.column(1)=ip_b;
00273           legs[1]=leg;
00274           names[1]=(*it).first;
00275         }
00276       } else {
00277         if(leg!=-1U && (legs[1]==leg || legs[2]==leg))
00278           continue;
00279         heights[0]=h;
00280         lowip.column(0)=ip_b;
00281         legs[0]=leg;
00282         names[0]=(*it).first;
00283       }
00284     }
00285   }
00286   
00287   //Fit a plane to the remaining 3 feet
00288   fmat::Column<3> ones(1.f);
00289   fmat::Column<3> dir;
00290   try {
00291     dir = invert(lowip.transpose())*ones;
00292   } catch(...) {
00293     std::cout << "Exception during ground plane processing, saving current posture..." << std::flush;
00294     if(PostureEngine * tpose=dynamic_cast<PostureEngine*>(this)) {
00295       tpose->setSaveFormat(true,state);
00296       tpose->saveFile("/error.pos");
00297     } else {
00298       PostureEngine pose;
00299       pose.takeSnapshot();
00300       pose.setWeights(1);
00301       pose.setSaveFormat(true,state);
00302       pose.saveFile("/error.pos");
00303     }
00304     std::cout << "Wrote current sensor info \"error.pos\" on memstick" << std::endl;
00305     cout << "Ground plane was using " << names[0] <<" ("<<heights[0]<<") " << names[1] << " ("<<heights[1]<<") " << names[2] << " ("<<heights[2]<<")"<< endl;
00306     cout << lowip;
00307     throw;
00308   }
00309   // todo this doesn't handle plane through the origin, what if plane is z=0 (e.g. [0,0,1,0])
00310   return PlaneEquation(dir, 1);
00311 }
00312 
00313 #else // NO LEGS -- constant ground plane
00314 
00315 PlaneEquation Kinematics::calculateGroundPlane() {
00316   return PlaneEquation(0,0,1,0);
00317 }
00318 
00319 PlaneEquation
00320 Kinematics::calculateGroundPlane(const fmat::Column<3>& down) {
00321   return PlaneEquation(-down, 0); // flat x-y plane through the origin with z pointing up
00322 }
00323 
00324 #endif
00325 
00326 fmat::Column<4> 
00327 Kinematics::projectToPlane(
00328       unsigned int j, const fmat::Column<3>& r_j,
00329       unsigned int b, const PlaneEquation& p_b,
00330       unsigned int f,
00331       float objCentroidHeight)
00332 {
00333   update();
00334   if((j!=b && (jointMaps[j]==NULL || jointMaps[b]==NULL)) || (f!=b && (jointMaps[f]==NULL || jointMaps[b]==NULL)) )
00335     return fmat::Column<4>(0.f);
00336   
00337   /*! Mathematical implementation:
00338    *  
00339    *  We'll convert the ray to the plane's reference frame, solve there.
00340    *  We find a point on the ray (ro_b) and the direction of the ray (rv_b).
00341    *  rv_b does not need to be normalized because we're going to find
00342    *  a scaling factor for it, and that factor accounts for current magnitude.
00343    *
00344    *  Proof, p=plane normal vector, d=plane displacement, r = ray direction, o = ray offset, x = [x y z] coordinates, t = scaling factor */
00345    
00346   /* Non-latex/doxygen version for easier reading in source (UTF-8):
00347    p⃑ · x⃑ = d    (definition of plane)
00348    x⃑ = r⃑(t) + o⃑    (definition of ray through point)
00349    p⃑ · ( r⃑(t)  + o⃑ ) = d
00350    p⃑ · r⃑(t) + p⃑ · o⃑ = d
00351    t ( p⃑ · r⃑ ) = d - p⃑ · o⃑
00352    t = (d - p⃑ · o⃑) / (p⃑ · r⃑)
00353    t = dist / align
00354    substitute back to find intersection: x⃑ = r⃑(t) + o⃑
00355    */
00356    
00357    /*! @f{eqnarray*} 
00358    \vec p \cdot\vec x &=& d \qquad{\rm(Definition\ of\ plane)}\\
00359    \vec x &=& t \vec r + \vec o \qquad{\rm(Definition\ of\ ray\ through\ point)}\\
00360    \vec p \cdot ( t \vec r  + \vec o ) &=& d \\
00361    \vec p \cdot t \vec r + \vec p \cdot \vec o &=& d \\
00362    t ( \vec p \cdot \vec r ) &=& d - \vec p \cdot \vec o \\
00363    t &=& \frac{d - \vec p \cdot \vec o}{\vec p \cdot \vec r} = \frac{dist}{align} \\
00364    && {\rm (substitute\ back\ to\ find\ } \vec x = t \vec r + \vec o{\rm )}
00365    @f}
00366    */
00367   fmat::Column<3> ro_b, rv_b;
00368   if(j==b)
00369     rv_b=r_j;
00370   else {
00371     fmat::Transform tr = jointMaps[j]->getT(*jointMaps[b]);
00372     ro_b = tr.translation();
00373     rv_b = tr.rotation() * r_j;
00374   }
00375   //std::cout << "ro_b: " << ro_b << "  rv_b: " << rv_b << std::endl;
00376   
00377   /*! Find distance from the ray offset (ro_b) and the closest point on the plane. */
00378   float dist = p_b.getDisplacement() - fmat::dotProduct(p_b.getDirection(),ro_b);
00379   /*! Object height is applied along the plane normal toward the ray origin
00380    *  (we assume the ray source is "above" ground) */
00381   dist += signbit(dist) ? objCentroidHeight : -objCentroidHeight;
00382   /*! Find scaling factor by projecting ray vector (rv_b) onto plane normal. */
00383   float align = fmat::dotProduct(p_b.getDirection(),rv_b);
00384   //std::cout << "dist " << dist << "  align " << align << std::endl;
00385   
00386   /*! Intersection point will be rv_b*dist/align + ro_b, but need to watch out
00387    *  for case where align==0 (rv_b and plane are parallel, no intersection) */
00388   fmat::Column<4> hit;
00389   if(std::abs(align)>numeric_limits<float>::epsilon())
00390     hit = fmat::pack(rv_b*(dist/align)+ro_b,1.f);
00391   else if(align!=0 && dist!=0 && signbit(align)!=signbit(dist))
00392     hit = fmat::pack(-rv_b,std::abs(align));
00393   else
00394     hit = fmat::pack(rv_b,std::abs(align));
00395   //std::cout << "hit " << hit << std::endl;
00396   
00397   return (f==b) ? hit : jointMaps[b]->getT(*jointMaps[f]) * hit;
00398 }
00399 
00400 fmat::Column<4>
00401 Kinematics::projectToGround(const VisionObjectEvent& visObj, const PlaneEquation& gndPlane, float objCentroidHeight) {
00402 #ifndef TGT_HAS_CAMERA
00403   return fmat::Column<4>();
00404 #else
00405   fmat::Column<3> imgRay_c;
00406   config->vision.computeRay(visObj.getCenterX(),visObj.getCenterY(), imgRay_c[0],imgRay_c[1],imgRay_c[2]);
00407   return projectToPlane(CameraFrameOffset, imgRay_c, BaseFrameOffset, calculateGroundPlane(), BaseFrameOffset, objCentroidHeight);
00408 #endif
00409 }
00410 
00411 
00412 void
00413 Kinematics::update() const {
00414   if(lastUpdateTime == state->lastSensorUpdateTime)
00415      return;
00416   for(unsigned int j=0; j<NumOutputs; j++) {
00417     if(jointMaps[j]!=NULL)
00418       jointMaps[j]->setQ(state->outputs[j]);
00419   }
00420   lastUpdateTime = state->lastSensorUpdateTime;
00421 }
00422 
00423 
00424 /*! @file
00425  * @brief 
00426  * @author ejt (Creator)
00427  */
00428 

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