Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
assisted_teleop.cpp
1 // Copyright (c) 2022 Joshua Wallace
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <utility>
16 
17 #include "nav2_behaviors/plugins/assisted_teleop.hpp"
18 #include "nav2_ros_common/node_utils.hpp"
19 #include "nav2_util/geometry_utils.hpp"
20 
21 namespace nav2_behaviors
22 {
23 AssistedTeleop::AssistedTeleop()
24 : TimedBehavior<AssistedTeleopAction>(),
25  feedback_(std::make_shared<AssistedTeleopAction::Feedback>())
26 {}
27 
28 void AssistedTeleop::onConfigure()
29 {
30  auto node = node_.lock();
31  if (!node) {
32  throw std::runtime_error{"Failed to lock node"};
33  }
34 
35  // set up parameters
36  nav2::declare_parameter_if_not_declared(
37  node,
38  "projection_time", rclcpp::ParameterValue(1.0));
39 
40  nav2::declare_parameter_if_not_declared(
41  node,
42  "simulation_time_step", rclcpp::ParameterValue(0.1));
43 
44  nav2::declare_parameter_if_not_declared(
45  node,
46  "cmd_vel_teleop", rclcpp::ParameterValue(std::string("cmd_vel_teleop")));
47 
48  node->get_parameter("projection_time", projection_time_);
49  node->get_parameter("simulation_time_step", simulation_time_step_);
50 
51  std::string cmd_vel_teleop;
52  node->get_parameter("cmd_vel_teleop", cmd_vel_teleop);
53 
54  vel_sub_ = std::make_unique<nav2_util::TwistSubscriber>(
55  node,
56  cmd_vel_teleop,
57  [&](geometry_msgs::msg::Twist::SharedPtr msg) {
58  teleop_twist_.twist = *msg;
59  }, [&](geometry_msgs::msg::TwistStamped::SharedPtr msg) {
60  teleop_twist_ = *msg;
61  });
62 
63  preempt_teleop_sub_ = node->create_subscription<std_msgs::msg::Empty>(
64  "preempt_teleop",
65  std::bind(
66  &AssistedTeleop::preemptTeleopCallback,
67  this, std::placeholders::_1));
68 }
69 
70 ResultStatus AssistedTeleop::onRun(const std::shared_ptr<const AssistedTeleopAction::Goal> command)
71 {
72  preempt_teleop_ = false;
73  command_time_allowance_ = command->time_allowance;
74  end_time_ = this->clock_->now() + command_time_allowance_;
75  return ResultStatus{Status::SUCCEEDED, AssistedTeleopActionResult::NONE, ""};
76 }
77 
78 void AssistedTeleop::onActionCompletion(std::shared_ptr<AssistedTeleopActionResult>/*result*/)
79 {
80  teleop_twist_ = geometry_msgs::msg::TwistStamped();
81  preempt_teleop_ = false;
82 }
83 
84 ResultStatus AssistedTeleop::onCycleUpdate()
85 {
86  feedback_->current_teleop_duration = elapsed_time_;
87  action_server_->publish_feedback(feedback_);
88 
89  rclcpp::Duration time_remaining = end_time_ - this->clock_->now();
90  if (time_remaining.seconds() < 0.0 && command_time_allowance_.seconds() > 0.0) {
91  stopRobot();
92  std::string error_msg = "Exceeded time allowance before reaching the " + behavior_name_ +
93  "goal - Exiting " + behavior_name_;
94  RCLCPP_WARN_STREAM(logger_, error_msg.c_str());
95  return ResultStatus{Status::FAILED, AssistedTeleopActionResult::TIMEOUT, error_msg};
96  }
97 
98  // user states that teleop was successful
99  if (preempt_teleop_) {
100  stopRobot();
101  return ResultStatus{Status::SUCCEEDED, AssistedTeleopActionResult::NONE, ""};
102  }
103 
104  geometry_msgs::msg::PoseStamped current_pose;
105  if (!nav2_util::getCurrentPose(
106  current_pose, *tf_, local_frame_, robot_base_frame_,
107  transform_tolerance_))
108  {
109  std::string error_msg = "Current robot pose is not available for " + behavior_name_;
110  RCLCPP_ERROR_STREAM(logger_, error_msg.c_str());
111  return ResultStatus{Status::FAILED, AssistedTeleopActionResult::TF_ERROR, error_msg};
112  }
113 
114  geometry_msgs::msg::Pose projected_pose = current_pose.pose;
115 
116  auto scaled_twist = std::make_unique<geometry_msgs::msg::TwistStamped>(teleop_twist_);
117  for (double time = simulation_time_step_; time < projection_time_;
118  time += simulation_time_step_)
119  {
120  projected_pose = projectPose(projected_pose, teleop_twist_.twist, simulation_time_step_);
121 
122  if (!local_collision_checker_->isCollisionFree(projected_pose)) {
123  if (time == simulation_time_step_) {
124  RCLCPP_DEBUG_STREAM_THROTTLE(
125  logger_,
126  *clock_,
127  1000,
128  behavior_name_.c_str() << " collided on first time step, setting velocity to zero");
129  scaled_twist->twist.linear.x = 0.0f;
130  scaled_twist->twist.linear.y = 0.0f;
131  scaled_twist->twist.angular.z = 0.0f;
132  break;
133  } else {
134  RCLCPP_DEBUG_STREAM_THROTTLE(
135  logger_,
136  *clock_,
137  1000,
138  behavior_name_.c_str() << " collision approaching in " << time << " seconds");
139  double scale_factor = time / projection_time_;
140  scaled_twist->twist.linear.x *= scale_factor;
141  scaled_twist->twist.linear.y *= scale_factor;
142  scaled_twist->twist.angular.z *= scale_factor;
143  break;
144  }
145  }
146  }
147  vel_pub_->publish(std::move(scaled_twist));
148 
149  return ResultStatus{Status::RUNNING, AssistedTeleopActionResult::NONE, ""};
150 }
151 
152 geometry_msgs::msg::Pose AssistedTeleop::projectPose(
153  const geometry_msgs::msg::Pose & pose,
154  const geometry_msgs::msg::Twist & twist,
155  double projection_time)
156 {
157  geometry_msgs::msg::Pose projected_pose = pose;
158 
159  double theta = tf2::getYaw(pose.orientation);
160 
161  projected_pose.position.x += projection_time * (
162  twist.linear.x * cos(theta) +
163  twist.linear.y * sin(theta));
164 
165  projected_pose.position.y += projection_time * (
166  twist.linear.x * sin(theta) -
167  twist.linear.y * cos(theta));
168 
169  double new_theta = theta + projection_time * twist.angular.z;
170  projected_pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(new_theta);
171 
172  return projected_pose;
173 }
174 
175 void AssistedTeleop::preemptTeleopCallback(const std_msgs::msg::Empty::SharedPtr)
176 {
177  preempt_teleop_ = true;
178 }
179 
180 } // namespace nav2_behaviors
181 
182 #include "pluginlib/class_list_macros.hpp"
An action server behavior for assisted teleop.
Abstract interface for behaviors to adhere to with pluginlib.
Definition: behavior.hpp:42