LoApp.C

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/LoApp.C
00003    \brief This file defines the non-inline member functions of the
00004    lobot::App 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/LoApp.C $
00039 // $Id: LoApp.C 13811 2010-08-21 02:00:08Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/LoApp.H"
00046 
00047 #include "Robots/LoBot/control/LoTurnArbiter.H"
00048 #include "Robots/LoBot/control/LoBehavior.H"
00049 
00050 #include "Robots/LoBot/slam/LoMap.H"
00051 #include "Robots/LoBot/lgmd/LocustModel.H"
00052 
00053 #include "Robots/LoBot/ui/LoMainWindow.H"
00054 #include "Robots/LoBot/ui/LoLocustViz.H"
00055 #include "Robots/LoBot/ui/LoLaserVizFlat.H"
00056 #include "Robots/LoBot/ui/LoLaserViz.H"
00057 #include "Robots/LoBot/ui/LoDrawable.H"
00058 
00059 #include "Robots/LoBot/io/LoRobot.H"
00060 #include "Robots/LoBot/io/LoLaserRangeFinder.H"
00061 #include "Robots/LoBot/io/LoDangerZone.H"
00062 
00063 #include "Robots/LoBot/io/LoInputSource.H"
00064 #include "Robots/LoBot/io/LoVideoStream.H"
00065 #include "Robots/LoBot/io/LoVideoRecorder.H"
00066 #include "Robots/LoBot/io/LoFireWireBus.H"
00067 
00068 #include "Robots/LoBot/config/LoConfigHelpers.H"
00069 #include "Robots/LoBot/config/LoCommonOpts.H"
00070 #include "Robots/LoBot/config/LoDefaults.H"
00071 
00072 #include "Robots/LoBot/thread/LoShutdown.H"
00073 #include "Robots/LoBot/thread/LoUpdateLock.H"
00074 #include "Robots/LoBot/thread/LoPause.H"
00075 
00076 #include "Robots/LoBot/misc/LoExcept.H"
00077 #include "Robots/LoBot/misc/LoRegistry.H"
00078 #include "Robots/LoBot/misc/factory.hh"
00079 
00080 #include "Robots/LoBot/util/LoMath.H"
00081 #include "Robots/LoBot/util/LoDebug.H"
00082 #include "Robots/LoBot/util/range.hh"
00083 
00084 // INVT image support
00085 #include "Image/Dims.H"
00086 
00087 // INVT utilities
00088 #include "Util/log.H"
00089 
00090 // Unix headers
00091 #include <glob.h>
00092 #include <unistd.h>
00093 
00094 // Standard C++ headers
00095 #include <algorithm>
00096 #include <functional>
00097 #include <iterator>
00098 #include <utility>
00099 
00100 #include <cmath>
00101 #include <cstdlib>
00102 #include <ctime>
00103 
00104 //----------------------------- NAMESPACE -------------------------------
00105 
00106 namespace lobot {
00107 
00108 //----------------------- FORWARD DECLARATIONS --------------------------
00109 
00110 bool video_enabled() ;
00111 
00112 Dims  grab_size() ;
00113 float grab_rate() ;
00114 
00115 std::string locust_directions() ;
00116 int locust_fov() ;
00117 
00118 bool playback_enabled() ;
00119 bool recording_enabled() ;
00120 std::string playback_stem() ;
00121 std::string recording_stem() ;
00122 
00123 bool laser_enabled() ;
00124 std::string laser_device() ;
00125 int laser_baud_rate() ;
00126 
00127 bool robot_enabled() ;
00128 std::string robot_platform() ;
00129 
00130 bool show_ui() ;
00131 static bool mapping_enabled() ;
00132 static bool viz_on(const Behavior*) ;
00133 
00134 //-------------------------- INITIALIZATION -----------------------------
00135 
00136 App& App::create(int argc, const char* argv[])
00137 {
00138    App& app = instance() ;
00139    app.m_argc = argc ;
00140    app.m_argv = argv ;
00141    return app ;
00142 }
00143 
00144 // Setup INVT model manager and command line options; start the signal
00145 // handling thread that responds to application shutdown signals.
00146 App::App()
00147    : m_compositor(0),
00148      m_lrf(0),
00149      m_input_source(0),
00150      m_robot(0),
00151      m_map(0),
00152      m_laser_viz(0),
00153      m_laser_viz_flat(0),
00154      m_locust_viz(0),
00155      m_model_manager("lobot"),
00156      m_cf_option(& OPT_ConfigFile, & m_model_manager),
00157      m_initialized(false)
00158 {
00159    Shutdown::start_listening() ;
00160 }
00161 
00162 // Create video streams to read images from MPEG files
00163 static void create_mpeg_video_streams(std::string mpeg_file_names_stem,
00164                                       App::VideoStreams* V)
00165 {
00166    mpeg_file_names_stem += "*" ;
00167    glob_t buf ;
00168    if (glob(mpeg_file_names_stem.c_str(), 0, 0, & buf) != 0)
00169       throw vstream_error(MPEG_FILES_NOT_FOUND) ;
00170 
00171    const int n = buf.gl_pathc ;
00172    V->reserve(n) ;
00173    for (int i = 0; i < n; ++i)
00174       V->push_back(new VideoStream(buf.gl_pathv[i])) ;
00175 
00176    globfree(& buf) ;
00177 }
00178 
00179 // Create video streams to grab from each camera connected to FireWire
00180 // bus.
00181 static void create_camera_video_streams(const Dims& resolution,
00182                                         float frame_rate,
00183                                         App::VideoStreams* V)
00184 {
00185    const int n = FireWireBus::instance().num_cameras() ;
00186    V->reserve(n) ;
00187    for (int i = 0; i < n; ++i)
00188       V->push_back(new VideoStream(i, resolution, frame_rate)) ;
00189 
00190    // Okay to release the FireWire camera nodes now. We won't be needing
00191    // them any further in this program.
00192    FireWireBus::instance().release_camera_nodes() ;
00193 }
00194 
00195 // Create video recorders attached to each of the video streams to store
00196 // the input frames to corresponding MPEG movies.
00197 static void create_video_recorders(const App::VideoStreams& V,
00198                                    const std::string& mpeg_name_root,
00199                                    App::VideoRecorders* R)
00200 {
00201    const int n = V.size() ;
00202    R->reserve(n) ;
00203    for (int i = 0; i < n; ++i)
00204       R->push_back(new VideoRecorder(mpeg_name_root + to_string(i), V[i])) ;
00205 }
00206 
00207 // Create an instance of the named locust model and set it up to use the
00208 // specified portion of the laser range finder's FOV as its source.
00209 static LocustModel*
00210 create_locust_model(const std::string& name, const LocustModel::InitParams& p)
00211 {
00212    typedef factory<LocustModel, LocustModel::InitParams> lgmd_factory ;
00213    try
00214    {
00215       return lgmd_factory::create(name, p) ;
00216    }
00217    catch (lgmd_factory::unknown_type&)
00218    {
00219       throw unknown_model(UNKNOWN_LOCUST_MODEL) ;
00220    }
00221 }
00222 
00223 // Create the desired number of instances of the user-specified locust
00224 // model. These objects will generate the LGMD spike trains from the
00225 // specified input source.
00226 //
00227 // We setup the virtual locusts starting from the left edge of the
00228 // composited input image and move rightwards till we hit the right edge.
00229 // For each virtual locust, we take into account the configured FOV and
00230 // overlap settings.
00231 static void
00232 create_locust_models(const InputSource* S, App::LocustModels* models)
00233 {
00234    if (S->using_video())
00235    {
00236       // FIXME: Since the switch to a laser range finder as the primary
00237       // sensing modality and changes thereafter to the way locust models
00238       // are created, rendered, etc., this block of code has become
00239       // hopelessly incorrect and simply won't work! It needs careful
00240       // thought and some major surgery...
00241       /*
00242       const Dims size = S->get_image_size() ;
00243       const std::string model_name = locust_model() ;
00244       const int fov = clamp(locust_fov(), LOBOT_MIN_LOCUST_FOV, size.w()) ;
00245       const int overlap = clamp(fov_overlap(), 0, fov/2) ;
00246 
00247       const int N = static_cast<int>(
00248          std::floor(static_cast<double>(size.w())/(fov - overlap))) ;
00249       models->reserve(N) ;
00250 
00251       const int W = size.w() - 1 ;
00252       const int B = size.h() - 1 ;
00253       const int T = 0 ;
00254       int L = 0 ; // start at left edge of composited input image
00255       for(;;)
00256       {
00257          int R = std::min(L + fov - 1, W) ;
00258          models->push_back(create_locust_model(model_name, S, L, R, B, T)) ;
00259          if (R >= W) // hit the right edge of composited input image
00260             break ;
00261          L = R + 1 - overlap ;
00262       }
00263       */
00264    }
00265    else if (S->using_laser())
00266    {
00267       std::vector<int> D = string_to_vector<int>(locust_directions());
00268       const int N = D.size() ;
00269       if (N <= 0)
00270          return ;
00271       std::sort(D.begin(), D.end(), std::greater<int>()) ; // left to right
00272 
00273       const std::string model_name = locust_model() ;
00274       const range<int> lrf_range   = S->lrf_angular_range() ;
00275       const int fov = clamp(locust_fov(), lrf_range) ;
00276 
00277       Drawable::Geometry g =
00278          get_conf<std::string>(model_name, "geometry", "0 75 975 75") ;
00279       g.width /= N ;
00280 
00281       LocustModel::InitParams p ;
00282       p.spike_range =
00283          get_conf<float>(model_name, "spike_range", make_range(0.0f, 300.0f)) ;
00284       p.source = S ;
00285 
00286       models->reserve(N) ;
00287       for(int i = 0; i < N; ++i, g.x += g.width)
00288       {
00289          const int L = std::min(D[i] + fov/2, lrf_range.max()) ;
00290          const int R = std::max(D[i] - fov/2, lrf_range.min()) ;
00291 
00292          p.direction = D[i] ;
00293          p.lrf_range.reset(L, R) ;
00294          p.name = std::string("lgmd[") + to_string(p.direction) + "]" ;
00295          p.geometry = g ;
00296 
00297          models->push_back(create_locust_model(model_name, p)) ;
00298       }
00299    }
00300 }
00301 
00302 // Start the different behaviours as specified in the config file.
00303 static void create_behaviours(App::Behaviours* B)
00304 {
00305    std::vector<std::string> behaviours = string_to_vector<std::string>(
00306       global_conf<std::string>("behaviors")) ;
00307    //dump(behaviours, "create_behaviours", "behaviours") ;
00308 
00309    const int N = behaviours.size() ;
00310    B->reserve(N) ;
00311    for (int i = 0; i < N; ++i)
00312       try
00313       {
00314          Behavior* b = factory<Behavior>::create(behaviours[i]) ;
00315          b->name = behaviours[i] ;
00316          B->push_back(b) ;
00317       }
00318       catch (factory<Behavior>::unknown_type&)
00319       {
00320          throw unknown_model(UNKNOWN_BEHAVIOR) ;
00321       }
00322 }
00323 
00324 // Create the motor subsystem's interface object, taking care of
00325 // ModelManager niceties.
00326 static Robot* create_robot(const std::string& robot_platfom, ModelManager& M)
00327 {
00328    typedef factory<Robot, ModelManager> robot_factory ;
00329    try
00330    {
00331       return robot_factory::create(robot_platfom, M) ;
00332    }
00333    catch (robot_factory::unknown_type&)
00334    {
00335       throw unknown_model(UNKNOWN_ROBOT_PLATFORM) ;
00336    }
00337    return 0 ; // keep compiler happy
00338 }
00339 
00340 // Let other threads know that the application object is fully loaded
00341 void App::signal_init()
00342 {
00343    m_initialized_cond.broadcast(signal_pred()) ;
00344 }
00345 
00346 bool App::signal_pred::operator()()
00347 {
00348    App& app = App::instance() ;
00349    app.m_initialized = true ;
00350    return true ;
00351 }
00352 
00353 // API to let other threads wait until application object is fully loaded
00354 void App::wait_init()
00355 {
00356    m_initialized_cond.wait(wait_pred()) ;
00357 }
00358 
00359 bool App::wait_pred::operator()()
00360 {
00361    return App::instance().m_initialized ;
00362 }
00363 
00364 //----------------------- COMMAND LINE PARSING --------------------------
00365 
00366 void App::parse_command_line()
00367 {
00368    std::string default_config_file = getenv("HOME") ;
00369    default_config_file += "/" ;
00370    default_config_file += LOBOT_DEFAULT_CONFIG_FILE_NAME ;
00371    m_model_manager.setOptionValString(& OPT_ConfigFile,
00372                                       default_config_file.c_str()) ;
00373 
00374    if (! m_model_manager.parseCommandLine(m_argc, m_argv, "", 0, 0))
00375       throw customization_error(BAD_OPTION) ;
00376 }
00377 
00378 //----------------------------- MAIN LOOP -------------------------------
00379 
00380 // Application object's run method (aka main loop)
00381 void App::run()
00382 {
00383    m_model_manager.start() ;
00384 
00385    // Load the config file (if any)
00386    try
00387    {
00388       Configuration::load(config_file()) ;
00389       //Configuration::dump() ;
00390    }
00391    catch (customization_error& e) // this is not fatal
00392    {
00393       LERROR("%s", e.what()) ; // simply report error and move on
00394    }
00395 
00396    // Create the video I/O objects
00397    if (video_enabled()) {
00398       if (playback_enabled())
00399          create_mpeg_video_streams(playback_stem(), & m_video_streams) ;
00400       else
00401          create_camera_video_streams(grab_size(), grab_rate(),
00402                                      & m_video_streams) ;
00403       m_compositor = new ImageCompositor() ;
00404       connect(m_video_streams, *m_compositor) ;
00405       if (recording_enabled())
00406          create_video_recorders(m_video_streams, recording_stem() + "-",
00407                                 & m_video_recorders) ;
00408       Configuration::set_internal("frame_rate",
00409                                   to_string(m_video_streams[0]->frameRate())) ;
00410    }
00411 
00412    // Create the laser range finder I/O object
00413    if (laser_enabled()) {
00414       m_lrf = new LaserRangeFinder(laser_device(), laser_baud_rate()) ;
00415       DangerZone::use(m_lrf) ;
00416    }
00417 
00418    // Create the robot interface object
00419    if (robot_enabled())
00420       m_robot = create_robot(robot_platform(), m_model_manager) ;
00421 
00422    // Create the map object if mapping is enabled
00423    if (mapping_enabled())
00424       m_map = new Map() ;
00425 
00426    // Setup the locust LGMD models
00427    if (video_enabled() && video_input())
00428       m_input_source = new InputSource(m_compositor) ;
00429    else if (laser_enabled() && laser_input())
00430       m_input_source = new InputSource(m_lrf) ;
00431    if (m_input_source)
00432       create_locust_models(m_input_source, & m_locusts) ;
00433 
00434    // Start the different behaviours
00435    create_behaviours(& m_behaviours) ;
00436 
00437    // Create and configure main window
00438    if (show_ui()) {
00439       MainWindow& W = MainWindow::instance() ;
00440       if (video_enabled() && video_input()) {
00441       }
00442       else if (laser_enabled() && laser_input()) {
00443          if (visualize("laser_viz"))
00444             W.push_back(m_laser_viz = new LaserViz(m_lrf)) ;
00445          if (visualize("laser_viz_flat"))
00446             W.push_back(m_laser_viz_flat = new LaserVizFlat(m_lrf)) ;
00447       }
00448 
00449       if (mapping_enabled() && visualize("map"))
00450          W.push_back(m_map) ;
00451 
00452       if (visualize(locust_model()))
00453          connect(m_locusts, W) ;
00454       if (visualize("locust_viz"))
00455          W.push_back(m_locust_viz = new LocustViz(m_locusts)) ;
00456 
00457       if (visualize("turn_arbiter"))
00458          W.push_back(& TurnArbiter::instance()) ;
00459       connect_if(m_behaviours, W, viz_on) ;
00460    }
00461 
00462    // Check whether the application should be started off in a paused
00463    // state or not.
00464    bool start_paused = global_conf("start_paused", false) ;
00465    if (start_paused && show_ui())
00466       Pause::set() ;
00467    else
00468       Pause::clear() ;
00469 
00470    // The application object is, at this point, fully loaded
00471    srand(time(0)) ;
00472    signal_init() ;
00473 
00474    // Grab data and do the locust jig
00475    int update_delay = clamp(global_conf("update_delay", 100), 1, 1000) * 1000;
00476    while (! Shutdown::signaled())
00477    {
00478       if (Pause::is_clear()) {
00479          UpdateLock::begin_write() ;
00480             std::for_each(m_video_streams.begin(), m_video_streams.end(),
00481                           std::mem_fun(& VideoStream::update)) ;
00482             std::for_each(m_video_recorders.begin(), m_video_recorders.end(),
00483                           std::mem_fun(& VideoRecorder::update)) ;
00484             if (m_compositor)
00485                m_compositor->update() ;
00486             if (m_lrf) {
00487                m_lrf->update() ;
00488                DangerZone::update() ;
00489             }
00490             if (m_robot)
00491                m_robot->update() ;
00492             std::for_each(m_locusts.begin(), m_locusts.end(),
00493                           std::mem_fun(& LocustModel::update)) ;
00494          UpdateLock::end_write() ;
00495       }
00496       usleep(update_delay) ;
00497    }
00498 
00499    // Kill the robot before quitting the application
00500    if (m_robot)
00501       m_robot->off() ;
00502 }
00503 
00504 //--------------------------- APP CLEAN-UP ------------------------------
00505 
00506 App::~App()
00507 {
00508    LERROR("cleaning up...") ;
00509 
00510    purge_container(m_behaviours) ;
00511 
00512    delete m_locust_viz ;
00513    delete m_laser_viz_flat ;
00514    delete m_laser_viz ;
00515    delete m_map ;
00516    delete m_robot ;
00517 
00518    purge_container(m_locusts) ;
00519    delete m_input_source ;
00520    delete m_lrf ;
00521    delete m_compositor ;
00522 
00523    purge_container(m_video_recorders) ;
00524    purge_container(m_video_streams) ;
00525 
00526    if (m_model_manager.started())
00527       m_model_manager.stop() ;
00528 }
00529 
00530 //------------------------------ HELPERS --------------------------------
00531 
00532 bool video_enabled()
00533 {
00534    return video_conf("use_video", true) ;
00535 }
00536 
00537 Dims grab_size()
00538 {
00539    try
00540    {
00541       Dims d ;
00542       convertFromString(video_conf<std::string>("grab_size"), d) ;
00543       return d ;
00544    }
00545    catch (std::exception&) // badly formatted dims string in config file
00546    {
00547       return LOBOT_DEFAULT_GRAB_SIZE ; // don't crash!
00548    }
00549 }
00550 
00551 float grab_rate()
00552 {
00553    return video_conf("grab_rate", LOBOT_DEFAULT_GRAB_RATE) ;
00554 }
00555 
00556 bool recording_enabled()
00557 {
00558    return recording_stem() != "" ;
00559 }
00560 
00561 std::string recording_stem()
00562 {
00563    return video_conf<std::string>("record") ;
00564 }
00565 
00566 bool playback_enabled()
00567 {
00568    return playback_stem() != "" ;
00569 }
00570 
00571 std::string playback_stem()
00572 {
00573    return video_conf<std::string>("play") ;
00574 }
00575 
00576 bool laser_enabled()
00577 {
00578    return laser_conf("use_laser", true) ;
00579 }
00580 
00581 std::string laser_device()
00582 {
00583    return laser_conf<std::string>("serial_port", "/dev/ttyACM0") ;
00584 }
00585 
00586 int laser_baud_rate()
00587 {
00588    return laser_conf("baud_rate", 115200) ;
00589 }
00590 
00591 std::string locust_directions()
00592 {
00593    return get_conf<std::string>(locust_model(), "locust_directions", "") ;
00594 }
00595 
00596 int locust_fov()
00597 {
00598    return get_conf(locust_model(), "locust_fov", LOBOT_DEFAULT_LOCUST_FOV) ;
00599 }
00600 
00601 bool show_ui()
00602 {
00603    return ui_conf("show_ui", true) ;
00604 }
00605 
00606 static bool mapping_enabled()
00607 {
00608    return get_conf("map", "enable", false) ;
00609 }
00610 
00611 static bool viz_on(const Behavior* B)
00612 {
00613    return visualize(B->name) ;
00614 }
00615 
00616 //-----------------------------------------------------------------------
00617 
00618 } // end of namespace encapsulating this file's definitions
00619 
00620 /* So things look consistent in everyone's emacs... */
00621 /* Local Variables: */
00622 /* indent-tabs-mode: nil */
00623 /* End: */
Generated on Sun May 8 08:05:55 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3