From 38cb346e8e1ce6a36bf7e5a593026d3184367b07 Mon Sep 17 00:00:00 2001 From: "(=YG=) Hyunseok Yang" Date: Sat, 22 Apr 2023 12:53:57 +0900 Subject: [PATCH 1/2] Refactoring nav2_costmap_2d plugins - obstacle_layer and voxel_layer add new plugins - global_obstacle_layer and global_voxel_layer inherited from obstacle_layer, voxel_layer --- nav2_bringup/params/acloi00_nav2_params.yaml | 32 +- nav2_bringup/params/acloi01_nav2_params.yaml | 29 +- nav2_costmap_2d/CMakeLists.txt | 2 + nav2_costmap_2d/costmap_plugins.xml | 6 + .../nav2_costmap_2d/global_obstacle_layer.hpp | 134 ++++++++ .../nav2_costmap_2d/global_voxel_layer.hpp | 80 +++++ .../nav2_costmap_2d/obstacle_layer.hpp | 16 - .../plugins/global_obstacle_layer.cpp | 294 ++++++++++++++++++ .../plugins/global_voxel_layer.cpp | 52 ++++ nav2_costmap_2d/plugins/obstacle_layer.cpp | 178 +---------- nav2_costmap_2d/plugins/voxel_layer.cpp | 2 +- 11 files changed, 621 insertions(+), 204 deletions(-) create mode 100644 nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp create mode 100644 nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp create mode 100644 nav2_costmap_2d/plugins/global_obstacle_layer.cpp create mode 100644 nav2_costmap_2d/plugins/global_voxel_layer.cpp diff --git a/nav2_bringup/params/acloi00_nav2_params.yaml b/nav2_bringup/params/acloi00_nav2_params.yaml index 85977173b4..ac4d45b48e 100644 --- a/nav2_bringup/params/acloi00_nav2_params.yaml +++ b/nav2_bringup/params/acloi00_nav2_params.yaml @@ -196,15 +196,34 @@ local_costmap: height: 4 resolution: 0.1 robot_radius: 0.34 - # plugins: ["voxel_layer", "inflation_layer"] - plugins: ["voxel_layer", "inflation_layer"] + plugins: ["global_obstacle_layer", "inflation_layer"] #filters: ["keepout_filter"] inflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 6.5 inflation_radius: 1.75 - voxel_layer: - plugin: "nav2_costmap_2d::VoxelLayer" + # voxel_layer: + # plugin: "nav2_costmap_2d::VoxelLayer" + # enabled: True + # publish_voxel_map: True + # origin_z: 0.0 + # z_resolution: 0.05 + # z_voxels: 16 + # max_obstacle_height: 2.0 + # mark_threshold: 0 + # observation_sources: scan + # scan: + # topic: /acloi01/scan + # max_obstacle_height: 5.0 + # clearing: True + # marking: True + # data_type: "LaserScan" + # raytrace_max_range: 15.0 + # raytrace_min_range: 0.0 + # obstacle_max_range: 5.0 + # obstacle_min_range: 0.0 + global_obstacle_layer: + plugin: "nav2_costmap_2d::GlobalObstacleLayer" enabled: True publish_voxel_map: True origin_z: 0.0 @@ -228,10 +247,6 @@ local_costmap: # init_scan_angle: False static_layer: map_subscribe_transient_local: True - global_obstacle_layer: - plugin: "nav2_global_obstacle_costmap_plugin::GlobalObstacleLayer" - enabled: True - topic: "/global_obstacle/obstacle_list" keepout_filter: plugin: "nav2_costmap_2d::KeepoutFilter" enabled: True @@ -249,7 +264,6 @@ global_costmap: robot_radius: 0.34 resolution: 0.1 track_unknown_space: true - #plugins: ["static_layer", "obstacle_layer", "global_obstacle_layer", "inflation_layer"] plugins: ["static_layer", "obstacle_layer", "inflation_layer"] #filters: ["keepout_filter"] obstacle_layer: diff --git a/nav2_bringup/params/acloi01_nav2_params.yaml b/nav2_bringup/params/acloi01_nav2_params.yaml index 6d46203a81..884e1ce953 100644 --- a/nav2_bringup/params/acloi01_nav2_params.yaml +++ b/nav2_bringup/params/acloi01_nav2_params.yaml @@ -196,13 +196,33 @@ local_costmap: height: 4 resolution: 0.1 robot_radius: 0.34 - plugins: ["voxel_layer", "inflation_layer"] + plugins: ["global_obstacle_layer", "inflation_layer"] inflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 6.5 inflation_radius: 1.75 - voxel_layer: - plugin: "nav2_costmap_2d::VoxelLayer" + # voxel_layer: + # plugin: "nav2_costmap_2d::VoxelLayer" + # enabled: True + # publish_voxel_map: True + # origin_z: 0.0 + # z_resolution: 0.05 + # z_voxels: 16 + # max_obstacle_height: 2.0 + # mark_threshold: 0 + # observation_sources: scan + # scan: + # topic: /acloi01/scan + # max_obstacle_height: 5.0 + # clearing: True + # marking: True + # data_type: "LaserScan" + # raytrace_max_range: 15.0 + # raytrace_min_range: 0.0 + # obstacle_max_range: 5.0 + # obstacle_min_range: 0.0 + global_obstacle_layer: + plugin: "nav2_costmap_2d::GlobalObstacleLayer" enabled: True publish_voxel_map: True origin_z: 0.0 @@ -221,6 +241,9 @@ local_costmap: raytrace_min_range: 0.0 obstacle_max_range: 5.0 obstacle_min_range: 0.0 + scan_link_offset: 2.0 + publish_cycle: 1.0 + # init_scan_angle: False static_layer: map_subscribe_transient_local: True always_send_full_costmap: True diff --git a/nav2_costmap_2d/CMakeLists.txt b/nav2_costmap_2d/CMakeLists.txt index 683cb04fdd..0f60c98679 100644 --- a/nav2_costmap_2d/CMakeLists.txt +++ b/nav2_costmap_2d/CMakeLists.txt @@ -84,8 +84,10 @@ add_library(layers SHARED plugins/inflation_layer.cpp plugins/static_layer.cpp plugins/obstacle_layer.cpp + plugins/global_obstacle_layer.cpp src/observation_buffer.cpp plugins/voxel_layer.cpp + plugins/global_voxel_layer.cpp plugins/range_sensor_layer.cpp ) ament_target_dependencies(layers diff --git a/nav2_costmap_2d/costmap_plugins.xml b/nav2_costmap_2d/costmap_plugins.xml index e5867d5108..06d1857e59 100644 --- a/nav2_costmap_2d/costmap_plugins.xml +++ b/nav2_costmap_2d/costmap_plugins.xml @@ -6,12 +6,18 @@ Listens to laser scan and point cloud messages and marks and clears grid cells. + + Listens to laser scan and point cloud messages and marks and clears grid cells. + Listens to OccupancyGrid messages and copies them in, like from map_server. Similar to obstacle costmap, but uses 3D voxel grid to store data. + + Similar to obstacle costmap, but uses 3D voxel grid to store data. + A range-sensor (sonar, IR) based obstacle layer for costmap_2d diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp new file mode 100644 index 0000000000..f2ef1a9fa3 --- /dev/null +++ b/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp @@ -0,0 +1,134 @@ +/********************************************************************* + * + * 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!! + *********************************************************************/ +#ifndef NAV2_COSTMAP_2D__GLOBAL_OBSTACLE_LAYER_HPP_ +#define NAV2_COSTMAP_2D__GLOBAL_OBSTACLE_LAYER_HPP_ + +#include "nav2_costmap_2d/obstacle_layer.hpp" + +namespace nav2_costmap_2d +{ + +/** + * @class ObstacleLayer + * @brief Takes in laser and pointcloud data to populate into 2D costmap + */ +class GlobalObstacleLayer : public ObstacleLayer +{ +public: + /** + * @brief A constructor + */ + GlobalObstacleLayer() + : ObstacleLayer() + {} + + /** + * @brief Initialization process of layer on startup + */ + virtual void onInitialize(); + /** + * @brief Update the bounds of the master costmap by this layer's update dimensions + * @param robot_x X pose of robot + * @param robot_y Y pose of robot + * @param robot_yaw Robot orientation + * @param min_x X min map coord of the window to update + * @param min_y Y min map coord of the window to update + * @param max_x X max map coord of the window to update + * @param max_y Y max map coord of the window to update + */ + virtual void updateBounds( + double robot_x, double robot_y, double robot_yaw, double * min_x, + double * min_y, + double * max_x, + double * max_y); + /** + * @brief Update the costs in the master costmap in the window + * @param master_grid The master costmap grid to update + * @param min_x X min map coord of the window to update + * @param min_y Y min map coord of the window to update + * @param max_x X max map coord of the window to update + * @param max_y Y max map coord of the window to update + */ + virtual void updateCosts( + nav2_costmap_2d::Costmap2D & master_grid, + int min_i, int min_j, int max_i, int max_j); + + /** + * @brief A callback to handle buffering LaserScan messages + * @param message The message returned from a message notifier + * @param buffer A pointer to the observation buffer to update + */ + void laserScanCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer); + + /** + * @brief A callback to handle buffering LaserScan messages which need filtering to turn Inf values into range_max. + * @param message The message returned from a message notifier + * @param buffer A pointer to the observation buffer to update + */ + void laserScanValidInfCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer); + +protected: + + rclcpp::Publisher::SharedPtr obstacle_grid_pub_; + std::unique_ptr grid_; + + bool is_init_scan_angle_; + bool use_init_scan_angle_; + double scan_start_angle_; + double scan_end_angle_; + float scan_link_offset_; + double current_robot_yaw_; + static char * cost_translation_table_; + rclcpp::Time last_publish_{0, 0, RCL_ROS_TIME}; + rclcpp::Duration publish_cycle_{1, 0}; + +protected: + void updateRobotYaw(const double robot_yaw); + +private: + void initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message); + void prepareGrid(nav2_costmap_2d::Costmap2D costmap); +}; + +} // namespace nav2_costmap_2d + +#endif // NAV2_COSTMAP_2D__OBSTACLE_LAYER_HPP_ diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp new file mode 100644 index 0000000000..a386dfd308 --- /dev/null +++ b/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp @@ -0,0 +1,80 @@ +/********************************************************************* + * + * 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!! + *********************************************************************/ +#ifndef NAV2_COSTMAP_2D__GLOBAL_VOXEL_LAYER_HPP_ +#define NAV2_COSTMAP_2D__GLOBAL_VOXEL_LAYER_HPP_ + +#include "nav2_costmap_2d/voxel_layer.hpp" + +namespace nav2_costmap_2d +{ +/** + * @class GlobalVoxelLayer + * @brief Takes laser and pointcloud data to populate a 3D voxel representation of the environment + */ +class GlobalVoxelLayer : public VoxelLayer +{ +public: + /** + * @brief Voxel Layer constructor + */ + GlobalVoxelLayer() + : VoxelLayer() + {}; + + /** + * @brief Update the bounds of the master costmap by this layer's update dimensions + * @param robot_x X pose of robot + * @param robot_y Y pose of robot + * @param robot_yaw Robot orientation + * @param min_x X min map coord of the window to update + * @param min_y Y min map coord of the window to update + * @param max_x X max map coord of the window to update + * @param max_y Y max map coord of the window to update + */ + virtual void updateBounds( + double robot_x, double robot_y, double robot_yaw, + double * min_x, double * min_y, + double * max_x, double * max_y); + +protected: + double current_robot_yaw_; +}; + +} // namespace nav2_costmap_2d + +#endif // NAV2_COSTMAP_2D__VOXEL_LAYER_HPP_ diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/obstacle_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/obstacle_layer.hpp index c529fe3b16..754b434930 100644 --- a/nav2_costmap_2d/include/nav2_costmap_2d/obstacle_layer.hpp +++ b/nav2_costmap_2d/include/nav2_costmap_2d/obstacle_layer.hpp @@ -250,22 +250,6 @@ class ObstacleLayer : public CostmapLayer std::vector static_clearing_observations_; std::vector static_marking_observations_; - rclcpp::Publisher::SharedPtr obstacle_grid_pub_; - std::unique_ptr grid_; - - bool is_init_scan_angle_; - bool use_init_scan_angle_; - double scan_start_angle_; - double scan_end_angle_; - float scan_link_offset_; - void initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message); - double current_robot_yaw_; - void updateRobotYaw(double robot_yaw); - static char * cost_translation_table_; - rclcpp::Time last_publish_{0, 0, RCL_ROS_TIME}; - rclcpp::Duration publish_cycle_{1, 0}; - void prepareGrid(nav2_costmap_2d::Costmap2D costmap); - bool rolling_window_; bool was_reset_; int combination_method_; diff --git a/nav2_costmap_2d/plugins/global_obstacle_layer.cpp b/nav2_costmap_2d/plugins/global_obstacle_layer.cpp new file mode 100644 index 0000000000..e660af0541 --- /dev/null +++ b/nav2_costmap_2d/plugins/global_obstacle_layer.cpp @@ -0,0 +1,294 @@ +/********************************************************************* + * + * 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 "nav2_costmap_2d/global_obstacle_layer.hpp" +#include "nav2_costmap_2d/cost_values.hpp" +#include "nav2_costmap_2d/observation_buffer.hpp" + +#include +PLUGINLIB_EXPORT_CLASS(nav2_costmap_2d::GlobalObstacleLayer, nav2_costmap_2d::Layer) + +using nav2_costmap_2d::FREE_SPACE; +using nav2_costmap_2d::ObservationBuffer; + +namespace nav2_costmap_2d +{ +char * GlobalObstacleLayer::cost_translation_table_ = NULL; + +void GlobalObstacleLayer::onInitialize() +{ + ObstacleLayer::onInitialize(); + + RCLCPP_INFO(logger_, "[GlobalObstacleLayer] onInitialize with Obstacle Index Publisher"); + + auto node = node_.lock(); + if (!node) { + throw std::runtime_error{"Failed to lock node"}; + } + + // The topics that we'll subscribe to from the parameter server + std::string topics_string; + node->get_parameter(name_ + "." + "observation_sources", topics_string); + + // now we need to split the topics based on whitespace which we can use a stringstream for + std::stringstream ss(topics_string); + std::string source; + while (ss >> source) { + bool init_scan_angle; + std::string data_type; + bool inf_is_valid; + double publish_cycle; + declareParameter(source + "." + "init_scan_angle", rclcpp::ParameterValue(true)); + declareParameter(source + "." + "scan_link_offset", rclcpp::ParameterValue(2.0)); + declareParameter(source + "." + "publish_cycle", rclcpp::ParameterValue(1.0)); + + node->get_parameter(name_ + "." + source + "." + "data_type", data_type); + node->get_parameter(name_ + "." + source + "." + "inf_is_valid", inf_is_valid); + node->get_parameter(name_ + "." + source + "." + "init_scan_angle", init_scan_angle); + node->get_parameter(name_ + "." + source + "." + "scan_link_offset", scan_link_offset_); + node->get_parameter(name_ + "." + source + "." + "publish_cycle", publish_cycle); + + if (publish_cycle > 0) { + rclcpp::Duration::from_seconds(publish_cycle); + } else { + publish_cycle_ = rclcpp::Duration(0, 0); + } + + // create a callback for the topic + if (data_type == "LaserScan") { + auto filter = std::dynamic_pointer_cast>(observation_notifiers_.back()); + if (inf_is_valid) { + filter->registerCallback( + std::bind( + &GlobalObstacleLayer::laserScanValidInfCallback, this, std::placeholders::_1, + observation_buffers_.back())); + + } else { + filter->registerCallback( + std::bind( + &GlobalObstacleLayer::laserScanCallback, this, std::placeholders::_1, + observation_buffers_.back())); + } + } + + // [sungkyu.kang] initialize use_init_scan_angle_ parameter + use_init_scan_angle_ = init_scan_angle; + } + + // [sungkyu.kang] create for obstacle index + is_init_scan_angle_ = false; + + auto custom_qos2 = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(); + obstacle_grid_pub_ = node->create_publisher( + "obstacle_map", + custom_qos2); + + if (cost_translation_table_ == NULL) { + cost_translation_table_ = new char[256]; + + // special values: + cost_translation_table_[0] = FREE_SPACE; // 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(1 + (97 * (i - 1)) / 251); + } + } +} + +void +GlobalObstacleLayer::initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message) +{ + // [sungkyu.kang] initialize scan angles + RCLCPP_INFO(logger_, "GlobalObstacleLayer::initializeScanAngle"); + if (use_init_scan_angle_) { + float current_angle = message->angle_min; + float range = 0.0; + + for (size_t i = 0; i < message->ranges.size(); i++) { + range = message->ranges[i]; + if (range >= message->range_min && range <= message->range_max) { + scan_start_angle_ = current_angle + 0.05; + break; + } + current_angle = current_angle + message->angle_increment; + } + current_angle = message->angle_max; + for (size_t i = message->ranges.size(); i > 0; i--) { + range = message->ranges[i]; + if (range >= message->range_min && range <= message->range_max) { + scan_end_angle_ = current_angle - 0.05; + break; + } + current_angle = current_angle - message->angle_increment; + } + } else { + scan_start_angle_ = message->angle_min; + scan_end_angle_ = message->angle_max; + } + + RCLCPP_INFO(logger_, " scan_start_angle: %f, scan_end_angle_: %f", scan_start_angle_, scan_end_angle_); + is_init_scan_angle_ = true; +} + +void +GlobalObstacleLayer::laserScanCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer) +{ + if (!is_init_scan_angle_) { + initializeScanAngle(message); + } + ObstacleLayer::laserScanCallback(message, buffer); +} + +void +GlobalObstacleLayer::laserScanValidInfCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message, + const std::shared_ptr & buffer) +{ + if (!is_init_scan_angle_) { + initializeScanAngle(raw_message); + } + laserScanValidInfCallback(raw_message, buffer); +} + +void GlobalObstacleLayer::updateRobotYaw(const double robot_yaw) +{ + // [sungkyu.kang] set current_robot_yaw_ + current_robot_yaw_ = robot_yaw; + // RCLCPP_INFO(logger_, "updateBounds - robot_yaw: %f, current_robot_yaw_: %f", robot_yaw, current_robot_yaw_); +} + +void +GlobalObstacleLayer::updateBounds( + double robot_x, double robot_y, double robot_yaw, double * min_x, + double * min_y, double * max_x, double * max_y) +{ + { + std::lock_guard guard(*getMutex()); + updateRobotYaw(robot_yaw); + } + + ObstacleLayer::updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y); +} + +void GlobalObstacleLayer::updateCosts( + nav2_costmap_2d::Costmap2D & master_grid, + int min_i, int min_j, + int max_i, int max_j) +{ + ObstacleLayer::updateCosts(master_grid, min_i, min_j, max_i, max_j); + + // [sungkyu.kang] publish current master grid + if (publish_cycle_ > rclcpp::Duration(0, 0) && obstacle_grid_pub_->get_subscription_count() > 0) { + const auto current_time = clock_->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 + { + prepareGrid(master_grid); + obstacle_grid_pub_->publish(std::move(grid_)); + last_publish_ = current_time; + } + } +} + +void GlobalObstacleLayer::prepareGrid(nav2_costmap_2d::Costmap2D costmap) +{ + std::unique_lock lock(*(costmap.getMutex())); + float grid_resolution = costmap.getResolution(); + unsigned int grid_width = costmap.getSizeInCellsX(); + unsigned int grid_height = costmap.getSizeInCellsY(); + + grid_ = std::make_unique(); + + 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; + + grid_->data.resize(grid_->info.width * grid_->info.height); + + unsigned char * data = costmap.getCharMap(); + + double normalLeftX = -1 * sin(current_robot_yaw_ - scan_start_angle_); + double normalLeftY = cos(current_robot_yaw_ - scan_start_angle_); + double normalRightX = sin(current_robot_yaw_ - scan_end_angle_); + double normalRightY = -1 * cos(current_robot_yaw_ - scan_end_angle_); + + // RCLCPP_INFO( + // logger_, + // "\nGlobalObstacleLayer::prepareGrid %f, %f, %f.\n %f, %f, %f, %f", current_robot_yaw_, current_robot_yaw_ - scan_start_angle_, current_robot_yaw_ - scan_end_angle_, normalLeftX, normalLeftY, normalRightX, normalRightY); + int x, y; + unsigned int row_index, col_index; + int scan_tf_offset_x = round(scan_link_offset_ * cos(current_robot_yaw_)); + int scan_tf_offset_y = round(scan_link_offset_ * sin(current_robot_yaw_)); + // RCLCPP_INFO(logger_, "scan_tf_offset_x: %d, scan_tf_offset_y: %d", scan_tf_offset_x, scan_tf_offset_y); + for (unsigned int i = 0; i < grid_->data.size(); i++) { + // [sungkyu.kang] check robot orientation. fill -1 back of robot. + row_index = i / grid_width; + col_index = i % grid_width; + x = col_index - grid_height / 2 - scan_tf_offset_x; + y = row_index - grid_width / 2 - scan_tf_offset_y; + // RCLCPP_INFO( + // logger_, + // " [%4d] row_index: %2d, col_index: %2d, (%2d, %2d)", i, row_index, col_index, x, y); + if (x * normalLeftX + y * normalLeftY > 0 && x * normalRightX + y * normalRightY > 0) { + // RCLCPP_INFO( logger_," hit!!"); + grid_->data[i] = -1; + } else { + grid_->data[i] = cost_translation_table_[data[i]]; + } + } + // RCLCPP_INFO(logger_, "GlobalObstacleLayer::prepareGrid finish "); +} +} // namespace nav2_costmap_2d diff --git a/nav2_costmap_2d/plugins/global_voxel_layer.cpp b/nav2_costmap_2d/plugins/global_voxel_layer.cpp new file mode 100644 index 0000000000..80ecf46423 --- /dev/null +++ b/nav2_costmap_2d/plugins/global_voxel_layer.cpp @@ -0,0 +1,52 @@ +/********************************************************************* + * + * 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/global_voxel_layer.hpp" + +#include +PLUGINLIB_EXPORT_CLASS(nav2_costmap_2d::GlobalVoxelLayer, nav2_costmap_2d::Layer) + +namespace nav2_costmap_2d +{ +void GlobalVoxelLayer::updateBounds( + double robot_x, double robot_y, double robot_yaw, + double * min_x, double * min_y, double * max_x, double * max_y) +{ + VoxelLayer::updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y); +} +} // namespace nav2_costmap_2d diff --git a/nav2_costmap_2d/plugins/obstacle_layer.cpp b/nav2_costmap_2d/plugins/obstacle_layer.cpp index c7acaff8d6..08d5068b2f 100644 --- a/nav2_costmap_2d/plugins/obstacle_layer.cpp +++ b/nav2_costmap_2d/plugins/obstacle_layer.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include "pluginlib/class_list_macros.hpp" #include "sensor_msgs/point_cloud2_iterator.hpp" @@ -61,10 +60,9 @@ using rcl_interfaces::msg::ParameterType; namespace nav2_costmap_2d { -char * ObstacleLayer::cost_translation_table_ = NULL; - ObstacleLayer::~ObstacleLayer() { + std::cout << "ObstacleLayer destructor called" << std::endl; dyn_params_handler_.reset(); for (auto & notifier : observation_notifiers_) { notifier.reset(); @@ -73,7 +71,8 @@ ObstacleLayer::~ObstacleLayer() void ObstacleLayer::onInitialize() { - RCLCPP_INFO(logger_, "[ObstacleLayer] onInitialize with Obstacle Index Publisher"); + std::cout << "ObstacleLayer onInitialize called" << std::endl; + bool track_unknown_space; double transform_tolerance; @@ -153,12 +152,6 @@ void ObstacleLayer::onInitialize() declareParameter(source + "." + "raytrace_max_range", rclcpp::ParameterValue(3.0)); declareParameter(source + "." + "raytrace_min_range", rclcpp::ParameterValue(0.0)); - bool init_scan_angle; - double publish_cycle; - declareParameter(source + "." + "init_scan_angle", rclcpp::ParameterValue(true)); - declareParameter(source + "." + "scan_link_offset", rclcpp::ParameterValue(2.0)); - declareParameter(source + "." + "publish_cycle", rclcpp::ParameterValue(1.0)); - node->get_parameter(name_ + "." + source + "." + "topic", topic); node->get_parameter(name_ + "." + source + "." + "sensor_frame", sensor_frame); node->get_parameter( @@ -192,15 +185,6 @@ void ObstacleLayer::onInitialize() node->get_parameter(name_ + "." + source + "." + "raytrace_min_range", raytrace_min_range); node->get_parameter(name_ + "." + source + "." + "raytrace_max_range", raytrace_max_range); - node->get_parameter(name_ + "." + source + "." + "init_scan_angle", init_scan_angle); - node->get_parameter(name_ + "." + source + "." + "scan_link_offset", scan_link_offset_); - node->get_parameter(name_ + "." + source + "." + "publish_cycle", publish_cycle); - - if (publish_cycle > 0) { - publish_cycle_ = rclcpp::Duration::from_seconds( publish_cycle ); - } else { - publish_cycle_ = rclcpp::Duration(0, 0); - } RCLCPP_DEBUG( logger_, @@ -302,34 +286,7 @@ void ObstacleLayer::onInitialize() target_frames.push_back(sensor_frame); observation_notifiers_.back()->setTargetFrames(target_frames); } - - // [sungkyu.kang] initialize use_init_scan_angle_ parameter - use_init_scan_angle_ = init_scan_angle; } - - // [sungkyu.kang] create for obstacle index - is_init_scan_angle_ = false; - - auto custom_qos2 = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(); - obstacle_grid_pub_ = node->create_publisher( - "obstacle_map", - custom_qos2); - - 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(1 + (97 * (i - 1)) / 251); - } - } } rcl_interfaces::msg::SetParametersResult @@ -366,49 +323,11 @@ ObstacleLayer::dynamicParametersCallback( return result; } -void -ObstacleLayer::initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message) -{ - // [sungkyu.kang] initialize scan angles - RCLCPP_INFO(logger_, "ObstacleLayer::initializeScanAngle"); - if (use_init_scan_angle_) { - float current_angle = message->angle_min; - float range = 0.0; - - for (size_t i = 0; i < message->ranges.size(); i++) { - range = message->ranges[i]; - if (range >= message->range_min && range <= message->range_max) { - scan_start_angle_ = current_angle + 0.05; - break; - } - current_angle = current_angle + message->angle_increment; - } - current_angle = message->angle_max; - for (size_t i = message->ranges.size(); i > 0; i--) { - range = message->ranges[i]; - if (range >= message->range_min && range <= message->range_max) { - scan_end_angle_ = current_angle - 0.05; - break; - } - current_angle = current_angle - message->angle_increment; - } - } else { - scan_start_angle_ = message->angle_min; - scan_end_angle_ = message->angle_max; - } - - RCLCPP_INFO(logger_, " scan_start_angle: %f, scan_end_angle_: %f", scan_start_angle_, scan_end_angle_); - is_init_scan_angle_ = true; -} - void ObstacleLayer::laserScanCallback( sensor_msgs::msg::LaserScan::ConstSharedPtr message, const std::shared_ptr & buffer) { - if (!is_init_scan_angle_) { - initializeScanAngle(message); - } // project the laser into a point cloud sensor_msgs::msg::PointCloud2 cloud; cloud.header = message->header; @@ -443,9 +362,6 @@ ObstacleLayer::laserScanValidInfCallback( sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message, const std::shared_ptr & buffer) { - if (!is_init_scan_angle_) { - initializeScanAngle(raw_message); - } // Filter positive infinities ("Inf"s) to max_range. float epsilon = 0.0001; // a tenth of a millimeter sensor_msgs::msg::LaserScan message = *raw_message; @@ -495,21 +411,12 @@ ObstacleLayer::pointCloud2Callback( buffer->unlock(); } -void -ObstacleLayer::updateRobotYaw(double robot_yaw) -{ - // [sungkyu.kang] set current_robot_yaw_ - current_robot_yaw_ = robot_yaw; - // RCLCPP_INFO(logger_, "updateBounds - robot_yaw: %f, current_robot_yaw_: %f", robot_yaw, current_robot_yaw_); -} - void ObstacleLayer::updateBounds( double robot_x, double robot_y, double robot_yaw, double * min_x, double * min_y, double * max_x, double * max_y) { std::lock_guard guard(*getMutex()); - updateRobotYaw(robot_yaw); if (rolling_window_) { updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2); } @@ -549,7 +456,6 @@ ObstacleLayer::updateBounds( sensor_msgs::PointCloud2ConstIterator iter_x(cloud, "x"); sensor_msgs::PointCloud2ConstIterator iter_y(cloud, "y"); sensor_msgs::PointCloud2ConstIterator iter_z(cloud, "z"); - // RCLCPP_INFO(logger_, "[ObstacleLayer] observation --------------------------- "); for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) { double px = *iter_x, py = *iter_y, pz = *iter_z; @@ -593,13 +499,11 @@ ObstacleLayer::updateBounds( unsigned int index = getIndex(mx, my); costmap_[index] = LETHAL_OBSTACLE; - touch(px, py, min_x, min_y, max_x, max_y); } } updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y); - } void @@ -648,84 +552,8 @@ ObstacleLayer::updateCosts( default: // Nothing break; } - - // [sungkyu.kang] publish current master grid - - if (publish_cycle_ > rclcpp::Duration(0, 0) && obstacle_grid_pub_->get_subscription_count() > 0) { - - auto current_time = clock_->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 - { - prepareGrid(master_grid); - obstacle_grid_pub_->publish(std::move(grid_)); - last_publish_ = current_time; - } - } - } -void ObstacleLayer::prepareGrid(nav2_costmap_2d::Costmap2D costmap) -{ - std::unique_lock lock(*(costmap.getMutex())); - float grid_resolution = costmap.getResolution(); - unsigned int grid_width = costmap.getSizeInCellsX(); - unsigned int grid_height = costmap.getSizeInCellsY(); - - grid_ = std::make_unique(); - - 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; - - grid_->data.resize(grid_->info.width * grid_->info.height); - - unsigned char * data = costmap.getCharMap(); - - double normalLeftX = -1 * sin(current_robot_yaw_ - scan_start_angle_); - double normalLeftY = cos(current_robot_yaw_ - scan_start_angle_); - double normalRightX = sin(current_robot_yaw_ - scan_end_angle_); - double normalRightY = -1 * cos(current_robot_yaw_ - scan_end_angle_); - - // RCLCPP_INFO( - // logger_, - // "\nObstacleLayer::prepareGrid %f, %f, %f.\n %f, %f, %f, %f", current_robot_yaw_, current_robot_yaw_ - scan_start_angle_, current_robot_yaw_ - scan_end_angle_, normalLeftX, normalLeftY, normalRightX, normalRightY); - int x, y; - unsigned int row_index, col_index; - int scan_tf_offset_x = round(scan_link_offset_ * cos(current_robot_yaw_)); - int scan_tf_offset_y = round(scan_link_offset_ * sin(current_robot_yaw_)); - // RCLCPP_INFO(logger_, "scan_tf_offset_x: %d, scan_tf_offset_y: %d", scan_tf_offset_x, scan_tf_offset_y); - for (unsigned int i = 0; i < grid_->data.size(); i++) { - // [sungkyu.kang] check robot orientation. fill -1 back of robot. - row_index = i / grid_width; - col_index = i % grid_width; - x = col_index - grid_height / 2 - scan_tf_offset_x; - y = row_index - grid_width / 2 - scan_tf_offset_y; - // RCLCPP_INFO( - // logger_, - // " [%4d] row_index: %2d, col_index: %2d, (%2d, %2d)", i, row_index, col_index, x, y); - if (x * normalLeftX + y * normalLeftY > 0 && x * normalRightX + y * normalRightY > 0) { - // RCLCPP_INFO( logger_," hit!!"); - grid_->data[i] = -1; - } else { - grid_->data[i] = cost_translation_table_[data[i]]; - } - } - // RCLCPP_INFO(logger_, "ObstacleLayer::prepareGrid finish "); -} - - void ObstacleLayer::addStaticObservation( nav2_costmap_2d::Observation & obs, diff --git a/nav2_costmap_2d/plugins/voxel_layer.cpp b/nav2_costmap_2d/plugins/voxel_layer.cpp index f60a9f5c7b..f8c62d06b9 100644 --- a/nav2_costmap_2d/plugins/voxel_layer.cpp +++ b/nav2_costmap_2d/plugins/voxel_layer.cpp @@ -146,7 +146,7 @@ void VoxelLayer::updateBounds( double * min_y, double * max_x, double * max_y) { std::lock_guard guard(*getMutex()); - updateRobotYaw(robot_yaw); + if (rolling_window_) { updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2); } From 654317d6e98fc0d614e2943f64beee0b94942d4c Mon Sep 17 00:00:00 2001 From: "(=YG=) Hyunseok Yang" Date: Mon, 24 Apr 2023 18:14:57 +0900 Subject: [PATCH 2/2] Apply global obstacle handling code into nav2_costmap_2d::global_voxel_layer also --- nav2_bringup/params/acloi00_nav2_params.yaml | 28 +- nav2_bringup/params/acloi02_nav2_params.yaml | 11 +- .../nav2_costmap_2d/global_obstacle_layer.hpp | 10 +- .../nav2_costmap_2d/global_voxel_layer.hpp | 52 ++++ .../plugins/global_obstacle_layer.cpp | 2 +- .../plugins/global_voxel_layer.cpp | 239 +++++++++++++++++- nav2_costmap_2d/plugins/obstacle_layer.cpp | 5 +- 7 files changed, 310 insertions(+), 37 deletions(-) diff --git a/nav2_bringup/params/acloi00_nav2_params.yaml b/nav2_bringup/params/acloi00_nav2_params.yaml index ac4d45b48e..4ac6ed05c4 100644 --- a/nav2_bringup/params/acloi00_nav2_params.yaml +++ b/nav2_bringup/params/acloi00_nav2_params.yaml @@ -196,34 +196,14 @@ local_costmap: height: 4 resolution: 0.1 robot_radius: 0.34 - plugins: ["global_obstacle_layer", "inflation_layer"] + plugins: ["global_voxel_layer", "inflation_layer"] #filters: ["keepout_filter"] inflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 6.5 inflation_radius: 1.75 - # voxel_layer: - # plugin: "nav2_costmap_2d::VoxelLayer" - # enabled: True - # publish_voxel_map: True - # origin_z: 0.0 - # z_resolution: 0.05 - # z_voxels: 16 - # max_obstacle_height: 2.0 - # mark_threshold: 0 - # observation_sources: scan - # scan: - # topic: /acloi01/scan - # max_obstacle_height: 5.0 - # clearing: True - # marking: True - # data_type: "LaserScan" - # raytrace_max_range: 15.0 - # raytrace_min_range: 0.0 - # obstacle_max_range: 5.0 - # obstacle_min_range: 0.0 - global_obstacle_layer: - plugin: "nav2_costmap_2d::GlobalObstacleLayer" + global_voxel_layer: + plugin: "nav2_costmap_2d::GlobalVoxelLayer" enabled: True publish_voxel_map: True origin_z: 0.0 @@ -233,7 +213,7 @@ local_costmap: mark_threshold: 0 observation_sources: scan scan: - topic: /acloi00/scan + topic: /acloi01/scan max_obstacle_height: 5.0 clearing: True marking: True diff --git a/nav2_bringup/params/acloi02_nav2_params.yaml b/nav2_bringup/params/acloi02_nav2_params.yaml index 0483746336..7758af912a 100644 --- a/nav2_bringup/params/acloi02_nav2_params.yaml +++ b/nav2_bringup/params/acloi02_nav2_params.yaml @@ -196,13 +196,13 @@ local_costmap: height: 4 resolution: 0.1 robot_radius: 0.34 - plugins: ["voxel_layer", "inflation_layer"] + plugins: ["global_voxel_layer", "inflation_layer"] inflation_layer: plugin: "nav2_costmap_2d::InflationLayer" cost_scaling_factor: 6.5 inflation_radius: 1.75 - voxel_layer: - plugin: "nav2_costmap_2d::VoxelLayer" + global_voxel_layer: + plugin: "nav2_costmap_2d::GlobalVoxelLayer" enabled: True publish_voxel_map: True origin_z: 0.0 @@ -212,7 +212,7 @@ local_costmap: mark_threshold: 0 observation_sources: scan scan: - topic: /acloi02/scan + topic: /acloi01/scan max_obstacle_height: 5.0 clearing: True marking: True @@ -221,6 +221,9 @@ local_costmap: raytrace_min_range: 0.0 obstacle_max_range: 5.0 obstacle_min_range: 0.0 + scan_link_offset: 2.0 + publish_cycle: 1.0 + # init_scan_angle: False static_layer: map_subscribe_transient_local: True always_send_full_costmap: True diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp index f2ef1a9fa3..bf9536a995 100644 --- a/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp +++ b/nav2_costmap_2d/include/nav2_costmap_2d/global_obstacle_layer.hpp @@ -61,6 +61,7 @@ class GlobalObstacleLayer : public ObstacleLayer * @brief Initialization process of layer on startup */ virtual void onInitialize(); + /** * @brief Update the bounds of the master costmap by this layer's update dimensions * @param robot_x X pose of robot @@ -72,10 +73,10 @@ class GlobalObstacleLayer : public ObstacleLayer * @param max_y Y max map coord of the window to update */ virtual void updateBounds( - double robot_x, double robot_y, double robot_yaw, double * min_x, - double * min_y, - double * max_x, - double * max_y); + double robot_x, double robot_y, double robot_yaw, + double * min_x, double * min_y, + double * max_x, double * max_y); + /** * @brief Update the costs in the master costmap in the window * @param master_grid The master costmap grid to update @@ -107,7 +108,6 @@ class GlobalObstacleLayer : public ObstacleLayer const std::shared_ptr & buffer); protected: - rclcpp::Publisher::SharedPtr obstacle_grid_pub_; std::unique_ptr grid_; diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp index a386dfd308..23081f7a6a 100644 --- a/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp +++ b/nav2_costmap_2d/include/nav2_costmap_2d/global_voxel_layer.hpp @@ -55,6 +55,10 @@ class GlobalVoxelLayer : public VoxelLayer GlobalVoxelLayer() : VoxelLayer() {}; + /** + * @brief Initialization process of layer on startup + */ + virtual void onInitialize(); /** * @brief Update the bounds of the master costmap by this layer's update dimensions @@ -71,8 +75,56 @@ class GlobalVoxelLayer : public VoxelLayer double * min_x, double * min_y, double * max_x, double * max_y); + /** + * @brief Update the costs in the master costmap in the window + * @param master_grid The master costmap grid to update + * @param min_x X min map coord of the window to update + * @param min_y Y min map coord of the window to update + * @param max_x X max map coord of the window to update + * @param max_y Y max map coord of the window to update + */ + virtual void updateCosts( + nav2_costmap_2d::Costmap2D & master_grid, + int min_i, int min_j, int max_i, int max_j); + + /** + * @brief A callback to handle buffering LaserScan messages + * @param message The message returned from a message notifier + * @param buffer A pointer to the observation buffer to update + */ + void laserScanCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer); + + /** + * @brief A callback to handle buffering LaserScan messages which need filtering to turn Inf values into range_max. + * @param message The message returned from a message notifier + * @param buffer A pointer to the observation buffer to update + */ + void laserScanValidInfCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer); + protected: + rclcpp::Publisher::SharedPtr obstacle_grid_pub_; + std::unique_ptr grid_; + + bool is_init_scan_angle_; + bool use_init_scan_angle_; + double scan_start_angle_; + double scan_end_angle_; + float scan_link_offset_; double current_robot_yaw_; + static char * cost_translation_table_; + rclcpp::Time last_publish_{0, 0, RCL_ROS_TIME}; + rclcpp::Duration publish_cycle_{1, 0}; + +protected: + void updateRobotYaw(const double robot_yaw); + +private: + void initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message); + void prepareGrid(nav2_costmap_2d::Costmap2D costmap); }; } // namespace nav2_costmap_2d diff --git a/nav2_costmap_2d/plugins/global_obstacle_layer.cpp b/nav2_costmap_2d/plugins/global_obstacle_layer.cpp index e660af0541..69a8118eee 100644 --- a/nav2_costmap_2d/plugins/global_obstacle_layer.cpp +++ b/nav2_costmap_2d/plugins/global_obstacle_layer.cpp @@ -54,7 +54,7 @@ void GlobalObstacleLayer::onInitialize() { ObstacleLayer::onInitialize(); - RCLCPP_INFO(logger_, "[GlobalObstacleLayer] onInitialize with Obstacle Index Publisher"); + RCLCPP_INFO(logger_, "[GlobalObstacleLayer] onInitialize with Obstacle Publisher"); auto node = node_.lock(); if (!node) { diff --git a/nav2_costmap_2d/plugins/global_voxel_layer.cpp b/nav2_costmap_2d/plugins/global_voxel_layer.cpp index 80ecf46423..d04ec60b3f 100644 --- a/nav2_costmap_2d/plugins/global_voxel_layer.cpp +++ b/nav2_costmap_2d/plugins/global_voxel_layer.cpp @@ -43,10 +43,247 @@ PLUGINLIB_EXPORT_CLASS(nav2_costmap_2d::GlobalVoxelLayer, nav2_costmap_2d::Layer namespace nav2_costmap_2d { +char * GlobalVoxelLayer::cost_translation_table_ = NULL; + +void GlobalVoxelLayer::onInitialize() +{ + VoxelLayer::onInitialize(); + + RCLCPP_INFO(logger_, "[GlobalVoxelLayer] onInitialize with Obstacle Publisher"); + + auto node = node_.lock(); + if (!node) { + throw std::runtime_error{"Failed to lock node"}; + } + + // The topics that we'll subscribe to from the parameter server + std::string topics_string; + node->get_parameter(name_ + "." + "observation_sources", topics_string); + + // now we need to split the topics based on whitespace which we can use a stringstream for + std::stringstream ss(topics_string); + std::string source; + while (ss >> source) { + bool init_scan_angle; + std::string data_type; + bool inf_is_valid; + double publish_cycle; + declareParameter(source + "." + "init_scan_angle", rclcpp::ParameterValue(true)); + declareParameter(source + "." + "scan_link_offset", rclcpp::ParameterValue(2.0)); + declareParameter(source + "." + "publish_cycle", rclcpp::ParameterValue(1.0)); + + node->get_parameter(name_ + "." + source + "." + "data_type", data_type); + node->get_parameter(name_ + "." + source + "." + "inf_is_valid", inf_is_valid); + node->get_parameter(name_ + "." + source + "." + "init_scan_angle", init_scan_angle); + node->get_parameter(name_ + "." + source + "." + "scan_link_offset", scan_link_offset_); + node->get_parameter(name_ + "." + source + "." + "publish_cycle", publish_cycle); + + if (publish_cycle > 0) { + rclcpp::Duration::from_seconds(publish_cycle); + } else { + publish_cycle_ = rclcpp::Duration(0, 0); + } + + // create a callback for the topic + if (data_type == "LaserScan") { + auto filter = std::dynamic_pointer_cast>(observation_notifiers_.back()); + if (inf_is_valid) { + filter->registerCallback( + std::bind( + &GlobalVoxelLayer::laserScanValidInfCallback, this, std::placeholders::_1, + observation_buffers_.back())); + + } else { + filter->registerCallback( + std::bind( + &GlobalVoxelLayer::laserScanCallback, this, std::placeholders::_1, + observation_buffers_.back())); + } + } + + // [sungkyu.kang] initialize use_init_scan_angle_ parameter + use_init_scan_angle_ = init_scan_angle; + } + + // [sungkyu.kang] create for obstacle index + is_init_scan_angle_ = false; + + auto custom_qos2 = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(); + obstacle_grid_pub_ = node->create_publisher( + "obstacle_map", + custom_qos2); + + if (cost_translation_table_ == NULL) { + cost_translation_table_ = new char[256]; + + // special values: + cost_translation_table_[0] = FREE_SPACE; // 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(1 + (97 * (i - 1)) / 251); + } + } +} + +void +GlobalVoxelLayer::initializeScanAngle(sensor_msgs::msg::LaserScan::ConstSharedPtr message) +{ + // [sungkyu.kang] initialize scan angles + RCLCPP_INFO(logger_, "GlobalVoxelLayer::initializeScanAngle"); + if (use_init_scan_angle_) { + float current_angle = message->angle_min; + float range = 0.0; + + for (size_t i = 0; i < message->ranges.size(); i++) { + range = message->ranges[i]; + if (range >= message->range_min && range <= message->range_max) { + scan_start_angle_ = current_angle + 0.05; + break; + } + current_angle = current_angle + message->angle_increment; + } + current_angle = message->angle_max; + for (size_t i = message->ranges.size(); i > 0; i--) { + range = message->ranges[i]; + if (range >= message->range_min && range <= message->range_max) { + scan_end_angle_ = current_angle - 0.05; + break; + } + current_angle = current_angle - message->angle_increment; + } + } else { + scan_start_angle_ = message->angle_min; + scan_end_angle_ = message->angle_max; + } + + RCLCPP_INFO(logger_, " scan_start_angle: %f, scan_end_angle_: %f", scan_start_angle_, scan_end_angle_); + is_init_scan_angle_ = true; +} + +void +GlobalVoxelLayer::laserScanCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr message, + const std::shared_ptr & buffer) +{ + if (!is_init_scan_angle_) { + initializeScanAngle(message); + } + ObstacleLayer::laserScanCallback(message, buffer); +} + +void +GlobalVoxelLayer::laserScanValidInfCallback( + sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message, + const std::shared_ptr & buffer) +{ + if (!is_init_scan_angle_) { + initializeScanAngle(raw_message); + } + laserScanValidInfCallback(raw_message, buffer); +} + +void GlobalVoxelLayer::updateRobotYaw(const double robot_yaw) +{ + // [sungkyu.kang] set current_robot_yaw_ + current_robot_yaw_ = robot_yaw; + // RCLCPP_INFO(logger_, "updateBounds - robot_yaw: %f, current_robot_yaw_: %f", robot_yaw, current_robot_yaw_); +} + void GlobalVoxelLayer::updateBounds( double robot_x, double robot_y, double robot_yaw, - double * min_x, double * min_y, double * max_x, double * max_y) + double * min_x, double * min_y, + double * max_x, double * max_y) { + { + std::lock_guard guard(*getMutex()); + updateRobotYaw(robot_yaw); + } + VoxelLayer::updateBounds(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y); } + +void GlobalVoxelLayer::updateCosts( + nav2_costmap_2d::Costmap2D & master_grid, + int min_i, int min_j, + int max_i, int max_j) +{ + ObstacleLayer::updateCosts(master_grid, min_i, min_j, max_i, max_j); + + // [sungkyu.kang] publish current master grid + if (publish_cycle_ > rclcpp::Duration(0, 0) && obstacle_grid_pub_->get_subscription_count() > 0) { + const auto current_time = clock_->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 + { + prepareGrid(master_grid); + obstacle_grid_pub_->publish(std::move(grid_)); + last_publish_ = current_time; + } + } +} + +void GlobalVoxelLayer::prepareGrid(nav2_costmap_2d::Costmap2D costmap) +{ + std::unique_lock lock(*(costmap.getMutex())); + float grid_resolution = costmap.getResolution(); + unsigned int grid_width = costmap.getSizeInCellsX(); + unsigned int grid_height = costmap.getSizeInCellsY(); + + grid_ = std::make_unique(); + + 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; + + grid_->data.resize(grid_->info.width * grid_->info.height); + + unsigned char * data = costmap.getCharMap(); + + double normalLeftX = -1 * sin(current_robot_yaw_ - scan_start_angle_); + double normalLeftY = cos(current_robot_yaw_ - scan_start_angle_); + double normalRightX = sin(current_robot_yaw_ - scan_end_angle_); + double normalRightY = -1 * cos(current_robot_yaw_ - scan_end_angle_); + + // RCLCPP_INFO( + // logger_, + // "\nGlobalVoxelLayer::prepareGrid %f, %f, %f.\n %f, %f, %f, %f", current_robot_yaw_, current_robot_yaw_ - scan_start_angle_, current_robot_yaw_ - scan_end_angle_, normalLeftX, normalLeftY, normalRightX, normalRightY); + int x, y; + unsigned int row_index, col_index; + int scan_tf_offset_x = round(scan_link_offset_ * cos(current_robot_yaw_)); + int scan_tf_offset_y = round(scan_link_offset_ * sin(current_robot_yaw_)); + // RCLCPP_INFO(logger_, "scan_tf_offset_x: %d, scan_tf_offset_y: %d", scan_tf_offset_x, scan_tf_offset_y); + for (unsigned int i = 0; i < grid_->data.size(); i++) { + // [sungkyu.kang] check robot orientation. fill -1 back of robot. + row_index = i / grid_width; + col_index = i % grid_width; + x = col_index - grid_height / 2 - scan_tf_offset_x; + y = row_index - grid_width / 2 - scan_tf_offset_y; + // RCLCPP_INFO( + // logger_, + // " [%4d] row_index: %2d, col_index: %2d, (%2d, %2d)", i, row_index, col_index, x, y); + if (x * normalLeftX + y * normalLeftY > 0 && x * normalRightX + y * normalRightY > 0) { + // RCLCPP_INFO( logger_," hit!!"); + grid_->data[i] = -1; + } else { + grid_->data[i] = cost_translation_table_[data[i]]; + } + } + // RCLCPP_INFO(logger_, "GlobalVoxelLayer::prepareGrid finish "); +} } // namespace nav2_costmap_2d diff --git a/nav2_costmap_2d/plugins/obstacle_layer.cpp b/nav2_costmap_2d/plugins/obstacle_layer.cpp index 08d5068b2f..5d153e7d76 100644 --- a/nav2_costmap_2d/plugins/obstacle_layer.cpp +++ b/nav2_costmap_2d/plugins/obstacle_layer.cpp @@ -413,8 +413,9 @@ ObstacleLayer::pointCloud2Callback( void ObstacleLayer::updateBounds( - double robot_x, double robot_y, double robot_yaw, double * min_x, - double * min_y, double * max_x, double * max_y) + double robot_x, double robot_y, double robot_yaw, + double * min_x, double * min_y, + double * max_x, double * max_y) { std::lock_guard guard(*getMutex()); if (rolling_window_) {