18 #include "angles/angles.h"
19 #include "nav2_core/controller_exceptions.hpp"
20 #include "nav2_util/geometry_utils.hpp"
21 #include "nav2_util/controller_utils.hpp"
22 #include "nav2_graceful_controller/graceful_controller.hpp"
23 #include "nav2_costmap_2d/costmap_filters/filter_values.hpp"
25 namespace nav2_graceful_controller
29 const nav2::LifecycleNode::WeakPtr & parent,
30 std::string name,
const std::shared_ptr<tf2_ros::Buffer> tf,
31 const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
33 nav2::LifecycleNode::SharedPtr node = parent.lock();
38 costmap_ros_ = costmap_ros;
41 logger_ = node->get_logger();
45 param_handler_ = std::make_unique<ParameterHandler>(
46 node, plugin_name_, logger_,
47 costmap_ros_->getCostmap()->getSizeInMetersX());
48 params_ = param_handler_->getParams();
51 path_handler_ = std::make_unique<PathHandler>(
52 params_->transform_tolerance, tf_buffer_, costmap_ros_);
55 control_law_ = std::make_unique<SmoothControlLaw>(
56 params_->k_phi, params_->k_delta, params_->beta, params_->lambda, params_->slowdown_radius,
57 params_->v_linear_min, params_->v_linear_max, params_->v_angular_max);
60 if(params_->use_collision_detection) {
61 collision_checker_ = std::make_unique<nav2_costmap_2d::
62 FootprintCollisionChecker<nav2_costmap_2d::Costmap2D *>>(costmap_ros_->getCostmap());
66 transformed_plan_pub_ = node->create_publisher<nav_msgs::msg::Path>(
"transformed_global_plan");
67 local_plan_pub_ = node->create_publisher<nav_msgs::msg::Path>(
"local_plan");
68 motion_target_pub_ = node->create_publisher<geometry_msgs::msg::PoseStamped>(
"motion_target");
69 slowdown_pub_ = node->create_publisher<visualization_msgs::msg::Marker>(
"slowdown");
71 RCLCPP_INFO(logger_,
"Configured Graceful Motion Controller: %s", plugin_name_.c_str());
78 "Cleaning up controller: %s of type graceful_controller::GracefulController",
79 plugin_name_.c_str());
80 transformed_plan_pub_.reset();
81 local_plan_pub_.reset();
82 motion_target_pub_.reset();
83 slowdown_pub_.reset();
84 collision_checker_.reset();
85 path_handler_.reset();
86 param_handler_.reset();
94 "Activating controller: %s of type nav2_graceful_controller::GracefulController",
95 plugin_name_.c_str());
96 transformed_plan_pub_->on_activate();
97 local_plan_pub_->on_activate();
98 motion_target_pub_->on_activate();
99 slowdown_pub_->on_activate();
100 param_handler_->activate();
107 "Deactivating controller: %s of type nav2_graceful_controller::GracefulController",
108 plugin_name_.c_str());
109 transformed_plan_pub_->on_deactivate();
110 local_plan_pub_->on_deactivate();
111 motion_target_pub_->on_deactivate();
112 slowdown_pub_->on_deactivate();
113 param_handler_->deactivate();
117 const geometry_msgs::msg::PoseStamped & pose,
118 const geometry_msgs::msg::Twist & ,
121 std::lock_guard<std::mutex> param_lock(param_handler_->getMutex());
123 geometry_msgs::msg::TwistStamped cmd_vel;
124 cmd_vel.header = pose.header;
127 geometry_msgs::msg::Pose pose_tolerance;
128 geometry_msgs::msg::Twist velocity_tolerance;
129 if (!goal_checker->
getTolerances(pose_tolerance, velocity_tolerance)) {
130 RCLCPP_WARN(logger_,
"Unable to retrieve goal checker's tolerances!");
132 goal_dist_tolerance_ = pose_tolerance.position.x;
136 control_law_->setCurvatureConstants(
137 params_->k_phi, params_->k_delta, params_->beta, params_->lambda);
138 control_law_->setSlowdownRadius(params_->slowdown_radius);
139 control_law_->setSpeedLimit(params_->v_linear_min, params_->v_linear_max, params_->v_angular_max);
142 auto transformed_plan = path_handler_->transformGlobalPlan(
143 pose, params_->max_robot_pose_search_dist);
149 transformed_plan_pub_->publish(transformed_plan);
152 geometry_msgs::msg::TransformStamped costmap_transform;
154 costmap_transform = tf_buffer_->lookupTransform(
155 costmap_ros_->getGlobalFrameID(), costmap_ros_->getBaseFrameID(),
157 }
catch (tf2::TransformException & ex) {
159 logger_,
"Could not transform %s to %s: %s",
160 costmap_ros_->getBaseFrameID().c_str(), costmap_ros_->getGlobalFrameID().c_str(),
166 double dist_to_goal = nav2_util::geometry_utils::calculate_path_length(transformed_plan);
169 if (dist_to_goal < goal_dist_tolerance_ || goal_reached_) {
170 goal_reached_ =
true;
171 double angle_to_goal = tf2::getYaw(transformed_plan.poses.back().pose.orientation);
173 size_t num_steps = fabs(angle_to_goal) / params_->in_place_collision_resolution;
175 num_steps = std::max(
static_cast<size_t>(1), num_steps);
176 bool collision_free =
true;
177 for (
size_t i = 1; i <= num_steps; ++i) {
178 double step =
static_cast<double>(i) /
static_cast<double>(num_steps);
179 double yaw = step * angle_to_goal;
180 geometry_msgs::msg::PoseStamped next_pose;
181 next_pose.header.frame_id = costmap_ros_->getBaseFrameID();
182 next_pose.pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(yaw);
183 geometry_msgs::msg::PoseStamped costmap_pose;
184 tf2::doTransform(next_pose, costmap_pose, costmap_transform);
185 if (params_->use_collision_detection &&
inCollision(
186 costmap_pose.pose.position.x, costmap_pose.pose.position.y,
187 tf2::getYaw(costmap_pose.pose.orientation)))
189 collision_free =
false;
194 if (collision_free) {
202 nav_msgs::msg::Path local_plan;
203 geometry_msgs::msg::PoseStamped target_pose;
205 double dist_to_target;
206 std::vector<double> target_distances;
209 bool is_first_iteration =
true;
210 for (
int i = transformed_plan.poses.size() - 1; i >= 0; --i) {
211 if (is_first_iteration) {
214 dist_to_target = params_->max_lookahead;
218 target_pose = nav2_util::getLookAheadPoint(dist_to_target, transformed_plan,
false);
219 is_first_iteration =
false;
225 dist_to_target = target_distances[i];
226 target_pose = transformed_plan.poses[i];
231 target_pose, dist_to_target, dist_to_goal, local_plan, costmap_transform, cmd_vel))
234 motion_target_pub_->publish(target_pose);
236 auto slowdown_marker = nav2_graceful_controller::createSlowdownMarker(
237 target_pose, params_->slowdown_radius);
238 slowdown_pub_->publish(slowdown_marker);
240 local_plan.header = transformed_plan.header;
241 local_plan_pub_->publish(local_plan);
252 path_handler_->setPlan(path);
253 goal_reached_ =
false;
254 do_initial_rotation_ =
true;
258 const double & speed_limit,
const bool & percentage)
260 std::lock_guard<std::mutex> param_lock(param_handler_->getMutex());
262 if (speed_limit == nav2_costmap_2d::NO_SPEED_LIMIT) {
263 params_->v_linear_max = params_->v_linear_max_initial;
264 params_->v_angular_max = params_->v_angular_max_initial;
268 params_->v_linear_max = std::max(
269 params_->v_linear_max_initial * speed_limit / 100.0, params_->v_linear_min);
270 params_->v_angular_max = params_->v_angular_max_initial * speed_limit / 100.0;
273 params_->v_linear_max = std::max(speed_limit, params_->v_linear_min);
275 params_->v_angular_max = params_->v_angular_max_initial *
276 speed_limit / params_->v_linear_max_initial;
282 geometry_msgs::msg::PoseStamped & target_pose,
283 double dist_to_target,
285 nav_msgs::msg::Path & trajectory,
286 geometry_msgs::msg::TransformStamped & costmap_transform,
287 geometry_msgs::msg::TwistStamped & cmd_vel)
290 if (dist_to_target > params_->max_lookahead) {
294 if (dist_to_goal < params_->max_lookahead) {
295 if (params_->prefer_final_rotation) {
298 double yaw = std::atan2(target_pose.pose.position.y, target_pose.pose.position.x);
299 target_pose.pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(yaw);
301 }
else if (dist_to_target < params_->min_lookahead) {
307 bool reversing =
false;
308 if (params_->allow_backward && target_pose.pose.position.x < 0.0) {
310 target_pose.pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(
311 tf2::getYaw(target_pose.pose.orientation) + M_PI);
315 if (
simulateTrajectory(target_pose, costmap_transform, trajectory, cmd_vel, reversing)) {
325 const geometry_msgs::msg::PoseStamped & motion_target,
326 const geometry_msgs::msg::TransformStamped & costmap_transform,
327 nav_msgs::msg::Path & trajectory,
328 geometry_msgs::msg::TwistStamped & cmd_vel,
331 trajectory.poses.clear();
334 geometry_msgs::msg::PoseStamped next_pose;
335 next_pose.header.frame_id = costmap_ros_->getBaseFrameID();
336 next_pose.pose.orientation.w = 1.0;
339 bool sim_initial_rotation = do_initial_rotation_ && params_->initial_rotation;
340 double angle_to_target =
341 std::atan2(motion_target.pose.position.y, motion_target.pose.position.x);
342 if (fabs(angle_to_target) < params_->initial_rotation_tolerance) {
343 sim_initial_rotation =
false;
344 do_initial_rotation_ =
false;
347 double distance = std::numeric_limits<double>::max();
348 double resolution_ = costmap_ros_->getCostmap()->getResolution();
349 double dt = (params_->v_linear_max > 0.0) ? resolution_ / params_->v_linear_max : 0.0;
352 unsigned int max_iter = 3 *
353 std::hypot(motion_target.pose.position.x, motion_target.pose.position.y) / resolution_;
357 if (sim_initial_rotation) {
359 double next_pose_yaw = tf2::getYaw(next_pose.pose.orientation);
363 if (trajectory.poses.empty()) {cmd_vel.twist = cmd;}
366 if (fabs(angle_to_target - next_pose_yaw) < params_->initial_rotation_tolerance) {
367 sim_initial_rotation =
false;
371 next_pose_yaw += cmd_vel.twist.angular.z * dt;
372 next_pose.pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(next_pose_yaw);
375 if (trajectory.poses.empty()) {
376 cmd_vel.twist = control_law_->calculateRegularVelocity(
377 motion_target.pose, next_pose.pose, backward);
381 next_pose.pose = control_law_->calculateNextPose(
382 dt, motion_target.pose, next_pose.pose, backward);
386 trajectory.poses.push_back(next_pose);
389 geometry_msgs::msg::PoseStamped global_pose;
390 tf2::doTransform(next_pose, global_pose, costmap_transform);
391 if (params_->use_collision_detection &&
inCollision(
392 global_pose.pose.position.x, global_pose.pose.position.y,
393 tf2::getYaw(global_pose.pose.orientation)))
399 distance = nav2_util::geometry_utils::euclidean_distance(motion_target.pose, next_pose.pose);
400 }
while(distance > resolution_ && trajectory.poses.size() < max_iter);
407 geometry_msgs::msg::Twist vel;
409 vel.angular.z = params_->rotation_scaling_factor * angle_to_target * params_->v_angular_max;
410 vel.angular.z = std::copysign(1.0, vel.angular.z) * std::max(abs(vel.angular.z),
411 params_->v_angular_min_in_place);
418 if (!costmap_ros_->getCostmap()->worldToMap(x, y, mx, my)) {
420 logger_,
"The path is not in the costmap. Cannot check for collisions. "
421 "Proceed at your own risk, slow the robot, or increase your costmap size.");
427 bool is_tracking_unknown =
428 costmap_ros_->getLayeredCostmap()->isTrackingUnknown();
429 bool consider_footprint = !costmap_ros_->getUseRadius();
431 double footprint_cost;
432 if (consider_footprint) {
433 footprint_cost = collision_checker_->footprintCostAtPose(
434 x, y, theta, costmap_ros_->getRobotFootprint());
436 footprint_cost = collision_checker_->pointCost(mx, my);
439 switch (
static_cast<unsigned char>(footprint_cost)) {
440 case (nav2_costmap_2d::LETHAL_OBSTACLE):
442 case (nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE):
443 return consider_footprint ? false :
true;
444 case (nav2_costmap_2d::NO_INFORMATION):
445 return is_tracking_unknown ? false :
true;
452 const std::vector<geometry_msgs::msg::PoseStamped> & poses,
453 std::vector<double> & distances)
455 distances.resize(poses.size());
457 double d = std::hypot(poses[0].pose.position.x, poses[0].pose.position.y);
460 for (
size_t i = 1; i < poses.size(); ++i) {
461 d += nav2_util::geometry_utils::euclidean_distance(poses[i - 1].pose, poses[i].pose);
467 std::vector<geometry_msgs::msg::PoseStamped> & path)
471 if (path.size() < 3) {
return;}
474 double initial_yaw = tf2::getYaw(path[1].pose.orientation);
475 for (
size_t i = 2; i < path.size() - 1; ++i) {
476 double this_yaw = tf2::getYaw(path[i].pose.orientation);
477 if (angles::shortest_angular_distance(this_yaw, initial_yaw) > 1e-6) {
return;}
482 for (
size_t i = 0; i < path.size() - 1; ++i) {
484 double dx = path[i + 1].pose.position.x - path[i].pose.position.x;
485 double dy = path[i + 1].pose.position.y - path[i].pose.position.y;
486 double yaw = std::atan2(dy, dx);
487 path[i].pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(yaw);
494 PLUGINLIB_EXPORT_CLASS(
controller interface that acts as a virtual base class for all controller plugins
Function-object for checking whether a goal has been reached.
virtual bool getTolerances(geometry_msgs::msg::Pose &pose_tolerance, geometry_msgs::msg::Twist &vel_tolerance)=0
Get the maximum possible tolerances used for goal checking in the major types. Any field without a va...
Graceful controller plugin.
void activate() override
Activate controller state machine.
void setPlan(const nav_msgs::msg::Path &path) override
nav2_core setPlan - Sets the global plan.
void computeDistanceAlongPath(const std::vector< geometry_msgs::msg::PoseStamped > &poses, std::vector< double > &distances)
Compute the distance to each pose in a path.
void deactivate() override
Deactivate controller state machine.
bool simulateTrajectory(const geometry_msgs::msg::PoseStamped &motion_target, const geometry_msgs::msg::TransformStamped &costmap_transform, nav_msgs::msg::Path &trajectory, geometry_msgs::msg::TwistStamped &cmd_vel, bool backward)
Simulate trajectory calculating in every step the new velocity command based on a new curvature value...
void cleanup() override
Cleanup controller state machine.
geometry_msgs::msg::Twist rotateToTarget(double angle_to_target)
Rotate the robot to face the motion target with maximum angular velocity.
void setSpeedLimit(const double &speed_limit, const bool &percentage) override
Limits the maximum linear speed of the robot.
void configure(const nav2::LifecycleNode::WeakPtr &parent, std::string name, std::shared_ptr< tf2_ros::Buffer > tf, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros) override
Configure controller state machine.
geometry_msgs::msg::TwistStamped computeVelocityCommands(const geometry_msgs::msg::PoseStamped &pose, const geometry_msgs::msg::Twist &velocity, nav2_core::GoalChecker *goal_checker) override
Compute the best command given the current pose and velocity.
bool inCollision(const double &x, const double &y, const double &theta)
Checks if the robot is in collision.
void validateOrientations(std::vector< geometry_msgs::msg::PoseStamped > &path)
Control law requires proper orientations, not all planners provide them.
bool validateTargetPose(geometry_msgs::msg::PoseStamped &target_pose, double dist_to_target, double dist_to_goal, nav_msgs::msg::Path &trajectory, geometry_msgs::msg::TransformStamped &costmap_transform, geometry_msgs::msg::TwistStamped &cmd_vel)
Validate a given target pose for calculating command velocity.