LoLGMDExtricateTTI.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoLGMDExtricateTTI.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::LGMDExtricateTTI class.
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/control/LoLGMDExtricateTTI.C $
00039 // $Id: LoLGMDExtricateTTI.C 14022 2010-09-23 18:49:14Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoLGMDExtricateTTI.H"
00046 #include "Robots/LoBot/control/LoMetrics.H"
00047 #include "Robots/LoBot/control/LoTurnArbiter.H"
00048 #include "Robots/LoBot/control/LoSpinArbiter.H"
00049 #include "Robots/LoBot/control/LoSpeedArbiter.H"
00050 
00051 #include "Robots/LoBot/LoApp.H"
00052 #include "Robots/LoBot/slam/LoMap.H"
00053 #include "Robots/LoBot/config/LoConfigHelpers.H"
00054 #include "Robots/LoBot/thread/LoUpdateLock.H"
00055 
00056 #include "Robots/LoBot/misc/LoExcept.H"
00057 #include "Robots/LoBot/misc/LoRegistry.H"
00058 #include "Robots/LoBot/misc/singleton.hh"
00059 
00060 #include "Robots/LoBot/util/LoGL.H"
00061 #include "Robots/LoBot/util/LoMath.H"
00062 
00063 // OpenGL headers
00064 #ifdef INVT_HAVE_LIBGL
00065 #include <GL/gl.h>
00066 #endif
00067 
00068 // Standard C++ headers
00069 #include <iomanip>
00070 #include <sstream>
00071 #include <algorithm>
00072 #include <limits>
00073 
00074 //----------------------------- NAMESPACE -------------------------------
00075 
00076 namespace lobot {
00077 
00078 //-------------------------- KNOB TWIDDLING -----------------------------
00079 
00080 namespace {
00081 
00082 // Retrieve settings from lgmd_extricate_tti section of config file
00083 template<typename T>
00084 inline T conf(const std::string& key, const T& default_value)
00085 {
00086    return get_conf<T>(LOBE_LGMD_EXTRICATE_TTI, key, default_value) ;
00087 }
00088 
00089 /// This inner class encapsulates various parameters that can be used
00090 /// to tweak different aspects of the lgmd_extricate_tti behaviour.
00091 class Params : public singleton<Params> {
00092    /// In order to build a virtual force field made up of repulsive as
00093    /// well as attractive forces, we use the distance estimates plus a
00094    /// threshold. When a distance estimate is below this threshold it
00095    /// will result in a repulsive force along the direction in which
00096    /// that locust is looking. Conversely, when a distance estimate
00097    /// exceeds the same threshold, it will cause an attractive force
00098    /// in that locust's direction. The sum of all these force vectors
00099    /// will result in a vector that can be used to drive and steer the
00100    /// robot away from obstacles.
00101    ///
00102    /// This setting specifies the value of the above-mentioned
00103    /// threshold in millimeters.
00104    float m_threshold ;
00105 
00106    /// To prevent this behaviour from becoming overly sensitive, we
00107    /// require the distance estimates corresponding to some minimum
00108    /// number of locusts to fall below the above threshold before the
00109    /// behaviour engages its extrication algorithm.
00110    ///
00111    /// This setting specifies the minimum number of distance estimates
00112    /// that must fall below the distance threshold before extrication
00113    /// will occur. It should be a reasonable number <= the number of
00114    /// locusts actually present.
00115    int m_count ;
00116 
00117    /// In some situations, it can be useful to amplify the magnitudes
00118    /// of the attractive and repulsive force vectors. These two
00119    /// settings specify the values for the amplification factors.
00120    /// Numbers greater then one will amplify the vectors; numbers
00121    /// between 0 and 1 will reduce the magnitudes of the force
00122    /// vectors; negative numbers will negate the directions of the
00123    /// force vectors.
00124    float m_att_amp, m_rep_amp ;
00125 
00126    /// When the robot is backing up due to an extrication command, the
00127    /// LGMD's fire a small amount. Often, the lgmd_extricate_tti
00128    /// behaviour construes this as further actionable input and issues
00129    /// yet more back-up commands, which can result in extended periods
00130    /// of reverse driving. These are entirely spurious extrications and
00131    /// should not occur. This setting helps with the above-mentioned
00132    /// problem by specifying a threshold speed (in m/s) that must be met
00133    /// before lgmd_extricate_tti will kick in (or interfere).
00134    ///
00135    /// By setting this to a small positive quantity such as 0.05 or 0.1,
00136    /// we can ensure that lgmd_extricate_tti remains passive when the
00137    /// robot is backing up (wherein it will report a negative speed). To
00138    /// disable this threshold check, simply use a negative number with a
00139    /// magnitude larger than the robot's top speed (e.g., -10).
00140    ///
00141    /// NOTE: Using a large positive number for this setting (e.g., 10)
00142    /// will result in effectively disabling the lgmd_extricate_tti
00143    /// behaviour. In fact, setting this configuration value to something
00144    /// greater than the robot's average cruising speed will pretty much
00145    /// disable the behaviour. An easier way to do that is to simply not
00146    /// include it in the config file's list of active behaviours. That
00147    /// way, it won't uselessly consume CPU and RAM. Therefore, it would
00148    /// be best to stick to a value such as 0.05 or 0.1m/s.
00149    float m_interference_threshold ;
00150 
00151    /// Users may specify what speed they would like the extrication to
00152    /// occur.
00153    ///
00154    /// CAUTION: It would be unwise to make this speed too high.
00155    float m_extricate_speed ;
00156 
00157    /// In case the RPM sensor is configured to be off, we will need to
00158    /// specify the extricate "speed" in terms of PWM values as well.
00159    ///
00160    /// NOTE: All speed related behaviours should specify both a speed
00161    /// in m/s and a PWM value.
00162    ///
00163    /// CAUTION: For the extricate behaviour, it would be unwise to
00164    /// make the extrication PWM too high.
00165    int m_extricate_pwm ;
00166 
00167    /// Usually, steering control is effected using the turn arbiter,
00168    /// which veers the robot in different directions while it moves,
00169    /// i.e., smooth car-like turns. However, the lgmd_extricate_tti
00170    /// behaviour also supports spin-style steering, i.e., momentarily
00171    /// stopping the robot and then turning it cw/ccw in-place. This flag
00172    /// turns on spin-style steering. By default, the behaviour uses the
00173    /// normal car-like steering mode.
00174    bool m_spin_style_steering ;
00175 
00176    /// Normally, this behaviour will only log an entry stating that it
00177    /// made a steering decision. However, with this flag, we can make it
00178    /// also log the states of each of its time-to-impact estimators.
00179    bool m_log_tti_predictions ;
00180 
00181    /// The number of milliseconds between successive iterations of this
00182    /// behaviour.
00183    ///
00184    /// WARNING: The ability to change a behaviour's update frequency is a
00185    /// very powerful feature whose misuse or abuse can wreak havoc! Be
00186    /// sure to use reasonable values for this setting.
00187    int m_update_delay ;
00188 
00189    /// The location and size (within the Robolocust main window) of the
00190    /// lgmd_extricate_tti behaviour's visualization.
00191    typedef Drawable::Geometry Geom ; // just so accessor definitions line-up
00192    Drawable::Geometry m_geometry ;
00193 
00194    /// Private constructor because this is a singleton.
00195    Params() ;
00196 
00197    // Boilerplate code to make generic singleton design pattern work
00198    friend class singleton<Params> ;
00199 
00200 public:
00201    /// Accessing the various parameters.
00202    //@{
00203    static float threshold()          {return instance().m_threshold ;}
00204    static int   count()              {return instance().m_count     ;}
00205    static float att_amp()            {return instance().m_att_amp   ;}
00206    static float rep_amp()            {return instance().m_rep_amp   ;}
00207    static float extricate_speed()    {return instance().m_extricate_speed    ;}
00208    static int   extricate_pwm()      {return instance().m_extricate_pwm      ;}
00209    static bool  spin_style_steering(){return instance().m_spin_style_steering;}
00210    static bool  log_tti_predictions(){return instance().m_log_tti_predictions;}
00211    static int   update_delay()       {return instance().m_update_delay       ;}
00212    static Geom  geometry()           {return instance().m_geometry           ;}
00213    static float interference_threshold() {
00214       return instance().m_interference_threshold ;
00215    }
00216    //@}
00217 } ;
00218 
00219 // Parameters initialization
00220 Params::Params()
00221    : m_threshold(clamp(conf("threshold", 300.0f), 50.0f, 1000.0f)),
00222      m_count(clamp(conf("count", 3), 1, 180)),
00223      m_att_amp(conf("attractive_amplifier", 1.0f)),
00224      m_rep_amp(conf("repulsive_amplifier", 1.0f)),
00225      m_interference_threshold(conf("interference_threshold", 0.1f)),
00226      m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)),
00227      m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)),
00228      m_spin_style_steering(conf("spin_style_steering", false)),
00229      m_log_tti_predictions(conf("log_tti_predictions", false)),
00230      m_update_delay(clamp(conf("update_delay", 150), 1, 1000)),
00231      m_geometry(conf<std::string>("geometry", "0 0 -1 -1"))
00232 {}
00233 
00234 } // end of local anonymous namespace encapsulating above helpers
00235 
00236 //-------------------------- INITIALIZATION -----------------------------
00237 
00238 LGMDExtricateTTI::LGMDExtricateTTI()
00239    : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_TTI, Params::geometry())
00240 {
00241    start(LOBE_LGMD_EXTRICATE_TTI) ;
00242 }
00243 
00244 LGMDExtricateTTI::Command::Command()
00245    : drive(0), turn(0)
00246 {}
00247 
00248 // Before the lgmd_extricate_tti behaviour's thread begins its regular
00249 // action processing loop, we must ensure that the sensor model for the
00250 // Bayesian time-to-impact estimation is properly setup and that the TTI
00251 // estimators for each locust have been created and are ready for use.
00252 void LGMDExtricateTTI::pre_run()
00253 {
00254    if (! App::robot())
00255       throw behavior_error(MOTOR_SYSTEM_MISSING) ;
00256 
00257    const App::LocustModels& L = App::locusts() ;
00258    const int N = L.size() ;
00259    m_tti.reserve(N) ;
00260    for (int i = 0; i < N; ++i)
00261       m_tti.push_back(new TTIEstimator(L[i])) ;
00262 }
00263 
00264 //---------------------- THE BEHAVIOUR'S ACTION -------------------------
00265 
00266 static void log_tti_predictions(float speed, const TTIEstimator* E)
00267 {
00268    using std::setw  ; using std::left ; using std::right ;
00269    using std::fixed ; using std::setprecision ;
00270 
00271    Metrics::Log log ;
00272    log << setw(Metrics::opw())  << left << LOBE_LGMD_EXTRICATE_TTI
00273        << "TTI estimator info:" << Metrics::newl() ;
00274 
00275    log << "     current robot speed = " << fixed << setprecision(3)
00276        << speed << " m/s" << Metrics::newl();
00277 
00278    log << "        locust direction = " << fixed << setprecision(0)
00279        << E->locust_direction() << " degrees" << Metrics::newl() ;
00280    log << "         LGMD spike rate = " << fixed << setprecision(0)
00281        << E->lgmd()             << " Hz" << Metrics::newl() ;
00282 
00283    log << "   actual time-to-impact = "
00284        << fixed << setprecision(1) << setw(4) << right
00285        << E->actual_tti()  << " seconds"<< Metrics::newl() ;
00286    log << "predicted time-to-impact = "
00287        << fixed << setprecision(1) << setw(4) << right
00288        << E->predicted_tti()<<" seconds"<< Metrics::newl() ;
00289 
00290    log << "         actual distance = "
00291        << fixed << setprecision(0) << setw(4) << right
00292        << E->actual_distance()   << " mm" << Metrics::newl() ;
00293    log << "      predicted distance = "
00294        << fixed << setprecision(0) << setw(4) << right
00295        << E->predicted_distance()<< " mm" << Metrics::newl() ;
00296 
00297    log << "   prediction confidence = " << fixed << setprecision(1)
00298        << E->confidence() * 100 << "%" ;
00299 }
00300 
00301 // This version of the extricate behaviour feeds the LGMD spikes of the
00302 // individual locusts into time-to-impact estimators and then computes
00303 // distances to obstacles based on these TTI estimates, which, in turn,
00304 // are used to build a vector force field composed of attractive and
00305 // repulsive force vectors. The sum of all the force vectors result in
00306 // the final steering vector.
00307 void LGMDExtricateTTI::action()
00308 {
00309    // To avoid holding update lock for an extended period, copy latest
00310    // LGMD spike rates to TTI estimators before running Bayesian state
00311    // estimation update equations.
00312    const int N = m_tti.size() ;
00313    UpdateLock::begin_read() ;
00314       for (int i = 0; i < N; ++i)
00315          m_tti[i]->copy_lgmd() ;
00316 
00317       float speed   = App::robot()->current_speed() ;
00318       float heading = App::robot()->current_heading() ;
00319    UpdateLock::end_read() ;
00320 
00321    // Don't interfere with a potentially ongoing extrication...
00322    if (speed < Params::interference_threshold()) {
00323       record_viz() ; // no extrication, therefore, nothing to record
00324       return ;
00325    }
00326 
00327    // Update TTI estimate for each locust and use the TTI to compute
00328    // distance to obstacle in that locust's direction.
00329    Vector velocity(speed * cos(heading), speed * sin(heading)) ;
00330    int  count = 0 ; // number of "distance readings" below configured threshold
00331    for (int i = 0; i < N; ++i)
00332    {
00333       TTIEstimator* estimator = m_tti[i] ;
00334       estimator->update() ;
00335       estimator->compute_distance(velocity) ;
00336       float D = estimator->distance() ;
00337       if (D > 0 && D <= Params::threshold())
00338          ++count ;
00339       if (Params::log_tti_predictions())
00340          log_tti_predictions(speed, estimator) ;
00341    }
00342 
00343    // Issue motor commands if we have the requisite number of distance
00344    // readings below the "danger zone" threshold.
00345    Command C ;
00346    Vector A, R, F ;
00347    if (count >= Params::count())
00348    {
00349       // Compute attractive, repulsive and total force vectors
00350       for (int i = 0; i < N; ++i)
00351       {
00352          const TTIEstimator* estimator = m_tti[i] ;
00353          float distance = estimator->distance() ;
00354          if (distance < 0) // no distance estimate from this locust
00355             continue ;
00356          float e = distance - Params::threshold() ; // distance "error"
00357          if (e < 0) // distance reading is inside danger zone
00358             R += (Params::rep_amp() * e) * estimator->direction() ;
00359          else // distance reading is outside danger zone
00360             A += (Params::att_amp() * e) * estimator->direction() ;
00361       }
00362       F = A + R ;
00363 
00364       // Decide drive and turn commands based on above force field
00365       const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max()) ;
00366       switch (quadrant(direction(F)))
00367       {
00368          case 1:
00369             C.drive =  1 ; C.turn =  T ;
00370             break ;
00371          case 2:
00372             C.drive = -1 ; C.turn = -T ;
00373             break ;
00374          case 3:
00375             C.drive = -1 ; C.turn =  T ;
00376             break ;
00377          case 4:
00378             C.drive =  1 ; C.turn = -T ;
00379             break ;
00380          default: // hunh?!? quadrant() shouldn't return anything outside [1,4]
00381             throw misc_error(LOGIC_ERROR) ;
00382       }
00383 
00384       if (Params::spin_style_steering())
00385          SpinArbiter::instance().vote(base::name,
00386                                       new SpinArbiter::Vote(C.turn)) ;
00387       else
00388       {
00389          SpeedArbiter::instance().vote(base::name,
00390             new SpeedArbiter::Vote(C.drive * Params::extricate_speed(),
00391                                    C.drive * Params::extricate_pwm())) ;
00392          TurnArbiter::instance().vote(base::name,
00393             new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ;
00394       }
00395 
00396       Metrics::Log log ;
00397       log << std::setw(Metrics::opw()) << std::left << base::name ;
00398       Map* M = App::map() ;
00399       if (M)
00400          log << M->current_pose() ;
00401    }
00402    record_viz(A, R, F, C) ;
00403 }
00404 
00405 // Record stuff for visualization
00406 void
00407 LGMDExtricateTTI::
00408 record_viz(const Vector& att, const Vector& rep, const Vector& tot,
00409            const LGMDExtricateTTI::Command& cmd)
00410 {
00411    viz_lock() ;
00412       m_attractive  = att ;
00413       m_repulsive   = rep ;
00414       m_total_force = tot ;
00415       m_cmd = cmd ;
00416    viz_unlock() ;
00417 }
00418 
00419 //--------------------------- VISUALIZATION -----------------------------
00420 
00421 #ifdef INVT_HAVE_LIBGL
00422 
00423 // Helper function to convert the magnitude of a vector into a string,
00424 // prefixing the supplied single character label to the mag string.
00425 static std::string mag(char label, const Vector& v)
00426 {
00427    using namespace std ;
00428 
00429    std::ostringstream v_str ;
00430    v_str << label   << ": " << fixed << setprecision(1) << magnitude(v) ;
00431    return v_str.str() ;
00432 }
00433 
00434 // This function renders the extrication decision as well as the faux
00435 // distance "readings" returned by the LGMD "range" sensor.
00436 void LGMDExtricateTTI::render_me()
00437 {
00438    // Make local copies of required visualization info so that extricate
00439    // thread isn't held up waiting for visualization to complete.
00440    const int N = m_tti.size() ;
00441 
00442    typedef std::pair<Vector, float> DDP ; // direction-distance pair
00443    std::vector<DDP> ddp ;
00444    ddp.reserve(N) ;
00445    float normalizer = std::numeric_limits<float>::min() ;
00446 
00447    viz_lock() ;
00448       Command C  = m_cmd ;
00449       Vector  A  = m_attractive  ;
00450       Vector  R  = m_repulsive   ;
00451       Vector  T  = m_total_force ;
00452 
00453       for (int i = 0; i < N; ++i) {
00454          ddp.push_back(DDP(m_tti[i]->direction(), m_tti[i]->distance())) ;
00455          if (normalizer < m_tti[i]->distance())
00456             normalizer = m_tti[i]->distance() ;
00457       }
00458    viz_unlock() ;
00459    normalizer = 1/normalizer ;
00460 
00461    // Draw principal axes to make it easier to understand what's going on
00462    // with the force vectors and turn command.
00463    unit_view_volume() ;
00464    glColor3f(1, 1, 1) ;
00465    glBegin(GL_LINES) ;
00466       glVertex2i(0, -1) ;
00467       glVertex2i(0,  1) ;
00468       glVertex2i(-1, 0) ;
00469       glVertex2i( 1, 0) ;
00470    glEnd() ;
00471 
00472    // Render the extrication decision's visualization
00473    if (C.drive == 0 && C.turn == 0)
00474       ; // no extrication took place
00475    else
00476    {
00477       // Render the TTI-based distance estimates
00478       glBegin(GL_LINES) ;
00479          for (int i = 0; i < N; ++i)
00480          {
00481             if (ddp[i].second < 0) // no distance estimate in this direction
00482                continue ;
00483             Vector d = (normalizer * ddp[i].second) * ddp[i].first ;
00484             if (ddp[i].second > Params::threshold())
00485                glColor3f(0, 0.8f, 0.2f) ; // greenish
00486             else
00487                glColor3f(0.8f, 0, 0.2f) ; // reddish
00488             glVertex2i(0, 0) ;
00489             glVertex2f(d.i, d.j) ;
00490          }
00491       glEnd() ;
00492 
00493       // Render force field vectors
00494       glColor3f(0, 1, 0) ;
00495       draw_vector(normalized(A)) ;
00496 
00497       glColor3f(1, 0, 0) ;
00498       draw_vector(normalized(R)) ;
00499 
00500       glColor3f(0.15f, 0.75f, 0.85f) ;
00501       draw_vector(normalized(T)) ;
00502 
00503       // Render drive and turn commands
00504       glColor3f(1, 1, 1) ;
00505       draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ;
00506    }
00507 
00508    // Label the visualization so that it is easy to tell which behaviour
00509    // is being visualized. Also show the magnitudes of the forces.
00510    restore_view_volume() ;
00511    text_view_volume() ;
00512       glColor3f(0, 1, 1) ;
00513       draw_label(3, 12, "LGMD Ext. TTI") ;
00514 
00515       glColor3f(0, 1, 0) ;
00516       draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ;
00517 
00518       glColor3f(1, 0, 0) ;
00519       draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ;
00520 
00521       glColor3f(0.15f, 0.75f, 0.85f) ;
00522       draw_label(3, m_geometry.height -  4, mag('T', T).c_str()) ;
00523    restore_view_volume() ;
00524 }
00525 
00526 #endif
00527 
00528 //----------------------------- CLEAN-UP --------------------------------
00529 
00530 LGMDExtricateTTI::~LGMDExtricateTTI()
00531 {
00532    purge_container(m_tti) ;
00533 }
00534 
00535 //-----------------------------------------------------------------------
00536 
00537 } // end of namespace encapsulating this file's definitions
00538 
00539 /* So things look consistent in everyone's emacs... */
00540 /* Local Variables: */
00541 /* indent-tabs-mode: nil */
00542 /* End: */
Generated on Sun May 8 08:41:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3