/* * Software License Agreement (BSD License) * * Copyright (c) 2019, DFKI GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include namespace mir_dwb_critics { bool PathProgressCritic::prepare(const geometry_msgs::Pose2D &pose, const nav_2d_msgs::Twist2D &vel, const geometry_msgs::Pose2D &goal, const nav_2d_msgs::Path2D &global_plan) { dwb_critics::MapGridCritic::reset(); unsigned int local_goal_x, local_goal_y; if (!getGoalPose(pose, global_plan, local_goal_x, local_goal_y, desired_angle_)) { return false; } // Enqueue just the last pose cell_values_.setValue(local_goal_x, local_goal_y, 0.0); queue_->enqueueCell(local_goal_x, local_goal_y); // MapGridCritic::propogateManhattanDistances propogateManhattanDistances(); return true; } void PathProgressCritic::onInit() { dwb_critics::MapGridCritic::onInit(); critic_nh_.param("xy_local_goal_tolerance", xy_local_goal_tolerance_, 0.20); critic_nh_.param("angle_threshold", angle_threshold_, M_PI_4); critic_nh_.param("heading_scale", heading_scale_, 1.0); reached_intermediate_goals_pub_ = critic_nh_.advertise("reached_intermediate_goals", 1); current_goal_pub_ = critic_nh_.advertise("current_goal", 1); closet_robot_goal_pub_ = critic_nh_.advertise("closet_robot_goal", 1); // divide heading scale by position scale because the sum will be multiplied by scale again heading_scale_ /= getScale(); } void PathProgressCritic::reset() { reached_intermediate_goals_.clear(); } double PathProgressCritic::scoreTrajectory(const dwb_msgs::Trajectory2D &traj) { double position_score = MapGridCritic::scoreTrajectory(traj); double heading_diff = fabs(remainder(traj.poses.back().theta - desired_angle_, 2 * M_PI)); double heading_score = heading_diff * heading_diff; return position_score + heading_scale_ * heading_score; } bool PathProgressCritic::getGoalPose(const geometry_msgs::Pose2D &robot_pose, const nav_2d_msgs::Path2D &global_plan, unsigned int &x, unsigned int &y, double &desired_angle) { const nav_core2::Costmap &costmap = *costmap_; const nav_grid::NavGridInfo &info = costmap.getInfo(); if (global_plan.poses.empty()) { ROS_ERROR_NAMED("PathProgressCritic", "The global plan was empty."); return false; } // Chuyển đổi dữ liệu kế hoạch đường đi 'nav_2d_msgs::Path2D' sang 'std::vector' std::vector plan = nav_2d_utils::adjustPlanResolution(global_plan, info.resolution).poses; // find the "start pose", i.e. the pose on the plan closest to the robot that is also on the local map // Tìm điểm pose bắt đầu trên kế hoạch đường đi gần nhất với robot đồng thời có tồn tại trong local map unsigned int start_index = 0; // số vị trí pose trong kế hoạch đường đi double distance_to_start = std::numeric_limits::infinity(); // Xét giá trị là vô cùng oo bool started_path = false; for (unsigned int i = 0; i < plan.size(); i++) { double g_x = plan[i].x; double g_y = plan[i].y; unsigned int map_x, map_y; if (worldToGridBounded(info, g_x, g_y, map_x, map_y) // chuyển tọa độ từ đơn vị mét sang pixel && costmap(map_x, map_y) != nav_core2::Costmap::NO_INFORMATION) { // chi phí phải khác -1 // Still on the costmap. Continue. double distance = nav_2d_utils::poseDistance(plan[i], robot_pose); // Tính khoảng cách từ vị trí của robot đến pose đang xét if (distance_to_start > distance) { start_index = i; distance_to_start = distance; started_path = true; } // else // { // // Plan is going away from the robot again. It's possible that it comes back and we would find a pose that's // // even closer to the robot, but then we would skip over parts of the plan. // ss <<" index_max_ " << index_max_; // break; // } } else if (started_path) { // Off the costmap after being on the costmap. break; } // else, we have not yet found a point on the costmap, so we just continue } geometry_msgs::PoseStamped pose; pose.header.stamp = ros::Time::now(); pose.header.frame_id = global_plan.header.frame_id; pose.pose.position.x = plan[start_index].x; pose.pose.position.y = plan[start_index].y; tf2::Quaternion temp; temp.setRPY(0, 0, plan[start_index].theta); pose.pose.orientation.x = temp.getX(); pose.pose.orientation.y = temp.getY(); pose.pose.orientation.z = temp.getZ(); pose.pose.orientation.w = temp.getW(); closet_robot_goal_pub_.publish(pose); // Nếu khồng tìm được điểm gần với robot nhất thì trả về false if (!started_path) { ROS_ERROR_NAMED("PathProgressCritic", "None of the points of the global plan were in the local costmap."); return false; } // current_index_ = start_index; // find the "last valid pose", i.e. the last pose on the plan after the start pose that is still on the local map // Tìm điểm pose cuôi cùng sau điểm pose bắt đầu và có tồn tại trong local map unsigned int last_valid_index = start_index; for (unsigned int i = start_index + 1; i < plan.size(); i++) { double g_x = plan[i].x; double g_y = plan[i].y; unsigned int map_x, map_y; if (worldToGridBounded(info, g_x, g_y, map_x, map_y) && costmap(map_x, map_y) != nav_core2::Costmap::NO_INFORMATION) { // Still on the costmap. Continue. last_valid_index = i; } else { // Off the costmap after being on the costmap. break; } } // find the "goal pose" by walking along the plan as long as each step leads far enough away from the starting point, // i.e. is within angle_threshold_ of the starting direction. // Tìm điểm mục tiêu chạy từ điểm start pose đến last valid pose dọc thep plan unsigned int goal_index = start_index; while (goal_index < last_valid_index && critic_nh_.ok()) { // Tìm kiếm vị trí điểm mục tiêu phù hợp goal_index = getGoalIndex(robot_pose, plan, start_index, last_valid_index); // check if goal already reached bool goal_already_reached = false; geometry_msgs::PoseArray poseArrayMsg; poseArrayMsg.header.stamp = ros::Time::now(); poseArrayMsg.header.frame_id = global_plan.header.frame_id; for (const auto &pose2D : reached_intermediate_goals_) { geometry_msgs::Pose pose; pose.position.x = pose2D.x; pose.position.y = pose2D.y; tf2::Quaternion temp; temp.setRPY(0, 0, pose2D.theta); pose.orientation.x = temp.getX(); pose.orientation.y = temp.getY(); pose.orientation.z = temp.getZ(); pose.orientation.w = temp.getW(); poseArrayMsg.poses.push_back(pose); } reached_intermediate_goals_pub_.publish(poseArrayMsg); for (auto &reached_intermediate_goal : reached_intermediate_goals_) { double distance = nav_2d_utils::poseDistance(reached_intermediate_goal, plan[goal_index]); if (distance < xy_local_goal_tolerance_) { goal_already_reached = true; // choose a new start_index by walking along the plan until we're outside xy_local_goal_tolerance_ and try again // (start_index is now > goal_index) for (start_index = goal_index; start_index <= last_valid_index; ++start_index) { distance = nav_2d_utils::poseDistance(reached_intermediate_goal, plan[start_index]); if (distance >= xy_local_goal_tolerance_) { break; } } break; } } pose.header.stamp = ros::Time::now(); pose.header.frame_id = global_plan.header.frame_id; pose.pose.position.x = plan[goal_index].x; pose.pose.position.y = plan[goal_index].y; tf2::Quaternion temp; temp.setRPY(0, 0, plan[goal_index].theta); pose.pose.orientation.x = temp.getX(); pose.pose.orientation.y = temp.getY(); pose.pose.orientation.z = temp.getZ(); pose.pose.orientation.w = temp.getW(); current_goal_pub_.publish(pose); if (!goal_already_reached) { // new goal not in reached_intermediate_goals_ double distance = nav_2d_utils::poseDistance(plan[goal_index], robot_pose); if (distance < xy_local_goal_tolerance_) { // the robot has currently reached the goal reached_intermediate_goals_.push_back(plan[goal_index]); ROS_DEBUG_NAMED("PathProgressCritic", "Number of reached_intermediate goals: %zu", reached_intermediate_goals_.size()); } else { // we've found a new goal! break; } } } if (start_index > goal_index) start_index = goal_index; ROS_ASSERT(goal_index <= last_valid_index); // save goal in x, y worldToGridBounded(info, plan[goal_index].x, plan[goal_index].y, x, y); desired_angle = plan[start_index].theta; return true; } unsigned int PathProgressCritic::getGoalIndex(const geometry_msgs::Pose2D &robot_pose, const std::vector &plan, unsigned int start_index, unsigned int last_valid_index) const { if (start_index >= last_valid_index) return last_valid_index; unsigned int goal_index = start_index; for (unsigned int i = start_index + 1; i <= last_valid_index; ++i) { const double current_direction_x = plan[i].x - plan[i - 1].x; const double current_direction_y = plan[i].y - plan[i - 1].y; if (fabs(current_direction_x) > 1e-9 || fabs(current_direction_y) > 1e-9) { double current_angle = atan2(current_direction_y, current_direction_x); if (fabs(remainder(robot_pose.theta - current_angle, 2 * M_PI)) > angle_threshold_) break; goal_index = i; } } if (nav_2d_utils::poseDistance(plan[goal_index], plan[last_valid_index]) <= xy_local_goal_tolerance_) goal_index = last_valid_index; return goal_index; } } // namespace mir_dwb_critics PLUGINLIB_EXPORT_CLASS(mir_dwb_critics::PathProgressCritic, dwb_local_planner::TrajectoryCritic)