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: */