LoLGMDExtricateVFF.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoLGMDExtricateVFF.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::LGMDExtricateVFF 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/LoLGMDExtricateVFF.C $
00039 // $Id: LoLGMDExtricateVFF.C 13840 2010-08-27 22:28:12Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoLGMDExtricateVFF.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/lgmd/LocustModel.H"
00054 #include "Robots/LoBot/config/LoConfigHelpers.H"
00055 #include "Robots/LoBot/thread/LoUpdateLock.H"
00056 
00057 #include "Robots/LoBot/misc/LoExcept.H"
00058 #include "Robots/LoBot/misc/LoRegistry.H"
00059 #include "Robots/LoBot/misc/singleton.hh"
00060 
00061 #include "Robots/LoBot/util/LoGL.H"
00062 #include "Robots/LoBot/util/LoMath.H"
00063 #include "Robots/LoBot/util/range.hh"
00064 
00065 // OpenGL headers
00066 #ifdef INVT_HAVE_LIBGL
00067 #include <GL/gl.h>
00068 #endif
00069 
00070 // Standard C++ headers
00071 #include <iomanip>
00072 #include <sstream>
00073 #include <algorithm>
00074 #include <memory>
00075 
00076 //----------------------------- NAMESPACE -------------------------------
00077 
00078 namespace lobot {
00079 
00080 //-------------------------- KNOB TWIDDLING -----------------------------
00081 
00082 namespace {
00083 
00084 // Retrieve settings from lgmd_extricate_vff section of config file
00085 template<typename T>
00086 static inline T conf(const std::string& key, const T& default_value)
00087 {
00088    return get_conf<T>(LOBE_LGMD_EXTRICATE_VFF, key, default_value) ;
00089 }
00090 
00091 /// This inner class encapsulates various parameters that can be used to
00092 /// tweak different aspects of the lgmd_extricate_vff behaviour.
00093 class Params : public singleton<Params> {
00094    /// A locust whose LGMD spike rate exceeds a certain threshold will
00095    /// result in a repulsive force along the direction in which that
00096    /// locust is looking. Conversely, when a locust's LGMD spike rate
00097    /// falls below this threshold, it will cause an attractive force.
00098    /// When all the forces are put together, we will get a final force
00099    /// vector that can be used to drive and steer the robot.
00100    ///
00101    /// This setting specifies the value of the above-mentioned
00102    /// threshold in spikes per second (i.e., Hz). It should be a
00103    /// reasonable value that lies within the [min, max] spike range
00104    /// specified in the chosen LGMD model's settings section. In case
00105    /// the robot is connected to actual locusts, then this setting's
00106    /// value should be some reasonable empirically determined value.
00107    float m_threshold ;
00108 
00109    /// To prevent this behaviour from becoming overly sensitive, we
00110    /// require the LGMD spike rates of a minimum number of locusts to
00111    /// exceed the above threshold before the behaviour engages its
00112    /// extrication algorithm.
00113    ///
00114    /// This setting specifies the above-mentioned minimum locust
00115    /// count. It should be a reasonable number <= the number of
00116    /// locusts actually present.
00117    int m_count ;
00118 
00119    /// In some situations, it can be useful to amplify the magnitudes
00120    /// of the attractive and repulsive force vectors. These two
00121    /// settings specify the values for the amplification factors.
00122    /// Numbers greater then one will amplify the vectors; numbers
00123    /// between 0 and 1 will reduce the magnitudes of the force
00124    /// vectors; negative numbers will negate the directions of the
00125    /// force vectors.
00126    float m_att_amp, m_rep_amp ;
00127 
00128    /// Users may specify what speed they would like the extrication to
00129    /// occur.
00130    ///
00131    /// CAUTION: It would be unwise to make this speed too high.
00132    float m_extricate_speed ;
00133 
00134    /// In case the RPM sensor is configured to be off, we will need to
00135    /// specify the extricate "speed" in terms of PWM values as well.
00136    ///
00137    /// NOTE: All speed related behaviours should specify both a speed
00138    /// in m/s and a PWM value.
00139    ///
00140    /// CAUTION: For the extricate behaviour, it would be unwise to
00141    /// make the extrication PWM too high.
00142    int m_extricate_pwm ;
00143 
00144    /// Usually, steering control is effected using the turn arbiter,
00145    /// which veers the robot in different directions while it moves,
00146    /// i.e., smooth car-like turns. However, the lgmd_extricate_vff
00147    /// behaviour also supports spin-style steering, i.e., momentarily
00148    /// stopping the robot and then turning it cw/ccw in-place. This flag
00149    /// turns on spin-style steering. By default, the behaviour uses the
00150    /// normal car-like steering mode.
00151    bool m_spin_style_steering ;
00152 
00153    /// The number of milliseconds between successive iterations of this
00154    /// behaviour.
00155    ///
00156    /// WARNING: The ability to change a behaviour's update frequency is a
00157    /// very powerful feature whose misuse or abuse can wreak havoc! Be
00158    /// sure to use reasonable values for this setting.
00159    int m_update_delay ;
00160 
00161    /// The location and size (within the Robolocust main window) of the
00162    /// lgmd_extricate_vff behaviour's visualization.
00163    Drawable::Geometry m_geometry ;
00164 
00165    /// Private constructor because this is a singleton.
00166    Params() ;
00167 
00168    // Boilerplate code to make generic singleton design pattern work
00169    friend class singleton<Params> ;
00170 
00171 public:
00172    /// Accessing the various parameters.
00173    //@{
00174    static float threshold()       {return instance().m_threshold ;}
00175    static int   count()           {return instance().m_count     ;}
00176    static float att_amp()         {return instance().m_att_amp   ;}
00177    static float rep_amp()         {return instance().m_rep_amp   ;}
00178    static float extricate_speed() {return instance().m_extricate_speed ;}
00179    static int   extricate_pwm()   {return instance().m_extricate_pwm   ;}
00180    static bool  spin_style_steering(){return instance().m_spin_style_steering;}
00181    static int   update_delay()       {return instance().m_update_delay       ;}
00182    static Drawable::Geometry geometry() {return instance().m_geometry        ;}
00183    //@}
00184 } ;
00185 
00186 // Parameters initialization
00187 Params::Params()
00188    : m_threshold(conf("threshold", 250)),
00189      m_count(conf("count", 1)),
00190      m_att_amp(conf("attractive_amplifier", 1.0f)),
00191      m_rep_amp(conf("repulsive_amplifier",  1.0f)),
00192      m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)),
00193      m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)),
00194      m_spin_style_steering(conf("spin_style_steering", false)),
00195      m_update_delay(clamp(conf("update_delay", 150), 1, 1000)),
00196      m_geometry(conf<std::string>("geometry", "0 0 10 10"))
00197 {}
00198 
00199 } // end of local anonymous namespace encapsulating above helpers
00200 
00201 //-------------------------- INITIALIZATION -----------------------------
00202 
00203 LGMDExtricateVFF::LGMDExtricateVFF()
00204    : base(Params::update_delay(), LOBE_LGMD_EXTRICATE_VFF, Params::geometry())
00205 {
00206    start(LOBE_LGMD_EXTRICATE_VFF) ;
00207 }
00208 
00209 LGMDExtricateVFF::Command::Command()
00210    : drive(0), turn(0)
00211 {}
00212 
00213 void LGMDExtricateVFF::pre_run()
00214 {
00215    if (! App::robot())
00216       throw behavior_error(MOTOR_SYSTEM_MISSING) ;
00217 }
00218 
00219 //---------------------- THE BEHAVIOUR'S ACTION -------------------------
00220 
00221 // Helper function object to compute the attractive and repulsive forces
00222 // for a block and maintain a running total.
00223 namespace {
00224 
00225 // DEVNOTE: This function object is meant to be used with the STL
00226 // for_each algorithm. The goal is to compute the total attractive and
00227 // repulsive forces using the current spike rates of all the availbale
00228 // LGMD's. Also, we want to count how many locusts have exceeded the
00229 // preconfigured spiking threshold.
00230 //
00231 // Since std::for_each() passes its function object parameter by value,
00232 // the call site will not get back the force vectors and threshold count
00233 // computed by this function object unless special measures are taken to
00234 // facilitate the return of these quantities.
00235 //
00236 // One possibility is to use object references. That is, the att, rep and
00237 // num_rep data members defined below can be of type Vector& and int&
00238 // respectively. In LGMDExtricateVFF::action(), we can create local
00239 // variables for the result vectors and threshold count, passing
00240 // references to them to this function object's constructor. Thus, when
00241 // std::for_each() is done, LGMDExtricateVFF::action() will have the
00242 // results.
00243 //
00244 // Alternatively, recalling that the STL for_each algorithm returns the
00245 // function object passed to it at the end of the for_each() function, we
00246 // can have the caller not discard for_each's return value (as is
00247 // typically done) and, instead, use the function's return value to
00248 // retrieve the force vectors and threshold count computed.
00249 //
00250 // This second approach is the one adopted here.
00251 class compute_forces {
00252    mutable Vector att  ; // attractive force
00253    mutable Vector rep  ; // repulsive  force
00254    mutable int num_rep ; // number of repulsive vectors
00255 public:
00256    compute_forces() ;
00257    void operator()(const LocustModel*) const ;
00258 
00259    const Vector& attractive()  const {return att ;}
00260    const Vector& repulsive()   const {return rep ;}
00261    const int repulsive_count() const {return num_rep ;}
00262 } ;
00263 
00264 compute_forces::compute_forces()
00265    : num_rep(0)
00266 {}
00267 
00268 // When the LGMD spike rate for the locust under consideration exceeds
00269 // the spike threshold set in the config file, it means that particular
00270 // locust has sensed an object on a collisional trajectory towards it.
00271 // When the spike rate distance measurement is below the threshold, there
00272 // ought not be anything nearby.
00273 //
00274 // If we think of the spike rate threshold as a kind of membrane, then
00275 // the difference between an actual LGMD spike rate and the threshold is
00276 // a measure of the amount by which the object being sensed has
00277 // penetrated the membrane (if the difference is positive; if negative,
00278 // then it tells us how "far away" the object is from the membrane).
00279 //
00280 // This difference, which we can think of as an error, can be used to
00281 // size the force vectors. The greater the error, the greater the
00282 // magnitude of the force.
00283 void compute_forces::operator()(const LocustModel* L) const
00284 {
00285    float lgmd = L->get_lgmd() ;
00286    float e = lgmd - Params::threshold() ; // threshold "error"
00287    Vector f = Vector(cos(L->direction()), sin(L->direction())) ;
00288    if (e < 0) // LGMD is below threshold
00289       att += -Params::att_amp() * e * f ;
00290    else { // LGMD spike rate is above threshold
00291       rep += -Params::rep_amp() * e * f ;
00292       ++num_rep ;
00293    }
00294 }
00295 
00296 } // end of local namespace encapsulating above helpers
00297 
00298 // This version of the extricate behaviour monitors the LGMD spike rates
00299 // of all the locusts and issues appropriate turn and drive commands when
00300 // things get too close by computing virtual attractive and repulsive
00301 // forces on the basis of the spikes.
00302 void LGMDExtricateVFF::action()
00303 {
00304    // Compute the attractive and repulsive forces and also the number of
00305    // LGMD's that have exceeded the spiking threshold.
00306    UpdateLock::begin_read() ;
00307       compute_forces force_field =
00308          std::for_each(App::locusts().begin(), App::locusts().end(),
00309                        compute_forces()) ;
00310    UpdateLock::end_read() ;
00311 
00312    // If the danger zone has been penetrated, use the attractive and
00313    // repulsive forces to determine correct motor commands.
00314    Command C ;
00315    Vector A, R, F ;
00316    if (force_field.repulsive_count() >= Params::count())
00317    {
00318       A = force_field.attractive() ;
00319       R = force_field.repulsive()  ;
00320       F = A + R ;
00321 
00322       const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max());
00323       switch (quadrant(direction(F)))
00324       {
00325          case 1:
00326             C.drive =  1 ; C.turn =  T ;
00327             break ;
00328          case 2:
00329             C.drive = -1 ; C.turn = -T ;
00330             break ;
00331          case 3:
00332             C.drive = -1 ; C.turn =  T ;
00333             break ;
00334          case 4:
00335             C.drive =  1 ; C.turn = -T ;
00336             break ;
00337          default: // hunh?!? quadrant() shouldn't return anything outside [1,4]
00338             throw misc_error(LOGIC_ERROR) ;
00339       }
00340 
00341       if (Params::spin_style_steering())
00342          SpinArbiter::instance().vote(base::name,
00343                                       new SpinArbiter::Vote(C.turn)) ;
00344       else
00345       {
00346          SpeedArbiter::instance().vote(base::name,
00347             new SpeedArbiter::Vote(C.drive * Params::extricate_speed(),
00348                                    C.drive * Params::extricate_pwm())) ;
00349          TurnArbiter::instance().vote(base::name,
00350             new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ;
00351       }
00352 
00353       Metrics::Log log ;
00354       log << std::setw(Metrics::opw()) << std::left << base::name ;
00355       Map* M = App::map() ;
00356       if (M)
00357          log << M->current_pose() ;
00358    }
00359 
00360    // Record stuff for visualization
00361    viz_lock() ;
00362       m_attractive  = A ;
00363       m_repulsive   = R ;
00364       m_total_force = F ;
00365 
00366       m_cmd = C ;
00367    viz_unlock() ;
00368 }
00369 
00370 //--------------------------- VISUALIZATION -----------------------------
00371 
00372 #ifdef INVT_HAVE_LIBGL
00373 
00374 // Helper function to convert the magnitude of a vector into a string,
00375 // prefixing the supplied single character label to the mag string.
00376 static std::string mag(char label, const Vector& v)
00377 {
00378    using namespace std ;
00379 
00380    std::ostringstream v_str ;
00381    v_str << label   << ": " << fixed << setprecision(1) << magnitude(v) ;
00382    return v_str.str() ;
00383 }
00384 
00385 // The visualization callback
00386 void LGMDExtricateVFF::render_me()
00387 {
00388    // Make local copies so that extricate thread isn't held up waiting
00389    // for visualization thread to complete.
00390    viz_lock() ;
00391       Command C = m_cmd ;
00392       Vector  A = m_attractive  ;
00393       Vector  R = m_repulsive   ;
00394       Vector  T = m_total_force ;
00395    viz_unlock() ;
00396 
00397    // Draw principal axes (to make it easier to understand what's going
00398    // on with the force vectors and turn command).
00399    unit_view_volume() ;
00400    glColor3f(1, 1, 1) ;
00401    glBegin(GL_LINES) ;
00402       glVertex2i(0, -1) ;
00403       glVertex2i(0,  1) ;
00404       glVertex2i(-1, 0) ;
00405       glVertex2i( 1, 0) ;
00406    glEnd() ;
00407 
00408    // Render the extrication decision's visualization
00409    if (C.drive == 0 && C.turn == 0)
00410       ; // no extrication took place
00411    else
00412    {
00413       glColor3f(0, 1, 0) ;
00414       draw_vector(normalized(A)) ;
00415 
00416       glColor3f(1, 0, 0) ;
00417       draw_vector(normalized(R)) ;
00418 
00419       glColor3f(0.15f, 0.75f, 0.85f) ;
00420       draw_vector(normalized(T)) ;
00421 
00422       glColor3f(1, 1, 1) ;
00423       draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ;
00424    }
00425 
00426    // Label the visualization so that it is easy to tell which behaviour
00427    // is being visualized. Also show the magnitudes of the forces.
00428    restore_view_volume() ;
00429    text_view_volume() ;
00430       glColor3f(0, 1, 1) ;
00431       draw_label(3, 12, "LGMD Ext. VFF") ;
00432 
00433       glColor3f(0, 1, 0) ;
00434       draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ;
00435 
00436       glColor3f(1, 0, 0) ;
00437       draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ;
00438 
00439       glColor3f(0.15f, 0.75f, 0.85f) ;
00440       draw_label(3, m_geometry.height -  4, mag('T', T).c_str()) ;
00441    restore_view_volume() ;
00442 }
00443 
00444 #endif
00445 
00446 //----------------------------- CLEAN-UP --------------------------------
00447 
00448 LGMDExtricateVFF::~LGMDExtricateVFF(){}
00449 
00450 //-----------------------------------------------------------------------
00451 
00452 } // end of namespace encapsulating this file's definitions
00453 
00454 /* So things look consistent in everyone's emacs... */
00455 /* Local Variables: */
00456 /* indent-tabs-mode: nil */
00457 /* End: */
Generated on Sun May 8 08:41:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3