LoMap.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:41:31 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3