00001 /** 00002 \file Robots/LoBot/slam/LoMap.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::Map 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/slam/LoMap.C $ 00039 // $Id: LoMap.C 13958 2010-09-17 11:45:00Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/slam/LoMap.H" 00046 #include "Robots/LoBot/slam/LoOccGrid.H" 00047 #include "Robots/LoBot/slam/LoCoords.H" 00048 #include "Robots/LoBot/slam/LoSlamParams.H" 00049 00050 #include "Robots/LoBot/config/LoConfigHelpers.H" 00051 #include "Robots/LoBot/util/LoGL.H" 00052 #include "Robots/LoBot/util/LoMath.H" 00053 #include "Robots/LoBot/misc/LoTypes.H" 00054 #include "Robots/LoBot/misc/singleton.hh" 00055 #include "Robots/LoBot/util/triple.hh" 00056 00057 // OpenGL headers 00058 #ifdef INVT_HAVE_LIBGLU 00059 #include <GL/glu.h> 00060 #endif 00061 00062 #ifdef INVT_HAVE_LIBGL 00063 #include <GL/gl.h> 00064 #endif 00065 00066 // Boost headers 00067 #include <boost/bind.hpp> 00068 00069 // Standard C++ headers 00070 #include <string> 00071 #include <algorithm> 00072 #include <functional> 00073 #include <iterator> 00074 00075 //----------------------------- NAMESPACE ------------------------------- 00076 00077 namespace lobot { 00078 00079 //-------------------------- KNOB TWIDDLING ----------------------------- 00080 00081 namespace { 00082 00083 // Quick helper to return settings from "map" section of config file 00084 template<typename T> 00085 inline T conf(const std::string& key, T default_value) 00086 { 00087 return get_conf<T>("map", key, default_value) ; 00088 } 00089 00090 // Overload for retrieving triples 00091 template<typename T> 00092 inline triple<T, T, T> 00093 conf(const std::string& key, const triple<T, T, T>& default_value) 00094 { 00095 return get_conf<T>("map", key, default_value) ; 00096 } 00097 00098 /// This local class encapsulates various parameters that can be used to 00099 /// tweak different aspects of the map. 00100 class MapParams : public singleton<MapParams> { 00101 /// Generally, when we draw the map, we also draw the robot's current 00102 /// pose w.r.t. the map. However, in some cases, we may wish to skip 00103 /// drawing the pose. This setting can be used to do that. By default, 00104 /// it is true, i.e., the robot pose is drawn. 00105 bool m_draw_pose ; 00106 00107 /// In addition to the pose, the map also draws some labels on its top 00108 /// left corner. This setting can be used to turn those labels off. By 00109 /// default, they are on. 00110 bool m_draw_labels ; 00111 00112 /// To allow users to better make out the map's dimensions and 00113 /// relative distances between objects, we can draw a grid. This 00114 /// setting can be used to specify the size of the grid's cells, which 00115 /// are all square. 00116 /// 00117 /// The value of this setting should be a floating point number. Its 00118 /// units are millimeters. If the grid spacing is a negative number, 00119 /// the grid will not be drawn. By default, the grid spacing is set to 00120 /// -1 and the grid is not drawn. 00121 float m_grid_spacing ; 00122 00123 /// If the grid is drawn, it will rendered in a light gray color so as 00124 /// to not interfere visually with the other things on the map. This 00125 /// setting specifies the gray level to use when rendering the map. 00126 /// This setting's value is expected to be a unitless number in the 00127 /// range zero to one. Lower values will result in a darker grid, 00128 /// higher numbers in a lighter grid. 00129 float m_grid_color ; 00130 00131 /// This setting controls whether we want to draw a border around the 00132 /// map's drawing area within the Robolocust UI. Usually, all 00133 /// drawables have borders drawn around them so that they are 00134 /// clearly demarcated from each other. However, in some cases, we may 00135 /// not want these borders around the map. By default, this flag is 00136 /// on, i.e., a border is drawn. 00137 bool m_draw_border ; 00138 00139 /// This setting allows users to customize the color in which the 00140 /// map's drawable border is rendered. By default, it is yellow. The 00141 /// value of this setting should be a triple of integers, each in the 00142 /// range [0,255]. 00143 GLColor m_border_color ; 00144 typedef const GLColor& Color ; 00145 00146 /// Private constructor because this is a singleton. 00147 MapParams() ; 00148 friend class singleton<MapParams> ; 00149 00150 public: 00151 /// Accessing the various parameters 00152 //@{ 00153 static bool draw_pose() {return instance().m_draw_pose ;} 00154 static bool draw_labels() {return instance().m_draw_labels ;} 00155 static bool draw_border() {return instance().m_draw_border ;} 00156 static Color border_color() {return instance().m_border_color ;} 00157 static float grid_spacing() {return instance().m_grid_spacing ;} 00158 static float grid_color() {return instance().m_grid_color ;} 00159 static bool draw_grid() {return instance().m_grid_spacing > 0 ;} 00160 //@} 00161 } ; 00162 00163 // Parameter initialization 00164 MapParams::MapParams() 00165 : m_draw_pose(conf("draw_pose", true)), 00166 m_draw_labels(conf("draw_labels", true)), 00167 m_grid_spacing(conf("grid_spacing", -1.0f)), 00168 m_grid_color(clamp(conf("grid_color", 0.75f), 0.25f, 0.95f)), 00169 m_draw_border(conf("draw_border", true)), 00170 m_border_color(conf<int>("border_color", make_triple(255, 255, 0))) 00171 { 00172 if (m_grid_spacing > 0) 00173 m_grid_spacing = clamp(m_grid_spacing, 1.0f, 1000.0f) ; 00174 } 00175 00176 } // end of local anonymous namespace encapsulating above helpers 00177 00178 //-------------------------- INITIALIZATION ----------------------------- 00179 00180 Map::Map() 00181 : Drawable("occupancy_grid", SlamParams::map_geometry()), 00182 m_pose(SlamParams::initial_pose()) 00183 { 00184 const int N = SlamParams::map_width() * SlamParams::map_height() ; 00185 m_grid.reserve(N) ; 00186 std::fill_n(std::back_inserter(m_grid), N, 128) ; 00187 00188 Drawable::m_border = MapParams::draw_border() ; 00189 Drawable::m_border_color = MapParams::border_color() ; 00190 } 00191 00192 void Map::add_pose_hook(const Map::PoseHook& H) 00193 { 00194 AutoMutex M(m_pose_hooks_mutex) ; 00195 m_pose_hooks.push_back(H) ; 00196 } 00197 00198 //---------------------------- MAP UPDATES ------------------------------ 00199 00200 // This function converts the log-odds representation of the map held by 00201 // the FastSLAM particle filter into normal probabilities. However, the 00202 // probabilities are expressed as bytes in the range [0,255] rather 00203 // rather than as floats in the range [0,1]. This makes visualization 00204 // using OpenGL particularly straightforward. 00205 // 00206 // Also: the occupancy grid built by FastSLAM uses p = 1 to indicate a 00207 // cell that is occupied and p = 0 for free space. For visualization, we 00208 // prefer to show free space as white and obstacles as black. Therefore, 00209 // we invert the probabilities held by the FastSLAM occupancy grid so 00210 // that empty areas in the map become 255 and blocked areas are 0. 00211 static unsigned char log_odds_to_byte(float log_odds) 00212 { 00213 return round(255 * (1 - log_odds_to_prob(log_odds))) ; 00214 } 00215 00216 // This method converts the log-odds occupancy grid built by FastSLAM 00217 // into a map that can be visualized easily. 00218 void Map::update(const OccGrid& g) 00219 { 00220 AutoMutex M(m_grid_mutex) ; 00221 std::transform(g.begin(), g.end(), m_grid.begin(), log_odds_to_byte) ; 00222 } 00223 00224 // Quick helper to trigger a pose hook 00225 static void trigger_pose_hook(const Map::PoseHook& H, const Pose& p) 00226 { 00227 H.first(p, H.second) ; 00228 } 00229 00230 // This method records the latest pose returned by FastSLAM 00231 void Map::update(const Pose& p) 00232 { 00233 // First, record the pose. 00234 // 00235 // DEVNOTE: We need to encapsulate this block of code within a pair of 00236 // braces to ensure that the pose write lock is released once it is no 00237 // longer required (we do not need to hold on it for the entire 00238 // duration of this function). 00239 { 00240 AutoWriteLock L(m_pose_lock) ; 00241 m_pose = p ; 00242 } 00243 00244 // Next, trigger the pose update hooks. 00245 // 00246 // DEVNOTE: We don't need the additional braces here because the pose 00247 // hooks auto mutex will go out of scope right after the hooks are 00248 // triggered, thereby releasing the mutex. 00249 AutoMutex M(m_pose_hooks_mutex) ; 00250 std::for_each(m_pose_hooks.begin(), m_pose_hooks.end(), 00251 boost::bind(trigger_pose_hook, _1, p)) ; 00252 } 00253 00254 //---------------------------- MAP ACCESS ------------------------------- 00255 00256 Pose Map::current_pose() const 00257 { 00258 AutoReadLock L(m_pose_lock) ; 00259 return m_pose ; 00260 } 00261 00262 // Extract square portion from map centered at robot's current position 00263 std::vector<unsigned char> Map::submap(int w) const 00264 { 00265 std::vector<unsigned char> M ; 00266 M.reserve(sqr(w * 2 + 1)) ; 00267 00268 int X, Y ; 00269 Pose p = current_pose() ; 00270 Coords::to_grid(p.x(), p.y(), &X, &Y) ; 00271 00272 const int W = SlamParams::map_width() ; 00273 const int H = SlamParams::map_height() ; 00274 00275 AutoMutex mutex(m_grid_mutex) ; 00276 int k = (Y - w) * W + (X - w) ; 00277 for (int y = Y - w; y <= Y + w; ++y, k += W - 2*w - 1) 00278 for (int x = X - w; x <= X + w; ++x, ++k) 00279 { 00280 if (x < 0 || x >= W || y < 0 || y >= H) 00281 M.push_back(0) ; // areas outside map are considered occupied 00282 else 00283 M.push_back(m_grid[k]) ; 00284 } 00285 00286 return M ; 00287 } 00288 00289 //--------------------------- VISUALIZATION ----------------------------- 00290 00291 #ifdef INVT_HAVE_LIBGLU 00292 00293 namespace { 00294 00295 // Helper class for encapsulating the GL textures used to visualize the 00296 // the Robolocust map. 00297 class MapTexture { 00298 unsigned int m_name ; 00299 int m_width, m_height ; 00300 public: 00301 MapTexture() ; 00302 void init(int width, int height) ; 00303 int width() const {return m_width ;} 00304 int height() const {return m_height ;} 00305 void update(const std::vector<unsigned char>&) ; 00306 void render(float left, float right, float bottom, float top) const ; 00307 void cleanup() ; 00308 } ; 00309 00310 MapTexture::MapTexture() 00311 : m_name(0), m_width(0), m_height(0) 00312 {} 00313 00314 void MapTexture::init(int width, int height) 00315 { 00316 m_width = width ; 00317 m_height = height ; 00318 00319 glGenTextures(1, &m_name) ; 00320 00321 glBindTexture(GL_TEXTURE_2D, m_name) ; 00322 glPixelStorei(GL_UNPACK_ALIGNMENT, 1) ; 00323 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) ; 00324 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) ; 00325 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ; 00326 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) ; 00327 00328 const int W = next_power_of_two(m_width) ; 00329 const int H = next_power_of_two(m_height) ; 00330 const int N = W * H ; 00331 GLubyte* texture = new GLubyte[N] ; 00332 std::fill_n(texture, N, GLubyte(0)) ; 00333 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, W, H, 0, 00334 GL_LUMINANCE, GL_UNSIGNED_BYTE, texture) ; 00335 delete[] texture ; 00336 } 00337 00338 // Re-create the GL texture using the latest obstacle map entries 00339 void MapTexture::update(const std::vector<unsigned char>& grid) 00340 { 00341 typedef std::vector<unsigned char> Texture ; 00342 std::vector<unsigned char> texture(grid.size()) ; 00343 00344 // Reverse the grid row-by-row to create GL texture (because GL will 00345 // draw the texture starting at the bottom, which, in our coordinate 00346 // system, will look flipped). 00347 Texture::iterator dst = texture.end() - m_width ; 00348 Texture::const_iterator src = grid.begin() ; 00349 for (; src != grid.end(); src += m_width, dst -= m_width) 00350 std::copy(src, src + m_width, dst) ; 00351 00352 glBindTexture(GL_TEXTURE_2D, m_name) ; 00353 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, 00354 GL_LUMINANCE, GL_UNSIGNED_BYTE, &texture[0]) ; 00355 } 00356 00357 // Render the texture representation of the obstacle map 00358 void MapTexture::render(float left, float right, float bottom, float top) const 00359 { 00360 glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT) ; 00361 glEnable(GL_TEXTURE_2D) ; 00362 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) ; 00363 glBindTexture(GL_TEXTURE_2D, m_name) ; 00364 00365 const float S = static_cast<float>(m_width)/next_power_of_two(m_width) ; 00366 const float T = static_cast<float>(m_height)/next_power_of_two(m_height) ; 00367 glBegin(GL_QUADS) ; 00368 glTexCoord2i(0, 0) ; 00369 glVertex2f(left, bottom) ; 00370 00371 glTexCoord2f(S, 0) ; 00372 glVertex2f(right, bottom) ; 00373 00374 glTexCoord2f(S, T) ; 00375 glVertex2f(right, top) ; 00376 00377 glTexCoord2f(0, T) ; 00378 glVertex2f(left, top) ; 00379 glEnd() ; 00380 00381 glDisable(GL_TEXTURE_2D) ; 00382 glPopAttrib() ; 00383 } 00384 00385 // Clean-up GL texture object 00386 void MapTexture::cleanup() 00387 { 00388 glDeleteTextures(1, &m_name) ; 00389 } 00390 00391 // Helper function for overlaying a spacing grid on the map 00392 void draw_grid(float L, float R, float B, float T) 00393 { 00394 glMatrixMode(GL_PROJECTION) ; 00395 glLoadIdentity() ; 00396 gluOrtho2D(T, B, L, R) ; 00397 00398 glMatrixMode(GL_MODELVIEW) ; 00399 glLoadIdentity() ; 00400 00401 const float C = MapParams::grid_color() ; 00402 const float S = MapParams::grid_spacing() ; 00403 00404 glPushAttrib(GL_CURRENT_BIT) ; 00405 glColor3f(C, C, C) ; 00406 glBegin(GL_LINES) ; 00407 for (float x = T - S; x > B; x -= S) { 00408 glVertex2f(x, L) ; 00409 glVertex2f(x, R) ; 00410 } 00411 for (float y = L + S; y < R; y += S) { 00412 glVertex2f(T, y) ; 00413 glVertex2f(B, y) ; 00414 } 00415 glEnd() ; 00416 glPopAttrib() ; 00417 } 00418 00419 // Helper function for rendering the robot's current pose 00420 void draw_pose(const Pose& P, const Drawable::Geometry& G, const float ext[]) 00421 { 00422 float x = (P.y() - ext[3])/(ext[2] - ext[3]) * (G.width - 1) ; 00423 float y = (P.x() - ext[0])/(ext[1] - ext[0]) * (G.height - 1) ; 00424 00425 glMatrixMode(GL_PROJECTION) ; 00426 glLoadIdentity() ; 00427 gluOrtho2D(0, G.width - 1, 0, G.height - 1) ; 00428 00429 glMatrixMode(GL_MODELVIEW) ; 00430 glLoadIdentity() ; 00431 glTranslatef(x, y, 0) ; 00432 glRotatef(90 + P.heading(), 0, 0, 1) ; 00433 00434 glPushAttrib(GL_CURRENT_BIT) ; 00435 glColor3f(1, 0, 0) ; 00436 glBegin(GL_TRIANGLES) ; 00437 glVertex2i(-10, 0) ; 00438 glVertex2i( 0, 0) ; 00439 glVertex2f(-17.5f, 5.0f) ; 00440 00441 glVertex2i( 0, 0) ; 00442 glVertex2i(-10, 0) ; 00443 glVertex2f(-17.5f, -5.0f) ; 00444 glEnd() ; 00445 glPopAttrib() ; 00446 } 00447 00448 // Quick helper to return a label for the current pose 00449 static std::string pose_label(const Pose& P) 00450 { 00451 std::ostringstream str ; 00452 str << "Pose: (" 00453 << round(P.x()) << ", " 00454 << round(P.y()) << ", " 00455 << round((P.t() < 180) ? P.t() : P.t() - 360) << ')' ; 00456 return str.str() ; 00457 } 00458 00459 // Instantiate a GL texture for the map 00460 MapTexture map_texture ; 00461 00462 } // end of local anonymous namespace encapsulating above helper class 00463 00464 void Map::gl_init() 00465 { 00466 map_texture.init(SlamParams::map_width(), SlamParams::map_height()) ; 00467 } 00468 00469 // This method renders the GL texture used to visualize the map and draws 00470 // the robot's current pose as well. 00471 void Map::render_me() 00472 { 00473 // First, render the occupancy grid 00474 setup_view_volume(0, 1, 0, 1) ; 00475 glRotatef(90, 0, 0, 1) ; 00476 glTranslatef(0, -1, 0) ; 00477 m_grid_mutex.acquire() ; 00478 std::vector<unsigned char> grid = m_grid ; 00479 m_grid_mutex.release() ; 00480 map_texture.update(grid) ; 00481 map_texture.render(0, 1, 0, 1) ; 00482 00483 // Next, draw a grid to help user make out map dimensions 00484 float ext[4] ; // map extents 00485 SlamParams::map_extents(ext) ; 00486 if (MapParams::draw_grid()) 00487 draw_grid(ext[0], ext[1], ext[2], ext[3]) ; 00488 00489 // Then, render the current pose 00490 Pose P = current_pose() ; 00491 if (MapParams::draw_pose()) 00492 draw_pose(P, m_geometry, ext) ; 00493 00494 // Finally, some labels 00495 if (MapParams::draw_labels()) { 00496 restore_view_volume() ; 00497 text_view_volume() ; 00498 glColor3f(1, 0, 0) ; 00499 draw_label(3, 12, "Map") ; 00500 if (MapParams::draw_pose()) 00501 draw_label(3, 24, pose_label(P).c_str()) ; 00502 } 00503 restore_view_volume() ; 00504 } 00505 00506 void Map::gl_cleanup() 00507 { 00508 map_texture.cleanup() ; 00509 } 00510 00511 #endif 00512 00513 //----------------------------------------------------------------------- 00514 00515 } // end of namespace encapsulating this file's definitions 00516 00517 /* So things look consistent in everyone's emacs... */ 00518 /* Local Variables: */ 00519 /* indent-tabs-mode: nil */ 00520 /* End: */