'No map received' in RVIZ when using a custom costmap2d-layer as plugin
Hello there,
I'm having an occupancy grid which has intermediate values from 1-100 and want it to be transformed to a costmap with also intermediate values. I've noticed that this doesn't seem to be possible with the given layers (static layer, inflation layer, obstacle layer, ...)
Therefore, I tried to write a custom layer for which I took the source code of the static_layer and replaced the interpretValue-function in order to map the 100 values of the occupancygrid to the 255 values of the costmap. I integrated my custom plugin into the globalcostmapparams.yaml and the system appears to properly load the plugin (at least there are no further errors or warnings that it couldn't be loaded).
The problem is: In RVIZ the global costmap - section throws a warning which says "No map received" (The Topic is '/movebase/globalcostmap/costmap' which works fine when static_layer is set as plugin). As a result of that, I can only see the coordinate system, but no map.
I'm using ROS Melodic.
Plugin source code (occgridtocostmap_layer.cpp):
#include<custom_layers/occgrid_to_costmap_layer.h>
#include <ros/ros.h>
#include <costmap_2d/costmap_layer.h>
#include <costmap_2d/layered_costmap.h>
#include <costmap_2d/GenericPluginConfig.h>
#include <costmap_2d/static_layer.h>
#include <costmap_2d/costmap_math.h>
#include <tf2/LinearMath/Transform.h>
#include <dynamic_reconfigure/server.h>
#include <nav_msgs/OccupancyGrid.h>
#include <map_msgs/OccupancyGridUpdate.h>
#include <message_filters/subscriber.h>
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(occgrid_to_costmap_layer_namespace::OTCLayer, costmap_2d::Layer)
using costmap_2d::NO_INFORMATION;
using costmap_2d::LETHAL_OBSTACLE;
using costmap_2d::FREE_SPACE;
namespace occgrid_to_costmap_layer_namespace
{
OTCLayer::OTCLayer() : dsrv_(NULL) {}
OTCLayer::~OTCLayer()
{
if (dsrv_)
delete dsrv_;
}
void OTCLayer::onInitialize()
{
ros::NodeHandle nh("~/" + name_), g_nh;
current_ = true;
global_frame_ = layered_costmap_->getGlobalFrameID();
std::string map_topic;
nh.param("map_topic", map_topic, std::string("map"));
nh.param("first_map_only", first_map_only_, false);
nh.param("subscribe_to_updates", subscribe_to_updates_, false);
nh.param("track_unknown_space", track_unknown_space_, true);
nh.param("use_maximum", use_maximum_, false);
int temp_lethal_threshold, temp_unknown_cost_value;
nh.param("lethal_cost_threshold", temp_lethal_threshold, int(100));
nh.param("unknown_cost_value", temp_unknown_cost_value, int(-1));
nh.param("trinary_costmap", trinary_costmap_, true);
lethal_threshold_ = std::max(std::min(temp_lethal_threshold, 100), 0);
unknown_cost_value_ = temp_unknown_cost_value;
// Only resubscribe if topic has changed
if (map_sub_.getTopic() != ros::names::resolve(map_topic))
{
// we'll subscribe to the latched topic that the map server uses
ROS_INFO("Requesting the map...");
map_sub_ = g_nh.subscribe(map_topic, 1, &OTCLayer::incomingMap, this);
map_received_ = false;
has_updated_data_ = false;
ros::Rate r(10);
while (!map_received_ && g_nh.ok())
{
ros::spinOnce();
r.sleep();
}
ROS_INFO("Received a %d X %d map at %f m/pix", getSizeInCellsX(), getSizeInCellsY(), getResolution());
if (subscribe_to_updates_)
{
ROS_INFO("Subscribing to updates");
map_update_sub_ = g_nh.subscribe(map_topic + "_updates", 10, &OTCLayer::incomingUpdate, this);
}
}
else
{
has_updated_data_ = true;
}
if (dsrv_)
{
delete dsrv_;
}
dsrv_ = new dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>(nh);
dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>::CallbackType cb = boost::bind(
&OTCLayer::reconfigureCB, this, _1, _2);
dsrv_->setCallback(cb);
}
void OTCLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level)
{
if (config.enabled != enabled_)
{
enabled_ = config.enabled;
has_updated_data_ = true;
x_ = y_ = 0;
width_ = size_x_;
height_ = size_y_;
}
}
void OTCLayer::matchSize()
{
// If we are using rolling costmap, the static map size is
// unrelated to the size of the layered costmap
if (!layered_costmap_->isRolling())
{
Costmap2D* master = layered_costmap_->getCostmap();
resizeMap(master->getSizeInCellsX(), master->getSizeInCellsY(), master->getResolution(),
master->getOriginX(), master->getOriginY());
}
}
unsigned char OTCLayer::interpretValue(unsigned char value)
{
if(value==-1){
return 255;
}
else if(value==0){
return 0;
}
else if(value==100){
return 254;
}
else{
int stepsize = 254/100;
return int((254-100*stepsize)+stepsize*value);
}
}
void OTCLayer::incomingMap(const nav_msgs::OccupancyGridConstPtr& new_map)
{
unsigned int size_x = new_map->info.width, size_y = new_map->info.height;
ROS_DEBUG("Received a %d X %d map at %f m/pix", size_x, size_y, new_map->info.resolution);
// resize costmap if size, resolution or origin do not match
Costmap2D* master = layered_costmap_->getCostmap();
if (!layered_costmap_->isRolling() &&
(master->getSizeInCellsX() != size_x ||
master->getSizeInCellsY() != size_y ||
master->getResolution() != new_map->info.resolution ||
master->getOriginX() != new_map->info.origin.position.x ||
master->getOriginY() != new_map->info.origin.position.y))
{
// Update the size of the layered costmap (and all layers, including this one)
ROS_INFO("Resizing costmap to %d X %d at %f m/pix", size_x, size_y, new_map->info.resolution);
layered_costmap_->resizeMap(size_x, size_y, new_map->info.resolution, new_map->info.origin.position.x,
new_map->info.origin.position.y,
true /* set size_locked to true, prevents reconfigureCb from overriding map size*/);
}
else if (size_x_ != size_x || size_y_ != size_y ||
resolution_ != new_map->info.resolution ||
origin_x_ != new_map->info.origin.position.x ||
origin_y_ != new_map->info.origin.position.y)
{
// only update the size of the costmap stored locally in this layer
ROS_INFO("Resizing static layer to %d X %d at %f m/pix", size_x, size_y, new_map->info.resolution);
resizeMap(size_x, size_y, new_map->info.resolution,
new_map->info.origin.position.x, new_map->info.origin.position.y);
}
unsigned int index = 0;
// initialize the costmap with static data
for (unsigned int i = 0; i < size_y; ++i)
{
for (unsigned int j = 0; j < size_x; ++j)
{
unsigned char value = new_map->data[index];
costmap_[index] = interpretValue(value);
++index;
}
}
map_frame_ = new_map->header.frame_id;
// we have a new map, update full size of map
x_ = y_ = 0;
width_ = size_x_;
height_ = size_y_;
map_received_ = true;
has_updated_data_ = true;
// shutdown the map subscrber if firt_map_only_ flag is on
if (first_map_only_)
{
ROS_INFO("Shutting down the map subscriber. first_map_only flag is on");
map_sub_.shutdown();
}
}
void OTCLayer::incomingUpdate(const map_msgs::OccupancyGridUpdateConstPtr& update)
{
unsigned int di = 0;
for (unsigned int y = 0; y < update->height ; y++)
{
unsigned int index_base = (update->y + y) * size_x_;
for (unsigned int x = 0; x < update->width ; x++)
{
unsigned int index = index_base + x + update->x;
costmap_[index] = interpretValue(update->data[di++]);
}
}
x_ = update->x;
y_ = update->y;
width_ = update->width;
height_ = update->height;
has_updated_data_ = true;
}
void OTCLayer::activate()
{
onInitialize();
}
void OTCLayer::deactivate()
{
map_sub_.shutdown();
if (subscribe_to_updates_)
map_update_sub_.shutdown();
}
void OTCLayer::reset()
{
if (first_map_only_)
{
has_updated_data_ = true;
}
else
{
onInitialize();
}
}
void OTCLayer::updateBounds(double robot_x, double robot_y, double robot_yaw, double* min_x, double* min_y,
double* max_x, double* max_y)
{
if( !layered_costmap_->isRolling() ){
if (!map_received_ || !(has_updated_data_ || has_extra_bounds_))
return;
}
useExtraBounds(min_x, min_y, max_x, max_y);
double wx, wy;
mapToWorld(x_, y_, wx, wy);
*min_x = std::min(wx, *min_x);
*min_y = std::min(wy, *min_y);
mapToWorld(x_ + width_, y_ + height_, wx, wy);
*max_x = std::max(wx, *max_x);
*max_y = std::max(wy, *max_y);
has_updated_data_ = false;
}
void OTCLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j)
{
if (!map_received_)
return;
if (!enabled_)
return;
if (!layered_costmap_->isRolling())
{
// if not rolling, the layered costmap (master_grid) has same coordinates as this layer
if (!use_maximum_)
updateWithTrueOverwrite(master_grid, min_i, min_j, max_i, max_j);
else
updateWithMax(master_grid, min_i, min_j, max_i, max_j);
}
else
{
// If rolling window, the master_grid is unlikely to have same coordinates as this layer
unsigned int mx, my;
double wx, wy;
// Might even be in a different frame
geometry_msgs::TransformStamped transform;
try
{
transform = tf_->lookupTransform(map_frame_, global_frame_, ros::Time(0));
}
catch (tf2::TransformException ex)
{
ROS_ERROR("%s", ex.what());
return;
}
// Copy map data given proper transformations
tf2::Transform tf2_transform;
tf2::convert(transform.transform, tf2_transform);
for (unsigned int i = min_i; i < max_i; ++i)
{
for (unsigned int j = min_j; j < max_j; ++j)
{
// Convert master_grid coordinates (i,j) into global_frame_(wx,wy) coordinates
layered_costmap_->getCostmap()->mapToWorld(i, j, wx, wy);
// Transform from global_frame_ to map_frame_
tf2::Vector3 p(wx, wy, 0);
p = tf2_transform*p;
// Set master_grid with cell from map
if (worldToMap(p.x(), p.y(), mx, my))
{
if (!use_maximum_)
master_grid.setCost(i, j, getCost(mx, my));
else
master_grid.setCost(i, j, std::max(getCost(mx, my), master_grid.getCost(i, j)));
}
}
}
}
}
} // namespace occgrid_to_costmap_layer_namespace
Plugin XML (costmap_plugins.xml):
<library path="lib/liboccgrid_to_costmap_layer">
<class type="occgrid_to_costmap_layer_namespace::OTCLayer" base_class_type="costmap_2d::Layer">
<description>Layer that maps occupancygrid to costmap with all intermediate values.</description>
</class>
</library>
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.2)
project(custom_layers)
add_compile_options(-std=c++11)
find_package(catkin REQUIRED COMPONENTS
costmap_2d
dynamic_reconfigure
map_msgs
message_filters
nav_msgs
roscpp
pluginlib
tf2
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_library(occgrid_to_costmap_layer
src/occgrid_to_costmap_layer.cpp
)
package.xml:
<?xml version="1.0"?>
<package format="2">
<name>custom_layers</name>
<version>0.0.0</version>
<description>The custom_layers package</description>
<maintainer email="faps@todo.todo">faps</maintainer>
<license>TODO</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>costmap_2d</build_depend>
<build_depend>dynamic_reconfigure</build_depend>
<build_depend>map_msgs</build_depend>
<build_depend>message_filters</build_depend>
<build_depend>nav_msgs</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>pluginlib</build_depend>
<build_depend>tf2</build_depend>
<build_export_depend>costmap_2d</build_export_depend>
<build_export_depend>dynamic_reconfigure</build_export_depend>
<build_export_depend>map_msgs</build_export_depend>
<build_export_depend>message_filters</build_export_depend>
<build_export_depend>nav_msgs</build_export_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>pluginlib</build_export_depend>
<build_export_depend>tf2</build_export_depend>
<exec_depend>costmap_2d</exec_depend>
<exec_depend>dynamic_reconfigure</exec_depend>
<exec_depend>map_msgs</exec_depend>
<exec_depend>message_filters</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>pluginlib</exec_depend>
<exec_depend>tf2</exec_depend>
<export>
<costmap_2d plugin="${prefix}/costmap_plugins.xml"/>
</export>
</package>
Thanks for any help!
Asked by Zimba96 on 2020-06-03 01:11:18 UTC
Answers
Turned out the plugins somehow aren't able to read the given params from global_costmap_params.yaml. Changing the default-value from
nh.param("map_topic", map_topic, std::string("map"));
to
nh.param("map_topic", map_topic, std::string("insert_required_topic_here"));
solved the issue.
Since this isn't the clean way to do it, any ideas what could be the causes that the plugin doesn't read the params properly?
Asked by Zimba96 on 2020-06-05 06:38:03 UTC
Comments
I would run rosparam get /move_base
and make sure parameters are being loaded where you think they are being loaded. Failing that, post them here.
Asked by David Lu on 2020-06-05 16:53:44 UTC
Comments
Doesn't answer your direct question, but have you tried setting
trinary_costmap
to false?Asked by David Lu on 2020-06-04 17:03:43 UTC
I just figured out that the main problem is that my plugins somehow aren't able to read the params from the global_costmap_params.yaml (see my answer). Any ideas on how to fix that? Thanks for your answer btw!
Asked by Zimba96 on 2020-06-05 06:33:23 UTC
Btw, the reason that I wrote this custom plugin was, that the static_map couldn't represent intermediate values by setting the trinary_costmap-param. Turned out that it was the same issue: Instead of using the set trinary_costmap-value "false" of the global_costmap_params.yaml it somehow always used the default-value "true". Manually changing the default value solved the problem..
Asked by Zimba96 on 2020-06-05 06:41:04 UTC