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