47 #include <std_msgs/Float32.h>
55 return x < 0.0 ? -1.0 : 1.0;
67 visualization_msgs::MarkerArray* viz,
double inflation = 1.0)
72 ROS_DEBUG(
"Path is off costmap (%f,%f)", x, y);
79 ROS_WARN(
"Inflation ratio cannot be less than 1.0");
87 for (
size_t i = 0; i < spec.size(); ++i)
89 spec[i].x *= inflation;
90 spec[i].y *= inflation;
94 std::vector<geometry_msgs::Point> footprint;
98 if (footprint.size() < 4)
111 for (
size_t i = 0; i < footprint.size(); ++i)
113 unsigned x0, y0, x1, y1;
116 ROS_DEBUG(
"Footprint point %lu is off costmap", i);
122 size_t next = (i + 1) % footprint.size();
125 ROS_DEBUG(
"Footprint point %lu is off costmap", next);
135 ROS_DEBUG(
"Collision along path at (%f,%f)", x, y);
149 GracefulControllerROS::~GracefulControllerROS()
155 if (collision_points_)
157 delete collision_points_;
167 global_plan_pub_ = private_nh.advertise<nav_msgs::Path>(
"global_plan", 1);
168 local_plan_pub_ = private_nh.advertise<nav_msgs::Path>(
"local_plan", 1);
169 target_pose_pub_ = private_nh.advertise<geometry_msgs::PoseStamped>(
"target_pose", 1);
172 costmap_ros_ = costmap_ros;
174 planner_util_.initialize(
tf, costmap, costmap_ros_->getGlobalFrameID());
176 bool publish_collision_points =
false;
177 private_nh.getParam(
"publish_collision_points", publish_collision_points);
178 if (publish_collision_points)
181 collision_point_pub_ = private_nh.advertise<visualization_msgs::MarkerArray>(
"collision_points", 1);
184 collision_points_ =
new visualization_msgs::MarkerArray();
187 std::string odom_topic;
188 if (private_nh.getParam(
"odom_topic", odom_topic))
190 odom_helper_.setOdomTopic(odom_topic);
191 private_nh.param(
"acc_dt", acc_dt_, 0.25);
194 bool use_vel_topic =
false;
195 private_nh.getParam(
"use_vel_topic", use_vel_topic);
199 max_vel_sub_ = nh.
subscribe<std_msgs::Float32>(
"max_vel_x", 1,
200 boost::bind(&GracefulControllerROS::velocityCallback,
this, _1));
204 dsrv_ =
new dynamic_reconfigure::Server<GracefulControllerConfig>(private_nh);
205 dynamic_reconfigure::Server<GracefulControllerConfig>::CallbackType cb =
206 boost::bind(&GracefulControllerROS::reconfigureCallback,
this, _1, _2);
207 dsrv_->setCallback(cb);
213 ROS_WARN(
"This planner has already been initialized, doing nothing.");
217 void GracefulControllerROS::reconfigureCallback(GracefulControllerConfig& config, uint32_t level)
220 std::lock_guard<std::mutex> lock(config_mutex_);
240 planner_util_.reconfigureCB(limits,
false);
242 max_vel_x_ = config.max_vel_x;
243 min_vel_x_ = config.min_vel_x;
244 max_vel_theta_ = config.max_vel_theta;
245 min_in_place_vel_theta_ = config.min_in_place_vel_theta;
246 max_x_to_max_theta_scale_factor_ = config.max_x_to_max_theta_scale_factor;
247 acc_lim_x_ = config.acc_lim_x;
248 acc_lim_theta_ = config.acc_lim_theta;
249 decel_lim_x_ = config.decel_lim_x;
250 xy_goal_tolerance_ = config.xy_goal_tolerance;
251 yaw_goal_tolerance_ = config.yaw_goal_tolerance;
252 xy_vel_goal_tolerance_ = config.xy_vel_goal_tolerance;
253 yaw_vel_goal_tolerance_ = config.yaw_vel_goal_tolerance;
254 min_lookahead_ = config.min_lookahead;
255 max_lookahead_ = config.max_lookahead;
256 initial_rotate_tolerance_ = config.initial_rotate_tolerance;
257 prefer_final_rotation_ = config.prefer_final_rotation;
258 compute_orientations_ = config.compute_orientations;
259 use_orientation_filter_ = config.use_orientation_filter;
260 yaw_filter_tolerance_ = config.yaw_filter_tolerance;
261 yaw_gap_tolerance_ = config.yaw_goal_tolerance;
262 latch_xy_goal_tolerance_ = config.latch_xy_goal_tolerance;
263 resolution_ = planner_util_.getCostmap()->getResolution();
265 if (decel_lim_x_ < 0.001)
268 decel_lim_x_ = acc_lim_x_;
271 if (max_x_to_max_theta_scale_factor_ < 0.001)
274 max_x_to_max_theta_scale_factor_ = 100.0;
279 max_vel_theta_limited_ = max_vel_x_ * max_x_to_max_theta_scale_factor_;
280 max_vel_theta_limited_ = std::min(max_vel_theta_limited_, max_vel_theta_);
283 std::make_shared<GracefulController>(config.k1, config.k2, config.min_vel_x, config.max_vel_x, decel_lim_x_,
284 config.max_vel_theta, config.beta, config.lambda);
286 scaling_vel_x_ = std::max(config.scaling_vel_x, config.min_vel_x);
287 scaling_factor_ = config.scaling_factor;
288 scaling_step_ = config.scaling_step;
291 bool GracefulControllerROS::computeVelocityCommands(geometry_msgs::Twist& cmd_vel)
296 "Planner is not initialized, call initialize() before using this "
302 std::lock_guard<std::mutex> lock(config_mutex_);
304 if (!costmap_ros_->getRobotPose(robot_pose_))
306 ROS_ERROR(
"Could not get the robot pose");
310 std::vector<geometry_msgs::PoseStamped> transformed_plan;
311 if (!planner_util_.getLocalPlan(robot_pose_, transformed_plan))
319 if (transformed_plan.empty())
321 ROS_WARN(
"Received an empty transform plan");
326 geometry_msgs::TransformStamped costmap_to_robot;
329 costmap_to_robot = buffer_->lookupTransform(costmap_ros_->getBaseFrameID(), costmap_ros_->getGlobalFrameID(),
333 tf2_transform.
setData(tf2_transform.inverse());
334 robot_to_costmap_transform_ =
tf2::toMsg(tf2_transform);
338 ROS_ERROR(
"Could not transform to %s", costmap_ros_->getBaseFrameID().c_str());
343 geometry_msgs::PoseStamped goal_pose;
344 if (!planner_util_.getGoal(goal_pose))
351 double robot_vel_x = 0.0, robot_vel_yaw = 0.0;
352 bool below_velocity_limits =
true;
353 if (!odom_helper_.getOdomTopic().empty())
357 geometry_msgs::PoseStamped robot_velocity;
358 odom_helper_.getRobotVel(robot_velocity);
359 robot_vel_x = fabs(robot_velocity.pose.position.x);
360 robot_vel_yaw = fabs(
tf2::getYaw(robot_velocity.pose.orientation));
361 if (robot_vel_x > xy_vel_goal_tolerance_ ||
362 robot_vel_yaw > yaw_vel_goal_tolerance_)
365 below_velocity_limits =
false;
370 double dist_to_goal = std::hypot(goal_pose.pose.position.x - robot_pose_.pose.position.x,
371 goal_pose.pose.position.y - robot_pose_.pose.position.y);
374 if ((dist_to_goal < xy_goal_tolerance_ && below_velocity_limits) || goal_tolerance_met_)
377 goal_tolerance_met_ = latch_xy_goal_tolerance_;
380 rotateTowards(
tf2::getYaw(goal_pose.pose.orientation), cmd_vel);
382 double yaw_start =
tf2::getYaw(robot_pose_.pose.orientation);
383 double yaw_end =
tf2::getYaw(goal_pose.pose.orientation);
384 size_t num_steps = fabs(yaw_end - yaw_start) / 0.1;
391 bool collision_free =
true;
392 for (
size_t i = 1; i <= num_steps; ++i)
394 double step =
static_cast<double>(i) /
static_cast<double>(num_steps);
395 double yaw = yaw_start + (
step * (yaw_start - yaw_end));
396 if (
isColliding(robot_pose_.pose.position.x, robot_pose_.pose.position.y, yaw, costmap_ros_, collision_points_))
398 ROS_WARN(
"Unable to rotate in place due to collision.");
399 if (collision_points_)
401 collision_point_pub_.publish(*collision_points_);
403 collision_free =
false;
416 double max_vel_x = max_vel_x_;
417 if (!odom_helper_.getOdomTopic().empty())
419 if (robot_vel_x > max_vel_x)
423 double decelerating_max_vel_x = robot_vel_x - (decel_lim_x_ * acc_dt_);
424 max_vel_x = std::max(max_vel_x, decelerating_max_vel_x);
425 max_vel_x = std::max(max_vel_x, min_vel_x_);
430 max_vel_x = robot_vel_x + (acc_lim_x_ * acc_dt_);
431 max_vel_x = std::min(max_vel_x, max_vel_x_);
432 max_vel_x = std::max(max_vel_x, min_vel_x_);
437 std::vector<geometry_msgs::PoseStamped> target_poses;
438 std::vector<double> target_distances;
439 for (
auto pose : transformed_plan)
442 geometry_msgs::PoseStamped transformed_pose;
444 target_poses.push_back(transformed_pose);
449 for (
int i = transformed_plan.size() - 1; i >= 0; --i)
455 geometry_msgs::PoseStamped target_pose = target_poses[i];
456 double dist_to_target = target_distances[i];
459 if (dist_to_target > max_lookahead_)
464 if (dist_to_goal < max_lookahead_)
466 if (prefer_final_rotation_)
470 double yaw = std::atan2(target_pose.pose.position.y, target_pose.pose.position.x);
471 target_pose.pose.orientation.z = sin(yaw / 2.0);
472 target_pose.pose.orientation.w = cos(yaw / 2.0);
475 else if (dist_to_target < min_lookahead_)
482 double sim_velocity = max_vel_x;
486 controller_->setVelocityLimits(min_vel_x_, sim_velocity, max_vel_theta_limited_);
488 if (simulate(target_pose, cmd_vel))
494 sim_velocity -= scaling_step_;
496 while (sim_velocity >= scaling_vel_x_);
499 ROS_ERROR(
"No pose in path was reachable");
503 bool GracefulControllerROS::simulate(
const geometry_msgs::PoseStamped& target_pose, geometry_msgs::Twist& cmd_vel)
506 std::vector<geometry_msgs::PoseStamped> simulated_path;
508 bool sim_initial_rotation_ = has_new_path_ && initial_rotate_tolerance_ > 0.0;
510 if (collision_points_)
512 collision_points_->markers.resize(0);
518 geometry_msgs::PoseStamped error = target_pose;
521 double error_angle =
tf2::getYaw(error.pose.orientation);
524 if (!simulated_path.empty())
526 double x = error.pose.position.x - simulated_path.back().pose.position.x;
527 double y = error.pose.position.y - simulated_path.back().pose.position.y;
529 double theta = -
tf2::getYaw(simulated_path.back().pose.orientation);
530 error.pose.position.x = x * cos(theta) - y * sin(theta);
531 error.pose.position.y = y * cos(theta) + x * sin(theta);
533 error_angle += theta;
534 error.pose.orientation.z = sin(error_angle / 2.0);
535 error.pose.orientation.w = cos(error_angle / 2.0);
539 double vel_x, vel_th;
540 if (sim_initial_rotation_)
542 geometry_msgs::Twist rotation;
543 if (fabs(rotateTowards(error, rotation)) < initial_rotate_tolerance_)
545 if (simulated_path.empty())
548 ROS_WARN(
"Done rotating towards path");
549 has_new_path_ =
false;
551 sim_initial_rotation_ =
false;
553 vel_x = rotation.linear.x;
554 vel_th = rotation.angular.z;
557 if (!sim_initial_rotation_)
559 if (!controller_->approach(error.pose.position.x, error.pose.position.y, error_angle, vel_x, vel_th))
566 if (simulated_path.empty())
569 cmd_vel.linear.x = vel_x;
570 cmd_vel.angular.z = vel_th;
572 else if (std::hypot(error.pose.position.x, error.pose.position.y) < resolution_)
576 target_pose_pub_.publish(target_pose);
578 if (collision_points_)
580 collision_point_pub_.publish(*collision_points_);
586 geometry_msgs::PoseStamped next_pose;
587 next_pose.header.frame_id = costmap_ros_->getBaseFrameID();
588 if (simulated_path.empty())
591 next_pose.pose.orientation.w = 1.0;
596 next_pose = simulated_path.back();
600 double dt = (vel_x > 0.0) ? resolution_ / vel_x : 0.1;
601 double yaw =
tf2::getYaw(next_pose.pose.orientation);
602 next_pose.pose.position.x += dt * vel_x * cos(yaw);
603 next_pose.pose.position.y += dt * vel_x * sin(yaw);
605 next_pose.pose.orientation.z = sin(yaw / 2.0);
606 next_pose.pose.orientation.w = cos(yaw / 2.0);
607 simulated_path.push_back(next_pose);
610 double footprint_scaling = 1.0;
611 if (vel_x > scaling_vel_x_)
615 double ratio = max_vel_x_ - scaling_vel_x_;
619 ratio = (vel_x - scaling_vel_x_) / ratio;
620 footprint_scaling += ratio * scaling_factor_;
626 if (
isColliding(next_pose.pose.position.x, next_pose.pose.position.y,
tf2::getYaw(next_pose.pose.orientation),
627 costmap_ros_, collision_points_, footprint_scaling))
630 if (collision_points_)
632 collision_point_pub_.publish(*collision_points_);
640 ROS_ERROR(
"Did not reach target_pose, but stopped simulating?");
644 bool GracefulControllerROS::isGoalReached()
649 "Planner is not initialized, call initialize() before using this "
654 if (!costmap_ros_->getRobotPose(robot_pose_))
656 ROS_ERROR(
"Could not get the robot pose");
660 geometry_msgs::PoseStamped goal;
661 if (!planner_util_.getGoal(goal))
667 double dist = std::hypot(goal.pose.position.x - robot_pose_.pose.position.x,
668 goal.pose.position.y - robot_pose_.pose.position.y);
673 double dist_reached = goal_tolerance_met_ || (dist < xy_goal_tolerance_);
675 bool below_velocity_limits =
true;
676 if (!odom_helper_.getOdomTopic().empty())
680 geometry_msgs::PoseStamped robot_velocity;
681 odom_helper_.getRobotVel(robot_velocity);
682 double robot_vel_x = fabs(robot_velocity.pose.position.x);
683 double robot_vel_yaw = fabs(
tf2::getYaw(robot_velocity.pose.orientation));
684 if (robot_vel_x > xy_vel_goal_tolerance_ ||
685 robot_vel_yaw > yaw_vel_goal_tolerance_)
688 below_velocity_limits =
false;
692 return dist_reached && (fabs(angle) < yaw_goal_tolerance_) && below_velocity_limits;
695 bool GracefulControllerROS::setPlan(
const std::vector<geometry_msgs::PoseStamped>& plan)
700 "Planner is not initialized, call initialize() before using this "
706 std::vector<geometry_msgs::PoseStamped> oriented_plan;
707 if (compute_orientations_)
713 oriented_plan = plan;
717 std::vector<geometry_msgs::PoseStamped> filtered_plan;
718 if (use_orientation_filter_)
724 filtered_plan = oriented_plan;
728 if (planner_util_.setPlan(filtered_plan))
731 has_new_path_ =
true;
732 goal_tolerance_met_ =
false;
733 ROS_INFO(
"Recieved a new path with %lu points", filtered_plan.size());
741 double GracefulControllerROS::rotateTowards(
const geometry_msgs::PoseStamped& pose, geometry_msgs::Twist& cmd_vel)
745 if (std::hypot(pose.pose.position.x, pose.pose.position.y) > 0.5)
748 yaw = std::atan2(pose.pose.position.y, pose.pose.position.x);
756 ROS_DEBUG_NAMED(
"graceful_controller",
"Rotating towards goal, error = %f", yaw);
759 rotateTowards(yaw, cmd_vel);
765 void GracefulControllerROS::rotateTowards(
double yaw, geometry_msgs::Twist& cmd_vel)
768 double max_vel_th = max_vel_theta_limited_;
769 if (!odom_helper_.getOdomTopic().empty())
773 geometry_msgs::PoseStamped robot_velocity;
774 odom_helper_.getRobotVel(robot_velocity);
775 double abs_vel = fabs(
tf2::getYaw(robot_velocity.pose.orientation));
776 double acc_limited = abs_vel + (acc_lim_theta_ * acc_dt_);
777 max_vel_th = std::min(max_vel_th, acc_limited);
778 max_vel_th = std::max(max_vel_th, min_in_place_vel_theta_);
781 cmd_vel.linear.x = 0.0;
782 cmd_vel.angular.z = std::sqrt(2 * acc_lim_theta_ * fabs(yaw));
783 cmd_vel.angular.z =
sign(yaw) * std::min(max_vel_th, std::max(min_in_place_vel_theta_, cmd_vel.angular.z));
786 void GracefulControllerROS::velocityCallback(
const std_msgs::Float32::ConstPtr& max_vel_x)
789 std::lock_guard<std::mutex> lock(config_mutex_);
791 max_vel_x_ = std::max(
static_cast<double>(max_vel_x->data), min_vel_x_);
794 max_vel_theta_limited_ = max_vel_x_ * max_x_to_max_theta_scale_factor_;
795 max_vel_theta_limited_ = std::min(max_vel_theta_limited_, max_vel_theta_);
799 std::vector<double>& distances)
801 distances.resize(poses.size());
804 for (
size_t i = 0; i < poses.size(); ++i)
807 distances[i] = std::hypot(poses[i].pose.position.x, poses[i].pose.position.y);
811 auto closest = std::min_element(std::begin(distances), std::end(distances));
815 for (
size_t i = std::distance(std::begin(distances), closest) + 1; i < distances.size(); ++i)
817 distances[i] = distances[i - 1] +
818 std::hypot(poses[i].pose.position.x - poses[i - 1].pose.position.x,
819 poses[i].pose.position.y - poses[i - 1].pose.position.y);