diff --git a/nav2_bringup/bringup/params/nav2_params.yaml b/nav2_bringup/bringup/params/nav2_params.yaml index 4fd94c03dcd48356f054345c1db7ec55a019ddeb..30762e8c945ad6b55dd348d9ce9d10069f43d8e7 100644 --- a/nav2_bringup/bringup/params/nav2_params.yaml +++ b/nav2_bringup/bringup/params/nav2_params.yaml @@ -152,7 +152,7 @@ controller_server: RotateToGoal.scale: 32.0 RotateToGoal.slowing_factor: 5.0 RotateToGoal.lookahead_time: -1.0 - + controller_server_rclcpp_node: ros__parameters: use_sim_time: True diff --git a/nav2_bringup/bringup/rviz/nav2_default_view.rviz b/nav2_bringup/bringup/rviz/nav2_default_view.rviz index dfdda36ba12199306f642f8dfa84a018623ee668..904e51cb62310eb0633104eb0938b0ab409f6ea1 100644 --- a/nav2_bringup/bringup/rviz/nav2_default_view.rviz +++ b/nav2_bringup/bringup/rviz/nav2_default_view.rviz @@ -293,7 +293,7 @@ Visualization Manager: Value: true Axis: Z Channel Name: intensity - Class: rviz_default_plugins/PointCloud + Class: rviz_default_plugins/PointCloud2 Color: 125; 125; 125 Color Transformer: FlatColor Decay Time: 0 @@ -407,7 +407,7 @@ Visualization Manager: Value: true Axis: Z Channel Name: intensity - Class: rviz_default_plugins/PointCloud + Class: rviz_default_plugins/PointCloud2 Color: 255; 255; 255 Color Transformer: RGB8 Decay Time: 0 diff --git a/nav2_costmap_2d/include/nav2_costmap_2d/voxel_layer.hpp b/nav2_costmap_2d/include/nav2_costmap_2d/voxel_layer.hpp index 22cf4269863308356a59ea689547a00d0435ba36..376ae45be8ef01325bd3d17399bd482ab91442cf 100644 --- a/nav2_costmap_2d/include/nav2_costmap_2d/voxel_layer.hpp +++ b/nav2_costmap_2d/include/nav2_costmap_2d/voxel_layer.hpp @@ -99,7 +99,8 @@ private: nav2_voxel_grid::VoxelGrid voxel_grid_; double z_resolution_, origin_z_; int unknown_threshold_, mark_threshold_, size_z_; - rclcpp::Publisher<sensor_msgs::msg::PointCloud>::SharedPtr clearing_endpoints_pub_; + rclcpp_lifecycle::LifecyclePublisher<sensor_msgs::msg::PointCloud2>::SharedPtr + clearing_endpoints_pub_; inline bool worldToMap3DFloat( double wx, double wy, double wz, double & mx, double & my, diff --git a/nav2_costmap_2d/plugins/voxel_layer.cpp b/nav2_costmap_2d/plugins/voxel_layer.cpp index 40840426e3fb09b88ca9b1986a041197431d07d1..b8e5261bc148f53bf8b45925b30ea5c07c4f1127 100644 --- a/nav2_costmap_2d/plugins/voxel_layer.cpp +++ b/nav2_costmap_2d/plugins/voxel_layer.cpp @@ -96,8 +96,9 @@ void VoxelLayer::onInitialize() voxel_pub_->on_activate(); } - clearing_endpoints_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud>( + clearing_endpoints_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>( "clearing_endpoints", custom_qos); + clearing_endpoints_pub_->on_activate(); unknown_threshold_ += (VOXEL_BITS - size_z_); matchSize(); @@ -302,11 +303,9 @@ void VoxelLayer::raytraceFreespace( double * max_x, double * max_y) { - auto clearing_endpoints_ = std::make_unique<sensor_msgs::msg::PointCloud>(); + auto clearing_endpoints_ = std::make_unique<sensor_msgs::msg::PointCloud2>(); - size_t clearing_observation_cloud_size = clearing_observation.cloud_->height * - clearing_observation.cloud_->width; - if (clearing_observation_cloud_size == 0) { + if (clearing_observation.cloud_->height == 0 || clearing_observation.cloud_->width == 0) { return; } @@ -334,10 +333,23 @@ void VoxelLayer::raytraceFreespace( } if (publish_clearing_points) { - clearing_endpoints_->points.clear(); - clearing_endpoints_->points.reserve(clearing_observation_cloud_size); + clearing_endpoints_->data.clear(); + clearing_endpoints_->width = clearing_observation.cloud_->width; + clearing_endpoints_->height = clearing_observation.cloud_->height; + clearing_endpoints_->is_dense = true; + clearing_endpoints_->is_bigendian = false; } + sensor_msgs::PointCloud2Modifier modifier(*clearing_endpoints_); + modifier.setPointCloud2Fields( + 3, "x", 1, sensor_msgs::msg::PointField::FLOAT32, + "y", 1, sensor_msgs::msg::PointField::FLOAT32, + "z", 1, sensor_msgs::msg::PointField::FLOAT32); + + sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_x(*clearing_endpoints_, "x"); + sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_y(*clearing_endpoints_, "y"); + sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_z(*clearing_endpoints_, "z"); + // we can pre-compute the enpoints of the map outside of the inner loop... we'll need these later double map_end_x = origin_x_ + getSizeInMetersX(); double map_end_y = origin_y_ + getSizeInMetersY(); @@ -414,11 +426,13 @@ void VoxelLayer::raytraceFreespace( max_y); if (publish_clearing_points) { - geometry_msgs::msg::Point32 point; - point.x = wpx; - point.y = wpy; - point.z = wpz; - clearing_endpoints_->points.push_back(point); + *clearing_endpoints_iter_x = wpx; + *clearing_endpoints_iter_y = wpy; + *clearing_endpoints_iter_z = wpz; + + ++clearing_endpoints_iter_x; + ++clearing_endpoints_iter_y; + ++clearing_endpoints_iter_z; } } } diff --git a/nav2_costmap_2d/src/costmap_2d_cloud.cpp b/nav2_costmap_2d/src/costmap_2d_cloud.cpp index 52157b3b7a5e104f4311e7bf80c2080819798a7b..8c40709be6d794367a624c4467f9de5c640068f3 100644 --- a/nav2_costmap_2d/src/costmap_2d_cloud.cpp +++ b/nav2_costmap_2d/src/costmap_2d_cloud.cpp @@ -31,8 +31,8 @@ #include <utility> #include "rclcpp/rclcpp.hpp" -#include "sensor_msgs/msg/point_cloud.hpp" -#include "sensor_msgs/msg/channel_float32.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" @@ -70,8 +70,63 @@ V_Cell g_unknown; rclcpp::Node::SharedPtr g_node; -rclcpp::Publisher<sensor_msgs::msg::PointCloud>::SharedPtr pub_marked; -rclcpp::Publisher<sensor_msgs::msg::PointCloud>::SharedPtr pub_unknown; +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) { @@ -133,65 +188,19 @@ void voxelCallback(const nav2_msgs::msg::VoxelGrid::ConstSharedPtr grid) } } - { - auto cloud = std::make_unique<sensor_msgs::msg::PointCloud>(); - cloud->points.resize(num_marked); - cloud->channels.resize(1); - cloud->channels[0].values.resize(num_marked); - cloud->channels[0].name = "rgb"; - cloud->header.frame_id = frame_id; - cloud->header.stamp = stamp; - - sensor_msgs::msg::ChannelFloat32 & chan = cloud->channels[0]; - for (uint32_t i = 0; i < num_marked; ++i) { - geometry_msgs::msg::Point32 & p = cloud->points[i]; - float & cval = chan.values[i]; - Cell & c = g_marked[i]; - - p.x = c.x; - p.y = c.y; - p.z = c.z; - - uint32_t r = g_colors_r[c.status] * 255.0; - uint32_t g = g_colors_g[c.status] * 255.0; - uint32_t b = g_colors_b[c.status] * 255.0; - // uint32_t a = g_colors_a[c.status] * 255.0; - - uint32_t col = (r << 16) | (g << 8) | b; - memcpy(&cval, &col, sizeof col); - } + 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::PointCloud>(); - cloud->points.resize(num_unknown); - cloud->channels.resize(1); - cloud->channels[0].values.resize(num_unknown); - cloud->channels[0].name = "rgb"; - cloud->header.frame_id = frame_id; - cloud->header.stamp = stamp; - - sensor_msgs::msg::ChannelFloat32 & chan = cloud->channels[0]; - for (uint32_t i = 0; i < num_unknown; ++i) { - geometry_msgs::msg::Point32 & p = cloud->points[i]; - float & cval = chan.values[i]; - Cell & c = g_unknown[i]; - - p.x = c.x; - p.y = c.y; - p.z = c.z; - - uint32_t r = g_colors_r[c.status] * 255.0; - uint32_t g = g_colors_g[c.status] * 255.0; - uint32_t b = g_colors_b[c.status] * 255.0; - // uint32_t a = g_colors_a[c.status] * 255.0; - - uint32_t col = (r << 16) | (g << 8) | b; - memcpy(&cval, &col, sizeof col); - } - + auto cloud = std::make_unique<sensor_msgs::msg::PointCloud2>(); + pointCloud2Helper(cloud, num_unknown, pcl_header, g_unknown); pub_unknown->publish(std::move(cloud)); } @@ -208,9 +217,9 @@ int main(int argc, char ** argv) RCLCPP_DEBUG(g_node->get_logger(), "Starting up costmap_2d_cloud"); - pub_marked = g_node->create_publisher<sensor_msgs::msg::PointCloud>( + pub_marked = g_node->create_publisher<sensor_msgs::msg::PointCloud2>( "voxel_marked_cloud", 1); - pub_unknown = g_node->create_publisher<sensor_msgs::msg::PointCloud>( + 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); diff --git a/nav2_dwb_controller/dwb_core/include/dwb_core/publisher.hpp b/nav2_dwb_controller/dwb_core/include/dwb_core/publisher.hpp index aba5354c652d1b7e97771ae80a31431ece887676..bd2b210e033cb75fe34d37179808a16e84f33e0f 100644 --- a/nav2_dwb_controller/dwb_core/include/dwb_core/publisher.hpp +++ b/nav2_dwb_controller/dwb_core/include/dwb_core/publisher.hpp @@ -124,7 +124,7 @@ protected: std::shared_ptr<LifecyclePublisher<nav_msgs::msg::Path>> transformed_pub_; std::shared_ptr<LifecyclePublisher<nav_msgs::msg::Path>> local_pub_; std::shared_ptr<LifecyclePublisher<visualization_msgs::msg::MarkerArray>> marker_pub_; - std::shared_ptr<LifecyclePublisher<sensor_msgs::msg::PointCloud>> cost_grid_pc_pub_; + std::shared_ptr<LifecyclePublisher<sensor_msgs::msg::PointCloud2>> cost_grid_pc_pub_; rclcpp_lifecycle::LifecycleNode::WeakPtr node_; rclcpp::Clock::SharedPtr clock_; diff --git a/nav2_dwb_controller/dwb_core/include/dwb_core/trajectory_critic.hpp b/nav2_dwb_controller/dwb_core/include/dwb_core/trajectory_critic.hpp index 2724bfd8a10af06f67a13aafd33ed06f0c2726f8..17899a29947c2f58a57298e7e1f9993066be3e8c 100644 --- a/nav2_dwb_controller/dwb_core/include/dwb_core/trajectory_critic.hpp +++ b/nav2_dwb_controller/dwb_core/include/dwb_core/trajectory_critic.hpp @@ -38,6 +38,7 @@ #include <string> #include <vector> #include <memory> +#include <utility> #include "rclcpp/rclcpp.hpp" #include "nav2_costmap_2d/costmap_2d_ros.hpp" @@ -166,7 +167,7 @@ public: * * @param pc PointCloud to add channels to */ - virtual void addCriticVisualization(sensor_msgs::msg::PointCloud &) {} + virtual void addCriticVisualization(std::vector<std::pair<std::string, std::vector<float>>> &) {} std::string getName() { diff --git a/nav2_dwb_controller/dwb_core/src/publisher.cpp b/nav2_dwb_controller/dwb_core/src/publisher.cpp index 70adb22d8196b0b87d065943decab18430f21449..7b860fef65a28667659b0929cdcd51ff8f84314c 100644 --- a/nav2_dwb_controller/dwb_core/src/publisher.cpp +++ b/nav2_dwb_controller/dwb_core/src/publisher.cpp @@ -40,6 +40,7 @@ #include <vector> #include <utility> +#include "sensor_msgs/point_cloud2_iterator.hpp" #include "nav_2d_utils/conversions.hpp" #include "nav2_util/node_utils.hpp" #include "sensor_msgs/msg/point_cloud2.hpp" @@ -105,7 +106,7 @@ DWBPublisher::on_configure() transformed_pub_ = node->create_publisher<nav_msgs::msg::Path>("transformed_global_plan", 1); local_pub_ = node->create_publisher<nav_msgs::msg::Path>("local_plan", 1); marker_pub_ = node->create_publisher<visualization_msgs::msg::MarkerArray>("marker", 1); - cost_grid_pc_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud>("cost_cloud", 1); + cost_grid_pc_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>("cost_cloud", 1); double marker_lifetime = 0.0; node->get_parameter(plugin_name_ + ".marker_lifetime", marker_lifetime); @@ -256,7 +257,7 @@ DWBPublisher::publishCostGrid( if (!publish_cost_grid_pc_) {return;} - auto cost_grid_pc = std::make_unique<sensor_msgs::msg::PointCloud>(); + auto cost_grid_pc = std::make_unique<sensor_msgs::msg::PointCloud2>(); cost_grid_pc->header.frame_id = costmap_ros->getGlobalFrameID(); cost_grid_pc->header.stamp = clock_->now(); @@ -264,38 +265,75 @@ DWBPublisher::publishCostGrid( double x_coord, y_coord; unsigned int size_x = costmap->getSizeInCellsX(); unsigned int size_y = costmap->getSizeInCellsY(); - cost_grid_pc->points.resize(size_x * size_y); - unsigned int i = 0; - for (unsigned int cy = 0; cy < size_y; cy++) { - for (unsigned int cx = 0; cx < size_x; cx++) { - costmap->mapToWorld(cx, cy, x_coord, y_coord); - cost_grid_pc->points[i].x = x_coord; - cost_grid_pc->points[i].y = y_coord; - i++; - } - } - sensor_msgs::msg::ChannelFloat32 totals; - totals.name = "total_cost"; - totals.values.resize(size_x * size_y, 0.0); + std::vector<std::pair<std::string, std::vector<float>>> cost_channels; + std::vector<float> total_cost(size_x * size_y, 0.0); for (TrajectoryCritic::Ptr critic : critics) { - unsigned int channel_index = cost_grid_pc->channels.size(); - critic->addCriticVisualization(*cost_grid_pc); - if (channel_index == cost_grid_pc->channels.size()) { + unsigned int channel_index = cost_channels.size(); + critic->addCriticVisualization(cost_channels); + if (channel_index == cost_channels.size()) { // No channels were added, so skip to next critic continue; } double scale = critic->getScale(); - for (i = 0; i < size_x * size_y; i++) { - totals.values[i] += cost_grid_pc->channels[channel_index].values[i] * scale; + for (unsigned int i = 0; i < size_x * size_y; i++) { + total_cost[i] += cost_channels[channel_index].second[i] * scale; + } + } + + cost_channels.push_back(std::make_pair("total_cost", total_cost)); + + cost_grid_pc->width = size_x * size_y; + cost_grid_pc->height = 1; + cost_grid_pc->fields.resize(3 + cost_channels.size()); // x,y,z, + cost channels + cost_grid_pc->is_dense = true; + cost_grid_pc->is_bigendian = false; + + int offset = 0; + for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i, offset += 4) { + cost_grid_pc->fields[i].offset = offset; + cost_grid_pc->fields[i].count = 1; + cost_grid_pc->fields[i].datatype = sensor_msgs::msg::PointField::FLOAT32; + if (i >= 3) { + cost_grid_pc->fields[i].name = cost_channels[i - 3].first; + } + } + + cost_grid_pc->fields[0].name = "x"; + cost_grid_pc->fields[1].name = "y"; + cost_grid_pc->fields[2].name = "z"; + + cost_grid_pc->point_step = offset; + cost_grid_pc->row_step = cost_grid_pc->point_step * cost_grid_pc->width; + cost_grid_pc->data.resize(cost_grid_pc->row_step * cost_grid_pc->height); + + std::vector<sensor_msgs::PointCloud2Iterator<float>> cost_grid_pc_iter; + + for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i) { + sensor_msgs::PointCloud2Iterator<float> iter(*cost_grid_pc, cost_grid_pc->fields[i].name); + cost_grid_pc_iter.push_back(iter); + } + + unsigned int j = 0; + for (unsigned int cy = 0; cy < size_y; cy++) { + for (unsigned int cx = 0; cx < size_x; cx++) { + costmap->mapToWorld(cx, cy, x_coord, y_coord); + *cost_grid_pc_iter[0] = x_coord; + *cost_grid_pc_iter[1] = y_coord; + *cost_grid_pc_iter[2] = 0.0; // z value + + for (size_t i = 3; i < cost_grid_pc_iter.size(); ++i) { + *cost_grid_pc_iter[i] = cost_channels[i - 3].second[j]; + ++cost_grid_pc_iter[i]; + } + ++cost_grid_pc_iter[0]; + ++cost_grid_pc_iter[1]; + ++cost_grid_pc_iter[2]; + j++; } } - cost_grid_pc->channels.push_back(totals); - // TODO(crdelsey): convert pc to pc2 - // sensor_msgs::msg::PointCloud2 cost_grid_pc2; - // convertPointCloudToPointCloud2(cost_grid_pc, cost_grid_pc2); cost_grid_pc_pub_->publish(std::move(cost_grid_pc)); } diff --git a/nav2_dwb_controller/dwb_critics/include/dwb_critics/base_obstacle.hpp b/nav2_dwb_controller/dwb_critics/include/dwb_critics/base_obstacle.hpp index 5f33445995cc56bb59b422e891ef23bb18f782f8..9bd4675ccb886203a0084a6e90bdb195beeb0cf3 100644 --- a/nav2_dwb_controller/dwb_critics/include/dwb_critics/base_obstacle.hpp +++ b/nav2_dwb_controller/dwb_critics/include/dwb_critics/base_obstacle.hpp @@ -35,6 +35,10 @@ #ifndef DWB_CRITICS__BASE_OBSTACLE_HPP_ #define DWB_CRITICS__BASE_OBSTACLE_HPP_ +#include <string> +#include <vector> +#include <utility> + #include "dwb_core/trajectory_critic.hpp" namespace dwb_critics @@ -55,7 +59,8 @@ class BaseObstacleCritic : public dwb_core::TrajectoryCritic public: void onInit() override; double scoreTrajectory(const dwb_msgs::msg::Trajectory2D & traj) override; - void addCriticVisualization(sensor_msgs::msg::PointCloud & pc) override; + void addCriticVisualization( + std::vector<std::pair<std::string, std::vector<float>>> & cost_channels) override; /** * @brief Return the obstacle score for a particular pose diff --git a/nav2_dwb_controller/dwb_critics/include/dwb_critics/map_grid.hpp b/nav2_dwb_controller/dwb_critics/include/dwb_critics/map_grid.hpp index 4d77e5019c08344db8fd15446a42f2ed6339ad51..611aad46445a337c2f7ada04f25a1d255daa2888 100644 --- a/nav2_dwb_controller/dwb_critics/include/dwb_critics/map_grid.hpp +++ b/nav2_dwb_controller/dwb_critics/include/dwb_critics/map_grid.hpp @@ -37,6 +37,9 @@ #include <vector> #include <memory> +#include <string> +#include <utility> + #include "dwb_core/trajectory_critic.hpp" #include "costmap_queue/costmap_queue.hpp" @@ -61,7 +64,8 @@ public: // Standard TrajectoryCritic Interface void onInit() override; double scoreTrajectory(const dwb_msgs::msg::Trajectory2D & traj) override; - void addCriticVisualization(sensor_msgs::msg::PointCloud & pc) override; + void addCriticVisualization( + std::vector<std::pair<std::string, std::vector<float>>> & cost_channels) override; double getScale() const override {return costmap_->getResolution() * 0.5 * scale_;} // Helper Functions diff --git a/nav2_dwb_controller/dwb_critics/src/base_obstacle.cpp b/nav2_dwb_controller/dwb_critics/src/base_obstacle.cpp index e409b86ce4e1f429e2f59b39034c2070ed55a8df..2dfa3d415a7c56ce3e815dfd4ea2eca3d1a30c08 100644 --- a/nav2_dwb_controller/dwb_critics/src/base_obstacle.cpp +++ b/nav2_dwb_controller/dwb_critics/src/base_obstacle.cpp @@ -31,6 +31,9 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include <vector> +#include <string> +#include <utility> #include "dwb_critics/base_obstacle.hpp" #include "dwb_core/exceptions.hpp" @@ -92,22 +95,23 @@ bool BaseObstacleCritic::isValidCost(const unsigned char cost) cost != nav2_costmap_2d::NO_INFORMATION; } -void BaseObstacleCritic::addCriticVisualization(sensor_msgs::msg::PointCloud & pc) +void BaseObstacleCritic::addCriticVisualization( + std::vector<std::pair<std::string, std::vector<float>>> & cost_channels) { - sensor_msgs::msg::ChannelFloat32 grid_scores; - grid_scores.name = name_; + std::pair<std::string, std::vector<float>> grid_scores; + grid_scores.first = name_; unsigned int size_x = costmap_->getSizeInCellsX(); unsigned int size_y = costmap_->getSizeInCellsY(); - grid_scores.values.resize(size_x * size_y); + grid_scores.second.resize(size_x * size_y); unsigned int i = 0; for (unsigned int cy = 0; cy < size_y; cy++) { for (unsigned int cx = 0; cx < size_x; cx++) { - grid_scores.values[i] = costmap_->getCost(cx, cy); + grid_scores.second[i] = costmap_->getCost(cx, cy); i++; } } - pc.channels.push_back(grid_scores); + cost_channels.push_back(grid_scores); } } // namespace dwb_critics diff --git a/nav2_dwb_controller/dwb_critics/src/map_grid.cpp b/nav2_dwb_controller/dwb_critics/src/map_grid.cpp index c70f7125e4c544d9703407d2177ef8fdbd9c112c..030e77cd637933495ef6f4398ff290963216f6da 100644 --- a/nav2_dwb_controller/dwb_critics/src/map_grid.cpp +++ b/nav2_dwb_controller/dwb_critics/src/map_grid.cpp @@ -35,6 +35,8 @@ #include "dwb_critics/map_grid.hpp" #include <cmath> #include <string> +#include <vector> +#include <utility> #include <algorithm> #include <memory> #include "dwb_core/exceptions.hpp" @@ -164,23 +166,24 @@ double MapGridCritic::scorePose(const geometry_msgs::msg::Pose2D & pose) return getScore(cell_x, cell_y); } -void MapGridCritic::addCriticVisualization(sensor_msgs::msg::PointCloud & pc) +void MapGridCritic::addCriticVisualization( + std::vector<std::pair<std::string, std::vector<float>>> & cost_channels) { - sensor_msgs::msg::ChannelFloat32 grid_scores; - grid_scores.name = name_; + std::pair<std::string, std::vector<float>> grid_scores; + grid_scores.first = name_; nav2_costmap_2d::Costmap2D * costmap = costmap_ros_->getCostmap(); unsigned int size_x = costmap->getSizeInCellsX(); unsigned int size_y = costmap->getSizeInCellsY(); - grid_scores.values.resize(size_x * size_y); + grid_scores.second.resize(size_x * size_y); unsigned int i = 0; for (unsigned int cy = 0; cy < size_y; cy++) { for (unsigned int cx = 0; cx < size_x; cx++) { - grid_scores.values[i] = getScore(cx, cy); + grid_scores.second[i] = getScore(cx, cy); i++; } } - pc.channels.push_back(grid_scores); + cost_channels.push_back(grid_scores); } } // namespace dwb_critics diff --git a/nav2_dwb_controller/dwb_critics/test/base_obstacle_test.cpp b/nav2_dwb_controller/dwb_critics/test/base_obstacle_test.cpp index 7cf8de890bfa529a6500709d5b27094bbe96b041..15f5dd70a6e5cfda275a44892c8b76a97781b6c4 100644 --- a/nav2_dwb_controller/dwb_critics/test/base_obstacle_test.cpp +++ b/nav2_dwb_controller/dwb_critics/test/base_obstacle_test.cpp @@ -35,6 +35,7 @@ #include <vector> #include <memory> #include <string> +#include <utility> #include "gtest/gtest.h" #include "rclcpp/rclcpp.hpp" @@ -141,8 +142,8 @@ TEST(BaseObstacle, CriticVisualization) costmap_ros->getCostmap()->setCost(10, 49, 24); costmap_ros->getCostmap()->setCost(45, 2, 12); - sensor_msgs::msg::PointCloud pointcloud; - critic->addCriticVisualization(pointcloud); + std::vector<std::pair<std::string, std::vector<float>>> cost_channels; + critic->addCriticVisualization(cost_channels); unsigned int size_x = costmap_ros->getCostmap()->getSizeInCellsX(); unsigned int size_y = costmap_ros->getCostmap()->getSizeInCellsY(); @@ -150,7 +151,7 @@ TEST(BaseObstacle, CriticVisualization) // The values in the pointcloud should be equal to the values in the costmap for (unsigned int y = 0; y < size_y; y++) { for (unsigned int x = 0; x < size_x; x++) { - float pointValue = pointcloud.channels[0].values[y * size_y + x]; + float pointValue = cost_channels[0].second[y * size_y + x]; ASSERT_EQ(static_cast<int>(pointValue), costmap_ros->getCostmap()->getCost(x, y)); } } diff --git a/nav2_system_tests/src/costmap_filters/keepout_params.yaml b/nav2_system_tests/src/costmap_filters/keepout_params.yaml index 72d93eddf9c4b47f66b146cab901f183719bf2c9..5d37d77ce2f97beaf858d6b20689a59f98d6a8ea 100644 --- a/nav2_system_tests/src/costmap_filters/keepout_params.yaml +++ b/nav2_system_tests/src/costmap_filters/keepout_params.yaml @@ -146,6 +146,8 @@ controller_server: RotateToGoal.scale: 32.0 RotateToGoal.slowing_factor: 5.0 RotateToGoal.lookahead_time: -1.0 + publish_cost_grid_pc: True + controller_server_rclcpp_node: ros__parameters: @@ -185,6 +187,10 @@ local_costmap: clearing: True marking: True data_type: "LaserScan" + raytrace_max_range: 3.0 + raytrace_min_range: 0.0 + obstacle_max_range: 2.5 + obstacle_min_range: 0.0 static_layer: map_subscribe_transient_local: True keepout_filter: diff --git a/nav2_system_tests/src/costmap_filters/test_keepout_launch.py b/nav2_system_tests/src/costmap_filters/test_keepout_launch.py index 9365f25771b05a9c56d56c12379a38727795d6bc..c2c7e093230473aca1e2cf2ff7d383a1df94a934 100755 --- a/nav2_system_tests/src/costmap_filters/test_keepout_launch.py +++ b/nav2_system_tests/src/costmap_filters/test_keepout_launch.py @@ -114,6 +114,13 @@ def generate_launch_description(): 'params_file': new_yaml, 'bt_xml_file': bt_navigator_xml, 'autostart': 'True'}.items()), + + Node( + package='nav2_costmap_2d', + executable='nav2_costmap_2d_cloud', + name='costmap_2d_cloud', + output='screen', + remappings=[('voxel_grid', 'local_costmap/voxel_grid')]), ]) diff --git a/nav2_system_tests/src/costmap_filters/tester_node.py b/nav2_system_tests/src/costmap_filters/tester_node.py index c5ccef680d9c89f54c1413427ae29d48ffbcddef..ca0b585b6654451b51cc2649db39ba4c6374ab5e 100755 --- a/nav2_system_tests/src/costmap_filters/tester_node.py +++ b/nav2_system_tests/src/costmap_filters/tester_node.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 # Copyright (c) 2018 Intel Corporation. # Copyright (c) 2020 Samsung Research Russia @@ -34,11 +34,11 @@ from nav_msgs.msg import OccupancyGrid from nav_msgs.msg import Path import rclpy - from rclpy.action import ActionClient from rclpy.node import Node from rclpy.qos import QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy from rclpy.qos import QoSProfile +from sensor_msgs.msg import PointCloud2 class TestType(Enum): @@ -96,26 +96,46 @@ class NavTester(Node): self.goal_pub = self.create_publisher(PoseStamped, 'goal_pose', 10) transient_local_qos = QoSProfile( - durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, - reliability=QoSReliabilityPolicy.RELIABLE, - history=QoSHistoryPolicy.KEEP_LAST, - depth=1) + durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1) volatile_qos = QoSProfile( - durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_VOLATILE, - reliability=QoSReliabilityPolicy.RELIABLE, - history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST, - depth=1) + durability=QoSDurabilityPolicy.RMW_QOS_POLICY_DURABILITY_VOLATILE, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST, + depth=1) self.model_pose_sub = self.create_subscription(PoseWithCovarianceStamped, 'amcl_pose', self.poseCallback, transient_local_qos) - + self.clearing_ep_sub = self.create_subscription(PointCloud2, + 'local_costmap/clearing_endpoints', + self.clearingEndpointsCallback, + transient_local_qos) self.test_type = test_type self.filter_test_result = True + self.clearing_endpoints_received = False + self.voxel_marked_received = False + self.voxel_unknown_received = False + self.cost_cloud_received = False + if self.test_type == TestType.KEEPOUT: self.plan_sub = self.create_subscription(Path, 'plan', self.planCallback, volatile_qos) + self.voxel_marked_sub = self.create_subscription(PointCloud2, + 'voxel_marked_cloud', + self.voxelMarkedCallback, + 1) + self.voxel_unknown_sub = self.create_subscription(PointCloud2, + 'voxel_unknown_cloud', + self.voxelUnknownCallback, + 1) + self.cost_cloud_sub = self.create_subscription(PointCloud2, + 'cost_cloud', + self.dwbCostCloudCallback, + 1) elif self.test_type == TestType.SPEED: self.speed_it = 0 # Expected chain of speed limits @@ -132,7 +152,8 @@ class NavTester(Node): self.initial_pose_received = False self.initial_pose = initial_pose self.goal_pose = goal_pose - self.action_client = ActionClient(self, NavigateToPose, 'navigate_to_pose') + self.action_client = ActionClient( + self, NavigateToPose, 'navigate_to_pose') def info_msg(self, msg: str): self.get_logger().info('\033[1;37;44m' + msg + '\033[0m') @@ -165,7 +186,8 @@ class NavTester(Node): # Sends a `NavToPose` action request and waits for completion self.info_msg("Waiting for 'NavigateToPose' action server") while not self.action_client.wait_for_server(timeout_sec=1.0): - self.info_msg("'NavigateToPose' action server not available, waiting...") + self.info_msg( + "'NavigateToPose' action server not available, waiting...") self.goal_pose = goal_pose if goal_pose is not None else self.goal_pose goal_msg = NavigateToPose.Goal() @@ -208,7 +230,8 @@ class NavTester(Node): self.warn_msg('Filter mask was not received') elif self.isInKeepout(x, y): self.filter_test_result = False - self.error_msg('Pose (' + str(x) + ', ' + str(y) + ') belongs to keepout zone') + self.error_msg('Pose (' + str(x) + ', ' + + str(y) + ') belongs to keepout zone') return False return True @@ -245,6 +268,23 @@ class NavTester(Node): self.error_msg('Path plan intersects with keepout zone') return + def clearingEndpointsCallback(self, msg): + if len(msg.data) > 0: + self.clearing_endpoints_received = True + + def voxelMarkedCallback(self, msg): + if len(msg.data) > 0: + self.voxel_marked_received = True + + def voxelUnknownCallback(self, msg): + if len(msg.data) > 0: + self.voxel_unknown_received = True + + def dwbCostCloudCallback(self, msg): + self.info_msg('Received cost_cloud points') + if len(msg.data) > 0: + self.cost_cloud_received = True + def speedLimitCallback(self, msg): self.info_msg('Received speed limit: ' + str(msg.speed_limit)) self.checkSpeed(self.speed_it, msg.speed_limit) @@ -266,6 +306,21 @@ class NavTester(Node): return False return True + def wait_for_pointcloud_subscribers(self, timeout): + start_time = time.time() + while not self.voxel_unknown_received or not self.voxel_marked_received \ + or not self.clearing_endpoints_received: + self.info_msg( + 'Waiting for voxel_marked_cloud/voxel_unknown_cloud/\ + clearing_endpoints msg to be received ...') + rclpy.spin_once(self, timeout_sec=1) + if (time.time() - start_time) > timeout: + self.error_msg( + 'Time out to waiting for voxel_marked_cloud/voxel_unknown_cloud/\ + clearing_endpoints msgs') + return False + return True + def reachesGoal(self, timeout, distance): goalReached = False start_time = time.time() @@ -305,7 +360,8 @@ class NavTester(Node): state = future.result().current_state.label self.info_msg('Result of get_state: %s' % state) else: - self.error_msg('Exception while calling service: %r' % future.exception()) + self.error_msg('Exception while calling service: %r' % + future.exception()) time.sleep(5) def shutdown(self): @@ -313,9 +369,11 @@ class NavTester(Node): self.action_client.destroy() transition_service = 'lifecycle_manager_navigation/manage_nodes' - mgr_client = self.create_client(ManageLifecycleNodes, transition_service) + mgr_client = self.create_client( + ManageLifecycleNodes, transition_service) while not mgr_client.wait_for_service(timeout_sec=1.0): - self.info_msg(transition_service + ' service not available, waiting...') + self.info_msg(transition_service + + ' service not available, waiting...') req = ManageLifecycleNodes.Request() req.command = ManageLifecycleNodes.Request().SHUTDOWN @@ -324,13 +382,16 @@ class NavTester(Node): self.info_msg('Shutting down navigation lifecycle manager...') rclpy.spin_until_future_complete(self, future) future.result() - self.info_msg('Shutting down navigation lifecycle manager complete.') + self.info_msg( + 'Shutting down navigation lifecycle manager complete.') except Exception as e: # noqa: B902 self.error_msg('Service call failed %r' % (e,)) transition_service = 'lifecycle_manager_localization/manage_nodes' - mgr_client = self.create_client(ManageLifecycleNodes, transition_service) + mgr_client = self.create_client( + ManageLifecycleNodes, transition_service) while not mgr_client.wait_for_service(timeout_sec=1.0): - self.info_msg(transition_service + ' service not available, waiting...') + self.info_msg(transition_service + + ' service not available, waiting...') req = ManageLifecycleNodes.Request() req.command = ManageLifecycleNodes.Request().SHUTDOWN @@ -339,7 +400,8 @@ class NavTester(Node): self.info_msg('Shutting down localization lifecycle manager...') rclpy.spin_until_future_complete(self, future) future.result() - self.info_msg('Shutting down localization lifecycle manager complete') + self.info_msg( + 'Shutting down localization lifecycle manager complete') except Exception as e: # noqa: B902 self.error_msg('Service call failed %r' % (e,)) @@ -382,16 +444,19 @@ def run_all_tests(robot_tester): robot_tester.wait_for_initial_pose() robot_tester.wait_for_node_active('bt_navigator') result = robot_tester.wait_for_filter_mask(10) - if (result): result = robot_tester.runNavigateAction() + if robot_tester.test_type == TestType.KEEPOUT: + result = result and robot_tester.wait_for_pointcloud_subscribers(10) + if (result): result = test_RobotMovesToGoal(robot_tester) if (result): if robot_tester.test_type == TestType.KEEPOUT: result = robot_tester.filter_test_result + result = result and robot_tester.cost_cloud_received elif robot_tester.test_type == TestType.SPEED: result = test_SpeedLimitsAllCorrect(robot_tester) @@ -438,7 +503,8 @@ def get_tester(args): def main(argv=sys.argv[1:]): # The robot(s) positions from the input arguments - parser = argparse.ArgumentParser(description='System-level costmap filters tester node') + parser = argparse.ArgumentParser( + description='System-level costmap filters tester node') parser.add_argument('-t', '--type', type=str, action='store', dest='type', help='Type of costmap filter being tested.') group = parser.add_mutually_exclusive_group(required=True)