LoExtricate.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoExtricate.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::Extricate 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/LoExtricate.C $
00039 // $Id: LoExtricate.C 13840 2010-08-27 22:28:12Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoExtricate.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/io/LoDangerZone.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 
00064 // OpenGL headers
00065 #ifdef INVT_HAVE_LIBGL
00066 #include <GL/gl.h>
00067 #endif
00068 
00069 // Standard C++ headers
00070 #include <iomanip>
00071 #include <sstream>
00072 #include <algorithm>
00073 #include <memory>
00074 
00075 //----------------------------- NAMESPACE -------------------------------
00076 
00077 namespace lobot {
00078 
00079 //-------------------------- KNOB TWIDDLING -----------------------------
00080 
00081 namespace {
00082 
00083 // Retrieve settings from extricate section of config file
00084 template<typename T>
00085 static inline T conf(const std::string& key, const T& default_value)
00086 {
00087    return get_conf<T>(LOBE_EXTRICATE, key, default_value) ;
00088 }
00089 
00090 /// This local class encapsulates various parameters that can be used to
00091 /// tweak different aspects of the extricate behaviour.
00092 class Params : public singleton<Params> {
00093    /// Users may specify what speed they would like the extrication to
00094    /// occur.
00095    ///
00096    /// CAUTION: It would be unwise to make this speed too high.
00097    float m_extricate_speed ;
00098 
00099    /// In case the RPM sensor is configured to be off, we will need to
00100    /// specify the extricate "speed" in terms of PWM values as well.
00101    ///
00102    /// NOTE: All speed related behaviours should specify both a speed
00103    /// in m/s and a PWM value.
00104    ///
00105    /// CAUTION: For the extricate behaviour, it would be unwise to
00106    /// make the extrication PWM too high.
00107    int m_extricate_pwm ;
00108 
00109    /// When lobot is running the LGMD-based avoidance behaviours, it can
00110    /// come to a complete halt due to the emergency stop behaviour and
00111    /// then not restart because the lack of motion will result in the
00112    /// LGMD spikes becoming dormant. To work around this problem, we can
00113    /// run the LRF-based extricate behaviour in addition to the
00114    /// LGMD-based extricate behaviours to restart the robot so as to
00115    /// start the spiking activity again.
00116    ///
00117    /// However, in this capacity, it would be best to reduce the
00118    /// frequency at which the LRF-based extricate behaviour runs.
00119    /// Furthermore, we would also want this behaviour to only perform its
00120    /// extrication actions if the robot is actually stopped.
00121    ///
00122    /// This setting is a flag that indicates whether the extricate
00123    /// behaviour should operate in "restart mode" as described above. By
00124    /// default, it is off, i.e., we assume this behaviour is the only
00125    /// active extricate behaviour and that it should perform extrication
00126    /// whenever the danger zone is penetrated, regardless of the robot's
00127    /// current motion state.
00128    bool m_restart_mode ;
00129 
00130    /// Usually, steering control is effected using the turn arbiter,
00131    /// which veers the robot in different directions while it moves,
00132    /// i.e., smooth car-like turns. However, the lgmd_extricate_tti
00133    /// behaviour also supports spin-style steering, i.e., momentarily
00134    /// stopping the robot and then turning it cw/ccw in-place. This flag
00135    /// turns on spin-style steering. By default, the behaviour uses the
00136    /// normal car-like steering mode.
00137    bool m_spin_style_steering ;
00138 
00139    /// The number of milliseconds between successive iterations of this
00140    /// behaviour.
00141    ///
00142    /// WARNING: The ability to change a behaviour's update frequency is a
00143    /// very powerful feature whose misuse or abuse can wreak havoc! Be
00144    /// sure to use reasonable values for this setting.
00145    int m_update_delay ;
00146 
00147    /// The location and size (within the Robolocust main window) of the
00148    /// goal behaviour's visualization.
00149    typedef Drawable::Geometry Geom ; // just for properly aligning accessors
00150    Geom m_geometry ;
00151 
00152    /// Private constructor because this is a singleton.
00153    Params() ;
00154    friend class singleton<Params> ;
00155 
00156 public:
00157    /// Accessing the various parameters
00158    //@{
00159    static float extricate_speed()    {return instance().m_extricate_speed    ;}
00160    static int   extricate_pwm()      {return instance().m_extricate_pwm      ;}
00161    static bool  restart_mode()       {return instance().m_restart_mode       ;}
00162    static bool  spin_style_steering(){return instance().m_spin_style_steering;}
00163    static int   update_delay()       {return instance().m_update_delay       ;}
00164    static Geom  geometry()           {return instance().m_geometry           ;}
00165    //@}
00166 } ;
00167 
00168 // Parameter initialization
00169 Params::Params()
00170    : m_extricate_speed(clamp(conf("extricate_speed", 0.15f), 0.1f, 0.75f)),
00171      m_extricate_pwm(clamp(conf("extricate_pwm", 25), 10, 50)),
00172      m_restart_mode(conf("restart_mode", false)),
00173      m_spin_style_steering(conf("spin_style_steering", false)),
00174      m_update_delay(clamp(conf("update_delay", 1750), 500, 10000)),
00175      m_geometry(conf<std::string>("geometry", "0 0 10 10"))
00176 {}
00177 
00178 } // end of local anonymous namespace encapsulating above helpers
00179 
00180 //-------------------------- INITIALIZATION -----------------------------
00181 
00182 Extricate::Extricate()
00183    : base(Params::update_delay(), LOBE_EXTRICATE, Params::geometry())
00184 {
00185    start(LOBE_EXTRICATE) ;
00186 }
00187 
00188 Extricate::Command::Command()
00189    : drive(0), turn(0)
00190 {}
00191 
00192 void Extricate::pre_run()
00193 {
00194    if (! App::robot())
00195       throw behavior_error(MOTOR_SYSTEM_MISSING) ;
00196 }
00197 
00198 //---------------------- THE BEHAVIOUR'S ACTION -------------------------
00199 
00200 // Helper function object to compute the attractive and repulsive forces
00201 // for a block and maintain a running total.
00202 namespace {
00203 
00204 // DEVNOTE: This function object is meant to be used with the STL
00205 // for_each algorithm. The goal is to compute the total attractive and
00206 // repulsive forces for all of the DangerZone's blocks one block at a
00207 // time. Since std::for_each() passes its function object parameter by
00208 // value, the call site will not get back the force vectors computed by
00209 // this function object unless special measures are taken to facilitate
00210 // the return of these vectors.
00211 //
00212 // One possibility is to use object references. That is, the att and rep
00213 // data members defined below can be of type Vector& and, in
00214 // Extricate::action(), we can create the result vectors and pass them to
00215 // this function object as references (via the constructor). Thus, when
00216 // std::for_each() is done, Extricate::action() will have the results.
00217 //
00218 // Alternatively, recalling that the STL for_each algorithm returns the
00219 // function object passed to it at the end of the for_each() function, we
00220 // can have the caller not discard for_each's return value (as is
00221 // typically done) and, instead, use the function's return value to
00222 // retrieve the force vectors computed for all the DangerZone blocks.
00223 //
00224 // This second approach is the one adopted here.
00225 class compute_forces {
00226    const LRFData& lrf ;
00227    mutable Vector att ; // attractive force
00228    mutable Vector rep ; // repulsive  force
00229 public:
00230    compute_forces(const LRFData&) ;
00231    void operator()(const DangerZone::Block&) const ;
00232 
00233    const Vector& attractive() const {return att ;}
00234    const Vector& repulsive()  const {return rep ;}
00235 } ;
00236 
00237 compute_forces::compute_forces(const LRFData& L)
00238    : lrf(L)
00239 {}
00240 
00241 // Each DangerZone::Block contains a distance threshold. When the LRF
00242 // returns a distance measurement less than this threshold, it means that
00243 // particular reading indicates something inside the danger zone. When
00244 // the distance measurement is greater than the block's distance
00245 // threshold, the object being sensed lies outside the danger zone.
00246 //
00247 // If we think of a block's distance threshold as a kind of membrane,
00248 // then the difference between a distance reading and the distance
00249 // threshold for the block to which that reading belongs is a measure of
00250 // the amount by which the object being sensed has penetrated the
00251 // membrane (if the difference is negative; if positive, then it tells us
00252 // how far away the object is from the membrane).
00253 //
00254 // This difference, or distance "error", can be used to size the force
00255 // vectors. The greater the error, the greater the magnitude of the
00256 // force.
00257 //
00258 // Since the distance thresholds for a block will be fairly small
00259 // relative to the total distance range that can be sensed by the laser
00260 // range finder, the magnitudes of negative distance errors (i.e., when
00261 // an object has penetrated the danger zone) will be much smaller than
00262 // the magnitudes of positive errors (especially for objects far away on
00263 // one side of the robot). For example, the Hokuyo URG-LX-01 can sense
00264 // distances in the range 60mm to 5600mm; and distance thresholds for the
00265 // robot are usually in the range of 250-450mm. So, if the robot is near
00266 // a wall on its right but has a clear path to its left and in front, the
00267 // total magnitude of the attractive force will be quite high while that
00268 // of the repulsive force will be rather low.
00269 //
00270 // To even out this lopsidedness, we square the errors for repulsive
00271 // forces but not for attractive forces. The force vector is then
00272 // computed simply by multiplying this scalar value and the unit vector
00273 // for each LRF reading's angle.
00274 void compute_forces::operator()(const DangerZone::Block& B) const
00275 {
00276    for (int angle = B.start(); angle <= B.end(); ++angle)
00277    {
00278       int distance = lrf[angle] ;
00279       if (distance < 0) // bad reading along this direction
00280          continue ;
00281       float  e = distance - B.danger_zone() ; // distance "error"
00282       Vector f = Vector(cos(angle), sin(angle)) ;
00283       if (e < 0) // distance reading is inside danger zone
00284          rep += sqr(e) * f ;
00285       else // distance reading is outside danger zone
00286          att += e * f ;
00287    }
00288 }
00289 
00290 } // end of local namespace encapsulating above helpers
00291 
00292 // The extricate behaviour monitors the robot's danger zone and issues
00293 // appropriate turn and drive commands to get the robot unstuck, which
00294 // happens when things have gotten too close (thereby triggering the
00295 // robot's emergency stop response).
00296 //
00297 // This behaviour works by assigning a repulsive force to each distance
00298 // reading that lies within the danger zone and an attractive force to
00299 // each reading outside it. The total force vector is then used to
00300 // determine suitable motor commands.
00301 void Extricate::action()
00302 {
00303    DangerZone::Blocks blocks ;
00304    std::auto_ptr<LRFData> lrf ;
00305 
00306    // Copy the danger zone blocks and corresponding LRF data so as to not
00307    // hold the update lock for too long.
00308    UpdateLock::begin_read() ;
00309       bool danger_zone_penetrated = DangerZone::penetrated() ;
00310       if (danger_zone_penetrated) {
00311          blocks.reserve(DangerZone::num_blocks()) ;
00312          std::copy(DangerZone::begin(), DangerZone::end(),
00313                    std::back_inserter(blocks)) ;
00314          lrf.reset(new LRFData(DangerZone::lrf_data())) ;
00315       }
00316       bool stopped = App::robot()->stopped() ;
00317    UpdateLock::end_read() ;
00318 
00319    // If the danger zone has been penetrated, determine the attractive
00320    // and repulsive forces and the corresponding motor commands. In
00321    // restart mode, also check if the robot is actually stopped before
00322    // commencing extrication.
00323    //
00324    // DEVNOTE: To understand the logic of the if statement below, let:
00325    //      D = Boolean variable indicating that danger zone is penetrated
00326    //      M = Boolean variable indicating beh. is configured for restart mode
00327    //      S = Boolean variable indicating robot is stopped
00328    // and  E = Boolean variable indicating extrication should be performed
00329    //
00330    // In normal mode, i.e., when R is false, we only need check D. In
00331    // restart mode, we have to check both D and S. Thus, utilizing the
00332    // symbols of Boolean algebra, we have:
00333    //      E = ~MD + MSD
00334    //
00335    // Simplifying the above, gives us:
00336    //      E = D(~M + MS) = D(~M + S)
00337    Command C ;
00338    Vector A, R, F ;
00339    if (danger_zone_penetrated && (! Params::restart_mode() || stopped))
00340    {
00341       compute_forces force_field =
00342          std::for_each(blocks.begin(), blocks.end(), compute_forces(*lrf)) ;
00343       A = force_field.attractive() ;
00344       R = force_field.repulsive()  ;
00345       F = A - R ;
00346 
00347       const int T = random(TurnArbiter::turn_step(), TurnArbiter::turn_max());
00348       switch (quadrant(direction(F)))
00349       {
00350          case 1:
00351             C.drive =  1 ; C.turn =  T ;
00352             break ;
00353          case 2:
00354             C.drive = -1 ; C.turn = -T ;
00355             break ;
00356          case 3:
00357             C.drive = -1 ; C.turn =  T ;
00358             break ;
00359          case 4:
00360             C.drive =  1 ; C.turn = -T ;
00361             break ;
00362          default: // hunh?!? quadrant() shouldn't return anything outside [1,4]
00363             throw misc_error(LOGIC_ERROR) ;
00364       }
00365 
00366       if (Params::spin_style_steering())
00367          SpinArbiter::instance().vote(base::name,
00368                                       new SpinArbiter::Vote(C.turn)) ;
00369       else
00370       {
00371          SpeedArbiter::instance().vote(base::name,
00372             new SpeedArbiter::Vote(C.drive * Params::extricate_speed(),
00373                                    C.drive * Params::extricate_pwm())) ;
00374          TurnArbiter::instance().vote(base::name,
00375             new TurnArbiter::Vote(turn_vote_centered_at(C.turn))) ;
00376       }
00377 
00378       Metrics::Log log ;
00379       log << std::setw(Metrics::opw()) << std::left << base::name ;
00380       Map* M = App::map() ;
00381       if (M)
00382          log << M->current_pose() ;
00383    }
00384 
00385    // Record stuff for visualization
00386    viz_lock() ;
00387       m_attractive  = A ;
00388       m_repulsive   = R ;
00389       m_total_force = F ;
00390 
00391       m_cmd = C ;
00392    viz_unlock() ;
00393 }
00394 
00395 //--------------------------- VISUALIZATION -----------------------------
00396 
00397 #ifdef INVT_HAVE_LIBGL
00398 
00399 // Helper function to convert the magnitude of a vector into a string,
00400 // prefixing the supplied single character label to the mag string.
00401 static std::string mag(char label, const Vector& v)
00402 {
00403    using namespace std ;
00404 
00405    std::ostringstream v_str ;
00406    v_str << label   << ": "
00407          << setw(8) << fixed << setprecision(1) << magnitude(v) ;
00408    return v_str.str() ;
00409 }
00410 
00411 // The visualization callback
00412 void Extricate::render_me()
00413 {
00414    // Make local copies so that extricate thread isn't held up waiting
00415    // for visualization thread to complete.
00416    viz_lock() ;
00417       Command C = m_cmd ;
00418       Vector  A = m_attractive  ;
00419       Vector  R = m_repulsive   ;
00420       Vector  T = m_total_force ;
00421    viz_unlock() ;
00422 
00423    // Draw principal axes (to make it easier to understand what's going
00424    // on with the force vectors and turn command).
00425    unit_view_volume() ;
00426    glColor3f(1, 1, 1) ;
00427    glBegin(GL_LINES) ;
00428       glVertex2i(0, -1) ;
00429       glVertex2i(0,  1) ;
00430       glVertex2i(-1, 0) ;
00431       glVertex2i( 1, 0) ;
00432    glEnd() ;
00433 
00434    // Render the extrication decision's visualization
00435    if (C.drive == 0 && C.turn == 0)
00436       ; // no extrication took place
00437    else
00438    {
00439       glColor3f(0, 1, 0) ;
00440       draw_vector(normalized(A)) ;
00441 
00442       glColor3f(1, 0, 0) ;
00443       draw_vector(normalized(R)) ;
00444 
00445       glColor3f(0.15f, 0.75f, 0.85f) ;
00446       draw_vector(normalized(T)) ;
00447 
00448       glColor3f(1, 1, 1) ;
00449       draw_vector(Vector(0.75f * C.drive * cos(C.turn), 0.75f * sin(C.turn))) ;
00450    }
00451 
00452    // Label the visualization so that it is easy to tell which behaviour
00453    // is being visualized. Also show the magnitudes of the forces.
00454    restore_view_volume() ;
00455    text_view_volume() ;
00456       glColor3f(0, 1, 1) ;
00457       draw_label(3, 12, "Extricate") ;
00458 
00459       glColor3f(0, 1, 0) ;
00460       draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ;
00461 
00462       glColor3f(1, 0, 0) ;
00463       draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ;
00464 
00465       glColor3f(0.15f, 0.75f, 0.85f) ;
00466       draw_label(3, m_geometry.height -  4, mag('T', T).c_str()) ;
00467    restore_view_volume() ;
00468 }
00469 
00470 #endif
00471 
00472 //----------------------------- CLEAN-UP --------------------------------
00473 
00474 Extricate::~Extricate(){}
00475 
00476 //-----------------------------------------------------------------------
00477 
00478 } // end of namespace encapsulating this file's definitions
00479 
00480 /* So things look consistent in everyone's emacs... */
00481 /* Local Variables: */
00482 /* indent-tabs-mode: nil */
00483 /* End: */
Generated on Sun May 8 08:41:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3