41 #include "tf2/convert.h"
42 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
44 #include "nav2_costmap_2d/costmap_filters/keepout_filter.hpp"
45 #include "nav2_costmap_2d/costmap_filters/filter_values.hpp"
51 : filter_info_sub_(nullptr), mask_sub_(nullptr), filter_mask_(nullptr),
57 const std::string & filter_info_topic)
59 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
61 rclcpp_lifecycle::LifecycleNode::SharedPtr node = node_.lock();
63 throw std::runtime_error{
"Failed to lock node"};
70 "KeepoutFilter: Subscribing to \"%s\" topic for filter info...",
72 filter_info_sub_ = node->create_subscription<nav2_msgs::msg::CostmapFilterInfo>(
74 std::bind(&KeepoutFilter::filterInfoCallback,
this, std::placeholders::_1));
76 global_frame_ = layered_costmap_->getGlobalFrameID();
79 node->get_parameter(name_ +
"." +
"override_lethal_cost", override_lethal_cost_);
80 declareParameter(
"lethal_override_cost", rclcpp::ParameterValue(MAX_NON_OBSTACLE));
81 node->get_parameter(name_ +
"." +
"lethal_override_cost", lethal_override_cost_);
84 lethal_override_cost_ = \
85 std::clamp<unsigned int>(lethal_override_cost_, FREE_SPACE, MAX_NON_OBSTACLE);
88 void KeepoutFilter::filterInfoCallback(
89 const nav2_msgs::msg::CostmapFilterInfo::SharedPtr msg)
91 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
93 rclcpp_lifecycle::LifecycleNode::SharedPtr node = node_.lock();
95 throw std::runtime_error{
"Failed to lock node"};
105 "KeepoutFilter: New costmap filter info arrived from %s topic. Updating old filter info.",
112 if (msg->base != BASE_DEFAULT || msg->multiplier != MULTIPLIER_DEFAULT) {
115 "KeepoutFilter: For proper use of keepout filter base and multiplier"
116 " in CostmapFilterInfo message should be set to their default values (%f and %f)",
117 BASE_DEFAULT, MULTIPLIER_DEFAULT);
125 "KeepoutFilter: Subscribing to \"%s\" topic for filter mask...",
127 mask_sub_ = node->create_subscription<nav_msgs::msg::OccupancyGrid>(
128 mask_topic_, rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
129 std::bind(&KeepoutFilter::maskCallback,
this, std::placeholders::_1));
132 void KeepoutFilter::maskCallback(
133 const nav_msgs::msg::OccupancyGrid::SharedPtr msg)
135 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
137 rclcpp_lifecycle::LifecycleNode::SharedPtr node = node_.lock();
139 throw std::runtime_error{
"Failed to lock node"};
145 "KeepoutFilter: Received filter mask from %s topic.",
mask_topic_.c_str());
149 "KeepoutFilter: New filter mask arrived from %s topic. Updating old filter mask.",
151 filter_mask_.reset();
160 int min_i,
int min_j,
int max_i,
int max_j,
161 const geometry_msgs::msg::Pose2D & pose)
163 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
167 RCLCPP_WARN_THROTTLE(
168 logger_, *(clock_), 2000,
169 "KeepoutFilter: Filter mask was not received");
173 tf2::Transform tf2_transform;
174 tf2_transform.setIdentity();
175 int mg_min_x, mg_min_y;
176 int mg_max_x, mg_max_y;
178 const std::string mask_frame = filter_mask_->header.frame_id;
180 if (mask_frame != global_frame_) {
183 geometry_msgs::msg::TransformStamped transform;
185 transform = tf_->lookupTransform(
186 mask_frame, global_frame_, tf2::TimePointZero,
188 }
catch (tf2::TransformException & ex) {
191 "KeepoutFilter: Failed to get costmap frame (%s) "
192 "transformation to mask frame (%s) with error: %s",
193 global_frame_.c_str(), mask_frame.c_str(), ex.what());
196 tf2::fromMsg(transform.transform, tf2_transform);
227 const double half_cell_size = 0.5 * filter_mask_->info.resolution;
228 wx = filter_mask_->info.origin.position.x + half_cell_size;
229 wy = filter_mask_->info.origin.position.y + half_cell_size;
232 if (mg_min_x >= max_i || mg_min_y >= max_j) {
236 mg_min_x = std::max(min_i, mg_min_x);
237 mg_min_y = std::max(min_j, mg_min_y);
241 wx = filter_mask_->info.origin.position.x +
242 filter_mask_->info.width * filter_mask_->info.resolution + half_cell_size;
243 wy = filter_mask_->info.origin.position.y +
244 filter_mask_->info.height * filter_mask_->info.resolution + half_cell_size;
247 if (mg_max_x <= min_i || mg_max_y <= min_j) {
251 mg_max_x = std::min(max_i, mg_max_x);
252 mg_max_y = std::min(max_j, mg_max_y);
256 unsigned int mg_min_x_u =
static_cast<unsigned int>(mg_min_x);
257 unsigned int mg_min_y_u =
static_cast<unsigned int>(mg_min_y);
258 unsigned int mg_max_x_u =
static_cast<unsigned int>(mg_max_x);
259 unsigned int mg_max_y_u =
static_cast<unsigned int>(mg_max_y);
262 bool is_pose_lethal =
false;
263 if (override_lethal_cost_) {
264 geometry_msgs::msg::Pose2D mask_pose;
265 if (
transformPose(global_frame_, pose, filter_mask_->header.frame_id, mask_pose)) {
266 unsigned int mask_robot_i, mask_robot_j;
267 if (
worldToMask(filter_mask_, mask_pose.x, mask_pose.y, mask_robot_i, mask_robot_j)) {
268 auto data =
getMaskCost(filter_mask_, mask_robot_i, mask_robot_j);
269 is_pose_lethal = (data == INSCRIBED_INFLATED_OBSTACLE || data == LETHAL_OBSTACLE);
270 if (is_pose_lethal) {
271 RCLCPP_WARN_THROTTLE(
272 logger_, *(clock_), 2000,
273 "KeepoutFilter: Pose is in keepout zone, reducing cost override to navigate out.");
280 if (is_pose_lethal || (last_pose_lethal_ && !is_pose_lethal)) {
281 lethal_state_update_min_x_ = std::min(mg_min_x_u, lethal_state_update_min_x_);
282 mg_min_x_u = lethal_state_update_min_x_;
283 lethal_state_update_min_y_ = std::min(mg_min_y_u, lethal_state_update_min_y_);
284 mg_min_y_u = lethal_state_update_min_y_;
285 lethal_state_update_max_x_ = std::max(mg_max_x_u, lethal_state_update_max_x_);
286 mg_max_x_u = lethal_state_update_max_x_;
287 lethal_state_update_max_y_ = std::max(mg_max_y_u, lethal_state_update_max_y_);
288 mg_max_y_u = lethal_state_update_max_y_;
293 lethal_state_update_max_x_ = 0u;
294 lethal_state_update_max_y_ = 0u;
301 double msk_wx, msk_wy;
303 unsigned char data, old_data;
307 unsigned char * master_array = master_grid.
getCharMap();
308 for (i = mg_min_x_u; i < mg_max_x_u; i++) {
309 for (j = mg_min_y_u; j < mg_max_y_u; j++) {
311 old_data = master_array[index];
315 if (mask_frame != global_frame_) {
317 tf2::Vector3 point(gl_wx, gl_wy, 0);
318 point = tf2_transform * point;
327 if (
worldToMask(filter_mask_, msk_wx, msk_wy, mx, my)) {
330 if (data == NO_INFORMATION) {
334 if (data > old_data || old_data == NO_INFORMATION) {
335 if (override_lethal_cost_ && is_pose_lethal) {
336 master_array[index] = lethal_override_cost_;
338 master_array[index] = data;
345 last_pose_lethal_ = is_pose_lethal;
350 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
352 filter_info_sub_.reset();
358 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
368 #include "pluginlib/class_list_macros.hpp"
A 2D costmap provides a mapping between points in the world and their associated "costs".
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
Convert from map coordinates to world coordinates.
unsigned int getIndex(unsigned int mx, unsigned int my) const
Given two map coordinates... compute the associated index.
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
void worldToMapNoBounds(double wx, double wy, int &mx, int &my) const
Convert from world coordinates to map coordinates without checking for legal bounds.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
bool worldToMask(nav_msgs::msg::OccupancyGrid::ConstSharedPtr filter_mask, double wx, double wy, unsigned int &mx, unsigned int &my) const
: Convert from world coordinates to mask coordinates. Similar to Costmap2D::worldToMap() method but w...
bool transformPose(const std::string global_frame, const geometry_msgs::msg::Pose2D &global_pose, const std::string mask_frame, geometry_msgs::msg::Pose2D &mask_pose) const
: Transforms robot pose from current layer frame to mask frame
std::string filter_info_topic_
: Name of costmap filter info topic
std::string mask_topic_
: Name of filter mask topic
tf2::Duration transform_tolerance_
: mask_frame->global_frame_ transform tolerance
unsigned char getMaskCost(nav_msgs::msg::OccupancyGrid::ConstSharedPtr filter_mask, const unsigned int mx, const unsigned int &my) const
Get the cost of a cell in the filter mask.
mutex_t * getMutex()
: returns pointer to a mutex
Reads in a keepout mask and marks keepout regions in the map to prevent planning or control in restri...
void process(nav2_costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j, const geometry_msgs::msg::Pose2D &pose)
Process the keepout layer at the current pose / bounds / grid.
KeepoutFilter()
A constructor.
void resetFilter()
Reset the costmap filter / topic / info.
void initializeFilter(const std::string &filter_info_topic)
Initialize the filter and subscribe to the info topic.
bool isActive()
If this filter is active.
Abstract class for layered costmap plugin implementations.
void declareParameter(const std::string ¶m_name, const rclcpp::ParameterValue &value)
Convenience functions for declaring ROS parameters.