trajectory_tracker.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, ATR, Atsushi Watanabe
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 /*
31  * This research was supported by a contract with the Ministry of Internal
32  Affairs and Communications entitled, 'Novel and innovative R&D making use
33  of brain structures'
34 
35  This software was implemented to accomplish the above research.
36  Original idea of the implemented control scheme was published on:
37  S. Iida, S. Yuta, "Vehicle command system and trajectory control for
38  autonomous mobile robots," in Proceedings of the 1991 IEEE/RSJ
39  International Workshop on Intelligent Robots and Systems (IROS),
40  1991, pp. 212-217.
41  */
42 
43 #include <algorithm>
44 #include <cmath>
45 #include <limits>
46 #include <string>
47 #include <vector>
48 
49 #include <Eigen/Core>
50 #include <Eigen/Geometry>
51 
52 #include <ros/ros.h>
53 
54 #include <dynamic_reconfigure/server.h>
55 #include <geometry_msgs/PoseStamped.h>
56 #include <geometry_msgs/Twist.h>
57 #include <nav_msgs/Odometry.h>
58 #include <nav_msgs/Path.h>
59 #include <std_msgs/Float32.h>
60 #include <std_msgs/Header.h>
61 
62 #include <tf2/utils.h>
65 
67 #include <trajectory_tracker_msgs/PathWithVelocity.h>
68 #include <trajectory_tracker_msgs/TrajectoryTrackerStatus.h>
69 
70 #include <trajectory_tracker/TrajectoryTrackerConfig.h>
74 
75 namespace trajectory_tracker
76 {
78 {
79 public:
80  TrackerNode();
81  ~TrackerNode();
82  void spin();
83 
84 private:
85  std::string topic_path_;
86  std::string topic_cmd_vel_;
87  std::string frame_robot_;
88  std::string frame_odom_;
89  double hz_;
90  double look_forward_;
91  double curv_forward_;
92  double k_[3];
93  double gain_at_vel_;
94  double d_lim_;
95  double d_stop_;
96  double vel_[2];
97  double acc_[2];
98  double acc_toc_[2];
101  double rotate_ang_;
113  double epsilon_;
114  double max_dt_;
119 
133 
135  std_msgs::Header path_header_;
137 
138  mutable boost::recursive_mutex parameter_server_mutex_;
139  dynamic_reconfigure::Server<TrajectoryTrackerConfig> parameter_server_;
140 
141  bool use_odom_;
144 
146  {
147  explicit TrackingResult(const int s)
148  : status(s)
149  , distance_remains(0.0)
150  , angle_remains(0.0)
151  , distance_remains_raw(0.0)
152  , angle_remains_raw(0.0)
153  , turning_in_place(false)
154  , signed_local_distance(0.0)
155  , distance_from_target(0.0)
156  , target_linear_vel(0.0)
157  , tracking_point_x(0.0)
158  , tracking_point_y(0.0)
159  , tracking_point_curv(0.0)
160  , path_step_done(0)
161  {
162  }
163 
164  int status; // same as trajectory_tracker_msgs::TrajectoryTrackerStatus::status
167  double distance_remains_raw; // remained distance without prediction
177  };
178 
179  template <typename MSG_TYPE>
180  void cbPath(const typename MSG_TYPE::ConstPtr&);
181  void cbSpeed(const std_msgs::Float32::ConstPtr&);
182  void cbOdometry(const nav_msgs::Odometry::ConstPtr&);
183  void cbTimer(const ros::TimerEvent&);
184  void cbOdomTimeout(const ros::TimerEvent&);
185  void control(const tf2::Stamped<tf2::Transform>&, const Eigen::Vector3d&, const double);
187  const tf2::Stamped<tf2::Transform>&, const Eigen::Vector3d&) const;
188  void cbParameter(const TrajectoryTrackerConfig& config, const uint32_t /* level */);
189 };
190 
192  : nh_()
193  , pnh_("~")
194  , tfl_(tfbuf_)
195  , is_path_updated_(false)
196 {
198  pnh_.param("frame_robot", frame_robot_, std::string("base_link"));
199  pnh_.param("frame_odom", frame_odom_, std::string("odom"));
200  neonavigation_common::compat::deprecatedParam(pnh_, "path", topic_path_, std::string("path"));
201  neonavigation_common::compat::deprecatedParam(pnh_, "cmd_vel", topic_cmd_vel_, std::string("cmd_vel"));
202  pnh_.param("hz", hz_, 50.0);
203  pnh_.param("use_odom", use_odom_, false);
204  pnh_.param("predict_odom", predict_odom_, true);
205  pnh_.param("max_dt", max_dt_, 0.1);
206  pnh_.param("odom_timeout_sec", odom_timeout_sec_, 0.1);
207 
208  sub_path_ = neonavigation_common::compat::subscribe<nav_msgs::Path>(
209  nh_, "path",
210  pnh_, topic_path_, 2,
211  boost::bind(&TrackerNode::cbPath<nav_msgs::Path>, this, _1));
212  sub_path_velocity_ = nh_.subscribe<trajectory_tracker_msgs::PathWithVelocity>(
213  "path_velocity", 2,
214  boost::bind(&TrackerNode::cbPath<trajectory_tracker_msgs::PathWithVelocity>, this, _1));
216  nh_, "speed",
217  pnh_, "speed", 20, &TrackerNode::cbSpeed, this);
218  pub_vel_ = neonavigation_common::compat::advertise<geometry_msgs::Twist>(
219  nh_, "cmd_vel",
220  pnh_, topic_cmd_vel_, 10);
221  pub_status_ = pnh_.advertise<trajectory_tracker_msgs::TrajectoryTrackerStatus>("status", 10, true);
222  pub_tracking_ = pnh_.advertise<geometry_msgs::PoseStamped>("tracking", 10, true);
223  if (use_odom_)
224  {
225  sub_odom_ = nh_.subscribe<nav_msgs::Odometry>("odom", 10, &TrackerNode::cbOdometry, this,
227  }
228 
229  boost::recursive_mutex::scoped_lock lock(parameter_server_mutex_);
230  parameter_server_.setCallback(boost::bind(&TrackerNode::cbParameter, this, _1, _2));
231 }
232 
233 void TrackerNode::cbParameter(const TrajectoryTrackerConfig& config, const uint32_t /* level */)
234 {
235  boost::recursive_mutex::scoped_lock lock(parameter_server_mutex_);
236  look_forward_ = config.look_forward;
237  curv_forward_ = config.curv_forward;
238  k_[0] = config.k_dist;
239  k_[1] = config.k_ang;
240  k_[2] = config.k_avel;
241  gain_at_vel_ = config.gain_at_vel;
242  d_lim_ = config.dist_lim;
243  d_stop_ = config.dist_stop;
244  rotate_ang_ = config.rotate_ang;
245  vel_[0] = config.max_vel;
246  vel_[1] = config.max_angvel;
247  acc_[0] = config.max_acc;
248  acc_[1] = config.max_angacc;
249  acc_toc_[0] = acc_[0] * config.acc_toc_factor;
250  acc_toc_[1] = acc_[1] * config.angacc_toc_factor;
251  path_step_ = config.path_step;
252  goal_tolerance_dist_ = config.goal_tolerance_dist;
253  goal_tolerance_ang_ = config.goal_tolerance_ang;
254  stop_tolerance_dist_ = config.stop_tolerance_dist;
255  stop_tolerance_ang_ = config.stop_tolerance_ang;
256  no_pos_cntl_dist_ = config.no_position_control_dist;
257  min_track_path_ = config.min_tracking_path;
258  allow_backward_ = config.allow_backward;
259  limit_vel_by_avel_ = config.limit_vel_by_avel;
260  check_old_path_ = config.check_old_path;
261  epsilon_ = config.epsilon;
262  use_time_optimal_control_ = config.use_time_optimal_control;
263  time_optimal_control_future_gain_ = config.time_optimal_control_future_gain;
264  k_ang_rotation_ = config.k_ang_rotation;
265  k_avel_rotation_ = config.k_avel_rotation;
266 }
267 
269 {
270  geometry_msgs::Twist cmd_vel;
271  cmd_vel.linear.x = 0;
272  cmd_vel.angular.z = 0;
273  pub_vel_.publish(cmd_vel);
274 }
275 
276 void TrackerNode::cbSpeed(const std_msgs::Float32::ConstPtr& msg)
277 {
278  vel_[0] = msg->data;
279 }
280 
281 namespace
282 {
283 float getVelocity(const geometry_msgs::PoseStamped& msg)
284 {
285  return std::numeric_limits<float>::quiet_NaN();
286 }
287 float getVelocity(const trajectory_tracker_msgs::PoseStampedWithVelocity& msg)
288 {
289  return msg.linear_velocity.x;
290 }
291 } // namespace
292 
293 template <typename MSG_TYPE>
294 void TrackerNode::cbPath(const typename MSG_TYPE::ConstPtr& msg)
295 {
296  path_header_ = msg->header;
297  path_.clear();
298  is_path_updated_ = true;
299  path_step_done_ = 0;
300  if (msg->poses.size() == 0)
301  return;
302 
303  trajectory_tracker::Pose2D in_place_turn_end;
304  bool in_place_turning = false;
305 
306  auto j = msg->poses.begin();
307  path_.push_back(trajectory_tracker::Pose2D(j->pose, getVelocity(*j)));
308  for (++j; j < msg->poses.end(); ++j)
309  {
310  const float velocity = getVelocity(*j);
311  if (std::isfinite(velocity) && velocity < -0.0)
312  {
313  ROS_ERROR_THROTTLE(1.0, "path_velocity.velocity.x must be positive");
314  path_.clear();
315  return;
316  }
317  const trajectory_tracker::Pose2D next(j->pose, velocity);
318 
319  if ((path_.back().pos_ - next.pos_).squaredNorm() >= std::pow(epsilon_, 2))
320  {
321  if (in_place_turning)
322  {
323  path_.push_back(in_place_turn_end);
324  in_place_turning = false;
325  }
326  path_.push_back(next);
327  }
328  else
329  {
330  in_place_turn_end = trajectory_tracker::Pose2D(
331  path_.back().pos_, next.yaw_, next.velocity_);
332  in_place_turning = true;
333  }
334  }
335  if (in_place_turning)
336  path_.push_back(in_place_turn_end);
337 }
338 
339 void TrackerNode::cbOdometry(const nav_msgs::Odometry::ConstPtr& odom)
340 {
341  if (odom->header.frame_id != frame_odom_)
342  {
343  ROS_WARN("frame_odom is invalid. Update from \"%s\" to \"%s\"", frame_odom_.c_str(), odom->header.frame_id.c_str());
344  frame_odom_ = odom->header.frame_id;
345  }
346  if (odom->child_frame_id != frame_robot_)
347  {
348  ROS_WARN("frame_robot is invalid. Update from \"%s\" to \"%s\"",
349  frame_robot_.c_str(), odom->child_frame_id.c_str());
350  frame_robot_ = odom->child_frame_id;
351  }
352  if (odom_timeout_sec_ != 0.0)
353  {
355  {
357  }
358  else
359  {
362  }
363  }
364 
365  if (prev_odom_stamp_ != ros::Time())
366  {
367  const double dt = std::min(max_dt_, (odom->header.stamp - prev_odom_stamp_).toSec());
368  nav_msgs::Odometry odom_compensated = *odom;
369  Eigen::Vector3d prediction_offset(0, 0, 0);
370  if (predict_odom_)
371  {
372  const double predict_dt = std::max(0.0, std::min(max_dt_, (ros::Time::now() - odom->header.stamp).toSec()));
373  tf2::Transform trans;
374  const tf2::Quaternion rotation(tf2::Vector3(0, 0, 1), odom->twist.twist.angular.z * predict_dt);
375  const tf2::Vector3 translation(odom->twist.twist.linear.x * predict_dt, 0, 0);
376 
377  prediction_offset[0] = odom->twist.twist.linear.x * predict_dt;
378  prediction_offset[1] = odom->twist.twist.angular.z * predict_dt;
379 
380  tf2::fromMsg(odom->pose.pose, trans);
381  trans.setOrigin(trans.getOrigin() + tf2::Transform(trans.getRotation()) * translation);
382  trans.setRotation(trans.getRotation() * rotation);
383  tf2::toMsg(trans, odom_compensated.pose.pose);
384  }
385 
386  tf2::Transform odom_to_robot;
387  tf2::fromMsg(odom_compensated.pose.pose, odom_to_robot);
388  const tf2::Stamped<tf2::Transform> robot_to_odom(
389  odom_to_robot.inverse(),
390  odom->header.stamp, odom->header.frame_id);
391 
392  control(robot_to_odom, prediction_offset, dt);
393  }
394  prev_odom_stamp_ = odom->header.stamp;
395 }
396 
398 {
399  try
400  {
402  tf2::fromMsg(
404  control(transform, Eigen::Vector3d(0, 0, 0), 1.0 / hz_);
405  }
406  catch (tf2::TransformException& e)
407  {
408  ROS_WARN("TF exception: %s", e.what());
409  trajectory_tracker_msgs::TrajectoryTrackerStatus status;
410  status.header.stamp = ros::Time::now();
411  status.distance_remains = 0.0;
412  status.angle_remains = 0.0;
413  status.path_header = path_header_;
414  status.status = trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH;
415  pub_status_.publish(status);
416  return;
417  }
418 }
419 
421 {
422  ROS_WARN_STREAM("Odometry timeout. Last odometry stamp: " << prev_odom_stamp_);
423  v_lim_.clear();
424  w_lim_.clear();
425  geometry_msgs::Twist cmd_vel;
426  cmd_vel.linear.x = 0.0;
427  cmd_vel.angular.z = 0.0;
428  pub_vel_.publish(cmd_vel);
429 
430  trajectory_tracker_msgs::TrajectoryTrackerStatus status;
431  status.header.stamp = ros::Time::now();
432  status.distance_remains = 0.0;
433  status.angle_remains = 0.0;
434  status.path_header = path_header_;
435  status.status = trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH;
436  pub_status_.publish(status);
437 }
438 
440 {
441  ros::Timer timer;
442  if (!use_odom_)
443  {
444  timer = nh_.createTimer(ros::Duration(1.0 / hz_), &TrackerNode::cbTimer, this);
445  }
446  ros::spin();
447 }
448 
450  const tf2::Stamped<tf2::Transform>& robot_to_odom,
451  const Eigen::Vector3d& prediction_offset,
452  const double dt)
453 {
454  trajectory_tracker_msgs::TrajectoryTrackerStatus status;
455  status.header.stamp = ros::Time::now();
456  status.path_header = path_header_;
457  if (is_path_updated_)
458  {
459  // Call getTrackingResult to update path_step_done_.
460  const TrackingResult initial_tracking_result = getTrackingResult(robot_to_odom, prediction_offset);
461  path_step_done_ = initial_tracking_result.path_step_done;
462  is_path_updated_ = false;
463  }
464  const TrackingResult tracking_result = getTrackingResult(robot_to_odom, prediction_offset);
465  switch (tracking_result.status)
466  {
467  case trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH:
468  case trajectory_tracker_msgs::TrajectoryTrackerStatus::FAR_FROM_PATH:
469  {
470  v_lim_.clear();
471  w_lim_.clear();
472  geometry_msgs::Twist cmd_vel;
473  cmd_vel.linear.x = 0;
474  cmd_vel.angular.z = 0;
475  pub_vel_.publish(cmd_vel);
476  break;
477  }
478  default:
479  {
480  if (tracking_result.turning_in_place)
481  {
482  v_lim_.set(0.0, tracking_result.target_linear_vel, acc_[0], dt);
483 
485  {
486  const double expected_angle_remains =
487  tracking_result.angle_remains + w_lim_.get() * dt * time_optimal_control_future_gain_;
488  w_lim_.set(trajectory_tracker::timeOptimalControl(expected_angle_remains, acc_toc_[1]), vel_[1], acc_[1], dt);
489  }
490  else
491  {
492  const double wvel_increment =
493  (-tracking_result.angle_remains * k_ang_rotation_ - w_lim_.get() * k_avel_rotation_) * dt;
494  w_lim_.increment(wvel_increment, vel_[1], acc_[1], dt);
495  }
496  ROS_DEBUG(
497  "trajectory_tracker: angular residual %0.3f, angular vel %0.3f",
498  tracking_result.angle_remains, w_lim_.get());
499  }
500  else
501  {
502  v_lim_.set(
504  tracking_result.target_linear_vel, acc_[0], dt);
505 
506  float wref = std::abs(v_lim_.get()) * tracking_result.tracking_point_curv;
507 
508  if (limit_vel_by_avel_ && std::abs(wref) > vel_[1])
509  {
510  v_lim_.set(
511  std::copysign(1.0, v_lim_.get()) * std::abs(vel_[1] / tracking_result.tracking_point_curv),
512  tracking_result.target_linear_vel, acc_[0], dt);
513  wref = std::copysign(1.0, wref) * vel_[1];
514  }
515 
516  const double k_ang =
517  (gain_at_vel_ == 0.0) ? (k_[1]) : (k_[1] * tracking_result.target_linear_vel / gain_at_vel_);
518  const double dist_diff = tracking_result.distance_from_target;
519  const double angle_diff = tracking_result.angle_remains;
520  const double wvel_diff = w_lim_.get() - wref;
521  w_lim_.increment(dt * (-dist_diff * k_[0] - angle_diff * k_ang - wvel_diff * k_[2]), vel_[1], acc_[1], dt);
522 
523  ROS_DEBUG(
524  "trajectory_tracker: distance residual %0.3f, angular residual %0.3f, ang vel residual %0.3f"
525  ", v_lim %0.3f, w_lim %0.3f signed_local_distance %0.3f, k_ang %0.3f",
526  dist_diff, angle_diff, wvel_diff, v_lim_.get(), w_lim_.get(), tracking_result.signed_local_distance, k_ang);
527  }
528  if (std::abs(tracking_result.distance_remains) < stop_tolerance_dist_ &&
529  std::abs(tracking_result.angle_remains) < stop_tolerance_ang_ &&
530  std::abs(tracking_result.distance_remains_raw) < stop_tolerance_dist_ &&
531  std::abs(tracking_result.angle_remains_raw) < stop_tolerance_ang_)
532  {
533  v_lim_.clear();
534  w_lim_.clear();
535  }
536  geometry_msgs::Twist cmd_vel;
537  cmd_vel.linear.x = v_lim_.get();
538  cmd_vel.angular.z = w_lim_.get();
539  pub_vel_.publish(cmd_vel);
540  path_step_done_ = tracking_result.path_step_done;
541  break;
542  }
543  }
544  status.status = tracking_result.status;
545  status.distance_remains = tracking_result.distance_remains;
546  status.angle_remains = tracking_result.angle_remains;
547  pub_status_.publish(status);
548 }
549 
551  const tf2::Stamped<tf2::Transform>& robot_to_odom, const Eigen::Vector3d& prediction_offset) const
552 {
553  if (path_header_.frame_id.size() == 0 || path_.size() == 0)
554  {
555  return TrackingResult(trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH);
556  }
557  // Transform
559  double transform_delay = 0;
560  tf2::Stamped<tf2::Transform> transform = robot_to_odom;
561  try
562  {
563  tf2::Stamped<tf2::Transform> odom_to_path;
564  tf2::fromMsg(
565  tfbuf_.lookupTransform(frame_odom_, path_header_.frame_id, ros::Time(0)), odom_to_path);
566  transform *= odom_to_path;
567  transform_delay = (ros::Time::now() - transform.stamp_).toSec();
568  if (std::abs(transform_delay) > 0.1 && check_old_path_)
569  {
571  1.0, "Timestamp of the transform is too old %f %f",
572  ros::Time::now().toSec(), transform.stamp_.toSec());
573  }
574 
575  const float trans_yaw = tf2::getYaw(transform.getRotation());
576  const Eigen::Transform<double, 2, Eigen::TransformTraits::AffineCompact> trans =
577  Eigen::Translation2d(
578  Eigen::Vector2d(transform.getOrigin().x(), transform.getOrigin().y())) *
579  Eigen::Rotation2Dd(trans_yaw);
580 
581  for (size_t i = 0; i < path_.size(); i += path_step_)
582  lpath.push_back(
584  trans * path_[i].pos_, trans_yaw + path_[i].yaw_, path_[i].velocity_));
585  }
586  catch (tf2::TransformException& e)
587  {
588  ROS_WARN("TF exception: %s", e.what());
589  return TrackingResult(trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH);
590  }
591 
592  const Eigen::Vector2d origin_raw = prediction_offset.head<2>();
593  const float yaw_raw = prediction_offset[2];
594 
595  const float yaw_predicted = w_lim_.get() * look_forward_ / 2;
596  const Eigen::Vector2d origin =
597  Eigen::Vector2d(std::cos(yaw_predicted), std::sin(yaw_predicted)) * v_lim_.get() * look_forward_;
598 
599  const double path_length = lpath.length();
600 
601  // Find nearest line strip
602  const trajectory_tracker::Path2D::ConstIterator it_local_goal =
603  lpath.findLocalGoal(lpath.cbegin() + path_step_done_, lpath.cend(), allow_backward_);
604 
605  const float max_search_range = (path_step_done_ > 0) ? 1.0 : 0.0;
607  lpath.findNearest(lpath.cbegin() + path_step_done_, it_local_goal, origin,
608  max_search_range, epsilon_);
609 
610  if (it_nearest == lpath.end())
611  {
612  return TrackingResult(trajectory_tracker_msgs::TrajectoryTrackerStatus::NO_PATH);
613  }
614 
615  const int i_nearest = std::distance(lpath.cbegin(), it_nearest);
616  const int i_nearest_prev = std::max(0, i_nearest - 1);
617  const int i_local_goal = std::distance(lpath.cbegin(), it_local_goal);
618 
619  const Eigen::Vector2d pos_on_line =
620  trajectory_tracker::projection2d(lpath[i_nearest_prev].pos_, lpath[i_nearest].pos_, origin);
621  const Eigen::Vector2d pos_on_line_raw =
622  trajectory_tracker::projection2d(lpath[i_nearest_prev].pos_, lpath[i_nearest].pos_, origin_raw);
623 
624  const float linear_vel =
625  std::isnan(lpath[i_nearest].velocity_) ? vel_[0] : lpath[i_nearest].velocity_;
626 
627  // Remained distance to the local goal
628  float remain_local = lpath.remainedDistance(lpath.cbegin(), it_nearest, it_local_goal, pos_on_line);
629  // Remained distance to the final goal
630  float distance_remains = lpath.remainedDistance(lpath.cbegin(), it_nearest, lpath.cend(), pos_on_line);
631  float distance_remains_raw = lpath.remainedDistance(lpath.cbegin(), it_nearest, lpath.cend(), pos_on_line_raw);
632  if (path_length < no_pos_cntl_dist_)
633  distance_remains = distance_remains_raw = remain_local = 0;
634 
635  // Signed distance error
636  const float dist_err = trajectory_tracker::lineDistance(
637  lpath[i_nearest_prev].pos_, lpath[i_nearest].pos_, origin);
638 
639  // Angular error
640  const Eigen::Vector2d vec = lpath[i_nearest].pos_ - lpath[i_nearest_prev].pos_;
641  float angle_remains = -atan2(vec[1], vec[0]);
642  const float angle_pose = allow_backward_ ? lpath[i_nearest].yaw_ : -angle_remains;
643  float sign_vel = 1.0;
644  if (std::cos(-angle_remains) * std::cos(angle_pose) + std::sin(-angle_remains) * std::sin(angle_pose) < 0)
645  {
646  sign_vel = -1.0;
647  angle_remains = angle_remains + M_PI;
648  }
649  angle_remains = trajectory_tracker::angleNormalized(angle_remains);
650 
651  // Curvature
652  const float curv = lpath.getCurvature(it_nearest, it_local_goal, pos_on_line, curv_forward_);
653 
654  ROS_DEBUG(
655  "trajectory_tracker: nearest: %d, local goal: %d, done: %d, goal: %lu, remain: %0.3f, remain_local: %0.3f",
656  i_nearest, i_local_goal, path_step_done_, lpath.size(), distance_remains, remain_local);
657 
658  bool arrive_local_goal(false);
659  bool in_place_turning = (vec[1] == 0.0 && vec[0] == 0.0);
660 
661  TrackingResult result(trajectory_tracker_msgs::TrajectoryTrackerStatus::FOLLOWING);
662 
663  // Stop and rotate
664  const bool large_angle_error = std::abs(rotate_ang_) < M_PI && std::cos(rotate_ang_) > std::cos(angle_remains);
665  if (large_angle_error ||
666  std::abs(remain_local) < stop_tolerance_dist_ ||
667  path_length < min_track_path_ ||
668  in_place_turning)
669  {
670  if (large_angle_error)
671  {
672  ROS_INFO_THROTTLE(1.0, "Stop and rotate due to large angular error: %0.3f", angle_remains);
673  }
674 
675  if (path_length < min_track_path_ ||
676  std::abs(remain_local) < stop_tolerance_dist_ ||
677  in_place_turning)
678  {
679  angle_remains = trajectory_tracker::angleNormalized(-(it_local_goal - 1)->yaw_);
680  if (it_local_goal != lpath.end())
681  arrive_local_goal = true;
682  }
683  if (path_length < stop_tolerance_dist_ || in_place_turning)
684  distance_remains = distance_remains_raw = 0.0;
685 
686  result.turning_in_place = true;
687  result.target_linear_vel = linear_vel;
688  result.distance_remains = distance_remains;
689  result.distance_remains_raw = distance_remains_raw;
690  result.angle_remains = angle_remains;
691  }
692  else
693  {
694  // Too far from given path
695  float dist_from_path = dist_err;
696  if (i_nearest == 0)
697  dist_from_path = -(lpath[i_nearest].pos_ - origin).norm();
698  else if (i_nearest + 1 >= static_cast<int>(path_.size()))
699  dist_from_path = -(lpath[i_nearest].pos_ - origin).norm();
700  if (std::abs(dist_from_path) > d_stop_)
701  {
702  result.distance_remains = distance_remains;
703  result.distance_remains_raw = distance_remains_raw;
704  result.angle_remains = angle_remains;
705  result.angle_remains_raw = angle_remains + yaw_raw;
706  result.status = trajectory_tracker_msgs::TrajectoryTrackerStatus::FAR_FROM_PATH;
707  return result;
708  }
709 
710  // Path following control
711  result.turning_in_place = false;
712  result.target_linear_vel = linear_vel;
713  result.distance_remains = distance_remains;
714  result.distance_remains_raw = distance_remains_raw;
715  result.angle_remains = angle_remains;
716  result.angle_remains_raw = angle_remains + yaw_raw;
718  result.signed_local_distance = -remain_local * sign_vel;
719  result.tracking_point_curv = curv;
720  result.tracking_point_x = pos_on_line[0];
721  result.tracking_point_y = pos_on_line[1];
722  }
723 
724  if (std::abs(result.distance_remains) < goal_tolerance_dist_ &&
725  std::abs(result.angle_remains) < goal_tolerance_ang_ &&
726  std::abs(result.distance_remains_raw) < goal_tolerance_dist_ &&
727  std::abs(result.angle_remains_raw) < goal_tolerance_ang_ &&
728  it_local_goal == lpath.end())
729  {
730  result.status = trajectory_tracker_msgs::TrajectoryTrackerStatus::GOAL;
731  }
732 
733  if (arrive_local_goal)
734  result.path_step_done = i_local_goal;
735  else
736  result.path_step_done = std::max(path_step_done_, i_nearest - 1);
737 
738  return result;
739 }
740 } // namespace trajectory_tracker
741 
742 int main(int argc, char** argv)
743 {
744  ros::init(argc, argv, "trajectory_tracker");
746  track.spin();
747 
748  return 0;
749 }
void cbSpeed(const std_msgs::Float32::ConstPtr &)
double remainedDistance(const ConstIterator &begin, const ConstIterator &nearest, const ConstIterator &end, const Eigen::Vector2d &target_on_line) const
Definition: path2d.h:170
void cbOdomTimeout(const ros::TimerEvent &)
TransportHints & reliable()
void cbParameter(const TrajectoryTrackerConfig &config, const uint32_t)
void publish(const boost::shared_ptr< M > &message) const
void cbTimer(const ros::TimerEvent &)
Quaternion getRotation() const
TF2SIMD_FORCE_INLINE void setRotation(const Quaternion &q)
trajectory_tracker::Path2D path_
std::vector< Pose2D >::const_iterator ConstIterator
Definition: path2d.h:95
float set(float v, const float vel, const float acc, const float dt)
Definition: basic_control.h:77
ConstIterator findLocalGoal(const ConstIterator &begin, const ConstIterator &end, const bool allow_backward_motion) const
Definition: path2d.h:104
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
dynamic_reconfigure::Server< TrajectoryTrackerConfig > parameter_server_
void setPeriod(const Duration &period, bool reset=true)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ConstIterator findNearest(const ConstIterator &begin, const ConstIterator &end, const Eigen::Vector2d &target, const float max_search_range=0, const float epsilon=1e-6) const
Definition: path2d.h:129
trajectory_tracker::VelAccLimitter w_lim_
boost::recursive_mutex parameter_server_mutex_
ros::Subscriber subscribe(ros::NodeHandle &nh_new, const std::string &topic_new, ros::NodeHandle &nh_old, const std::string &topic_old, uint32_t queue_size, void(*fp)(M), const ros::TransportHints &transport_hints=ros::TransportHints())
#define ROS_WARN(...)
ROSCPP_DECL void spin(Spinner &spinner)
tf2_ros::TransformListener tfl_
float getCurvature(const ConstIterator &begin, const ConstIterator &end, const Eigen::Vector2d &target_on_line, const float max_search_range) const
Definition: path2d.h:204
TransportHints & tcpNoDelay(bool nodelay=true)
#define ROS_ERROR_THROTTLE(rate,...)
TF2SIMD_FORCE_INLINE Vector3 & getOrigin()
void control(const tf2::Stamped< tf2::Transform > &, const Eigen::Vector3d &, const double)
float lineDistance(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c)
Definition: eigen_line.h:58
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void fromMsg(const A &, B &b)
trajectory_tracker::VelAccLimitter v_lim_
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
Transform inverse() const
void cbPath(const typename MSG_TYPE::ConstPtr &)
TF2SIMD_FORCE_INLINE void setOrigin(const Vector3 &origin)
#define ROS_WARN_STREAM(args)
double getYaw(const A &a)
Eigen::Vector2d projection2d(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c)
Definition: eigen_line.h:89
B toMsg(const A &a)
void cbOdometry(const nav_msgs::Odometry::ConstPtr &)
bool isValid()
float increment(const float v, const float vel, const float acc, const float dt)
Definition: basic_control.h:72
float length() const
Definition: path2d.h:97
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
static Time now()
#define ROS_INFO_THROTTLE(rate,...)
float angleNormalized(float ang)
Definition: basic_control.h:53
float timeOptimalControl(const float angle, const float acc)
Definition: basic_control.h:38
TrackingResult getTrackingResult(const tf2::Stamped< tf2::Transform > &, const Eigen::Vector3d &) const
Eigen::Vector2d pos_
Definition: path2d.h:50
void deprecatedParam(const ros::NodeHandle &nh, const std::string &key, T &param, const T &default_value)
int main(int argc, char **argv)
float clip(const float v, const float max)
Definition: basic_control.h:43
#define ROS_DEBUG(...)


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