SUMO - Simulation of Urban MObility

euler.cpp

Go to the documentation of this file.
00001 /*
00002 #include "vcl_iostream.h"
00003 #include <vcl_fstream.h>
00004 #include "vcl_complex.h"
00005 #include "vcl_cmath.h"
00006 */
00007 
00008 #include "points.h"
00009 #include "BiArc.h"
00010 #include "euler.h" 
00011 #include <utils/common/StdDefs.h>
00012 #include <complex>
00013 #include <iostream>
00014 
00015 #define eGamma    1e-8
00016 
00017 //global lookup table: This is only instantiated when required and cleaned upon program exit
00018 static EulerSpiralLookupTable *globalEulerSpiralLookupTable;
00019 
00020 //ensures single instantiation of the global variable
00021 EulerSpiralLookupTable* EulerSpiralLookupTable::get_globalEulerSpiralLookupTable()
00022 {
00023   if (! globalEulerSpiralLookupTable)
00024     globalEulerSpiralLookupTable = new EulerSpiralLookupTable();
00025 
00026   return globalEulerSpiralLookupTable;
00027 }
00028 
00029 EulerSpiralLookupTable::EulerSpiralLookupTable()
00030 {
00031 }
00032 
00033 EulerSpiralLookupTable::~EulerSpiralLookupTable()
00034 {
00035 }
00036 
00037 //delta theta values for the table (tells you about the accuracy of the lookup)
00038 double EulerSpiralLookupTable::dt()
00039 {
00040   return _dt;
00041 }
00042 
00043 double EulerSpiralLookupTable::theta(int N)
00044 {
00045   return _theta[N];
00046 }
00047 
00048 double EulerSpiralLookupTable::k0(double start_angle, double end_angle)
00049 {
00050   //assume it is already 0-2Pi
00051   double sangle, eangle;
00052 
00053   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00054   else                  sangle = start_angle;
00055 
00056   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00057   else                eangle = end_angle;
00058   
00059   //output bilinear interpolated data from the tables 
00060   int ilow, ihigh, jlow, jhigh;
00061 
00062   ilow = (int)floor((sangle+M_PI)/_dt);
00063   ihigh = (int)ceil((sangle+M_PI)/_dt);
00064 
00065   jlow = (int)floor((eangle+M_PI)/_dt);
00066   jhigh = (int)ceil((eangle+M_PI)/_dt);
00067 
00068   double slow = _theta[ilow];
00069   double elow = _theta[jlow];
00070 
00071   double a = (sangle - slow)/_dt;
00072   double b = (eangle - elow)/_dt;
00073 
00074   double k0 = (1-a)*(1-b)*ES_k0[ilow][jlow] + a*(1-b)*ES_k0[ihigh][jlow] +
00075               (1-a)*b*ES_k0[ilow][jhigh] + a*b*ES_k0[ihigh][jhigh];
00076 
00077   return k0;
00078 }
00079 
00080 double EulerSpiralLookupTable::k1(double start_angle, double end_angle)
00081 {
00082   //output bilinear interpolated data from the tables
00083 
00084   //assume it is already 0-2Pi
00085   double sangle, eangle;
00086 
00087   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00088   else                  sangle = start_angle;
00089 
00090   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00091   else                eangle = end_angle;
00092   
00093   //output bilinear interpolated data from the tables 
00094   int ilow, ihigh, jlow, jhigh;
00095 
00096   ilow = (int)floor((sangle+M_PI)/_dt);
00097   ihigh = (int)ceil((sangle+M_PI)/_dt);
00098 
00099   jlow = (int)floor((eangle+M_PI)/_dt);
00100   jhigh = (int)ceil((eangle+M_PI)/_dt);
00101 
00102   double slow = _theta[ilow];
00103   double elow = _theta[jlow];
00104 
00105   double a = (sangle - slow)/_dt;
00106   double b = (eangle - elow)/_dt;
00107 
00108   double k1 = (1-a)*(1-b)*ES_k1[ilow][jlow] + a*(1-b)*ES_k1[ihigh][jlow] +
00109               (1-a)*b*ES_k1[ilow][jhigh] + a*b*ES_k1[ihigh][jhigh];
00110 
00111   return k1;
00112 }
00113 
00114 double EulerSpiralLookupTable::gamma(double start_angle, double end_angle)
00115 {
00116   //output bilinear interpolated data from the tables
00117   
00118   //assume it is already 0-2Pi
00119   double sangle, eangle;
00120 
00121   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00122   else                  sangle = start_angle;
00123 
00124   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00125   else                eangle = end_angle;
00126   
00127   //output bilinear interpolated data from the tables 
00128   int ilow, ihigh, jlow, jhigh;
00129 
00130   ilow = (int)floor((sangle+M_PI)/_dt);
00131   ihigh = (int)ceil((sangle+M_PI)/_dt);
00132 
00133   jlow = (int)floor((eangle+M_PI)/_dt);
00134   jhigh = (int)ceil((eangle+M_PI)/_dt);
00135 
00136   double slow = _theta[ilow];
00137   double elow = _theta[jlow];
00138 
00139   double a = (sangle - slow)/_dt;
00140   double b = (eangle - elow)/_dt;
00141 
00142   double gamma = (1-a)*(1-b)*ES_gamma[ilow][jlow] + a*(1-b)*ES_gamma[ihigh][jlow] +
00143               (1-a)*b*ES_gamma[ilow][jhigh] + a*b*ES_gamma[ihigh][jhigh];
00144 
00145   return gamma;
00146 }
00147 
00148 double EulerSpiralLookupTable::L(double start_angle, double end_angle)
00149 {
00150   //output bilinear interpolated data from the tables
00151   
00152   //assume it is already 0-2Pi
00153   double sangle, eangle;
00154 
00155   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00156   else                  sangle = start_angle;
00157 
00158   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00159   else                eangle = end_angle;
00160   
00161   //output bilinear interpolated data from the tables 
00162   int ilow, ihigh, jlow, jhigh;
00163 
00164   ilow = (int)floor((sangle+M_PI)/_dt);
00165   ihigh = (int)ceil((sangle+M_PI)/_dt);
00166 
00167   jlow = (int)floor((eangle+M_PI)/_dt);
00168   jhigh = (int)ceil((eangle+M_PI)/_dt);
00169 
00170   double slow = _theta[ilow];
00171   double elow = _theta[jlow];
00172 
00173   double a = (sangle - slow)/_dt;
00174   double b = (eangle - elow)/_dt;
00175 
00176   double L = (1-a)*(1-b)*ES_L[ilow][jlow] + a*(1-b)*ES_L[ihigh][jlow] +
00177               (1-a)*b*ES_L[ilow][jhigh] + a*b*ES_L[ihigh][jhigh];
00178 
00179   return L;
00180 }
00181 
00182 
00183 // Computes the Euler spiral for the given params
00184 //if the global lookup table is available, it looks up the ES params first and then optimizes them
00185 //this should dramatically cut down in the time to optimize
00186 void EulerSpiral::compute_es_params ()
00187 {
00188   //compute scaling distance
00189   double d = euc_distance(params.start_pt, params.end_pt);
00190   params.psi = angle0To2Pi(atan2(params.end_pt.y()-params.start_pt.y(),params.end_pt.x()-params.start_pt.x()));
00191 
00192   //degeneracy check
00193   if (d<eError)
00194     return; 
00195 
00196   //first compute a biarc estimate
00197   _bi_arc_estimate.set_start_params(params.start_pt, params.start_angle);
00198   _bi_arc_estimate.set_end_params(params.end_pt, params.end_angle);
00199   _bi_arc_estimate.compute_biarc_params();
00200 
00201   //get the total turning angle::This is an important parameter because
00202   //it defines the one solution out of many possible solutions
00203   params.turningAngle = _bi_arc_estimate.params.K1*_bi_arc_estimate.params.L1 +
00204                  _bi_arc_estimate.params.K2*_bi_arc_estimate.params.L2;
00205 
00206   //From here on, normlize the parameters and use these to perform the optimization
00207 
00208   double k0_init_est = _bi_arc_estimate.params.K1*d;
00209   double L_init_est = _bi_arc_estimate.params.L()/d;
00210   double dstep = 0.1;
00211 
00212   //Alternately, we can get the initial values from the lookup table and perform 
00213   //the optimization from there
00214   //double k0_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->k0(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle));
00215   //double L_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->L(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle));
00216   //double dstep = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->dt()/4;
00217 
00218   //then perform a simple gradient descent to find the real solution
00219   double error = compute_error(k0_init_est, L_init_est);
00220   double prev_error = error;
00221   
00222   double k0 = k0_init_est;
00223   double L = L_init_est;
00224 
00225   double e1, e2, e3, e4;
00226 
00227   for (int i=0;i<MAX_NUM_ITERATIONS;i++)
00228   {
00229      if (error<eError)
00230         break;
00231     
00232      e1 = compute_error(k0 + dstep, L);
00233      e2 = compute_error(k0 - dstep, L);
00234      e3 = compute_error(k0, L + dstep);
00235      if (L>dstep)  e4 = compute_error(k0, L - dstep);
00236     
00237      error = MIN2(MIN2(e1,e2),MIN2(e3,e4));
00238     
00239      if (error>prev_error)
00240      {
00241        dstep = dstep/2;
00242        continue;
00243      }
00244     
00245      if    (error==e1)  k0 = k0 + dstep;
00246      else if (error==e2) k0 = k0 - dstep;
00247      else if (error==e3) L = L + dstep;
00248      else if (error==e4) L = L - dstep;
00249       
00250      prev_error = error;
00251   }
00252 
00253   //store the parameters
00254   params.K0 = k0/d;
00255   params.L = L*d;
00256   params.gamma = 2*(params.turningAngle - k0*L)/(L*L)/(d*d);
00257   params.K2 = (k0 + params.gamma*L)/d;
00258   params.error = error;
00259 }
00260 
00261 //compute the extrinsic points
00262 void EulerSpiral::computeSpiral(std::vector<Point2D<double> > &spiral, double ds, int NPts)
00263 {
00264   if (ds==0 && NPts==0){
00265     //use default values
00266     NPts = 100;
00267     ds = params.L/NPts;
00268   }
00269 
00270   spiral.clear();
00271   spiral.push_back(params.start_pt);
00272 
00273   double s=ds;
00274   if (NPts == 0) 
00275     NPts = (int) (params.L / ds);
00276   for (int i=1; i<NPts; i++,s+=ds){
00277     Point2D<double> cur_pt = compute_end_pt(s);
00278     spiral.push_back(cur_pt);
00279   }
00280   spiral.push_back(params.end_pt);
00281 }
00282 
00284 // Supporting functions
00286 
00287 Point2D<double> EulerSpiral::compute_es_point(EulerSpiralParams& es_params, double arclength, bool bNormalized)
00288 {
00289   params = es_params;
00290   return compute_end_pt(params.K0, params.gamma, arclength, bNormalized);
00291 }
00292 
00293 Point2D<double> EulerSpiral::compute_end_pt(double arclength, bool bNormalized)
00294 {
00295   return compute_end_pt(params.K0, params.gamma, arclength, bNormalized);
00296 }
00297 
00298 Point2D<double> EulerSpiral::compute_end_pt(double k0, double gamma, double L, bool bNormalized)
00299 {
00300   Point2D<double> start_pt;
00301   Point2D<double> end_pt;
00302 
00303   double theta;
00304 
00305   if (bNormalized){
00306     start_pt = Point2D<double>(0,0);
00307     theta = CCW(params.psi, params.start_angle);
00308   }
00309   else {
00310     start_pt = params.start_pt;
00311     theta = params.start_angle;
00312   }
00313 
00314   if (L==0)
00315     return start_pt;
00316 
00317   if (fabs(gamma)<eGamma) 
00318   { 
00319     if (fabs(k0)<eK)
00320     {
00321       //straight line
00322       end_pt.setX(start_pt.getX()+L*cos(theta));
00323       end_pt.setY(start_pt.getY()+L*sin(theta));
00324     }
00325     else 
00326     { 
00327       //circle
00328       double const_term = 1.0/k0;
00329       end_pt.setX(start_pt.getX()+const_term*(sin(k0*L+theta)-sin(theta)));
00330       end_pt.setY(start_pt.getY()-const_term*(cos(k0*L+theta)-cos(theta))); 
00331     }
00332     return end_pt;
00333   }
00334 
00335   double const1 = sqrt(M_PI*fabs(gamma));
00336   double const2 = sqrt(M_PI/fabs(gamma));
00337 
00338   Point2D<double> fresnel1 = get_fresnel_integral((k0+gamma*L)/const1);
00339   Point2D<double> fresnel2 = get_fresnel_integral(k0/const1);
00340 
00341   double C = (fresnel1.getX() - fresnel2.getX());
00342   if(gamma<0) {
00343       C *= -1.;
00344   }
00345   double S = fresnel1.getY() - fresnel2.getY();
00346 
00347   double cos_term = cos(theta-((k0*k0)/(2.0*gamma)));
00348   double sin_term = sin(theta-((k0*k0)/(2.0*gamma))); 
00349   
00350   end_pt.setX(start_pt.getX() + const2*(C*cos_term - S*sin_term));
00351   end_pt.setY(start_pt.getY() + const2*(C*sin_term + S*cos_term));
00352 
00353   return end_pt;
00354 }
00355 
00356 inline double   EulerSpiral::compute_error(double k0, double L)
00357 {
00358   //assumes normalized parameters
00359 
00360   //compute the endpoint of the Euler spiral with the given intrinsic parameters
00361   double gamma = 2*(params.turningAngle - k0*L)/(L*L);
00362   Point2D<double> cur_end_pt = compute_end_pt(k0, gamma, L, true);
00363 
00364   //the error is the distance between the current end point and the desired end point
00365   return euc_distance(Point2D<double>(1,0), cur_end_pt);
00366 }
00367 
00368 // Fresnel Integral code from Numerical recipes in C
00369 // EPS   is the relative error; 
00370 // MAXIT is the maximum number of iterations allowed; 
00371 // FPMIN is a number near the smallest representable floating-point number;
00372 // XMIN  is the dividing line between using the series and continued fraction.
00373 
00374 #define EPS    6.0e-8
00375 #define MAXIT  100
00376 #define FPMIN  1.0e-30
00377 #define XMIN   1.5
00378 
00379 //Computes the Fresnel integrals S(x) and C(x) for all real x
00380 Point2D<double> EulerSpiral::get_fresnel_integral(double x)
00381 {
00382   bool odd;
00383   int k,n;
00384   double a,ax,fact,pix2,sign,sum,sumc,sums,term,test;
00385 
00386   std::complex<double> b,cc,d,h,del,cs;
00387   Point2D<double> result;
00388 
00389   ax=fabs(x);
00390   if (ax < sqrt(FPMIN)) 
00391   { 
00392     //Special case: avoid failure of convergence test because of undeflow.
00393     result.setY(0.0);
00394     result.setX(ax);
00395   }
00396   else {
00397     if (ax <= XMIN)
00398     {
00399       // Evaluate both series simultaneously.
00400       sum=sums=0.0;
00401       sumc=ax;
00402       sign=1.0;
00403       fact=(M_PI/2.0)*ax*ax;
00404       odd=true;
00405       term=ax;
00406       n=3;
00407 
00408       for (k=1;k<=MAXIT;k++)
00409       { 
00410         term *= fact/k;
00411         sum  += sign*term/n;
00412         test=fabs(sum)*EPS;
00413         if (odd) 
00414         {
00415           sign = -sign;
00416           sums=sum;
00417           sum=sumc;
00418         }
00419         else {
00420           sumc=sum;
00421           sum=sums;
00422         }
00423 
00424         if (term < test) break;
00425         odd=!odd;
00426         n +=2;
00427       }
00428 
00429       if (k > MAXIT) 
00430         std::cout << "series failed in fresnel" << std::endl;
00431 
00432       result.setY(sums); 
00433       result.setX(sumc);
00434     }
00435     else {
00436       // Evaluate continued fraction by modified Lentz's method
00437       pix2=M_PI*ax*ax;
00438       b   = std::complex<double>(1.0,-pix2);
00439       cc  = std::complex<double>(1.0/FPMIN,0.0);
00440       d=h = std::complex<double>(1.0,0.0)/b;
00441       n = -1;
00442 
00443       for (k=2;k<=MAXIT;k++) 
00444       {
00445         n +=2;
00446         a = -n*(n+1);
00447         b= b+std::complex<double>(4.0,0.0);
00448         d=(std::complex<double>(1.0,0.0)/((a*d)+b));
00449 
00450         //Denominators cannot be zero
00451         cc=(b+(std::complex<double>(a,0.0)/cc));
00452 
00453         del=(cc*d);
00454         h=h*del;
00455         if ((fabs(del.real()-1.0)+fabs(del.imag())) < EPS)
00456           break;
00457       }
00458       if (k > MAXIT) 
00459         std::cout << "cf failed in frenel" << std::endl;
00460 
00461       h=std::complex<double>(ax,-ax)*h;
00462       cs=std::complex<double>(0.5,0.5)*(std::complex<double>(1.0,0.0) - std::complex<double>(cos(0.5*pix2),sin(0.5*pix2))*h );
00463 
00464       result.setX(cs.real());
00465       result.setY(cs.imag());
00466     }
00467   }
00468 
00469   if (x<0){ //use antisymmetry
00470     result = -1*result;  
00471   }
00472 
00473   return result;
00474 }
00475 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Defines