trajectory_tracker_test.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-2020, 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 #ifndef TRAJECTORY_TRACKER_TEST_H
31 #define TRAJECTORY_TRACKER_TEST_H
32 
33 #include <algorithm>
34 #include <string>
35 #include <vector>
36 #include <list>
37 
38 #include <boost/thread.hpp>
39 
40 #include <Eigen/Core>
41 #include <Eigen/Geometry>
42 
43 #include <ros/ros.h>
44 
45 #include <geometry_msgs/Twist.h>
46 #include <nav_msgs/Odometry.h>
47 #include <nav_msgs/Path.h>
48 #include <rosgraph_msgs/Clock.h>
51 
52 #include <trajectory_tracker_msgs/PathWithVelocity.h>
53 #include <trajectory_tracker_msgs/TrajectoryTrackerStatus.h>
54 
55 #include <gtest/gtest.h>
56 
57 class TrajectoryTrackerTest : public ::testing::Test
58 {
59 private:
70 
73 
74  std::list<nav_msgs::Odometry> odom_buffer_;
75 
76 protected:
77  std_msgs::Header last_path_header_;
78  double error_lin_;
80  double error_ang_;
81 
82 private:
83  void cbStatus(const trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr& msg)
84  {
85  status_ = msg;
86  }
87  void cbCmdVel(const geometry_msgs::Twist::ConstPtr& msg)
88  {
89  const ros::Time now = ros::Time::now();
90  if (cmd_vel_time_ == ros::Time(0))
91  cmd_vel_time_ = now;
92  const float dt = std::min((now - cmd_vel_time_).toSec(), 0.1);
93 
94  yaw_ += msg->angular.z * dt;
95  pos_ += Eigen::Vector2d(std::cos(yaw_), std::sin(yaw_)) * msg->linear.x * dt;
96  cmd_vel_time_ = now;
97  cmd_vel_ = msg;
99  }
100 
101 public:
102  Eigen::Vector2d pos_;
103  double yaw_;
104  trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr status_;
105  geometry_msgs::Twist::ConstPtr cmd_vel_;
107 
109  : nh_("")
110  , pnh_("~")
111  {
112  sub_cmd_vel_ = nh_.subscribe(
113  "cmd_vel", 1, &TrajectoryTrackerTest::cbCmdVel, this);
114  sub_status_ = nh_.subscribe(
115  "trajectory_tracker/status", 1, &TrajectoryTrackerTest::cbStatus, this);
116  pub_path_ = nh_.advertise<nav_msgs::Path>("path", 1, true);
117  pub_path_vel_ = nh_.advertise<trajectory_tracker_msgs::PathWithVelocity>("path_velocity", 1, true);
118  pub_odom_ = nh_.advertise<nav_msgs::Odometry>("odom", 10, true);
119 
120  double delay;
121  pnh_.param("odom_delay", delay, 0.0);
122  delay_ = ros::Duration(delay);
123  pnh_.param("error_lin", error_lin_, 0.01);
124  pnh_.param("error_large_lin", error_large_lin_, 0.1);
125  pnh_.param("error_ang", error_ang_, 0.01);
126 
127  ros::Rate wait(10);
128  for (size_t i = 0; i < 100; ++i)
129  {
130  wait.sleep();
131  ros::spinOnce();
132  if (pub_path_.getNumSubscribers() > 0)
133  break;
134  }
135  }
136  void initState(const Eigen::Vector2d& pos, const float yaw)
137  {
138  // Wait trajectory_tracker node
139  ros::Rate rate(10);
140  const auto start = ros::WallTime::now();
141  while (ros::ok())
142  {
143  nav_msgs::Path path;
144  path.header.frame_id = "odom";
145  path.header.stamp = ros::Time::now();
146  pub_path_.publish(path);
147 
148  yaw_ = yaw;
149  pos_ = pos;
151 
152  rate.sleep();
153  ros::spinOnce();
154  if (status_ &&
155  status_->status != trajectory_tracker_msgs::TrajectoryTrackerStatus::FOLLOWING)
156  break;
157  ASSERT_LT(ros::WallTime::now(), start + ros::WallDuration(10.0))
158  << "trajectory_tracker status timeout, status: "
159  << (status_ ? std::to_string(static_cast<int>(status_->status)) : "none");
160  }
161  }
162  void waitUntilStart(const std::function<void()> func = nullptr)
163  {
164  ros::Rate rate(50);
165  const auto start = ros::WallTime::now();
166  while (ros::ok())
167  {
168  if (func)
169  func();
170 
172  rate.sleep();
173  ros::spinOnce();
174  if (status_ &&
175  status_->status == trajectory_tracker_msgs::TrajectoryTrackerStatus::FOLLOWING)
176  break;
177  ASSERT_LT(ros::WallTime::now(), start + ros::WallDuration(10.0))
178  << "trajectory_tracker status timeout, status: "
179  << (status_ ? std::to_string(static_cast<int>(status_->status)) : "none");
180  }
181  initial_cmd_vel_time_ = ros::Time::now();
182  cmd_vel_count_ = 0;
183  }
184  void publishPath(const std::vector<Eigen::Vector3d>& poses)
185  {
186  nav_msgs::Path path;
187  path.header.frame_id = "odom";
188  path.header.stamp = ros::Time::now();
189 
190  for (const Eigen::Vector3d& p : poses)
191  {
192  const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
193 
194  geometry_msgs::PoseStamped pose;
195  pose.header.frame_id = path.header.frame_id;
196  pose.pose.position.x = p[0];
197  pose.pose.position.y = p[1];
198  pose.pose.orientation.x = q.x();
199  pose.pose.orientation.y = q.y();
200  pose.pose.orientation.z = q.z();
201  pose.pose.orientation.w = q.w();
202 
203  path.poses.push_back(pose);
204  }
205  pub_path_.publish(path);
206  last_path_header_ = path.header;
207  }
208  void publishPathVelocity(const std::vector<Eigen::Vector4d>& poses)
209  {
210  trajectory_tracker_msgs::PathWithVelocity path;
211  path.header.frame_id = "odom";
212  path.header.stamp = ros::Time::now();
213 
214  for (const Eigen::Vector4d& p : poses)
215  {
216  const Eigen::Quaterniond q(Eigen::AngleAxisd(p[2], Eigen::Vector3d(0, 0, 1)));
217 
218  trajectory_tracker_msgs::PoseStampedWithVelocity pose;
219  pose.header.frame_id = path.header.frame_id;
220  pose.pose.position.x = p[0];
221  pose.pose.position.y = p[1];
222  pose.pose.orientation.x = q.x();
223  pose.pose.orientation.y = q.y();
224  pose.pose.orientation.z = q.z();
225  pose.pose.orientation.w = q.w();
226  pose.linear_velocity.x = p[3];
227 
228  path.poses.push_back(pose);
229  }
230  // needs sleep to prevent that the empty path from initState arrives later.
231  ros::Duration(0.5).sleep();
232  pub_path_vel_.publish(path);
233  last_path_header_ = path.header;
234  }
236  {
237  const ros::Time now = ros::Time::now();
238 
239  const Eigen::Quaterniond q(Eigen::AngleAxisd(yaw_, Eigen::Vector3d(0, 0, 1)));
240 
241  nav_msgs::Odometry odom;
242  odom.header.frame_id = "odom";
243  odom.header.stamp = now;
244  odom.child_frame_id = "base_link";
245  odom.pose.pose.position.x = pos_[0];
246  odom.pose.pose.position.y = pos_[1];
247  odom.pose.pose.position.z = 0.0;
248  odom.pose.pose.orientation.x = q.x();
249  odom.pose.pose.orientation.y = q.y();
250  odom.pose.pose.orientation.z = q.z();
251  odom.pose.pose.orientation.w = q.w();
252  if (cmd_vel_)
253  {
254  odom.twist.twist.linear = cmd_vel_->linear;
255  odom.twist.twist.angular = cmd_vel_->angular;
256  }
257 
258  odom_buffer_.push_back(odom);
259 
260  const ros::Time pub_time = now - delay_;
261 
262  while (odom_buffer_.size() > 0)
263  {
264  nav_msgs::Odometry odom = odom_buffer_.front();
265  if (odom.header.stamp > pub_time)
266  break;
267 
268  odom_buffer_.pop_front();
269 
270  if (odom.header.stamp != trans_stamp_last_)
271  {
272  geometry_msgs::TransformStamped trans;
273  trans.header = odom.header;
274  trans.header.stamp += ros::Duration(0.1);
275  trans.child_frame_id = odom.child_frame_id;
276  trans.transform.translation.x = odom.pose.pose.position.x;
277  trans.transform.translation.y = odom.pose.pose.position.y;
278  trans.transform.rotation.x = odom.pose.pose.orientation.x;
279  trans.transform.rotation.y = odom.pose.pose.orientation.y;
280  trans.transform.rotation.z = odom.pose.pose.orientation.z;
281  trans.transform.rotation.w = odom.pose.pose.orientation.w;
282 
283  tfb_.sendTransform(trans);
284  pub_odom_.publish(odom);
285  }
286  trans_stamp_last_ = odom.header.stamp;
287  }
288  }
289  double getCmdVelFrameRate() const
290  {
291  return cmd_vel_count_ / (cmd_vel_time_ - initial_cmd_vel_time_).toSec();
292  }
293 };
294 
295 #endif // TRAJECTORY_TRACKER_TEST_H
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())
bool sleep() const
std_msgs::Header last_path_header_
void cbCmdVel(const geometry_msgs::Twist::ConstPtr &msg)
std::list< nav_msgs::Odometry > odom_buffer_
trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr status_
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
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)
uint32_t getNumSubscribers() const
static WallTime now()
void cbStatus(const trajectory_tracker_msgs::TrajectoryTrackerStatus::ConstPtr &msg)
static Time now()
void waitUntilStart(const std::function< void()> func=nullptr)
ROSCPP_DECL void spinOnce()


trajectory_tracker
Author(s): Atsushi Watanabe
autogenerated on Wed May 12 2021 02:20:40