Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

Modules/BallLocator/GT2004ProcessModels/ConstantSpeedModel.cpp

Go to the documentation of this file.
00001 /**
00002 * @file ConstantSpeedModel.cpp
00003 * Contains a KalmanBallLocator process model for balls with constant speed
00004 *
00005 * @author <a href="mailto:stefanuhrig@gmx.net">Stefan Uhrig</a>
00006 */
00007 //------------------------------------------------------------------------------
00008 #include "Tools/Math/MVTools.h"
00009 #include "ConstantSpeedModel.h"
00010 #include "Tools/Debugging/Debugging.h"
00011 //------------------------------------------------------------------------------
00012 // Discrete process update matrix (dt must be set during update)
00013 double KalmanConstantSpeedModel::defaultA[16] =
00014 {
00015   1.0, 0.0, 0.0, 0.0,
00016   0.0, 1.0, 0.0, 0.0,
00017   0.0, 0.0, 1.0, 0.0,
00018   0.0, 0.0, 0.0, 1.0
00019 };
00020 // Initial internal covariance matrix of filter
00021 double KalmanConstantSpeedModel::defaultP[16] = 
00022 {
00023   1.0, 0.0, 0.0, 0.0,
00024   0.0, 1.0, 0.0, 0.0,
00025   0.0, 0.0, 1.0, 0.0,
00026   0.0, 0.0, 0.0, 1.0
00027 };
00028 // Process covariance matrix
00029 // Entries must be multiplied with powers of dt during update
00030 // Assume variances of 0.004 m^2 and 0.004 m^2 per s^2
00031 double KalmanConstantSpeedModel::defaultQ[16] =
00032 {
00033   0.004, 0.000, 0.004, 0.000,
00034   0.000, 0.004, 0.000, 0.004,
00035   0.004, 0.000, 0.004, 0.000,
00036   0.000, 0.004, 0.000, 0.004
00037 };
00038 // Measurement covariance matrix
00039 // Assume variances of 0.0001 m^2 and 0.0001 m^2 per s^2
00040 double KalmanConstantSpeedModel::defaultR[16] =
00041 {
00042   0.01*0.01, 0.00*0.00, 0.00*0.00, 0.00*0.00,
00043   0.00*0.00, 0.01*0.01, 0.00*0.00, 0.00*0.00,
00044   0.00*0.00, 0.00*0.00, 0.03*0.03, 0.00*0.00,
00045   0.00*0.00, 0.00*0.00, 0.00*0.00, 0.03*0.03
00046 };
00047 //------------------------------------------------------------------------------
00048 KalmanConstantSpeedModel::KalmanConstantSpeedModel() : KalmanProcessModelBase(), 
00049       panningVelocityThreshold(pi/3) //current threshold is 60°/s
00050 {
00051   A = defaultA;
00052   globQ = defaultQ;
00053   globR = defaultR;
00054 
00055   // Calculate variance adaption polynom
00056   double x[2] =
00057   {
00058     0.0, // 0m
00059     1.5 // 1.5m
00060   };
00061   double fx[2] =
00062   {
00063     0.01*0.01, // 1cm
00064     0.1*0.1   // 10cm
00065   };
00066   double Vx[2] =
00067   {
00068     0.0, // 0m
00069     2.5 // 2.5m
00070   };
00071   double Vfx[2] =
00072   {
00073     0.0001, 
00074     0.1   
00075   };
00076   calculateVarianceAdaptionPolynom(x, fx, varianceAdaptionPosition);
00077   calculateVarianceAdaptionPolynom(Vx, Vfx, varianceAdaptionSpeed);
00078  
00079   reset();
00080 }
00081 //------------------------------------------------------------------------------
00082 KalmanConstantSpeedModel::~KalmanConstantSpeedModel()
00083 {
00084 }
00085 //------------------------------------------------------------------------------
00086 void KalmanConstantSpeedModel::calculateVarianceAdaptionPolynom(
00087                                  double x[2], double fx[2], double result[2])
00088 {
00089   double dMatrixValues[4] =
00090   {
00091     x[0]*x[0],  1,
00092     x[1]*x[1],  1
00093   };
00094   Matrix_nxn<double, 2> A(dMatrixValues);
00095   Vector_n<double, 2> b(fx);
00096 
00097   Vector_n<double, 2> sol = A.solve(b);
00098   result[0] = sol[0];
00099   result[1] = sol[1];
00100 }
00101 //------------------------------------------------------------------------------
00102 void KalmanConstantSpeedModel::reset()
00103 {
00104   initState = NotInited;
00105   P = defaultP;
00106   lastLiklihood = 0.0;
00107   lastTime = 0.0;
00108   double zeros[4] = {0.0, 0.0, 0.0, 0.0};
00109   x_act = zeros;
00110 }
00111 //------------------------------------------------------------------------------
00112 KalmanUpdateResult
00113   KalmanConstantSpeedModel::update(double time, double x, double y, const RobotPose& pose, double panningVelocity)
00114 {
00115   KalmanUpdateResult res;
00116 
00117   // Initialise filter if necessary
00118   if (initState == NotInited)
00119   {
00120     // Initialise position
00121     x_act[0] = z[0] = x;
00122     x_act[1] = z[1] = y;
00123 
00124     initState = PositionInited;
00125     lastTime = time;
00126 
00127     res.liklihood = 0.0;
00128     return res;
00129   }
00130 
00131   // Determine and check time difference 
00132   double dt = time - lastTime;
00133   if (MVTools::isNearZero(dt))
00134   {
00135     // Send last state if time difference is near zero
00136     res.state.x = x_act[0];
00137     res.state.y = x_act[1];
00138     res.state.vx = x_act[2];
00139     res.state.vy = x_act[3];
00140     res.liklihood = lastLiklihood;
00141     return res;
00142   }
00143 
00144   if (initState == PositionInited)
00145   {
00146     // Initialise speed
00147     x_act[2] = z[2] = (x - x_act[0]) / dt;
00148     x_act[3] = z[3] = (y - x_act[1]) / dt;
00149     x_act[0] = z[0] = x;
00150     x_act[1] = z[1] = y;
00151 
00152     initState = Ready;
00153     lastTime = time;
00154 
00155     res.liklihood = 0.0;
00156     return res;
00157   }
00158 
00159   
00160   try
00161   {
00162     // Adapt process covariance matrix
00163     double dt2 = dt*dt;
00164     double dt3 = dt2*dt;
00165     double dt4 = dt2*dt2;
00166     Q = globQ;
00167     Q[0][0] *= dt4;
00168     Q[0][2] *= dt3;
00169     Q[1][1] *= dt4;
00170     Q[1][3] *= dt3;
00171     Q[2][0] *= dt3;
00172     Q[2][2] *= dt2;
00173     Q[3][1] *= dt3;
00174     Q[3][3] *= dt2;
00175   
00176     // Perform time update
00177     try
00178     {
00179       A[0][2] = dt;
00180       A[1][3] = dt;
00181       x_minus = A*x_act;                              // (2.1)
00182       P_minus = A*P*transpose(A) + Q;                 // (2.2)
00183   
00184       // Update measured state
00185       z[2] = (x - z[0])/dt;
00186       z[3] = (y - z[1])/dt;
00187       z[0] = x;
00188       z[1] = y;
00189     }
00190     catch (MVException m)
00191     {
00192       // We shouldn't end up here      
00193       OutputException("time-update", m);
00194       reset();
00195       res.liklihood = 0.0;
00196       return res;
00197     }
00198 
00199     // Adapt measurement covariance matrix
00200     double radiussquare = x*x + y*y;
00201     double angle = atan2(y,x);
00202     double ca = cos(angle);
00203     double sa = sin(angle);
00204 
00205     double posA, posB, spdA, spdB;
00206     
00207     double abAspectRatio = (panningVelocity < panningVelocityThreshold) ? 0.15 : 0.15*panningVelocity/(panningVelocityThreshold); 
00208     double bPanningFactor = (panningVelocity < panningVelocityThreshold) ? 1.0 : 1.0*panningVelocity/(panningVelocityThreshold);
00209     posB = varianceAdaptionPosition[0]*radiussquare +
00210         varianceAdaptionPosition[1];
00211     posB *= bPanningFactor;
00212     posA = posB*abAspectRatio;
00213 
00214     spdB = varianceAdaptionSpeed[0]*radiussquare +
00215         varianceAdaptionSpeed[1];
00216     spdB *= bPanningFactor;
00217     spdA = spdB*abAspectRatio;
00218   
00219     Matrix2x2<double> Lambda(Vector2<double>(posB, 0), Vector2<double>(0, posA));
00220     Matrix2x2<double> M(Vector2<double>(ca, sa), Vector2<double>(-sa, ca));
00221     Matrix2x2<double> Rxy = M*Lambda*M.transpose(); 
00222     Lambda[0][0] = spdB;
00223     Lambda[1][1] = spdA;
00224     Matrix2x2<double> RVxy = M*Lambda*M.transpose(); 
00225 
00226     Lambda[0][0] = sqrt(posB);
00227     Lambda[1][1] = sqrt(posA);
00228     Matrix2x2<double> draw = M*Lambda;
00229     
00230     Vector2<double> ellipseA(1000.0*draw[0][0], 1000.0*draw[0][1]);
00231     Vector2<double> ellipseB(1000.0*draw[1][0], 1000.0*draw[1][1]);
00232     Vector2<double> ballMeasuredPos(x*1000.0, y*1000.0);
00233     ellipseA += ballMeasuredPos;
00234     ellipseB += ballMeasuredPos;
00235     ellipseA = Geometry::relative2FieldCoord(pose, ellipseA.x,
00236                                   ellipseA.y);
00237     ellipseB = Geometry::relative2FieldCoord(pose, ellipseB.x,
00238                                   ellipseB.y);
00239     ballMeasuredPos = Geometry::relative2FieldCoord(pose, ballMeasuredPos.x,
00240                                   ballMeasuredPos.y);
00241     LINE(ballLocatorField, ballMeasuredPos.x, ballMeasuredPos.y, 
00242          ellipseA.x, ellipseA.y,
00243          3, 0, Drawings::pink);
00244     LINE(ballLocatorField, ballMeasuredPos.x, ballMeasuredPos.y, 
00245          ellipseB.x, ellipseB.y,
00246          3, 0, Drawings::pink);
00247          
00248     R = globR;
00249     R[0][0] = Rxy[0][0];//NOTE: this is *not* some kind of transposition, it's because
00250     R[0][1] = Rxy[1][0];// the 2 matrixes use different storing conventions
00251     R[1][0] = Rxy[0][1];// this will have to be corrected in a new Matrix class
00252     R[1][1] = Rxy[1][1];// release
00253 
00254     R[2][2] = RVxy[0][0];
00255     R[2][3] = RVxy[1][0];
00256     R[3][2] = RVxy[0][1];
00257     R[3][3] = RVxy[1][1];
00258 
00259     // Calculate weight (see equation 2.6)
00260     C = P_minus + R;
00261     double determinant;
00262     try
00263     {
00264       C_inv = invert(C);
00265       determinant = det(C);
00266       if (determinant < 0.0)
00267         throw MVException(MVException::DetNegative);
00268       if (MVTools::isNearZero(determinant))
00269         throw MVException(MVException::DetCloseToZero);
00270     }
00271     catch (MVException m)
00272     {
00273       // We shouldn't end up here      
00274       OutputException("invert_c", m);
00275       reset();
00276       res.liklihood = 0.0;
00277       return res;
00278     }
00279     
00280     double exponent = -0.5*(z - x_minus)*(C_inv*(z - x_minus));
00281     MVException m;
00282     if (exponent > MVTools::getMaxExpDouble())
00283     {
00284       // Sometimes exponent ist too high to represent the result with
00285       // a double. Set it to the maximum value
00286       exponent = MVTools::getMaxExpDouble();
00287     }
00288     double denominator = 4*pi*pi*sqrt(determinant);
00289     res.liklihood = exp(exponent)/(denominator);
00290 
00291     // Measurement update
00292     try
00293     {
00294       K = P_minus * C_inv;                            // (2.3)
00295       x_act = x_minus + K*(z - x_minus);              // (2.4)
00296       P = (P.eye() - K)*P_minus;                      // (2.5)
00297     }
00298     catch (MVException m)
00299     {
00300       // We should not end up here
00301       OutputException("measurement-update", m);
00302       reset();
00303       res.liklihood = 0.0;
00304       return res;
00305     }
00306 
00307     // Set state und save information of this update step
00308     lastLiklihood = res.liklihood;
00309     res.state.x = x_act[0];
00310     res.state.y = x_act[1];
00311     res.state.vx = x_act[2];
00312     res.state.vy = x_act[3];
00313     lastTime = time;
00314   }
00315   catch (...)
00316   {
00317     // We should not end up here
00318     OUTPUT(idText, text,
00319            "UnknownException in KalmanConstantSpeedModel::update()");
00320     reset();
00321     res.liklihood = 0.0;
00322     return res;
00323   }
00324 
00325   return res;
00326 }
00327 //------------------------------------------------------------------------------
00328 KalmanPredictResult KalmanConstantSpeedModel::predict(double time)
00329 {
00330   KalmanPredictResult res;
00331 
00332   // If model was not initialised yet we can't predict a state
00333   if (initState != Ready)
00334     return res;
00335 
00336   // Calculate difference between actual time and the time the ball was
00337   // last seen.
00338   double dt = time - lastTime;
00339 
00340   // Adapt process matrix A to time difference
00341   A[0][2] = dt;
00342   A[1][3] = dt;
00343     
00344   // Predict a state using the process model
00345   try
00346   {
00347     x_predict = A*x_act;
00348   }
00349   catch (MVException m)
00350   {
00351     // We should not end up here
00352     OutputException("prediction", m);
00353     reset();
00354     res.liklihood = 0.0;
00355     return res;
00356   }
00357   res.state.x = x_predict[0];
00358   res.state.y = x_predict[1];
00359   res.state.vx = x_predict[2];
00360   res.state.vy = x_predict[3];
00361   res.liklihood = lastLiklihood;
00362 
00363   return res;
00364 }
00365 //------------------------------------------------------------------------------
00366 void KalmanConstantSpeedModel::adapt(const OdometryData& lastOdometry,
00367                                      const OdometryData& actualOdometry)
00368 {
00369   // Adapt state and measured state to the new position of the robot
00370   Vector2<double> tempBallPosition = 
00371     (lastOdometry + Pose2D(x_act[0]*1000.0, x_act[1]*1000.0) -
00372      actualOdometry).translation;
00373   x_act[0] = tempBallPosition.x / 1000.0;
00374   x_act[1] = tempBallPosition.y / 1000.0;
00375     
00376   tempBallPosition = 
00377     (lastOdometry + Pose2D(z[0]*1000.0, z[1]*1000.0) -
00378      actualOdometry).translation;
00379   z[0] = tempBallPosition.x / 1000.0;
00380   z[1] = tempBallPosition.y / 1000.0;
00381 
00382   double vx, vy;
00383   double speed_rot = lastOdometry.getAngle() - actualOdometry.getAngle();
00384   vx = x_act[2];
00385   vy = x_act[3];
00386   x_act[2] = vx*cos(speed_rot) - vy*sin(speed_rot);
00387   x_act[3] = vx*sin(speed_rot) + vy*cos(speed_rot);
00388   
00389   vx = z[2];
00390   vy = z[3];
00391   z[2] = vx*cos(speed_rot) - vy*sin(speed_rot);
00392   z[3] = vx*sin(speed_rot) + vy*cos(speed_rot);
00393 }
00394 //------------------------------------------------------------------------------
00395 
00396 /*
00397  * Change log :
00398  * $Log: ConstantSpeedModel.cpp,v $
00399  * Revision 1.1  2004/07/14 21:40:15  spranger
00400  * moved kalmanprocessmodel to gt2004processmodel
00401  *
00402  * Revision 1.12  2004/07/07 10:15:39  uhrig
00403  * Fixed doucmentation (for doxygen)
00404  *
00405  * Revision 1.11  2004/06/30 18:05:18  nistico
00406  * Kalman "critical" panning speed threshold raised to 60°/s
00407  *
00408  * Revision 1.10  2004/06/22 09:43:17  nistico
00409  * The radial component of the measurement variance is
00410  * also affected by panning velocity now
00411  *
00412  * Revision 1.9  2004/06/22 09:10:48  uhrig
00413  * Bugfix in predict function (process matrix A was not adapted to time difference)
00414  *
00415  * Revision 1.8  2004/06/21 11:55:54  nistico
00416  * Comments / cleaning
00417  *
00418  * Revision 1.7  2004/06/20 20:04:38  kindler
00419  * - adapted to new Matrix2x2 operator[] access method.
00420  *
00421  * Revision 1.6  2004/06/18 10:24:51  nistico
00422  * Kalman measurement covariance matrix now adjusted also based on
00423  * head panning angular velocity
00424  *
00425  * Revision 1.5  2004/06/16 17:57:00  nistico
00426  * Speed covariance submatrix dynamically calculated
00427  * based on distance from ball
00428  * More detailed identification of exception
00429  * Bug fixes
00430  *
00431  * Revision 1.3  2004/06/15 22:14:10  uhrig
00432  * Tidied up a little bit
00433  *
00434  * Revision 1.2  2004/06/15 17:33:00  nistico
00435  * Measurement model for position now depends on distance, retuned speed
00436  * component variance.
00437  * The code needs to be cleaned up and beautified.
00438  *
00439  * Revision 1.1.1.1  2004/05/22 17:15:25  cvsadm
00440  * created new repository GT2004_WM
00441  *
00442  * Revision 1.4  2004/04/20 14:12:16  uhrig
00443  *  - odometry processing corrected
00444  *  - fixed position model disabled
00445  *  - covariance matrices tuned
00446  *
00447  * Revision 1.3  2004/04/07 12:28:56  risler
00448  * ddd checkin after go04 - first part
00449  *
00450  * Revision 1.2  2004/03/30 15:47:30  risler
00451  * covariance matrix fixed should be symmetric
00452  *
00453  * Revision 1.1.1.1  2004/03/29 08:28:51  Administrator
00454  * initial transfer from tamara
00455  *
00456  * Revision 1.2  2004/03/15 12:28:52  risler
00457  * change log added
00458  *
00459  *
00460  */

Generated on Thu Sep 23 19:57:25 2004 for GT2004 by doxygen 1.3.6