test_trajectory_tracker.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018, the neonavigation authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <string>
31 #include <vector>
32 
33 #include <boost/thread.hpp>
34 
35 #include <Eigen/Core>
36 #include <Eigen/Geometry>
37 
38 #include <ros/ros.h>
39 
40 #include <geometry_msgs/Twist.h>
41 #include <nav_msgs/Path.h>
42 #include <rosgraph_msgs/Clock.h>
45 
46 #include <trajectory_tracker_msgs/TrajectoryTrackerStatus.h>
47 #include <trajectory_tracker_msgs/PathWithVelocity.h>
48 
49 #include <gtest/gtest.h>
50 
51 class TrajectoryTrackerTest : public ::testing::Test
52 {
53 private:
60 
62 
63  void cbStatus(const trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr& msg)
64  {
65  status_ = msg;
66  }
67  void cbCmdVel(const geometry_msgs::Twist::ConstPtr& msg)
68  {
69  const ros::Time now = ros::Time::now();
70  if (cmd_vel_time_ == ros::Time(0))
71  cmd_vel_time_ = now;
72  const float dt = (now - cmd_vel_time_).toSec();
73 
74  yaw_ += msg->angular.z * dt;
75  pos_ += Eigen::Vector2d(std::cos(yaw_), std::sin(yaw_)) * msg->linear.x * dt;
76  cmd_vel_time_ = now;
77  cmd_vel_ = msg;
78  }
79 
80 public:
81  Eigen::Vector2d pos_;
82  double yaw_;
83  trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr status_;
84  geometry_msgs::Twist::ConstPtr cmd_vel_;
85 
87  : nh_("")
88  {
89  sub_cmd_vel_ = nh_.subscribe(
90  "cmd_vel", 1, &TrajectoryTrackerTest::cbCmdVel, this);
91  sub_status_ = nh_.subscribe(
92  "trajectory_tracker/status", 1, &TrajectoryTrackerTest::cbStatus, this);
93  pub_path_ = nh_.advertise<nav_msgs::Path>("path", 1, true);
94  pub_path_vel_ = nh_.advertise<trajectory_tracker_msgs::PathWithVelocity>("path_velocity", 1, true);
95  }
96  void initState(const Eigen::Vector2d& pos, const float yaw)
97  {
98  nav_msgs::Path path;
99  path.header.frame_id = "odom";
100  path.header.stamp = ros::Time::now();
101  pub_path_.publish(path);
102 
103  // Wait until trajectory_tracker node
104  ros::Rate rate(10);
105  while (ros::ok())
106  {
107  yaw_ = yaw;
108  pos_ = pos;
110 
111  rate.sleep();
112  ros::spinOnce();
113  if (status_)
114  break;
115  }
116  }
118  {
119  ros::Rate rate(50);
120  while (ros::ok())
121  {
123  rate.sleep();
124  ros::spinOnce();
125  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::FOLLOWING)
126  break;
127  }
128  }
129  void publishPath(const std::vector<Eigen::Vector3d>& poses)
130  {
131  nav_msgs::Path path;
132  path.header.frame_id = "odom";
133  path.header.stamp = ros::Time::now();
134 
135  for (const Eigen::Vector3d& p : poses)
136  {
137  const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
138 
139  geometry_msgs::PoseStamped pose;
140  pose.header.frame_id = path.header.frame_id;
141  pose.pose.position.x = p[0];
142  pose.pose.position.y = p[1];
143  pose.pose.orientation.x = q.x();
144  pose.pose.orientation.y = q.y();
145  pose.pose.orientation.z = q.z();
146  pose.pose.orientation.w = q.w();
147 
148  path.poses.push_back(pose);
149  }
150  pub_path_.publish(path);
151  }
152  void publishPathVelocity(const std::vector<Eigen::Vector4d>& poses)
153  {
154  trajectory_tracker_msgs::PathWithVelocity path;
155  path.header.frame_id = "odom";
156  path.header.stamp = ros::Time::now();
157 
158  for (const Eigen::Vector4d& p : poses)
159  {
160  const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
161 
162  trajectory_tracker_msgs::PoseStampedWithVelocity pose;
163  pose.header.frame_id = path.header.frame_id;
164  pose.pose.position.x = p[0];
165  pose.pose.position.y = p[1];
166  pose.pose.orientation.x = q.x();
167  pose.pose.orientation.y = q.y();
168  pose.pose.orientation.z = q.z();
169  pose.pose.orientation.w = q.w();
170  pose.linear_velocity.x = p[3];
171 
172  path.poses.push_back(pose);
173  }
174  // needs sleep to prevent that the empty path from initState arrives later.
175  ros::Duration(0.5).sleep();
176  pub_path_vel_.publish(path);
177  }
179  {
180  const Eigen::Quaterniond q(Eigen::AngleAxisd(yaw_, Eigen::Vector3d(0, 0, 1)));
181  geometry_msgs::TransformStamped trans;
182  trans.header.frame_id = "odom";
183  trans.header.stamp = ros::Time::now() + ros::Duration(0.1);
184  trans.child_frame_id = "base_link";
185  trans.transform.translation.x = pos_[0];
186  trans.transform.translation.y = pos_[1];
187  trans.transform.rotation.x = q.x();
188  trans.transform.rotation.y = q.y();
189  trans.transform.rotation.z = q.z();
190  trans.transform.rotation.w = q.w();
191  tfb_.sendTransform(trans);
192  }
193 };
194 
196 {
197  initState(Eigen::Vector2d(0, 0), 0);
198 
199  std::vector<Eigen::Vector3d> poses;
200  for (double x = 0.0; x < 0.5; x += 0.01)
201  poses.push_back(Eigen::Vector3d(x, 0.0, 0.0));
202  poses.push_back(Eigen::Vector3d(0.5, 0.0, 0.0));
203  publishPath(poses);
204 
205  waitUntilStart();
206 
207  ros::Rate rate(50);
208  const ros::Time start = ros::Time::now();
209  while (ros::ok())
210  {
211  ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
212 
214  rate.sleep();
215  ros::spinOnce();
216  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
217  break;
218  }
219  for (int i = 0; i < 25; ++i)
220  {
222  rate.sleep();
223  ros::spinOnce();
224  }
225 
226  ASSERT_NEAR(yaw_, 0.0, 1e-2);
227  ASSERT_NEAR(pos_[0], 0.5, 1e-2);
228  ASSERT_NEAR(pos_[1], 0.0, 1e-2);
229 }
230 
231 TEST_F(TrajectoryTrackerTest, StraightStopConvergence)
232 {
233  const double vels[] =
234  {
235  0.02, 0.05, 0.1, 0.2, 0.5, 1.0
236  };
237  const double path_length = 2.0;
238  for (const double vel : vels)
239  {
240  const std::string info_message = "linear vel: " + std::to_string(vel);
241 
242  initState(Eigen::Vector2d(0, 0.01), 0);
243 
244  std::vector<Eigen::Vector4d> poses;
245  for (double x = 0.0; x < path_length; x += 0.01)
246  poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, vel));
247  poses.push_back(Eigen::Vector4d(path_length, 0.0, 0.0, vel));
248  publishPathVelocity(poses);
249 
250  waitUntilStart();
251 
252  ros::Rate rate(50);
253  const ros::Time start = ros::Time::now();
254  while (ros::ok())
255  {
256  ASSERT_LT(ros::Time::now() - start, ros::Duration(5.0 + path_length / vel)) << info_message;
257 
259  rate.sleep();
260  ros::spinOnce();
261  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
262  break;
263  }
264  for (int i = 0; i < 25; ++i)
265  {
267  rate.sleep();
268  ros::spinOnce();
269  }
270 
271  EXPECT_NEAR(yaw_, 0.0, 1e-2) << info_message;
272  EXPECT_NEAR(pos_[0], path_length, 1e-2) << info_message;
273  EXPECT_NEAR(pos_[1], 0.0, 1e-2) << info_message;
274  }
275 }
276 
277 TEST_F(TrajectoryTrackerTest, StraightVelocityChange)
278 {
279  initState(Eigen::Vector2d(0, 0), 0);
280 
281  std::vector<Eigen::Vector4d> poses;
282  for (double x = 0.0; x < 0.5; x += 0.01)
283  poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, 0.3));
284  for (double x = 0.5; x < 1.5; x += 0.01)
285  poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, 0.5));
286  poses.push_back(Eigen::Vector4d(1.5, 0.0, 0.0, 0.5));
287  publishPathVelocity(poses);
288 
289  waitUntilStart();
290 
291  ros::Rate rate(50);
292  const ros::Time start = ros::Time::now();
293  while (ros::ok())
294  {
295  ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
296 
298  rate.sleep();
299  ros::spinOnce();
300  if (0.3 < pos_[0] && pos_[0] < 0.4)
301  {
302  ASSERT_NEAR(cmd_vel_->linear.x, 0.3, 1e-2);
303  }
304  else if (0.9 < pos_[0] && pos_[0] < 1.0)
305  {
306  ASSERT_NEAR(cmd_vel_->linear.x, 0.5, 1e-2);
307  }
308 
309  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
310  break;
311  }
312  for (int i = 0; i < 25; ++i)
313  {
315  rate.sleep();
316  ros::spinOnce();
317  }
318 
319  ASSERT_NEAR(yaw_, 0.0, 1e-2);
320  ASSERT_NEAR(pos_[0], 1.5, 1e-2);
321  ASSERT_NEAR(pos_[1], 0.0, 1e-2);
322 }
323 
325 {
326  initState(Eigen::Vector2d(0, 0), 0);
327 
328  std::vector<Eigen::Vector3d> poses;
329  Eigen::Vector3d p(0.0, 0.0, 0.0);
330  for (double t = 0.0; t < 1.0; t += 0.01)
331  {
332  p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.005);
333  poses.push_back(p);
334  }
335  for (double t = 0.0; t < 1.0; t += 0.01)
336  {
337  p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.0);
338  poses.push_back(p);
339  }
340  publishPath(poses);
341 
342  waitUntilStart();
343 
344  ros::Rate rate(50);
345  const ros::Time start = ros::Time::now();
346  while (ros::ok())
347  {
348  ASSERT_LT(ros::Time::now() - start, ros::Duration(20.0));
349 
351  rate.sleep();
352  ros::spinOnce();
353  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
354  break;
355  }
356  for (int i = 0; i < 25; ++i)
357  {
359  rate.sleep();
360  ros::spinOnce();
361  }
362 
363  ASSERT_NEAR(yaw_, p[2], 1e-2);
364  ASSERT_NEAR(pos_[0], p[0], 1e-1);
365  ASSERT_NEAR(pos_[1], p[1], 1e-1);
366 }
367 
369 {
370  const float init_yaw_array[] =
371  {
372  0.0,
373  3.0
374  };
375  for (const float init_yaw : init_yaw_array)
376  {
377  initState(Eigen::Vector2d(0, 0), init_yaw);
378 
379  const float target_angle_array[] =
380  {
381  0.5,
382  -0.5
383  };
384  for (const float ang : target_angle_array)
385  {
386  std::vector<Eigen::Vector3d> poses;
387  poses.push_back(Eigen::Vector3d(0.0, 0.0, init_yaw + ang));
388  publishPath(poses);
389 
390  waitUntilStart();
391 
392  ros::Rate rate(50);
393  const ros::Time start = ros::Time::now();
394  while (ros::ok())
395  {
396  ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
397 
399  rate.sleep();
400  ros::spinOnce();
401 
402  if (cmd_vel_)
403  {
404  ASSERT_GT(cmd_vel_->angular.z * ang, -1e-2);
405  }
406  if (status_)
407  {
408  ASSERT_LT(status_->angle_remains * ang, 1e-2);
409  }
410 
411  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
412  break;
413  }
414  ASSERT_TRUE(static_cast<bool>(cmd_vel_));
415  for (int i = 0; i < 25; ++i)
416  {
418  rate.sleep();
419  ros::spinOnce();
420  }
421 
422  ASSERT_NEAR(yaw_, init_yaw + ang, 1e-2);
423  }
424  }
425 }
426 
428 {
429  initState(Eigen::Vector2d(0, 0), 0);
430 
431  std::vector<Eigen::Vector3d> poses;
432  Eigen::Vector3d p(0.0, 0.0, 0.0);
433  for (double t = 0.0; t < 0.5; t += 0.01)
434  {
435  p -= Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, -0.01);
436  poses.push_back(p);
437  }
438  for (double t = 0.0; t < 0.5; t += 0.01)
439  {
440  p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.01);
441  poses.push_back(p);
442  }
443  publishPath(poses);
444 
445  waitUntilStart();
446 
447  ros::Rate rate(50);
448  const ros::Time start = ros::Time::now();
449  while (ros::ok())
450  {
451  ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
452 
454  rate.sleep();
455  ros::spinOnce();
456  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
457  break;
458  }
459  for (int i = 0; i < 25; ++i)
460  {
462  rate.sleep();
463  ros::spinOnce();
464  }
465 
466  ASSERT_NEAR(yaw_, p[2], 1e-2);
467  ASSERT_NEAR(pos_[0], p[0], 1e-1);
468  ASSERT_NEAR(pos_[1], p[1], 1e-1);
469 }
470 
471 TEST_F(TrajectoryTrackerTest, SwitchBackWithPathUpdate)
472 {
473  initState(Eigen::Vector2d(0, 0), 0);
474 
475  std::vector<Eigen::Vector3d> poses;
476  std::vector<Eigen::Vector3d> poses_second_half;
477  Eigen::Vector3d p(0.0, 0.0, 0.0);
478  for (double t = 0.0; t < 0.5; t += 0.01)
479  {
480  p -= Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, -0.01);
481  poses.push_back(p);
482  }
483  const Eigen::Vector2d pos_local_goal = p.head<2>();
484  for (double t = 0.0; t < 1.0; t += 0.01)
485  {
486  p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.01);
487  poses.push_back(p);
488  poses_second_half.push_back(p);
489  }
490  publishPath(poses);
491 
492  waitUntilStart();
493 
494  int cnt_arrive_local_goal(0);
495  ros::Rate rate(50);
496  const ros::Time start = ros::Time::now();
497  while (ros::ok())
498  {
499  ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
500 
502  rate.sleep();
503  ros::spinOnce();
504  if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
505  break;
506 
507  if ((pos_local_goal - pos_).norm() < 0.1)
508  cnt_arrive_local_goal++;
509 
510  if (cnt_arrive_local_goal > 25)
511  publishPath(poses_second_half);
512  else
513  publishPath(poses);
514  }
515  for (int i = 0; i < 25; ++i)
516  {
518  rate.sleep();
519  ros::spinOnce();
520  }
521 
522  ASSERT_NEAR(yaw_, p[2], 1e-2);
523  ASSERT_NEAR(pos_[0], p[0], 1e-1);
524  ASSERT_NEAR(pos_[1], p[1], 1e-1);
525 }
526 
528 {
529  ros::NodeHandle nh("/");
530  bool use_sim_time;
531  nh.param("/use_sim_time", use_sim_time, false);
532  if (!use_sim_time)
533  return;
534 
535  ros::Publisher pub = nh.advertise<rosgraph_msgs::Clock>("clock", 1);
536 
537  ros::WallRate rate(500.0); // 500% speed
539  while (ros::ok())
540  {
541  rosgraph_msgs::Clock clock;
542  clock.clock.fromNSec(time.toNSec());
543  pub.publish(clock);
544  rate.sleep();
545  time += ros::WallDuration(0.01);
546  }
547 }
548 
549 int main(int argc, char** argv)
550 {
551  testing::InitGoogleTest(&argc, argv);
552  ros::init(argc, argv, "test_trajectory_tracker");
553 
554  boost::thread time_thread(timeSource);
555 
556  return RUN_ALL_TESTS();
557 }
void publish(const boost::shared_ptr< M > &message) const
void publishPath(const std::vector< Eigen::Vector3d > &poses)
geometry_msgs::Twist::ConstPtr cmd_vel_
tf2_ros::TransformBroadcaster tfb_
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void timeSource()
bool sleep() const
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
void cbCmdVel(const geometry_msgs::Twist::ConstPtr &msg)
geometry_msgs::TransformStamped t
trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr status_
int main(int argc, char **argv)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
TEST_F(TrajectoryTrackerTest, StraightStop)
TFSIMD_FORCE_INLINE const tfScalar & x() const
void initState(const Eigen::Vector2d &pos, const float yaw)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void sendTransform(const geometry_msgs::TransformStamped &transform)
bool sleep()
void publishPathVelocity(const std::vector< Eigen::Vector4d > &poses)
static WallTime now()
void cbStatus(const trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr &msg)
static Time now()
ROSCPP_DECL void spinOnce()


trajectory_tracker
Author(s): Atsushi Watanabe
autogenerated on Tue Jul 9 2019 05:00:09