moveit2
The MoveIt Motion Planning Framework for ROS 2.
time_optimal_trajectory_generation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Georgia Tech Research Corporation
3  * All rights reserved.
4  *
5  * Author: Tobias Kunz <tobias@gatech.edu>
6  * Date: 05/2012
7  *
8  * Humanoid Robotics Lab Georgia Institute of Technology
9  * Director: Mike Stilman http://www.golems.org
10  *
11  * Algorithm details and publications:
12  * http://www.golems.org/node/1570
13  *
14  * This file is provided under the following "BSD-style" License:
15  * Redistribution and use in source and binary forms, with or
16  * without modification, are permitted provided that the following
17  * conditions are met:
18  * * Redistributions of source code must retain the above copyright
19  * notice, this list of conditions and the following disclaimer.
20  * * Redistributions in binary form must reproduce the above
21  * copyright notice, this list of conditions and the following
22  * disclaimer in the documentation and/or other materials provided
23  * with the distribution.
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <rclcpp/logger.hpp>
40 #include <rclcpp/logging.hpp>
41 #include <limits>
42 #include <Eigen/Geometry>
43 #include <algorithm>
44 #include <cmath>
46 #include <vector>
47 
48 namespace trajectory_processing
49 {
50 namespace
51 {
52 static const rclcpp::Logger LOGGER =
53  rclcpp::get_logger("moveit_trajectory_processing.time_optimal_trajectory_generation");
54 constexpr double DEFAULT_TIMESTEP = 1e-3;
55 constexpr double EPS = 1e-6;
56 constexpr double DEFAULT_SCALING_FACTOR = 1.0;
57 } // namespace
58 
60 {
61 public:
62  LinearPathSegment(const Eigen::VectorXd& start, const Eigen::VectorXd& end)
63  : PathSegment((end - start).norm()), end_(end), start_(start)
64  {
65  }
66 
67  Eigen::VectorXd getConfig(double s) const override
68  {
69  s /= length_;
70  s = std::max(0.0, std::min(1.0, s));
71  return (1.0 - s) * start_ + s * end_;
72  }
73 
74  Eigen::VectorXd getTangent(double /* s */) const override
75  {
76  return (end_ - start_) / length_;
77  }
78 
79  Eigen::VectorXd getCurvature(double /* s */) const override
80  {
81  return Eigen::VectorXd::Zero(start_.size());
82  }
83 
84  std::list<double> getSwitchingPoints() const override
85  {
86  return std::list<double>();
87  }
88 
89  LinearPathSegment* clone() const override
90  {
91  return new LinearPathSegment(*this);
92  }
93 
94 private:
95  Eigen::VectorXd end_;
96  Eigen::VectorXd start_;
97 };
98 
100 {
101 public:
102  CircularPathSegment(const Eigen::VectorXd& start, const Eigen::VectorXd& intersection, const Eigen::VectorXd& end,
103  double max_deviation)
104  {
105  if ((intersection - start).norm() < 0.000001 || (end - intersection).norm() < 0.000001)
106  {
107  length_ = 0.0;
108  radius_ = 1.0;
109  center_ = intersection;
110  x_ = Eigen::VectorXd::Zero(start.size());
111  y_ = Eigen::VectorXd::Zero(start.size());
112  return;
113  }
114 
115  const Eigen::VectorXd start_direction = (intersection - start).normalized();
116  const Eigen::VectorXd end_direction = (end - intersection).normalized();
117  const double start_dot_end = start_direction.dot(end_direction);
118 
119  // catch division by 0 in computations below
120  if (start_dot_end > 0.999999 || start_dot_end < -0.999999)
121  {
122  length_ = 0.0;
123  radius_ = 1.0;
124  center_ = intersection;
125  x_ = Eigen::VectorXd::Zero(start.size());
126  y_ = Eigen::VectorXd::Zero(start.size());
127  return;
128  }
129 
130  const double angle = acos(start_dot_end);
131  const double start_distance = (start - intersection).norm();
132  const double end_distance = (end - intersection).norm();
133 
134  // enforce max deviation
135  double distance = std::min(start_distance, end_distance);
136  distance = std::min(distance, max_deviation * sin(0.5 * angle) / (1.0 - cos(0.5 * angle)));
137 
138  radius_ = distance / tan(0.5 * angle);
139  length_ = angle * radius_;
140 
141  center_ = intersection + (end_direction - start_direction).normalized() * radius_ / cos(0.5 * angle);
142  x_ = (intersection - distance * start_direction - center_).normalized();
143  y_ = start_direction;
144  }
145 
146  Eigen::VectorXd getConfig(double s) const override
147  {
148  const double angle = s / radius_;
149  return center_ + radius_ * (x_ * cos(angle) + y_ * sin(angle));
150  }
151 
152  Eigen::VectorXd getTangent(double s) const override
153  {
154  const double angle = s / radius_;
155  return -x_ * sin(angle) + y_ * cos(angle);
156  }
157 
158  Eigen::VectorXd getCurvature(double s) const override
159  {
160  const double angle = s / radius_;
161  return -1.0 / radius_ * (x_ * cos(angle) + y_ * sin(angle));
162  }
163 
164  std::list<double> getSwitchingPoints() const override
165  {
166  std::list<double> switching_points;
167  const double dim = x_.size();
168  for (unsigned int i = 0; i < dim; ++i)
169  {
170  double switching_angle = atan2(y_[i], x_[i]);
171  if (switching_angle < 0.0)
172  {
173  switching_angle += M_PI;
174  }
175  const double switching_point = switching_angle * radius_;
176  if (switching_point < length_)
177  {
178  switching_points.push_back(switching_point);
179  }
180  }
181  switching_points.sort();
182  return switching_points;
183  }
184 
185  CircularPathSegment* clone() const override
186  {
187  return new CircularPathSegment(*this);
188  }
189 
190 private:
191  double radius_;
192  Eigen::VectorXd center_;
193  Eigen::VectorXd x_;
194  Eigen::VectorXd y_;
195 };
196 
197 Path::Path(const std::list<Eigen::VectorXd>& path, double max_deviation) : length_(0.0)
198 {
199  if (path.size() < 2)
200  return;
201  std::list<Eigen::VectorXd>::const_iterator path_iterator1 = path.begin();
202  std::list<Eigen::VectorXd>::const_iterator path_iterator2 = path_iterator1;
203  ++path_iterator2;
204  std::list<Eigen::VectorXd>::const_iterator path_iterator3;
205  Eigen::VectorXd start_config = *path_iterator1;
206  while (path_iterator2 != path.end())
207  {
208  path_iterator3 = path_iterator2;
209  ++path_iterator3;
210  if (max_deviation > 0.0 && path_iterator3 != path.end())
211  {
212  CircularPathSegment* blend_segment =
213  new CircularPathSegment(0.5 * (*path_iterator1 + *path_iterator2), *path_iterator2,
214  0.5 * (*path_iterator2 + *path_iterator3), max_deviation);
215  Eigen::VectorXd end_config = blend_segment->getConfig(0.0);
216  if ((end_config - start_config).norm() > 0.000001)
217  {
218  path_segments_.push_back(std::make_unique<LinearPathSegment>(start_config, end_config));
219  }
220  path_segments_.emplace_back(blend_segment);
221 
222  start_config = blend_segment->getConfig(blend_segment->getLength());
223  }
224  else
225  {
226  path_segments_.push_back(std::make_unique<LinearPathSegment>(start_config, *path_iterator2));
227  start_config = *path_iterator2;
228  }
229  path_iterator1 = path_iterator2;
230  ++path_iterator2;
231  }
232 
233  // Create list of switching point candidates, calculate total path length and
234  // absolute positions of path segments
235  for (std::unique_ptr<PathSegment>& path_segment : path_segments_)
236  {
237  path_segment->position_ = length_;
238  std::list<double> local_switching_points = path_segment->getSwitchingPoints();
239  for (std::list<double>::const_iterator point = local_switching_points.begin();
240  point != local_switching_points.end(); ++point)
241  {
242  switching_points_.push_back(std::make_pair(length_ + *point, false));
243  }
244  length_ += path_segment->getLength();
245  while (!switching_points_.empty() && switching_points_.back().first >= length_)
246  switching_points_.pop_back();
247  switching_points_.push_back(std::make_pair(length_, true));
248  }
249  switching_points_.pop_back();
250 }
251 
252 Path::Path(const Path& path) : length_(path.length_), switching_points_(path.switching_points_)
253 {
254  for (const std::unique_ptr<PathSegment>& path_segment : path.path_segments_)
255  {
256  path_segments_.emplace_back(path_segment->clone());
257  }
258 }
259 
260 double Path::getLength() const
261 {
262  return length_;
263 }
264 
265 PathSegment* Path::getPathSegment(double& s) const
266 {
267  std::list<std::unique_ptr<PathSegment>>::const_iterator it = path_segments_.begin();
268  std::list<std::unique_ptr<PathSegment>>::const_iterator next = it;
269  ++next;
270  while (next != path_segments_.end() && s >= (*next)->position_)
271  {
272  it = next;
273  ++next;
274  }
275  s -= (*it)->position_;
276  return (*it).get();
277 }
278 
279 Eigen::VectorXd Path::getConfig(double s) const
280 {
281  const PathSegment* path_segment = getPathSegment(s);
282  return path_segment->getConfig(s);
283 }
284 
285 Eigen::VectorXd Path::getTangent(double s) const
286 {
287  const PathSegment* path_segment = getPathSegment(s);
288  return path_segment->getTangent(s);
289 }
290 
291 Eigen::VectorXd Path::getCurvature(double s) const
292 {
293  const PathSegment* path_segment = getPathSegment(s);
294  return path_segment->getCurvature(s);
295 }
296 
297 double Path::getNextSwitchingPoint(double s, bool& discontinuity) const
298 {
299  std::list<std::pair<double, bool>>::const_iterator it = switching_points_.begin();
300  while (it != switching_points_.end() && it->first <= s)
301  {
302  ++it;
303  }
304  if (it == switching_points_.end())
305  {
306  discontinuity = true;
307  return length_;
308  }
309  discontinuity = it->second;
310  return it->first;
311 }
312 
313 std::list<std::pair<double, bool>> Path::getSwitchingPoints() const
314 {
315  return switching_points_;
316 }
317 
318 Trajectory::Trajectory(const Path& path, const Eigen::VectorXd& max_velocity, const Eigen::VectorXd& max_acceleration,
319  double time_step)
320  : path_(path)
321  , max_velocity_(max_velocity)
322  , max_acceleration_(max_acceleration)
323  , joint_num_(max_velocity.size())
324  , valid_(true)
325  , time_step_(time_step)
326  , cached_time_(std::numeric_limits<double>::max())
327 {
328  if (time_step_ == 0)
329  {
330  valid_ = false;
331  RCLCPP_ERROR(LOGGER, "The trajectory is invalid because the time step is 0.");
332  return;
333  }
334  trajectory_.push_back(TrajectoryStep(0.0, 0.0));
335  double after_acceleration = getMinMaxPathAcceleration(0.0, 0.0, true);
336  while (valid_ && !integrateForward(trajectory_, after_acceleration) && valid_)
337  {
338  double before_acceleration;
339  TrajectoryStep switching_point;
340  if (getNextSwitchingPoint(trajectory_.back().path_pos_, switching_point, before_acceleration, after_acceleration))
341  {
342  break;
343  }
344  integrateBackward(trajectory_, switching_point.path_pos_, switching_point.path_vel_, before_acceleration);
345  }
346 
347  if (valid_)
348  {
349  double before_acceleration = getMinMaxPathAcceleration(path_.getLength(), 0.0, false);
350  integrateBackward(trajectory_, path_.getLength(), 0.0, before_acceleration);
351  }
352 
353  if (valid_)
354  {
355  // Calculate timing
356  std::list<TrajectoryStep>::iterator previous = trajectory_.begin();
357  std::list<TrajectoryStep>::iterator it = previous;
358  it->time_ = 0.0;
359  ++it;
360  while (it != trajectory_.end())
361  {
362  it->time_ =
363  previous->time_ + (it->path_pos_ - previous->path_pos_) / ((it->path_vel_ + previous->path_vel_) / 2.0);
364  previous = it;
365  ++it;
366  }
367  }
368 }
369 
371 {
372 }
373 
374 // Returns true if end of path is reached.
375 bool Trajectory::getNextSwitchingPoint(double path_pos, TrajectoryStep& next_switching_point,
376  double& before_acceleration, double& after_acceleration)
377 {
378  TrajectoryStep acceleration_switching_point(path_pos, 0.0);
379  double acceleration_before_acceleration, acceleration_after_acceleration;
380  bool acceleration_reached_end;
381  do
382  {
383  acceleration_reached_end =
384  getNextAccelerationSwitchingPoint(acceleration_switching_point.path_pos_, acceleration_switching_point,
385  acceleration_before_acceleration, acceleration_after_acceleration);
386  } while (!acceleration_reached_end &&
387  acceleration_switching_point.path_vel_ > getVelocityMaxPathVelocity(acceleration_switching_point.path_pos_));
388 
389  TrajectoryStep velocity_switching_point(path_pos, 0.0);
390  double velocity_before_acceleration, velocity_after_acceleration;
391  bool velocity_reached_end;
392  do
393  {
394  velocity_reached_end = getNextVelocitySwitchingPoint(velocity_switching_point.path_pos_, velocity_switching_point,
395  velocity_before_acceleration, velocity_after_acceleration);
396  } while (
397  !velocity_reached_end && velocity_switching_point.path_pos_ <= acceleration_switching_point.path_pos_ &&
398  (velocity_switching_point.path_vel_ > getAccelerationMaxPathVelocity(velocity_switching_point.path_pos_ - EPS) ||
399  velocity_switching_point.path_vel_ > getAccelerationMaxPathVelocity(velocity_switching_point.path_pos_ + EPS)));
400 
401  if (acceleration_reached_end && velocity_reached_end)
402  {
403  return true;
404  }
405  else if (!acceleration_reached_end &&
406  (velocity_reached_end || acceleration_switching_point.path_pos_ <= velocity_switching_point.path_pos_))
407  {
408  next_switching_point = acceleration_switching_point;
409  before_acceleration = acceleration_before_acceleration;
410  after_acceleration = acceleration_after_acceleration;
411  return false;
412  }
413  else
414  {
415  next_switching_point = velocity_switching_point;
416  before_acceleration = velocity_before_acceleration;
417  after_acceleration = velocity_after_acceleration;
418  return false;
419  }
420 }
421 
422 bool Trajectory::getNextAccelerationSwitchingPoint(double path_pos, TrajectoryStep& next_switching_point,
423  double& before_acceleration, double& after_acceleration)
424 {
425  double switching_path_pos = path_pos;
426  double switching_path_vel;
427  while (true)
428  {
429  bool discontinuity;
430  switching_path_pos = path_.getNextSwitchingPoint(switching_path_pos, discontinuity);
431 
432  if (switching_path_pos > path_.getLength() - EPS)
433  {
434  return true;
435  }
436 
437  if (discontinuity)
438  {
439  const double before_path_vel = getAccelerationMaxPathVelocity(switching_path_pos - EPS);
440  const double after_path_vel = getAccelerationMaxPathVelocity(switching_path_pos + EPS);
441  switching_path_vel = std::min(before_path_vel, after_path_vel);
442  before_acceleration = getMinMaxPathAcceleration(switching_path_pos - EPS, switching_path_vel, false);
443  after_acceleration = getMinMaxPathAcceleration(switching_path_pos + EPS, switching_path_vel, true);
444 
445  if ((before_path_vel > after_path_vel ||
446  getMinMaxPhaseSlope(switching_path_pos - EPS, switching_path_vel, false) >
447  getAccelerationMaxPathVelocityDeriv(switching_path_pos - 2.0 * EPS)) &&
448  (before_path_vel < after_path_vel || getMinMaxPhaseSlope(switching_path_pos + EPS, switching_path_vel, true) <
449  getAccelerationMaxPathVelocityDeriv(switching_path_pos + 2.0 * EPS)))
450  {
451  break;
452  }
453  }
454  else
455  {
456  switching_path_vel = getAccelerationMaxPathVelocity(switching_path_pos);
457  before_acceleration = 0.0;
458  after_acceleration = 0.0;
459 
460  if (getAccelerationMaxPathVelocityDeriv(switching_path_pos - EPS) < 0.0 &&
461  getAccelerationMaxPathVelocityDeriv(switching_path_pos + EPS) > 0.0)
462  {
463  break;
464  }
465  }
466  }
467 
468  next_switching_point = TrajectoryStep(switching_path_pos, switching_path_vel);
469  return false;
470 }
471 
472 bool Trajectory::getNextVelocitySwitchingPoint(double path_pos, TrajectoryStep& next_switching_point,
473  double& before_acceleration, double& after_acceleration)
474 {
475  bool start = false;
476  path_pos -= DEFAULT_TIMESTEP;
477  do
478  {
479  path_pos += DEFAULT_TIMESTEP;
480 
481  if (getMinMaxPhaseSlope(path_pos, getVelocityMaxPathVelocity(path_pos), false) >=
482  getVelocityMaxPathVelocityDeriv(path_pos))
483  {
484  start = true;
485  }
486  } while ((!start || getMinMaxPhaseSlope(path_pos, getVelocityMaxPathVelocity(path_pos), false) >
487  getVelocityMaxPathVelocityDeriv(path_pos)) &&
488  path_pos < path_.getLength());
489 
490  if (path_pos >= path_.getLength())
491  {
492  return true; // end of trajectory reached
493  }
494 
495  double before_path_pos = path_pos - DEFAULT_TIMESTEP;
496  double after_path_pos = path_pos;
497  while (after_path_pos - before_path_pos > EPS)
498  {
499  path_pos = (before_path_pos + after_path_pos) / 2.0;
500  if (getMinMaxPhaseSlope(path_pos, getVelocityMaxPathVelocity(path_pos), false) >
501  getVelocityMaxPathVelocityDeriv(path_pos))
502  {
503  before_path_pos = path_pos;
504  }
505  else
506  {
507  after_path_pos = path_pos;
508  }
509  }
510 
511  before_acceleration = getMinMaxPathAcceleration(before_path_pos, getVelocityMaxPathVelocity(before_path_pos), false);
512  after_acceleration = getMinMaxPathAcceleration(after_path_pos, getVelocityMaxPathVelocity(after_path_pos), true);
513  next_switching_point = TrajectoryStep(after_path_pos, getVelocityMaxPathVelocity(after_path_pos));
514  return false;
515 }
516 
517 // Returns true if end of path is reached
518 bool Trajectory::integrateForward(std::list<TrajectoryStep>& trajectory, double acceleration)
519 {
520  double path_pos = trajectory.back().path_pos_;
521  double path_vel = trajectory.back().path_vel_;
522 
523  std::list<std::pair<double, bool>> switching_points = path_.getSwitchingPoints();
524  std::list<std::pair<double, bool>>::iterator next_discontinuity = switching_points.begin();
525 
526  while (true)
527  {
528  while ((next_discontinuity != switching_points.end()) &&
529  (next_discontinuity->first <= path_pos || !next_discontinuity->second))
530  {
531  ++next_discontinuity;
532  }
533 
534  double old_path_pos = path_pos;
535  double old_path_vel = path_vel;
536 
537  path_vel += time_step_ * acceleration;
538  path_pos += time_step_ * 0.5 * (old_path_vel + path_vel);
539 
540  if (next_discontinuity != switching_points.end() && path_pos > next_discontinuity->first)
541  {
542  // Avoid having a TrajectoryStep with path_pos near a switching point which will cause an almost identical
543  // TrajectoryStep get added in the next run (https://github.com/ros-planning/moveit/issues/1665)
544  if (path_pos - next_discontinuity->first < EPS)
545  {
546  continue;
547  }
548  path_vel = old_path_vel +
549  (next_discontinuity->first - old_path_pos) * (path_vel - old_path_vel) / (path_pos - old_path_pos);
550  path_pos = next_discontinuity->first;
551  }
552 
553  if (path_pos > path_.getLength())
554  {
555  trajectory.push_back(TrajectoryStep(path_pos, path_vel));
556  return true;
557  }
558  else if (path_vel < 0.0)
559  {
560  valid_ = false;
561  RCLCPP_ERROR(LOGGER, "Error while integrating forward: Negative path velocity");
562  return true;
563  }
564 
565  if (path_vel > getVelocityMaxPathVelocity(path_pos) &&
566  getMinMaxPhaseSlope(old_path_pos, getVelocityMaxPathVelocity(old_path_pos), false) <=
567  getVelocityMaxPathVelocityDeriv(old_path_pos))
568  {
569  path_vel = getVelocityMaxPathVelocity(path_pos);
570  }
571 
572  trajectory.push_back(TrajectoryStep(path_pos, path_vel));
573  acceleration = getMinMaxPathAcceleration(path_pos, path_vel, true);
574 
575  if (path_vel == 0 && acceleration == 0)
576  {
577  // The position will never change if velocity and acceleration are zero.
578  // The loop will spin indefinitely as no exit condition is met.
579  valid_ = false;
580  RCLCPP_ERROR(LOGGER, "Error while integrating forward: zero acceleration and velocity. Are any relevant "
581  "acceleration components limited to zero?");
582  return true;
583  }
584 
585  if (path_vel > getAccelerationMaxPathVelocity(path_pos) || path_vel > getVelocityMaxPathVelocity(path_pos))
586  {
587  // Find more accurate intersection with max-velocity curve using bisection
588  TrajectoryStep overshoot = trajectory.back();
589  trajectory.pop_back();
590  double before = trajectory.back().path_pos_;
591  double before_path_vel = trajectory.back().path_vel_;
592  double after = overshoot.path_pos_;
593  double after_path_vel = overshoot.path_vel_;
594  while (after - before > EPS)
595  {
596  const double midpoint = 0.5 * (before + after);
597  double midpoint_path_vel = 0.5 * (before_path_vel + after_path_vel);
598 
599  if (midpoint_path_vel > getVelocityMaxPathVelocity(midpoint) &&
600  getMinMaxPhaseSlope(before, getVelocityMaxPathVelocity(before), false) <=
601  getVelocityMaxPathVelocityDeriv(before))
602  {
603  midpoint_path_vel = getVelocityMaxPathVelocity(midpoint);
604  }
605 
606  if (midpoint_path_vel > getAccelerationMaxPathVelocity(midpoint) ||
607  midpoint_path_vel > getVelocityMaxPathVelocity(midpoint))
608  {
609  after = midpoint;
610  after_path_vel = midpoint_path_vel;
611  }
612  else
613  {
614  before = midpoint;
615  before_path_vel = midpoint_path_vel;
616  }
617  }
618  trajectory.push_back(TrajectoryStep(before, before_path_vel));
619 
620  if (getAccelerationMaxPathVelocity(after) < getVelocityMaxPathVelocity(after))
621  {
622  if (after > next_discontinuity->first)
623  {
624  return false;
625  }
626  else if (getMinMaxPhaseSlope(trajectory.back().path_pos_, trajectory.back().path_vel_, true) >
627  getAccelerationMaxPathVelocityDeriv(trajectory.back().path_pos_))
628  {
629  return false;
630  }
631  }
632  else
633  {
634  if (getMinMaxPhaseSlope(trajectory.back().path_pos_, trajectory_.back().path_vel_, false) >
635  getVelocityMaxPathVelocityDeriv(trajectory_.back().path_pos_))
636  {
637  return false;
638  }
639  }
640  }
641  }
642 }
643 
644 void Trajectory::integrateBackward(std::list<TrajectoryStep>& start_trajectory, double path_pos, double path_vel,
645  double acceleration)
646 {
647  std::list<TrajectoryStep>::iterator start2 = start_trajectory.end();
648  --start2;
649  std::list<TrajectoryStep>::iterator start1 = start2;
650  --start1;
651  std::list<TrajectoryStep> trajectory;
652  double slope;
653  assert(start1->path_pos_ <= path_pos);
654 
655  while (start1 != start_trajectory.begin() || path_pos >= 0.0)
656  {
657  if (start1->path_pos_ <= path_pos)
658  {
659  trajectory.push_front(TrajectoryStep(path_pos, path_vel));
660  path_vel -= time_step_ * acceleration;
661  path_pos -= time_step_ * 0.5 * (path_vel + trajectory.front().path_vel_);
662  acceleration = getMinMaxPathAcceleration(path_pos, path_vel, false);
663  slope = (trajectory.front().path_vel_ - path_vel) / (trajectory.front().path_pos_ - path_pos);
664 
665  if (path_vel < 0.0)
666  {
667  valid_ = false;
668  RCLCPP_ERROR(LOGGER, "Error while integrating backward: Negative path velocity");
669  end_trajectory_ = trajectory;
670  return;
671  }
672  }
673  else
674  {
675  --start1;
676  --start2;
677  }
678 
679  // Check for intersection between current start trajectory and backward
680  // trajectory segments
681  const double start_slope = (start2->path_vel_ - start1->path_vel_) / (start2->path_pos_ - start1->path_pos_);
682  const double intersection_path_pos =
683  (start1->path_vel_ - path_vel + slope * path_pos - start_slope * start1->path_pos_) / (slope - start_slope);
684  if (std::max(start1->path_pos_, path_pos) - EPS <= intersection_path_pos &&
685  intersection_path_pos <= EPS + std::min(start2->path_pos_, trajectory.front().path_pos_))
686  {
687  const double intersection_path_vel =
688  start1->path_vel_ + start_slope * (intersection_path_pos - start1->path_pos_);
689  start_trajectory.erase(start2, start_trajectory.end());
690  start_trajectory.push_back(TrajectoryStep(intersection_path_pos, intersection_path_vel));
691  start_trajectory.splice(start_trajectory.end(), trajectory);
692  return;
693  }
694  }
695 
696  valid_ = false;
697  RCLCPP_ERROR(LOGGER, "Error while integrating backward: Did not hit start trajectory");
698  end_trajectory_ = trajectory;
699 }
700 
701 double Trajectory::getMinMaxPathAcceleration(double path_pos, double path_vel, bool max)
702 {
703  Eigen::VectorXd config_deriv = path_.getTangent(path_pos);
704  Eigen::VectorXd config_deriv2 = path_.getCurvature(path_pos);
705  double factor = max ? 1.0 : -1.0;
706  double max_path_acceleration = std::numeric_limits<double>::max();
707  for (unsigned int i = 0; i < joint_num_; ++i)
708  {
709  if (config_deriv[i] != 0.0)
710  {
711  max_path_acceleration =
712  std::min(max_path_acceleration, max_acceleration_[i] / std::abs(config_deriv[i]) -
713  factor * config_deriv2[i] * path_vel * path_vel / config_deriv[i]);
714  }
715  }
716  return factor * max_path_acceleration;
717 }
718 
719 double Trajectory::getMinMaxPhaseSlope(double path_pos, double path_vel, bool max)
720 {
721  return getMinMaxPathAcceleration(path_pos, path_vel, max) / path_vel;
722 }
723 
724 double Trajectory::getAccelerationMaxPathVelocity(double path_pos) const
725 {
726  double max_path_velocity = std::numeric_limits<double>::infinity();
727  const Eigen::VectorXd config_deriv = path_.getTangent(path_pos);
728  const Eigen::VectorXd config_deriv2 = path_.getCurvature(path_pos);
729  for (unsigned int i = 0; i < joint_num_; ++i)
730  {
731  if (config_deriv[i] != 0.0)
732  {
733  for (unsigned int j = i + 1; j < joint_num_; ++j)
734  {
735  if (config_deriv[j] != 0.0)
736  {
737  double a_ij = config_deriv2[i] / config_deriv[i] - config_deriv2[j] / config_deriv[j];
738  if (a_ij != 0.0)
739  {
740  max_path_velocity = std::min(max_path_velocity, sqrt((max_acceleration_[i] / std::abs(config_deriv[i]) +
741  max_acceleration_[j] / std::abs(config_deriv[j])) /
742  std::abs(a_ij)));
743  }
744  }
745  }
746  }
747  else if (config_deriv2[i] != 0.0)
748  {
749  max_path_velocity = std::min(max_path_velocity, sqrt(max_acceleration_[i] / std::abs(config_deriv2[i])));
750  }
751  }
752  return max_path_velocity;
753 }
754 
755 double Trajectory::getVelocityMaxPathVelocity(double path_pos) const
756 {
757  const Eigen::VectorXd tangent = path_.getTangent(path_pos);
758  double max_path_velocity = std::numeric_limits<double>::max();
759  for (unsigned int i = 0; i < joint_num_; ++i)
760  {
761  max_path_velocity = std::min(max_path_velocity, max_velocity_[i] / std::abs(tangent[i]));
762  }
763  return max_path_velocity;
764 }
765 
766 double Trajectory::getAccelerationMaxPathVelocityDeriv(double path_pos)
767 {
768  return (getAccelerationMaxPathVelocity(path_pos + EPS) - getAccelerationMaxPathVelocity(path_pos - EPS)) /
769  (2.0 * EPS);
770 }
771 
772 double Trajectory::getVelocityMaxPathVelocityDeriv(double path_pos)
773 {
774  const Eigen::VectorXd tangent = path_.getTangent(path_pos);
775  double max_path_velocity = std::numeric_limits<double>::max();
776  unsigned int active_constraint;
777  for (unsigned int i = 0; i < joint_num_; ++i)
778  {
779  const double this_max_path_velocity = max_velocity_[i] / std::abs(tangent[i]);
780  if (this_max_path_velocity < max_path_velocity)
781  {
782  max_path_velocity = this_max_path_velocity;
783  active_constraint = i;
784  }
785  }
786  return -(max_velocity_[active_constraint] * path_.getCurvature(path_pos)[active_constraint]) /
787  (tangent[active_constraint] * std::abs(tangent[active_constraint]));
788 }
789 
791 {
792  return valid_;
793 }
794 
796 {
797  return trajectory_.back().time_;
798 }
799 
800 std::list<Trajectory::TrajectoryStep>::const_iterator Trajectory::getTrajectorySegment(double time) const
801 {
802  if (time >= trajectory_.back().time_)
803  {
804  std::list<TrajectoryStep>::const_iterator last = trajectory_.end();
805  last--;
806  return last;
807  }
808  else
809  {
810  if (time < cached_time_)
811  {
812  cached_trajectory_segment_ = trajectory_.begin();
813  }
814  while (time >= cached_trajectory_segment_->time_)
815  {
816  ++cached_trajectory_segment_;
817  }
818  cached_time_ = time;
819  return cached_trajectory_segment_;
820  }
821 }
822 
823 Eigen::VectorXd Trajectory::getPosition(double time) const
824 {
825  std::list<TrajectoryStep>::const_iterator it = getTrajectorySegment(time);
826  std::list<TrajectoryStep>::const_iterator previous = it;
827  previous--;
828 
829  double time_step = it->time_ - previous->time_;
830  const double acceleration =
831  2.0 * (it->path_pos_ - previous->path_pos_ - time_step * previous->path_vel_) / (time_step * time_step);
832 
833  time_step = time - previous->time_;
834  const double path_pos =
835  previous->path_pos_ + time_step * previous->path_vel_ + 0.5 * time_step * time_step * acceleration;
836 
837  return path_.getConfig(path_pos);
838 }
839 
840 Eigen::VectorXd Trajectory::getVelocity(double time) const
841 {
842  std::list<TrajectoryStep>::const_iterator it = getTrajectorySegment(time);
843  std::list<TrajectoryStep>::const_iterator previous = it;
844  previous--;
845 
846  double time_step = it->time_ - previous->time_;
847  const double acceleration =
848  2.0 * (it->path_pos_ - previous->path_pos_ - time_step * previous->path_vel_) / (time_step * time_step);
849 
850  const double path_pos =
851  previous->path_pos_ + time_step * previous->path_vel_ + 0.5 * time_step * time_step * acceleration;
852  const double path_vel = previous->path_vel_ + time_step * acceleration;
853 
854  return path_.getTangent(path_pos) * path_vel;
855 }
856 
857 Eigen::VectorXd Trajectory::getAcceleration(double time) const
858 {
859  std::list<TrajectoryStep>::const_iterator it = getTrajectorySegment(time);
860  std::list<TrajectoryStep>::const_iterator previous = it;
861  previous--;
862 
863  double time_step = it->time_ - previous->time_;
864  const double acceleration =
865  2.0 * (it->path_pos_ - previous->path_pos_ - time_step * previous->path_vel_) / (time_step * time_step);
866 
867  const double path_pos =
868  previous->path_pos_ + time_step * previous->path_vel_ + 0.5 * time_step * time_step * acceleration;
869  const double path_vel = previous->path_vel_ + time_step * acceleration;
870  Eigen::VectorXd path_acc =
871  (path_.getTangent(path_pos) * path_vel - path_.getTangent(previous->path_pos_) * previous->path_vel_);
872  if (time_step > 0.0)
873  path_acc /= time_step;
874  return path_acc;
875 }
876 
877 TimeOptimalTrajectoryGeneration::TimeOptimalTrajectoryGeneration(const double path_tolerance, const double resample_dt,
878  const double min_angle_change)
879  : path_tolerance_(path_tolerance), resample_dt_(resample_dt), min_angle_change_(min_angle_change)
880 {
881 }
882 
884  const double max_velocity_scaling_factor,
885  const double max_acceleration_scaling_factor) const
886 {
887  if (trajectory.empty())
888  return true;
889 
890  const moveit::core::JointModelGroup* group = trajectory.getGroup();
891  if (!group)
892  {
893  RCLCPP_ERROR(LOGGER, "It looks like the planner did not set the group the plan was computed for");
894  return false;
895  }
896 
897  // Validate scaling
898  double velocity_scaling_factor = verifyScalingFactor(max_velocity_scaling_factor, VELOCITY);
899  double acceleration_scaling_factor = verifyScalingFactor(max_acceleration_scaling_factor, ACCELERATION);
900 
901  // Get the velocity and acceleration limits for all active joints
902  const moveit::core::RobotModel& rmodel = group->getParentModel();
903  const std::vector<std::string>& vars = group->getVariableNames();
904  std::vector<size_t> active_joint_indices;
905  if (!group->computeJointVariableIndices(group->getActiveJointModelNames(), active_joint_indices))
906  {
907  RCLCPP_ERROR(LOGGER, "Failed to get active variable indices.");
908  }
909 
910  const size_t num_active_joints = active_joint_indices.size();
911  Eigen::VectorXd max_velocity(num_active_joints);
912  Eigen::VectorXd max_acceleration(num_active_joints);
913  for (size_t idx = 0; idx < num_active_joints; ++idx)
914  {
915  // For active joints only (skip mimic joints and other types)
916  const moveit::core::VariableBounds& bounds = rmodel.getVariableBounds(vars[active_joint_indices[idx]]);
917 
918  // Limits need to be non-zero, otherwise we never exit
919  if (bounds.velocity_bounded_)
920  {
921  if (bounds.max_velocity_ <= 0.0)
922  {
923  RCLCPP_ERROR(LOGGER, "Invalid max_velocity %f specified for '%s', must be greater than 0.0",
924  bounds.max_velocity_, vars[idx].c_str());
925  return false;
926  }
927  max_velocity[idx] =
928  std::min(std::fabs(bounds.max_velocity_), std::fabs(bounds.min_velocity_)) * velocity_scaling_factor;
929  }
930  else
931  {
932  RCLCPP_ERROR_STREAM(LOGGER, "No velocity limit was defined for joint "
933  << vars[idx].c_str()
934  << "! You have to define velocity limits in the URDF or joint_limits.yaml");
935  return false;
936  }
937 
938  if (bounds.acceleration_bounded_)
939  {
940  if (bounds.max_acceleration_ < 0.0)
941  {
942  RCLCPP_ERROR(LOGGER, "Invalid max_acceleration %f specified for '%s', must be greater than 0.0",
943  bounds.max_acceleration_, vars[idx].c_str());
944  return false;
945  }
946  max_acceleration[idx] = std::min(std::fabs(bounds.max_acceleration_), std::fabs(bounds.min_acceleration_)) *
947  acceleration_scaling_factor;
948  }
949  else
950  {
951  RCLCPP_ERROR_STREAM(LOGGER, "No acceleration limit was defined for joint "
952  << vars[idx].c_str()
953  << "! You have to define acceleration limits in the URDF or "
954  "joint_limits.yaml");
955  return false;
956  }
957  }
958 
959  return doTimeParameterizationCalculations(trajectory, max_velocity, max_acceleration);
960 }
961 
963  const std::vector<moveit_msgs::msg::JointLimits>& joint_limits,
964  const double max_velocity_scaling_factor,
965  const double max_acceleration_scaling_factor) const
966 {
967  std::unordered_map<std::string, double> velocity_limits;
968  std::unordered_map<std::string, double> acceleration_limits;
969  for (const auto& limit : joint_limits)
970  {
971  // If custom limits are not defined here, they will be supplied from getRobotModelBounds() later
972  if (limit.has_velocity_limits)
973  {
974  velocity_limits[limit.joint_name] = limit.max_velocity;
975  }
976  if (limit.has_acceleration_limits)
977  {
978  acceleration_limits[limit.joint_name] = limit.max_acceleration;
979  }
980  }
981  return computeTimeStamps(trajectory, velocity_limits, acceleration_limits, max_velocity_scaling_factor,
982  max_acceleration_scaling_factor);
983 }
984 
986  robot_trajectory::RobotTrajectory& trajectory, const std::unordered_map<std::string, double>& velocity_limits,
987  const std::unordered_map<std::string, double>& acceleration_limits, const double max_velocity_scaling_factor,
988  const double max_acceleration_scaling_factor) const
989 {
990  if (trajectory.empty())
991  return true;
992 
993  // Get the default joint limits from the robot model, then overwrite any that are provided as arguments
994  const moveit::core::JointModelGroup* group = trajectory.getGroup();
995  if (!group)
996  {
997  RCLCPP_ERROR(LOGGER, "It looks like the planner did not set the group the plan was computed for");
998  return false;
999  }
1000 
1001  // Validate scaling
1002  double velocity_scaling_factor = verifyScalingFactor(max_velocity_scaling_factor, VELOCITY);
1003  double acceleration_scaling_factor = verifyScalingFactor(max_acceleration_scaling_factor, ACCELERATION);
1004 
1005  // Get the velocity and acceleration limits for specified joints
1006  const moveit::core::RobotModel& rmodel = group->getParentModel();
1007  const std::vector<std::string>& vars = group->getVariableNames();
1008  std::vector<size_t> indices;
1009  if (!group->computeJointVariableIndices(group->getActiveJointModelNames(), indices))
1010  {
1011  RCLCPP_ERROR(LOGGER, "Failed to get active variable indices.");
1012  }
1013 
1014  const size_t num_joints = indices.size();
1015 
1016  Eigen::VectorXd max_velocity(num_joints);
1017  Eigen::VectorXd max_acceleration(num_joints);
1018  for (const auto idx : indices)
1019  {
1020  const moveit::core::VariableBounds& bounds = rmodel.getVariableBounds(vars[idx]);
1021 
1022  // VELOCITY LIMIT
1023  // Check if a custom limit was specified as an argument
1024  bool set_velocity_limit = false;
1025  auto it = velocity_limits.find(vars[idx]);
1026  if (it != velocity_limits.end())
1027  {
1028  max_velocity[idx] = it->second * velocity_scaling_factor;
1029  set_velocity_limit = true;
1030  }
1031 
1032  if (bounds.velocity_bounded_ && !set_velocity_limit)
1033  {
1034  // Set the default velocity limit, from robot model
1035  if (bounds.max_velocity_ < 0.0)
1036  {
1037  RCLCPP_ERROR(LOGGER, "Invalid max_velocity %f specified for '%s', must be greater than 0.0",
1038  bounds.max_velocity_, vars[idx].c_str());
1039  return false;
1040  }
1041  max_velocity[idx] =
1042  std::min(std::fabs(bounds.max_velocity_), std::fabs(bounds.min_velocity_)) * velocity_scaling_factor;
1043  set_velocity_limit = true;
1044  }
1045 
1046  if (!set_velocity_limit)
1047  {
1048  RCLCPP_ERROR_STREAM(LOGGER, "No velocity limit was defined for joint "
1049  << vars[idx].c_str()
1050  << "! You have to define velocity limits in the URDF or "
1051  "joint_limits.yaml");
1052  return false;
1053  }
1054 
1055  // ACCELERATION LIMIT
1056  // Check if a custom limit was specified as an argument
1057  bool set_acceleration_limit = false;
1058  it = acceleration_limits.find(vars[idx]);
1059  if (it != acceleration_limits.end())
1060  {
1061  max_acceleration[idx] = it->second * acceleration_scaling_factor;
1062  set_acceleration_limit = true;
1063  }
1064 
1065  if (bounds.acceleration_bounded_ && !set_acceleration_limit)
1066  {
1067  // Set the default acceleration limit, from robot model
1068  if (bounds.max_acceleration_ < 0.0)
1069  {
1070  RCLCPP_ERROR(LOGGER, "Invalid max_acceleration %f specified for '%s', must be greater than 0.0",
1071  bounds.max_acceleration_, vars[idx].c_str());
1072  return false;
1073  }
1074  max_acceleration[idx] = std::min(std::fabs(bounds.max_acceleration_), std::fabs(bounds.min_acceleration_)) *
1075  acceleration_scaling_factor;
1076  set_acceleration_limit = true;
1077  }
1078  if (!set_acceleration_limit)
1079  {
1080  RCLCPP_ERROR_STREAM(LOGGER, "No acceleration limit was defined for joint "
1081  << vars[idx].c_str()
1082  << "! You have to define acceleration limits in the URDF or "
1083  "joint_limits.yaml");
1084  return false;
1085  }
1086  }
1087 
1088  return doTimeParameterizationCalculations(trajectory, max_velocity, max_acceleration);
1089 }
1090 
1091 bool totgComputeTimeStamps(const size_t num_waypoints, robot_trajectory::RobotTrajectory& trajectory,
1092  const double max_velocity_scaling_factor, const double max_acceleration_scaling_factor)
1093 {
1094  // The algorithm is:
1095  // 1. Run TOTG with default settings once to find the optimal trajectory duration
1096  // 2. Calculate the timestep to get the desired num_waypoints:
1097  // new_delta_t = duration/(n-1) // subtract one for the initial waypoint
1098  // 3. Run TOTG again with the new timestep. This gives the exact num_waypoints you want (plus or minus one due to
1099  // numerical rounding)
1100 
1101  if (num_waypoints < 2)
1102  {
1103  RCLCPP_ERROR(LOGGER, "computeTimeStamps() requires num_waypoints > 1");
1104  return false;
1105  }
1106 
1107  TimeOptimalTrajectoryGeneration default_totg(0.1 /* default path tolerance */, 0.1 /* default resample_dt */);
1108  default_totg.computeTimeStamps(trajectory, max_velocity_scaling_factor, max_acceleration_scaling_factor);
1109  double optimal_duration = trajectory.getDuration();
1110  double new_resample_dt = optimal_duration / (num_waypoints - 1);
1111  TimeOptimalTrajectoryGeneration resample_totg(0.1 /* path tolerance */, new_resample_dt);
1112  resample_totg.computeTimeStamps(trajectory, max_velocity_scaling_factor, max_acceleration_scaling_factor);
1113  return true;
1114 }
1115 
1116 bool TimeOptimalTrajectoryGeneration::doTimeParameterizationCalculations(robot_trajectory::RobotTrajectory& trajectory,
1117  const Eigen::VectorXd& max_velocity,
1118  const Eigen::VectorXd& max_acceleration) const
1119 {
1120  // This lib does not actually work properly when angles wrap around, so we need to unwind the path first
1121  trajectory.unwind();
1122 
1123  const moveit::core::JointModelGroup* group = trajectory.getGroup();
1124  if (!group)
1125  {
1126  RCLCPP_ERROR(LOGGER, "It looks like the planner did not set the group the plan was computed for");
1127  return false;
1128  }
1129 
1130  if (hasMixedJointTypes(group))
1131  {
1132  RCLCPP_WARN(LOGGER, "There is a combination of revolute and prismatic joints in the robot model. TOTG's "
1133  "`path_tolerance` will not function correctly.");
1134  }
1135 
1136  const unsigned num_points = trajectory.getWayPointCount();
1137  const std::vector<int>& idx = group->getVariableIndexList();
1138  const unsigned num_joints = group->getVariableCount();
1139 
1140  // Have to convert into Eigen data structs and remove repeated points
1141  // (https://github.com/tobiaskunz/trajectories/issues/3)
1142  std::list<Eigen::VectorXd> points;
1143  for (size_t p = 0; p < num_points; ++p)
1144  {
1145  moveit::core::RobotStatePtr waypoint = trajectory.getWayPointPtr(p);
1146  Eigen::VectorXd new_point(num_joints);
1147  // The first point should always be kept
1148  bool diverse_point = (p == 0);
1149 
1150  for (size_t j = 0; j < num_joints; ++j)
1151  {
1152  new_point[j] = waypoint->getVariablePosition(idx[j]);
1153  // If any joint angle is different, it's a unique waypoint
1154  if (p > 0 && std::fabs(new_point[j] - points.back()[j]) > min_angle_change_)
1155  {
1156  diverse_point = true;
1157  }
1158  }
1159 
1160  if (diverse_point)
1161  {
1162  points.push_back(new_point);
1163  // If the last point is not a diverse_point we replace the last added point with it to make sure to always have
1164  // the input end point as the last point
1165  }
1166  else if (p == num_points - 1)
1167  {
1168  points.back() = new_point;
1169  }
1170  }
1171 
1172  // Return trajectory with only the first waypoint if there are not multiple diverse points
1173  if (points.size() == 1)
1174  {
1176  waypoint.zeroVelocities();
1177  waypoint.zeroAccelerations();
1178  trajectory.clear();
1179  trajectory.addSuffixWayPoint(waypoint, 0.0);
1180  return true;
1181  }
1182 
1183  // Now actually call the algorithm
1184  Trajectory parameterized(Path(points, path_tolerance_), max_velocity, max_acceleration, DEFAULT_TIMESTEP);
1185  if (!parameterized.isValid())
1186  {
1187  RCLCPP_ERROR(LOGGER, "Unable to parameterize trajectory.");
1188  return false;
1189  }
1190 
1191  // Compute sample count
1192  size_t sample_count = std::ceil(parameterized.getDuration() / resample_dt_);
1193 
1194  // Resample and fill in trajectory
1196  trajectory.clear();
1197  double last_t = 0;
1198  for (size_t sample = 0; sample <= sample_count; ++sample)
1199  {
1200  // always sample the end of the trajectory as well
1201  double t = std::min(parameterized.getDuration(), sample * resample_dt_);
1202  Eigen::VectorXd position = parameterized.getPosition(t);
1203  Eigen::VectorXd velocity = parameterized.getVelocity(t);
1204  Eigen::VectorXd acceleration = parameterized.getAcceleration(t);
1205 
1206  for (size_t j = 0; j < num_joints; ++j)
1207  {
1208  waypoint.setVariablePosition(idx[j], position[j]);
1209  waypoint.setVariableVelocity(idx[j], velocity[j]);
1210  waypoint.setVariableAcceleration(idx[j], acceleration[j]);
1211  }
1212 
1213  trajectory.addSuffixWayPoint(waypoint, t - last_t);
1214  last_t = t;
1215  }
1216 
1217  return true;
1218 }
1219 
1220 bool TimeOptimalTrajectoryGeneration::hasMixedJointTypes(const moveit::core::JointModelGroup* group) const
1221 {
1222  const std::vector<const moveit::core::JointModel*>& joint_models = group->getActiveJointModels();
1223 
1224  bool have_prismatic =
1225  std::any_of(joint_models.cbegin(), joint_models.cend(), [](const moveit::core::JointModel* joint_model) {
1226  return joint_model->getType() == moveit::core::JointModel::JointType::PRISMATIC;
1227  });
1228 
1229  bool have_revolute =
1230  std::any_of(joint_models.cbegin(), joint_models.cend(), [](const moveit::core::JointModel* joint_model) {
1231  return joint_model->getType() == moveit::core::JointModel::JointType::REVOLUTE;
1232  });
1233 
1234  return have_prismatic && have_revolute;
1235 }
1236 
1237 double TimeOptimalTrajectoryGeneration::verifyScalingFactor(const double requested_scaling_factor,
1238  const LimitType limit_type) const
1239 {
1240  std::string limit_type_str;
1241  double scaling_factor = DEFAULT_SCALING_FACTOR;
1242 
1243  const auto limit_type_it = LIMIT_TYPES.find(limit_type);
1244  if (limit_type_it != LIMIT_TYPES.end())
1245  {
1246  limit_type_str = limit_type_it->second + "_";
1247  }
1248 
1249  if (requested_scaling_factor > 0.0 && requested_scaling_factor <= 1.0)
1250  {
1251  scaling_factor = requested_scaling_factor;
1252  }
1253  else
1254  {
1255  RCLCPP_WARN(LOGGER, "Invalid max_%sscaling_factor %f specified, defaulting to %f instead.", limit_type_str.c_str(),
1256  requested_scaling_factor, scaling_factor);
1257  }
1258  return scaling_factor;
1259 }
1260 } // namespace trajectory_processing
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
Definition of a kinematic model. This class is not thread safe, however multiple instances can be cre...
Definition: robot_model.h:76
const VariableBounds & getVariableBounds(const std::string &variable) const
Get the bounds for a specific variable. Throw an exception of variable is not found.
Definition: robot_model.h:433
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
void setVariableAcceleration(const std::string &variable, double value)
Set the acceleration of a variable. If an unknown variable name is specified, an exception is thrown.
Definition: robot_state.h:381
void zeroVelocities()
Set all velocities to 0.0.
void setVariableVelocity(const std::string &variable, double value)
Set the velocity of a variable. If an unknown variable name is specified, an exception is thrown.
Definition: robot_state.h:282
void setVariablePosition(const std::string &variable, double value)
Set the position of a single variable. An exception is thrown if the variable name is not known.
Definition: robot_state.h:188
void zeroAccelerations()
Set all accelerations to 0.0.
Maintain a sequence of waypoints and the time durations between these waypoints.
const moveit::core::JointModelGroup * getGroup() const
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
Add a point to the trajectory.
moveit::core::RobotStatePtr & getWayPointPtr(std::size_t index)
const moveit::core::RobotState & getWayPoint(std::size_t index) const
CircularPathSegment(const Eigen::VectorXd &start, const Eigen::VectorXd &intersection, const Eigen::VectorXd &end, double max_deviation)
Eigen::VectorXd getConfig(double s) const override
LinearPathSegment(const Eigen::VectorXd &start, const Eigen::VectorXd &end)
virtual Eigen::VectorXd getConfig(double s) const =0
virtual Eigen::VectorXd getCurvature(double s) const =0
virtual Eigen::VectorXd getTangent(double s) const =0
Eigen::VectorXd getTangent(double s) const
std::list< std::pair< double, bool > > getSwitchingPoints() const
Return a list of all switching points as a pair (arc length to switching point, discontinuity)
Eigen::VectorXd getCurvature(double s) const
double getNextSwitchingPoint(double s, bool &discontinuity) const
Get the next switching point.
Path(const std::list< Eigen::VectorXd > &path, double max_deviation=0.0)
TimeOptimalTrajectoryGeneration(const double path_tolerance=0.1, const double resample_dt=0.1, const double min_angle_change=0.001)
bool computeTimeStamps(robot_trajectory::RobotTrajectory &trajectory, const double max_velocity_scaling_factor=1.0, const double max_acceleration_scaling_factor=1.0) const override
Compute a trajectory with waypoints spaced equally in time (according to resample_dt_)....
Eigen::VectorXd getAcceleration(double time) const
Return the acceleration vector for a given point in time.
bool isValid() const
Call this method after constructing the object to make sure the trajectory generation succeeded witho...
Eigen::VectorXd getPosition(double time) const
Return the position/configuration vector for a given point in time.
Trajectory(const Path &path, const Eigen::VectorXd &max_velocity, const Eigen::VectorXd &max_acceleration, double time_step=0.001)
Generates a time-optimal trajectory.
double getDuration() const
Returns the optimal duration of the trajectory.
Eigen::VectorXd getVelocity(double time) const
Return the velocity vector for a given point in time.
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.h:55
const std::unordered_map< LimitType, std::string > LIMIT_TYPES
bool totgComputeTimeStamps(const size_t num_waypoints, robot_trajectory::RobotTrajectory &trajectory, const double max_velocity_scaling_factor=1.0, const double max_acceleration_scaling_factor=1.0)
Compute a trajectory with the desired number of waypoints. Resampling the trajectory doesn't change t...