16 #ifndef NAV2_BEHAVIORS__PLUGINS__DRIVE_ON_HEADING_HPP_
17 #define NAV2_BEHAVIORS__PLUGINS__DRIVE_ON_HEADING_HPP_
24 #include "nav2_behaviors/timed_behavior.hpp"
25 #include "nav2_msgs/action/drive_on_heading.hpp"
26 #include "nav2_msgs/action/back_up.hpp"
27 #include "nav2_util/node_utils.hpp"
29 namespace nav2_behaviors
36 template<
typename ActionT = nav2_msgs::action::DriveOnHeading>
45 feedback_(std::make_shared<typename ActionT::Feedback>()),
48 simulate_ahead_time_(0.0)
59 Status
onRun(
const std::shared_ptr<const typename ActionT::Goal> command)
override
61 if (command->target.y != 0.0 || command->target.z != 0.0) {
64 "DrivingOnHeading in Y and Z not supported, will only move in X.");
65 return Status::FAILED;
69 if (!((command->target.x > 0.0) == (command->speed > 0.0)) ) {
70 RCLCPP_ERROR(this->logger_,
"Speed and command sign did not match");
71 return Status::FAILED;
74 command_x_ = command->target.x;
75 command_speed_ = command->speed;
76 command_time_allowance_ = command->time_allowance;
78 end_time_ = this->clock_->now() + command_time_allowance_;
80 if (!nav2_util::getCurrentPose(
81 initial_pose_, *this->tf_, this->global_frame_, this->robot_base_frame_,
82 this->transform_tolerance_))
84 RCLCPP_ERROR(this->logger_,
"Initial robot pose is not available.");
85 return Status::FAILED;
88 return Status::SUCCEEDED;
97 rclcpp::Duration time_remaining = end_time_ - this->clock_->now();
98 if (time_remaining.seconds() < 0.0 && command_time_allowance_.seconds() > 0.0) {
102 "Exceeded time allowance before reaching the DriveOnHeading goal - Exiting DriveOnHeading");
103 return Status::FAILED;
106 geometry_msgs::msg::PoseStamped current_pose;
107 if (!nav2_util::getCurrentPose(
108 current_pose, *this->tf_, this->global_frame_, this->robot_base_frame_,
109 this->transform_tolerance_))
111 RCLCPP_ERROR(this->logger_,
"Current robot pose is not available.");
112 return Status::FAILED;
115 double diff_x = initial_pose_.pose.position.x - current_pose.pose.position.x;
116 double diff_y = initial_pose_.pose.position.y - current_pose.pose.position.y;
117 double distance = hypot(diff_x, diff_y);
119 feedback_->distance_traveled = distance;
120 this->action_server_->publish_feedback(feedback_);
122 if (distance >= std::fabs(command_x_)) {
124 return Status::SUCCEEDED;
127 auto cmd_vel = std::make_unique<geometry_msgs::msg::Twist>();
128 cmd_vel->linear.y = 0.0;
129 cmd_vel->angular.z = 0.0;
131 bool forward = command_speed_ > 0.0;
132 if (acceleration_limit_ == 0.0 || deceleration_limit_ == 0.0) {
133 RCLCPP_INFO_ONCE(this->logger_,
"DriveOnHeading: no acceleration or deceleration limits set");
134 cmd_vel->linear.x = command_speed_;
136 double current_speed = last_vel_ == std::numeric_limits<double>::max() ? 0.0 : last_vel_;
137 double min_feasible_speed, max_feasible_speed;
139 min_feasible_speed = current_speed + deceleration_limit_ / this->cycle_frequency_;
140 max_feasible_speed = current_speed + acceleration_limit_ / this->cycle_frequency_;
142 min_feasible_speed = current_speed - acceleration_limit_ / this->cycle_frequency_;
143 max_feasible_speed = current_speed - deceleration_limit_ / this->cycle_frequency_;
145 cmd_vel->linear.x = std::clamp(command_speed_, min_feasible_speed, max_feasible_speed);
148 auto remaining_distance = std::fabs(command_x_) - distance;
149 double max_vel_to_stop = std::sqrt(-2.0 * deceleration_limit_ * remaining_distance);
150 if (max_vel_to_stop < std::abs(cmd_vel->linear.x)) {
151 cmd_vel->linear.x = forward ? max_vel_to_stop : -max_vel_to_stop;
156 if (std::fabs(cmd_vel->linear.x) < minimum_speed_) {
157 cmd_vel->linear.x = forward ? minimum_speed_ : -minimum_speed_;
160 geometry_msgs::msg::Pose2D pose2d;
161 pose2d.x = current_pose.pose.position.x;
162 pose2d.y = current_pose.pose.position.y;
163 pose2d.theta = tf2::getYaw(current_pose.pose.orientation);
167 RCLCPP_WARN(this->logger_,
"Collision Ahead - Exiting DriveOnHeading");
168 return Status::FAILED;
171 last_vel_ = cmd_vel->linear.x;
172 this->vel_pub_->publish(std::move(cmd_vel));
174 return Status::RUNNING;
177 void onCleanup()
override {last_vel_ = std::numeric_limits<double>::max();}
179 void onActionCompletion()
override
181 last_vel_ = std::numeric_limits<double>::max();
193 const double & distance,
194 geometry_msgs::msg::Twist * cmd_vel,
195 geometry_msgs::msg::Pose2D & pose2d)
199 double sim_position_change;
200 const double diff_dist = abs(command_x_) - distance;
201 const int max_cycle_count =
static_cast<int>(this->cycle_frequency_ * simulate_ahead_time_);
202 geometry_msgs::msg::Pose2D init_pose = pose2d;
203 bool fetch_data =
true;
205 while (cycle_count < max_cycle_count) {
206 sim_position_change = cmd_vel->linear.x * (cycle_count / this->cycle_frequency_);
207 pose2d.x = init_pose.x + sim_position_change * cos(init_pose.theta);
208 pose2d.y = init_pose.y + sim_position_change * sin(init_pose.theta);
211 if (diff_dist - abs(sim_position_change) <= 0.) {
215 if (!this->collision_checker_->isCollisionFree(pose2d, fetch_data)) {
228 auto node = this->node_.lock();
230 throw std::runtime_error{
"Failed to lock node"};
233 nav2_util::declare_parameter_if_not_declared(
235 "simulate_ahead_time", rclcpp::ParameterValue(2.0));
236 node->get_parameter(
"simulate_ahead_time", simulate_ahead_time_);
238 nav2_util::declare_parameter_if_not_declared(
239 node, this->behavior_name_ +
".acceleration_limit",
240 rclcpp::ParameterValue(0.0));
241 nav2_util::declare_parameter_if_not_declared(
242 node, this->behavior_name_ +
".deceleration_limit",
243 rclcpp::ParameterValue(0.0));
244 nav2_util::declare_parameter_if_not_declared(
245 node, this->behavior_name_ +
".minimum_speed",
246 rclcpp::ParameterValue(0.0));
247 node->get_parameter(this->behavior_name_ +
".acceleration_limit", acceleration_limit_);
248 node->get_parameter(this->behavior_name_ +
".deceleration_limit", deceleration_limit_);
249 node->get_parameter(this->behavior_name_ +
".minimum_speed", minimum_speed_);
250 if (acceleration_limit_ < 0.0 || deceleration_limit_ > 0.0) {
253 "DriveOnHeading: acceleration_limit and deceleration_limit must be "
254 "positive and negative respectively");
255 acceleration_limit_ = std::abs(acceleration_limit_);
256 deceleration_limit_ = -std::abs(deceleration_limit_);
260 typename ActionT::Feedback::SharedPtr feedback_;
262 geometry_msgs::msg::PoseStamped initial_pose_;
264 double command_speed_;
265 rclcpp::Duration command_time_allowance_{0, 0};
266 rclcpp::Time end_time_;
267 double simulate_ahead_time_;
268 double acceleration_limit_;
269 double deceleration_limit_;
270 double minimum_speed_;
271 double last_vel_ = std::numeric_limits<double>::max();
An action server Behavior for spinning in.
void onConfigure() override
Configuration of behavior action.
DriveOnHeading()
A constructor for nav2_behaviors::DriveOnHeading.
Status onCycleUpdate() override
Loop function to run behavior.
Status onRun(const std::shared_ptr< const typename ActionT::Goal > command) override
Initialization to run behavior.
bool isCollisionFree(const double &distance, geometry_msgs::msg::Twist *cmd_vel, geometry_msgs::msg::Pose2D &pose2d)
Check if pose is collision free.