Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

GaitedFootsteps.cc

Go to the documentation of this file.
00001 #include "Shared/RobotInfo.h"
00002 // only makes sense for legged, but also aperios compiler lacks tr1/functional_hash.h
00003 #if defined(TGT_HAS_LEGS) && !defined(TGT_IS_AIBO) && (!defined(__GNUC__) || __GNUC__>=4)
00004 
00005 #include "GaitedFootsteps.h"
00006 #include "Motion/KinematicJoint.h"
00007 #include "Motion/IKSolver.h"
00008 #include <algorithm>
00009 #include <iterator>
00010 
00011 fmat::fmatReal GaitedFootsteps::State::ROUND=1/6.0;
00012 const fmat::fmatReal GaitedFootsteps::State::ORIROUND=30/M_PI;
00013 
00014 GaitedFootsteps::~GaitedFootsteps() {
00015   delete kinematics;
00016   kinematics=NULL;
00017   for(std::vector<PlannerObstacle2D*>::const_iterator it=obstacles.begin(); it!=obstacles.end(); ++it)
00018     delete *it;
00019   obstacles.clear();
00020 }
00021 
00022 float GaitedFootsteps::heuristic(const State& st, const State& goal) const {
00023   if(speed>0)
00024     return (st.pos - goal.pos).norm() / speed * relaxation;
00025   else
00026     return (st.pos - goal.pos).norm() / p.strideLenX * flightDuration * relaxation;
00027 }
00028 
00029 bool GaitedFootsteps::validate(const State& st) const {
00030   return true;
00031   /*const std::set<size_t>& group = (st.phase==0) ? groups.back() : groups[st.phase-1];
00032   for(std::set<size_t>::const_iterator lit=group.begin(); lit!=group.end(); ++lit) {
00033     for(std::vector< PlannerObstacle* >::const_iterator oit=obstacles.begin(); oit!=obstacles.end(); ++oit) {
00034       if((*oit)->collides(st.footPos[*lit])) {
00035         return false;
00036       }
00037     }
00038   }
00039   return true;*/
00040 }
00041 
00042 //! Generates a vector of successor states, paired with the cost to get to that state from @a st
00043 /*! Note that this implementation returns a reference to a static instance, which is not thread safe but slightly faster.
00044  *
00045  * for each leg which can be lifted:
00046  *     expand each step position for that leg
00047  *     body motion: stance motion before lift and during flight?
00048  */
00049 const std::vector<std::pair<float,GaitedFootsteps::State> >& GaitedFootsteps::expand(const State* parent, const State& st, const State& goal) const {
00050   //std::cout << "Expanding " << st << std::endl;
00051   static std::vector<std::pair<float,GaitedFootsteps::State> > ans;
00052   ans.clear();
00053   
00054   // check stability of support feet...
00055   std::vector<fmat::Column<2> > support;
00056   support.reserve(NumLegs - groups[st.phase].size());
00057   fmat::Column<3> gravOriAxis = fmat::crossProduct(fmat::UNIT3_Z,gravity);
00058   fmat::Quaternion gravOri = fmat::Quaternion::fromAxisAngle(gravOriAxis,std::asin(gravOriAxis.norm()));
00059   for(size_t i=0; i<NumLegs; ++i) {
00060     if(groups[st.phase].count(i) == 0) {
00061       fmat::Column<3> lpos3 = fmat::pack( st.oriRot.transpose()*(st.footPos[i]-st.pos), 0);
00062       support.push_back(fmat::SubVector<2,const fmat::fmatReal>(gravOri * lpos3));
00063       // also check feet kinematics... might be unnecessary
00064       p.projectToGround(ground, p.groundPlane[3], gravity, lpos3);
00065       IKSolver& solver = childMap[FootFrameOffset + i]->getIK();
00066       bool success = solver.solve(fmat::Column<3>(),*childMap[FootFrameOffset + i],IKSolver::Point(lpos3));
00067       if(!success) // foot has stretched out of reach
00068         return ans;
00069     }
00070   }
00071   ConvexPolyObstacle supportPoly;
00072   supportPoly.hull(std::set<fmat::Column<2> >(support.begin(),support.end()));
00073   fmat::Column<3> com; float totalMass;
00074   kinematics->sumCenterOfMass(com,totalMass); // not accounting for motion of flight legs on center of mass (com)!
00075   if(!supportPoly.collides(fmat::SubVector<2,const fmat::fmatReal>(gravOri * com)))
00076     return ans;
00077 
00078   const fmat::Column<2> gd = goal.pos - st.pos;
00079   const float goalDir = stepReorient ? std::atan2(gd[1],gd[0])-st.oriAngle : 0;
00080   addCandidate(parent, st, goalDir, ans, gd.norm());
00081   addCandidate(parent, st, goalDir+(float)M_PI, ans);
00082   for(size_t i=1; i<ncand/2; ++i) {
00083     float per = i/float(ncand/2);
00084     //per*=per; // weights steps "forward" (but commented-out because too tight)
00085     addCandidate(parent, st, goalDir + static_cast<float>(M_PI)*per, ans);
00086     addCandidate(parent, st, goalDir - static_cast<float>(M_PI)*per, ans);
00087   }
00088   
00089   /*if(groups.size()>2) {
00090     // if more than two groups, consider a no-op to go out of phase
00091     // this relies on checking leg kinematics however, to ensure a
00092     // leg doesn't get ignored and dragged along
00093     ans.push_back(std::make_pair(0,st));
00094     State& nxt = ans.back().second;
00095     if(++nxt.phase >= groups.size())
00096       nxt.phase=0;
00097   }*/
00098   
00099   /*for(size_t i=0; i<ans.size(); ++i) {
00100     std::cout << ans[i].first << ' ' << ans[i].second << std::endl;
00101   }*/
00102 
00103   return ans;
00104 }
00105 
00106 PlannerObstacle2D* GaitedFootsteps::checkObstacles(const fmat::Column<2>& pt) const {
00107   for(std::vector< PlannerObstacle2D* >::const_iterator oit=obstacles.begin(); oit!=obstacles.end(); ++oit) {
00108     //cout << ' ' << (*oit)->collides(lpos2);
00109     if((*oit)->collides(pt)) {
00110       //cout << endl;
00111       return *oit;
00112     }
00113   }
00114   return NULL;
00115 }
00116 
00117 bool GaitedFootsteps::addRotation(const State& st, float strideAngle, float strideDist, const fmat::Column<2>& strideDir, const fmat::Column<2>& stride, std::vector<std::pair<float,GaitedFootsteps::State> >& candidates, float maxDist) const {
00118   float rotAngle = 2 * std::atan2(strideDist/2,rotDist); // heuristic to limit rotation speed
00119   if(strideDir[0]>0) {
00120     //forward motion
00121     if(strideAngle>0) {
00122       //walking in positive rotation
00123       rotAngle = std::min(strideAngle,rotAngle); // don't over-rotate
00124     } else {
00125       // walking in negative rotation
00126       rotAngle = -std::min(-strideAngle,rotAngle); // don't over-rotate
00127     }
00128   } else {
00129     if(strideAngle<0)
00130       rotAngle=-rotAngle;
00131   }
00132   fmat::Matrix<2,2> rot = fmat::rotation2D(st.oriAngle+rotAngle);
00133   
00134   std::vector< std::pair<size_t,fmat::Column<2> > > footPos;
00135   footPos.reserve(groups[st.phase].size());
00136   for(std::set<size_t>::const_iterator lit=groups[st.phase].begin(); lit!=groups[st.phase].end(); ++lit) {
00137     // lpos3 is egocentric 3D coordinates
00138     fmat::Column<2> lpos2 = st.pos + rot*(neutrals[*lit] + stride);
00139     //cout << "Leg " << *lit << " at " << lpos2;
00140     PlannerObstacle2D* obs = checkObstacles(lpos2);
00141     if(obs!=NULL) {
00142       if(!stepRehab)
00143         return false;
00144       
00145       fmat::Column<2> v = obs->gradient(lpos2);
00146       lpos2 += v + v*(0.25f/v.norm()); // add a small margin to avoid numerical instability on obstacle boundary
00147       // convert new lpos2 back to egocentric lpos3
00148       fmat::Column<3> lpos3 = fmat::pack( rot.transpose()*(lpos2-st.pos), 0);
00149       
00150       // now check IK is still valid (or update lpos2 with reached position?)
00151       //bool success=true;
00152       p.projectToGround(ground, p.groundPlane[3], gravity, lpos3);
00153       IKSolver& solver = childMap[FootFrameOffset + *lit]->getIK();
00154       bool success = solver.solve(fmat::Column<3>(),*childMap[FootFrameOffset + *lit],IKSolver::Point(lpos3));
00155       /*for(unsigned int j=0; j<JointsPerLeg; ++j) {
00156         KinematicJoint * ckj = childMap[LegOffset + JointsPerLeg*leg + j];
00157         if(ckj!=NULL)
00158           sd.legJoints[j] = ckj->getQ();
00159       }*/
00160       // retrieve achieved position in case gait sends leg out of reach
00161       //lpos3 = childMap[FootFrameOffset+leg]->getWorldPosition();
00162       
00163       if(!success)
00164         return false;
00165       
00166       // if still colliding, give up
00167       if(checkObstacles(lpos2)!=NULL)
00168         return false;
00169     }
00170     footPos.push_back(std::make_pair(*lit,lpos2));
00171     //cout << endl;
00172   }
00173   
00174   float t = strideDist / groups.size(); // distance body travels before next step
00175   if(maxDist>0)
00176     t = std::min(maxDist,t);
00177   
00178   candidates.push_back(std::make_pair(speed>0?t/speed:flightDuration,st));
00179   State& nxt = candidates.back().second;
00180   if(++nxt.phase >= groups.size())
00181     nxt.phase=0;
00182   for(size_t i=0; i<footPos.size(); ++i)
00183     nxt.footPos[footPos[i].first] = footPos[i].second;
00184   nxt.oriAngle += rotAngle / groups.size();
00185   nxt.oriRot = fmat::rotation2D(nxt.oriAngle);
00186   
00187   nxt.pos += st.oriRot*strideDir*t;
00188   return true;
00189 }
00190 
00191 void GaitedFootsteps::addCandidate(const State* parent, const State& st, float angle, std::vector<std::pair<float,GaitedFootsteps::State> >& candidates, float maxDist/*=0*/) const {
00192   const fmat::Column<2> dir = fmat::pack(std::cos(angle),std::sin(angle));
00193   if(parent!=NULL && fmat::dotProduct(dir,st.pos-parent->pos)<-0.5)
00194     return; // don't consider actions which reverse previous motion
00195   const fmat::Column<2> d = fmat::pack(p.strideLenX*dir[1], p.strideLenY*dir[0]);
00196   const float strideDist = p.strideLenX*p.strideLenY / d.norm(); // distance of this step
00197   
00198   const fmat::Column<2> stride = dir*strideDist;
00199   addRotation(st,angle,strideDist,dir,stride,candidates,maxDist);
00200   //if(addRotation(st,angle,strideDist,dir,stride,candidates,maxDist))
00201   //  return;
00202   
00203   std::vector< std::pair<size_t,fmat::Column<2> > > footPos;
00204   footPos.reserve(groups[st.phase].size());
00205   for(std::set<size_t>::const_iterator lit=groups[st.phase].begin(); lit!=groups[st.phase].end(); ++lit) {
00206     // lpos3 is egocentric 3D coordinates
00207     fmat::Column<2> lpos2 = st.pos + st.oriRot * (neutrals[*lit] + stride);
00208     //cout << "Leg " << *lit << " at " << lpos2;
00209     PlannerObstacle2D* obs = checkObstacles(lpos2);
00210     if(obs!=NULL) {
00211       if(!stepRehab)
00212         return;
00213       
00214       fmat::Column<2> v = obs->gradient(lpos2);
00215       lpos2 += v + v*(0.25f/v.norm()); // add a small margin to avoid numerical instability on obstacle boundary
00216       // convert new lpos2 back to egocentric lpos3
00217       fmat::Column<3> lpos3 = fmat::pack(st.oriRot.transpose()*(lpos2-st.pos),0);
00218       
00219       // now check IK is still valid (or update lpos2 with reached position?)
00220       //bool success=true;
00221       p.projectToGround(ground, p.groundPlane[3], gravity, lpos3);
00222       IKSolver& solver = childMap[FootFrameOffset + *lit]->getIK();
00223       bool success = solver.solve(fmat::Column<3>(),*childMap[FootFrameOffset + *lit],IKSolver::Point(lpos3));
00224       /*for(unsigned int j=0; j<JointsPerLeg; ++j) {
00225         KinematicJoint * ckj = childMap[LegOffset + JointsPerLeg*leg + j];
00226         if(ckj!=NULL)
00227           sd.legJoints[j] = ckj->getQ();
00228       }*/
00229       // retrieve achieved position in case gait sends leg out of reach
00230       //lpos3 = childMap[FootFrameOffset+leg]->getWorldPosition();
00231       
00232       if(!success)
00233         return;
00234       
00235       // if still colliding, give up
00236       if(checkObstacles(lpos2)!=NULL)
00237         return;
00238     }
00239     footPos.push_back(std::make_pair(*lit,lpos2));
00240     //cout << endl;
00241   }
00242   
00243   float t = strideDist / groups.size(); // distance body travels before next step
00244   if(maxDist>0)
00245     t = std::min(maxDist,t);
00246   
00247   candidates.push_back(std::make_pair(speed>0?t/speed:flightDuration,st));
00248   State& nxt = candidates.back().second;
00249   if(++nxt.phase >= groups.size())
00250     nxt.phase=0;
00251   for(size_t i=0; i<footPos.size(); ++i)
00252     nxt.footPos[footPos[i].first] = footPos[i].second;
00253   
00254   nxt.pos += st.oriRot*dir*t;
00255   
00256   //std::cout << "\tCandidate " << candidates.back().first << ' ' << nxt.pos << std::endl;
00257   
00258   /*for(size_t leg=0; leg<NumLegs; ++leg) {
00259     StepData sd;
00260     (fmat::SubVector<2>)sd.pos = neutralPos[leg] + stride;
00261     XWalkParameters::projectToGround(ground, p.groundPlane[3], gravity, sd.pos);
00262     IKSolver& solver = childMap[FootFrameOffset+leg]->getIK();
00263     bool success = solver.solve(fmat::Column<3>(),*childMap[FootFrameOffset+leg],IKSolver::Point(sd.pos));
00264     for(unsigned int j=0; j<JointsPerLeg; ++j) {
00265       KinematicJoint * ckj = childMap[LegOffset + JointsPerLeg*leg + j];
00266       if(ckj!=NULL)
00267         sd.legJoints[j] = ckj->getQ();
00268     }
00269     // retrieve achieved position in case gait sends leg out of reach
00270     sd.pos = childMap[FootFrameOffset+leg]->getWorldPosition();
00271     //std::cout << sd.pos[0] <<'\t' << sd.pos[1] << '\t' << sd.pos[2] << std::endl;
00272     if(!success)
00273       std::cerr << "WARNING: unable to reach footstep at angle " << ang/M_PI*180 << "° on leg " << leg << std::endl;
00274     candidates[leg].push_back(sd);
00275   }*/
00276 }
00277 
00278 void GaitedFootsteps::setGait(const KinematicJoint& kj, const XWalkParameters& xp, size_t discretization) {
00279   std::fill(childMap, childMap+NumReferenceFrames, static_cast<KinematicJoint*>(NULL));
00280   delete kinematics;
00281   kinematics = dynamic_cast<KinematicJoint*>(kj.clone());
00282   kinematics->buildChildMap(childMap,0,NumReferenceFrames);
00283 
00284   ncand = discretization;
00285   //p = xp; // buggy
00286   p.setParseTree(xp.stealParseTree());
00287   p.readParseTree();
00288   
00289   p.packGroundGravity(ground,gravity);
00290   
00291   // each mid-stride position is base primarily on the location of the first leg joint
00292   // the strideBias then offsets the x position, and the stance width offsets the y position
00293   neutrals.resize(NumLegs);
00294   fmat::Column<2> neutralPos[NumLegs];
00295   for(unsigned int leg=0; leg<NumLegs; ++leg) {
00296     XWalkParameters::LegParameters& legParam = p.legParams[leg];
00297             #if defined(TGT_IS_MANTIS)
00298             const unsigned int JointsPerLeg = 4; // hack! fix it later
00299             #endif
00300         KinematicJoint * legRoot = childMap[LegOffset+JointsPerLeg*leg];
00301     fmat::Column<3> fstPos = legRoot->getWorldPosition();
00302     neutralPos[leg][0] = fstPos[0] + legParam.strideBias;
00303     neutralPos[leg][1] = ((fstPos[1]<0) ? -*legParam.stanceWidth : *legParam.stanceWidth);
00304     
00305     neutrals[leg] = neutralPos[leg];
00306     //std::cout << neutrals[leg] << std::endl;
00307     //(fmat::SubVector<2>)neutrals[leg] = neutralPos[leg];
00308     //XWalkParameters::projectToGround(ground, p.groundPlane[3], gravity, neutrals[leg]);
00309     
00310     flightDuration = std::max(p.legParams[leg].flightDuration/1000.f, flightDuration);
00311   }
00312 
00313   // Rotations will be done about a point a "body width" away, yields natural
00314   // (and efficient) arcing motion instead of stop-and-turn jitters
00315   // So, have to find a general estimate of how far this is: max of most distant neutral pos
00316   rotDist=0;
00317   for(unsigned int leg=0; leg<NumLegs; ++leg) {
00318     rotDist = std::max(neutrals[leg].norm(), rotDist);
00319   }
00320   
00321   // The footstep is half the stride length in each direction
00322   // However, feet can have different air times, which decreases their stride
00323   // since they spend more of the cycle in the air
00324   // stride[leg] = groundTime[leg] * speed
00325   // groundTime[leg] = period - airTime[leg]
00326   // stride[leg] = (period - airTime[leg]) * speed
00327   
00328   // maxGroundTime = maxStride / speed
00329   // period = maxGroundTime + minAirTime
00330   // period = maxStride / speed + minAirTime
00331   
00332   // stride[leg] = (maxStride / speed + minAirTime - airTime[leg]) * speed
00333   // stride[leg] = maxStride + minAirTime*speed - airTime[leg]*speed
00334   
00335   // For now, ignoring this timing issue, assume all feet take equal-length steps
00336   /*std::vector< std::vector<StepData> > candidates;
00337   candidates.clear();
00338   candidates.resize(NumLegs);
00339   for(size_t i=0; i<discretization; ++i) {
00340     float ang = static_cast<float>(M_PI)*2*i/discretization;
00341     fmat::Column<2> dir = fmat::pack(std::cos(ang),std::sin(ang));
00342     fmat::Column<2> d = fmat::pack(p.strideLenX*dir[1], p.strideLenY*dir[0]);
00343     float t = p.strideLenX*p.strideLenY / std::sqrt(d[0]*d[0] + d[1]*d[1]); // distance along dir
00344     fmat::Column<2> stride = dir*t/2; // divide by 2 because stride is diameter, we want radius
00345     
00346     for(size_t leg=0; leg<NumLegs; ++leg) {
00347       StepData sd;
00348       (fmat::SubVector<2>)sd.pos = neutralPos[leg] + stride;
00349       XWalkParameters::projectToGround(ground, p.groundPlane[3], gravity, sd.pos);
00350       IKSolver& solver = childMap[FootFrameOffset+leg]->getIK();
00351       bool success = solver.solve(fmat::Column<3>(),*childMap[FootFrameOffset+leg],IKSolver::Point(sd.pos));
00352       for(unsigned int j=0; j<JointsPerLeg; ++j) {
00353         KinematicJoint * ckj = childMap[LegOffset + JointsPerLeg*leg + j];
00354         if(ckj!=NULL)
00355           sd.legJoints[j] = ckj->getQ();
00356       }
00357       // retrieve achieved position in case gait sends leg out of reach
00358       sd.pos = childMap[FootFrameOffset+leg]->getWorldPosition();
00359       //std::cout << sd.pos[0] <<'\t' << sd.pos[1] << '\t' << sd.pos[2] << std::endl;
00360       if(!success)
00361         std::cerr << "WARNING: unable to reach footstep at angle " << ang/M_PI*180 << "° on leg " << leg << std::endl;
00362       candidates[leg].push_back(sd);
00363     }
00364   }*/
00365   
00366   groups.clear();
00367   std::map< float, std::set<size_t> > phasedGroups;
00368   for(size_t leg=0; leg<NumLegs; ++leg)
00369     phasedGroups[p.legParams[leg].flightPhase].insert(leg);
00370   for(std::map< float, std::set<size_t> >::const_iterator it=phasedGroups.begin(); it!=phasedGroups.end(); ++it)
00371     groups.push_back(it->second);
00372   
00373   for(size_t i=0; i<groups.size(); ++i) {
00374     std::cout << "Group " << i << ": ";
00375     for(std::set<size_t>::const_iterator git=groups[i].begin(); git!=groups[i].end(); ++git)
00376       std::cout << "\t" << *git;
00377     std::cout << std::endl;
00378   }
00379 
00380   State::ROUND = 1 / (std::min(p.strideLenX,p.strideLenY)/groups.size()/2);
00381   std::cout << p.strideLenX << ' ' << p.strideLenY << " Rounding factor " << State::ROUND << std::endl;
00382 }
00383 
00384 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:41 2016 by Doxygen 1.6.3