LoScanMatch.C

00001 /**
00002    \file  Robots/LoBot/misc/LoScanMatch.C
00003    \brief Implementation of Lu and Milios's Iterative Dual Correspondence
00004    laser range finder scan matching.
00005 */
00006 
00007 // //////////////////////////////////////////////////////////////////// //
00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00009 // by the University of Southern California (USC) and the iLab at USC.  //
00010 // See http://iLab.usc.edu for information about this project.          //
00011 // //////////////////////////////////////////////////////////////////// //
00012 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00013 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00014 // in Visual Environments, and Applications'' by Christof Koch and      //
00015 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00016 // pending; application number 09/912,225 filed July 23, 2001; see      //
00017 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00018 // //////////////////////////////////////////////////////////////////// //
00019 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00022 // redistribute it and/or modify it under the terms of the GNU General  //
00023 // Public License as published by the Free Software Foundation; either  //
00024 // version 2 of the License, or (at your option) any later version.     //
00025 //                                                                      //
00026 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00027 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00028 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00029 // PURPOSE.  See the GNU General Public License for more details.       //
00030 //                                                                      //
00031 // You should have received a copy of the GNU General Public License    //
00032 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00033 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00034 // Boston, MA 02111-1307 USA.                                           //
00035 // //////////////////////////////////////////////////////////////////// //
00036 //
00037 // Primary maintainer for this file: mviswana usc edu
00038 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/slam/LoScanMatch.C $
00039 // $Id: LoScanMatch.C 13572 2010-06-16 18:42:45Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/slam/LoScanMatch.H"
00046 #include "Robots/LoBot/config/LoConfigHelpers.H"
00047 #include "Robots/LoBot/misc/LoExcept.H"
00048 #include "Robots/LoBot/util/LoMath.H"
00049 #include "Robots/LoBot/util/triple.hh"
00050 
00051 // INVT utilities
00052 #include "Util/log.H"
00053 
00054 // GSL headers
00055 #ifdef INVT_HAVE_LIBGSL
00056 #include <gsl/gsl_eigen.h>
00057 #include <gsl/gsl_matrix.h>
00058 #endif
00059 
00060 // Standard C++ headers
00061 #include <vector>
00062 #include <algorithm>
00063 #include <iterator>
00064 #include <functional>
00065 #include <limits>
00066 
00067 //----------------------------- NAMESPACE -------------------------------
00068 
00069 namespace lobot {
00070 
00071 //-------------------------- KNOB TWIDDLING -----------------------------
00072 
00073 // DEVNOTE: For some reason, this anonymous namespace does not seem to
00074 // work properly. The parameters all seem to be zero. Not sure why.
00075 // Anyhoo, removing the anonymous namespace to get this to work...
00076 //namespace {
00077 
00078 // Quick helper to return settings from scan_match section of config file
00079 template<typename T>
00080 inline T conf(const std::string& key, T default_value)
00081 {
00082    return get_conf<T>("scan_match", key, default_value) ;
00083 }
00084 
00085 // Override for retrieving triples
00086 template<typename T>
00087 inline triple<T, T, T>
00088 conf(const std::string& key, const triple<T, T, T>& default_value)
00089 {
00090    return get_conf<T>("scan_match", key, default_value) ;
00091 }
00092 
00093 /// This local class encapsulates various parameters that can be used to
00094 /// tweak different aspects of scan matching.
00095 class Params : public singleton<Params> {
00096    /// Since different laser range finders have varying capabilities and
00097    /// because it would be nice to be able to use this implementation of
00098    /// the ICP algorithm with devices other than just the Hokuyo attached
00099    /// to lobot, we require users to specify the relevant LRF
00100    /// characteristics with three numbers.
00101    ///
00102    /// The first of these numbers specifies the start angle for the range
00103    /// readings. Usually, this would be a negative number indicating that
00104    /// the laser range finder sweeps from the right to the left.
00105    ///
00106    /// The second number specifies the angular step between consecutive
00107    /// range readings. For lobot's Hokuyo, this is one degree; but for
00108    /// other LRF's, it could be 0.5 degrees.
00109    ///
00110    /// Finally, the third number in the above triumvirate specifies the
00111    /// total number of range readings.
00112    ///
00113    /// DEVNOTE: The third number should actually be an integer. But we
00114    /// declare it float for the sake of convenience (so as to avoid
00115    /// writing a special API for retrieving triple<float, float, int>
00116    /// from the config file).
00117    triple<float, float, float> m_lrf_specs ;
00118 
00119    /// In each iteration of ICP, the algorithm searches a small angular
00120    /// range around each LRF reading to find the best correspondence
00121    /// between the current and previous LRF scans. This setting specifies
00122    /// the above-mentioned angular search range.
00123    int m_lrf_search_range ;
00124 
00125    /// When finding correspondences between the current and previous
00126    /// scans, we reject any wherein the LRF range measurements exceed
00127    /// this threshold.
00128    float m_outlier_threshold ;
00129 
00130    /// The ICP algorithm works by iteratively solving for the rotation
00131    /// and translation required to register a data shape with a model
00132    /// shape. Initially, the transformation is assumed to be zero.
00133    /// However, we can seed the initial transformation using the robot's
00134    /// odometry so that the algorithm has a better estimate to start off
00135    /// with. This can reduce the amount of work the algorithm has to do
00136    /// to register the previous LRF scan with the current one.
00137    ///
00138    /// By default, this flag is off so that the implementation follows
00139    /// the classic ICP description by Besl and McKay, viz., starting off
00140    /// with zero registration.
00141    bool m_seed_using_odometry ;
00142 
00143    /// Since the ICP algorithm uses multiple iterations to converge to a
00144    /// solution, it could (potentially) go on forever. To prevent an
00145    /// infinite loop, we use this setting to specify the maximum number
00146    /// of iterations to use.
00147    int m_max_iterations ;
00148 
00149    /// In general, the ICP algorithm should converge within a few
00150    /// iterations rather than going on for the maximum set above. In each
00151    /// iteration, the algorithm will compute a transformation and a total
00152    /// error between the two scans when that transformation is applied.
00153    /// The algorithm will terminate when the difference in this error for
00154    /// two consecutive iterations falls below the threshold specified by
00155    /// the following setting.
00156    float m_tau ;
00157 
00158    /// Private constructor because this is a singleton.
00159    Params() ;
00160    friend class singleton<Params> ;
00161 
00162 public:
00163    /// Accessing the various parameters
00164    //@{
00165    static float lrf_start() {return instance().m_lrf_specs.first      ;}
00166    static float lrf_res()   {return instance().m_lrf_specs.second     ;}
00167    static int   lrf_num()   {return int(instance().m_lrf_specs.third) ;}
00168    static int   lrf_search_range()   {return instance().m_lrf_search_range   ;}
00169    static float outlier_threshold()  {return instance().m_outlier_threshold  ;}
00170    static bool  seed_using_odometry(){return instance().m_seed_using_odometry;}
00171    static int   max_iterations()     {return instance().m_max_iterations     ;}
00172    static float tau()                {return instance().m_tau                ;}
00173    //@}
00174 } ;
00175 
00176 // Parameter initialization
00177 Params::Params()
00178    : m_lrf_specs(conf("lrf_specs", make_triple(-119.0f, 1.0f, 255.0f))),
00179      m_lrf_search_range(clamp(conf("lrf_search_range", 15), 0, 360)),
00180      m_outlier_threshold(clamp(conf("outlier_threshold", 100.0f),
00181                                0.001f, 1000.0f)),
00182      m_seed_using_odometry(conf("seed_using_odometry", false)),
00183      m_max_iterations(clamp(conf("max_iterations", 30), 5, 100)),
00184      m_tau(clamp(conf("tau", 0.1f), 1e-6f, 1e+6f))
00185 {}
00186 
00187 //} // end of local anonymous namespace encapsulating above helpers
00188 
00189 //--------------------- SCANS AND TRANSFORMATIONS -----------------------
00190 
00191 // Initializing a scan's range reading given its polar and Cartesian
00192 // coordinates.
00193 Scan::RangeReading::RangeReading(float r, float x, float y)
00194    : m_r(r), m_x(x), m_y(y)
00195 {}
00196 
00197 // Quick function object to convert a polar range reading into
00198 // corresponding Cartesian coordinates.
00199 namespace {
00200 
00201 class polar_to_cartesian :
00202          public std::binary_function<float, Pose, Scan::RangeReading> {
00203    typedef result_type R ;
00204    typedef first_argument_type  A1 ;
00205    typedef second_argument_type A2 ;
00206    mutable float t ;
00207 public:
00208    polar_to_cartesian() ;
00209    R operator()(A1 r, const A2& pose) const ;
00210 } ;
00211 
00212 polar_to_cartesian::polar_to_cartesian()
00213    : t(Params::lrf_start())
00214 {}
00215 
00216 polar_to_cartesian::R
00217 polar_to_cartesian::
00218 operator()(polar_to_cartesian::A1 r, const polar_to_cartesian::A2& P) const
00219 {
00220    R range_reading(r,                                // polar
00221                    P.x() + r * cos(P.theta() + t),   // Cartesian x
00222                    P.y() + r * sin(P.theta() + t)) ; // Cartesian y
00223    t += Params::lrf_res() ;
00224    return range_reading ;
00225 }
00226 
00227 } // end of local anonymous namespace encapsulating above helper
00228 
00229 // Initialize a scan given its pose and the range readings
00230 Scan::Scan(const Pose& P, const std::vector<int>& R)
00231    : m_pose(P)
00232 {
00233    m_range_readings.reserve(R.size()) ;
00234    std::transform(R.begin(), R.end(), std::back_inserter(m_range_readings),
00235                   std::bind2nd(polar_to_cartesian(), P)) ;
00236 }
00237 
00238 Scan::Scan(const Pose& P, const std::vector<float>& R)
00239    : m_pose(P)
00240 {
00241    m_range_readings.reserve(R.size()) ;
00242    std::transform(R.begin(), R.end(), std::back_inserter(m_range_readings),
00243                   std::bind2nd(polar_to_cartesian(), P)) ;
00244 }
00245 
00246 // Scan clean-up
00247 Scan::~Scan(){}
00248 
00249 // A transformation encapsulates the rotation and translation required to
00250 // register one scan with another.
00251 Transformation::Transformation(float w, float Tx, float Ty)
00252    : m_w(w), m_Tx(Tx), m_Ty(Ty)
00253 {}
00254 
00255 // Convenience constructor for initializing a transformation using a
00256 // triple.
00257 Transformation::Transformation(const triple<float, float, float>& T)
00258    : m_w(T.first), m_Tx(T.second), m_Ty(T.third)
00259 {}
00260 
00261 // Quick helper to compute the pose difference between two Scans and
00262 // return the result via a Transformation.
00263 static Transformation operator-(const Scan& a, const Scan& b)
00264 {
00265    return Transformation(a.theta() - b.theta(), a.x() - b.x(), a.y() - b.y()) ;
00266 }
00267 
00268 //----------------------- POINT CORRESPONDENCES -------------------------
00269 
00270 namespace {
00271 
00272 // The Iterative Closest Point algorithm works by finding corresponding
00273 // points between two scans without knowing the transformation required
00274 // to convert one scan to the other and then using these correspondence
00275 // pairs to iteratively solve for the unknown transformation.
00276 //
00277 // This local class encapsulates the correspondence. It is simply a Nx1
00278 // matrix wherein C[i] = j implies that the i-th point in one scan
00279 // corresponds to the j-th point in the other. A value of -1 for C[i]
00280 // indicates that there is no correspondence for the i-th point.
00281 class Correspondence {
00282    // The Nx1 correspondence matrix is simply a one-dimensional array.
00283    std::vector<int> C ;
00284 
00285 public:
00286    // On initialization, all correspondence pairs are set to -1.
00287    Correspondence(int N) ;
00288 
00289    // Retrieving the i-th correspondence pair.
00290    int operator[](int i) const {return C[i] ;}
00291 
00292    // Setting the i-th correspondence pair.
00293    int& operator[](int i) {return C[i] ;}
00294 } ;
00295 
00296 Correspondence::Correspondence(int N)
00297 {
00298    C.reserve(N) ;
00299    std::fill_n(std::back_inserter(C), N, -1) ;
00300 }
00301 
00302 // This function finds the model point closest to the i-th data point.
00303 // Only the LRF angular range specified by [min, max] is searched.
00304 int closest_point(const Scan& data, const Scan& model, int i, int min, int max)
00305 {
00306    int cp = -1 ;
00307    float min_d = std::numeric_limits<float>::max() ;
00308 
00309    const Scan::RangeReading& D = data[i] ;
00310    for (int j = min; j <= max; ++j)
00311    {
00312       const Scan::RangeReading& M = model[j] ;
00313       if (M.r() < 0) // bad LRF reading
00314          continue ;
00315 
00316       float d = sqrtf(sqr(D.x() - M.x()) + sqr(D.y() - M.y())) ;
00317       if (d < min_d) {
00318          cp = j ;
00319          min_d = d ;
00320       }
00321    }
00322    return cp ;
00323 }
00324 
00325 // This function finds the correspondence pairs between the data and
00326 // model shapes.
00327 Correspondence find_correspondence(const Scan& data, const Scan& model)
00328 {
00329    const int N = Params::lrf_num() ;
00330    const int n = round(Params::lrf_search_range()/Params::lrf_res()) ;
00331 
00332    Correspondence C(N) ;
00333    for (int i = 0; i < N; ++i)
00334    {
00335       const float r = data[i].r() ;
00336       if (r < 0) // bad LRF reading
00337          continue ;
00338 
00339       int j = closest_point(data, model,
00340                             i, std::max(i - n, 0), std::min(i + n, N - 1)) ;
00341       if (abs(r - model[j].r()) < Params::outlier_threshold())
00342          C[i] = j ;
00343       //LERROR("C[%3d] = %3d", i, C[i]) ;
00344    }
00345    return C ;
00346 }
00347 
00348 } // end of local anononymous namespace encapsulating above helpers
00349 
00350 //----------------- QUATERNION BASED TRANSFORMATIONS --------------------
00351 
00352 namespace {
00353 
00354 // Besl and McKay represent the rotation for registering a data shape
00355 // with a model shape using a quaternion. The translation vector is then
00356 // tacked on to the quaternion to produce a so-called registration
00357 // vector. The first four components of this vector are the
00358 // quaternion-based rotation and the last three components specify the
00359 // translation component of the transformation.
00360 //
00361 // We use this helper class to encapsulate the registration vector
00362 // described above. This class also holds the mean-square error
00363 // associated with the transformation between the data and model shapes.
00364 class ICPTransformation {
00365    // The 7-component registration vector: first four components are the
00366    // quaternion used to represent the rotation between data and model
00367    // shapes; the next three components are the translation vector
00368    // between the two shapes.
00369    float q[7] ;
00370 
00371    // The mean-square error in the distances between the corresponding
00372    // points of the two shapes when the data shape is registered with the
00373    // model shape using the transformation specified by the registration
00374    // vector.
00375    float mserr ;
00376 
00377 public:
00378    // This constructor initializes the registration vector so that the
00379    // rotation and translation are zero.
00380    ICPTransformation() ;
00381 
00382    // This constructor initializes the registration vector and
00383    // mean-square error using the supplied parameters.
00384    ICPTransformation(float q[7], float mserr) ;
00385 
00386    // This constructor initializes the registration vector using the
00387    // supplied z-axis rotation and 2D translation. Usually, these values
00388    // would be obtained from odometry. Thus, this constructor "seeds" the
00389    // ICP algorithm using odometry rather than zero.
00390    ICPTransformation(const Transformation&) ;
00391 
00392    // Copy and assignment.
00393    ICPTransformation(const ICPTransformation&) ;
00394    ICPTransformation& operator=(const ICPTransformation&) ;
00395 
00396    // Return the mean-square error associated with this registration.
00397    float error() const {return mserr ;}
00398 
00399    // A helper function to transform an LRF scan by applying the
00400    // registration vector held by this object. The transformed scan is
00401    // returned via a new Scan object.
00402    Scan apply(const Scan&) const ;
00403 
00404    // This function converts the quaternion-based rotation into the
00405    // corresponding Euler angle. It also extracts the translation
00406    // component of the transformation from the registration vector.
00407    //
00408    // NOTE: Since we are only interested in 2D for the LRF scan matching
00409    // procedure, the resulting transformation only needs one Euler angle
00410    // (corresponding to a rotation about the z-axis) and two translation
00411    // components (z-component is zero).
00412    Transformation convert() const ;
00413 } ;
00414 
00415 // Initialize registration vector for zero rotation and zero translation.
00416 // This constructor is used at the start of the ICP algorithm.
00417 ICPTransformation::ICPTransformation()
00418    : mserr(-1)
00419 {
00420    q[0] = 1 ;
00421    q[1] = q[2] = q[3] = q[4] = q[5] = q[6] = 0 ;
00422 }
00423 
00424 // Initialize registration vector using supplied parameters. This
00425 // constructor is used when we solve for the rotation and translation
00426 // between data and model shapes in each iteration of the ICP algorithm.
00427 ICPTransformation::ICPTransformation(float quat[7], float err)
00428    : mserr(err)
00429 {
00430    q[0] = quat[0] ;
00431    q[1] = quat[1] ;
00432    q[2] = quat[2] ;
00433    q[3] = quat[3] ;
00434    q[4] = quat[4] ;
00435    q[5] = quat[5] ;
00436    q[6] = quat[6] ;
00437 }
00438 
00439 // Initialize registration vector using supplied rotation about z-axis
00440 // and 2D translation vector.
00441 ICPTransformation::ICPTransformation(const Transformation& t)
00442    : mserr(-1)
00443 {
00444    q[0] = cos(t.w()/2) ;
00445    q[1] = 0 ;
00446    q[2] = 0 ;
00447    q[3] = sin(t.w()/2) ;
00448    q[4] = t.Tx() ;
00449    q[5] = t.Ty() ;
00450    q[6] = 0 ;
00451 }
00452 
00453 // Copy constructor
00454 ICPTransformation::ICPTransformation(const ICPTransformation& that)
00455    : mserr(that.mserr)
00456 {
00457    q[0] = that.q[0] ;
00458    q[1] = that.q[1] ;
00459    q[2] = that.q[2] ;
00460    q[3] = that.q[3] ;
00461    q[4] = that.q[4] ;
00462    q[5] = that.q[5] ;
00463    q[6] = that.q[6] ;
00464 }
00465 
00466 // Assignment operator
00467 ICPTransformation& ICPTransformation::operator=(const ICPTransformation& that)
00468 {
00469    if (this != &that) {
00470       q[0]  = that.q[0] ;
00471       q[1]  = that.q[1] ;
00472       q[2]  = that.q[2] ;
00473       q[3]  = that.q[3] ;
00474       q[4]  = that.q[4] ;
00475       q[5]  = that.q[5] ;
00476       q[6]  = that.q[6] ;
00477       mserr = that.mserr ;
00478    }
00479    return *this ;
00480 }
00481 
00482 // Apply rotation and translation to each point in the given LRF scan and
00483 // return a new Scan as the result.
00484 Scan ICPTransformation::apply(const Scan& s) const
00485 {
00486    float R11 = sqr(q[0]) + sqr(q[1]) - sqr(q[2]) - sqr(q[3]) ;
00487    float R12 = 2 * (q[1]*q[2] - q[0]*q[3]) ;
00488    float R21 = 2 * (q[1]*q[2] + q[0]*q[3]) ;
00489    float R22 = sqr(q[0]) + sqr(q[2]) - sqr(q[1]) - sqr(q[3]) ;
00490 
00491    const int N = Params::lrf_num() ;
00492 
00493    Scan t(s) ;
00494    for (int i = 0; i < N; ++i)
00495    {
00496       Scan::RangeReading& r = t[i] ;
00497       r = Scan::RangeReading(r.r(),
00498                              R11 * r.x() + R12 * r.y() + q[4],
00499                              R21 * r.x() + R22 * r.y() + q[5]) ;
00500    }
00501    return t ;
00502 }
00503 
00504 // Convert the quaternion to an Euler angle specifying rotation about
00505 // z-axis and extract the 2D component of the translation from the
00506 // registration vector.
00507 //
00508 // NOTE: See
00509 // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
00510 // for explanation of these formulae.
00511 Transformation ICPTransformation::convert() const
00512 {
00513    /*
00514    float x = atan(2*(q[0]*q[1] + q[2]*q[3]), 1 - 2*(sqr(q[1]) + sqr(q[2]))) ;
00515    float y = asin(2*(q[0]*q[2] - q[3]*q[1])) ;
00516    // */
00517    float z = atan(2*(q[0]*q[3] + q[1]*q[2]), 1 - 2*(sqr(q[2]) + sqr(q[3]))) ;
00518    //LERROR("rotation = [%g %g %g]", x, y, z) ;
00519 
00520    return Transformation(z, q[4], q[5]) ;
00521 }
00522 
00523 } // end of local anonymous namespace encapsulating above helper class
00524 
00525 //---------------------- ITERATIVE CLOSEST POINT ------------------------
00526 
00527 // This function solves for the rotation and translation between two
00528 // scans using the supplied point correspondences. Section III (pages 241
00529 // through 243) of the Besl-McKay paper describes the math implemented
00530 // here.
00531 //
00532 // NOTE: The paper works for 3D shapes. In our case, we only need 2D.
00533 // Therefore, the formulae used in this function are slightly different
00534 // from the math in the paper. Basically, the stuff required for 3D is
00535 // taken out and only the necessary 2D stuff is computed.
00536 #ifdef INVT_HAVE_LIBGSL
00537 
00538 static ICPTransformation
00539 solve(const Scan& data, const Scan& model, const Correspondence& C)
00540 {
00541    const int N = Params::lrf_num() ;
00542 
00543    // First, we compute the center of mass of the data and model shapes.
00544    // The CoM of the data shape is denoted mu_p and that of the model is
00545    // denoted mu_x. See equation (23) on page 243.
00546    //
00547    // In this loop, we can also compute the cross-covariance matrix,
00548    // which is referred to as sigma_px in the paper and denoted as E_px
00549    // here. This is equation (24) on page 243.
00550    //
00551    // NOTE: Since we only need 2D, we ignore the z-components of the CoM
00552    // points mu_p and mu_x. Furthermore, we ignore the third row and
00553    // third column of the cross-covariance matrix (they are all zeros).
00554    int n = 0 ; // total number of correspondences
00555    float mu_p_x  = 0, mu_p_y  = 0, mu_x_x  = 0, mu_x_y  = 0 ;
00556    float E_px_11 = 0, E_px_12 = 0, E_px_21 = 0, E_px_22 = 0 ;
00557    for (int i = 0; i < N; ++i)
00558       if (C[i] >= 0) // i-th data point corresponds to something on model shape
00559       {
00560          ++n ;
00561 
00562          const Scan::RangeReading& D = data[i] ;
00563          const Scan::RangeReading& M = model[C[i]] ;
00564 
00565          // Equation (23), page 243
00566          mu_p_x += D.x() ;
00567          mu_p_y += D.y() ;
00568          mu_x_x += M.x() ;
00569          mu_x_y += M.y() ;
00570 
00571          // Equation (24), page 243
00572          E_px_11 += D.x() * M.x() ;
00573          E_px_12 += D.x() * M.y() ;
00574          E_px_21 += D.y() * M.x() ;
00575          E_px_22 += D.y() * M.y() ;
00576       }
00577 
00578    // Now we have the sums. We need the averages for the two CoM's. This
00579    // will complete equation (23).
00580    mu_p_x  /= n ;
00581    mu_p_y  /= n ;
00582    mu_x_x  /= n ;
00583    mu_x_y  /= n ;
00584 
00585    // For the cross-covariance matrix, we have the sum in the first term
00586    // of equation (24). Dividing by the total number of correspondences
00587    // will give us the first term.
00588    E_px_11 /= n ;
00589    E_px_12 /= n ;
00590    E_px_21 /= n ;
00591    E_px_22 /= n ;
00592 
00593    // Finally, we subtract the CoM's to complete equation (24)...
00594    E_px_11 -= mu_p_x * mu_x_x ;
00595    E_px_12 -= mu_p_x * mu_x_y ;
00596    E_px_21 -= mu_p_y * mu_x_x ;
00597    E_px_22 -= mu_p_y * mu_x_y ;
00598 
00599    // Now, we formulate the symmetric 4x4 matrix Q(E_px) as shown in
00600    // equation (25), page 243.
00601    //
00602    // NOTE: Since we're working in 2D, the four corners and the four
00603    // central elements of Q are the only relevant ones. The remaining
00604    // eight elements of the matrix are zero.
00605    //
00606    // NOTE 2: We have skipped a few things getting to this formulation.
00607    // First, we have to compute E_px - transpose(E_px). Then, we have to
00608    // use elements (2,3), (3,1) and (1,2) of the resulting matrix to
00609    // create a column vector denoted as Delta. Finally, trace(E_px),
00610    // Delta, transpose(Delta) and E_px + transpose(E_px) - trace(E_px) * I_3
00611    // are used to formulate Q.
00612    //
00613    // Taking advantage of the fact that we're dealing with 2D rather than
00614    // 3D, it turns out that only the third element of the column vector
00615    // Delta is significant (the other two are zero). Furthermore, as the
00616    // third row and third column of the cross-covariance matrix E_px
00617    // contain all zeros, the math for formulating Q can be done by hand.
00618    //
00619    // Putting it all together, gives us the following value for the
00620    // matrix Q.
00621    gsl_matrix* Q = gsl_matrix_alloc(4, 4) ;
00622    gsl_matrix_set_zero(Q) ;
00623    gsl_matrix_set(Q, 0, 0,  E_px_11 + E_px_22) ;
00624    gsl_matrix_set(Q, 0, 3,  E_px_12 - E_px_21) ;
00625    gsl_matrix_set(Q, 1, 1,  E_px_11 - E_px_22) ;
00626    gsl_matrix_set(Q, 1, 2,  E_px_12 + E_px_21) ;
00627    gsl_matrix_set(Q, 2, 1,  E_px_21 + E_px_12) ;
00628    gsl_matrix_set(Q, 2, 2,  E_px_22 - E_px_11) ;
00629    gsl_matrix_set(Q, 3, 0,  E_px_12 - E_px_21) ;
00630    gsl_matrix_set(Q, 3, 3, -E_px_11 - E_px_22) ;
00631 
00632    // Once we have Q(E_px), we can solve for the rotation as the
00633    // eigenvector corresponding to the maximum eigenvalue of the matrix
00634    // Q. See the paragraph between equations (25) and (26) on page 243 of
00635    // the Besl-McKay paper.
00636    gsl_eigen_symmv_workspace* w = gsl_eigen_symmv_alloc(4) ;
00637    gsl_vector* eigen_values  = gsl_vector_alloc(4) ;
00638    gsl_matrix* eigen_vectors = gsl_matrix_alloc(4, 4) ;
00639 
00640    gsl_eigen_symmv(Q, eigen_values, eigen_vectors, w) ;
00641    gsl_eigen_symmv_sort(eigen_values, eigen_vectors, GSL_EIGEN_SORT_VAL_DESC) ;
00642 
00643    float q[7] ;
00644    q[0] = static_cast<float>(gsl_matrix_get(eigen_vectors, 0, 0)) ;
00645    q[1] = static_cast<float>(gsl_matrix_get(eigen_vectors, 1, 0)) ;
00646    q[2] = static_cast<float>(gsl_matrix_get(eigen_vectors, 2, 0)) ;
00647    q[3] = static_cast<float>(gsl_matrix_get(eigen_vectors, 3, 0)) ;
00648 
00649    gsl_matrix_free(eigen_vectors) ;
00650    gsl_vector_free(eigen_values) ;
00651    gsl_eigen_symmv_free(w) ;
00652    gsl_matrix_free(Q) ;
00653 
00654    // This is the rotation matrix associated with the above quaternion.
00655    // See equation (21) on page 243.
00656    //
00657    // NOTE: Since we're dealing with 2D, the third row and third column
00658    // of the rotation matrix R are of no consequence to us. Therefore, we
00659    // can get by with just the top-left four elements of R.
00660    float R11 = sqr(q[0]) + sqr(q[1]) - sqr(q[2]) - sqr(q[3]) ;
00661    float R12 = 2 * (q[1]*q[2] - q[0]*q[3]) ;
00662    float R21 = 2 * (q[1]*q[2] + q[0]*q[3]) ;
00663    float R22 = sqr(q[0]) + sqr(q[2]) - sqr(q[1]) - sqr(q[3]) ;
00664 
00665    // To find the translation between the data shape and the model, we
00666    // rotate the CoM of the data shape and then subtract the result from
00667    // the CoM of the model shape. See equation (26) on page 243.
00668    //
00669    // NOTE: Due to our concern with only 2D, the z-component of the
00670    // translation vector is zero.
00671    q[4] = mu_x_x - (R11 * mu_p_x + R12 * mu_p_y) ;
00672    q[5] = mu_x_y - (R21 * mu_p_x + R22 * mu_p_y) ;
00673    q[6] = 0 ;
00674 
00675    // Finally, we need to figure out the amount of error between the data
00676    // shape and the model corresponding to the transformation computed
00677    // above. We do that by applying the rotation and translation to each
00678    // point in the data shape and then finding the Euclidean distance to
00679    // the corresponding point on the model shape. The sum of the squares
00680    // of each of these distances will be the mean-square error that we're
00681    // interested in.
00682    //
00683    // This loop implements equation (22) on page 243 of the Besl-McKay
00684    // paper. As usual, we have omitted the z-component of the
00685    // transformation because, for Robolocust's scan matching, we're
00686    // working in two dimensions.
00687    float  err = 0 ;
00688    for (int i = 0; i < N; ++i)
00689       if (C[i] >= 0) // i-th data point corresponds to something on model shape
00690       {
00691          const Scan::RangeReading& D = data[i] ;
00692          const Scan::RangeReading& M = model[C[i]] ;
00693 
00694          err += sqr(M.x() - (R11 * D.x() + R12 * D.y()) - q[4])
00695               + sqr(M.y() - (R21 * D.x() + R22 * D.y()) - q[5]) ;
00696       }
00697    err /= n ;
00698 
00699    /*
00700    LERROR("q = [%8.3f %8.3f %8.3f %8.3f] [%8.3f %8.3f %8.3f]",
00701           q[0], q[1], q[2], q[3], q[4], q[5], q[6]) ;
00702    // */
00703 
00704    // That's it: we have the quaternion-based rotation and translation
00705    // plus the mean-square error associated with this transformation. We
00706    // bundle these two things together and return the result for use in
00707    // step b of the ICP algorithm (see section A, page 244 of the
00708    // Besl-McKay paper).
00709    return ICPTransformation(q, err) ;
00710 }
00711 
00712 #else // GNU Scientific Library not available
00713 
00714 // Can't do eigenvalue and eigenvector computations
00715 static ICPTransformation solve(const Scan&, const Scan&, const Correspondence&)
00716 {
00717    throw missing_libs(MISSING_LIBGSL) ;
00718 }
00719 
00720 #endif // INVT_HAVE_LIBGSL
00721 
00722 // This function implements the Iterative Closest Point algorithm
00723 // described by Besl and McKay. The goal for Robolocust is to match two
00724 // laser range finder scans and return the transformation required to
00725 // properly register the previous scan with reference to the current one.
00726 //
00727 // In terms of the paper's terminology: we consider the current scan to
00728 // be the model shape and the previous one to be the data shape; we want
00729 // to register the data (previous scan) to the model (current scan). The
00730 // resulting transformation required to go from the previous scan to the
00731 // current one should then be able to correct for odometric drift/errors.
00732 Transformation match_scans(const Scan& curr, const Scan& prev)
00733 {
00734    // ICP init: the transformation required to register the data shape to
00735    // the model shape starts off with zero rotation and zero translation.
00736    // Additionally, the intermediate data shape that converges onto the
00737    // model shape as the algorithm progresses is intialized using the
00738    // original data shape.
00739    //
00740    // NOTE: In the paper, the authors use P_k and P_[k+1] to denote the
00741    // fact that the "intermediate" data shape changes with each iteration
00742    // of the algorithm. Here, we simply use the variable P and omit the
00743    // subscripts.
00744    ICPTransformation q ;
00745    Scan P = prev ;
00746 
00747    // Optionally, instead of zeros, we can use raw odometry to "seed" the
00748    // initial transformation.
00749    if (Params::seed_using_odometry()) {
00750       q = curr - prev ;
00751       P = q.apply(prev) ;
00752    }
00753 
00754    // We will need to keep track of the mean square errors in the
00755    // transformation computed in each iteration of the ICP algorithm so
00756    // that we know when we can terminate (when the error drops below some
00757    // preset threshold).
00758    float prev_error = std::numeric_limits<float>::max() ;
00759 
00760    // ICP loop: now, we solve for the rotation and translation required
00761    // to align the data shape (prev scan) to the model shape (current
00762    // scan) until the mean square error associated with transformation
00763    // drops below some preset threshold. To ensure that this loop doesn't
00764    // go on forever (in case the algorithm never converges), we cap the
00765    // maximum number of iterations.
00766    for (int i = 1; i <= Params::max_iterations(); ++i)
00767    {
00768       // Steps a and b (section A, page 244): find the correspondence
00769       // pairs between the data and model shapes and then use these
00770       // correspondences to compute the appropriate rotation and
00771       // translation required for the registration.
00772       q = solve(prev, curr, find_correspondence(P, curr)) ;
00773 
00774       // Step c (section A, page 244): apply the above registration to
00775       // the data shape to get the new intermediate, registered data
00776       // shape, viz., P_[k+1].
00777       P = q.apply(prev) ;
00778 
00779       // Step d (section A, page 244): terminate the iteration when the
00780       // change in the mean-square error falls below a preset threshold
00781       // tau.
00782       float error = abs(prev_error - q.error()) ;
00783       /*
00784       Transformation t = q.convert() ;
00785       LERROR("iter %3d: [%8.3f%9.3f%7.1f] [%9.3e %10.3e]",
00786              i, t.Tx(), t.Ty(), t.w(), q.error(), error) ;
00787       // */
00788       if (error < Params::tau())
00789          break ;
00790 
00791       // Setup for next iteration
00792       prev_error = q.error() ;
00793    }
00794 
00795    // Either ICP converged within the max allowable iterations or it
00796    // didn't. Either ways, return the latest transformation.
00797    return q.convert() ;
00798 }
00799 
00800 //-----------------------------------------------------------------------
00801 
00802 } // end of namespace encapsulating this file's definitions
00803 
00804 /* So things look consistent in everyone's emacs... */
00805 /* Local Variables: */
00806 /* indent-tabs-mode: nil */
00807 /* End: */
Generated on Sun May 8 08:41:31 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3