Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

WaypointEngine.cc

Go to the documentation of this file.
00001 #include "WaypointEngine.h"
00002 #include "Shared/mathutils.h"
00003 
00004 void WaypointEngine::go() {
00005   isRunning=true;
00006   for(unsigned int i=0; i<3; i++) {
00007     curVel[i]=0;
00008     pathStartPos[i]=sourcePos[i]=curPos[i];
00009   }
00010   curWaypoint=waypoints.begin();
00011   if ( waypoints.empty() )
00012     return;
00013   Waypoint target(curPos[0],curPos[1],Waypoint::POSTYPE_ABSOLUTE,curPos[2],false,0,isTracking,defaultTurnSpeed);
00014   target.apply(waypoints.front(),eps);
00015   targetPos[0]=target.x;
00016   targetPos[1]=target.y;
00017   targetPos[2]=target.angle;
00018   lastUpdateTime=get_time();
00019   setTargetWaypoint(curWaypoint);
00020 }
00021 
00022 void WaypointEngine::pause() {
00023   // cout<<"Pausing"<<endl;
00024   isRunning=false;
00025 }
00026 
00027 void WaypointEngine::unpause() {
00028   if(curWaypoint==waypoints.end())
00029     go();
00030   isRunning=true;
00031   for(unsigned int i=0; i<3; i++)
00032     curVel[i]=0;
00033   lastUpdateTime=get_time();
00034 }
00035 
00036 bool WaypointEngine::cycle() {
00037   if(!isRunning)
00038     return false;
00039   
00040   unsigned int curtime=get_time();
00041   if(curWaypoint!=waypoints.end()) {
00042     computeCurrentPosition(curtime);
00043     checkNextWaypoint(curtime);
00044   }
00045   if(curWaypoint!=waypoints.end()) {
00046     computeIdeal(curtime);
00047     computeNewVelocity(curtime);
00048   }
00049   
00050   return true;
00051 }
00052 
00053 #ifdef TGT_IS_CREATE
00054 float WaypointEngine::tgtCreateTurnFudgeFactor(float angle) {
00055   float fudge;
00056   float thresholds[4] = {0.0, 2.5, 5.0, 10.0};
00057   float scales[4] = {0.0, 0.039, 0.051, 0.06};
00058   float degAngle = fabs(angle * 180.0/M_PI);
00059 
00060   bool found = false;
00061   for(unsigned int i = 1; i < 4; i++) {
00062     if(degAngle >= thresholds[i-1] && degAngle < thresholds[i]) {
00063       if((i-1) != 0)
00064   fudge = ((scales[i] - scales[i-1])/(thresholds[i]-thresholds[i-1]))*degAngle + (2 * scales[i-1]) - scales[i];
00065       else
00066   fudge = ((scales[i] - scales[i-1])/(thresholds[i]-thresholds[i-1]))*degAngle;
00067       found = true;
00068     }
00069   }
00070 
00071   if(!found)
00072     fudge = scales[3];
00073 
00074   if(angle <= 0)
00075     fudge *= -1;
00076 
00077   return fudge;
00078 }
00079 #endif
00080 
00081 float WaypointEngine::fudgedAngle(float originalAngle) {
00082 #ifdef TGT_IS_CREATE
00083   return originalAngle + tgtCreateTurnFudgeFactor(originalAngle);
00084 #else
00085   return originalAngle;
00086 #endif
00087 }
00088 
00089 void WaypointEngine::addEgocentricWaypoint(float forward, float left, float angle, bool angleIsRelative, float fwdSpeed, float turnSpeed) {
00090   waypoints.push_back(Waypoint(forward,left,Waypoint::POSTYPE_EGOCENTRIC,fudgedAngle(angle),angleIsRelative,fwdSpeed,isTracking,turnSpeed>=0?turnSpeed:defaultTurnSpeed));
00091 }
00092 
00093 
00094 void WaypointEngine::setTargetWaypoint(WaypointListIter_t iter) {
00095   //cout << "Moving to waypoint " << iter << endl;
00096   bool isLoop=false;
00097   if(iter==waypoints.end()) {
00098     if(isLooping && waypoints.size()>0) { //restart at beginning
00099       iter=waypoints.begin();
00100       for(unsigned int i=0; i<3; i++)
00101         pathStartPos[i]=curPos[i];
00102       isLoop=true;
00103     } else { //not looping, halt
00104       isRunning=false;
00105       curWaypoint=iter;
00106       for(unsigned int i=0; i<3; i++) {
00107         sourcePos[i]=targetPos[i];
00108         targetPos[i]=curPos[i];
00109         curVel[i]=0;
00110       }       
00111       return;
00112     }
00113   }
00114   if(iter== (iter+1) || isLoop)
00115     for(unsigned int i=0; i<3; i++)
00116       sourcePos[i]=targetPos[i];
00117   else
00118     for(unsigned int i=0; i<3; i++)
00119       sourcePos[i]=curPos[i];
00120   
00121   Waypoint target;
00122   if(isLoop)
00123     target=calcAbsoluteCoords(iter,pathStartPos[0],pathStartPos[1],pathStartPos[2]);
00124   else
00125     target=calcAbsoluteCoords(iter);
00126   targetPos[0]=target.x;
00127   targetPos[1]=target.y;
00128   targetPos[2]=target.angle;
00129   
00130   float dx=targetPos[0]-sourcePos[0];
00131   float dy=targetPos[1]-sourcePos[1];
00132   waypointDistance=std::sqrt(dx*dx+dy*dy);
00133   waypointTime=get_time();
00134   curWaypoint=iter;
00135   float radiusRatio=std::sin(target.arc/2);
00136   arcRadius = (radiusRatio==0) ? 0 : (waypointDistance/2)/radiusRatio;
00137   pathLength = arcRadius!=0 ? arcRadius*target.arc : waypointDistance;
00138   //
00139   
00140   // std::cout << "Target is now: ("<<targetPos[0]<<','<<targetPos[1]<<"  hdg" <<targetPos[2]<<")" << std::endl;
00141 }
00142 
00143 
00144 unsigned int WaypointEngine::getBinSize() const {
00145   unsigned int numPrecision=9;
00146   unsigned int wpSize=0;
00147   unsigned int boilerplateSize=0;
00148   boilerplateSize+=strlen("#WyP\n");
00149   boilerplateSize+=strlen("#add_{point|arc} {ego|off|abs} x_val y_val {hold|follow} angle_val speed_val arc_val\n");
00150   wpSize+=strlen("max_turn_speed ")+numPrecision+1;
00151   wpSize+=strlen("track_path false\n");
00152   wpSize+=strlen("add_point ")+4+numPrecision*5+1*5+strlen("follow");
00153   boilerplateSize+=strlen("#END\n");
00154   return wpSize*waypoints.size()+boilerplateSize;
00155 }
00156 
00157 unsigned int WaypointEngine::loadBuffer(const char buf[], unsigned int len, const char* filename) {
00158   unsigned int origlen=len;
00159   waypoints.clear();
00160   if(strncmp("#WyP\n",buf,5)!=0 && strncmp("#WyP\r",buf,5)!=0) {
00161     return 0;
00162   }
00163   
00164   float turn=defaultTurnSpeed; //init to current mode, in case file doesn't specify
00165   bool track=isTracking;
00166   char cmd[40];
00167   char posType[40];
00168   float x_val=0;
00169   float y_val=0;
00170   char angType[40];
00171   bool ang_val=0;
00172   float angle_val=0;
00173   float speed_val=0;
00174   float arc_val=0;
00175   unsigned int linenum=1;
00176   while(len<=origlen && len>0) {
00177     //    printf("%d %.9s\n",linenum+1,buf);
00178     if(buf[0]=='\r') {
00179       buf++; len--;
00180       if(buf[0]=='\n') {
00181         buf++; len--;
00182       }
00183       linenum++;
00184       continue;
00185     }
00186     if(buf[0]=='\n') {
00187       buf++; len--;
00188       linenum++;
00189       continue;
00190     }
00191     if(buf[0]=='#') {
00192       if(strncmp("#END\n",buf,5)==0 || strncmp("#END\r",buf,5)==0) {
00193         return origlen-len+5;
00194       } else if(strncmp("#END\r\n",buf,6)==0) {
00195         return origlen-len+6;
00196       } else {
00197         while(len>0 && *buf!='\n' && *buf!='\r') {len--;buf++;}
00198         if(*buf=='\n') { //in case of \r\n
00199           buf++;
00200           len--;
00201         }
00202         linenum++;
00203         continue;
00204       }
00205     }
00206     int used=-1U;
00207     sscanf(buf,"%40s%n",cmd,&used);
00208     if(!checkInc(used,buf,len,"*** ERROR Waypoint list load corrupted - ran out of room line %d\n",linenum)) return 0;
00209     if(strncasecmp(cmd,"add_point",9)==0 || strncasecmp(cmd,"add_arc",7)==0) {
00210       sscanf(buf,"%40s %g %g %40s %g %g %g%n",posType,&x_val,&y_val,angType,&angle_val,&speed_val,&arc_val,&used);
00211       if(!checkInc(used,buf,len,"*** ERROR Waypoint list load corrupted - bad read on add at line %d\n",linenum)) return 0;
00212       if(strncasecmp(angType,"hold",4)==0)
00213         ang_val=false;
00214       else if(strncasecmp(angType,"follow",6)==0)
00215         ang_val=true;
00216       else {
00217         printf("*** ERROR WaypointEngine: Invalid angle value type %s\n",angType);
00218         return 0;
00219       }
00220       if(strncasecmp(cmd,"add_point",9)==0) {
00221         if(strncasecmp(posType,"ego",3)==0)
00222           addEgocentricWaypoint(x_val,y_val,angle_val,ang_val,speed_val);
00223         else if(strncasecmp(posType,"off",3)==0)
00224           addOffsetWaypoint(x_val,y_val,angle_val,ang_val,speed_val);
00225         else if(strncasecmp(posType,"abs",3)==0)
00226           addAbsoluteWaypoint(x_val,y_val,angle_val,ang_val,speed_val);
00227         else {
00228           printf("*** ERROR WaypointEngine: Invalid position type %s\n",posType);
00229           return 0;
00230         }           
00231         waypoints.back().arc=arc_val;
00232       } else {
00233         if(strncasecmp(posType,"ego",3)==0)
00234           addEgocentricArc(x_val,y_val,angle_val,ang_val,speed_val,arc_val);
00235         else if(strncasecmp(posType,"off",3)==0)
00236           addOffsetArc(x_val,y_val,angle_val,ang_val,speed_val,arc_val);
00237         else if(strncasecmp(posType,"abs",3)==0)
00238           addAbsoluteArc(x_val,y_val,angle_val,ang_val,speed_val,arc_val);
00239         else {
00240           printf("*** ERROR WaypointEngine: Invalid position type %s\n",posType);
00241           return 0;
00242         }
00243       }
00244       waypoints.back().trackPath=track;
00245       waypoints.back().turnSpeed=turn;
00246     } else if(strncasecmp(cmd,"track_path",10)==0) {
00247       int track_tmp;
00248       sscanf(buf,"%d%n",&track_tmp,&used);
00249       track=track_tmp;
00250       if(!checkInc(used,buf,len,"*** ERROR Waypoint load corrupted - bad read on track_path line %d\n",linenum)) return 0;
00251     } else if(strncasecmp(cmd,"max_turn_speed",14)==0) {
00252       sscanf(buf,"%g%n",&turn,&used);
00253       if(!checkInc(used,buf,len,"*** ERROR Waypoint load corrupted - bad read on max_turn_speed line %d\n",linenum)) return 0;
00254     } else {
00255       printf("*** ERROR WaypointEngine: Invalid command %s\n",cmd);
00256       return 0;
00257     }
00258     
00259     linenum++;
00260   }
00261   std::cout << "*** WARNING WaypointEngine: load missing #END" << std::endl;
00262   return origlen-len;
00263 }
00264 
00265 unsigned int WaypointEngine::saveBuffer(char buf[], unsigned int len) const {
00266   unsigned int origLen=len;
00267   unsigned int used;
00268   unsigned int cnt=0;
00269   
00270   used=snprintf(buf,len,"#WyP\n");
00271   if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on header\n")) return 0;
00272   
00273   used=snprintf(buf,len,"#add_{point|arc} {ego|off|abs} x_val y_val {hold|follow} angle_val speed_val arc_val\n");
00274   if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on header\n")) return 0;
00275   
00276   //set our state variables so we'll be forced to output the first set
00277   float turn=waypoints.front().turnSpeed-1;
00278   bool track=!waypoints.front().trackPath;
00279   
00280   for(WaypointListConstIter_t it=waypoints.begin(); it!=waypoints.end(); it++) {
00281     if(it->turnSpeed!=turn) {
00282       turn=it->turnSpeed;
00283       used=snprintf(buf,len,"max_turn_speed %g\n",turn);
00284       if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on waypoint %d turnSpeed\n",cnt)) return 0;
00285     }
00286     if(it->trackPath!=track) {
00287       track=it->trackPath;
00288       used=snprintf(buf,len,"track_path %d\n",track);
00289       if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on waypoint %d\n trackPath",cnt)) return 0;
00290     }
00291     const char * posType=NULL;
00292     switch(it->posType) {
00293       case Waypoint::POSTYPE_EGOCENTRIC:
00294         posType="EGO"; break;
00295       case Waypoint::POSTYPE_OFFSET:
00296         posType="OFF"; break;
00297       case Waypoint::POSTYPE_ABSOLUTE:
00298         posType="ABS"; break;
00299     }
00300     if(it->arc!=0)
00301       used=snprintf(buf,len,"add_point %s %g %g %s %g %g %g\n",posType,it->x,it->y,(it->angleIsRelative?"FOLLOW":"HOLD"),it->angle,it->speed,it->arc);
00302     else //todo - store center of circle
00303       used=snprintf(buf,len,"add_point %s %g %g %s %g %g %g\n",posType,it->x,it->y,(it->angleIsRelative?"FOLLOW":"HOLD"),it->angle,it->speed,it->arc);
00304     if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on waypoint %d\n",cnt)) return 0;
00305     cnt++;
00306   }
00307   
00308   used=snprintf(buf,len,"#END\n");
00309   if(!checkInc(used,buf,len,"*** ERROR Waypoint list save failed on footer\n")) return 0;
00310   
00311   return origLen-len;
00312 }
00313 
00314 void WaypointEngine::init() {
00315   eps[0]=eps[1]= 5.0f; //5 mm
00316   eps[2]=0.0175f; //1 degree
00317   for(unsigned int i=0; i<3; i++)
00318     pathStartPos[i]=targetPos[i]=sourcePos[i]=curPos[i]=curVel[i]=0;
00319   for(unsigned int i=0; i<4; i++)
00320     idealPos[i]=0;
00321 }
00322 
00323 /*! This is replicated in WaypointList, so any updates should be made there as well */
00324 void WaypointEngine::fixArc(float arc) {
00325   Waypoint& center=waypoints.back();
00326   float cdx=center.x; //center delta
00327   float cdy=center.y; //center delta
00328   if(center.posType==Waypoint::POSTYPE_ABSOLUTE) {
00329     //have to find location of waypoint before last one
00330     if(waypoints.size()<=1) {
00331       cdx-=pathStartPos[0];
00332       cdy-=pathStartPos[1];
00333     } else {
00334       Waypoint start=calcAbsoluteCoords(waypoints.end()-2);
00335       cdx-=start.x;
00336       cdy-=start.y;
00337     }
00338   }
00339   float r=std::sqrt(cdx*cdx+cdy*cdy); //radius of circle
00340   float ca=std::atan2(cdy,cdx); //angle to center of circle
00341   center.x-=r*std::cos(ca-arc); //now x is the endpoint
00342   center.y-=r*std::sin(ca-arc); //now y is the endpoint
00343   center.arc=arc;
00344 }
00345 
00346 void WaypointEngine::computeCurrentPosition(unsigned int t) {
00347   float dt=(t-lastUpdateTime)/1000.f;
00348   float df=dt*curVel[0];
00349   float ds=dt*curVel[1];
00350   float da=dt*curVel[2];
00351   
00352   float avgAngle=curPos[2]+da/2;
00353   float ca=std::cos(avgAngle);
00354   float sa=std::sin(avgAngle);
00355   
00356   curPos[0]+=df*ca-ds*sa;
00357   curPos[1]+=df*sa+ds*ca;
00358   curPos[2]+=da;
00359   curPos[2]=mathutils::normalizeAngle(curPos[2]);
00360   
00361   lastUpdateTime=t;
00362 }
00363 
00364 void WaypointEngine::checkNextWaypoint(unsigned int /*t*/) {
00365   float rx=targetPos[0]-curPos[0];
00366   float ry=targetPos[1]-curPos[1];
00367   float ra=targetPos[2]-curPos[2];
00368   if(fabs(rx)<eps[0] && fabs(ry)<eps[1] && fabs(ra)<eps[2]) {
00369     setTargetWaypoint(curWaypoint+1);
00370   }
00371 }
00372 
00373 void WaypointEngine::computeIdeal(unsigned int t) {
00374   Waypoint& cur=(*curWaypoint);
00375   if(cur.trackPath) {
00376     float dx=targetPos[0]-sourcePos[0];
00377     float dy=targetPos[1]-sourcePos[1];
00378     //--------------- here
00379     float dt=(t-waypointTime)/1000.f; //time we've been traveling towards current waypoint
00380     float ideal_travel=dt*cur.speed; //distance we should have covered
00381     float p=1; //will be set to percentage of path length to waypoint we have covered
00382     if(pathLength!=0) {
00383       p=ideal_travel/pathLength;
00384       if(p>1)
00385         p=1;
00386     }
00387     if(arcRadius==0) { //radius is "infinite" - straight line solution
00388       idealPos[0]=sourcePos[0]+dx*p;
00389       idealPos[1]=sourcePos[1]+dy*p;
00390       idealPos[2]=targetPos[2];
00391       idealPos[3]=std::atan2(dy,dx);
00392     } else {
00393       // have to find center of arc's circle
00394       float bearing=std::atan2(dy,dx); //bearing of target from source
00395       float center_bearing=bearing+((float)M_PI-cur.arc)/2; //bearing of center from source
00396       float cx=sourcePos[0]+arcRadius*std::cos(center_bearing); //x pos of center
00397       float cy=sourcePos[1]+arcRadius*std::sin(center_bearing); //y pos of center
00398       float arc_bearing=center_bearing-(float)M_PI+cur.arc*p; //bearing from center to current ideal location
00399       //sout->printf("%g %g (%g,%g) %g\n",center_bearing,arcRadius,cx,cy,arc_bearing);
00400       idealPos[0]=cx+arcRadius*std::cos(arc_bearing);
00401       idealPos[1]=cy+arcRadius*std::sin(arc_bearing);
00402       idealPos[3]=arc_bearing+(float)M_PI/2;
00403       idealPos[2]=cur.angle;
00404       if(cur.angleIsRelative)
00405         idealPos[2]+=idealPos[3];
00406       idealPos[2]=mathutils::normalizeAngle(idealPos[2]);
00407       idealPos[3]=mathutils::normalizeAngle(idealPos[3]);
00408     }
00409   } else {
00410     idealPos[0]=curPos[0];
00411     idealPos[1]=curPos[1];
00412     float rx=targetPos[0]-curPos[0];
00413     float ry=targetPos[1]-curPos[1];
00414     if(std::abs(rx)<eps[0] && std::abs(ry)<eps[1]) {
00415       idealPos[2]=targetPos[2];
00416     } else {
00417       idealPos[2]=cur.angle;
00418       if(cur.angleIsRelative) {
00419         float dx=targetPos[0]-curPos[0];
00420         float dy=targetPos[1]-curPos[1];
00421         idealPos[2]+=std::atan2(dy,dx);
00422       }
00423       idealPos[2]=mathutils::normalizeAngle(idealPos[2]);
00424     }
00425     idealPos[3]=std::atan2(ry,rx);
00426     if(arcRadius!=0) {
00427       //---------------here
00428       float dt=(t-waypointTime)/1000.f; //time we've been traveling towards current waypoint
00429       float ideal_travel=dt*cur.speed; //distance we should have covered
00430       float p=1; //will be set to percentage of path length to waypoint we have covered
00431       if(pathLength!=0) {
00432         p=ideal_travel/pathLength;
00433         if(p>1)
00434           p=1;
00435       }
00436       float arc=cur.arc*(1-p)/2;
00437       idealPos[2]=mathutils::normalizeAngle(idealPos[2]-arc);
00438       idealPos[3]=mathutils::normalizeAngle(idealPos[3]-arc);
00439     }
00440   }
00441 }
00442   
00443 void WaypointEngine::computeNewVelocity(unsigned int /*t*/) {
00444   Waypoint& cur=(*curWaypoint);
00445   
00446   //first we'll start with the velocity we would use if we were on path
00447   //determine distance remaining (only an approximation for arcing paths)
00448   float dx=targetPos[0]-idealPos[0];
00449   float dy=targetPos[1]-idealPos[1];
00450   float spd=std::sqrt(dx*dx+dy*dy)/(FrameTime*NumFrames)*1000.f;
00451   if(spd>cur.speed) {
00452     //we're far away - go at full speed
00453     curVel[0]=cur.speed*std::cos(idealPos[3]-curPos[2]);
00454     curVel[1]=cur.speed*std::sin(idealPos[3]-curPos[2]);
00455   } else {
00456     //we're about to overshoot... just go fast enough to get on target
00457     curVel[0]=spd*std::cos(idealPos[3]-curPos[2]);
00458     curVel[1]=spd*std::sin(idealPos[3]-curPos[2]);
00459   }
00460   if(arcRadius==0)
00461     curVel[2]=0;
00462   else
00463     curVel[2]=cur.speed/arcRadius;
00464   
00465   //sout->printf("ideal vel: %g %g %g\n",curVel[0],curVel[1],curVel[2]);
00466   
00467   // then we'll add the errors
00468   float ex=idealPos[0]-curPos[0];
00469   float ey=idealPos[1]-curPos[1];
00470   float ed=std::sqrt(ex*ex+ey*ey);
00471   float ehead=std::atan2(ey,ex)-curPos[2];
00472   float ea=mathutils::normalizeAngle(idealPos[2]-curPos[2]);
00473   float easpd=ea/(FrameTime*NumFrames)*1000.f;
00474   easpd=mathutils::limitRange(easpd,-cur.turnSpeed,cur.turnSpeed);
00475   curVel[0]+=Pcorr*ed*std::cos(ehead);
00476   curVel[1]+=Pcorr*ed*std::sin(ehead);
00477   curVel[2]+=easpd;
00478 }
00479 
00480 /*! @file
00481  * @brief Defines WaypointEngine, which provides computation and management of a desired path through a series of waypoints
00482  * @author ejt (Creator)
00483  */

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