ompl_rviz_planner.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Willow Garage nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 /* 
00036    Author: Dave Coleman <dave@dav.ee>
00037    Desc:   Visualize planning with OMPL in Rviz
00038 */
00039 
00040 // ROS
00041 #include <ros/ros.h>
00042 #include <ros/package.h> // for getting file path for loading images
00043 
00044 // For reading image files
00045 #include <ompl_rviz_viewer/utilities/ppm.h>
00046 
00047 // Display in Rviz tool
00048 #include <ompl_rviz_viewer/ompl_rviz_viewer.h>
00049 
00050 // Custom validity checker that accounts for cost
00051 #include <ompl_rviz_viewer/two_dimensional_validity_checker.h>
00052 
00053 // OMPL planner
00054 #include <ompl/geometric/SimpleSetup.h>
00055 #include <ompl/geometric/planners/rrt/RRT.h>
00056 #include <ompl/geometric/planners/rrt/TRRT.h>
00057 
00058 namespace ob = ompl::base;
00059 namespace og = ompl::geometric;
00060 namespace bnu = boost::numeric::ublas;
00061 
00062 namespace ompl_rviz_viewer
00063 {
00064 
00068 class OmplRvizPlanner
00069 {
00070 
00071  private:
00072   // The RGB image data
00073   PPMImage *image_;
00074 
00075   // The cost for each x,y - which is derived from the RGB data
00076   bnu::matrix<int> cost_;
00077 
00078   // The resulting graph that was searched
00079   ob::PlannerDataPtr planner_data_;
00080 
00081   // Save the simple setup until the program ends so that the planner data is not lost
00082   og::SimpleSetupPtr simple_setup_;
00083 
00084   // The visual tools for interfacing with Rviz
00085   ompl_rviz_viewer::OmplRvizViewerPtr viewer_;
00086 
00087   // Remember the min and max cost from the cost_ matrix for color visualization
00088   int max_cost_;
00089   int min_cost_;
00090 
00091   // The cost at which it becomes an obstacle
00092   double max_threshold_;
00093 
00094   // The percentage of the top min/max cost value that is considered an obstacle, e.g. 10 is top 10% of peaks
00095   //  static const double MAX_THRESHOLD_PERCENTAGE_ = 40;
00096   static const double MAX_THRESHOLD_PERCENTAGE_ = 1;
00097 
00098   // The number of dimensions - always 2 for images
00099   static const unsigned int DIMENSIONS = 2;
00100 
00101   // Use random goal and start locations
00102   static const bool USE_RANDOM_STATES = true;
00103 
00104  public:
00105 
00109   OmplRvizPlanner()
00110   {
00111     image_ = NULL;
00112 
00113     bool verbose = true;
00114     viewer_.reset(new ompl_rviz_viewer::OmplRvizViewer(verbose));
00115   }
00116 
00120   ~OmplRvizPlanner()
00121   {
00122     delete image_;
00123   }
00124 
00128   void runImage( std::string image_path )
00129   {
00130     // Load cost map from image file
00131     image_ = readPPM( image_path.c_str() );
00132 
00133     // Error check
00134     if( !image_ )
00135     {
00136       ROS_ERROR( "No image data loaded " );
00137       return;
00138     }
00139 
00140     // Disallow non-square
00141     if( image_->x != image_->y )
00142     {
00143       ROS_ERROR( "Does not currently support non-square images because of some weird bug. Feel free to fork and fix!" );
00144       return;
00145     }
00146 
00147     ROS_INFO_STREAM( "Map Height: " << image_->y << " Map Width: " << image_->x );
00148 
00149     // Create an array of ints that represent the cost of every pixel
00150     cost_.resize( image_->x, image_->y );
00151 
00152     // gets the min and max values of the cost map
00153     getMinMaxCost();
00154 
00155     // Generate the cost map
00156     createCostMap();
00157 
00158     ROS_INFO_STREAM( "OMPL version: " << OMPL_VERSION );
00159 
00160     // OMPL Processing -------------------------------------------------------------------------------------------------
00161     // Run OMPL and display
00162     if( planWithSimpleSetup() )
00163     {
00164       // Make line color
00165       std_msgs::ColorRGBA color;
00166       color.a = 1.0;
00167 
00168       // Render the map ---------------------------------------------------
00169       viewer_->displayTriangles(image_, cost_);
00170       ros::Duration(0.1).sleep();
00171 
00172       // Visualize the explored space ---------------------------------------
00173       viewer_->displayGraph(cost_, planner_data_);
00174       ros::Duration(0.1).sleep();
00175 
00176       // Visualize the sample locations -----------------------------------
00177       viewer_->displaySamples(cost_, planner_data_);
00178       ros::Duration(0.1).sleep();
00179 
00180       // Show basic solution ----------------------------------------
00181       if( false )
00182       {
00183         // Visualize the chosen path
00184         color.r = 1.0;
00185         color.g = 0.0;
00186         color.b = 0.0;
00187         viewer_->displayResult( simple_setup_->getSolutionPath(), &color, cost_ );
00188         ros::Duration(0.25).sleep();
00189       }
00190 
00191       // Interpolate -------------------------------------------------------
00192       if( true )
00193       {
00194         simple_setup_->getSolutionPath().interpolate();
00195 
00196         // Visualize the chosen path
00197         color.r = 0.0;
00198         color.g = 1.0;
00199         color.b = 0.0;
00200         viewer_->displayResult( simple_setup_->getSolutionPath(), &color, cost_ );
00201         //      ros::Duration(0.25).sleep();
00202       }
00203 
00204       // Simplify solution ------------------------------------------------------
00205       if( false )
00206       {
00207         simple_setup_->simplifySolution();
00208 
00209         // Visualize the chosen path
00210         color.r = 0.0;
00211         color.g = 0.5;
00212         color.b = 0.5;
00213         viewer_->displayResult( simple_setup_->getSolutionPath(), &color, cost_ );
00214         ros::Duration(0.25).sleep();
00215       }
00216     }
00217     else // just show the map
00218     {
00219       // Render the map
00220       viewer_->displayTriangles(image_, cost_);
00221       ros::Duration(0.25).sleep();
00222     }
00223 
00224   }
00225 
00226  private:
00227 
00231   void createCostMap()
00232   {
00233     // This factor is the author's visual preference for scaling a cost map in Rviz
00234     const double artistic_scale = 2.0;
00235 
00236     // This scale adapts that factor depending on the cost map min max
00237     const double scale = (max_cost_ - min_cost_ ) / ( image_->x / artistic_scale );
00238 
00239     // Dynamically calculate the obstacle threshold
00240     max_threshold_ = max_cost_ - ( MAX_THRESHOLD_PERCENTAGE_ / 100 * (max_cost_ - min_cost_) );
00241 
00242     // Preprocess the pixel data for cost and give it a nice colored tint
00243     for( size_t i = 0; i < image_->getSize(); ++i )
00244     {
00245       // Calculate cost
00246       cost_.data()[i]  = ( image_->data[ i ].red ) / scale;
00247 
00248       // Prevent cost from being zero
00249       if( !cost_.data()[i] )
00250         cost_.data()[i] = 1;
00251 
00252       // Color different if it is an obstacle
00253       if( cost_.data()[i] > max_threshold_ )
00254       {
00255         image_->data[ i ].red = 255; //image_->data[ i ].red;
00256         image_->data[ i ].green = image_->data[ i ].green;
00257         image_->data[ i ].blue = image_->data[ i ].blue;
00258       }
00259 
00260     }
00261 
00262   }
00263 
00267   void getMinMaxCost()
00268   {
00269     // Find the min and max cost from the image
00270     min_cost_ = image_->data[ 0 ].red;
00271     max_cost_ = image_->data[ 0 ].red;
00272 
00273     for( size_t i = 0; i < image_->getSize(); ++i )
00274     {
00275       // Max
00276       if( image_->data[ i ].red > max_cost_ )
00277         max_cost_ = image_->data[ i ].red;
00278       // Min
00279       else if( image_->data[ i ].red < min_cost_ )
00280         min_cost_ = image_->data[ i ].red;
00281     }
00282   }
00283 
00289   void showStartGoal(ob::ScopedState<> start, ob::ScopedState<> goal)
00290   {
00291     // Modify the map to show start and end locations
00292     image_->data[ image_->getID( start[0], start[1] ) ].red = 50;
00293     image_->data[ image_->getID( start[0], start[1] ) ].blue = 50;
00294     image_->data[ image_->getID( start[0], start[1] ) ].green = 255;
00295 
00296     image_->data[ image_->getID( goal[0], goal[1] ) ].red = 255;
00297     image_->data[ image_->getID( goal[0], goal[1] ) ].blue = 255;
00298     image_->data[ image_->getID( goal[0], goal[1] ) ].green = 10;
00299   }
00300 
00304   bool planWithSimpleSetup()
00305   {
00306     // Construct the state space we are planning in
00307     ob::StateSpacePtr space( new ob::RealVectorStateSpace( DIMENSIONS ));
00308 
00309     // Set the bounds for the R^2
00310     ob::RealVectorBounds bounds( DIMENSIONS );
00311     bounds.setLow( 0 ); // both dimensions start at 0
00312     bounds.setHigh( 0, image_->x - 1 ); // allow for non-square images
00313     bounds.setHigh( 1, image_->y - 1 ); // allow for non-square images
00314     space->as<ob::RealVectorStateSpace>()->setBounds( bounds );
00315 
00316     // Define a simple setup class ---------------------------------------
00317     simple_setup_ = og::SimpleSetupPtr( new og::SimpleSetup(space) );
00318 
00319     // Set the setup planner (TRRT)
00320     og::TRRT *trrt = new og::TRRT( simple_setup_->getSpaceInformation() );
00321     //og::RRT *trrt = new og::RRT( simple_setup_->getSpaceInformation() );
00322 
00323     simple_setup_->setPlanner(ob::PlannerPtr(trrt));
00324 
00325     // Set state validity checking for this space
00326     simple_setup_->setStateValidityChecker( ob::StateValidityCheckerPtr( new TwoDimensionalValidityChecker( simple_setup_->getSpaceInformation(), cost_, max_threshold_ ) ) );
00327 
00328     // Start and Goal State ---------------------------------------------
00329 
00330     // Create start space
00331     ob::ScopedState<> start(space);
00332     if( USE_RANDOM_STATES )
00333     {
00334       start.random();
00335     }
00336     else // Manually set the start location
00337     {
00338       start[0] = 95;
00339       start[1] = 10;
00340     }
00341 
00342     // Create a goal state
00343     ob::ScopedState<> goal(space);
00344     if( USE_RANDOM_STATES )
00345     {
00346       goal.random();
00347     }
00348     else // Manually set the start location
00349     {
00350       goal[0] = 40;
00351       goal[1] = 300;
00352     }
00353 
00354     // Visualize on map
00355     showStartGoal(start, goal);
00356 
00357     // set the start and goal states
00358     simple_setup_->setStartAndGoalStates(start, goal);
00359 
00360     // Setup -----------------------------------------------------------
00361 
00362     // Auto setup parameters (optional actually)
00363     simple_setup_->setup();
00364 
00365     // The interval in which obstacles are checked for between states
00366     // simple_setup_->getSpaceInformation()->setStateValidityCheckingResolution(0.005);
00367 
00368     // Debug - this call is optional, but we put it in to get more output information
00369     simple_setup_->print();
00370 
00371     // Solve -----------------------------------------------------------
00372     ROS_INFO( "Starting OMPL motion planner..." );
00373 
00374     // attempt to solve the problem within x seconds of planning time
00375     ob::PlannerStatus solved = simple_setup_->solve( 10.0 );
00376 
00377     if (solved)
00378     {
00379       ROS_INFO("Solution Found");
00380 
00381       // Get information about the exploration data structure the motion planner used. Used later in visualizing
00382       planner_data_.reset( new ob::PlannerData( simple_setup_->getSpaceInformation() ) );
00383       simple_setup_->getPlannerData( *planner_data_ );
00384     }
00385     else
00386     {
00387       ROS_INFO("No Solution Found");
00388     }
00389 
00390     return solved;
00391   }
00392 
00393 }; // end of class
00394 
00395 } // namespace
00396 
00397 
00398 // *********************************************************************************************************
00399 // Main
00400 // *********************************************************************************************************
00401 int main( int argc, char** argv )
00402 {
00403   ros::init(argc, argv, "ompl_rviz_viewer");
00404   ROS_INFO( "OMPL RViz Viewer ----------------------------------------- " );
00405 
00406   // Allow the action server to recieve and send ros messages
00407   ros::AsyncSpinner spinner(1);
00408   spinner.start();
00409 
00410   bool verbose = true;
00411 
00412   std::string image_path;
00413 
00414   // Check if user has passed in an image to read
00415   if( argc > 1 )
00416   {
00417     image_path = argv[1];
00418   }
00419   else // use default image
00420   {
00421     // Get image path based on package name
00422     image_path = ros::package::getPath("ompl_rviz_viewer");
00423     if( image_path.empty() )
00424     {
00425       ROS_ERROR( "Unable to get OMPL RViz Viewer package path " );
00426       return false;
00427     }
00428 
00429     // Seed random
00430     srand ( time(NULL) );
00431 
00432     // Choose random image
00433     switch( 0 ) //rand() % 4 )
00434     {
00435       case 0:
00436         image_path.append( "/resources/grand_canyon.ppm" );
00437         break;
00438       case 1:
00439         image_path.append( "/resources/height_map0.ppm" );
00440         break;
00441       case 2:
00442         image_path.append( "/resources/height_map1.ppm" );
00443         break;
00444       case 3:
00445         image_path.append( "/resources/height_map2.ppm" );
00446         break;
00447     }
00448   }
00449 
00450   // Create the planner
00451   ompl_rviz_viewer::OmplRvizPlanner planner;
00452 
00453   // Load an image and run the planner
00454   ROS_INFO_STREAM_NAMED("main","Loading image " << image_path);
00455   planner.runImage( image_path );
00456 
00457   // Wait to let anything still being published finish
00458   ros::Duration(1).sleep();
00459 
00460   ROS_INFO_STREAM("Shutting down. TESTING HERE");
00461 
00462   return 0;
00463 }
00464 
00465 


ompl_rviz_viewer
Author(s): Dave Coleman
autogenerated on Thu Jul 17 2014 18:58:04