1515
1616#include < algorithm>
1717#include < string>
18+ #include < limits>
1819#include < memory>
1920#include < utility>
2021
@@ -103,6 +104,8 @@ void RegulatedPurePursuitController::configure(
103104 node, plugin_name_ + " .rotate_to_heading_min_angle" , rclcpp::ParameterValue (0.785 ));
104105 declare_parameter_if_not_declared (
105106 node, plugin_name_ + " .max_angular_accel" , rclcpp::ParameterValue (3.2 ));
107+ declare_parameter_if_not_declared (
108+ node, plugin_name_ + " .allow_reversing" , rclcpp::ParameterValue (false ));
106109
107110 node->get_parameter (plugin_name_ + " .desired_linear_vel" , desired_linear_vel_);
108111 base_desired_linear_vel_ = desired_linear_vel_;
@@ -148,6 +151,7 @@ void RegulatedPurePursuitController::configure(
148151 node->get_parameter (plugin_name_ + " .use_rotate_to_heading" , use_rotate_to_heading_);
149152 node->get_parameter (plugin_name_ + " .rotate_to_heading_min_angle" , rotate_to_heading_min_angle_);
150153 node->get_parameter (plugin_name_ + " .max_angular_accel" , max_angular_accel_);
154+ node->get_parameter (plugin_name_ + " .allow_reversing" , allow_reversing_);
151155 node->get_parameter (" controller_frequency" , control_frequency);
152156
153157 transform_tolerance_ = tf2::durationFromSec (transform_tolerance);
@@ -160,6 +164,16 @@ void RegulatedPurePursuitController::configure(
160164 use_cost_regulated_linear_velocity_scaling_ = false ;
161165 }
162166
167+ /* * Possible to drive in reverse direction if and only if
168+ "use_rotate_to_heading" parameter is set to false **/
169+
170+ if (use_rotate_to_heading_ && allow_reversing_) {
171+ RCLCPP_WARN (
172+ logger_, " Disabling reversing. Both use_rotate_to_heading and allow_reversing "
173+ " parameter cannot be set to true. By default setting use_rotate_to_heading true" );
174+ allow_reversing_ = false ;
175+ }
176+
163177 global_path_pub_ = node->create_publisher <nav_msgs::msg::Path>(" received_global_plan" , 1 );
164178 carrot_pub_ = node->create_publisher <geometry_msgs::msg::PointStamped>(" lookahead_point" , 1 );
165179 carrot_arc_pub_ = node->create_publisher <nav_msgs::msg::Path>(" lookahead_collision_arc" , 1 );
@@ -243,7 +257,19 @@ geometry_msgs::msg::TwistStamped RegulatedPurePursuitController::computeVelocity
243257 auto transformed_plan = transformGlobalPlan (pose);
244258
245259 // Find look ahead distance and point on path and publish
246- const double lookahead_dist = getLookAheadDistance (speed);
260+ double lookahead_dist = getLookAheadDistance (speed);
261+
262+ // Check for reverse driving
263+ if (allow_reversing_) {
264+ // Cusp check
265+ double dist_to_direction_change = findDirectionChange (pose);
266+
267+ // if the lookahead distance is further than the cusp, use the cusp distance instead
268+ if (dist_to_direction_change < lookahead_dist) {
269+ lookahead_dist = dist_to_direction_change;
270+ }
271+ }
272+
247273 auto carrot_pose = getLookAheadPoint (lookahead_dist, transformed_plan);
248274 carrot_pub_->publish (createCarrotMsg (carrot_pose));
249275
@@ -261,6 +287,12 @@ geometry_msgs::msg::TwistStamped RegulatedPurePursuitController::computeVelocity
261287 curvature = 2.0 * carrot_pose.pose .position .y / carrot_dist2;
262288 }
263289
290+ // Setting the velocity direction
291+ double sign = 1.0 ;
292+ if (allow_reversing_) {
293+ sign = carrot_pose.pose .position .x >= 0.0 ? 1.0 : -1.0 ;
294+ }
295+
264296 linear_vel = desired_linear_vel_;
265297
266298 // Make sure we're in compliance with basic constraints
@@ -274,7 +306,7 @@ geometry_msgs::msg::TwistStamped RegulatedPurePursuitController::computeVelocity
274306 applyConstraints (
275307 fabs (lookahead_dist - sqrt (carrot_dist2)),
276308 lookahead_dist, curvature, speed,
277- costAtPose (pose.pose .position .x , pose.pose .position .y ), linear_vel);
309+ costAtPose (pose.pose .position .x , pose.pose .position .y ), linear_vel, sign );
278310
279311 // Apply curvature to angular velocity after constraining linear velocity
280312 angular_vel = linear_vel * curvature;
@@ -427,7 +459,7 @@ double RegulatedPurePursuitController::costAtPose(const double & x, const double
427459void RegulatedPurePursuitController::applyConstraints (
428460 const double & dist_error, const double & lookahead_dist,
429461 const double & curvature, const geometry_msgs::msg::Twist & curr_speed,
430- const double & pose_cost, double & linear_vel)
462+ const double & pose_cost, double & linear_vel, double & sign )
431463{
432464 double curvature_vel = linear_vel;
433465 double cost_vel = linear_vel;
@@ -474,11 +506,13 @@ void RegulatedPurePursuitController::applyConstraints(
474506 }
475507
476508 // Limit linear velocities to be valid and kinematically feasible, v = v0 + a * dt
509+ linear_vel = sign * linear_vel;
477510 double & dt = control_duration_;
478511 const double max_feasible_linear_speed = curr_speed.linear .x + max_linear_accel_ * dt;
479512 const double min_feasible_linear_speed = curr_speed.linear .x - max_linear_decel_ * dt;
480513 linear_vel = std::clamp (linear_vel, min_feasible_linear_speed, max_feasible_linear_speed);
481- linear_vel = std::clamp (linear_vel, 0.0 , desired_linear_vel_);
514+ linear_vel = std::clamp (fabs (linear_vel), 0.0 , desired_linear_vel_);
515+ linear_vel = sign * linear_vel;
482516}
483517
484518void RegulatedPurePursuitController::setPlan (const nav_msgs::msg::Path & path)
@@ -568,6 +602,34 @@ nav_msgs::msg::Path RegulatedPurePursuitController::transformGlobalPlan(
568602 return transformed_plan;
569603}
570604
605+ double RegulatedPurePursuitController::findDirectionChange (
606+ const geometry_msgs::msg::PoseStamped & pose)
607+ {
608+ // Iterating through the global path to determine the position of the cusp
609+ for (unsigned int pose_id = 1 ; pose_id < global_plan_.poses .size (); ++pose_id) {
610+ // We have two vectors for the dot product OA and AB. Determining the vectors.
611+ double oa_x = global_plan_.poses [pose_id].pose .position .x -
612+ global_plan_.poses [pose_id - 1 ].pose .position .x ;
613+ double oa_y = global_plan_.poses [pose_id].pose .position .y -
614+ global_plan_.poses [pose_id - 1 ].pose .position .y ;
615+ double ab_x = global_plan_.poses [pose_id + 1 ].pose .position .x -
616+ global_plan_.poses [pose_id].pose .position .x ;
617+ double ab_y = global_plan_.poses [pose_id + 1 ].pose .position .y -
618+ global_plan_.poses [pose_id].pose .position .y ;
619+
620+ /* Checking for the existance of cusp, in the path, using the dot product
621+ and determine it's distance from the robot. If there is no cusp in the path,
622+ then just determine the distance to the goal location. */
623+ if ( (oa_x * ab_x) + (oa_y * ab_y) < 0.0 ) {
624+ auto x = global_plan_.poses [pose_id].pose .position .x - pose.pose .position .x ;
625+ auto y = global_plan_.poses [pose_id].pose .position .y - pose.pose .position .y ;
626+ return hypot (x, y); // returning the distance if there is a cusp
627+ }
628+ }
629+
630+ return std::numeric_limits<double >::max ();
631+ }
632+
571633bool RegulatedPurePursuitController::transformPose (
572634 const std::string frame,
573635 const geometry_msgs::msg::PoseStamped & in_pose,
0 commit comments