(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Creating a New Layer

Description: How to create a new layer in Hydro+ navigation

Keywords: navigation

Tutorial Level: INTERMEDIATE

Simple Layer

Here we'll create a simple layer that just puts a lethal point on the costmap one meter in front of the robot.

Create the Package

catkin_create_pkg simple_layers roscpp costmap_2d dynamic_reconfigure

roscreate-pkg simple_layers roscpp costmap_2d dynamic_reconfigure

Header File

   1 #ifndef SIMPLE_LAYER_H_
   2 #define SIMPLE_LAYER_H_
   3 #include <ros/ros.h>
   4 #include <costmap_2d/layer.h>
   5 #include <costmap_2d/layered_costmap.h>
   6 #include <costmap_2d/GenericPluginConfig.h>
   7 #include <dynamic_reconfigure/server.h>
   8 
   9 namespace simple_layer_namespace
  10 {
  11 
  12 class SimpleLayer : public costmap_2d::Layer
  13 {
  14 public:
  15   SimpleLayer();
  16 
  17   virtual void onInitialize();
  18   virtual void updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x, double* min_y, double* max_x,
  19                              double* max_y);
  20   virtual void updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j);
  21 
  22 private:
  23   void reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level);
  24 
  25   double mark_x_, mark_y_;
  26   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig> *dsrv_;
  27 };
  28 }
  29 #endif
  30 

Main Source Code

   1 #include<simple_layers/simple_layer.h>
   2 #include <pluginlib/class_list_macros.h>
   3 
   4 PLUGINLIB_EXPORT_CLASS(simple_layer_namespace::SimpleLayer, costmap_2d::Layer)
   5 
   6 using costmap_2d::LETHAL_OBSTACLE;
   7 
   8 namespace simple_layer_namespace
   9 {
  10 
  11 SimpleLayer::SimpleLayer() {}
  12 
  13 void SimpleLayer::onInitialize()
  14 {
  15   ros::NodeHandle nh("~/" + name_);
  16   current_ = true;
  17 
  18   dsrv_ = new dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>(nh);
  19   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>::CallbackType cb = boost::bind(
  20       &SimpleLayer::reconfigureCB, this, _1, _2);
  21   dsrv_->setCallback(cb);
  22 }
  23 
  24 
  25 void SimpleLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level)
  26 {
  27   enabled_ = config.enabled;
  28 }
  29 
  30 void SimpleLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x,
  31                                            double* min_y, double* max_x, double* max_y)
  32 {
  33   if (!enabled_)
  34     return;
  35 
  36   mark_x_ = robot_x + cos(robot_yaw);
  37   mark_y_ = robot_y + sin(robot_yaw);
  38 
  39   *min_x = std::min(*min_x, mark_x_);
  40   *min_y = std::min(*min_y, mark_y_);
  41   *max_x = std::max(*max_x, mark_x_);
  42   *max_y = std::max(*max_y, mark_y_);
  43 }
  44 
  45 void SimpleLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i,
  46                                           int max_j)
  47 {
  48   if (!enabled_)
  49     return;
  50   unsigned int mx;
  51   unsigned int my;
  52   if(master_grid.worldToMap(mark_x_, mark_y_, mx, my)){
  53     master_grid.setCost(mx, my, LETHAL_OBSTACLE);
  54   }
  55 }
  56 
  57 } // end namespace
  58 

Initial Explanation

Use of Dynamic Reconfigure

This layer uses the GenericPluginConfig which consists of only a flag called enabled, for easy enabling and disabling of this particular layer. You can create your own custom dynamic_reconfigure configuration and insert it instead.

updateBounds

The updateBounds method does not change the costmap just yet. It just defines the area that will need to be updated. We calculate the point we want to change (mark_x_, mark_y_) and then expand the min/max bounds to be sure it includes the new point.

updateCosts

First, we calculate which grid cell our point is in using worldToMap. Then we set the cost of that cell. Pretty simple.

Compiling the Layer

Add the following to your CMakeLists.txt

rosbuild_add_library(simple_layer src/simple_layer.cpp)

include_directories(${catkin_INCLUDE_DIRS} include)

add_library(simple_layer src/simple_layer.cpp)

Plugin Declaration

Create an XML file, which here we're calling costmap_plugins.xml

   1 <library path="lib/libsimple_layer">
   2   <class type="simple_layer_namespace::SimpleLayer" base_class_type="costmap_2d::Layer">
   3     <description>Demo Layer that adds a point 1 meter in front of the robot</description>
   4   </class>
   5 </library>

Changes to Metadata

These lines need to be added to your manifest.xml to get the list of plugins to be registered.

These lines need to be added to your package.xml to get the list of plugins to be registered.

   1   <export>
   2     <costmap_2d plugin="${prefix}/costmap_plugins.xml" />
   3   </export>

Grid Layer

The previous 'layer' did not actually store any information other than the single point needed. If you want to store a full costmap in the layer, the approach may look something like this.

New Include

   1 #ifndef GRID_LAYER_H_
   2 #define GRID_LAYER_H_
   3 #include <ros/ros.h>
   4 #include <costmap_2d/layer.h>
   5 #include <costmap_2d/layered_costmap.h>
   6 #include <costmap_2d/GenericPluginConfig.h>
   7 #include <dynamic_reconfigure/server.h>
   8 
   9 namespace simple_layer_namespace
  10 {
  11 
  12 class GridLayer : public costmap_2d::Layer, public costmap_2d::Costmap2D
  13 {
  14 public:
  15   GridLayer();
  16 
  17   virtual void onInitialize();
  18   virtual void updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x, double* min_y, double* max_x,
  19                              double* max_y);
  20   virtual void updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j);
  21   bool isDiscretized()
  22   {
  23     return true;
  24   }
  25 
  26   virtual void matchSize();
  27 
  28 private:
  29   void reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level);
  30   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig> *dsrv_;
  31 };
  32 }
  33 #endif
  34 

Two key differences:

  • This layer now extends the Costmap2D class
  • isDiscretized is set to True (used for resizing)

New Source Code

   1 #include<simple_layers/grid_layer.h>
   2 #include <pluginlib/class_list_macros.h>
   3 
   4 PLUGINLIB_EXPORT_CLASS(simple_layer_namespace::GridLayer, costmap_2d::Layer)
   5 
   6 using costmap_2d::LETHAL_OBSTACLE;
   7 using costmap_2d::NO_INFORMATION;
   8 
   9 namespace simple_layer_namespace
  10 {
  11 
  12 GridLayer::GridLayer() {}
  13 
  14 void GridLayer::onInitialize()
  15 {
  16   ros::NodeHandle nh("~/" + name_);
  17   current_ = true;
  18   default_value_ = NO_INFORMATION;
  19   matchSize();
  20 
  21   dsrv_ = new dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>(nh);
  22   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>::CallbackType cb = boost::bind(
  23       &GridLayer::reconfigureCB, this, _1, _2);
  24   dsrv_->setCallback(cb);
  25 }
  26 
  27 
  28 void GridLayer::matchSize()
  29 {
  30   Costmap2D* master = layered_costmap_->getCostmap();
  31   resizeMap(master->getSizeInCellsX(), master->getSizeInCellsY(), master->getResolution(),
  32             master->getOriginX(), master->getOriginY());
  33 }
  34 
  35 
  36 void GridLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level)
  37 {
  38   enabled_ = config.enabled;
  39 }
  40 
  41 void GridLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x,
  42                                            double* min_y, double* max_x, double* max_y)
  43 {
  44   if (!enabled_)
  45     return;
  46 
  47   double mark_x = robot_x + cos(robot_yaw), mark_y = robot_y + sin(robot_yaw);
  48   unsigned int mx;
  49   unsigned int my;
  50   if(worldToMap(mark_x, mark_y, mx, my)){
  51     setCost(mx, my, LETHAL_OBSTACLE);
  52   }
  53   
  54   *min_x = std::min(*min_x, mark_x);
  55   *min_y = std::min(*min_y, mark_y);
  56   *max_x = std::max(*max_x, mark_x);
  57   *max_y = std::max(*max_y, mark_y);
  58 }
  59 
  60 void GridLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i,
  61                                           int max_j)
  62 {
  63   if (!enabled_)
  64     return;
  65 
  66   for (int j = min_j; j < max_j; j++)
  67   {
  68     for (int i = min_i; i < max_i; i++)
  69     {
  70       int index = getIndex(i, j);
  71       if (costmap_[index] == NO_INFORMATION)
  72         continue;
  73       master_grid.setCost(i, j, costmap_[index]); 
  74     }
  75   }
  76 }
  77 
  78 } // end namespace
  79 

Fairly similar, but note the differences:

  • In initialization, we set default_value_ and call matchSize. Match size matches the size of the master grid and fills it with the value specified in default_value_, which by default is zero.
  • We also implement the matchSize method to do what we said above. This is left to the user to implement for cases where additional adjustments need to be made.
  • Instead of setting the value in updateCosts, we set the value in the layer's own costmap using the setCost method.
  • Now in the updateCosts method, we overwrite the previous values of the master costmap to include our marks. We do not overwrite if the layer's costmap value is NO_INFORMATION.

Additional Changes

You must build the new layer similarly in CMakelists.txt.

rosbuild_add_library(grid_layer src/grid_layer.cpp)

add_library(grid_layer src/grid_layer.cpp)

Also, costmap_plugins.xml is updated to now include both libraries.

   1 <class_libraries>
   2   <library path="lib/libsimple_layer">
   3     <class type="simple_layer_namespace::SimpleLayer" base_class_type="costmap_2d::Layer">
   4       <description>Demo Layer that adds a point 1 meter in front of the robot</description>
   5     </class>
   6   </library>
   7   <library path="lib/libgrid_layer">
   8     <class type="simple_layer_namespace::GridLayer" base_class_type="costmap_2d::Layer">
   9       <description>Demo Layer that marks all points that were ever one meter in front of the robot</description>
  10     </class>
  11   </library>
  12 </class_libraries>

Wiki: costmap_2d/Tutorials/Creating a New Layer (last edited 2015-06-16 22:53:51 by DavidLu)