Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
assisted_teleop_behavior_tester.cpp
1 // Copyright (c) 2020 Sarthak Mittal
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License. Reserved.
15 
16 #include <string>
17 #include <random>
18 #include <tuple>
19 #include <memory>
20 #include <iostream>
21 #include <chrono>
22 #include <sstream>
23 #include <iomanip>
24 
25 #include "assisted_teleop_behavior_tester.hpp"
26 #include "nav2_util/geometry_utils.hpp"
27 
28 using namespace std::chrono_literals;
29 using namespace std::chrono; // NOLINT
30 
31 namespace nav2_system_tests
32 {
33 
34 AssistedTeleopBehaviorTester::AssistedTeleopBehaviorTester()
35 : is_active_(false),
36  initial_pose_received_(false)
37 {
38  node_ = rclcpp::Node::make_shared("assisted_teleop_behavior_test");
39 
40  tf_buffer_ = std::make_shared<tf2_ros::Buffer>(node_->get_clock());
41  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
42 
43  client_ptr_ = rclcpp_action::create_client<AssistedTeleop>(
44  node_->get_node_base_interface(),
45  node_->get_node_graph_interface(),
46  node_->get_node_logging_interface(),
47  node_->get_node_waitables_interface(),
48  "assisted_teleop");
49 
50  initial_pose_pub_ =
51  node_->create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>("initialpose", 10);
52 
53  preempt_pub_ =
54  node_->create_publisher<std_msgs::msg::Empty>("preempt_teleop", 10);
55 
56  cmd_vel_pub_ =
57  node_->create_publisher<geometry_msgs::msg::TwistStamped>("cmd_vel_teleop", 10);
58 
59  subscription_ = node_->create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
60  "amcl_pose", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
61  std::bind(&AssistedTeleopBehaviorTester::amclPoseCallback, this, std::placeholders::_1));
62 
63  filtered_vel_sub_ = node_->create_subscription<geometry_msgs::msg::TwistStamped>(
64  "cmd_vel",
65  rclcpp::SystemDefaultsQoS(),
66  std::bind(&AssistedTeleopBehaviorTester::filteredVelCallback, this, std::placeholders::_1));
67 
68  std::string costmap_topic = "/local_costmap/costmap_raw";
69  std::string footprint_topic = "/local_costmap/published_footprint";
70 
71  costmap_sub_ = std::make_shared<nav2_costmap_2d::CostmapSubscriber>(
72  node_,
73  costmap_topic);
74 
75  footprint_sub_ = std::make_shared<nav2_costmap_2d::FootprintSubscriber>(
76  node_,
77  footprint_topic,
78  *tf_buffer_);
79 
80  collision_checker_ = std::make_unique<nav2_costmap_2d::CostmapTopicCollisionChecker>(
81  *costmap_sub_,
82  *footprint_sub_
83  );
84 
85  stamp_ = node_->now();
86  executor_.add_node(node_);
87 }
88 
89 AssistedTeleopBehaviorTester::~AssistedTeleopBehaviorTester()
90 {
91  if (is_active_) {
92  deactivate();
93  }
94 }
95 
96 void AssistedTeleopBehaviorTester::activate()
97 {
98  if (is_active_) {
99  throw std::runtime_error("Trying to activate while already active");
100  return;
101  }
102 
103  while (!initial_pose_received_) {
104  RCLCPP_WARN(node_->get_logger(), "Initial pose not received");
105  sendInitialPose();
106  std::this_thread::sleep_for(100ms);
107  executor_.spin_some();
108  }
109 
110  // Wait for lifecycle_manager_navigation to activate behavior_server
111  std::this_thread::sleep_for(10s);
112 
113  if (!client_ptr_) {
114  RCLCPP_ERROR(node_->get_logger(), "Action client not initialized");
115  is_active_ = false;
116  return;
117  }
118 
119  if (!client_ptr_->wait_for_action_server(10s)) {
120  RCLCPP_ERROR(node_->get_logger(), "Action server not available after waiting");
121  is_active_ = false;
122  return;
123  }
124 
125  RCLCPP_INFO(this->node_->get_logger(), "Assisted Teleop action server is ready");
126  is_active_ = true;
127 }
128 
129 void AssistedTeleopBehaviorTester::deactivate()
130 {
131  if (!is_active_) {
132  throw std::runtime_error("Trying to deactivate while already inactive");
133  }
134  is_active_ = false;
135 }
136 
137 bool AssistedTeleopBehaviorTester::defaultAssistedTeleopTest(
138  const float lin_vel,
139  const float ang_vel)
140 {
141  if (!is_active_) {
142  RCLCPP_ERROR(node_->get_logger(), "Not activated");
143  return false;
144  }
145 
146  RCLCPP_INFO(node_->get_logger(), "Sending goal");
147 
148  auto goal_handle_future = client_ptr_->async_send_goal(nav2_msgs::action::AssistedTeleop::Goal());
149 
150  if (executor_.spin_until_future_complete(goal_handle_future) !=
151  rclcpp::FutureReturnCode::SUCCESS)
152  {
153  RCLCPP_ERROR(node_->get_logger(), "send goal call failed :(");
154  return false;
155  }
156 
157  rclcpp_action::ClientGoalHandle<AssistedTeleop>::SharedPtr goal_handle = goal_handle_future.get();
158  if (!goal_handle) {
159  RCLCPP_ERROR(node_->get_logger(), "Goal was rejected by server");
160  return false;
161  }
162 
163  // Wait for the server to be done with the goal
164  auto result_future = client_ptr_->async_get_result(goal_handle);
165 
166  rclcpp::Rate r(1);
167 
168  counter_ = 0;
169  auto start_time = std::chrono::system_clock::now();
170  while (rclcpp::ok()) {
171  geometry_msgs::msg::TwistStamped cmd_vel = geometry_msgs::msg::TwistStamped();
172  cmd_vel.twist.linear.x = lin_vel;
173  cmd_vel.twist.angular.z = ang_vel;
174  cmd_vel_pub_->publish(cmd_vel);
175 
176  if (counter_ > 1) {
177  break;
178  }
179 
180  auto current_time = std::chrono::system_clock::now();
181  if (current_time - start_time > 25s) {
182  RCLCPP_ERROR(node_->get_logger(), "Exceeded Timeout");
183  return false;
184  }
185 
186  executor_.spin_some();
187  r.sleep();
188  }
189 
190  auto preempt_msg = std_msgs::msg::Empty();
191  preempt_pub_->publish(preempt_msg);
192 
193  RCLCPP_INFO(node_->get_logger(), "Waiting for result");
194  if (executor_.spin_until_future_complete(result_future) !=
195  rclcpp::FutureReturnCode::SUCCESS)
196  {
197  RCLCPP_ERROR(node_->get_logger(), "get result call failed :(");
198  return false;
199  }
200 
201  rclcpp_action::ClientGoalHandle<AssistedTeleop>::WrappedResult
202  wrapped_result = result_future.get();
203 
204  switch (wrapped_result.code) {
205  case rclcpp_action::ResultCode::SUCCEEDED: break;
206  case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(
207  node_->get_logger(),
208  "Goal was aborted");
209  return false;
210  case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(
211  node_->get_logger(),
212  "Goal was canceled");
213  return false;
214  default: RCLCPP_ERROR(node_->get_logger(), "Unknown result code");
215  return false;
216  }
217 
218  RCLCPP_INFO(node_->get_logger(), "result received");
219 
220  geometry_msgs::msg::PoseStamped current_pose;
221  if (!nav2_util::getCurrentPose(current_pose, *tf_buffer_, "odom")) {
222  RCLCPP_ERROR(node_->get_logger(), "Current robot pose is not available.");
223  return false;
224  }
225 
226  if (!collision_checker_->isCollisionFree(current_pose.pose)) {
227  RCLCPP_ERROR(node_->get_logger(), "Ended in collision");
228  return false;
229  }
230 
231  return true;
232 }
233 
234 void AssistedTeleopBehaviorTester::sendInitialPose()
235 {
236  geometry_msgs::msg::PoseWithCovarianceStamped pose;
237  pose.header.frame_id = "map";
238  pose.header.stamp = stamp_;
239  pose.pose.pose.position.x = -2.0;
240  pose.pose.pose.position.y = -0.5;
241  pose.pose.pose.position.z = 0.0;
242  pose.pose.pose.orientation.x = 0.0;
243  pose.pose.pose.orientation.y = 0.0;
244  pose.pose.pose.orientation.z = 0.0;
245  pose.pose.pose.orientation.w = 1.0;
246  for (int i = 0; i < 35; i++) {
247  pose.pose.covariance[i] = 0.0;
248  }
249  pose.pose.covariance[0] = 0.08;
250  pose.pose.covariance[7] = 0.08;
251  pose.pose.covariance[35] = 0.05;
252 
253  initial_pose_pub_->publish(pose);
254  RCLCPP_INFO(node_->get_logger(), "Sent initial pose");
255 }
256 
257 void AssistedTeleopBehaviorTester::amclPoseCallback(
258  const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr)
259 {
260  initial_pose_received_ = true;
261 }
262 
263 void AssistedTeleopBehaviorTester::filteredVelCallback(
264  geometry_msgs::msg::TwistStamped::SharedPtr msg)
265 {
266  if (msg->twist.linear.x == 0.0f) {
267  counter_++;
268  } else {
269  counter_ = 0;
270  }
271 }
272 
273 } // namespace nav2_system_tests