LoTurnArbiter.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoTurnArbiter.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::TurnArbiter 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/LoTurnArbiter.C $
00039 // $Id: LoTurnArbiter.C 13521 2010-06-06 14:23:03Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoTurnArbiter.H"
00046 
00047 #include "Robots/LoBot/config/LoConfigHelpers.H"
00048 #include "Robots/LoBot/thread/LoUpdateLock.H"
00049 
00050 #include "Robots/LoBot/misc/LoExcept.H"
00051 #include "Robots/LoBot/util/LoGL.H"
00052 #include "Robots/LoBot/util/LoDebug.H"
00053 #include "Robots/LoBot/util/LoSTL.H"
00054 
00055 // OpenGL headers
00056 #ifdef INVT_HAVE_LIBGL
00057 #include <GL/gl.h>
00058 #endif
00059 
00060 // Standard C++ headers
00061 #include <algorithm>
00062 #include <functional>
00063 #include <iterator>
00064 #include <utility>
00065 
00066 //----------------------------- NAMESPACE -------------------------------
00067 
00068 namespace lobot {
00069 
00070 //--------------------------- LOCAL HELPERS -----------------------------
00071 
00072 // Retrieve settings from turn_arbiter section of config file
00073 template<typename T>
00074 static inline T conf(const std::string& key, const T& default_value)
00075 {
00076    return get_conf<T>("turn_arbiter", key, default_value) ;
00077 }
00078 
00079 //-------------------------- INITIALIZATION -----------------------------
00080 
00081 TurnArbiter::TurnArbiter()
00082    : Arbiter(clamp(conf("update_delay", 500), 1, 1000),
00083              "turn_arbiter", conf<std::string>("geometry", "480 420 140 140"))
00084 {
00085    start("turn_arbiter") ;
00086 }
00087 
00088 float TurnArbiter::get_configured_priority(const std::string& behaviour) const
00089 {
00090    return abs(get_conf(behaviour, "turn_priority", 0.0f)) ;
00091 }
00092 
00093 //-------------------------- MOTOR COMMANDS -----------------------------
00094 
00095 // Helper to scale all the vote values in a Vote by the given weight
00096 static TurnArbiter::Vote operator*(TurnArbiter::Vote V, float weight)
00097 {
00098    std::transform(V.begin(), V.end(), V.begin(),
00099                   std::bind2nd(std::multiplies<float>(), weight)) ;
00100    return V ;
00101 }
00102 
00103 // To issue an appropriate motor command, the turn arbiter must first
00104 // combine the votes from all the behaviours, weighting each vote by its
00105 // corresponding behaviour's priority. It then smooths the resulting vote
00106 // using a Gaussian and picks the motor command with the maximum vote
00107 // value.
00108 void TurnArbiter::motor_cmd(const Arbiter::Votes& votes, Robot* robot)
00109 {
00110    Vote result ;
00111 
00112    // First, compute weighted sum of all votes
00113    Arbiter::Votes::const_iterator it = votes.begin() ;
00114    for (; it != votes.end(); ++it)
00115       result +=
00116          (*dynamic_cast<Vote*>((*it)->vote)) * priority((*it)->behavior_name) ;
00117    //result.dump("TurnArbiter::motor_cmd before smooting") ;
00118 
00119    // Then, Gaussian smooth the weighted sum
00120    typedef Vote::VoteMap::value_type DVP ; // direction-vote pair
00121    std::vector<DVP> V(result.m_votes.begin(), result.m_votes.end()) ;
00122 
00123    const int   N = V.size() ;
00124    const int   W = clamp(Params::smoothing_width(), 1, N) ;
00125    const float S = 2 * Params::sigma() * Params::sigma() ;
00126    const float s = 1/(2.506628f /* sqrt(2*pi) */ * Params::sigma()) ;
00127 
00128    for  (int i = 0; i < N; ++i)
00129    {
00130       float v = 0 ;
00131       for (int j = i - W; j <= i + W; ++j) {
00132          if (j < 0 || j >= N)
00133             continue ;
00134          float d = V[j].first - V[i].first ;
00135          v += V[j].second * exp(-(d*d)/S) * s ;
00136       }
00137       result[V[i].first] = v ;
00138    }
00139    //result.dump("TurnArbiter::motor_cmd after smoothing") ;
00140 
00141    result.normalize() ;
00142    //result.dump("TurnArbiter::motor_cmd after normalization") ;
00143 
00144    viz_lock() ;
00145       m_vote = result ; // record most recent vote for visualization
00146       //m_vote.dump("TurnArbiter::motor_cmd") ;
00147    viz_unlock() ;
00148 
00149    // Finally, pick the command with the maximum votes
00150    Vote::iterator max = std::max_element(result.begin(), result.end()) ;
00151    //LERROR("max vote %g for direction: %d", max.value(), max.direction()) ;
00152 
00153    UpdateLock::begin_write() ;
00154       robot->turn(max.direction()) ;
00155    UpdateLock::end_write() ;
00156 }
00157 
00158 //------------------------------- VOTES ---------------------------------
00159 
00160 // Vote initialization: neutral for all directions
00161 TurnArbiter::Vote::Vote()
00162 {
00163    const int M = turn_max() ;
00164    const int S = turn_step() ;
00165    m_votes[0]  = 0 ;
00166    for (int direction = +S; direction <= +M; direction += S)
00167       m_votes[direction] = 0 ;
00168    for (int direction = -S; direction >= -M; direction -= S)
00169       m_votes[direction] = 0 ;
00170 }
00171 
00172 // Return the supported turn directions a behaviour can vote on
00173 std::vector<int> TurnArbiter::Vote::get_directions() const
00174 {
00175    std::vector<int> V ;
00176    V.reserve(m_votes.size()) ;
00177    std::transform(m_votes.begin(), m_votes.end(), std::back_inserter(V),
00178                   get_first<VoteMap::value_type>) ;
00179    return V ;
00180 }
00181 
00182 class abs_diff {
00183    int direction ;
00184 public:
00185    abs_diff(int d) : direction(d) {}
00186    bool operator()(const std::pair<int, float>& a,
00187                    const std::pair<int, float>& b) const {
00188       return abs(a.first - direction) < abs(b.first - direction) ;
00189    }
00190 } ;
00191 
00192 // Operator to access vote value corresponding to specified turn
00193 // direction.
00194 TurnArbiter::Vote::VoteMap::mapped_type&
00195 TurnArbiter::Vote::
00196 operator[](int direction)
00197 {
00198    VoteMap::iterator it = m_votes.find(direction) ;
00199    if (it == m_votes.end())
00200       //throw arbiter_error(UNSUPPORTED_TURN_DIRECTION) ;
00201       it = std::min_element(m_votes.begin(), m_votes.end(),
00202                             abs_diff(direction)) ;
00203    return it->second ;
00204 }
00205 
00206 // Vote clean-up
00207 TurnArbiter::Vote::~Vote(){}
00208 
00209 // Vote iterator start constructor
00210 TurnArbiter::Vote::iterator::iterator(const TurnArbiter::Vote& V)
00211    : m_vote(const_cast<TurnArbiter::Vote&>(V)),
00212      m_iterator(m_vote.m_votes.begin())
00213 {}
00214 
00215 // Vote iterator end constructor
00216 TurnArbiter::Vote::iterator::iterator(const TurnArbiter::Vote& V, bool)
00217    : m_vote(const_cast<TurnArbiter::Vote&>(V)),
00218      m_iterator(m_vote.m_votes.end())
00219 {}
00220 
00221 // Vote iterator copy constructor
00222 TurnArbiter::Vote::iterator::iterator(const TurnArbiter::Vote::iterator& it)
00223    : m_vote(it.m_vote),
00224      m_iterator(it.m_iterator)
00225 {}
00226 
00227 // Vote iterator assignment operator
00228 TurnArbiter::Vote::iterator&
00229 TurnArbiter::Vote::iterator::operator=(const TurnArbiter::Vote::iterator& it)
00230 {
00231    if (& it != this) {
00232       m_vote     = it.m_vote ;
00233       m_iterator = it.m_iterator ;
00234    }
00235    return *this ;
00236 }
00237 
00238 // Vote iterator destructor
00239 TurnArbiter::Vote::iterator::~iterator(){}
00240 
00241 // Adding votes
00242 TurnArbiter::Vote& TurnArbiter::Vote::operator+=(const TurnArbiter::Vote& V)
00243 {
00244    std::transform(V.begin(), V.end(), begin(), begin(), std::plus<float>()) ;
00245    return *this ;
00246 }
00247 
00248 /*
00249   The following function object is meant to be used with the STL
00250   transform algorithm and scales votes to lie in the range [-1, +1]. It
00251   needs to know the min and max vote values that occur in a vote and then
00252   simply performs a linear interpolation to compute the scaled vote value
00253   like so:
00254 
00255                             v - m     s - (-1)
00256                             -----  =  --------
00257                             M - m     1 - (-1)
00258 
00259                             s + 1     v - m
00260                      ===>   -----  =  -----
00261                               2       M - m
00262 
00263                      ===>       s  =  2(v - m)/(M - m) - 1
00264 
00265    where m = min vote value
00266          M = max vote value
00267          v = unscaled vote value
00268          s = scaled vote value
00269 */
00270 class scale_vote {
00271    float min, max ;
00272 public:
00273    scale_vote(float m, float M) : min(m), max(M) {}
00274    float operator()(const float& v) const {
00275       return 2 * (v - min)/(max - min) - 1 ;
00276    }
00277 } ;
00278 
00279 // Normalizing votes so that all directions' votes are in the [-1, +1]
00280 // range.
00281 void TurnArbiter::Vote::normalize()
00282 {
00283    float min = *std::min_element(begin(), end()) ;
00284    float max = *std::max_element(begin(), end()) ;
00285    normalize(min, max) ;
00286 }
00287 
00288 // Normalizing votes so that all directions' votes are in the [-1, +1]
00289 // range.
00290 void TurnArbiter::Vote::normalize(float min, float max)
00291 {
00292    std::transform(begin(), end(), begin(), scale_vote(min, max)) ;
00293 }
00294 
00295 // Debug support: dump a vote's direction-value pairs
00296 void TurnArbiter::Vote::dump(const std::string& caller) const
00297 {
00298    lobot::dump(m_votes, caller, "m_votes") ;
00299 }
00300 
00301 /*
00302    The following helper function returns a vote for driving in the
00303    specified direction with votes for the other directions falling
00304    linearly away from +1.
00305 
00306    To illustrate how this function works, let us say that the supported
00307    steering directions go from -6 degrees (on the right) to +6 degrees
00308    (on the left) in steps of 3 degrees. That is, turn_max is 6 and
00309    turn_step is 3 and, therefore, the supported steering directions are
00310    6, 3, 0, -3, -6.
00311 
00312    If we would like to make a medium left turn, i.e., turn direction is
00313    3, then the votes returned by this function will be +1 for 3 and less
00314    than that for the other directions. The amount by which the other
00315    directions' votes will be less depends on the turn_max and turn_step
00316    parameters. In this example, the vote step is 3/6 (step/max) or 0.5.
00317    Thus, the steering direction 3 will get a vote of +1; 6 and 0 will get
00318    1 - 0.5 = 0.5; and -3 will get 1 - 2*0.5 = 0; and -6 will be assigned
00319    1 - 3*.5 = -0.5. That is, the votes will look like so:
00320 
00321                            6   3   0   -3   -6
00322                           0.5  1  0.5   0  -0.5
00323 
00324    As we can see, the votes for the directions other than the one at
00325    which they are to be "centered", viz., C, fall away according to the
00326    angular distance between C and that particular direction. The vote for
00327    the direction C is +1 and for the others is something less than 1.
00328    That is, the vote for some direction d is 1 - something. This
00329    something is a function of the angular distance between d and C, i.e.,
00330    d - C.
00331 
00332    Now, the vote step for consecutive directions is simply s/T, where s
00333    is the turn step and T the turn max. In the above example, the vote
00334    step is 3/6 = 0.5. What this means is that if the vote for some
00335    direction is pegged at v, then the vote for its two surrounding
00336    directions will be v +/- 0.5.
00337 
00338    Therefore, if we are d - C degrees away from some turn angle C, we can
00339    divide d - C by the turn step s to get the number of angular steps
00340    between d and C. We then multiply this number by the vote step to get
00341    the total number of vote steps between the directions d and C.
00342    Finally, we subtract that from 1 (the vote for C) to get the vote for
00343    the direction d.
00344 
00345    This gives us the following expression for the vote for a steering
00346    angle d:
00347 
00348                               d - C    s
00349               vote[d] =  1 -  ----- x ---  = 1 - (d - C)/T
00350                                 s      T
00351 
00352    But notice that if we were to use the above expression as-is, the
00353    votes for directions whose magnitudes are less than C will be greater
00354    than 1. For example, the vote for direction 0 will be 1 - (0 - 3)/6 =
00355    1 - (-3)/6 = 1 + 0.5 = 1.5!
00356 
00357    The problem is that we are really interested in the magnitude of the
00358    difference between d and C and not its sign. In other words, if we
00359    think of (d - C) as an error, then we only want the amount of error
00360    and not the direction in which the error goes. This gives us:
00361 
00362                        vote[d] = 1 - abs(d - C)/T
00363 */
00364 TurnArbiter::Vote turn_vote_centered_at(float C)
00365 {
00366    const int T = TurnArbiter::turn_max() ;
00367 
00368    TurnArbiter::Vote V ;
00369    TurnArbiter::Vote::iterator it = V.begin() ;
00370    for (; it != V.end(); ++it)
00371       *it = clamp(1 - abs(it.direction() - C)/T, -1.0f, +1.0f) ;
00372    return V ;
00373 }
00374 
00375 //--------------------------- VISUALIZATION -----------------------------
00376 
00377 #ifdef INVT_HAVE_LIBGL
00378 
00379 void TurnArbiter::render_me()
00380 {
00381    // Copy most recent vote so that turn arbiter thread is not
00382    // unnecessarily held up while the rendering takes place.
00383    viz_lock() ;
00384       Vote V = m_vote ;
00385    viz_unlock() ;
00386 
00387    // Render the votes visualization
00388    unit_view_volume() ;
00389    glBegin(GL_LINES) ;
00390       for (Vote::iterator it = V.begin(); it; ++it)
00391       {
00392          float vote = it.value() ;
00393          float direction = it.direction() ;
00394          if (vote < 0) // voted against this direction
00395          {
00396             glColor3f(1, 0, 0) ;
00397             glVertex2i(0, 0) ;
00398             glVertex2f(-vote * cos(direction), -vote * sin(direction)) ;
00399          }
00400          else if (vote > 0) // voted for this direction
00401          {
00402             glColor3f(0, 1, 0) ;
00403             glVertex2i(0, 0) ;
00404             glVertex2f(vote * cos(direction), vote * sin(direction)) ;
00405          }
00406       }
00407 
00408       Vote::iterator max = std::max_element(V.begin(), V.end()) ;
00409       if (max.value() > 0) // this is the direction that won
00410       {
00411          glColor3f(1, 1, 1) ;
00412          glLineWidth(2) ;
00413          glVertex2i(0, 0) ;
00414          glVertex2f(cos(max.direction()), sin(max.direction())) ;
00415       }
00416    glEnd() ;
00417 
00418    // Label the visualization so that it is easy to tell what is being
00419    // visualized.
00420    restore_view_volume() ;
00421    text_view_volume() ;
00422    glColor3f(0, 1, 1) ;
00423    draw_label(3, 12, "Turn Arbiter") ;
00424 
00425    restore_view_volume() ;
00426 }
00427 
00428 #endif
00429 
00430 //----------------------- TURN ARBITER CLEAN-UP -------------------------
00431 
00432 TurnArbiter::~TurnArbiter(){}
00433 
00434 //-------------------------- KNOB TWIDDLING -----------------------------
00435 
00436 // Parameters initialization
00437 TurnArbiter::Params::Params()
00438    : m_turn_max (clamp(conf("turn_max", 20), 5, 60)),
00439      m_turn_step(clamp(conf("turn_step", 1), 1, m_turn_max/2)),
00440      m_smoothing_width(conf("smoothing_window_width", 7)),
00441      m_sigma(clamp(conf("smoothing_sigma", 1.0f), 0.5f, m_turn_max/2.0f))
00442 {}
00443 
00444 // Parameters clean-up
00445 TurnArbiter::Params::~Params(){}
00446 
00447 //-----------------------------------------------------------------------
00448 
00449 } // end of namespace encapsulating this file's definitions
00450 
00451 /* So things look consistent in everyone's emacs... */
00452 /* Local Variables: */
00453 /* indent-tabs-mode: nil */
00454 /* End: */
Generated on Sun May 8 08:41:27 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3