add humble-navigation2

This commit is contained in:
X-lanni
2025-05-27 19:03:40 +08:00
parent 974abb5e1e
commit e74ec539c2
1280 changed files with 204114 additions and 0 deletions
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2012, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* author: Dave Hershberger
*/
#include <cstdio> // for EOF
#include <string>
#include <sstream>
#include <vector>
namespace nav2_costmap_2d
{
/** @brief Parse a vector of vector of floats from a string.
* @param input
* @param error_return
* Syntax is [[1.0, 2.0], [3.3, 4.4, 5.5], ...] */
std::vector<std::vector<float>> parseVVF(const std::string & input, std::string & error_return)
{
std::vector<std::vector<float>> result;
std::stringstream input_ss(input);
int depth = 0;
std::vector<float> current_vector;
while (!!input_ss && !input_ss.eof()) {
switch (input_ss.peek()) {
case EOF:
break;
case '[':
depth++;
if (depth > 2) {
error_return = "Array depth greater than 2";
return result;
}
input_ss.get();
current_vector.clear();
break;
case ']':
depth--;
if (depth < 0) {
error_return = "More close ] than open [";
return result;
}
input_ss.get();
if (depth == 1) {
result.push_back(current_vector);
}
break;
case ',':
case ' ':
case '\t':
input_ss.get();
break;
default: // All other characters should be part of the numbers.
if (depth != 2) {
std::stringstream err_ss;
err_ss << "Numbers at depth other than 2. Char was '" << char(input_ss.peek()) << "'.";
error_return = err_ss.str();
return result;
}
float value;
input_ss >> value;
if (!!input_ss) {
current_vector.push_back(value);
}
break;
}
}
if (depth != 0) {
error_return = "Unterminated vector string.";
} else {
error_return = "";
}
return result;
}
} // end namespace nav2_costmap_2d
@@ -0,0 +1,167 @@
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include <string>
#include <algorithm>
#include <memory>
#include "nav2_costmap_2d/clear_costmap_service.hpp"
#include "nav2_costmap_2d/costmap_2d_ros.hpp"
namespace nav2_costmap_2d
{
using std::vector;
using std::string;
using std::shared_ptr;
using std::any_of;
using ClearExceptRegion = nav2_msgs::srv::ClearCostmapExceptRegion;
using ClearAroundRobot = nav2_msgs::srv::ClearCostmapAroundRobot;
using ClearEntirely = nav2_msgs::srv::ClearEntireCostmap;
ClearCostmapService::ClearCostmapService(
const nav2_util::LifecycleNode::WeakPtr & parent,
Costmap2DROS & costmap)
: costmap_(costmap)
{
auto node = parent.lock();
logger_ = node->get_logger();
reset_value_ = costmap_.getCostmap()->getDefaultValue();
clear_except_service_ = node->create_service<ClearExceptRegion>(
"clear_except_" + costmap_.getName(),
std::bind(
&ClearCostmapService::clearExceptRegionCallback, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
clear_around_service_ = node->create_service<ClearAroundRobot>(
"clear_around_" + costmap.getName(),
std::bind(
&ClearCostmapService::clearAroundRobotCallback, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
clear_entire_service_ = node->create_service<ClearEntirely>(
"clear_entirely_" + costmap_.getName(),
std::bind(
&ClearCostmapService::clearEntireCallback, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
ClearCostmapService::~ClearCostmapService()
{
// make sure services shutdown.
clear_except_service_.reset();
clear_around_service_.reset();
clear_entire_service_.reset();
}
void ClearCostmapService::clearExceptRegionCallback(
const shared_ptr<rmw_request_id_t>/*request_header*/,
const shared_ptr<ClearExceptRegion::Request> request,
const shared_ptr<ClearExceptRegion::Response>/*response*/)
{
RCLCPP_INFO(
logger_, "%s",
("Received request to clear except a region the " + costmap_.getName()).c_str());
clearRegion(request->reset_distance, true);
}
void ClearCostmapService::clearAroundRobotCallback(
const shared_ptr<rmw_request_id_t>/*request_header*/,
const shared_ptr<ClearAroundRobot::Request> request,
const shared_ptr<ClearAroundRobot::Response>/*response*/)
{
clearRegion(request->reset_distance, false);
}
void ClearCostmapService::clearEntireCallback(
const std::shared_ptr<rmw_request_id_t>/*request_header*/,
const std::shared_ptr<ClearEntirely::Request>/*request*/,
const std::shared_ptr<ClearEntirely::Response>/*response*/)
{
RCLCPP_INFO(
logger_, "%s",
("Received request to clear entirely the " + costmap_.getName()).c_str());
clearEntirely();
}
void ClearCostmapService::clearRegion(const double reset_distance, bool invert)
{
double x, y;
if (!getPosition(x, y)) {
RCLCPP_ERROR(
logger_, "%s",
"Cannot clear map because robot pose cannot be retrieved.");
return;
}
auto layers = costmap_.getLayeredCostmap()->getPlugins();
for (auto & layer : *layers) {
if (layer->isClearable()) {
auto costmap_layer = std::static_pointer_cast<CostmapLayer>(layer);
clearLayerRegion(costmap_layer, x, y, reset_distance, invert);
}
}
// AlexeyMerzlyakov: No need to clear layer region for costmap filters
// as they are always supposed to be not clearable.
}
void ClearCostmapService::clearLayerRegion(
shared_ptr<CostmapLayer> & costmap, double pose_x, double pose_y, double reset_distance,
bool invert)
{
std::unique_lock<Costmap2D::mutex_t> lock(*(costmap->getMutex()));
double start_point_x = pose_x - reset_distance / 2;
double start_point_y = pose_y - reset_distance / 2;
double end_point_x = start_point_x + reset_distance;
double end_point_y = start_point_y + reset_distance;
int start_x, start_y, end_x, end_y;
costmap->worldToMapEnforceBounds(start_point_x, start_point_y, start_x, start_y);
costmap->worldToMapEnforceBounds(end_point_x, end_point_y, end_x, end_y);
costmap->clearArea(start_x, start_y, end_x, end_y, invert);
double ox = costmap->getOriginX(), oy = costmap->getOriginY();
double width = costmap->getSizeInMetersX(), height = costmap->getSizeInMetersY();
costmap->addExtraBounds(ox, oy, ox + width, oy + height);
}
void ClearCostmapService::clearEntirely()
{
std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_.getCostmap()->getMutex()));
costmap_.resetLayers();
}
bool ClearCostmapService::getPosition(double & x, double & y) const
{
geometry_msgs::msg::PoseStamped pose;
if (!costmap_.getRobotPose(pose)) {
return false;
}
x = pose.pose.position.x;
y = pose.pose.position.y;
return true;
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,556 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include "nav2_costmap_2d/costmap_2d.hpp"
#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>
#include "nav2_costmap_2d/cost_values.hpp"
#include "nav2_util/occ_grid_values.hpp"
namespace nav2_costmap_2d
{
Costmap2D::Costmap2D(
unsigned int cells_size_x, unsigned int cells_size_y, double resolution,
double origin_x, double origin_y, unsigned char default_value)
: size_x_(cells_size_x), size_y_(cells_size_y), resolution_(resolution), origin_x_(origin_x),
origin_y_(origin_y), costmap_(NULL), default_value_(default_value)
{
access_ = new mutex_t();
// create the costmap
initMaps(size_x_, size_y_);
resetMaps();
}
Costmap2D::Costmap2D(const nav_msgs::msg::OccupancyGrid & map)
: default_value_(FREE_SPACE)
{
access_ = new mutex_t();
// fill local variables
size_x_ = map.info.width;
size_y_ = map.info.height;
resolution_ = map.info.resolution;
origin_x_ = map.info.origin.position.x;
origin_y_ = map.info.origin.position.y;
// create the costmap
costmap_ = new unsigned char[size_x_ * size_y_];
// fill the costmap with a data
int8_t data;
for (unsigned int it = 0; it < size_x_ * size_y_; it++) {
data = map.data[it];
if (data == nav2_util::OCC_GRID_UNKNOWN) {
costmap_[it] = NO_INFORMATION;
} else {
// Linear conversion from OccupancyGrid data range [OCC_GRID_FREE..OCC_GRID_OCCUPIED]
// to costmap data range [FREE_SPACE..LETHAL_OBSTACLE]
costmap_[it] = std::round(
static_cast<double>(data) * (LETHAL_OBSTACLE - FREE_SPACE) /
(nav2_util::OCC_GRID_OCCUPIED - nav2_util::OCC_GRID_FREE));
}
}
}
void Costmap2D::deleteMaps()
{
// clean up data
std::unique_lock<mutex_t> lock(*access_);
delete[] costmap_;
costmap_ = NULL;
}
void Costmap2D::initMaps(unsigned int size_x, unsigned int size_y)
{
std::unique_lock<mutex_t> lock(*access_);
delete[] costmap_;
costmap_ = new unsigned char[size_x * size_y];
}
void Costmap2D::resizeMap(
unsigned int size_x, unsigned int size_y, double resolution,
double origin_x, double origin_y)
{
size_x_ = size_x;
size_y_ = size_y;
resolution_ = resolution;
origin_x_ = origin_x;
origin_y_ = origin_y;
initMaps(size_x, size_y);
// reset our maps to have no information
resetMaps();
}
void Costmap2D::resetMaps()
{
std::unique_lock<mutex_t> lock(*access_);
memset(costmap_, default_value_, size_x_ * size_y_ * sizeof(unsigned char));
}
void Costmap2D::resetMap(unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn)
{
resetMapToValue(x0, y0, xn, yn, default_value_);
}
void Costmap2D::resetMapToValue(
unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn, unsigned char value)
{
std::unique_lock<mutex_t> lock(*(access_));
unsigned int len = xn - x0;
for (unsigned int y = y0 * size_x_ + x0; y < yn * size_x_ + x0; y += size_x_) {
memset(costmap_ + y, value, len * sizeof(unsigned char));
}
}
bool Costmap2D::copyCostmapWindow(
const Costmap2D & map, double win_origin_x, double win_origin_y,
double win_size_x,
double win_size_y)
{
// check for self windowing
if (this == &map) {
// ROS_ERROR("Cannot convert this costmap into a window of itself");
return false;
}
// clean up old data
deleteMaps();
// compute the bounds of our new map
unsigned int lower_left_x, lower_left_y, upper_right_x, upper_right_y;
if (!map.worldToMap(win_origin_x, win_origin_y, lower_left_x, lower_left_y) ||
!map.worldToMap(
win_origin_x + win_size_x, win_origin_y + win_size_y, upper_right_x,
upper_right_y))
{
// ROS_ERROR("Cannot window a map that the window bounds don't fit inside of");
return false;
}
size_x_ = upper_right_x - lower_left_x;
size_y_ = upper_right_y - lower_left_y;
resolution_ = map.resolution_;
origin_x_ = win_origin_x;
origin_y_ = win_origin_y;
// initialize our various maps and reset markers for inflation
initMaps(size_x_, size_y_);
// copy the window of the static map and the costmap that we're taking
copyMapRegion(
map.costmap_, lower_left_x, lower_left_y, map.size_x_, costmap_, 0, 0, size_x_,
size_x_,
size_y_);
return true;
}
bool Costmap2D::copyWindow(
const Costmap2D & source,
unsigned int sx0, unsigned int sy0, unsigned int sxn, unsigned int syn,
unsigned int dx0, unsigned int dy0)
{
const unsigned int sz_x = sxn - sx0;
const unsigned int sz_y = syn - sy0;
if (sxn > source.getSizeInCellsX() || syn > source.getSizeInCellsY()) {
return false;
}
if (dx0 + sz_x > size_x_ || dy0 + sz_y > size_y_) {
return false;
}
copyMapRegion(
source.costmap_, sx0, sy0, source.size_x_,
costmap_, dx0, dy0, size_x_,
sz_x, sz_y);
return true;
}
Costmap2D & Costmap2D::operator=(const Costmap2D & map)
{
// check for self assignement
if (this == &map) {
return *this;
}
// clean up old data
deleteMaps();
size_x_ = map.size_x_;
size_y_ = map.size_y_;
resolution_ = map.resolution_;
origin_x_ = map.origin_x_;
origin_y_ = map.origin_y_;
// initialize our various maps
initMaps(size_x_, size_y_);
// copy the cost map
memcpy(costmap_, map.costmap_, size_x_ * size_y_ * sizeof(unsigned char));
return *this;
}
Costmap2D::Costmap2D(const Costmap2D & map)
: costmap_(NULL)
{
access_ = new mutex_t();
*this = map;
}
// just initialize everything to NULL by default
Costmap2D::Costmap2D()
: size_x_(0), size_y_(0), resolution_(0.0), origin_x_(0.0), origin_y_(0.0), costmap_(NULL)
{
access_ = new mutex_t();
}
Costmap2D::~Costmap2D()
{
deleteMaps();
delete access_;
}
unsigned int Costmap2D::cellDistance(double world_dist)
{
double cells_dist = std::max(0.0, ceil(world_dist / resolution_));
return (unsigned int)cells_dist;
}
unsigned char * Costmap2D::getCharMap() const
{
return costmap_;
}
unsigned char Costmap2D::getCost(unsigned int mx, unsigned int my) const
{
return costmap_[getIndex(mx, my)];
}
unsigned char Costmap2D::getCost(unsigned int undex) const
{
return costmap_[undex];
}
void Costmap2D::setCost(unsigned int mx, unsigned int my, unsigned char cost)
{
costmap_[getIndex(mx, my)] = cost;
}
void Costmap2D::mapToWorld(unsigned int mx, unsigned int my, double & wx, double & wy) const
{
wx = origin_x_ + (mx + 0.5) * resolution_;
wy = origin_y_ + (my + 0.5) * resolution_;
}
bool Costmap2D::worldToMap(double wx, double wy, unsigned int & mx, unsigned int & my) const
{
if (wx < origin_x_ || wy < origin_y_) {
return false;
}
mx = static_cast<unsigned int>((wx - origin_x_) / resolution_);
my = static_cast<unsigned int>((wy - origin_y_) / resolution_);
if (mx < size_x_ && my < size_y_) {
return true;
}
return false;
}
void Costmap2D::worldToMapNoBounds(double wx, double wy, int & mx, int & my) const
{
mx = static_cast<int>((wx - origin_x_) / resolution_);
my = static_cast<int>((wy - origin_y_) / resolution_);
}
void Costmap2D::worldToMapEnforceBounds(double wx, double wy, int & mx, int & my) const
{
// Here we avoid doing any math to wx,wy before comparing them to
// the bounds, so their values can go out to the max and min values
// of double floating point.
if (wx < origin_x_) {
mx = 0;
} else if (wx > resolution_ * size_x_ + origin_x_) {
mx = size_x_ - 1;
} else {
mx = static_cast<int>((wx - origin_x_) / resolution_);
}
if (wy < origin_y_) {
my = 0;
} else if (wy > resolution_ * size_y_ + origin_y_) {
my = size_y_ - 1;
} else {
my = static_cast<int>((wy - origin_y_) / resolution_);
}
}
void Costmap2D::updateOrigin(double new_origin_x, double new_origin_y)
{
// project the new origin into the grid
int cell_ox, cell_oy;
cell_ox = static_cast<int>((new_origin_x - origin_x_) / resolution_);
cell_oy = static_cast<int>((new_origin_y - origin_y_) / resolution_);
// compute the associated world coordinates for the origin cell
// because we want to keep things grid-aligned
double new_grid_ox, new_grid_oy;
new_grid_ox = origin_x_ + cell_ox * resolution_;
new_grid_oy = origin_y_ + cell_oy * resolution_;
// To save casting from unsigned int to int a bunch of times
int size_x = size_x_;
int size_y = size_y_;
// we need to compute the overlap of the new and existing windows
int lower_left_x, lower_left_y, upper_right_x, upper_right_y;
lower_left_x = std::min(std::max(cell_ox, 0), size_x);
lower_left_y = std::min(std::max(cell_oy, 0), size_y);
upper_right_x = std::min(std::max(cell_ox + size_x, 0), size_x);
upper_right_y = std::min(std::max(cell_oy + size_y, 0), size_y);
unsigned int cell_size_x = upper_right_x - lower_left_x;
unsigned int cell_size_y = upper_right_y - lower_left_y;
// we need a map to store the obstacles in the window temporarily
unsigned char * local_map = new unsigned char[cell_size_x * cell_size_y];
// copy the local window in the costmap to the local map
copyMapRegion(
costmap_, lower_left_x, lower_left_y, size_x_, local_map, 0, 0, cell_size_x,
cell_size_x,
cell_size_y);
// now we'll set the costmap to be completely unknown if we track unknown space
resetMaps();
// update the origin with the appropriate world coordinates
origin_x_ = new_grid_ox;
origin_y_ = new_grid_oy;
// compute the starting cell location for copying data back in
int start_x = lower_left_x - cell_ox;
int start_y = lower_left_y - cell_oy;
// now we want to copy the overlapping information back into the map, but in its new location
copyMapRegion(
local_map, 0, 0, cell_size_x, costmap_, start_x, start_y, size_x_, cell_size_x,
cell_size_y);
// make sure to clean up
delete[] local_map;
}
bool Costmap2D::setConvexPolygonCost(
const std::vector<geometry_msgs::msg::Point> & polygon,
unsigned char cost_value)
{
// we assume the polygon is given in the global_frame...
// we need to transform it to map coordinates
std::vector<MapLocation> map_polygon;
for (unsigned int i = 0; i < polygon.size(); ++i) {
MapLocation loc;
if (!worldToMap(polygon[i].x, polygon[i].y, loc.x, loc.y)) {
// ("Polygon lies outside map bounds, so we can't fill it");
return false;
}
map_polygon.push_back(loc);
}
std::vector<MapLocation> polygon_cells;
// get the cells that fill the polygon
convexFillCells(map_polygon, polygon_cells);
// set the cost of those cells
for (unsigned int i = 0; i < polygon_cells.size(); ++i) {
unsigned int index = getIndex(polygon_cells[i].x, polygon_cells[i].y);
costmap_[index] = cost_value;
}
return true;
}
void Costmap2D::polygonOutlineCells(
const std::vector<MapLocation> & polygon,
std::vector<MapLocation> & polygon_cells)
{
PolygonOutlineCells cell_gatherer(*this, costmap_, polygon_cells);
for (unsigned int i = 0; i < polygon.size() - 1; ++i) {
raytraceLine(cell_gatherer, polygon[i].x, polygon[i].y, polygon[i + 1].x, polygon[i + 1].y);
}
if (!polygon.empty()) {
unsigned int last_index = polygon.size() - 1;
// we also need to close the polygon by going from the last point to the first
raytraceLine(
cell_gatherer, polygon[last_index].x, polygon[last_index].y, polygon[0].x,
polygon[0].y);
}
}
void Costmap2D::convexFillCells(
const std::vector<MapLocation> & polygon,
std::vector<MapLocation> & polygon_cells)
{
// we need a minimum polygon of a triangle
if (polygon.size() < 3) {
return;
}
// first get the cells that make up the outline of the polygon
polygonOutlineCells(polygon, polygon_cells);
// quick bubble sort to sort points by x
MapLocation swap;
unsigned int i = 0;
while (i < polygon_cells.size() - 1) {
if (polygon_cells[i].x > polygon_cells[i + 1].x) {
swap = polygon_cells[i];
polygon_cells[i] = polygon_cells[i + 1];
polygon_cells[i + 1] = swap;
if (i > 0) {
--i;
}
} else {
++i;
}
}
i = 0;
MapLocation min_pt;
MapLocation max_pt;
unsigned int min_x = polygon_cells[0].x;
unsigned int max_x = polygon_cells[polygon_cells.size() - 1].x;
// walk through each column and mark cells inside the polygon
for (unsigned int x = min_x; x <= max_x; ++x) {
if (i >= polygon_cells.size() - 1) {
break;
}
if (polygon_cells[i].y < polygon_cells[i + 1].y) {
min_pt = polygon_cells[i];
max_pt = polygon_cells[i + 1];
} else {
min_pt = polygon_cells[i + 1];
max_pt = polygon_cells[i];
}
i += 2;
while (i < polygon_cells.size() && polygon_cells[i].x == x) {
if (polygon_cells[i].y < min_pt.y) {
min_pt = polygon_cells[i];
} else if (polygon_cells[i].y > max_pt.y) {
max_pt = polygon_cells[i];
}
++i;
}
MapLocation pt;
// loop though cells in the column
for (unsigned int y = min_pt.y; y <= max_pt.y; ++y) {
pt.x = x;
pt.y = y;
polygon_cells.push_back(pt);
}
}
}
unsigned int Costmap2D::getSizeInCellsX() const
{
return size_x_;
}
unsigned int Costmap2D::getSizeInCellsY() const
{
return size_y_;
}
double Costmap2D::getSizeInMetersX() const
{
return (size_x_ - 1 + 0.5) * resolution_;
}
double Costmap2D::getSizeInMetersY() const
{
return (size_y_ - 1 + 0.5) * resolution_;
}
double Costmap2D::getOriginX() const
{
return origin_x_;
}
double Costmap2D::getOriginY() const
{
return origin_y_;
}
double Costmap2D::getResolution() const
{
return resolution_;
}
bool Costmap2D::saveMap(std::string file_name)
{
FILE * fp = fopen(file_name.c_str(), "w");
if (!fp) {
return false;
}
fprintf(fp, "P2\n%u\n%u\n%u\n", size_x_, size_y_, 0xff);
for (unsigned int iy = 0; iy < size_y_; iy++) {
for (unsigned int ix = 0; ix < size_x_; ix++) {
unsigned char cost = getCost(ix, iy);
fprintf(fp, "%d ", cost);
}
fprintf(fp, "\n");
}
fclose(fp);
return true;
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,231 @@
/*
* Copyright (C) 2009, Willow Garage, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/point_cloud2.hpp"
#include "sensor_msgs/point_cloud2_iterator.hpp"
#include "nav2_voxel_grid/voxel_grid.hpp"
#include "nav2_msgs/msg/voxel_grid.hpp"
#include "nav2_util/execution_timer.hpp"
static inline void mapToWorld3D(
const unsigned int mx,
const unsigned int my, const unsigned int mz,
const double origin_x, const double origin_y, const double origin_z,
const double x_resolution, const double y_resolution,
const double z_resolution,
double & wx, double & wy, double & wz)
{
// returns the center point of the cell
wx = origin_x + (mx + 0.5) * x_resolution;
wy = origin_y + (my + 0.5) * y_resolution;
wz = origin_z + (mz + 0.5) * z_resolution;
}
struct Cell
{
double x;
double y;
double z;
nav2_voxel_grid::VoxelStatus status;
};
typedef std::vector<Cell> V_Cell;
float g_colors_r[] = {0.0f, 0.0f, 1.0f};
float g_colors_g[] = {0.0f, 0.0f, 0.0f};
float g_colors_b[] = {0.0f, 1.0f, 0.0f};
float g_colors_a[] = {0.0f, 0.5f, 1.0f};
V_Cell g_marked;
V_Cell g_unknown;
rclcpp::Node::SharedPtr g_node;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_marked;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_unknown;
/**
* @brief An helper function to fill pointcloud2 of both the marked and unknown points from voxel_grid
* @param cloud PointCloud2 Ptr which needs to be filled
* @param num_channels Represents the total number of points that are going to be filled
* @param header Carries the header information that needs to be assigned to PointCloud2 header
* @param g_cells contains the x, y, z values that needs to be added to the PointCloud2
*/
void pointCloud2Helper(
std::unique_ptr<sensor_msgs::msg::PointCloud2> & cloud,
uint32_t num_channels,
std_msgs::msg::Header header,
V_Cell & g_cells)
{
cloud->header = header;
cloud->width = num_channels;
cloud->height = 1;
cloud->is_dense = true;
cloud->is_bigendian = false;
sensor_msgs::PointCloud2Modifier modifier(*cloud);
modifier.setPointCloud2Fields(
6, "x", 1, sensor_msgs::msg::PointField::FLOAT32,
"y", 1, sensor_msgs::msg::PointField::FLOAT32,
"z", 1, sensor_msgs::msg::PointField::FLOAT32,
"r", 1, sensor_msgs::msg::PointField::UINT8,
"g", 1, sensor_msgs::msg::PointField::UINT8,
"b", 1, sensor_msgs::msg::PointField::UINT8);
sensor_msgs::PointCloud2Iterator<float> iter_x(*cloud, "x");
sensor_msgs::PointCloud2Iterator<float> iter_y(*cloud, "y");
sensor_msgs::PointCloud2Iterator<float> iter_z(*cloud, "z");
sensor_msgs::PointCloud2Iterator<uint8_t> iter_r(*cloud, "r");
sensor_msgs::PointCloud2Iterator<uint8_t> iter_g(*cloud, "g");
sensor_msgs::PointCloud2Iterator<uint8_t> iter_b(*cloud, "b");
for (uint32_t i = 0; i < num_channels; ++i) {
Cell & c = g_cells[i];
// assigning value to the point cloud2's iterator
*iter_x = c.x;
*iter_y = c.y;
*iter_z = c.z;
*iter_r = g_colors_r[c.status] * 255.0;
*iter_g = g_colors_g[c.status] * 255.0;
*iter_b = g_colors_b[c.status] * 255.0;
++iter_x;
++iter_y;
++iter_z;
++iter_r;
++iter_g;
++iter_b;
}
}
void voxelCallback(const nav2_msgs::msg::VoxelGrid::ConstSharedPtr grid)
{
if (grid->data.empty()) {
RCLCPP_ERROR(g_node->get_logger(), "Received empty voxel grid");
return;
}
nav2_util::ExecutionTimer timer;
timer.start();
RCLCPP_DEBUG(g_node->get_logger(), "Received voxel grid");
const std::string frame_id = grid->header.frame_id;
const rclcpp::Time stamp = grid->header.stamp;
const uint32_t * data = &grid->data.front();
const double x_origin = grid->origin.x;
const double y_origin = grid->origin.y;
const double z_origin = grid->origin.z;
const double x_res = grid->resolutions.x;
const double y_res = grid->resolutions.y;
const double z_res = grid->resolutions.z;
const uint32_t x_size = grid->size_x;
const uint32_t y_size = grid->size_y;
const uint32_t z_size = grid->size_z;
g_marked.clear();
g_unknown.clear();
uint32_t num_marked = 0;
uint32_t num_unknown = 0;
for (uint32_t y_grid = 0; y_grid < y_size; ++y_grid) {
for (uint32_t x_grid = 0; x_grid < x_size; ++x_grid) {
for (uint32_t z_grid = 0; z_grid < z_size; ++z_grid) {
nav2_voxel_grid::VoxelStatus status =
nav2_voxel_grid::VoxelGrid::getVoxel(
x_grid, y_grid,
z_grid, x_size, y_size, z_size, data);
if (status == nav2_voxel_grid::UNKNOWN) {
Cell c;
c.status = status;
mapToWorld3D(
x_grid, y_grid, z_grid, x_origin, y_origin,
z_origin, x_res, y_res, z_res, c.x, c.y, c.z);
g_unknown.push_back(c);
++num_unknown;
} else if (status == nav2_voxel_grid::MARKED) {
Cell c;
c.status = status;
mapToWorld3D(
x_grid, y_grid, z_grid, x_origin, y_origin,
z_origin, x_res, y_res, z_res, c.x, c.y, c.z);
g_marked.push_back(c);
++num_marked;
}
}
}
}
std_msgs::msg::Header pcl_header;
pcl_header.frame_id = frame_id;
pcl_header.stamp = stamp;
{
auto cloud = std::make_unique<sensor_msgs::msg::PointCloud2>();
pointCloud2Helper(cloud, num_marked, pcl_header, g_marked);
pub_marked->publish(std::move(cloud));
}
{
auto cloud = std::make_unique<sensor_msgs::msg::PointCloud2>();
pointCloud2Helper(cloud, num_unknown, pcl_header, g_unknown);
pub_unknown->publish(std::move(cloud));
}
timer.end();
RCLCPP_DEBUG(
g_node->get_logger(), "Published %d points in %f seconds",
num_marked + num_unknown, timer.elapsed_time_in_seconds());
}
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
g_node = rclcpp::Node::make_shared("costmap_2d_cloud");
RCLCPP_DEBUG(g_node->get_logger(), "Starting up costmap_2d_cloud");
pub_marked = g_node->create_publisher<sensor_msgs::msg::PointCloud2>(
"voxel_marked_cloud", 1);
pub_unknown = g_node->create_publisher<sensor_msgs::msg::PointCloud2>(
"voxel_unknown_cloud", 1);
auto sub = g_node->create_subscription<nav2_msgs::msg::VoxelGrid>(
"voxel_grid", rclcpp::SystemDefaultsQoS(), voxelCallback);
rclcpp::spin(g_node->get_node_base_interface());
rclcpp::shutdown();
return 0;
}
@@ -0,0 +1,162 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
* Steve Macenski
*********************************************************************/
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include "rclcpp/rclcpp.hpp"
#include "visualization_msgs/msg/marker.hpp"
#include "nav2_msgs/msg/voxel_grid.hpp"
#include "nav2_voxel_grid/voxel_grid.hpp"
#include "nav2_util/execution_timer.hpp"
struct Cell
{
double x;
double y;
double z;
nav2_voxel_grid::VoxelStatus status;
};
typedef std::vector<Cell> V_Cell;
float g_colors_r[] = {0.0f, 0.0f, 1.0f};
float g_colors_g[] = {0.0f, 0.0f, 0.0f};
float g_colors_b[] = {0.0f, 1.0f, 0.0f};
float g_colors_a[] = {0.0f, 0.5f, 1.0f};
V_Cell g_cells;
rclcpp::Node::SharedPtr g_node;
rclcpp::Publisher<visualization_msgs::msg::Marker>::SharedPtr pub;
void voxelCallback(const nav2_msgs::msg::VoxelGrid::ConstSharedPtr grid)
{
if (grid->data.empty()) {
RCLCPP_ERROR(g_node->get_logger(), "Received voxel grid");
return;
}
nav2_util::ExecutionTimer timer;
timer.start();
RCLCPP_DEBUG(g_node->get_logger(), "Received voxel grid");
const std::string frame_id = grid->header.frame_id;
const rclcpp::Time stamp = grid->header.stamp;
const uint32_t * data = &grid->data.front();
const double x_origin = grid->origin.x;
const double y_origin = grid->origin.y;
const double z_origin = grid->origin.z;
const double x_res = grid->resolutions.x;
const double y_res = grid->resolutions.y;
const double z_res = grid->resolutions.z;
const uint32_t x_size = grid->size_x;
const uint32_t y_size = grid->size_y;
const uint32_t z_size = grid->size_z;
g_cells.clear();
uint32_t num_markers = 0;
for (uint32_t y_grid = 0; y_grid < y_size; ++y_grid) {
for (uint32_t x_grid = 0; x_grid < x_size; ++x_grid) {
for (uint32_t z_grid = 0; z_grid < z_size; ++z_grid) {
nav2_voxel_grid::VoxelStatus status =
nav2_voxel_grid::VoxelGrid::getVoxel(
x_grid, y_grid,
z_grid, x_size, y_size, z_size, data);
if (status == nav2_voxel_grid::MARKED) {
Cell c;
c.status = status;
c.x = x_origin + (x_grid + 0.5) * x_res;
c.y = y_origin + (y_grid + 0.5) * y_res;
c.z = z_origin + (z_grid + 0.5) * z_res;
g_cells.push_back(c);
++num_markers;
}
}
}
}
auto m = std::make_unique<visualization_msgs::msg::Marker>();
m->header.frame_id = frame_id;
m->header.stamp = stamp;
m->ns = g_node->get_namespace();
m->id = 0;
m->type = visualization_msgs::msg::Marker::CUBE_LIST;
m->action = visualization_msgs::msg::Marker::ADD;
m->pose.orientation.w = 1.0;
m->scale.x = x_res;
m->scale.y = y_res;
m->scale.z = z_res;
m->color.r = g_colors_r[nav2_voxel_grid::MARKED];
m->color.g = g_colors_g[nav2_voxel_grid::MARKED];
m->color.b = g_colors_b[nav2_voxel_grid::MARKED];
m->color.a = g_colors_a[nav2_voxel_grid::MARKED];
m->points.resize(num_markers);
for (uint32_t i = 0; i < num_markers; ++i) {
Cell & c = g_cells[i];
geometry_msgs::msg::Point & p = m->points[i];
p.x = c.x;
p.y = c.y;
p.z = c.z;
}
pub->publish(std::move(m));
timer.end();
RCLCPP_INFO(
g_node->get_logger(), "Published %d markers in %f seconds",
num_markers, timer.elapsed_time_in_seconds());
}
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
g_node = rclcpp::Node::make_shared("costmap_2d_marker");
RCLCPP_DEBUG(g_node->get_logger(), "Starting costmap_2d_marker");
pub = g_node->create_publisher<visualization_msgs::msg::Marker>(
"visualization_marker", 1);
auto sub = g_node->create_subscription<nav2_msgs::msg::VoxelGrid>(
"voxel_grid", rclcpp::SystemDefaultsQoS(), voxelCallback);
rclcpp::spin(g_node->get_node_base_interface());
}
@@ -0,0 +1,51 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include <memory>
#include "nav2_costmap_2d/costmap_2d_ros.hpp"
#include "rclcpp/rclcpp.hpp"
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<nav2_costmap_2d::Costmap2DROS>("costmap");
rclcpp::spin(node->get_node_base_interface());
rclcpp::shutdown();
return 0;
}
@@ -0,0 +1,264 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* Copyright (c) 2019, Samsung Research America, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include "nav2_costmap_2d/costmap_2d_publisher.hpp"
#include <string>
#include <memory>
#include <utility>
#include "nav2_costmap_2d/cost_values.hpp"
namespace nav2_costmap_2d
{
char * Costmap2DPublisher::cost_translation_table_ = NULL;
Costmap2DPublisher::Costmap2DPublisher(
const nav2_util::LifecycleNode::WeakPtr & parent,
Costmap2D * costmap,
std::string global_frame,
std::string topic_name,
bool always_send_full_costmap)
: costmap_(costmap),
global_frame_(global_frame),
topic_name_(topic_name),
active_(false),
always_send_full_costmap_(always_send_full_costmap)
{
auto node = parent.lock();
clock_ = node->get_clock();
logger_ = node->get_logger();
auto custom_qos = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable();
// TODO(bpwilcox): port onNewSubscription functionality for publisher
costmap_pub_ = node->create_publisher<nav_msgs::msg::OccupancyGrid>(
topic_name,
custom_qos);
costmap_raw_pub_ = node->create_publisher<nav2_msgs::msg::Costmap>(
topic_name + "_raw",
custom_qos);
costmap_update_pub_ = node->create_publisher<map_msgs::msg::OccupancyGridUpdate>(
topic_name + "_updates", custom_qos);
// Create a service that will use the callback function to handle requests.
costmap_service_ = node->create_service<nav2_msgs::srv::GetCostmap>(
"get_costmap", std::bind(
&Costmap2DPublisher::costmap_service_callback,
this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
if (cost_translation_table_ == NULL) {
cost_translation_table_ = new char[256];
// special values:
cost_translation_table_[0] = 0; // NO obstacle
cost_translation_table_[253] = 99; // INSCRIBED obstacle
cost_translation_table_[254] = 100; // LETHAL obstacle
cost_translation_table_[255] = -1; // UNKNOWN
// regular cost values scale the range 1 to 252 (inclusive) to fit
// into 1 to 98 (inclusive).
for (int i = 1; i < 253; i++) {
cost_translation_table_[i] = static_cast<char>(1 + (97 * (i - 1)) / 251);
}
}
xn_ = yn_ = 0;
x0_ = costmap_->getSizeInCellsX();
y0_ = costmap_->getSizeInCellsY();
}
Costmap2DPublisher::~Costmap2DPublisher() {}
// TODO(bpwilcox): find equivalent/workaround to ros::SingleSubscriberPublishr
/*
void Costmap2DPublisher::onNewSubscription(const ros::SingleSubscriberPublisher& pub)
{
prepareGrid();
pub.publish(grid_);
} */
// prepare grid_ message for publication.
void Costmap2DPublisher::prepareGrid()
{
std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_->getMutex()));
grid_resolution = costmap_->getResolution();
grid_width = costmap_->getSizeInCellsX();
grid_height = costmap_->getSizeInCellsY();
grid_ = std::make_unique<nav_msgs::msg::OccupancyGrid>();
grid_->header.frame_id = global_frame_;
grid_->header.stamp = clock_->now();
grid_->info.resolution = grid_resolution;
grid_->info.width = grid_width;
grid_->info.height = grid_height;
double wx, wy;
costmap_->mapToWorld(0, 0, wx, wy);
grid_->info.origin.position.x = wx - grid_resolution / 2;
grid_->info.origin.position.y = wy - grid_resolution / 2;
grid_->info.origin.position.z = 0.0;
grid_->info.origin.orientation.w = 1.0;
saved_origin_x_ = costmap_->getOriginX();
saved_origin_y_ = costmap_->getOriginY();
grid_->data.resize(grid_->info.width * grid_->info.height);
unsigned char * data = costmap_->getCharMap();
for (unsigned int i = 0; i < grid_->data.size(); i++) {
grid_->data[i] = cost_translation_table_[data[i]];
}
}
void Costmap2DPublisher::prepareCostmap()
{
std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_->getMutex()));
double resolution = costmap_->getResolution();
costmap_raw_ = std::make_unique<nav2_msgs::msg::Costmap>();
costmap_raw_->header.frame_id = global_frame_;
costmap_raw_->header.stamp = clock_->now();
costmap_raw_->metadata.layer = "master";
costmap_raw_->metadata.resolution = resolution;
costmap_raw_->metadata.size_x = costmap_->getSizeInCellsX();
costmap_raw_->metadata.size_y = costmap_->getSizeInCellsY();
double wx, wy;
costmap_->mapToWorld(0, 0, wx, wy);
costmap_raw_->metadata.origin.position.x = wx - resolution / 2;
costmap_raw_->metadata.origin.position.y = wy - resolution / 2;
costmap_raw_->metadata.origin.position.z = 0.0;
costmap_raw_->metadata.origin.orientation.w = 1.0;
costmap_raw_->data.resize(costmap_raw_->metadata.size_x * costmap_raw_->metadata.size_y);
unsigned char * data = costmap_->getCharMap();
for (unsigned int i = 0; i < costmap_raw_->data.size(); i++) {
costmap_raw_->data[i] = data[i];
}
}
void Costmap2DPublisher::publishCostmap()
{
if (costmap_raw_pub_->get_subscription_count() > 0) {
prepareCostmap();
costmap_raw_pub_->publish(std::move(costmap_raw_));
}
float resolution = costmap_->getResolution();
if (always_send_full_costmap_ || grid_resolution != resolution ||
grid_width != costmap_->getSizeInCellsX() ||
grid_height != costmap_->getSizeInCellsY() ||
saved_origin_x_ != costmap_->getOriginX() ||
saved_origin_y_ != costmap_->getOriginY())
{
if (costmap_pub_->get_subscription_count() > 0) {
prepareGrid();
costmap_pub_->publish(std::move(grid_));
}
} else if (x0_ < xn_) {
if (costmap_update_pub_->get_subscription_count() > 0) {
std::unique_lock<Costmap2D::mutex_t> lock(*(costmap_->getMutex()));
// Publish Just an Update
auto update = std::make_unique<map_msgs::msg::OccupancyGridUpdate>();
update->header.stamp = rclcpp::Time();
update->header.frame_id = global_frame_;
update->x = x0_;
update->y = y0_;
update->width = xn_ - x0_;
update->height = yn_ - y0_;
update->data.resize(update->width * update->height);
unsigned int i = 0;
for (unsigned int y = y0_; y < yn_; y++) {
for (unsigned int x = x0_; x < xn_; x++) {
unsigned char cost = costmap_->getCost(x, y);
update->data[i++] = cost_translation_table_[cost];
}
}
costmap_update_pub_->publish(std::move(update));
}
}
xn_ = yn_ = 0;
x0_ = costmap_->getSizeInCellsX();
y0_ = costmap_->getSizeInCellsY();
}
void
Costmap2DPublisher::costmap_service_callback(
const std::shared_ptr<rmw_request_id_t>/*request_header*/,
const std::shared_ptr<nav2_msgs::srv::GetCostmap::Request>/*request*/,
const std::shared_ptr<nav2_msgs::srv::GetCostmap::Response> response)
{
RCLCPP_DEBUG(logger_, "Received costmap service request");
// TODO(bpwilcox): Grab correct orientation information
tf2::Quaternion quaternion;
quaternion.setRPY(0.0, 0.0, 0.0);
auto size_x = costmap_->getSizeInCellsX();
auto size_y = costmap_->getSizeInCellsY();
auto data_length = size_x * size_y;
unsigned char * data = costmap_->getCharMap();
auto current_time = clock_->now();
response->map.header.stamp = current_time;
response->map.header.frame_id = global_frame_;
response->map.metadata.size_x = size_x;
response->map.metadata.size_y = size_y;
response->map.metadata.resolution = costmap_->getResolution();
response->map.metadata.layer = "master";
response->map.metadata.map_load_time = current_time;
response->map.metadata.update_time = current_time;
response->map.metadata.origin.position.x = costmap_->getOriginX();
response->map.metadata.origin.position.y = costmap_->getOriginY();
response->map.metadata.origin.position.z = 0.0;
response->map.metadata.origin.orientation = tf2::toMsg(quaternion);
response->map.data.resize(data_length);
response->map.data.assign(data, data + data_length);
}
} // end namespace nav2_costmap_2d
@@ -0,0 +1,769 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include "nav2_costmap_2d/costmap_2d_ros.hpp"
#include <memory>
#include <chrono>
#include <string>
#include <vector>
#include <utility>
#include "nav2_costmap_2d/layered_costmap.hpp"
#include "nav2_util/execution_timer.hpp"
#include "nav2_util/node_utils.hpp"
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "tf2_ros/create_timer_ros.h"
#include "nav2_util/robot_utils.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
using namespace std::chrono_literals;
using std::placeholders::_1;
using rcl_interfaces::msg::ParameterType;
namespace nav2_costmap_2d
{
Costmap2DROS::Costmap2DROS(const std::string & name)
: Costmap2DROS(name, "/", name) {}
Costmap2DROS::Costmap2DROS(const rclcpp::NodeOptions & options)
: nav2_util::LifecycleNode("costmap", "", options),
name_("costmap"),
default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
default_types_{
"nav2_costmap_2d::StaticLayer",
"nav2_costmap_2d::ObstacleLayer",
"nav2_costmap_2d::InflationLayer"}
{
is_lifecycle_follower_ = false;
RCLCPP_INFO(get_logger(), "Creating Costmap");
declare_parameter("always_send_full_costmap", rclcpp::ParameterValue(false));
declare_parameter("footprint_padding", rclcpp::ParameterValue(0.01f));
declare_parameter("footprint", rclcpp::ParameterValue(std::string("[]")));
declare_parameter("global_frame", rclcpp::ParameterValue(std::string("map")));
declare_parameter("height", rclcpp::ParameterValue(5));
declare_parameter("width", rclcpp::ParameterValue(5));
declare_parameter("lethal_cost_threshold", rclcpp::ParameterValue(100));
declare_parameter(
"map_topic", rclcpp::ParameterValue(
(parent_namespace_ == "/" ? "/" : parent_namespace_ + "/") + std::string("map")));
declare_parameter("observation_sources", rclcpp::ParameterValue(std::string("")));
declare_parameter("origin_x", rclcpp::ParameterValue(0.0));
declare_parameter("origin_y", rclcpp::ParameterValue(0.0));
declare_parameter("plugins", rclcpp::ParameterValue(default_plugins_));
declare_parameter("filters", rclcpp::ParameterValue(std::vector<std::string>()));
declare_parameter("publish_frequency", rclcpp::ParameterValue(1.0));
declare_parameter("resolution", rclcpp::ParameterValue(0.1));
declare_parameter("robot_base_frame", rclcpp::ParameterValue(std::string("base_link")));
declare_parameter("robot_radius", rclcpp::ParameterValue(0.1));
declare_parameter("rolling_window", rclcpp::ParameterValue(false));
declare_parameter("track_unknown_space", rclcpp::ParameterValue(false));
declare_parameter("transform_tolerance", rclcpp::ParameterValue(0.3));
declare_parameter("trinary_costmap", rclcpp::ParameterValue(true));
declare_parameter("unknown_cost_value", rclcpp::ParameterValue(static_cast<unsigned char>(0xff)));
declare_parameter("update_frequency", rclcpp::ParameterValue(5.0));
declare_parameter("use_maximum", rclcpp::ParameterValue(false));
}
Costmap2DROS::Costmap2DROS(
const std::string & name,
const std::string & parent_namespace,
const std::string & local_namespace)
: nav2_util::LifecycleNode(name, "",
// NodeOption arguments take precedence over the ones provided on the command line
// use this to make sure the node is placed on the provided namespace
// TODO(orduno) Pass a sub-node instead of creating a new node for better handling
// of the namespaces
rclcpp::NodeOptions().arguments({
"--ros-args", "-r", std::string("__ns:=") +
nav2_util::add_namespaces(parent_namespace, local_namespace),
"--ros-args", "-r", name + ":" + std::string("__node:=") + name
})),
name_(name),
parent_namespace_(parent_namespace),
default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
default_types_{
"nav2_costmap_2d::StaticLayer",
"nav2_costmap_2d::ObstacleLayer",
"nav2_costmap_2d::InflationLayer"}
{
RCLCPP_INFO(get_logger(), "Creating Costmap");
declare_parameter("always_send_full_costmap", rclcpp::ParameterValue(false));
declare_parameter("footprint_padding", rclcpp::ParameterValue(0.01f));
declare_parameter("footprint", rclcpp::ParameterValue(std::string("[]")));
declare_parameter("global_frame", rclcpp::ParameterValue(std::string("map")));
declare_parameter("height", rclcpp::ParameterValue(5));
declare_parameter("width", rclcpp::ParameterValue(5));
declare_parameter("lethal_cost_threshold", rclcpp::ParameterValue(100));
declare_parameter(
"map_topic", rclcpp::ParameterValue(
(parent_namespace_ == "/" ? "/" : parent_namespace_ + "/") + std::string("map")));
declare_parameter("observation_sources", rclcpp::ParameterValue(std::string("")));
declare_parameter("origin_x", rclcpp::ParameterValue(0.0));
declare_parameter("origin_y", rclcpp::ParameterValue(0.0));
declare_parameter("plugins", rclcpp::ParameterValue(default_plugins_));
declare_parameter("filters", rclcpp::ParameterValue(std::vector<std::string>()));
declare_parameter("publish_frequency", rclcpp::ParameterValue(1.0));
declare_parameter("resolution", rclcpp::ParameterValue(0.1));
declare_parameter("robot_base_frame", rclcpp::ParameterValue(std::string("base_link")));
declare_parameter("robot_radius", rclcpp::ParameterValue(0.1));
declare_parameter("rolling_window", rclcpp::ParameterValue(false));
declare_parameter("track_unknown_space", rclcpp::ParameterValue(false));
declare_parameter("transform_tolerance", rclcpp::ParameterValue(0.3));
declare_parameter("trinary_costmap", rclcpp::ParameterValue(true));
declare_parameter("unknown_cost_value", rclcpp::ParameterValue(static_cast<unsigned char>(0xff)));
declare_parameter("update_frequency", rclcpp::ParameterValue(5.0));
declare_parameter("use_maximum", rclcpp::ParameterValue(false));
}
Costmap2DROS::~Costmap2DROS()
{
}
nav2_util::CallbackReturn
Costmap2DROS::on_configure(const rclcpp_lifecycle::State & /*state*/)
{
RCLCPP_INFO(get_logger(), "Configuring");
getParameters();
callback_group_ = create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive, false);
// Create the costmap itself
layered_costmap_ = std::make_unique<LayeredCostmap>(
global_frame_, rolling_window_, track_unknown_space_);
if (!layered_costmap_->isSizeLocked()) {
layered_costmap_->resizeMap(
(unsigned int)(map_width_meters_ / resolution_),
(unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
}
// Create the transform-related objects
tf_buffer_ = std::make_shared<tf2_ros::Buffer>(get_clock());
auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
get_node_base_interface(),
get_node_timers_interface(),
callback_group_);
tf_buffer_->setCreateTimerInterface(timer_interface);
tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
// Then load and add the plug-ins to the costmap
for (unsigned int i = 0; i < plugin_names_.size(); ++i) {
RCLCPP_INFO(get_logger(), "Using plugin \"%s\"", plugin_names_[i].c_str());
std::shared_ptr<Layer> plugin = plugin_loader_.createSharedInstance(plugin_types_[i]);
// lock the costmap because no update is allowed until the plugin is initialized
std::unique_lock<Costmap2D::mutex_t> lock(*(layered_costmap_->getCostmap()->getMutex()));
layered_costmap_->addPlugin(plugin);
// TODO(mjeronimo): instead of get(), use a shared ptr
plugin->initialize(
layered_costmap_.get(), plugin_names_[i], tf_buffer_.get(),
shared_from_this(), callback_group_);
lock.unlock();
RCLCPP_INFO(get_logger(), "Initialized plugin \"%s\"", plugin_names_[i].c_str());
}
// and costmap filters as well
for (unsigned int i = 0; i < filter_names_.size(); ++i) {
RCLCPP_INFO(get_logger(), "Using costmap filter \"%s\"", filter_names_[i].c_str());
std::shared_ptr<Layer> filter = plugin_loader_.createSharedInstance(filter_types_[i]);
// lock the costmap because no update is allowed until the filter is initialized
std::unique_lock<Costmap2D::mutex_t> lock(*(layered_costmap_->getCostmap()->getMutex()));
layered_costmap_->addFilter(filter);
filter->initialize(
layered_costmap_.get(), filter_names_[i], tf_buffer_.get(),
shared_from_this(), callback_group_);
lock.unlock();
RCLCPP_INFO(get_logger(), "Initialized costmap filter \"%s\"", filter_names_[i].c_str());
}
// Create the publishers and subscribers
footprint_sub_ = create_subscription<geometry_msgs::msg::Polygon>(
"footprint",
rclcpp::SystemDefaultsQoS(),
std::bind(&Costmap2DROS::setRobotFootprintPolygon, this, std::placeholders::_1));
footprint_pub_ = create_publisher<geometry_msgs::msg::PolygonStamped>(
"published_footprint", rclcpp::SystemDefaultsQoS());
costmap_publisher_ = std::make_unique<Costmap2DPublisher>(
shared_from_this(),
layered_costmap_->getCostmap(), global_frame_,
"costmap", always_send_full_costmap_);
// Set the footprint
if (use_radius_) {
setRobotFootprint(makeFootprintFromRadius(robot_radius_));
} else {
std::vector<geometry_msgs::msg::Point> new_footprint;
makeFootprintFromString(footprint_, new_footprint);
setRobotFootprint(new_footprint);
}
// Add cleaning service
clear_costmap_service_ = std::make_unique<ClearCostmapService>(shared_from_this(), *this);
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
executor_->add_callback_group(callback_group_, get_node_base_interface());
executor_thread_ = std::make_unique<nav2_util::NodeThread>(executor_);
return nav2_util::CallbackReturn::SUCCESS;
}
nav2_util::CallbackReturn
Costmap2DROS::on_activate(const rclcpp_lifecycle::State & /*state*/)
{
RCLCPP_INFO(get_logger(), "Activating");
costmap_publisher_->on_activate();
footprint_pub_->on_activate();
// First, make sure that the transform between the robot base frame
// and the global frame is available
std::string tf_error;
RCLCPP_INFO(get_logger(), "Checking transform");
rclcpp::Rate r(2);
while (rclcpp::ok() &&
!tf_buffer_->canTransform(
global_frame_, robot_base_frame_, tf2::TimePointZero, &tf_error))
{
RCLCPP_INFO(
get_logger(), "Timed out waiting for transform from %s to %s"
" to become available, tf error: %s",
robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
// The error string will accumulate and errors will typically be the same, so the last
// will do for the warning above. Reset the string here to avoid accumulation
tf_error.clear();
r.sleep();
}
// Create a thread to handle updating the map
stopped_ = true; // to active plugins
stop_updates_ = false;
map_update_thread_shutdown_ = false;
map_update_thread_ = std::make_unique<std::thread>(
std::bind(&Costmap2DROS::mapUpdateLoop, this, map_update_frequency_));
start();
// Add callback for dynamic parameters
dyn_params_handler = this->add_on_set_parameters_callback(
std::bind(&Costmap2DROS::dynamicParametersCallback, this, _1));
return nav2_util::CallbackReturn::SUCCESS;
}
nav2_util::CallbackReturn
Costmap2DROS::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
{
RCLCPP_INFO(get_logger(), "Deactivating");
dyn_params_handler.reset();
stop();
// Map thread stuff
map_update_thread_shutdown_ = true;
if (map_update_thread_->joinable()) {
map_update_thread_->join();
}
costmap_publisher_->on_deactivate();
footprint_pub_->on_deactivate();
return nav2_util::CallbackReturn::SUCCESS;
}
nav2_util::CallbackReturn
Costmap2DROS::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
{
RCLCPP_INFO(get_logger(), "Cleaning up");
layered_costmap_.reset();
tf_listener_.reset();
tf_buffer_.reset();
footprint_sub_.reset();
footprint_pub_.reset();
costmap_publisher_.reset();
clear_costmap_service_.reset();
executor_thread_.reset();
return nav2_util::CallbackReturn::SUCCESS;
}
nav2_util::CallbackReturn
Costmap2DROS::on_shutdown(const rclcpp_lifecycle::State &)
{
RCLCPP_INFO(get_logger(), "Shutting down");
return nav2_util::CallbackReturn::SUCCESS;
}
void
Costmap2DROS::getParameters()
{
RCLCPP_DEBUG(get_logger(), " getParameters");
// Get all of the required parameters
get_parameter("always_send_full_costmap", always_send_full_costmap_);
get_parameter("footprint", footprint_);
get_parameter("footprint_padding", footprint_padding_);
get_parameter("global_frame", global_frame_);
get_parameter("height", map_height_meters_);
get_parameter("origin_x", origin_x_);
get_parameter("origin_y", origin_y_);
get_parameter("publish_frequency", map_publish_frequency_);
get_parameter("resolution", resolution_);
get_parameter("robot_base_frame", robot_base_frame_);
get_parameter("robot_radius", robot_radius_);
get_parameter("rolling_window", rolling_window_);
get_parameter("track_unknown_space", track_unknown_space_);
get_parameter("transform_tolerance", transform_tolerance_);
get_parameter("update_frequency", map_update_frequency_);
get_parameter("width", map_width_meters_);
get_parameter("plugins", plugin_names_);
get_parameter("filters", filter_names_);
auto node = shared_from_this();
if (plugin_names_ == default_plugins_) {
for (size_t i = 0; i < default_plugins_.size(); ++i) {
nav2_util::declare_parameter_if_not_declared(
node, default_plugins_[i] + ".plugin", rclcpp::ParameterValue(default_types_[i]));
}
}
plugin_types_.resize(plugin_names_.size());
filter_types_.resize(filter_names_.size());
// 1. All plugins must have 'plugin' param defined in their namespace to define the plugin type
for (size_t i = 0; i < plugin_names_.size(); ++i) {
plugin_types_[i] = nav2_util::get_plugin_type_param(node, plugin_names_[i]);
}
for (size_t i = 0; i < filter_names_.size(); ++i) {
filter_types_[i] = nav2_util::get_plugin_type_param(node, filter_names_[i]);
}
// 2. The map publish frequency cannot be 0 (to avoid a divde-by-zero)
if (map_publish_frequency_ > 0) {
publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
} else {
publish_cycle_ = rclcpp::Duration(-1s);
}
// 3. If the footprint has been specified, it must be in the correct format
use_radius_ = true;
if (footprint_ != "" && footprint_ != "[]") {
// Footprint parameter has been specified, try to convert it
std::vector<geometry_msgs::msg::Point> new_footprint;
if (makeFootprintFromString(footprint_, new_footprint)) {
// The specified footprint is valid, so we'll use that instead of the radius
use_radius_ = false;
} else {
// Footprint provided but invalid, so stay with the radius
RCLCPP_ERROR(
get_logger(), "The footprint parameter is invalid: \"%s\", using radius (%lf) instead",
footprint_.c_str(), robot_radius_);
}
}
}
void
Costmap2DROS::setRobotFootprint(const std::vector<geometry_msgs::msg::Point> & points)
{
unpadded_footprint_ = points;
padded_footprint_ = points;
padFootprint(padded_footprint_, footprint_padding_);
layered_costmap_->setFootprint(padded_footprint_);
}
void
Costmap2DROS::setRobotFootprintPolygon(
const geometry_msgs::msg::Polygon::SharedPtr footprint)
{
setRobotFootprint(toPointVector(footprint));
}
void
Costmap2DROS::getOrientedFootprint(std::vector<geometry_msgs::msg::Point> & oriented_footprint)
{
geometry_msgs::msg::PoseStamped global_pose;
if (!getRobotPose(global_pose)) {
return;
}
double yaw = tf2::getYaw(global_pose.pose.orientation);
transformFootprint(
global_pose.pose.position.x, global_pose.pose.position.y, yaw,
padded_footprint_, oriented_footprint);
}
void
Costmap2DROS::mapUpdateLoop(double frequency)
{
RCLCPP_DEBUG(get_logger(), "mapUpdateLoop frequency: %lf", frequency);
// the user might not want to run the loop every cycle
if (frequency == 0.0) {
return;
}
RCLCPP_DEBUG(get_logger(), "Entering loop");
rclcpp::WallRate r(frequency); // 200ms by default
while (rclcpp::ok() && !map_update_thread_shutdown_) {
nav2_util::ExecutionTimer timer;
// Execute after start() will complete plugins activation
if (!stopped_) {
// Lock while modifying layered costmap and publishing values
std::scoped_lock<std::mutex> lock(_dynamic_parameter_mutex);
// Measure the execution time of the updateMap method
timer.start();
updateMap();
timer.end();
RCLCPP_DEBUG(get_logger(), "Map update time: %.9f", timer.elapsed_time_in_seconds());
if (publish_cycle_ > rclcpp::Duration(0s) && layered_costmap_->isInitialized()) {
unsigned int x0, y0, xn, yn;
layered_costmap_->getBounds(&x0, &xn, &y0, &yn);
costmap_publisher_->updateBounds(x0, xn, y0, yn);
auto current_time = now();
if ((last_publish_ + publish_cycle_ < current_time) || // publish_cycle_ is due
(current_time < last_publish_)) // time has moved backwards, probably due to a switch to sim_time // NOLINT
{
RCLCPP_DEBUG(get_logger(), "Publish costmap at %s", name_.c_str());
costmap_publisher_->publishCostmap();
last_publish_ = current_time;
}
}
}
// Make sure to sleep for the remainder of our cycle time
r.sleep();
#if 0
// TODO(bpwilcox): find ROS2 equivalent or port for r.cycletime()
if (r.period() > tf2::durationFromSec(1 / frequency)) {
RCLCPP_WARN(
get_logger(),
"Costmap2DROS: Map update loop missed its desired rate of %.4fHz... "
"the loop actually took %.4f seconds", frequency, r.period());
}
#endif
}
}
void
Costmap2DROS::updateMap()
{
RCLCPP_DEBUG(get_logger(), "Updating map...");
if (!stop_updates_) {
// get global pose
geometry_msgs::msg::PoseStamped pose;
if (getRobotPose(pose)) {
const double & x = pose.pose.position.x;
const double & y = pose.pose.position.y;
const double yaw = tf2::getYaw(pose.pose.orientation);
layered_costmap_->updateMap(x, y, yaw);
auto footprint = std::make_unique<geometry_msgs::msg::PolygonStamped>();
footprint->header = pose.header;
transformFootprint(x, y, yaw, padded_footprint_, *footprint);
RCLCPP_DEBUG(get_logger(), "Publishing footprint");
footprint_pub_->publish(std::move(footprint));
initialized_ = true;
}
}
}
void
Costmap2DROS::start()
{
RCLCPP_INFO(get_logger(), "start");
std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
// check if we're stopped or just paused
if (stopped_) {
// if we're stopped we need to re-subscribe to topics
for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
plugin != plugins->end();
++plugin)
{
(*plugin)->activate();
}
for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
filter != filters->end();
++filter)
{
(*filter)->activate();
}
stopped_ = false;
}
stop_updates_ = false;
// block until the costmap is re-initialized.. meaning one update cycle has run
rclcpp::Rate r(20.0);
while (rclcpp::ok() && !initialized_) {
RCLCPP_DEBUG(get_logger(), "Sleeping, waiting for initialized_");
r.sleep();
}
}
void
Costmap2DROS::stop()
{
stop_updates_ = true;
// layered_costmap_ is set only if on_configure has been called
if (layered_costmap_) {
std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
// unsubscribe from topics
for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
plugin != plugins->end(); ++plugin)
{
(*plugin)->deactivate();
}
for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
filter != filters->end(); ++filter)
{
(*filter)->deactivate();
}
}
initialized_ = false;
stopped_ = true;
}
void
Costmap2DROS::pause()
{
stop_updates_ = true;
initialized_ = false;
}
void
Costmap2DROS::resume()
{
stop_updates_ = false;
// block until the costmap is re-initialized.. meaning one update cycle has run
rclcpp::Rate r(100.0);
while (!initialized_) {
r.sleep();
}
}
void
Costmap2DROS::resetLayers()
{
Costmap2D * top = layered_costmap_->getCostmap();
top->resetMap(0, 0, top->getSizeInCellsX(), top->getSizeInCellsY());
// Reset each of the plugins
std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
plugin != plugins->end(); ++plugin)
{
(*plugin)->reset();
}
for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
filter != filters->end(); ++filter)
{
(*filter)->reset();
}
}
bool
Costmap2DROS::getRobotPose(geometry_msgs::msg::PoseStamped & global_pose)
{
return nav2_util::getCurrentPose(
global_pose, *tf_buffer_,
global_frame_, robot_base_frame_, transform_tolerance_);
}
bool
Costmap2DROS::transformPoseToGlobalFrame(
const geometry_msgs::msg::PoseStamped & input_pose,
geometry_msgs::msg::PoseStamped & transformed_pose)
{
if (input_pose.header.frame_id == global_frame_) {
transformed_pose = input_pose;
return true;
} else {
return nav2_util::transformPoseInTargetFrame(
input_pose, transformed_pose, *tf_buffer_,
global_frame_, transform_tolerance_);
}
}
rcl_interfaces::msg::SetParametersResult
Costmap2DROS::dynamicParametersCallback(std::vector<rclcpp::Parameter> parameters)
{
auto result = rcl_interfaces::msg::SetParametersResult();
bool resize_map = false;
std::lock_guard<std::mutex> lock_reinit(_dynamic_parameter_mutex);
for (auto parameter : parameters) {
const auto & type = parameter.get_type();
const auto & name = parameter.get_name();
if (type == ParameterType::PARAMETER_DOUBLE) {
if (name == "robot_radius") {
robot_radius_ = parameter.as_double();
// Set the footprint
if (use_radius_) {
setRobotFootprint(makeFootprintFromRadius(robot_radius_));
}
} else if (name == "footprint_padding") {
footprint_padding_ = parameter.as_double();
padded_footprint_ = unpadded_footprint_;
padFootprint(padded_footprint_, footprint_padding_);
layered_costmap_->setFootprint(padded_footprint_);
} else if (name == "transform_tolerance") {
transform_tolerance_ = parameter.as_double();
} else if (name == "publish_frequency") {
map_publish_frequency_ = parameter.as_double();
if (map_publish_frequency_ > 0) {
publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
} else {
publish_cycle_ = rclcpp::Duration(-1s);
}
} else if (name == "resolution") {
resize_map = true;
resolution_ = parameter.as_double();
} else if (name == "origin_x") {
resize_map = true;
origin_x_ = parameter.as_double();
} else if (name == "origin_y") {
resize_map = true;
origin_y_ = parameter.as_double();
}
} else if (type == ParameterType::PARAMETER_INTEGER) {
if (name == "width") {
if (parameter.as_int() > 0) {
resize_map = true;
map_width_meters_ = parameter.as_int();
} else {
RCLCPP_ERROR(
get_logger(), "You try to set width of map to be negative or zero,"
" this isn't allowed, please give a positive value.");
result.successful = false;
return result;
}
} else if (name == "height") {
if (parameter.as_int() > 0) {
resize_map = true;
map_height_meters_ = parameter.as_int();
} else {
RCLCPP_ERROR(
get_logger(), "You try to set height of map to be negative or zero,"
" this isn't allowed, please give a positive value.");
result.successful = false;
return result;
}
}
} else if (type == ParameterType::PARAMETER_STRING) {
if (name == "footprint") {
footprint_ = parameter.as_string();
std::vector<geometry_msgs::msg::Point> new_footprint;
if (makeFootprintFromString(footprint_, new_footprint)) {
setRobotFootprint(new_footprint);
}
} else if (name == "robot_base_frame") {
// First, make sure that the transform between the robot base frame
// and the global frame is available
std::string tf_error;
RCLCPP_INFO(get_logger(), "Checking transform");
if (!tf_buffer_->canTransform(
global_frame_, parameter.as_string(), tf2::TimePointZero,
tf2::durationFromSec(1.0), &tf_error))
{
RCLCPP_WARN(
get_logger(), "Timed out waiting for transform from %s to %s"
" to become available, tf error: %s",
parameter.as_string().c_str(), global_frame_.c_str(), tf_error.c_str());
RCLCPP_WARN(
get_logger(), "Rejecting robot_base_frame change to %s , leaving it to its original"
" value of %s", parameter.as_string().c_str(), robot_base_frame_.c_str());
result.successful = false;
return result;
}
robot_base_frame_ = parameter.as_string();
}
}
}
if (resize_map && !layered_costmap_->isSizeLocked()) {
layered_costmap_->resizeMap(
(unsigned int)(map_width_meters_ / resolution_),
(unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
updateMap();
}
result.successful = true;
return result;
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,218 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include <nav2_costmap_2d/costmap_layer.hpp>
#include <stdexcept>
#include <algorithm>
namespace nav2_costmap_2d
{
void CostmapLayer::touch(
double x, double y, double * min_x, double * min_y, double * max_x,
double * max_y)
{
*min_x = std::min(x, *min_x);
*min_y = std::min(y, *min_y);
*max_x = std::max(x, *max_x);
*max_y = std::max(y, *max_y);
}
void CostmapLayer::matchSize()
{
Costmap2D * master = layered_costmap_->getCostmap();
resizeMap(
master->getSizeInCellsX(), master->getSizeInCellsY(), master->getResolution(),
master->getOriginX(), master->getOriginY());
}
void CostmapLayer::clearArea(int start_x, int start_y, int end_x, int end_y, bool invert)
{
current_ = false;
unsigned char * grid = getCharMap();
for (int x = 0; x < static_cast<int>(getSizeInCellsX()); x++) {
bool xrange = x > start_x && x < end_x;
for (int y = 0; y < static_cast<int>(getSizeInCellsY()); y++) {
if ((xrange && y > start_y && y < end_y) == invert) {
continue;
}
int index = getIndex(x, y);
if (grid[index] != NO_INFORMATION) {
grid[index] = NO_INFORMATION;
}
}
}
}
void CostmapLayer::addExtraBounds(double mx0, double my0, double mx1, double my1)
{
extra_min_x_ = std::min(mx0, extra_min_x_);
extra_max_x_ = std::max(mx1, extra_max_x_);
extra_min_y_ = std::min(my0, extra_min_y_);
extra_max_y_ = std::max(my1, extra_max_y_);
has_extra_bounds_ = true;
}
void CostmapLayer::useExtraBounds(double * min_x, double * min_y, double * max_x, double * max_y)
{
if (!has_extra_bounds_) {
return;
}
*min_x = std::min(extra_min_x_, *min_x);
*min_y = std::min(extra_min_y_, *min_y);
*max_x = std::max(extra_max_x_, *max_x);
*max_y = std::max(extra_max_y_, *max_y);
extra_min_x_ = 1e6;
extra_min_y_ = 1e6;
extra_max_x_ = -1e6;
extra_max_y_ = -1e6;
has_extra_bounds_ = false;
}
void CostmapLayer::updateWithMax(
nav2_costmap_2d::Costmap2D & master_grid, int min_i, int min_j,
int max_i,
int max_j)
{
if (!enabled_) {
return;
}
unsigned char * master_array = master_grid.getCharMap();
unsigned int span = master_grid.getSizeInCellsX();
for (int j = min_j; j < max_j; j++) {
unsigned int it = j * span + min_i;
for (int i = min_i; i < max_i; i++) {
if (costmap_[it] == NO_INFORMATION) {
it++;
continue;
}
unsigned char old_cost = master_array[it];
if (old_cost == NO_INFORMATION || old_cost < costmap_[it]) {
master_array[it] = costmap_[it];
}
it++;
}
}
}
void CostmapLayer::updateWithTrueOverwrite(
nav2_costmap_2d::Costmap2D & master_grid, int min_i,
int min_j,
int max_i,
int max_j)
{
if (!enabled_) {
return;
}
if (costmap_ == nullptr) {
throw std::runtime_error("Can't update costmap layer: It has't been initialized yet!");
}
unsigned char * master = master_grid.getCharMap();
unsigned int span = master_grid.getSizeInCellsX();
for (int j = min_j; j < max_j; j++) {
unsigned int it = span * j + min_i;
for (int i = min_i; i < max_i; i++) {
master[it] = costmap_[it];
it++;
}
}
}
void CostmapLayer::updateWithOverwrite(
nav2_costmap_2d::Costmap2D & master_grid,
int min_i, int min_j, int max_i, int max_j)
{
if (!enabled_) {
return;
}
unsigned char * master = master_grid.getCharMap();
unsigned int span = master_grid.getSizeInCellsX();
for (int j = min_j; j < max_j; j++) {
unsigned int it = span * j + min_i;
for (int i = min_i; i < max_i; i++) {
if (costmap_[it] != NO_INFORMATION) {
master[it] = costmap_[it];
}
it++;
}
}
}
void CostmapLayer::updateWithAddition(
nav2_costmap_2d::Costmap2D & master_grid,
int min_i, int min_j, int max_i, int max_j)
{
if (!enabled_) {
return;
}
unsigned char * master_array = master_grid.getCharMap();
unsigned int span = master_grid.getSizeInCellsX();
for (int j = min_j; j < max_j; j++) {
unsigned int it = j * span + min_i;
for (int i = min_i; i < max_i; i++) {
if (costmap_[it] == NO_INFORMATION) {
it++;
continue;
}
unsigned char old_cost = master_array[it];
if (old_cost == NO_INFORMATION) {
master_array[it] = costmap_[it];
} else {
int sum = old_cost + costmap_[it];
if (sum >= nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE) {
master_array[it] = nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE - 1;
} else {
master_array[it] = sum;
}
}
it++;
}
}
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <nav2_costmap_2d/costmap_math.hpp>
#include <vector>
double distanceToLine(double pX, double pY, double x0, double y0, double x1, double y1)
{
double A = pX - x0;
double B = pY - y0;
double C = x1 - x0;
double D = y1 - y0;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0) {
xx = x0;
yy = y0;
} else if (param > 1) {
xx = x1;
yy = y1;
} else {
xx = x0 + param * C;
yy = y0 + param * D;
}
return distance(pX, pY, xx, yy);
}
@@ -0,0 +1,97 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <memory>
#include "nav2_costmap_2d/costmap_subscriber.hpp"
namespace nav2_costmap_2d
{
CostmapSubscriber::CostmapSubscriber(
const nav2_util::LifecycleNode::WeakPtr & parent,
const std::string & topic_name)
: topic_name_(topic_name)
{
auto node = parent.lock();
costmap_sub_ = node->create_subscription<nav2_msgs::msg::Costmap>(
topic_name_,
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
std::bind(&CostmapSubscriber::costmapCallback, this, std::placeholders::_1));
}
CostmapSubscriber::CostmapSubscriber(
const rclcpp::Node::WeakPtr & parent,
const std::string & topic_name)
: topic_name_(topic_name)
{
auto node = parent.lock();
costmap_sub_ = node->create_subscription<nav2_msgs::msg::Costmap>(
topic_name_,
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
std::bind(&CostmapSubscriber::costmapCallback, this, std::placeholders::_1));
}
std::shared_ptr<Costmap2D> CostmapSubscriber::getCostmap()
{
if (!costmap_received_) {
throw std::runtime_error("Costmap is not available");
}
toCostmap2D();
return costmap_;
}
void CostmapSubscriber::toCostmap2D()
{
auto current_costmap_msg = std::atomic_load(&costmap_msg_);
if (costmap_ == nullptr) {
costmap_ = std::make_shared<Costmap2D>(
current_costmap_msg->metadata.size_x, current_costmap_msg->metadata.size_y,
current_costmap_msg->metadata.resolution, current_costmap_msg->metadata.origin.position.x,
current_costmap_msg->metadata.origin.position.y);
} else if (costmap_->getSizeInCellsX() != current_costmap_msg->metadata.size_x || // NOLINT
costmap_->getSizeInCellsY() != current_costmap_msg->metadata.size_y ||
costmap_->getResolution() != current_costmap_msg->metadata.resolution ||
costmap_->getOriginX() != current_costmap_msg->metadata.origin.position.x ||
costmap_->getOriginY() != current_costmap_msg->metadata.origin.position.y)
{
// Update the size of the costmap
costmap_->resizeMap(
current_costmap_msg->metadata.size_x, current_costmap_msg->metadata.size_y,
current_costmap_msg->metadata.resolution,
current_costmap_msg->metadata.origin.position.x,
current_costmap_msg->metadata.origin.position.y);
}
unsigned char * master_array = costmap_->getCharMap();
unsigned int index = 0;
for (unsigned int i = 0; i < current_costmap_msg->metadata.size_x; ++i) {
for (unsigned int j = 0; j < current_costmap_msg->metadata.size_y; ++j) {
master_array[index] = current_costmap_msg->data[index];
++index;
}
}
}
void CostmapSubscriber::costmapCallback(const nav2_msgs::msg::Costmap::SharedPtr msg)
{
std::atomic_store(&costmap_msg_, msg);
if (!costmap_received_) {
costmap_received_ = true;
}
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,103 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Modified by: Shivang Patel (shivaan14@gmail.com)
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include "nav2_costmap_2d/costmap_topic_collision_checker.hpp"
#include "nav2_costmap_2d/cost_values.hpp"
#include "nav2_costmap_2d/exceptions.hpp"
#include "nav2_costmap_2d/footprint.hpp"
#include "nav2_util/line_iterator.hpp"
using namespace std::chrono_literals;
namespace nav2_costmap_2d
{
CostmapTopicCollisionChecker::CostmapTopicCollisionChecker(
CostmapSubscriber & costmap_sub,
FootprintSubscriber & footprint_sub,
std::string name)
: name_(name),
costmap_sub_(costmap_sub),
footprint_sub_(footprint_sub),
collision_checker_(nullptr)
{}
bool CostmapTopicCollisionChecker::isCollisionFree(
const geometry_msgs::msg::Pose2D & pose,
bool fetch_costmap_and_footprint)
{
try {
if (scorePose(pose, fetch_costmap_and_footprint) >= LETHAL_OBSTACLE) {
return false;
}
return true;
} catch (const IllegalPoseException & e) {
RCLCPP_ERROR(rclcpp::get_logger(name_), "%s", e.what());
return false;
} catch (const CollisionCheckerException & e) {
RCLCPP_ERROR(rclcpp::get_logger(name_), "%s", e.what());
return false;
} catch (...) {
RCLCPP_ERROR(rclcpp::get_logger(name_), "Failed to check pose score!");
return false;
}
}
double CostmapTopicCollisionChecker::scorePose(
const geometry_msgs::msg::Pose2D & pose,
bool fetch_costmap_and_footprint)
{
if (fetch_costmap_and_footprint) {
try {
collision_checker_.setCostmap(costmap_sub_.getCostmap());
} catch (const std::runtime_error & e) {
throw CollisionCheckerException(e.what());
}
}
unsigned int cell_x, cell_y;
if (!collision_checker_.worldToMap(pose.x, pose.y, cell_x, cell_y)) {
RCLCPP_DEBUG(rclcpp::get_logger(name_), "Map Cell: [%d, %d]", cell_x, cell_y);
throw IllegalPoseException(name_, "Pose Goes Off Grid.");
}
return collision_checker_.footprintCost(getFootprint(pose, fetch_costmap_and_footprint));
}
Footprint CostmapTopicCollisionChecker::getFootprint(
const geometry_msgs::msg::Pose2D & pose,
bool fetch_latest_footprint)
{
if (fetch_latest_footprint) {
std_msgs::msg::Header header;
if (!footprint_sub_.getFootprintInRobotFrame(footprint_, header)) {
throw CollisionCheckerException("Current footprint not available.");
}
}
Footprint footprint;
transformFootprint(pose.x, pose.y, pose.theta, footprint_, footprint);
return footprint;
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,220 @@
/*
* Copyright (c) 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "nav2_costmap_2d/footprint.hpp"
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include "geometry_msgs/msg/point32.hpp"
#include "nav2_costmap_2d/array_parser.hpp"
#include "nav2_costmap_2d/costmap_math.hpp"
namespace nav2_costmap_2d
{
void calculateMinAndMaxDistances(
const std::vector<geometry_msgs::msg::Point> & footprint,
double & min_dist, double & max_dist)
{
min_dist = std::numeric_limits<double>::max();
max_dist = 0.0;
if (footprint.size() <= 2) {
return;
}
for (unsigned int i = 0; i < footprint.size() - 1; ++i) {
// check the distance from the robot center point to the first vertex
double vertex_dist = distance(0.0, 0.0, footprint[i].x, footprint[i].y);
double edge_dist = distanceToLine(
0.0, 0.0, footprint[i].x, footprint[i].y,
footprint[i + 1].x, footprint[i + 1].y);
min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
}
// we also need to do the last vertex and the first vertex
double vertex_dist = distance(0.0, 0.0, footprint.back().x, footprint.back().y);
double edge_dist = distanceToLine(
0.0, 0.0, footprint.back().x, footprint.back().y,
footprint.front().x, footprint.front().y);
min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
}
geometry_msgs::msg::Point32 toPoint32(geometry_msgs::msg::Point pt)
{
geometry_msgs::msg::Point32 point32;
point32.x = pt.x;
point32.y = pt.y;
point32.z = pt.z;
return point32;
}
geometry_msgs::msg::Point toPoint(geometry_msgs::msg::Point32 pt)
{
geometry_msgs::msg::Point point;
point.x = pt.x;
point.y = pt.y;
point.z = pt.z;
return point;
}
geometry_msgs::msg::Polygon toPolygon(std::vector<geometry_msgs::msg::Point> pts)
{
geometry_msgs::msg::Polygon polygon;
for (unsigned int i = 0; i < pts.size(); i++) {
polygon.points.push_back(toPoint32(pts[i]));
}
return polygon;
}
std::vector<geometry_msgs::msg::Point> toPointVector(geometry_msgs::msg::Polygon::SharedPtr polygon)
{
std::vector<geometry_msgs::msg::Point> pts;
for (unsigned int i = 0; i < polygon->points.size(); i++) {
pts.push_back(toPoint(polygon->points[i]));
}
return pts;
}
void transformFootprint(
double x, double y, double theta,
const std::vector<geometry_msgs::msg::Point> & footprint_spec,
std::vector<geometry_msgs::msg::Point> & oriented_footprint)
{
// build the oriented footprint at a given location
oriented_footprint.resize(footprint_spec.size());
double cos_th = cos(theta);
double sin_th = sin(theta);
for (unsigned int i = 0; i < footprint_spec.size(); ++i) {
double new_x = x + (footprint_spec[i].x * cos_th - footprint_spec[i].y * sin_th);
double new_y = y + (footprint_spec[i].x * sin_th + footprint_spec[i].y * cos_th);
geometry_msgs::msg::Point & new_pt = oriented_footprint[i];
new_pt.x = new_x;
new_pt.y = new_y;
}
}
void transformFootprint(
double x, double y, double theta,
const std::vector<geometry_msgs::msg::Point> & footprint_spec,
geometry_msgs::msg::PolygonStamped & oriented_footprint)
{
// build the oriented footprint at a given location
oriented_footprint.polygon.points.clear();
double cos_th = cos(theta);
double sin_th = sin(theta);
for (unsigned int i = 0; i < footprint_spec.size(); ++i) {
geometry_msgs::msg::Point32 new_pt;
new_pt.x = x + (footprint_spec[i].x * cos_th - footprint_spec[i].y * sin_th);
new_pt.y = y + (footprint_spec[i].x * sin_th + footprint_spec[i].y * cos_th);
oriented_footprint.polygon.points.push_back(new_pt);
}
}
void padFootprint(std::vector<geometry_msgs::msg::Point> & footprint, double padding)
{
// pad footprint in place
for (unsigned int i = 0; i < footprint.size(); i++) {
geometry_msgs::msg::Point & pt = footprint[i];
pt.x += sign0(pt.x) * padding;
pt.y += sign0(pt.y) * padding;
}
}
std::vector<geometry_msgs::msg::Point> makeFootprintFromRadius(double radius)
{
std::vector<geometry_msgs::msg::Point> points;
// Loop over 16 angles around a circle making a point each time
int N = 16;
geometry_msgs::msg::Point pt;
for (int i = 0; i < N; ++i) {
double angle = i * 2 * M_PI / N;
pt.x = cos(angle) * radius;
pt.y = sin(angle) * radius;
points.push_back(pt);
}
return points;
}
bool makeFootprintFromString(
const std::string & footprint_string,
std::vector<geometry_msgs::msg::Point> & footprint)
{
std::string error;
std::vector<std::vector<float>> vvf = parseVVF(footprint_string, error);
if (error != "") {
RCLCPP_ERROR(
rclcpp::get_logger(
"nav2_costmap_2d"), "Error parsing footprint parameter: '%s'", error.c_str());
RCLCPP_ERROR(
rclcpp::get_logger(
"nav2_costmap_2d"), " Footprint string was '%s'.", footprint_string.c_str());
return false;
}
// convert vvf into points.
if (vvf.size() < 3) {
RCLCPP_ERROR(
rclcpp::get_logger(
"nav2_costmap_2d"),
"You must specify at least three points for the robot footprint, reverting to previous footprint."); //NOLINT
return false;
}
footprint.reserve(vvf.size());
for (unsigned int i = 0; i < vvf.size(); i++) {
if (vvf[i].size() == 2) {
geometry_msgs::msg::Point point;
point.x = vvf[i][0];
point.y = vvf[i][1];
point.z = 0;
footprint.push_back(point);
} else {
RCLCPP_ERROR(
rclcpp::get_logger(
"nav2_costmap_2d"),
"Points in the footprint specification must be pairs of numbers. Found a point with %d numbers.", //NOLINT
static_cast<int>(vvf[i].size()));
return false;
}
}
return true;
}
} // end namespace nav2_costmap_2d
@@ -0,0 +1,149 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Modified by: Shivang Patel (shivaang14@gmail.com)
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include "nav2_costmap_2d/footprint_collision_checker.hpp"
#include "nav2_costmap_2d/cost_values.hpp"
#include "nav2_costmap_2d/exceptions.hpp"
#include "nav2_costmap_2d/footprint.hpp"
#include "nav2_util/line_iterator.hpp"
using namespace std::chrono_literals;
namespace nav2_costmap_2d
{
template<typename CostmapT>
FootprintCollisionChecker<CostmapT>::FootprintCollisionChecker()
: costmap_(nullptr)
{
}
template<typename CostmapT>
FootprintCollisionChecker<CostmapT>::FootprintCollisionChecker(
CostmapT costmap)
: costmap_(costmap)
{
}
template<typename CostmapT>
double FootprintCollisionChecker<CostmapT>::footprintCost(const Footprint footprint)
{
// now we really have to lay down the footprint in the costmap_ grid
unsigned int x0, x1, y0, y1;
double footprint_cost = 0.0;
// get the cell coord of the first point
if (!worldToMap(footprint[0].x, footprint[0].y, x0, y0)) {
return static_cast<double>(LETHAL_OBSTACLE);
}
// cache the start to eliminate a worldToMap call
unsigned int xstart = x0;
unsigned int ystart = y0;
// we need to rasterize each line in the footprint
for (unsigned int i = 0; i < footprint.size() - 1; ++i) {
// get the cell coord of the second point
if (!worldToMap(footprint[i + 1].x, footprint[i + 1].y, x1, y1)) {
return static_cast<double>(LETHAL_OBSTACLE);
}
footprint_cost = std::max(lineCost(x0, x1, y0, y1), footprint_cost);
// the second point is next iteration's first point
x0 = x1;
y0 = y1;
// if in collision, no need to continue
if (footprint_cost == static_cast<double>(LETHAL_OBSTACLE)) {
return footprint_cost;
}
}
// we also need to connect the first point in the footprint to the last point
// the last iteration's x1, y1 are the last footprint point's coordinates
return std::max(lineCost(xstart, x1, ystart, y1), footprint_cost);
}
template<typename CostmapT>
double FootprintCollisionChecker<CostmapT>::lineCost(int x0, int x1, int y0, int y1) const
{
double line_cost = 0.0;
double point_cost = -1.0;
for (nav2_util::LineIterator line(x0, y0, x1, y1); line.isValid(); line.advance()) {
point_cost = pointCost(line.getX(), line.getY()); // Score the current point
// if in collision, no need to continue
if (point_cost == static_cast<double>(LETHAL_OBSTACLE)) {
return point_cost;
}
if (line_cost < point_cost) {
line_cost = point_cost;
}
}
return line_cost;
}
template<typename CostmapT>
bool FootprintCollisionChecker<CostmapT>::worldToMap(
double wx, double wy, unsigned int & mx, unsigned int & my)
{
return costmap_->worldToMap(wx, wy, mx, my);
}
template<typename CostmapT>
double FootprintCollisionChecker<CostmapT>::pointCost(int x, int y) const
{
return costmap_->getCost(x, y);
}
template<typename CostmapT>
void FootprintCollisionChecker<CostmapT>::setCostmap(CostmapT costmap)
{
costmap_ = costmap;
}
template<typename CostmapT>
double FootprintCollisionChecker<CostmapT>::footprintCostAtPose(
double x, double y, double theta, const Footprint footprint)
{
double cos_th = cos(theta);
double sin_th = sin(theta);
Footprint oriented_footprint;
for (unsigned int i = 0; i < footprint.size(); ++i) {
geometry_msgs::msg::Point new_pt;
new_pt.x = x + (footprint[i].x * cos_th - footprint[i].y * sin_th);
new_pt.y = y + (footprint[i].x * sin_th + footprint[i].y * cos_th);
oriented_footprint.push_back(new_pt);
}
return footprintCost(oriented_footprint);
}
// declare our valid template parameters
template class FootprintCollisionChecker<std::shared_ptr<nav2_costmap_2d::Costmap2D>>;
template class FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>;
} // namespace nav2_costmap_2d
@@ -0,0 +1,117 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include <memory>
#include "nav2_costmap_2d/footprint_subscriber.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#include "tf2/utils.h"
#pragma GCC diagnostic pop
namespace nav2_costmap_2d
{
FootprintSubscriber::FootprintSubscriber(
const nav2_util::LifecycleNode::WeakPtr & parent,
const std::string & topic_name,
tf2_ros::Buffer & tf,
std::string robot_base_frame,
double transform_tolerance)
: tf_(tf),
robot_base_frame_(robot_base_frame),
transform_tolerance_(transform_tolerance)
{
auto node = parent.lock();
footprint_sub_ = node->create_subscription<geometry_msgs::msg::PolygonStamped>(
topic_name, rclcpp::SystemDefaultsQoS(),
std::bind(&FootprintSubscriber::footprint_callback, this, std::placeholders::_1));
}
FootprintSubscriber::FootprintSubscriber(
const rclcpp::Node::WeakPtr & parent,
const std::string & topic_name,
tf2_ros::Buffer & tf,
std::string robot_base_frame,
double transform_tolerance)
: tf_(tf),
robot_base_frame_(robot_base_frame),
transform_tolerance_(transform_tolerance)
{
auto node = parent.lock();
footprint_sub_ = node->create_subscription<geometry_msgs::msg::PolygonStamped>(
topic_name, rclcpp::SystemDefaultsQoS(),
std::bind(&FootprintSubscriber::footprint_callback, this, std::placeholders::_1));
}
bool
FootprintSubscriber::getFootprintRaw(
std::vector<geometry_msgs::msg::Point> & footprint,
std_msgs::msg::Header & footprint_header)
{
if (!footprint_received_) {
return false;
}
auto current_footprint = std::atomic_load(&footprint_);
footprint = toPointVector(
std::make_shared<geometry_msgs::msg::Polygon>(current_footprint->polygon));
footprint_header = current_footprint->header;
return true;
}
bool
FootprintSubscriber::getFootprintInRobotFrame(
std::vector<geometry_msgs::msg::Point> & footprint,
std_msgs::msg::Header & footprint_header)
{
if (!getFootprintRaw(footprint, footprint_header)) {
return false;
}
geometry_msgs::msg::PoseStamped current_pose;
if (!nav2_util::getCurrentPose(
current_pose, tf_, footprint_header.frame_id, robot_base_frame_,
transform_tolerance_, footprint_header.stamp))
{
return false;
}
double x = current_pose.pose.position.x;
double y = current_pose.pose.position.y;
double theta = tf2::getYaw(current_pose.pose.orientation);
std::vector<geometry_msgs::msg::Point> temp;
transformFootprint(-x, -y, 0, footprint, temp);
transformFootprint(0, 0, -theta, temp, footprint);
footprint_header.frame_id = robot_base_frame_;
footprint_header.stamp = current_pose.header.stamp;
return true;
}
void
FootprintSubscriber::footprint_callback(const geometry_msgs::msg::PolygonStamped::SharedPtr msg)
{
std::atomic_store(&footprint_, msg);
if (!footprint_received_) {
footprint_received_ = true;
}
}
} // namespace nav2_costmap_2d
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "nav2_costmap_2d/layer.hpp"
#include <string>
#include <vector>
#include "nav2_util/node_utils.hpp"
namespace nav2_costmap_2d
{
Layer::Layer()
: layered_costmap_(nullptr),
name_(),
tf_(nullptr),
current_(false),
enabled_(false)
{}
void
Layer::initialize(
LayeredCostmap * parent,
std::string name,
tf2_ros::Buffer * tf,
const nav2_util::LifecycleNode::WeakPtr & node,
rclcpp::CallbackGroup::SharedPtr callback_group)
{
layered_costmap_ = parent;
name_ = name;
tf_ = tf;
node_ = node;
callback_group_ = callback_group;
{
auto node_shared_ptr = node_.lock();
logger_ = node_shared_ptr->get_logger();
clock_ = node_shared_ptr->get_clock();
}
onInitialize();
}
const std::vector<geometry_msgs::msg::Point> &
Layer::getFootprint() const
{
return layered_costmap_->getFootprint();
}
void
Layer::declareParameter(
const std::string & param_name,
const rclcpp::ParameterValue & value)
{
auto node = node_.lock();
if (!node) {
throw std::runtime_error{"Failed to lock node"};
}
local_params_.insert(param_name);
nav2_util::declare_parameter_if_not_declared(
node, getFullName(param_name), value);
}
void
Layer::declareParameter(
const std::string & param_name,
const rclcpp::ParameterType & param_type)
{
auto node = node_.lock();
if (!node) {
throw std::runtime_error{"Failed to lock node"};
}
local_params_.insert(param_name);
nav2_util::declare_parameter_if_not_declared(
node, getFullName(param_name), param_type);
}
bool
Layer::hasParameter(const std::string & param_name)
{
auto node = node_.lock();
if (!node) {
throw std::runtime_error{"Failed to lock node"};
}
return node->has_parameter(getFullName(param_name));
}
std::string
Layer::getFullName(const std::string & param_name)
{
return std::string(name_ + "." + param_name);
}
} // end namespace nav2_costmap_2d
@@ -0,0 +1,296 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
* David V. Lu!!
*********************************************************************/
#include "nav2_costmap_2d/layered_costmap.hpp"
#include <algorithm>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include <limits>
#include "nav2_costmap_2d/footprint.hpp"
using std::vector;
namespace nav2_costmap_2d
{
LayeredCostmap::LayeredCostmap(std::string global_frame, bool rolling_window, bool track_unknown)
: primary_costmap_(), combined_costmap_(),
global_frame_(global_frame),
rolling_window_(rolling_window),
current_(false),
minx_(0.0),
miny_(0.0),
maxx_(0.0),
maxy_(0.0),
bx0_(0),
bxn_(0),
by0_(0),
byn_(0),
initialized_(false),
size_locked_(false),
circumscribed_radius_(1.0),
inscribed_radius_(0.1)
{
if (track_unknown) {
primary_costmap_.setDefaultValue(255);
combined_costmap_.setDefaultValue(255);
} else {
primary_costmap_.setDefaultValue(0);
combined_costmap_.setDefaultValue(0);
}
}
LayeredCostmap::~LayeredCostmap()
{
while (plugins_.size() > 0) {
plugins_.pop_back();
}
while (filters_.size() > 0) {
filters_.pop_back();
}
}
void LayeredCostmap::addPlugin(std::shared_ptr<Layer> plugin)
{
std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
plugins_.push_back(plugin);
}
void LayeredCostmap::addFilter(std::shared_ptr<Layer> filter)
{
std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
filters_.push_back(filter);
}
void LayeredCostmap::resizeMap(
unsigned int size_x, unsigned int size_y, double resolution,
double origin_x,
double origin_y,
bool size_locked)
{
std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
size_locked_ = size_locked;
primary_costmap_.resizeMap(size_x, size_y, resolution, origin_x, origin_y);
combined_costmap_.resizeMap(size_x, size_y, resolution, origin_x, origin_y);
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end(); ++plugin)
{
(*plugin)->matchSize();
}
for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
filter != filters_.end(); ++filter)
{
(*filter)->matchSize();
}
}
bool LayeredCostmap::isOutofBounds(double robot_x, double robot_y)
{
unsigned int mx, my;
return !combined_costmap_.worldToMap(robot_x, robot_y, mx, my);
}
void LayeredCostmap::updateMap(double robot_x, double robot_y, double robot_yaw)
{
// Lock for the remainder of this function, some plugins (e.g. VoxelLayer)
// implement thread unsafe updateBounds() functions.
std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
// if we're using a rolling buffer costmap...
// we need to update the origin using the robot's position
if (rolling_window_) {
double new_origin_x = robot_x - combined_costmap_.getSizeInMetersX() / 2;
double new_origin_y = robot_y - combined_costmap_.getSizeInMetersY() / 2;
primary_costmap_.updateOrigin(new_origin_x, new_origin_y);
combined_costmap_.updateOrigin(new_origin_x, new_origin_y);
}
if (isOutofBounds(robot_x, robot_y)) {
RCLCPP_WARN(
rclcpp::get_logger("nav2_costmap_2d"),
"Robot is out of bounds of the costmap!");
}
if (plugins_.size() == 0 && filters_.size() == 0) {
return;
}
minx_ = miny_ = std::numeric_limits<double>::max();
maxx_ = maxy_ = std::numeric_limits<double>::lowest();
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end(); ++plugin)
{
double prev_minx = minx_;
double prev_miny = miny_;
double prev_maxx = maxx_;
double prev_maxy = maxy_;
(*plugin)->updateBounds(robot_x, robot_y, robot_yaw, &minx_, &miny_, &maxx_, &maxy_);
if (minx_ > prev_minx || miny_ > prev_miny || maxx_ < prev_maxx || maxy_ < prev_maxy) {
RCLCPP_WARN(
rclcpp::get_logger(
"nav2_costmap_2d"), "Illegal bounds change, was [tl: (%f, %f), br: (%f, %f)], but "
"is now [tl: (%f, %f), br: (%f, %f)]. The offending layer is %s",
prev_minx, prev_miny, prev_maxx, prev_maxy,
minx_, miny_, maxx_, maxy_,
(*plugin)->getName().c_str());
}
}
for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
filter != filters_.end(); ++filter)
{
double prev_minx = minx_;
double prev_miny = miny_;
double prev_maxx = maxx_;
double prev_maxy = maxy_;
(*filter)->updateBounds(robot_x, robot_y, robot_yaw, &minx_, &miny_, &maxx_, &maxy_);
if (minx_ > prev_minx || miny_ > prev_miny || maxx_ < prev_maxx || maxy_ < prev_maxy) {
RCLCPP_WARN(
rclcpp::get_logger(
"nav2_costmap_2d"), "Illegal bounds change, was [tl: (%f, %f), br: (%f, %f)], but "
"is now [tl: (%f, %f), br: (%f, %f)]. The offending filter is %s",
prev_minx, prev_miny, prev_maxx, prev_maxy,
minx_, miny_, maxx_, maxy_,
(*filter)->getName().c_str());
}
}
int x0, xn, y0, yn;
combined_costmap_.worldToMapEnforceBounds(minx_, miny_, x0, y0);
combined_costmap_.worldToMapEnforceBounds(maxx_, maxy_, xn, yn);
x0 = std::max(0, x0);
xn = std::min(static_cast<int>(combined_costmap_.getSizeInCellsX()), xn + 1);
y0 = std::max(0, y0);
yn = std::min(static_cast<int>(combined_costmap_.getSizeInCellsY()), yn + 1);
RCLCPP_DEBUG(
rclcpp::get_logger(
"nav2_costmap_2d"), "Updating area x: [%d, %d] y: [%d, %d]", x0, xn, y0, yn);
if (xn < x0 || yn < y0) {
return;
}
if (filters_.size() == 0) {
// If there are no filters enabled just update costmap sequentially by each plugin
combined_costmap_.resetMap(x0, y0, xn, yn);
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end(); ++plugin)
{
(*plugin)->updateCosts(combined_costmap_, x0, y0, xn, yn);
}
} else {
// Costmap Filters enabled
// 1. Update costmap by plugins
primary_costmap_.resetMap(x0, y0, xn, yn);
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end(); ++plugin)
{
(*plugin)->updateCosts(primary_costmap_, x0, y0, xn, yn);
}
// 2. Copy processed costmap window to a final costmap.
// primary_costmap_ remain to be untouched for further usage by plugins.
if (!combined_costmap_.copyWindow(primary_costmap_, x0, y0, xn, yn, x0, y0)) {
RCLCPP_ERROR(
rclcpp::get_logger("nav2_costmap_2d"),
"Can not copy costmap (%i,%i)..(%i,%i) window",
x0, y0, xn, yn);
throw std::runtime_error{"Can not copy costmap"};
}
// 3. Apply filters over the plugins in order to make filters' work
// not being considered by plugins on next updateMap() calls
for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
filter != filters_.end(); ++filter)
{
(*filter)->updateCosts(combined_costmap_, x0, y0, xn, yn);
}
}
bx0_ = x0;
bxn_ = xn;
by0_ = y0;
byn_ = yn;
initialized_ = true;
}
bool LayeredCostmap::isCurrent()
{
current_ = true;
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end(); ++plugin)
{
current_ = current_ && ((*plugin)->isCurrent() || !(*plugin)->isEnabled());
}
for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
filter != filters_.end(); ++filter)
{
current_ = current_ && ((*filter)->isCurrent() || !(*filter)->isEnabled());
}
return current_;
}
void LayeredCostmap::setFootprint(const std::vector<geometry_msgs::msg::Point> & footprint_spec)
{
footprint_ = footprint_spec;
nav2_costmap_2d::calculateMinAndMaxDistances(
footprint_spec,
inscribed_radius_, circumscribed_radius_);
for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
plugin != plugins_.end();
++plugin)
{
(*plugin)->onFootprintChanged();
}
for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
filter != filters_.end();
++filter)
{
(*filter)->onFootprintChanged();
}
}
} // namespace nav2_costmap_2d
@@ -0,0 +1,236 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, 2013, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Eitan Marder-Eppstein
*********************************************************************/
#include "nav2_costmap_2d/observation_buffer.hpp"
#include <algorithm>
#include <list>
#include <string>
#include <vector>
#include <chrono>
#include "tf2/convert.h"
#include "sensor_msgs/point_cloud2_iterator.hpp"
using namespace std::chrono_literals;
namespace nav2_costmap_2d
{
ObservationBuffer::ObservationBuffer(
const nav2_util::LifecycleNode::WeakPtr & parent,
std::string topic_name,
double observation_keep_time,
double expected_update_rate,
double min_obstacle_height, double max_obstacle_height, double obstacle_max_range,
double obstacle_min_range,
double raytrace_max_range, double raytrace_min_range, tf2_ros::Buffer & tf2_buffer,
std::string global_frame,
std::string sensor_frame,
tf2::Duration tf_tolerance)
: tf2_buffer_(tf2_buffer),
observation_keep_time_(rclcpp::Duration::from_seconds(observation_keep_time)),
expected_update_rate_(rclcpp::Duration::from_seconds(expected_update_rate)),
global_frame_(global_frame),
sensor_frame_(sensor_frame),
topic_name_(topic_name),
min_obstacle_height_(min_obstacle_height), max_obstacle_height_(max_obstacle_height),
obstacle_max_range_(obstacle_max_range), obstacle_min_range_(obstacle_min_range),
raytrace_max_range_(raytrace_max_range), raytrace_min_range_(
raytrace_min_range), tf_tolerance_(tf_tolerance)
{
auto node = parent.lock();
clock_ = node->get_clock();
logger_ = node->get_logger();
last_updated_ = node->now();
}
ObservationBuffer::~ObservationBuffer()
{
}
void ObservationBuffer::bufferCloud(const sensor_msgs::msg::PointCloud2 & cloud)
{
geometry_msgs::msg::PointStamped global_origin;
// create a new observation on the list to be populated
observation_list_.push_front(Observation());
// check whether the origin frame has been set explicitly
// or whether we should get it from the cloud
std::string origin_frame = sensor_frame_ == "" ? cloud.header.frame_id : sensor_frame_;
try {
// given these observations come from sensors...
// we'll need to store the origin pt of the sensor
geometry_msgs::msg::PointStamped local_origin;
local_origin.header.stamp = cloud.header.stamp;
local_origin.header.frame_id = origin_frame;
local_origin.point.x = 0;
local_origin.point.y = 0;
local_origin.point.z = 0;
tf2_buffer_.transform(local_origin, global_origin, global_frame_, tf_tolerance_);
tf2::convert(global_origin.point, observation_list_.front().origin_);
// make sure to pass on the raytrace/obstacle range
// of the observation buffer to the observations
observation_list_.front().raytrace_max_range_ = raytrace_max_range_;
observation_list_.front().raytrace_min_range_ = raytrace_min_range_;
observation_list_.front().obstacle_max_range_ = obstacle_max_range_;
observation_list_.front().obstacle_min_range_ = obstacle_min_range_;
sensor_msgs::msg::PointCloud2 global_frame_cloud;
// transform the point cloud
tf2_buffer_.transform(cloud, global_frame_cloud, global_frame_, tf_tolerance_);
global_frame_cloud.header.stamp = cloud.header.stamp;
// now we need to remove observations from the cloud that are below
// or above our height thresholds
sensor_msgs::msg::PointCloud2 & observation_cloud = *(observation_list_.front().cloud_);
observation_cloud.height = global_frame_cloud.height;
observation_cloud.width = global_frame_cloud.width;
observation_cloud.fields = global_frame_cloud.fields;
observation_cloud.is_bigendian = global_frame_cloud.is_bigendian;
observation_cloud.point_step = global_frame_cloud.point_step;
observation_cloud.row_step = global_frame_cloud.row_step;
observation_cloud.is_dense = global_frame_cloud.is_dense;
unsigned int cloud_size = global_frame_cloud.height * global_frame_cloud.width;
sensor_msgs::PointCloud2Modifier modifier(observation_cloud);
modifier.resize(cloud_size);
unsigned int point_count = 0;
// copy over the points that are within our height bounds
sensor_msgs::PointCloud2Iterator<float> iter_z(global_frame_cloud, "z");
std::vector<unsigned char>::const_iterator iter_global = global_frame_cloud.data.begin(),
iter_global_end = global_frame_cloud.data.end();
std::vector<unsigned char>::iterator iter_obs = observation_cloud.data.begin();
for (; iter_global != iter_global_end; ++iter_z, iter_global +=
global_frame_cloud.point_step)
{
if ((*iter_z) <= max_obstacle_height_ &&
(*iter_z) >= min_obstacle_height_)
{
std::copy(iter_global, iter_global + global_frame_cloud.point_step, iter_obs);
iter_obs += global_frame_cloud.point_step;
++point_count;
}
}
// resize the cloud for the number of legal points
modifier.resize(point_count);
observation_cloud.header.stamp = cloud.header.stamp;
observation_cloud.header.frame_id = global_frame_cloud.header.frame_id;
} catch (tf2::TransformException & ex) {
// if an exception occurs, we need to remove the empty observation from the list
observation_list_.pop_front();
RCLCPP_ERROR(
logger_,
"TF Exception that should never happen for sensor frame: %s, cloud frame: %s, %s",
sensor_frame_.c_str(),
cloud.header.frame_id.c_str(), ex.what());
return;
}
// if the update was successful, we want to update the last updated time
last_updated_ = clock_->now();
// we'll also remove any stale observations from the list
purgeStaleObservations();
}
// returns a copy of the observations
void ObservationBuffer::getObservations(std::vector<Observation> & observations)
{
// first... let's make sure that we don't have any stale observations
purgeStaleObservations();
// now we'll just copy the observations for the caller
std::list<Observation>::iterator obs_it;
for (obs_it = observation_list_.begin(); obs_it != observation_list_.end(); ++obs_it) {
observations.push_back(*obs_it);
}
}
void ObservationBuffer::purgeStaleObservations()
{
if (!observation_list_.empty()) {
std::list<Observation>::iterator obs_it = observation_list_.begin();
// if we're keeping observations for no time... then we'll only keep one observation
if (observation_keep_time_ == rclcpp::Duration(0.0s)) {
observation_list_.erase(++obs_it, observation_list_.end());
return;
}
// otherwise... we'll have to loop through the observations to see which ones are stale
for (obs_it = observation_list_.begin(); obs_it != observation_list_.end(); ++obs_it) {
Observation & obs = *obs_it;
// check if the observation is out of date... and if it is,
// remove it and those that follow from the list
if ((clock_->now() - obs.cloud_->header.stamp) >
observation_keep_time_)
{
observation_list_.erase(obs_it, observation_list_.end());
return;
}
}
}
}
bool ObservationBuffer::isCurrent() const
{
if (expected_update_rate_ == rclcpp::Duration(0.0s)) {
return true;
}
bool current = (clock_->now() - last_updated_) <=
expected_update_rate_;
if (!current) {
RCLCPP_WARN(
logger_,
"The %s observation buffer has not been updated for %.2f seconds, "
"and it should be updated every %.2f seconds.",
topic_name_.c_str(),
(clock_->now() - last_updated_).seconds(),
expected_update_rate_.seconds());
}
return current;
}
void ObservationBuffer::resetLastUpdated()
{
last_updated_ = clock_->now();
}
} // namespace nav2_costmap_2d