25 #include "spin_behavior_tester.hpp"
27 using namespace std::chrono_literals;
28 using namespace std::chrono;
30 namespace nav2_system_tests
33 SpinBehaviorTester::SpinBehaviorTester()
35 initial_pose_received_(false)
37 node_ = rclcpp::Node::make_shared(
"spin_behavior_test");
40 tf_buffer_ = std::make_shared<tf2_ros::Buffer>(node_->get_clock());
41 tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
42 tf_broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(node_);
43 if (std::getenv(
"MAKE_FAKE_COSTMAP") != NULL) {
45 make_fake_costmap_ =
true;
47 make_fake_costmap_ =
false;
50 client_ptr_ = rclcpp_action::create_client<Spin>(
51 node_->get_node_base_interface(),
52 node_->get_node_graph_interface(),
53 node_->get_node_logging_interface(),
54 node_->get_node_waitables_interface(),
58 node_->create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>(
"initialpose", 10);
59 fake_costmap_publisher_ =
60 node_->create_publisher<nav2_msgs::msg::Costmap>(
61 "local_costmap/costmap_raw",
62 rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
63 fake_footprint_publisher_ = node_->create_publisher<geometry_msgs::msg::PolygonStamped>(
64 "local_costmap/published_footprint", rclcpp::SystemDefaultsQoS());
66 subscription_ = node_->create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
67 "amcl_pose", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
68 std::bind(&SpinBehaviorTester::amclPoseCallback,
this, std::placeholders::_1));
70 stamp_ = node_->now();
73 SpinBehaviorTester::~SpinBehaviorTester()
80 void SpinBehaviorTester::activate()
83 throw std::runtime_error(
"Trying to activate while already active");
86 if (!make_fake_costmap_) {
87 while (!initial_pose_received_) {
88 RCLCPP_WARN(node_->get_logger(),
"Initial pose not received");
90 std::this_thread::sleep_for(100ms);
91 rclcpp::spin_some(node_);
98 std::this_thread::sleep_for(10s);
101 RCLCPP_ERROR(node_->get_logger(),
"Action client not initialized");
106 if (!client_ptr_->wait_for_action_server(10s)) {
107 RCLCPP_ERROR(node_->get_logger(),
"Action server not available after waiting");
112 RCLCPP_INFO(this->node_->get_logger(),
"Spin action server is ready");
116 void SpinBehaviorTester::deactivate()
119 throw std::runtime_error(
"Trying to deactivate while already inactive");
124 bool SpinBehaviorTester::defaultSpinBehaviorTest(
125 const float target_yaw,
126 const double tolerance)
129 RCLCPP_ERROR(node_->get_logger(),
"Not activated");
134 std::this_thread::sleep_for(5s);
136 if (make_fake_costmap_) {
140 auto goal_msg = Spin::Goal();
141 goal_msg.target_yaw = target_yaw;
144 if (make_fake_costmap_) {
145 sendFakeCostmap(target_yaw);
149 geometry_msgs::msg::PoseStamped initial_pose;
150 if (!nav2_util::getCurrentPose(initial_pose, *tf_buffer_,
"odom")) {
151 RCLCPP_ERROR(node_->get_logger(),
"Current robot pose is not available.");
154 RCLCPP_INFO(node_->get_logger(),
"Found current robot pose");
158 fabs(tf2::getYaw(initial_pose.pose.orientation)));
159 RCLCPP_INFO(node_->get_logger(),
"Before sending goal");
162 if (make_fake_costmap_) {
163 sendFakeCostmap(target_yaw);
167 rclcpp::sleep_for(std::chrono::milliseconds(100));
169 auto goal_handle_future = client_ptr_->async_send_goal(goal_msg);
171 if (rclcpp::spin_until_future_complete(node_, goal_handle_future) !=
172 rclcpp::FutureReturnCode::SUCCESS)
174 RCLCPP_ERROR(node_->get_logger(),
"send goal call failed :(");
178 rclcpp_action::ClientGoalHandle<Spin>::SharedPtr goal_handle = goal_handle_future.get();
180 RCLCPP_ERROR(node_->get_logger(),
"Goal was rejected by server");
185 auto result_future = client_ptr_->async_get_result(goal_handle);
187 RCLCPP_INFO(node_->get_logger(),
"Waiting for result");
188 rclcpp::sleep_for(std::chrono::milliseconds(1000));
190 if (make_fake_costmap_) {
192 sendFakeCostmap(target_yaw);
193 RCLCPP_INFO(node_->get_logger(),
"target_yaw %lf", target_yaw);
195 float step_size = tolerance / 4.0;
196 for (
float command_yaw = 0.0;
197 abs(command_yaw) < abs(target_yaw);
198 command_yaw = command_yaw + step_size)
200 sendFakeOdom(command_yaw);
201 sendFakeCostmap(target_yaw);
202 rclcpp::sleep_for(std::chrono::milliseconds(1));
204 sendFakeOdom(target_yaw);
205 sendFakeCostmap(target_yaw);
206 RCLCPP_INFO(node_->get_logger(),
"After sending goal");
208 if (rclcpp::spin_until_future_complete(node_, result_future) !=
209 rclcpp::FutureReturnCode::SUCCESS)
211 RCLCPP_ERROR(node_->get_logger(),
"get result call failed :(");
215 rclcpp_action::ClientGoalHandle<Spin>::WrappedResult wrapped_result = result_future.get();
217 switch (wrapped_result.code) {
218 case rclcpp_action::ResultCode::SUCCEEDED:
break;
219 case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(
223 case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(
225 "Goal was canceled");
227 default: RCLCPP_ERROR(node_->get_logger(),
"Unknown result code");
231 RCLCPP_INFO(node_->get_logger(),
"result received");
233 geometry_msgs::msg::PoseStamped current_pose;
234 if (!nav2_util::getCurrentPose(current_pose, *tf_buffer_,
"odom")) {
235 RCLCPP_ERROR(node_->get_logger(),
"Current robot pose is not available.");
239 double goal_yaw = angles::normalize_angle(
240 tf2::getYaw(initial_pose.pose.orientation) + target_yaw);
241 double dyaw = angles::shortest_angular_distance(
242 goal_yaw, tf2::getYaw(current_pose.pose.orientation));
244 if (fabs(dyaw) > tolerance) {
247 "Init Yaw is %lf (tolerance %lf)",
248 fabs(tf2::getYaw(initial_pose.pose.orientation)), tolerance);
251 "Current Yaw is %lf (tolerance %lf)",
252 fabs(tf2::getYaw(current_pose.pose.orientation)), tolerance);
255 "Angular distance from goal is %lf (tolerance %lf)",
256 fabs(dyaw), tolerance);
263 void SpinBehaviorTester::sendFakeCostmap(
float angle)
265 nav2_msgs::msg::Costmap fake_costmap;
267 fake_costmap.header.frame_id =
"odom";
268 fake_costmap.header.stamp = stamp_;
269 fake_costmap.metadata.layer =
"master";
270 fake_costmap.metadata.resolution = .1;
271 fake_costmap.metadata.size_x = 100;
272 fake_costmap.metadata.size_y = 100;
273 fake_costmap.metadata.origin.position.x = 0;
274 fake_costmap.metadata.origin.position.y = 0;
275 fake_costmap.metadata.origin.orientation.w = 1.0;
276 float costmap_val = 0;
277 for (
int ix = 0; ix < 100; ix++) {
278 for (
int iy = 0; iy < 100; iy++) {
279 if (abs(angle) > M_PI_2f32) {
283 fake_costmap.data.push_back(costmap_val);
286 fake_costmap_publisher_->publish(fake_costmap);
289 void SpinBehaviorTester::sendInitialPose()
291 geometry_msgs::msg::PoseWithCovarianceStamped pose;
292 pose.header.frame_id =
"map";
293 pose.header.stamp = stamp_;
294 pose.pose.pose.position.x = -2.0;
295 pose.pose.pose.position.y = -0.5;
296 pose.pose.pose.position.z = 0.0;
297 pose.pose.pose.orientation.x = 0.0;
298 pose.pose.pose.orientation.y = 0.0;
299 pose.pose.pose.orientation.z = 0.0;
300 pose.pose.pose.orientation.w = 1.0;
301 for (
int i = 0; i < 35; i++) {
302 pose.pose.covariance[i] = 0.0;
304 pose.pose.covariance[0] = 0.08;
305 pose.pose.covariance[7] = 0.08;
306 pose.pose.covariance[35] = 0.05;
308 publisher_->publish(pose);
309 RCLCPP_INFO(node_->get_logger(),
"Sent initial pose");
312 void SpinBehaviorTester::sendFakeOdom(
float angle)
314 geometry_msgs::msg::TransformStamped transformStamped;
316 transformStamped.header.stamp = stamp_;
317 transformStamped.header.frame_id =
"odom";
318 transformStamped.child_frame_id =
"base_link";
319 transformStamped.transform.translation.x = 0.0;
320 transformStamped.transform.translation.y = 0.0;
321 transformStamped.transform.translation.z = 0.0;
323 q.setRPY(0, 0, angle);
324 transformStamped.transform.rotation.x = q.x();
325 transformStamped.transform.rotation.y = q.y();
326 transformStamped.transform.rotation.z = q.z();
327 transformStamped.transform.rotation.w = q.w();
329 tf_broadcaster_->sendTransform(transformStamped);
331 geometry_msgs::msg::PolygonStamped footprint;
332 footprint.header.frame_id =
"odom";
333 footprint.header.stamp = stamp_;
334 footprint.polygon.points.resize(4);
335 footprint.polygon.points[0].x = 0.22;
336 footprint.polygon.points[0].y = 0.22;
337 footprint.polygon.points[1].x = 0.22;
338 footprint.polygon.points[1].y = -0.22;
339 footprint.polygon.points[2].x = -0.22;
340 footprint.polygon.points[2].y = -0.22;
341 footprint.polygon.points[3].x = -0.22;
342 footprint.polygon.points[3].y = 0.22;
343 fake_footprint_publisher_->publish(footprint);
345 void SpinBehaviorTester::amclPoseCallback(
346 const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr)
348 initial_pose_received_ =
true;