LoOpenPath.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoOpenPath.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::OpenPath 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/LoOpenPath.C $
00039 // $Id: LoOpenPath.C 14306 2010-12-09 00:11:01Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoOpenPath.H"
00046 #include "Robots/LoBot/control/LoSpinArbiter.H"
00047 
00048 #include "Robots/LoBot/LoApp.H"
00049 #include "Robots/LoBot/ui/LoLaserViz.H"
00050 #include "Robots/LoBot/config/LoConfigHelpers.H"
00051 #include "Robots/LoBot/thread/LoUpdateLock.H"
00052 
00053 #include "Robots/LoBot/misc/LoExcept.H"
00054 #include "Robots/LoBot/misc/LoRegistry.H"
00055 #include "Robots/LoBot/misc/singleton.hh"
00056 
00057 #include "Robots/LoBot/util/LoGL.H"
00058 #include "Robots/LoBot/util/LoMath.H"
00059 #include "Robots/LoBot/util/range.hh"
00060 
00061 // OpenGL headers
00062 #ifdef INVT_HAVE_LIBGLU
00063 #include <GL/glu.h>
00064 #endif
00065 
00066 #ifdef INVT_HAVE_LIBGL
00067 #include <GL/gl.h>
00068 #endif
00069 
00070 // Standard C++ headers
00071 #include <algorithm>
00072 #include <map>
00073 #include <functional>
00074 #include <utility>
00075 
00076 // Standard C headers
00077 #include <math.h>
00078 
00079 //----------------------------- NAMESPACE -------------------------------
00080 
00081 namespace lobot {
00082 
00083 //-------------------------- KNOB TWIDDLING -----------------------------
00084 
00085 namespace {
00086 
00087 // Retrieve settings from open_path section of config file
00088 template<typename T>
00089 inline T conf(const std::string& key, const T& default_value)
00090 {
00091    return get_conf<T>(LOBE_OPEN_PATH, key, default_value) ;
00092 }
00093 
00094 // Overload of above function for retrieving ranges
00095 template<typename T>
00096 inline range<T> conf(const std::string& key, const range<T>& default_value)
00097 {
00098    return get_conf<T>(LOBE_OPEN_PATH, key, default_value) ;
00099 }
00100 
00101 /// This inner class encapsulates various parameters that can be used
00102 /// to tweak different aspects of the open path behaviour.
00103 class Params : public singleton<Params> {
00104    /// The width of the robot. This setting specifies the minimum
00105    /// width (in mm) that each open path must be.
00106    float m_path_width ;
00107 
00108    /// We will only consider open paths that are at least the length
00109    /// (in mm) specified by this setting.
00110    float m_min_path_length ;
00111 
00112    /// A derived parameter that keeps track of the angular range about
00113    /// a given direction that must be searched for candidate open
00114    /// paths.
00115    int m_alpha ;
00116 
00117    /// Instead of looking for open paths across the laser range
00118    /// finder's entire FOV, we can restrict this behaviour to search
00119    /// only some subportion of it. This setting specifies the angular
00120    /// range to search for open paths. It is a single number
00121    /// specifying the angular range for each side of zero degrees.
00122    int m_fov ;
00123 
00124    /// Instead of looking for open paths at each angle, we can use
00125    /// this parameter to skip some angles.
00126    int m_step ;
00127 
00128    /// To ensure that the robot doesn't keep turning unnecessarily, we
00129    /// mark some angular range in front of the robot as a dead zone.
00130    /// When the most open path lies in this angular range, the open
00131    /// path behaviour will simply keep driving straight and not affect
00132    /// the steering.
00133    ///
00134    /// To disable this policy, simply specify a range that lies
00135    /// outside the laser range finder's FOV (e.g., 260 to 280). That
00136    /// way, all open paths will lie outside of the dead zone and the
00137    /// robot will always steer towards the most open path  in every
00138    /// iteration of this behaviour.
00139    ///
00140    /// To have the robot never turn towards the most open path, simply
00141    /// use the entire FOV of the laser range finder. Then, since all
00142    /// open paths will lie in the dead zone, the robot will never turn
00143    /// towards the mostopen path. (Note, however, that an easier way
00144    /// to do this is to simply disable the open path behaviour.)
00145    ///
00146    /// This setting expects two integers. The first specifies the
00147    /// minimum of the dead zone's angular range (usually a negative
00148    /// number indicating an angle on the robot's right). The second
00149    /// number is the dead zone's maximum (usually a positive number
00150    /// for a direction on the left of the robot).
00151    range<int> m_dead_zone ;
00152 
00153    /// Usually, steering control is effected using the turn arbiter,
00154    /// which veers the robot in different directions while it moves,
00155    /// i.e., smooth car-like turns. However, the lgmd_extricate_tti
00156    /// behaviour also supports spin-style steering, i.e., momentarily
00157    /// stopping the robot and then turning it cw/ccw in-place. This flag
00158    /// turns on spin-style steering. By default, the behaviour uses the
00159    /// normal car-like steering mode.
00160    bool m_spin_style_steering ;
00161 
00162    /// The number of milliseconds between successive iterations of this
00163    /// behaviour.
00164    ///
00165    /// WARNING: The ability to change a behaviour's update frequency is a
00166    /// very powerful feature whose misuse or abuse can wreak havoc! Be
00167    /// sure to use reasonable values for this setting.
00168    int m_update_delay ;
00169 
00170    /// The location and size (within the Robolocust main window) of the
00171    /// goal behaviour's visualization.
00172    typedef Drawable::Geometry Geom ; // just for properly aligning accessors
00173    Geom m_geometry ;
00174 
00175    /// Private constructor because this is a singleton.
00176    Params() ;
00177 
00178    // Boilerplate code to make generic singleton design pattern work
00179    friend class singleton<Params> ;
00180 
00181 public:
00182    /// Accessing the various parameters.
00183    //@{
00184    static float path_width()      {return instance().m_path_width ;}
00185    static float min_path_length() {return instance().m_min_path_length ;}
00186    static int   alpha()           {return instance().m_alpha ;}
00187    static int   fov()             {return instance().m_fov   ;}
00188    static int   step()            {return instance().m_step  ;}
00189    static range<int> dead_zone()  {return instance().m_dead_zone ;}
00190    static bool  spin_style_steering(){return instance().m_spin_style_steering;}
00191    static int   update_delay()       {return instance().m_update_delay       ;}
00192    static Geom  geometry()           {return instance().m_geometry           ;}
00193    //@}
00194 } ;
00195 
00196 // Parameters initialization
00197 Params::Params()
00198    : m_path_width(clamp(conf("path_width", 175.0f), 150.0f, 500.0f)),
00199      m_min_path_length(clamp(conf("min_path_length", 250.0f),
00200                              100.0f, 2500.0f)),
00201      m_alpha(round(atan(m_path_width/2/m_min_path_length))),
00202      m_fov(clamp(conf("fov", 90), 0, 180)),
00203      m_step(clamp(conf("step", 10), 1, 45)),
00204      m_dead_zone(conf("dead_zone", range<int>(-20, 20))),
00205      m_spin_style_steering(conf("spin_style_steering", false)),
00206      m_update_delay(clamp(conf("update_delay", 600), 250, 10000)),
00207      m_geometry(conf<std::string>("geometry", "0 0 10 10"))
00208 {}
00209 
00210 } // end of local anonymous namespace encapsulating above helpers
00211 
00212 //-------------------------- INITIALIZATION -----------------------------
00213 
00214 OpenPath::OpenPath()
00215    : base(Params::update_delay(), LOBE_OPEN_PATH, Params::geometry())
00216 {
00217    start(LOBE_OPEN_PATH) ;
00218 }
00219 
00220 void OpenPath::pre_run()
00221 {
00222    if (! App::lrf())
00223       throw behavior_error(LASER_RANGE_FINDER_MISSING) ;
00224    if (! App::robot())
00225       throw behavior_error(MOTOR_SYSTEM_MISSING) ;
00226 
00227    Drawable* v = App::laser_viz() ;
00228    if (v)
00229       v->add_hook(RenderHook(render_paths,
00230                              reinterpret_cast<unsigned long>(this))) ;
00231 }
00232 
00233 OpenPath::PathInfo::PathInfo(float length, float width)
00234    : m_info(length, width, length * width)
00235 {
00236    if (length < 0 || width < 0)
00237       m_info.third = -1 ;
00238 }
00239 
00240 //---------------------- THE BEHAVIOUR'S ACTION -------------------------
00241 
00242 void OpenPath::action()
00243 {
00244    // Make local copy of LRF data to avoid holding update lock for too
00245    // long...
00246    UpdateLock::begin_read() ;
00247       LRFData lrf(App::lrf()) ;
00248    UpdateLock::end_read() ;
00249 
00250    // The list of candidate open paths is stored as a mapping between LRF
00251    // measurement angles and the corresponding path lengths along those
00252    // directions.
00253    Paths paths ;
00254 
00255    // Find the candidate open paths in each direction supported by LRF
00256    int fov = std::max(lrf.min_angle() + Params::alpha(), -Params::fov()) ;
00257    for (int angle = 0; angle >= fov; angle -= Params::step())
00258    {
00259       PathInfo p = open_path(angle, lrf) ;
00260       if (p.area() > 0)
00261          paths.insert(std::make_pair(angle, p)) ;
00262    }
00263 
00264    fov = std::min(lrf.max_angle() - Params::alpha(), Params::fov()) ;
00265    for (int angle = Params::step(); angle <= fov; angle += Params::step())
00266    {
00267       PathInfo p = open_path(angle, lrf) ;
00268       if (p.area() > 0)
00269          paths.insert(std::make_pair(angle, p)) ;
00270    }
00271    viz_lock() ;
00272       m_paths = paths ; // record raw paths for visualization
00273    viz_unlock() ;
00274    //dump(paths, "OpenPath::action", "paths(raw)") ;
00275 
00276    // Find most open path (i.e., path with max length)
00277    Paths::const_iterator max =
00278       std::max_element(paths.begin(), paths.end(), map_value_compare(paths)) ;
00279    //LERROR("max reading = [%4d %8.1f]", max->first, max->second) ;
00280 
00281    // Steer towards most open path only if it is significantly to the
00282    // left or right of the robot. Otherwise, just keep going straight.
00283    if (! Params::dead_zone().in(max->first))
00284    {
00285       if (Params::spin_style_steering())
00286          SpinArbiter::instance().vote(base::name,
00287                                       new SpinArbiter::Vote(max->first)) ;
00288       else
00289       {
00290          const int T = TurnArbiter::turn_max() ;
00291          TurnArbiter::Vote* V = new TurnArbiter::Vote(
00292             turn_vote_centered_at(clamp(max->first, -T, T))) ;
00293          //V->dump("OpenPath::action") ;
00294 
00295          // Record the above vote for visualization before turning it over
00296          // to the turn arbiter. Otherwise, it is possible the vote might
00297          // get deleted by the turn arbiter, which would cause a segfault
00298          // here when this thread attempts to dereference the pointer.
00299          viz_lock() ;
00300             m_vote = *V ;
00301          viz_unlock() ;
00302 
00303          TurnArbiter::instance().vote(base::name, V) ;
00304       }
00305    }
00306 }
00307 
00308 // This function returns an open path (if available) at the specified
00309 // direction.
00310 OpenPath::PathInfo OpenPath::open_path(int theta, const LRFData& lrf) const
00311 {
00312    const float w = Params::path_width()/2 ;
00313 
00314    float L = lrf.max_distance() * 2 ;
00315    for (int x = theta - Params::alpha(); x <= theta + Params::alpha(); ++x)
00316    {
00317       int D = lrf[x] ;
00318       if (D < 0)
00319          continue ;
00320       if (D * sin(abs(theta - x)) <= w) {
00321          float d = D * cos(abs(theta - x)) ;
00322          if (d < L)
00323             L = d ;
00324       }
00325    }
00326 
00327    if (L > lrf.max_distance() || L < Params::min_path_length())
00328       return PathInfo(-1, -1) ;
00329    return PathInfo(L, Params::path_width()) ;
00330 }
00331 //--------------------------- VISUALIZATION -----------------------------
00332 
00333 #ifdef INVT_HAVE_LIBGL
00334 
00335 void OpenPath::render_me()
00336 {
00337    // Make local copy so that open path behaviour's thread isn't held up
00338    // waiting for visualization thread to complete.
00339    viz_lock() ;
00340       TurnArbiter::Vote V = m_vote ;
00341    viz_unlock() ;
00342 
00343    // Render the votes visualization
00344    unit_view_volume() ;
00345    glBegin(GL_LINES) ;
00346       for (TurnArbiter::Vote::iterator it = V.begin(); it; ++it)
00347       {
00348          float vote = it.value() ;
00349          float direction = it.direction() ;
00350          if (vote < 0) // voted against this direction
00351          {
00352             glColor3f(1, 0, 0) ;
00353             glVertex2i(0, 0) ;
00354             glVertex2f(-vote * cos(direction), -vote * sin(direction)) ;
00355          }
00356          else if (vote > 0) // voted for this direction
00357          {
00358             glColor3f(0, 1, 0) ;
00359             glVertex2i(0, 0) ;
00360             glVertex2f(vote * cos(direction), vote * sin(direction)) ;
00361          }
00362       }
00363    glEnd() ;
00364 
00365    // Label the visualization so that it is easy to tell which behaviour
00366    // is being visualized.
00367    restore_view_volume() ;
00368    text_view_volume() ;
00369       glColor3f(0, 1, 1) ;
00370       draw_label(3, 12, "Open Path") ;
00371    restore_view_volume() ;
00372 }
00373 
00374 void OpenPath::render_paths(unsigned long client_data)
00375 {
00376    (reinterpret_cast<OpenPath*>(client_data))->render_paths() ;
00377 }
00378 
00379 void OpenPath::render_paths()
00380 {
00381    // Make local copy so that open path behaviour's thread isn't held up
00382    // waiting for visualization thread to complete.
00383    viz_lock() ;
00384       Paths P = m_paths ;
00385    viz_unlock() ;
00386 
00387    // Render the open paths, overlaying them on the LRF visualization
00388    glPushMatrix() ;
00389    glRotatef(get_conf("laser_viz", "lrf_direction", 90.0f), 0, 0, 1) ;
00390    glBegin(GL_LINES) ;
00391       glColor3f(1, 1, 1) ;
00392       for (Paths::const_iterator it = P.begin(); it != P.end(); ++it)
00393       {
00394          glVertex2i(0, 0) ;
00395          glVertex2f(it->second.length() * cos(it->first),
00396                     it->second.length() * sin(it->first));
00397       }
00398    glEnd() ;
00399    glPopMatrix() ;
00400 }
00401 
00402 #endif
00403 
00404 //----------------------------- CLEAN-UP --------------------------------
00405 
00406 OpenPath::~OpenPath(){}
00407 
00408 //-----------------------------------------------------------------------
00409 
00410 } // end of namespace encapsulating this file's definitions
00411 
00412 /* So things look consistent in everyone's emacs... */
00413 /* Local Variables: */
00414 /* indent-tabs-mode: nil */
00415 /* End: */
Generated on Sun May 8 08:05:44 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3