LobayMain.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/LobayMain.C
00003    \brief Robolocust metrics log analyzer for Bayesian TTI predictions.
00004 
00005    This file defines the main function for a multithreaded analysis
00006    program that loads all the Robolocust metrics logs associated with the
00007    Bayesian time-to-impact prediction experiments and processes these
00008    logs to produce the desired results files for each set of related
00009    experiments.
00010 
00011    Here's the background for this program: the Robolocust project aims to
00012    use locusts for robot navigation. Specifically, the locust sports a
00013    visual interneuron known as the Lobula Giant Movement Detector (LGMD)
00014    that spikes preferentially in response to objects moving toward the
00015    animal on collisional trajectories. Robolocust's goal is to use an
00016    array of locusts, each looking in a different direction. As the robot
00017    moves, we expect to receive greater spiking activity from the locusts
00018    looking in the direction in which obstacles are approaching (or being
00019    approached) and use this information to veer the robot away.
00020 
00021    Before we mount actual locusts on a robot, we would like to first
00022    simulate this LGMD-based navigation. Toward that end, we use a laser
00023    range finder (LRF) mounted on an iRobot Create driven by a quad-core
00024    mini-ITX computer. A computational model of the LGMD developed by
00025    Gabbiani, et al. takes the LRF distance readings and the Create's
00026    odometry as input and produces artificial LGMD spikes based on the
00027    time-to-impact of approaching objects. We simulate multiple virtual
00028    locusts by using different angular portions of the LRF's field of
00029    view. To simulate reality a little better, we inject Gaussian noise
00030    into the artificial spikes.
00031 
00032    We have devised three different LGMD-based obstacle avoidance
00033    algorithms:
00034 
00035       1. EMD: pairs of adjacent LGMD's are fed into Reichardt motion
00036               detectors to determine the dominant direction of spiking
00037               activity and steer the robot away from that direction;
00038 
00039       2. VFF: a spike rate threshold is used to "convert" each virtual
00040               locust's spike into an attractive or repulsive virtual
00041               force; all the force vectors are combined to produce the
00042               final steering vector;
00043 
00044       3. TTI: each locust's spike rate is fed into a Bayesian state
00045               estimator that computes the time-to-impact given a spike
00046               rate; these TTI estimates are then used to determine
00047               distances to approaching objects, thereby effecting the
00048               LGMD array's use as a kind of range sensor; the distances
00049               are compared against a threshold to produce attractive and
00050               repulsive forces, with the sum of the force field vectors
00051               determining the final steering direction.
00052 
00053    As mentioned above, the TTI algorithm uses a Bayesian state estimator
00054    that predicts the time-to-impact given an LGMD spike rate. The
00055    Bayesian time-to-impact prediction experiments are designed to
00056    evaluate this TTI prediction model. Here's how these experiments were
00057    conducted:
00058 
00059    The robot was driven straight ahead towards a wall starting at a point
00060    2.5 meters away. A single virtual locust looking straight ahead was
00061    used to generate LGMD spikes. The robot was configured to stop just
00062    short of hitting the wall.
00063 
00064    As it moved toward the wall, the robot's controller would record the
00065    current speed, LGMD spike rate, actual time-to-impact, predicted TTI,
00066    actual distance to wall, predicted distance and the prediction's
00067    confidence level to a log file.
00068 
00069    We varied the robot's speed, the amount of noise in the artificially
00070    generated LGMD spikes and the delta value for the spike generation
00071    model. The delta value is a parameter that controls when the peak
00072    spike rate is achieved w.r.t. the point at which a collision takes
00073    place, i.e., when time-to-impact is zero. To illustrate, let us say we
00074    use a delta of 1.0 seconds; this means that the spike generation model
00075    will produce a peak when the time-to-impact is 1.0 seconds, i.e., when
00076    the approaching wall is 1 second away from the robot. Similarly, when
00077    delta is 0.5, the LGMD peak will be achieved when the robot is half a
00078    second away from colliding with the wall; at delta = 2, the peak will
00079    be at 2 seconds from collision; so on and so forth.
00080 
00081    The TTI prediction experiments were run with the following parameters:
00082 
00083       - noise: 0Hz, 25Hz, 50Hz, 100Hz
00084       - speed: 0.1m/s, 0.2m/s, 0.3m/s, 0.4m/s
00085       - delta: 0.25s, 0.50s, 0.75s, 1.00s, 1.25s, 1.50s, 1.75s, 2.00s
00086 
00087    For each noise level, robot speed and delta value, we ran the robot 10
00088    times. Thus, if we consider each set of 10 such individual runs to be
00089    one dataset, we have a total of 4 noise levels times 4 speeds times 8
00090    delta values = 128 datasets.
00091 
00092    If "bay" is the root data directory for the Bayesian TTI experiments,
00093    then each dataset's log files are stored in a subdirectory of "bay" in
00094    a hierarchy as depicted below:
00095 
00096                           bay
00097                            |
00098                            +-- 000
00099                            |    |
00100                            |    +-- 0.1
00101                            |    |    |
00102                            |    |    +-- 0.25
00103                            |    |    +-- 0.50
00104                            |    |    +-- 0.75
00105                            |    |    +-- 1.00
00106                            |    |    +-- 1.25
00107                            |    |    +-- 1.50
00108                            |    |    +-- 1.75
00109                            |    |    +-- 2.00
00110                            |    |
00111                            |    +-- 0.2
00112                            |    |    :
00113                            |    |    :
00114                            |    |
00115                            |    +-- 0.3
00116                            |    |    :
00117                            |    |
00118                            |    +-- 0.4
00119                            |         :
00120                            |         :
00121                            |
00122                            +-- 025
00123                            |    :
00124                            |    :
00125                            |
00126                            +-- 050
00127                            |    :
00128                            |    :
00129                            |
00130                            +-- 100
00131                                 :
00132                                 :
00133 
00134    NOTE: The above directory hierarchy is not fixed by the lobot
00135    controller. It is simply how we configured the controller to work
00136    while running these experiments and collecting the data.
00137 
00138    The objective of this program is to load an entire dataset from each
00139    of the above directories and then write out a results file whose
00140    format is shown below:
00141 
00142         TTI   LGMD Spike Rate   Predicted TTI   Confidence Level
00143         ---   ---------------   -------------   ----------------
00144               mean   stdev      mean   stdev    mean   stdev
00145 
00146    Since each dataset's results can be computed independently of every
00147    other dataset, we use multiple threads to process several datasets in
00148    parallel. Given the Bayesian TTI prediction experiments' data root
00149    directory, this program finds all the subdirectories containing log
00150    files and then launches as many threads as CPU's available to walk
00151    through this directory list and perform the necessary log file
00152    parsing.
00153 */
00154 
00155 // //////////////////////////////////////////////////////////////////// //
00156 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00157 // by the University of Southern California (USC) and the iLab at USC.  //
00158 // See http://iLab.usc.edu for information about this project.          //
00159 // //////////////////////////////////////////////////////////////////// //
00160 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00161 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00162 // in Visual Environments, and Applications'' by Christof Koch and      //
00163 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00164 // pending; application number 09/912,225 filed July 23, 2001; see      //
00165 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00166 // //////////////////////////////////////////////////////////////////// //
00167 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00168 //                                                                      //
00169 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00170 // redistribute it and/or modify it under the terms of the GNU General  //
00171 // Public License as published by the Free Software Foundation; either  //
00172 // version 2 of the License, or (at your option) any later version.     //
00173 //                                                                      //
00174 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00175 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00176 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00177 // PURPOSE.  See the GNU General Public License for more details.       //
00178 //                                                                      //
00179 // You should have received a copy of the GNU General Public License    //
00180 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00181 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00182 // Boston, MA 02111-1307 USA.                                           //
00183 // //////////////////////////////////////////////////////////////////// //
00184 //
00185 // Primary maintainer for this file: mviswana usc edu
00186 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/LobayMain.C $
00187 // $Id: LobayMain.C 14083 2010-09-30 13:59:37Z mviswana $
00188 //
00189 
00190 //--------------------------- LIBRARY CHECKS ----------------------------
00191 
00192 #if !defined(INVT_HAVE_BOOST_PROGRAM_OPTIONS)
00193 
00194 #include <iostream>
00195 
00196 int main()
00197 {
00198    std::cerr << "Sorry, this program requires the following libraries:\n"
00199              << "\tlibboost_program_options\n\n" ;
00200    std::cerr << "Please ensure development packages for above libraries "
00201              << "are installed\n"
00202              << "and then rebuild this program to get it to work.\n" ;
00203    return 255 ;
00204 }
00205 
00206 #else // various required libraries available
00207 
00208 //------------------------------ HEADERS --------------------------------
00209 
00210 // lobot headers
00211 #include "Robots/LoBot/baylog/LoBaylogAnalyzer.H"
00212 #include "Robots/LoBot/baylog/LoDirList.H"
00213 
00214 #include "Robots/LoBot/thread/LoThread.H"
00215 #include "Robots/LoBot/config/LoConfigHelpers.H"
00216 
00217 #include "Robots/LoBot/util/LoFile.H"
00218 #include "Robots/LoBot/util/LoSTL.H"
00219 #include "Robots/LoBot/util/LoSysConf.H"
00220 #include "Robots/LoBot/misc/LoExcept.H"
00221 #include "Robots/LoBot/misc/singleton.hh"
00222 
00223 // Boost headers
00224 #include <boost/program_options.hpp>
00225 
00226 // Standard C++ headers
00227 #include <iostream>
00228 #include <algorithm>
00229 #include <string>
00230 #include <vector>
00231 #include <stdexcept>
00232 
00233 // Standard C headers
00234 #include <stdlib.h>
00235 
00236 // Standard Unix headers
00237 #include <unistd.h>
00238 
00239 //-------------------------- KNOB TWIDDLING -----------------------------
00240 
00241 namespace {
00242 
00243 // Retrieve settings from global section of config file
00244 template<typename T>
00245 inline T conf(const std::string& key, const T& default_value)
00246 {
00247    return lobot::global_conf<T>(key, default_value) ;
00248 }
00249 
00250 /// This inner class encapsulates various parameters that can be used to
00251 /// tweak different aspects of the Bayesian TTI prediction analysis.
00252 class MainParams : public lobot::singleton<MainParams> {
00253    /// The log files for the Bayesian time-to-impact prediction
00254    /// experiments are stored under a directory hierarchy that is
00255    /// arranged to match the different LGMD spike rate noise levels,
00256    /// robot speeds and delta values used to gather the data.
00257    ///
00258    /// This setting specifies the root directory under which the
00259    /// above-mentioned hierarchy of subdirectories appears.
00260    std::string m_root ;
00261 
00262    /// The lobay program processes multiple datasets in parallel by first
00263    /// reading in the entire list of directories containing log files and
00264    /// then launching threads to process each dataset. This setting
00265    /// specifies a regular expression that will be used to match dataset
00266    /// directory names.
00267    std::string m_dataset ;
00268 
00269    /// Private constructor because this is a singleton.
00270    MainParams() ;
00271 
00272    // Boilerplate code to make generic singleton design pattern work
00273    friend class lobot::singleton<MainParams> ;
00274 
00275 public:
00276    /// Accessing the various parameters.
00277    //@{
00278    static const std::string& root()    {return instance().m_root    ;}
00279    static const std::string& dataset() {return instance().m_dataset ;}
00280    //@}
00281 } ;
00282 
00283 // Parameters initialization
00284 MainParams::MainParams()
00285    : m_root(conf<std::string>("root_dir", "/tmp/lobay")),
00286      m_dataset(conf<std::string>("dataset_dir_name",
00287                                  "[01][025][05]/0\\.[1-4]/[0-2]\\.[0257][05]"))
00288 {}
00289 
00290 // Shortcut
00291 typedef MainParams Params ;
00292 
00293 } // end of local anonymous namespace encapsulating above helpers
00294 
00295 //-------------------------- PROGRAM OPTIONS ----------------------------
00296 
00297 namespace {
00298 
00299 // If the user does not supply the -c option on the command line, we will
00300 // fall back to the default config file name returned by this function.
00301 std::string default_config_file()
00302 {
00303    return std::string(getenv("HOME")) + "/.lobayrc" ;
00304 }
00305 
00306 // Helper function to take care of the annoying details of using
00307 // Boost.program_options to get at the command line arguments.
00308 //
00309 // DEVNOTE: This code is lifted almost verbatim from the
00310 // Boost.program_options tutorial and examples that comes with the Boost
00311 // documentation. There may be better (i.e., neater, more effective,
00312 // clearer, more efficient, whatever) ways to use the library.
00313 std::string parse(int argc, char* argv[])
00314 {
00315    std::string config_file_name ;
00316 
00317    // Specify the command line options
00318    namespace po = boost::program_options ;
00319    po::options_description options("Command line options") ;
00320    options.add_options()
00321       // the -c option for specifying the config file
00322       ("config-file,c",
00323        po::value<std::string>(&config_file_name)->
00324           default_value(default_config_file()),
00325        "specify configuration settings file") ;
00326 
00327    // Setup done: now parse argc and argv...
00328    po::variables_map varmap ;
00329    po::store(po::parse_command_line(argc, argv, options), varmap) ;
00330    po::notify(varmap) ;
00331 
00332    return config_file_name ;
00333 }
00334 
00335 // Helper function to read the lobay program's config file. If the
00336 // specified file doesn't exist, the program will rely on default
00337 // settings. If the config file contains problematic constructs, an error
00338 // will be reported but the program will continue on, simply ignoring the
00339 // bad settings.
00340 void load_config_file(const std::string& file_name)
00341 {
00342    using namespace lobot ;
00343    try
00344    {
00345       Configuration::load(file_name) ;
00346       //Configuration::dump() ;
00347    }
00348    catch (customization_error& e)
00349    {
00350       if (e.code() != NO_SUCH_CONFIG_FILE)
00351          std::cerr << e.what() << '\n' ;
00352    }
00353 }
00354 
00355 } // end of local anonymous namespace encapsulating above helpers
00356 
00357 //------------------------- METRICS ANALYSIS ----------------------------
00358 
00359 namespace {
00360 
00361 // This function reads all the Robolocust metrics logs in the specified
00362 // directory and combines them to produce the desired average trajectory
00363 // and other pertinent info.
00364 void process_datasets(const std::vector<std::string>& dirs)
00365 {
00366    using namespace lobot ;
00367 
00368    // Analyze datasets in parallel
00369    DirList dir_list(dirs) ;
00370    const int T = std::min(dir_list.size(), num_cpu()) ;
00371    std::vector<BaylogAnalyzer*> analyzer_threads ;
00372    analyzer_threads.reserve(T) ;
00373    for (int i = 0; i < T; ++i)
00374       analyzer_threads.push_back(BaylogAnalyzer::create(dir_list)) ;
00375 
00376    // Now we wait for the analyzer threads to do their thing...
00377    //
00378    // DEVNOTE: If we don't pause this main thread briefly before invoking
00379    // the wait API, we could be in big trouble because the scheduler
00380    // might decide to go right on executing this thread before it begins
00381    // any of the analyzer threads, in which case the wait will fail as
00382    // none of the other threads would have started up just yet, i.e., the
00383    // thread count would be zero, causing this thread to mistakenly
00384    // conclude that all the analyzers are done and that it is safe to
00385    // delete them. When those threads then start executing, they will try
00386    // to reference their associated analyzer objects, which, because they
00387    // no longer exist, will end up taking us on a scenic, albeit short
00388    // and tragic, bus ride to Segfault City.
00389    //
00390    // DEVNOTE 2: A better way to do this is to use another condition
00391    // variable rather than the one used implicitly by Thread::wait_all(),
00392    // over which we have no control. For example, we could wait on a
00393    // condition variable that tests a counter going all the way up to T,
00394    // the number of analyzer threads created. When each analyzer is done,
00395    // it will increment the counter. Since we would have explicit control
00396    // over this variable over here, we can be assured that this thread
00397    // won't mistakenly assume all the analyzers are done.
00398    //
00399    // Of course, the Right Thing is good and all. But why bother? A short
00400    // sleeps works just as well for most practical purposes...
00401    sleep(1) ; // HACK! to prevent segfault; see comment above
00402    Thread::wait_all() ;
00403    purge_container(analyzer_threads) ;
00404 }
00405 
00406 } // end of local anonymous namespace encapsulating above helpers
00407 
00408 //------------------------------- MAIN ----------------------------------
00409 
00410 int main(int argc, char* argv[])
00411 {
00412    int ret = 0 ;
00413    try
00414    {
00415       load_config_file(parse(argc, argv)) ;
00416       process_datasets(lobot::find_dir(Params::root(), Params::dataset())) ;
00417    }
00418    catch (lobot::uhoh& e)
00419    {
00420       std::cerr << e.what() << '\n' ;
00421       ret = e.code() ;
00422    }
00423    catch (std::exception& e)
00424    {
00425       std::cerr << e.what() << '\n' ;
00426       ret = 127 ;
00427    }
00428    catch(...)
00429    {
00430       std::cerr << "unknown exception\n" ;
00431       ret = 255 ;
00432    }
00433    return ret ;
00434 }
00435 
00436 //-----------------------------------------------------------------------
00437 
00438 #endif // library checks
00439 
00440 /* So things look consistent in everyone's emacs... */
00441 /* Local Variables: */
00442 /* indent-tabs-mode: nil */
00443 /* End: */
Generated on Sun May 8 08:41:30 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3