LoGoal.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/control/LoGoal.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::Goal 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/LoGoal.C $
00039 // $Id: LoGoal.C 13812 2010-08-21 04:04:10Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/control/LoGoal.H"
00046 #include "Robots/LoBot/control/LoMetrics.H"
00047 #include "Robots/LoBot/control/LoTurnArbiter.H"
00048 #include "Robots/LoBot/control/LoSpinArbiter.H"
00049 
00050 #include "Robots/LoBot/LoApp.H"
00051 #include "Robots/LoBot/slam/LoMap.H"
00052 #include "Robots/LoBot/slam/LoSlamParams.H"
00053 
00054 #include "Robots/LoBot/config/LoConfigHelpers.H"
00055 
00056 #include "Robots/LoBot/thread/LoShutdown.H"
00057 #include "Robots/LoBot/thread/LoUpdateLock.H"
00058 #include "Robots/LoBot/thread/LoPause.H"
00059 
00060 #include "Robots/LoBot/misc/LoExcept.H"
00061 #include "Robots/LoBot/misc/LoRegistry.H"
00062 #include "Robots/LoBot/misc/singleton.hh"
00063 
00064 #include "Robots/LoBot/util/LoGL.H"
00065 #include "Robots/LoBot/util/LoMath.H"
00066 
00067 // OpenGL headers
00068 #ifdef INVT_HAVE_LIBGLU
00069 #include <GL/glu.h>
00070 #endif
00071 
00072 #ifdef INVT_HAVE_LIBGL
00073 #include <GL/gl.h>
00074 #endif
00075 
00076 // Standard C++ headers
00077 #include <iomanip>
00078 #include <sstream>
00079 #include <numeric>
00080 #include <algorithm>
00081 #include <functional>
00082 #include <iterator>
00083 #include <utility>
00084 
00085 //----------------------------- NAMESPACE -------------------------------
00086 
00087 namespace lobot {
00088 
00089 //-------------------------- KNOB TWIDDLING -----------------------------
00090 
00091 namespace {
00092 
00093 // Retrieve settings from goal section of config file
00094 template<typename T>
00095 inline T conf(const std::string& key, const T& default_value)
00096 {
00097    return get_conf<T>(LOBE_GOAL, key, default_value) ;
00098 }
00099 
00100 // Overload for retrieving pairs
00101 template<typename T>
00102 inline std::pair<T, T>
00103 conf(const std::string& key, const std::pair<T, T>& default_value)
00104 {
00105    return get_conf<T>(LOBE_GOAL, key, default_value) ;
00106 }
00107 
00108 /// This local class encapsulates various parameters that can be used to
00109 /// tweak different aspects of the goal behaviour.
00110 class Params : public singleton<Params> {
00111    /// This behaviour can be configured to actively steer the robot to
00112    /// each goal or to simply monitor the robot's current location to
00113    /// check if the robot is at a goal or not. When set, this flag will
00114    /// make the goal behaviour actively seek each goal in its list. By
00115    /// default, this flag is off, i.e., the behaviour only monitors the
00116    /// robot's location without affecting its steering in any way.
00117    bool m_seek_mode ;
00118 
00119    /// What should we do when we reach a goal? The options are:
00120    ///
00121    ///    - continue on to the next goal
00122    ///    - pause robot and wait for user to start it moving again
00123    ///
00124    /// This setting specifies which of the above two actions to use. By
00125    /// default, the goal behaviour implements the "continue" action,
00126    /// i.e., when the robot reaches a goal, it simply carries on to the
00127    /// next one. However, by setting this flag, the behaviour will pause
00128    /// the robot and wait for the user to explicitly signal resumption of
00129    /// normal operations.
00130    bool m_pause ;
00131 
00132    /// By default, when the final goal in the goal list is reached, the
00133    /// goal behaviour will maintain that goal. Thus, if another behaviour
00134    /// causes the robot to move away from the final goal in the goal
00135    /// list, the goal behaviour will redirect the robot back to that
00136    /// goal.
00137    ///
00138    /// However, by setting this flag, we can have the goal behaviour loop
00139    /// back to the first goal in the list and start over.
00140    bool m_loop ;
00141 
00142    /// In loop mode, instead of starting over at the first goal, we can
00143    /// have the goal behaviour backtrack over the goal list, i.e., seek
00144    /// each of the goals in reverse. This flag enables backtracking. It
00145    /// is off by default.
00146    bool m_backtrack ;
00147 
00148    /// When configured to actively steer the robot towards goals, the
00149    /// goal behaviour directs the robot towards the current goal by
00150    /// implementing the VFF method described by Borenstein and Koren (see
00151    /// "Real-time Obstacle Avoidance for Fast Mobile Robots," IEEE
00152    /// Transactions on Systems, Man, and Cybernetics 19(5):1179--1187,
00153    /// Sep/Oct 1989).
00154    ///
00155    /// The following setting specifies the size of the active window
00156    /// within the certainty grid (i.e., occupancy map). This size is
00157    /// specified in terms of the number of cells occupied by the active
00158    /// window.
00159    ///
00160    /// NOTE: Actually, this setting specifies half of the active window's
00161    /// size. That is, internally, this number is doubled.
00162    int m_vff_window_size ;
00163 
00164    /// The VFF method mentioned above computes a repulsive force vector
00165    /// for each cell within the active window and an attractive force
00166    /// vector for the goal. The sum of all the repulsive forces and the
00167    /// single attractive force result in a final steering vector.
00168    ///
00169    /// This setting specifies the force constants to use for the
00170    /// individual force vectors. It should be a pair of numbers. The
00171    /// first number is the repulsive force constant and the second one is
00172    /// the attractive force constant.
00173    std::pair<float, float> m_vff_forces ;
00174 
00175    /// Usually, steering control is effected using the turn arbiter,
00176    /// which veers the robot in different directions while it moves,
00177    /// i.e., smooth car-like turns. However, the lgmd_extricate_tti
00178    /// behaviour also supports spin-style steering, i.e., momentarily
00179    /// stopping the robot and then turning it cw/ccw in-place. This flag
00180    /// turns on spin-style steering. By default, the behaviour uses the
00181    /// normal car-like steering mode.
00182    bool m_spin_style_steering ;
00183 
00184    /// The number of milliseconds between successive iterations of this
00185    /// behaviour.
00186    ///
00187    /// WARNING: The ability to change a behaviour's update frequency is a
00188    /// very powerful feature whose misuse or abuse can wreak havoc! Be
00189    /// sure to use reasonable values for this setting.
00190    int m_update_delay ;
00191 
00192    /// The location and size (within the Robolocust main window) of the
00193    /// goal behaviour's visualization.
00194    typedef Drawable::Geometry Geom ; // just for properly aligning accessors
00195    Drawable::Geometry m_geometry ;
00196 
00197    /// In addition to rendering its steering votes, the goal behaviour
00198    /// can also render the goal list on the Robolocust map. This flag
00199    /// turns on goal list rendering. It is off by default.
00200    bool m_render_goals ;
00201 
00202    /// Private constructor because this is a singleton.
00203    Params() ;
00204    friend class singleton<Params> ;
00205 
00206 public:
00207    /// Accessing the various parameters
00208    //@{
00209    static bool  seek_mode()    {return instance().m_seek_mode    ;}
00210    static bool  pause()        {return instance().m_pause        ;}
00211    static bool  loop()         {return instance().m_loop         ;}
00212    static bool  backtrack()    {return instance().m_backtrack    ;}
00213    static bool  render_goals() {return instance().m_render_goals ;}
00214    static bool  spin_style_steering(){return instance().m_spin_style_steering;}
00215    static int   update_delay()       {return instance().m_update_delay       ;}
00216    static Geom  geometry()           {return instance().m_geometry           ;}
00217    static int   vff_window_size()    {return instance().m_vff_window_size    ;}
00218    static float vff_rep_force()      {return instance().m_vff_forces.first   ;}
00219    static float vff_att_force()      {return instance().m_vff_forces.second  ;}
00220    //@}
00221 } ;
00222 
00223 // Parameter initialization
00224 Params::Params()
00225    : m_seek_mode(conf("actively_seek_goals", false)),
00226      m_pause(conf("pause_at_goal", false)),
00227      m_loop(conf("loop", false)),
00228      m_backtrack(conf("backtrack", false)),
00229      m_vff_forces(conf("vff_forces", std::make_pair(100.0f, 1000.0f))),
00230      m_spin_style_steering(conf("spin_style_steering", false)),
00231      m_update_delay(clamp(conf("update_delay", 1750), 500, 10000)),
00232      m_geometry(conf<std::string>("geometry", "0 0 10 10")),
00233      m_render_goals(conf("render_goals", false))
00234 {
00235    const float R = clamp(robot_conf("size", 400), 150, 1000) ;
00236    const float s = SlamParams::map_cell_size() ;
00237    const int   n = round(R/s)/2 ; // num cells occupied by half robot
00238    m_vff_window_size = clamp(conf("vff_window_size", 3*n/2), n + 1, 4 * n) ;
00239 
00240    m_vff_forces.first  = clamp(m_vff_forces.first,  1.0f, 1e6f) ;
00241    m_vff_forces.second = clamp(m_vff_forces.second, 1.0f, 1e6f) ;
00242 }
00243 
00244 } // end of local anonymous namespace encapsulating above helpers
00245 
00246 //-------------------------- INITIALIZATION -----------------------------
00247 
00248 Goal::Goal()
00249    : base(Params::update_delay(), LOBE_GOAL, Params::geometry()),
00250      m_goal(-1), m_turn_dir(0)
00251 {
00252    start(LOBE_GOAL) ;
00253 }
00254 
00255 Goal::Target::Target(float L, float R, float B, float T)
00256    : m_left(L), m_right(R), m_bottom(B), m_top(T),
00257      m_center_x((L + R)/2), m_center_y((B + T)/2)
00258 {}
00259 
00260 void Goal::pre_run()
00261 {
00262    if (! App::robot())
00263       throw behavior_error(MOTOR_SYSTEM_MISSING) ;
00264 
00265    Map* map = App::map() ;
00266    if (! map)
00267       throw behavior_error(MAPPING_DISABLED) ;
00268 
00269    // Read in goal list
00270    float L, R, B, T ;
00271    SlamParams::map_extents(&L, &R, &B, &T) ;
00272    std::istringstream goals(conf<std::string>("goals", "")) ;
00273    for(;;)
00274    {
00275       float l, r, b, t ;
00276       goals >> l >> r >> b >> t ;
00277       if (! goals)
00278          break ;
00279       m_goals.push_back(Target(clamp(l, L, R), clamp(r, l, R),
00280                                clamp(b, B, T), clamp(t, b, T))) ;
00281    }
00282    if (m_goals.empty())
00283       throw behavior_error(NO_GOALS) ;
00284 
00285    // Start off seeking the first goal
00286    m_goal = 0 ;
00287 
00288    // Easy way to implement backtracking: simply append the goals in
00289    // reverse to the goal list.
00290    if (Params::loop() && Params::backtrack())
00291       std::copy(m_goals.rbegin() + 1, m_goals.rend() - 1,
00292                 std::back_inserter(m_goals)) ;
00293 
00294    // Init VFF mask
00295    const int   W = Params::vff_window_size() ;
00296    const float S = SlamParams::map_cell_size() ;
00297    const float F = -Params::vff_rep_force() ;
00298 
00299    int x, y ;
00300    float dx, dy ;
00301    m_vff_mask.reserve(sqr(W * 2 + 1)) ;
00302    for (y =  W, dy = y * S; y >= -W; --y, dy -= S)
00303    for (x = -W, dx = x * S; x <=  W; ++x, dx += S)
00304    {
00305       float d2 = sqr(dx) + sqr(dy) ;
00306       float d  = sqrtf(d2) ;
00307       m_vff_mask.push_back(Vector((x == 0) ? 0 : F/d2 * dx/d,
00308                                   (y == 0) ? 0 : F/d2 * dy/d)) ;
00309    }
00310 
00311    // Add goal rendering callback to map object
00312    if (Params::render_goals())
00313       map->add_hook(RenderHook(
00314          render_goals, reinterpret_cast<unsigned long>(this))) ;
00315 }
00316 
00317 //---------------------- THE BEHAVIOUR'S ACTION -------------------------
00318 
00319 // Override from base class because we want to monitor the Pause state.
00320 // Every time the application resumes from a paused state, we want to
00321 // send a message to the metrics log stating that goal seeking has begun.
00322 // When a goal is reached, the action method will log that event.
00323 //
00324 // DEVNOTE: In the while loop below, we use the App::map() function's
00325 // returned pointer without checking it. This is okay because pre_run()
00326 // would have thrown an exception if it were not.
00327 void Goal::run()
00328 {
00329    try
00330    {
00331       App::wait_for_init() ;
00332       pre_run() ;
00333 
00334       const Map* M = App::map() ;
00335       bool paused_previously = Pause::is_set() ;
00336       while (! Shutdown::signaled())
00337       {
00338          bool paused_now = Pause::is_set() ;
00339          if (! paused_now) {
00340             if (paused_previously)
00341                log("seeking goal", m_goal, m_goals[m_goal], M->current_pose());
00342             action() ;
00343          }
00344          paused_previously = paused_now ;
00345          usleep(m_update_delay) ;
00346       }
00347 
00348       post_run() ;
00349    }
00350    catch (uhoh& e)
00351    {
00352       LERROR("behaviour %s encountered an error: %s", name.c_str(), e.what()) ;
00353       return ;
00354    }
00355 }
00356 
00357 // To drive towards the current goal, we turn based on the difference
00358 // between the current bearing and the steering vector computed by the
00359 // VFF technique. This steering vector takes into account the local
00360 // obstacle configuration as well as the goal location.
00361 void Goal::action()
00362 {
00363    const Map* map = App::map() ;
00364    const Pose P = map->current_pose() ;
00365 
00366    int turn_dir = 0 ;
00367    Vector Ft, Fr, R ;
00368 
00369    const Target goal = m_goals[m_goal] ;
00370    if (goal.at(P.x(), P.y()))
00371    {
00372       log("reached goal", m_goal, goal, P) ;
00373 
00374       if (Params::pause())
00375          Pause::set() ;
00376 
00377       ++m_goal ;
00378       if (m_goal == static_cast<int>(m_goals.size())) {
00379          if (Params::loop())
00380             m_goal = 0 ;
00381          else // maintain final goal in list
00382             --m_goal ;
00383       }
00384    }
00385    else if (Params::seek_mode()) // not yet at goal and configured to seek it
00386    {
00387       // We use Borenstein and Koren's VFF method to steer the robot
00388       // towards the goal. The first step is to obtain the VFF active
00389       // window, which is a subportion of the occupancy map centered at
00390       // the robot's current position.
00391       std::vector<unsigned char> vff_window =
00392          map->submap(Params::vff_window_size()) ;
00393 
00394       // The lobot::Map class uses zero to indicate the presence of an
00395       // obstacle and 255 for free space. We need to flip that so that
00396       // the repulsive force computations work correctly.
00397       std::transform(vff_window.begin(), vff_window.end(), vff_window.begin(),
00398                      std::bind1st(std::minus<unsigned char>(), 255)) ;
00399 
00400       // Now, compute the repulsive force vector by convolving the VFF
00401       // vector mask with the certainty values in the active window.
00402       Fr = std::inner_product(m_vff_mask.begin(), m_vff_mask.end(),
00403                               vff_window.begin(), Vector(0, 0)) ;
00404 
00405       // Compute the target/goal's attractive force vector...
00406       Ft = normalized(Vector(goal.x() - P.x(), goal.y() - P.y()))
00407          * Params::vff_att_force() ;
00408 
00409       // Compute the steering vector...
00410       R = Ft + Fr ;
00411 
00412       // Follow the VFF steering vector by determining the amount of
00413       // error between it and the robot's current heading.
00414       const int T = TurnArbiter::turn_max() ;
00415       const float current_heading =
00416          (P.bearing() < 180) ? P.bearing() : P.bearing() - 360 ;
00417       const int heading_error = round(direction(R) - current_heading) ;
00418       if (Params::spin_style_steering())
00419       {
00420          turn_dir = heading_error ;
00421          SpinArbiter::instance().vote(base::name,
00422                                       new SpinArbiter::Vote(turn_dir)) ;
00423       }
00424       else
00425       {
00426          turn_dir = clamp(heading_error, -T, T) ;
00427          TurnArbiter::instance().vote(base::name,
00428             new TurnArbiter::Vote(turn_vote_centered_at(turn_dir))) ;
00429       }
00430    }
00431 
00432    // Record VFF vectors for visualization
00433    viz_lock() ;
00434       m_attractive = Ft ;
00435       m_repulsive  = Fr ;
00436       m_steering   = R  ;
00437       m_turn_dir   = turn_dir ;
00438    viz_unlock() ;
00439 }
00440 
00441 // Helper to send appropriate messages to metrics log
00442 void
00443 Goal::
00444 log(const char* msg, int index, const Goal::Target& goal, const Pose& p) const
00445 {
00446    int n = index + 1 ;
00447    const int N = m_goals.size()/2 + 1 ;
00448    if (Params::loop() && Params::backtrack() && n > N)
00449       n = N - (n - N) ;
00450 
00451    Metrics::Log() << std::setw(Metrics::opw()) << std::left
00452                   << (std::string(msg) + " #" + to_string(n))
00453                   << goal << " from/at" << Metrics::newl()
00454                   << dup_string(" ", 6 + Metrics::opw()) << p ;
00455 }
00456 
00457 //--------------------------- VISUALIZATION -----------------------------
00458 
00459 #ifdef INVT_HAVE_LIBGL
00460 
00461 // Helper function to convert the magnitude of a vector into a string,
00462 // prefixing the supplied single character label to the mag string.
00463 static std::string mag(char label, const Vector& v)
00464 {
00465    using namespace std ;
00466 
00467    std::ostringstream v_str ;
00468    v_str << label   << ": "
00469          << setw(8) << fixed << setprecision(1) << magnitude(v) ;
00470    return v_str.str() ;
00471 }
00472 
00473 void Goal::render_me()
00474 {
00475    // Make local copy so that goal behaviour's thread isn't held up
00476    // waiting for visualization thread to complete.
00477    viz_lock() ;
00478       Vector A = m_attractive ;
00479       Vector R = m_repulsive  ;
00480       Vector T = m_steering   ;
00481       int turn = m_turn_dir   ;
00482    viz_unlock() ;
00483 
00484    // Draw principal axes (to make it easier to understand what's going
00485    // on with the force vectors and turn command).
00486    unit_view_volume() ;
00487    glColor3f(1, 1, 1) ;
00488    glBegin(GL_LINES) ;
00489       glVertex2i(0, -1) ;
00490       glVertex2i(0,  1) ;
00491       glVertex2i(-1, 0) ;
00492       glVertex2i( 1, 0) ;
00493    glEnd() ;
00494 
00495    // Render the VFF vectors
00496    if (is_zero(T.i) && is_zero(T.j))
00497       ; // no steering took place
00498    else
00499    {
00500       glColor3f(0, 1, 0) ;
00501       draw_vector(normalized(A)) ;
00502 
00503       glColor3f(1, 0, 0) ;
00504       draw_vector(normalized(R)) ;
00505 
00506       glColor3f(0.15f, 0.75f, 0.85f) ;
00507       draw_vector(normalized(T)) ;
00508 
00509       glColor3f(1, 1, 1) ;
00510       draw_vector(Vector(0.75f * cos(turn), 0.75f * sin(turn))) ;
00511    }
00512 
00513    // Label the visualization so that it is easy to tell which behaviour
00514    // is being visualized. Also label the speed value and, if applicable,
00515    // the distance reading that resulted in the chosen speed.
00516    restore_view_volume() ;
00517    text_view_volume() ;
00518       glColor3f(0, 1, 1) ;
00519       draw_label(3, 12, "Goal") ;
00520 
00521       glColor3f(0, 1, 0) ;
00522       draw_label(3, m_geometry.height - 28, mag('A', A).c_str()) ;
00523 
00524       glColor3f(1, 0, 0) ;
00525       draw_label(3, m_geometry.height - 16, mag('R', R).c_str()) ;
00526 
00527       glColor3f(0.15f, 0.75f, 0.85f) ;
00528       draw_label(3, m_geometry.height -  4, mag('T', T).c_str()) ;
00529    restore_view_volume() ;
00530 }
00531 
00532 void Goal::render_goals(unsigned long client_data)
00533 {
00534    (reinterpret_cast<Goal*>(client_data))->render_goals() ;
00535 }
00536 
00537 void Goal::render_goals()
00538 {
00539    // Setup 2D "view volume" to match real/physical coordinate system
00540    // except that the whole thing is rotated 90 degrees ccw so that our
00541    // notion of "up" matches that of the robot's.
00542    float L, R, B, T ; // map extents
00543    SlamParams::map_extents(&L, &R, &B, &T) ;
00544    setup_view_volume(T, B, L, R) ;
00545 
00546    // Now we're ready to draw the goal locations...
00547    glPushAttrib(GL_POINT_BIT | GL_LINE_BIT) ;
00548    glPointSize(5) ;
00549    glColor3f(0, 0, 1) ;
00550    glBegin(GL_POINTS) ;
00551       std::for_each(m_goals.begin(), m_goals.end(), render_goal) ;
00552    glEnd() ;
00553 
00554    // For current goal being sought, we also draw a dashed box around it
00555    glEnable(GL_LINE_STIPPLE) ;
00556    glLineStipple(1, 0xCCCC) ;
00557    glColor3f(1, 0, 0) ;
00558    glBegin(GL_LINE_LOOP) ;
00559       const Target& current = m_goals[m_goal] ; // WARNING: m_goal: no viz lock
00560       glVertex2f(current.T(), current.L()) ;
00561       glVertex2f(current.B(), current.L()) ;
00562       glVertex2f(current.B(), current.R()) ;
00563       glVertex2f(current.T(), current.R()) ;
00564    glEnd() ;
00565    glPopAttrib() ;
00566 
00567    // Reset GL transformations so next drawable won't get screwed
00568    restore_view_volume() ;
00569 }
00570 
00571 void Goal::render_goal(const Goal::Target& t)
00572 {
00573    glVertex2f(t.y(), t.x()) ;//coord sys 90 degree ccw rotation ==> swap x & y
00574 }
00575 
00576 #endif
00577 
00578 //----------------------------- CLEAN-UP --------------------------------
00579 
00580 Goal::~Goal(){}
00581 
00582 //-----------------------------------------------------------------------
00583 
00584 } // end of namespace encapsulating this file's definitions
00585 
00586 /* So things look consistent in everyone's emacs... */
00587 /* Local Variables: */
00588 /* indent-tabs-mode: nil */
00589 /* End: */
Generated on Sun May 8 08:41:22 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3