test_trajectory_tracker.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2018, the neonavigation authors
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the copyright holder nor the names of its 
00014  *       contributors may be used to endorse or promote products derived from 
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <string>
00031 #include <vector>
00032 
00033 #include <boost/thread.hpp>
00034 
00035 #include <Eigen/Core>
00036 #include <Eigen/Geometry>
00037 
00038 #include <ros/ros.h>
00039 
00040 #include <geometry_msgs/Twist.h>
00041 #include <nav_msgs/Path.h>
00042 #include <rosgraph_msgs/Clock.h>
00043 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
00044 #include <tf2_ros/transform_broadcaster.h>
00045 
00046 #include <trajectory_tracker_msgs/TrajectoryTrackerStatus.h>
00047 #include <trajectory_tracker_msgs/PathWithVelocity.h>
00048 
00049 #include <gtest/gtest.h>
00050 
00051 class TrajectoryTrackerTest : public ::testing::Test
00052 {
00053 private:
00054   ros::NodeHandle nh_;
00055   ros::Subscriber sub_cmd_vel_;
00056   ros::Subscriber sub_status_;
00057   ros::Publisher pub_path_;
00058   ros::Publisher pub_path_vel_;
00059   tf2_ros::TransformBroadcaster tfb_;
00060 
00061   ros::Time cmd_vel_time_;
00062 
00063   void cbStatus(const trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr& msg)
00064   {
00065     status_ = msg;
00066   }
00067   void cbCmdVel(const geometry_msgs::Twist::ConstPtr& msg)
00068   {
00069     const ros::Time now = ros::Time::now();
00070     if (cmd_vel_time_ == ros::Time(0))
00071       cmd_vel_time_ = now;
00072     const float dt = (now - cmd_vel_time_).toSec();
00073 
00074     yaw_ += msg->angular.z * dt;
00075     pos_ += Eigen::Vector2d(std::cos(yaw_), std::sin(yaw_)) * msg->linear.x * dt;
00076     cmd_vel_time_ = now;
00077     cmd_vel_ = msg;
00078   }
00079 
00080 public:
00081   Eigen::Vector2d pos_;
00082   double yaw_;
00083   trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr status_;
00084   geometry_msgs::Twist::ConstPtr cmd_vel_;
00085 
00086   TrajectoryTrackerTest()
00087     : nh_("")
00088   {
00089     sub_cmd_vel_ = nh_.subscribe(
00090         "cmd_vel", 1, &TrajectoryTrackerTest::cbCmdVel, this);
00091     sub_status_ = nh_.subscribe(
00092         "trajectory_tracker/status", 1, &TrajectoryTrackerTest::cbStatus, this);
00093     pub_path_ = nh_.advertise<nav_msgs::Path>("path", 1, true);
00094     pub_path_vel_ = nh_.advertise<trajectory_tracker_msgs::PathWithVelocity>("path_velocity", 1, true);
00095   }
00096   void initState(const Eigen::Vector2d& pos, const float yaw)
00097   {
00098     nav_msgs::Path path;
00099     path.header.frame_id = "odom";
00100     path.header.stamp = ros::Time::now();
00101     pub_path_.publish(path);
00102 
00103     // Wait until trajectory_tracker node
00104     ros::Rate rate(10);
00105     while (ros::ok())
00106     {
00107       yaw_ = yaw;
00108       pos_ = pos;
00109       publishTransform();
00110 
00111       rate.sleep();
00112       ros::spinOnce();
00113       if (status_)
00114         break;
00115     }
00116   }
00117   void waitUntilStart()
00118   {
00119     ros::Rate rate(50);
00120     while (ros::ok())
00121     {
00122       publishTransform();
00123       rate.sleep();
00124       ros::spinOnce();
00125       if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::FOLLOWING)
00126         break;
00127     }
00128   }
00129   void publishPath(const std::vector<Eigen::Vector3d>& poses)
00130   {
00131     nav_msgs::Path path;
00132     path.header.frame_id = "odom";
00133     path.header.stamp = ros::Time::now();
00134 
00135     for (const Eigen::Vector3d& p : poses)
00136     {
00137       const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
00138 
00139       geometry_msgs::PoseStamped pose;
00140       pose.header.frame_id = path.header.frame_id;
00141       pose.pose.position.x = p[0];
00142       pose.pose.position.y = p[1];
00143       pose.pose.orientation.x = q.x();
00144       pose.pose.orientation.y = q.y();
00145       pose.pose.orientation.z = q.z();
00146       pose.pose.orientation.w = q.w();
00147 
00148       path.poses.push_back(pose);
00149     }
00150     pub_path_.publish(path);
00151   }
00152   void publishPathVelocity(const std::vector<Eigen::Vector4d>& poses)
00153   {
00154     trajectory_tracker_msgs::PathWithVelocity path;
00155     path.header.frame_id = "odom";
00156     path.header.stamp = ros::Time::now();
00157 
00158     for (const Eigen::Vector4d& p : poses)
00159     {
00160       const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
00161 
00162       trajectory_tracker_msgs::PoseStampedWithVelocity pose;
00163       pose.header.frame_id = path.header.frame_id;
00164       pose.pose.position.x = p[0];
00165       pose.pose.position.y = p[1];
00166       pose.pose.orientation.x = q.x();
00167       pose.pose.orientation.y = q.y();
00168       pose.pose.orientation.z = q.z();
00169       pose.pose.orientation.w = q.w();
00170       pose.linear_velocity.x = p[3];
00171 
00172       path.poses.push_back(pose);
00173     }
00174     // needs sleep to prevent that the empty path from initState arrives later.
00175     ros::Duration(0.5).sleep();
00176     pub_path_vel_.publish(path);
00177   }
00178   void publishTransform()
00179   {
00180     const Eigen::Quaterniond q(Eigen::AngleAxisd(yaw_, Eigen::Vector3d(0, 0, 1)));
00181     geometry_msgs::TransformStamped trans;
00182     trans.header.frame_id = "odom";
00183     trans.header.stamp = ros::Time::now() + ros::Duration(0.1);
00184     trans.child_frame_id = "base_link";
00185     trans.transform.translation.x = pos_[0];
00186     trans.transform.translation.y = pos_[1];
00187     trans.transform.rotation.x = q.x();
00188     trans.transform.rotation.y = q.y();
00189     trans.transform.rotation.z = q.z();
00190     trans.transform.rotation.w = q.w();
00191     tfb_.sendTransform(trans);
00192   }
00193 };
00194 
00195 TEST_F(TrajectoryTrackerTest, StraightStop)
00196 {
00197   initState(Eigen::Vector2d(0, 0), 0);
00198 
00199   std::vector<Eigen::Vector3d> poses;
00200   for (double x = 0.0; x < 0.5; x += 0.01)
00201     poses.push_back(Eigen::Vector3d(x, 0.0, 0.0));
00202   poses.push_back(Eigen::Vector3d(0.5, 0.0, 0.0));
00203   publishPath(poses);
00204 
00205   waitUntilStart();
00206 
00207   ros::Rate rate(50);
00208   const ros::Time start = ros::Time::now();
00209   while (ros::ok())
00210   {
00211     ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
00212 
00213     publishTransform();
00214     rate.sleep();
00215     ros::spinOnce();
00216     if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00217       break;
00218   }
00219   for (int i = 0; i < 25; ++i)
00220   {
00221     publishTransform();
00222     rate.sleep();
00223     ros::spinOnce();
00224   }
00225 
00226   ASSERT_NEAR(yaw_, 0.0, 1e-2);
00227   ASSERT_NEAR(pos_[0], 0.5, 1e-2);
00228   ASSERT_NEAR(pos_[1], 0.0, 1e-2);
00229 }
00230 
00231 TEST_F(TrajectoryTrackerTest, StraightStopConvergence)
00232 {
00233   const double vels[] =
00234       {
00235         0.02, 0.05, 0.1, 0.2, 0.5, 1.0
00236       };
00237   const double path_length = 2.0;
00238   for (const double vel : vels)
00239   {
00240     const std::string info_message = "linear vel: " + std::to_string(vel);
00241 
00242     initState(Eigen::Vector2d(0, 0.01), 0);
00243 
00244     std::vector<Eigen::Vector4d> poses;
00245     for (double x = 0.0; x < path_length; x += 0.01)
00246       poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, vel));
00247     poses.push_back(Eigen::Vector4d(path_length, 0.0, 0.0, vel));
00248     publishPathVelocity(poses);
00249 
00250     waitUntilStart();
00251 
00252     ros::Rate rate(50);
00253     const ros::Time start = ros::Time::now();
00254     while (ros::ok())
00255     {
00256       ASSERT_LT(ros::Time::now() - start, ros::Duration(5.0 + path_length / vel)) << info_message;
00257 
00258       publishTransform();
00259       rate.sleep();
00260       ros::spinOnce();
00261       if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00262         break;
00263     }
00264     for (int i = 0; i < 25; ++i)
00265     {
00266       publishTransform();
00267       rate.sleep();
00268       ros::spinOnce();
00269     }
00270 
00271     EXPECT_NEAR(yaw_, 0.0, 1e-2) << info_message;
00272     EXPECT_NEAR(pos_[0], path_length, 1e-2) << info_message;
00273     EXPECT_NEAR(pos_[1], 0.0, 1e-2) << info_message;
00274   }
00275 }
00276 
00277 TEST_F(TrajectoryTrackerTest, StraightVelocityChange)
00278 {
00279   initState(Eigen::Vector2d(0, 0), 0);
00280 
00281   std::vector<Eigen::Vector4d> poses;
00282   for (double x = 0.0; x < 0.5; x += 0.01)
00283     poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, 0.3));
00284   for (double x = 0.5; x < 1.5; x += 0.01)
00285     poses.push_back(Eigen::Vector4d(x, 0.0, 0.0, 0.5));
00286   poses.push_back(Eigen::Vector4d(1.5, 0.0, 0.0, 0.5));
00287   publishPathVelocity(poses);
00288 
00289   waitUntilStart();
00290 
00291   ros::Rate rate(50);
00292   const ros::Time start = ros::Time::now();
00293   while (ros::ok())
00294   {
00295     ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
00296 
00297     publishTransform();
00298     rate.sleep();
00299     ros::spinOnce();
00300     if (0.3 < pos_[0] && pos_[0] < 0.4)
00301     {
00302       ASSERT_NEAR(cmd_vel_->linear.x, 0.3, 1e-2);
00303     }
00304     else if (0.9 < pos_[0] && pos_[0] < 1.0)
00305     {
00306       ASSERT_NEAR(cmd_vel_->linear.x, 0.5, 1e-2);
00307     }
00308 
00309     if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00310       break;
00311   }
00312   for (int i = 0; i < 25; ++i)
00313   {
00314     publishTransform();
00315     rate.sleep();
00316     ros::spinOnce();
00317   }
00318 
00319   ASSERT_NEAR(yaw_, 0.0, 1e-2);
00320   ASSERT_NEAR(pos_[0], 1.5, 1e-2);
00321   ASSERT_NEAR(pos_[1], 0.0, 1e-2);
00322 }
00323 
00324 TEST_F(TrajectoryTrackerTest, CurveFollow)
00325 {
00326   initState(Eigen::Vector2d(0, 0), 0);
00327 
00328   std::vector<Eigen::Vector3d> poses;
00329   Eigen::Vector3d p(0.0, 0.0, 0.0);
00330   for (double t = 0.0; t < 1.0; t += 0.01)
00331   {
00332     p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.005);
00333     poses.push_back(p);
00334   }
00335   for (double t = 0.0; t < 1.0; t += 0.01)
00336   {
00337     p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.0);
00338     poses.push_back(p);
00339   }
00340   publishPath(poses);
00341 
00342   waitUntilStart();
00343 
00344   ros::Rate rate(50);
00345   const ros::Time start = ros::Time::now();
00346   while (ros::ok())
00347   {
00348     ASSERT_LT(ros::Time::now() - start, ros::Duration(20.0));
00349 
00350     publishTransform();
00351     rate.sleep();
00352     ros::spinOnce();
00353     if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00354       break;
00355   }
00356   for (int i = 0; i < 25; ++i)
00357   {
00358     publishTransform();
00359     rate.sleep();
00360     ros::spinOnce();
00361   }
00362 
00363   ASSERT_NEAR(yaw_, p[2], 1e-2);
00364   ASSERT_NEAR(pos_[0], p[0], 1e-1);
00365   ASSERT_NEAR(pos_[1], p[1], 1e-1);
00366 }
00367 
00368 TEST_F(TrajectoryTrackerTest, InPlaceTurn)
00369 {
00370   const float init_yaw_array[] =
00371       {
00372         0.0,
00373         3.0
00374       };
00375   for (const float init_yaw : init_yaw_array)
00376   {
00377     initState(Eigen::Vector2d(0, 0), init_yaw);
00378 
00379     const float target_angle_array[] =
00380         {
00381           0.5,
00382           -0.5
00383         };
00384     for (const float ang : target_angle_array)
00385     {
00386       std::vector<Eigen::Vector3d> poses;
00387       poses.push_back(Eigen::Vector3d(0.0, 0.0, init_yaw + ang));
00388       publishPath(poses);
00389 
00390       waitUntilStart();
00391 
00392       ros::Rate rate(50);
00393       const ros::Time start = ros::Time::now();
00394       while (ros::ok())
00395       {
00396         ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
00397 
00398         publishTransform();
00399         rate.sleep();
00400         ros::spinOnce();
00401 
00402         if (cmd_vel_)
00403         {
00404           ASSERT_GT(cmd_vel_->angular.z * ang, -1e-2);
00405         }
00406         if (status_)
00407         {
00408           ASSERT_LT(status_->angle_remains * ang, 1e-2);
00409         }
00410 
00411         if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00412           break;
00413       }
00414       ASSERT_TRUE(static_cast<bool>(cmd_vel_));
00415       for (int i = 0; i < 25; ++i)
00416       {
00417         publishTransform();
00418         rate.sleep();
00419         ros::spinOnce();
00420       }
00421 
00422       ASSERT_NEAR(yaw_, init_yaw + ang, 1e-2);
00423     }
00424   }
00425 }
00426 
00427 TEST_F(TrajectoryTrackerTest, SwitchBack)
00428 {
00429   initState(Eigen::Vector2d(0, 0), 0);
00430 
00431   std::vector<Eigen::Vector3d> poses;
00432   Eigen::Vector3d p(0.0, 0.0, 0.0);
00433   for (double t = 0.0; t < 0.5; t += 0.01)
00434   {
00435     p -= Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, -0.01);
00436     poses.push_back(p);
00437   }
00438   for (double t = 0.0; t < 0.5; t += 0.01)
00439   {
00440     p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.01);
00441     poses.push_back(p);
00442   }
00443   publishPath(poses);
00444 
00445   waitUntilStart();
00446 
00447   ros::Rate rate(50);
00448   const ros::Time start = ros::Time::now();
00449   while (ros::ok())
00450   {
00451     ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
00452 
00453     publishTransform();
00454     rate.sleep();
00455     ros::spinOnce();
00456     if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00457       break;
00458   }
00459   for (int i = 0; i < 25; ++i)
00460   {
00461     publishTransform();
00462     rate.sleep();
00463     ros::spinOnce();
00464   }
00465 
00466   ASSERT_NEAR(yaw_, p[2], 1e-2);
00467   ASSERT_NEAR(pos_[0], p[0], 1e-1);
00468   ASSERT_NEAR(pos_[1], p[1], 1e-1);
00469 }
00470 
00471 TEST_F(TrajectoryTrackerTest, SwitchBackWithPathUpdate)
00472 {
00473   initState(Eigen::Vector2d(0, 0), 0);
00474 
00475   std::vector<Eigen::Vector3d> poses;
00476   std::vector<Eigen::Vector3d> poses_second_half;
00477   Eigen::Vector3d p(0.0, 0.0, 0.0);
00478   for (double t = 0.0; t < 0.5; t += 0.01)
00479   {
00480     p -= Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, -0.01);
00481     poses.push_back(p);
00482   }
00483   const Eigen::Vector2d pos_local_goal = p.head<2>();
00484   for (double t = 0.0; t < 1.0; t += 0.01)
00485   {
00486     p += Eigen::Vector3d(std::cos(p[2]) * 0.05, std::sin(p[2]) * 0.05, 0.01);
00487     poses.push_back(p);
00488     poses_second_half.push_back(p);
00489   }
00490   publishPath(poses);
00491 
00492   waitUntilStart();
00493 
00494   int cnt_arrive_local_goal(0);
00495   ros::Rate rate(50);
00496   const ros::Time start = ros::Time::now();
00497   while (ros::ok())
00498   {
00499     ASSERT_LT(ros::Time::now() - start, ros::Duration(10.0));
00500 
00501     publishTransform();
00502     rate.sleep();
00503     ros::spinOnce();
00504     if (status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL)
00505       break;
00506 
00507     if ((pos_local_goal - pos_).norm() < 0.1)
00508       cnt_arrive_local_goal++;
00509 
00510     if (cnt_arrive_local_goal > 25)
00511       publishPath(poses_second_half);
00512     else
00513       publishPath(poses);
00514   }
00515   for (int i = 0; i < 25; ++i)
00516   {
00517     publishTransform();
00518     rate.sleep();
00519     ros::spinOnce();
00520   }
00521 
00522   ASSERT_NEAR(yaw_, p[2], 1e-2);
00523   ASSERT_NEAR(pos_[0], p[0], 1e-1);
00524   ASSERT_NEAR(pos_[1], p[1], 1e-1);
00525 }
00526 
00527 void timeSource()
00528 {
00529   ros::NodeHandle nh("/");
00530   bool use_sim_time;
00531   nh.param("/use_sim_time", use_sim_time, false);
00532   if (!use_sim_time)
00533     return;
00534 
00535   ros::Publisher pub = nh.advertise<rosgraph_msgs::Clock>("clock", 1);
00536 
00537   ros::WallRate rate(500.0);  // 500% speed
00538   ros::WallTime time = ros::WallTime::now();
00539   while (ros::ok())
00540   {
00541     rosgraph_msgs::Clock clock;
00542     clock.clock.fromNSec(time.toNSec());
00543     pub.publish(clock);
00544     rate.sleep();
00545     time += ros::WallDuration(0.01);
00546   }
00547 }
00548 
00549 int main(int argc, char** argv)
00550 {
00551   testing::InitGoogleTest(&argc, argv);
00552   ros::init(argc, argv, "test_trajectory_tracker");
00553 
00554   boost::thread time_thread(timeSource);
00555 
00556   return RUN_ALL_TESTS();
00557 }


trajectory_tracker
Author(s): Atsushi Watanabe
autogenerated on Sat Jun 22 2019 20:07:25