21 #include "nav2_behaviors/plugins/spin.hpp"
22 #include "nav2_ros_common/node_utils.hpp"
23 #include "nav2_util/geometry_utils.hpp"
25 using namespace std::chrono_literals;
27 namespace nav2_behaviors
32 feedback_(std::make_shared<SpinAction::Feedback>()),
33 min_rotational_vel_(0.0),
34 max_rotational_vel_(0.0),
35 rotational_acc_lim_(0.0),
39 simulate_ahead_time_(0.0)
43 Spin::~Spin() =
default;
47 auto node = node_.lock();
49 throw std::runtime_error{
"Failed to lock node"};
52 simulate_ahead_time_ = node->declare_or_get_parameter(
"simulate_ahead_time", 2.0);
53 max_rotational_vel_ = node->declare_or_get_parameter(
"max_rotational_vel", 1.0);
54 min_rotational_vel_ = node->declare_or_get_parameter(
"min_rotational_vel", 0.4);
55 rotational_acc_lim_ = node->declare_or_get_parameter(
"rotational_acc_lim", 3.2);
60 geometry_msgs::msg::PoseStamped current_pose;
61 if (!nav2_util::getCurrentPose(
62 current_pose, *tf_, local_frame_, robot_base_frame_,
63 transform_tolerance_))
65 std::string error_msg =
"Current robot pose is not available.";
66 RCLCPP_ERROR(logger_,
"%s", error_msg.c_str());
67 return ResultStatus{Status::FAILED, SpinActionResult::TF_ERROR, error_msg};
70 prev_yaw_ = tf2::getYaw(current_pose.pose.orientation);
73 cmd_yaw_ = command->target_yaw;
75 logger_,
"Turning %0.2f for spin behavior.",
78 command_time_allowance_ = command->time_allowance;
79 cmd_disable_collision_checks_ = command->disable_collision_checks;
80 end_time_ = this->clock_->now() + command_time_allowance_;
82 return ResultStatus{Status::SUCCEEDED, SpinActionResult::NONE,
""};
87 rclcpp::Duration time_remaining = end_time_ - this->clock_->now();
88 if (time_remaining.seconds() < 0.0 && command_time_allowance_.seconds() > 0.0) {
89 std::string error_msg =
"Exceeded time allowance before reaching the Spin goal - Exiting Spin";
90 RCLCPP_WARN(logger_,
"%s", error_msg.c_str());
91 return ResultStatus{Status::FAILED, SpinActionResult::TIMEOUT, error_msg};
94 geometry_msgs::msg::PoseStamped current_pose;
95 if (!nav2_util::getCurrentPose(
96 current_pose, *tf_, local_frame_, robot_base_frame_,
97 transform_tolerance_))
99 std::string error_msg =
"Current robot pose is not available.";
100 RCLCPP_ERROR(logger_,
"%s", error_msg.c_str());
101 return ResultStatus{Status::FAILED, SpinActionResult::TF_ERROR, error_msg};
104 const double current_yaw = tf2::getYaw(current_pose.pose.orientation);
106 double delta_yaw = current_yaw - prev_yaw_;
107 if (abs(delta_yaw) > M_PI) {
108 delta_yaw = copysign(2 * M_PI - abs(delta_yaw), prev_yaw_);
111 relative_yaw_ += delta_yaw;
112 prev_yaw_ = current_yaw;
114 feedback_->angular_distance_traveled =
static_cast<float>(relative_yaw_);
115 action_server_->publish_feedback(feedback_);
117 double remaining_yaw = abs(cmd_yaw_) - abs(relative_yaw_);
118 if (remaining_yaw < 1e-6) {
120 return ResultStatus{Status::SUCCEEDED, SpinActionResult::NONE,
""};
123 double vel = sqrt(2 * rotational_acc_lim_ * remaining_yaw);
124 vel = std::min(std::max(vel, min_rotational_vel_), max_rotational_vel_);
126 auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>();
127 cmd_vel->header.frame_id = robot_base_frame_;
128 cmd_vel->header.stamp = clock_->now();
129 cmd_vel->twist.angular.z = copysign(vel, cmd_yaw_);
131 geometry_msgs::msg::Pose pose = current_pose.pose;
134 std::string error_msg =
"Collision Ahead - Exiting Spin";
135 RCLCPP_WARN(logger_,
"%s", error_msg.c_str());
136 return ResultStatus{Status::FAILED, SpinActionResult::COLLISION_AHEAD, error_msg};
139 vel_pub_->publish(std::move(cmd_vel));
141 return ResultStatus{Status::RUNNING, SpinActionResult::NONE,
""};
145 const double & relative_yaw,
146 const geometry_msgs::msg::Twist & cmd_vel,
147 geometry_msgs::msg::Pose & pose)
149 if (cmd_disable_collision_checks_) {
155 double sim_position_change;
156 const int max_cycle_count =
static_cast<int>(cycle_frequency_ * simulate_ahead_time_);
157 geometry_msgs::msg::Pose init_pose = pose;
158 double init_theta = tf2::getYaw(init_pose.orientation);
159 bool fetch_data =
true;
161 while (cycle_count < max_cycle_count) {
162 sim_position_change = cmd_vel.angular.z * (cycle_count / cycle_frequency_);
163 double new_theta = init_theta + sim_position_change;
164 pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(new_theta);
167 if (abs(relative_yaw) - abs(sim_position_change) <= 0.0) {
171 if (!local_collision_checker_->isCollisionFree(pose, fetch_data)) {
181 #include "pluginlib/class_list_macros.hpp"
An action server behavior for spinning in.
ResultStatus onRun(const std::shared_ptr< const SpinActionGoal > command) override
Initialization to run behavior.
ResultStatus onCycleUpdate() override
Loop function to run behavior.
void onConfigure() override
Configuration of behavior action.
bool isCollisionFree(const double &distance, const geometry_msgs::msg::Twist &cmd_vel, geometry_msgs::msg::Pose &pose)
Check if pose is collision free.
Abstract interface for behaviors to adhere to with pluginlib.