16 #ifndef NAV2_BEHAVIORS__PLUGINS__DRIVE_ON_HEADING_HPP_
17 #define NAV2_BEHAVIORS__PLUGINS__DRIVE_ON_HEADING_HPP_
23 #include "nav2_behaviors/timed_behavior.hpp"
24 #include "nav2_msgs/action/drive_on_heading.hpp"
25 #include "nav2_msgs/action/back_up.hpp"
26 #include "nav2_util/node_utils.hpp"
27 #include "geometry_msgs/msg/twist_stamped.hpp"
29 namespace nav2_behaviors
36 template<
typename ActionT = nav2_msgs::action::DriveOnHeading>
39 using CostmapInfoType = nav2_core::CostmapInfoType;
47 feedback_(std::make_shared<typename ActionT::Feedback>()),
50 simulate_ahead_time_(0.0)
63 if (command->target.y != 0.0 || command->target.z != 0.0) {
66 "DrivingOnHeading in Y and Z not supported, will only move in X.");
67 return ResultStatus{Status::FAILED, ActionT::Result::INVALID_INPUT};
71 if (!((command->target.x > 0.0) == (command->speed > 0.0)) ) {
72 RCLCPP_ERROR(this->logger_,
"Speed and command sign did not match");
73 return ResultStatus{Status::FAILED, ActionT::Result::INVALID_INPUT};
76 command_x_ = command->target.x;
77 command_speed_ = command->speed;
78 command_time_allowance_ = command->time_allowance;
80 end_time_ = this->clock_->now() + command_time_allowance_;
82 if (!nav2_util::getCurrentPose(
83 initial_pose_, *this->tf_, this->local_frame_, this->robot_base_frame_,
84 this->transform_tolerance_))
86 RCLCPP_ERROR(this->logger_,
"Initial robot pose is not available.");
87 return ResultStatus{Status::FAILED, ActionT::Result::TF_ERROR};
90 return ResultStatus{Status::SUCCEEDED, ActionT::Result::NONE};
99 rclcpp::Duration time_remaining = end_time_ - this->clock_->now();
100 if (time_remaining.seconds() < 0.0 && command_time_allowance_.seconds() > 0.0) {
104 "Exceeded time allowance before reaching the DriveOnHeading goal - Exiting DriveOnHeading");
105 return ResultStatus{Status::FAILED, ActionT::Result::TIMEOUT};
108 geometry_msgs::msg::PoseStamped current_pose;
109 if (!nav2_util::getCurrentPose(
110 current_pose, *this->tf_, this->local_frame_, this->robot_base_frame_,
111 this->transform_tolerance_))
113 RCLCPP_ERROR(this->logger_,
"Current robot pose is not available.");
114 return ResultStatus{Status::FAILED, ActionT::Result::TF_ERROR};
117 double diff_x = initial_pose_.pose.position.x - current_pose.pose.position.x;
118 double diff_y = initial_pose_.pose.position.y - current_pose.pose.position.y;
119 double distance = hypot(diff_x, diff_y);
121 feedback_->distance_traveled = distance;
122 this->action_server_->publish_feedback(feedback_);
124 if (distance >= std::fabs(command_x_)) {
126 return ResultStatus{Status::SUCCEEDED, ActionT::Result::NONE};
129 auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>();
130 cmd_vel->header.stamp = this->clock_->now();
131 cmd_vel->header.frame_id = this->robot_base_frame_;
132 cmd_vel->twist.linear.y = 0.0;
133 cmd_vel->twist.angular.z = 0.0;
134 cmd_vel->twist.linear.x = command_speed_;
136 geometry_msgs::msg::Pose2D pose2d;
137 pose2d.x = current_pose.pose.position.x;
138 pose2d.y = current_pose.pose.position.y;
139 pose2d.theta = tf2::getYaw(current_pose.pose.orientation);
143 RCLCPP_WARN(this->logger_,
"Collision Ahead - Exiting DriveOnHeading");
144 return ResultStatus{Status::FAILED, ActionT::Result::COLLISION_AHEAD};
147 this->vel_pub_->publish(std::move(cmd_vel));
149 return ResultStatus{Status::RUNNING, ActionT::Result::NONE};
167 const double & distance,
168 const geometry_msgs::msg::Twist & cmd_vel,
169 geometry_msgs::msg::Pose2D & pose2d)
173 double sim_position_change;
174 const double diff_dist = abs(command_x_) - distance;
175 const int max_cycle_count =
static_cast<int>(this->cycle_frequency_ * simulate_ahead_time_);
176 geometry_msgs::msg::Pose2D init_pose = pose2d;
177 bool fetch_data =
true;
179 while (cycle_count < max_cycle_count) {
180 sim_position_change = cmd_vel.linear.x * (cycle_count / this->cycle_frequency_);
181 pose2d.x = init_pose.x + sim_position_change * cos(init_pose.theta);
182 pose2d.y = init_pose.y + sim_position_change * sin(init_pose.theta);
185 if (diff_dist - abs(sim_position_change) <= 0.) {
189 if (!this->local_collision_checker_->isCollisionFree(pose2d, fetch_data)) {
202 auto node = this->node_.lock();
204 throw std::runtime_error{
"Failed to lock node"};
207 nav2_util::declare_parameter_if_not_declared(
209 "simulate_ahead_time", rclcpp::ParameterValue(2.0));
210 node->get_parameter(
"simulate_ahead_time", simulate_ahead_time_);
213 typename ActionT::Feedback::SharedPtr feedback_;
215 geometry_msgs::msg::PoseStamped initial_pose_;
217 double command_speed_;
218 rclcpp::Duration command_time_allowance_{0, 0};
219 rclcpp::Time end_time_;
220 double simulate_ahead_time_;
An action server Behavior for spinning in.
ResultStatus onCycleUpdate() override
Loop function to run behavior.
void onConfigure() override
Configuration of behavior action.
DriveOnHeading()
A constructor for nav2_behaviors::DriveOnHeading.
bool isCollisionFree(const double &distance, const geometry_msgs::msg::Twist &cmd_vel, geometry_msgs::msg::Pose2D &pose2d)
Check if pose is collision free.
ResultStatus onRun(const std::shared_ptr< const typename ActionT::Goal > command) override
Initialization to run behavior.
CostmapInfoType getResourceInfo() override
Method to determine the required costmap info.