17 #include "nav2_behaviors/plugins/assisted_teleop.hpp"
18 #include "nav2_ros_common/node_utils.hpp"
19 #include "nav2_util/geometry_utils.hpp"
21 namespace nav2_behaviors
23 AssistedTeleop::AssistedTeleop()
24 : TimedBehavior<AssistedTeleopAction>(),
25 feedback_(std::make_shared<AssistedTeleopAction::Feedback>())
28 void AssistedTeleop::onConfigure()
30 auto node = node_.lock();
32 throw std::runtime_error{
"Failed to lock node"};
36 projection_time_ = node->declare_or_get_parameter(
37 behavior_name_ +
".projection_time", 1.0);
38 simulation_time_step_ = node->declare_or_get_parameter(
39 behavior_name_ +
".simulation_time_step", 0.1);
40 teleop_command_timeout_ = node->declare_or_get_parameter(
41 behavior_name_ +
".teleop_command_timeout", 0.25);
42 std::string cmd_vel_teleop = node->declare_or_get_parameter(
43 behavior_name_ +
".cmd_vel_teleop", std::string(
"cmd_vel_teleop"));
45 vel_sub_ = std::make_unique<nav2_util::TwistSubscriber>(
48 [&](
const geometry_msgs::msg::Twist::ConstSharedPtr & msg) {
49 teleop_twist_.twist = *msg;
50 teleop_twist_.header.stamp = clock_->now();
51 received_first_command_ =
true;
52 }, [&](
const geometry_msgs::msg::TwistStamped::ConstSharedPtr & msg) {
54 received_first_command_ =
true;
57 preempt_teleop_sub_ = node->create_subscription<std_msgs::msg::Empty>(
60 &AssistedTeleop::preemptTeleopCallback,
61 this, std::placeholders::_1));
64 ResultStatus AssistedTeleop::onRun(
const std::shared_ptr<const AssistedTeleopAction::Goal> command)
66 preempt_teleop_ =
false;
67 received_first_command_ =
false;
68 command_time_allowance_ = command->time_allowance;
69 end_time_ = this->clock_->now() + command_time_allowance_;
70 return ResultStatus{Status::SUCCEEDED, AssistedTeleopActionResult::NONE,
""};
73 void AssistedTeleop::onActionCompletion(std::shared_ptr<AssistedTeleopActionResult>)
75 teleop_twist_ = geometry_msgs::msg::TwistStamped();
76 received_first_command_ =
false;
77 preempt_teleop_ =
false;
82 feedback_->current_teleop_duration = elapsed_time_;
83 action_server_->publish_feedback(feedback_);
85 rclcpp::Duration time_remaining = end_time_ - this->clock_->now();
86 if (time_remaining.seconds() < 0.0 && command_time_allowance_.seconds() > 0.0) {
88 std::string error_msg =
"Exceeded time allowance before reaching the " + behavior_name_ +
89 "goal - Exiting " + behavior_name_;
90 RCLCPP_WARN_STREAM(logger_, error_msg.c_str());
91 return ResultStatus{Status::FAILED, AssistedTeleopActionResult::TIMEOUT, error_msg};
95 if (preempt_teleop_) {
97 return ResultStatus{Status::SUCCEEDED, AssistedTeleopActionResult::NONE,
""};
101 if (isTeleopCommandStale(this->clock_->now())) {
103 std::string error_msg =
"No teleop command received within teleop_command_timeout (" +
104 std::to_string(teleop_command_timeout_) +
" s) - Exiting " + behavior_name_;
105 RCLCPP_WARN_STREAM(logger_, error_msg.c_str());
106 return ResultStatus{Status::FAILED, AssistedTeleopActionResult::TELEOP_INPUT_TIMEOUT,
110 geometry_msgs::msg::PoseStamped current_pose;
111 if (!getCurrentPoseChecked(current_pose)) {
113 std::string error_msg =
"Current robot pose is not available for " + behavior_name_;
114 RCLCPP_ERROR_STREAM(logger_, error_msg.c_str());
115 return ResultStatus{Status::FAILED, AssistedTeleopActionResult::TF_ERROR, error_msg};
118 geometry_msgs::msg::Pose projected_pose = current_pose.pose;
120 auto scaled_twist = std::make_unique<geometry_msgs::msg::TwistStamped>(teleop_twist_);
121 bool fetch_data =
true;
122 for (
double time = simulation_time_step_; time < projection_time_;
123 time += simulation_time_step_)
125 projected_pose = projectPose(projected_pose, teleop_twist_.twist, simulation_time_step_);
127 if (!local_collision_checker_->isCollisionFree(projected_pose, fetch_data)) {
128 if (time == simulation_time_step_) {
129 RCLCPP_DEBUG_STREAM_THROTTLE(
133 behavior_name_.c_str() <<
" collided on first time step, setting velocity to zero");
134 scaled_twist->twist.linear.x = 0.0f;
135 scaled_twist->twist.linear.y = 0.0f;
136 scaled_twist->twist.angular.z = 0.0f;
139 RCLCPP_DEBUG_STREAM_THROTTLE(
143 behavior_name_.c_str() <<
" collision approaching in " << time <<
" seconds");
144 double scale_factor = time / projection_time_;
145 scaled_twist->twist.linear.x *= scale_factor;
146 scaled_twist->twist.linear.y *= scale_factor;
147 scaled_twist->twist.angular.z *= scale_factor;
153 vel_pub_->publish(std::move(scaled_twist));
155 return ResultStatus{Status::RUNNING, AssistedTeleopActionResult::NONE,
""};
158 geometry_msgs::msg::Pose AssistedTeleop::projectPose(
159 const geometry_msgs::msg::Pose & pose,
160 const geometry_msgs::msg::Twist & twist,
161 double projection_time)
163 geometry_msgs::msg::Pose projected_pose = pose;
165 double theta = tf2::getYaw(pose.orientation);
167 projected_pose.position.x += projection_time * (
168 twist.linear.x * cos(theta) -
169 twist.linear.y * sin(theta));
171 projected_pose.position.y += projection_time * (
172 twist.linear.x * sin(theta) +
173 twist.linear.y * cos(theta));
175 double new_theta = theta + projection_time * twist.angular.z;
176 projected_pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(new_theta);
178 return projected_pose;
181 void AssistedTeleop::preemptTeleopCallback(
const std_msgs::msg::Empty::ConstSharedPtr &)
183 preempt_teleop_ =
true;
186 bool AssistedTeleop::isTeleopCommandStale(
const rclcpp::Time & now)
const
188 if (teleop_command_timeout_ <= 0.0 || !received_first_command_) {
191 return (now - rclcpp::Time(teleop_twist_.header.stamp, now.get_clock_type())).seconds() >
192 teleop_command_timeout_;
197 #include "pluginlib/class_list_macros.hpp"
An action server behavior for assisted teleop.
Abstract interface for behaviors to adhere to with pluginlib.