moveit2
The MoveIt Motion Planning Framework for ROS 2.
robot_trajectory.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Ioan Sucan, Adam Leeper */
36 
37 #include <math.h>
40 #include <rclcpp/duration.hpp>
41 #include <rclcpp/logger.hpp>
42 #include <rclcpp/logging.hpp>
43 #include <rclcpp/time.hpp>
44 #include <tf2_eigen/tf2_eigen.hpp>
45 #include <numeric>
46 #include <optional>
47 #include <moveit/utils/logger.hpp>
48 
49 namespace robot_trajectory
50 {
51 namespace
52 {
53 rclcpp::Logger getLogger()
54 {
55  return moveit::getLogger("robot_trajectory");
56 }
57 } // namespace
58 
59 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model)
60  : robot_model_(robot_model), group_(nullptr)
61 {
62 }
63 
64 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model, const std::string& group)
65  : robot_model_(robot_model), group_(group.empty() ? nullptr : robot_model->getJointModelGroup(group))
66 {
67 }
68 
69 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model,
70  const moveit::core::JointModelGroup* group)
71  : robot_model_(robot_model), group_(group)
72 {
73 }
74 
76 {
77  *this = other; // default assignment operator performs a shallow copy
78  if (deepcopy)
79  {
80  waypoints_.clear();
81  for (const auto& waypoint : other.waypoints_)
82  {
83  waypoints_.emplace_back(std::make_shared<moveit::core::RobotState>(*waypoint));
84  }
85  }
86 }
87 
88 const std::string& RobotTrajectory::getGroupName() const
89 {
90  if (group_)
91  return group_->getName();
92  static const std::string EMPTY;
93  return EMPTY;
94 }
95 
97 {
98  return std::accumulate(duration_from_previous_.begin(), duration_from_previous_.end(), 0.0);
99 }
100 
102 {
103  if (duration_from_previous_.empty())
104  {
105  RCLCPP_WARN(getLogger(), "Too few waypoints to calculate a duration. Returning 0.");
106  return 0.0;
107  }
108 
109  // If the initial segment has a duration of 0, exclude it from the average calculation
110  if (duration_from_previous_[0] == 0)
111  {
112  if (duration_from_previous_.size() <= 1)
113  {
114  RCLCPP_WARN(getLogger(), "First and only waypoint has a duration of 0.");
115  return 0.0;
116  }
117  else
118  return getDuration() / static_cast<double>(duration_from_previous_.size() - 1);
119  }
120  else
121  return getDuration() / static_cast<double>(duration_from_previous_.size());
122 }
123 
125 {
126  robot_model_.swap(other.robot_model_);
127  std::swap(group_, other.group_);
128  waypoints_.swap(other.waypoints_);
129  duration_from_previous_.swap(other.duration_from_previous_);
130 }
131 
132 RobotTrajectory& RobotTrajectory::append(const RobotTrajectory& source, double dt, size_t start_index, size_t end_index)
133 {
134  end_index = std::min(end_index, source.waypoints_.size());
135  if (start_index >= end_index)
136  return *this;
137  waypoints_.insert(waypoints_.end(), std::next(source.waypoints_.begin(), start_index),
138  std::next(source.waypoints_.begin(), end_index));
139  std::size_t index = duration_from_previous_.size();
140  duration_from_previous_.insert(duration_from_previous_.end(),
141  std::next(source.duration_from_previous_.begin(), start_index),
142  std::next(source.duration_from_previous_.begin(), end_index));
143  if (duration_from_previous_.size() > index)
144  duration_from_previous_[index] = dt;
145 
146  return *this;
147 }
148 
150 {
151  std::reverse(waypoints_.begin(), waypoints_.end());
152  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
153  {
154  // reversing the trajectory implies inverting the velocity profile
155  waypoint->invertVelocity();
156  }
157  if (!duration_from_previous_.empty())
158  {
159  duration_from_previous_.push_back(duration_from_previous_.front());
160  std::reverse(duration_from_previous_.begin(), duration_from_previous_.end());
161  duration_from_previous_.pop_back();
162  }
163 
164  return *this;
165 }
166 
168 {
169  if (waypoints_.empty())
170  return *this;
171 
172  const std::vector<const moveit::core::JointModel*>& cont_joints =
173  group_ ? group_->getContinuousJointModels() : robot_model_->getContinuousJointModels();
174 
175  for (const moveit::core::JointModel* cont_joint : cont_joints)
176  {
177  // unwrap continuous joints
178  double running_offset = 0.0;
179  double last_value = waypoints_[0]->getJointPositions(cont_joint)[0];
180  cont_joint->enforcePositionBounds(&last_value);
181  waypoints_[0]->setJointPositions(cont_joint, &last_value);
182 
183  for (std::size_t j = 1; j < waypoints_.size(); ++j)
184  {
185  double current_value = waypoints_[j]->getJointPositions(cont_joint)[0];
186  cont_joint->enforcePositionBounds(&current_value);
187  if (last_value > current_value + M_PI)
188  {
189  running_offset += 2.0 * M_PI;
190  }
191  else if (current_value > last_value + M_PI)
192  {
193  running_offset -= 2.0 * M_PI;
194  }
195 
196  last_value = current_value;
197  current_value += running_offset;
198  waypoints_[j]->setJointPositions(cont_joint, &current_value);
199  }
200  }
201  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
202  {
203  waypoint->update();
204  }
205 
206  return *this;
207 }
208 
210 {
211  if (waypoints_.empty())
212  return *this;
213 
214  const std::vector<const moveit::core::JointModel*>& cont_joints =
215  group_ ? group_->getContinuousJointModels() : robot_model_->getContinuousJointModels();
216 
217  for (const moveit::core::JointModel* cont_joint : cont_joints)
218  {
219  double reference_value0 = state.getJointPositions(cont_joint)[0];
220  double reference_value = reference_value0;
221  cont_joint->enforcePositionBounds(&reference_value);
222 
223  // unwrap continuous joints
224  double running_offset = reference_value0 - reference_value;
225 
226  double last_value = waypoints_[0]->getJointPositions(cont_joint)[0];
227  cont_joint->enforcePositionBounds(&last_value);
228  if (last_value > reference_value + M_PI)
229  {
230  running_offset -= 2.0 * M_PI;
231  }
232  else if (last_value < reference_value - M_PI)
233  {
234  running_offset += 2.0 * M_PI;
235  }
236  double current_start_value = last_value + running_offset;
237  waypoints_[0]->setJointPositions(cont_joint, &current_start_value);
238 
239  for (std::size_t j = 1; j < waypoints_.size(); ++j)
240  {
241  double current_value = waypoints_[j]->getJointPositions(cont_joint)[0];
242  cont_joint->enforcePositionBounds(&current_value);
243  if (last_value > current_value + M_PI)
244  {
245  running_offset += 2.0 * M_PI;
246  }
247  else if (current_value > last_value + M_PI)
248  {
249  running_offset -= 2.0 * M_PI;
250  }
251 
252  last_value = current_value;
253  current_value += running_offset;
254  waypoints_[j]->setJointPositions(cont_joint, &current_value);
255  }
256  }
257  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
258  {
259  waypoint->update();
260  }
261 
262  return *this;
263 }
264 
265 void RobotTrajectory::getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory& trajectory,
266  const std::vector<std::string>& joint_filter) const
267 {
268  trajectory = moveit_msgs::msg::RobotTrajectory();
269  if (waypoints_.empty())
270  return;
271  const std::vector<const moveit::core::JointModel*>& jnts =
272  group_ ? group_->getActiveJointModels() : robot_model_->getActiveJointModels();
273 
274  std::vector<const moveit::core::JointModel*> onedof;
275  std::vector<const moveit::core::JointModel*> mdof;
276  trajectory.joint_trajectory.joint_names.clear();
277  trajectory.multi_dof_joint_trajectory.joint_names.clear();
278 
279  for (const moveit::core::JointModel* active_joint : jnts)
280  {
281  // only consider joints listed in joint_filter
282  if (!joint_filter.empty() &&
283  std::find(joint_filter.begin(), joint_filter.end(), active_joint->getName()) == joint_filter.end())
284  continue;
285 
286  if (active_joint->getVariableCount() == 1)
287  {
288  trajectory.joint_trajectory.joint_names.push_back(active_joint->getName());
289  onedof.push_back(active_joint);
290  }
291  else
292  {
293  trajectory.multi_dof_joint_trajectory.joint_names.push_back(active_joint->getName());
294  mdof.push_back(active_joint);
295  }
296  }
297 
298  if (!onedof.empty())
299  {
300  trajectory.joint_trajectory.header.frame_id = robot_model_->getModelFrame();
301  trajectory.joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
302  trajectory.joint_trajectory.points.resize(waypoints_.size());
303  }
304 
305  if (!mdof.empty())
306  {
307  trajectory.multi_dof_joint_trajectory.header.frame_id = robot_model_->getModelFrame();
308  trajectory.multi_dof_joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
309  trajectory.multi_dof_joint_trajectory.points.resize(waypoints_.size());
310  }
311 
312  static const auto ZERO_DURATION = rclcpp::Duration::from_seconds(0);
313  double total_time = 0.0;
314  for (std::size_t i = 0; i < waypoints_.size(); ++i)
315  {
316  if (duration_from_previous_.size() > i)
317  total_time += duration_from_previous_[i];
318 
319  if (!onedof.empty())
320  {
321  trajectory.joint_trajectory.points[i].positions.resize(onedof.size());
322  trajectory.joint_trajectory.points[i].velocities.reserve(onedof.size());
323 
324  for (std::size_t j = 0; j < onedof.size(); ++j)
325  {
326  trajectory.joint_trajectory.points[i].positions[j] =
327  waypoints_[i]->getVariablePosition(onedof[j]->getFirstVariableIndex());
328  // if we have velocities/accelerations/effort, copy those too
329  if (waypoints_[i]->hasVelocities())
330  {
331  trajectory.joint_trajectory.points[i].velocities.push_back(
332  waypoints_[i]->getVariableVelocity(onedof[j]->getFirstVariableIndex()));
333  }
334  if (waypoints_[i]->hasAccelerations())
335  {
336  trajectory.joint_trajectory.points[i].accelerations.push_back(
337  waypoints_[i]->getVariableAcceleration(onedof[j]->getFirstVariableIndex()));
338  }
339  if (waypoints_[i]->hasEffort())
340  {
341  trajectory.joint_trajectory.points[i].effort.push_back(
342  waypoints_[i]->getVariableEffort(onedof[j]->getFirstVariableIndex()));
343  }
344  }
345  // clear velocities if we have an incomplete specification
346  if (trajectory.joint_trajectory.points[i].velocities.size() != onedof.size())
347  trajectory.joint_trajectory.points[i].velocities.clear();
348  // clear accelerations if we have an incomplete specification
349  if (trajectory.joint_trajectory.points[i].accelerations.size() != onedof.size())
350  trajectory.joint_trajectory.points[i].accelerations.clear();
351  // clear effort if we have an incomplete specification
352  if (trajectory.joint_trajectory.points[i].effort.size() != onedof.size())
353  trajectory.joint_trajectory.points[i].effort.clear();
354 
355  if (duration_from_previous_.size() > i)
356  {
357  trajectory.joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
358  }
359  else
360  {
361  trajectory.joint_trajectory.points[i].time_from_start = ZERO_DURATION;
362  }
363  }
364  if (!mdof.empty())
365  {
366  trajectory.multi_dof_joint_trajectory.points[i].transforms.resize(mdof.size());
367  for (std::size_t j = 0; j < mdof.size(); ++j)
368  {
369  geometry_msgs::msg::TransformStamped ts = tf2::eigenToTransform(waypoints_[i]->getJointTransform(mdof[j]));
370  trajectory.multi_dof_joint_trajectory.points[i].transforms[j] = ts.transform;
371  // TODO: currently only checking for planar multi DOF joints / need to add check for floating
372  if (waypoints_[i]->hasVelocities() && (mdof[j]->getType() == moveit::core::JointModel::JointType::PLANAR))
373  {
374  const std::vector<std::string> names = mdof[j]->getVariableNames();
375  const double* velocities = waypoints_[i]->getJointVelocities(mdof[j]);
376 
377  geometry_msgs::msg::Twist point_velocity;
378 
379  for (std::size_t k = 0; k < names.size(); ++k)
380  {
381  if (names[k].find("/x") != std::string::npos)
382  {
383  point_velocity.linear.x = velocities[k];
384  }
385  else if (names[k].find("/y") != std::string::npos)
386  {
387  point_velocity.linear.y = velocities[k];
388  }
389  else if (names[k].find("/z") != std::string::npos)
390  {
391  point_velocity.linear.z = velocities[k];
392  }
393  else if (names[k].find("/theta") != std::string::npos)
394  {
395  point_velocity.angular.z = velocities[k];
396  }
397  }
398  trajectory.multi_dof_joint_trajectory.points[i].velocities.push_back(point_velocity);
399  }
400  }
401  if (duration_from_previous_.size() > i)
402  {
403  trajectory.multi_dof_joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
404  }
405  else
406  {
407  trajectory.multi_dof_joint_trajectory.points[i].time_from_start = ZERO_DURATION;
408  }
409  }
410  }
411 }
412 
414  const trajectory_msgs::msg::JointTrajectory& trajectory)
415 {
416  // make a copy just in case the next clear() removes the memory for the reference passed in
417  const moveit::core::RobotState copy(reference_state); // NOLINT(performance-unnecessary-copy-initialization)
418  clear();
419  std::size_t state_count = trajectory.points.size();
420  rclcpp::Time last_time_stamp = trajectory.header.stamp;
421  rclcpp::Time this_time_stamp = last_time_stamp;
422 
423  for (std::size_t i = 0; i < state_count; ++i)
424  {
425  this_time_stamp = rclcpp::Time(trajectory.header.stamp) + trajectory.points[i].time_from_start;
426  auto st = std::make_shared<moveit::core::RobotState>(copy);
427  st->setVariablePositions(trajectory.joint_names, trajectory.points[i].positions);
428  if (!trajectory.points[i].velocities.empty())
429  st->setVariableVelocities(trajectory.joint_names, trajectory.points[i].velocities);
430  if (!trajectory.points[i].accelerations.empty())
431  st->setVariableAccelerations(trajectory.joint_names, trajectory.points[i].accelerations);
432  if (!trajectory.points[i].effort.empty())
433  st->setVariableEffort(trajectory.joint_names, trajectory.points[i].effort);
434  addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
435  last_time_stamp = this_time_stamp;
436  }
437 
438  return *this;
439 }
440 
442  const moveit_msgs::msg::RobotTrajectory& trajectory)
443 {
444  // make a copy just in case the next clear() removes the memory for the reference passed in
445  const moveit::core::RobotState& copy = reference_state;
446  clear();
447 
448  std::size_t state_count =
449  std::max(trajectory.joint_trajectory.points.size(), trajectory.multi_dof_joint_trajectory.points.size());
450  rclcpp::Time last_time_stamp = trajectory.joint_trajectory.points.empty() ?
451  trajectory.multi_dof_joint_trajectory.header.stamp :
452  trajectory.joint_trajectory.header.stamp;
453  rclcpp::Time this_time_stamp = last_time_stamp;
454 
455  for (std::size_t i = 0; i < state_count; ++i)
456  {
457  auto st = std::make_shared<moveit::core::RobotState>(copy);
458  if (trajectory.joint_trajectory.points.size() > i)
459  {
460  st->setVariablePositions(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].positions);
461  if (!trajectory.joint_trajectory.points[i].velocities.empty())
462  {
463  st->setVariableVelocities(trajectory.joint_trajectory.joint_names,
464  trajectory.joint_trajectory.points[i].velocities);
465  }
466  if (!trajectory.joint_trajectory.points[i].accelerations.empty())
467  {
468  st->setVariableAccelerations(trajectory.joint_trajectory.joint_names,
469  trajectory.joint_trajectory.points[i].accelerations);
470  }
471  if (!trajectory.joint_trajectory.points[i].effort.empty())
472  st->setVariableEffort(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].effort);
473  this_time_stamp = rclcpp::Time(trajectory.joint_trajectory.header.stamp) +
474  trajectory.joint_trajectory.points[i].time_from_start;
475  }
476  if (trajectory.multi_dof_joint_trajectory.points.size() > i)
477  {
478  for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.joint_names.size(); ++j)
479  {
480  Eigen::Isometry3d t = tf2::transformToEigen(trajectory.multi_dof_joint_trajectory.points[i].transforms[j]);
481  st->setJointPositions(trajectory.multi_dof_joint_trajectory.joint_names[j], t);
482  }
483  this_time_stamp = rclcpp::Time(trajectory.multi_dof_joint_trajectory.header.stamp) +
484  trajectory.multi_dof_joint_trajectory.points[i].time_from_start;
485  }
486 
487  addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
488  last_time_stamp = this_time_stamp;
489  }
490  return *this;
491 }
492 
494  const moveit_msgs::msg::RobotState& state,
495  const moveit_msgs::msg::RobotTrajectory& trajectory)
496 {
497  moveit::core::RobotState st(reference_state);
499  return setRobotTrajectoryMsg(st, trajectory);
500 }
501 
502 void RobotTrajectory::findWayPointIndicesForDurationAfterStart(double duration, int& before, int& after,
503  double& blend) const
504 {
505  if (duration < 0.0)
506  {
507  before = 0;
508  after = 0;
509  blend = 0;
510  return;
511  }
512 
513  // Find indices
514  std::size_t index = 0, num_points = waypoints_.size();
515  double running_duration = 0.0;
516  for (; index < num_points; ++index)
517  {
518  running_duration += duration_from_previous_[index];
519  if (running_duration >= duration)
520  break;
521  }
522  before = std::max<int>(index - 1, 0);
523  after = std::min<int>(index, num_points - 1);
524 
525  // Compute duration blend
526  double before_time = running_duration - duration_from_previous_[index];
527  if (after == before)
528  {
529  blend = 1.0;
530  }
531  else
532  {
533  blend = (duration - before_time) / duration_from_previous_[index];
534  }
535 }
536 
537 double RobotTrajectory::getWayPointDurationFromStart(std::size_t index) const
538 {
539  if (duration_from_previous_.empty())
540  return 0.0;
541  if (index >= duration_from_previous_.size())
542  index = duration_from_previous_.size() - 1;
543 
544  double time = 0.0;
545  for (std::size_t i = 0; i <= index; ++i)
546  time += duration_from_previous_[i];
547  return time;
548 }
549 
550 bool RobotTrajectory::getStateAtDurationFromStart(const double request_duration,
551  moveit::core::RobotStatePtr& output_state) const
552 {
553  // If there are no waypoints we can't do anything
554  if (getWayPointCount() == 0)
555  return false;
556 
557  int before = 0, after = 0;
558  double blend = 1.0;
559  findWayPointIndicesForDurationAfterStart(request_duration, before, after, blend);
560  // ROS_DEBUG_NAMED("robot_trajectory", "Interpolating %.3f of the way between index %d and %d.", blend, before,
561  // after);
562  waypoints_[before]->interpolate(*waypoints_[after], blend, *output_state);
563  return true;
564 }
565 
566 void RobotTrajectory::print(std::ostream& out, std::vector<int> variable_indexes) const
567 {
568  size_t num_points = getWayPointCount();
569  if (num_points == 0)
570  {
571  out << "Empty trajectory.";
572  return;
573  }
574 
575  std::ios::fmtflags old_settings = out.flags();
576  int old_precision = out.precision();
577  out << std::fixed << std::setprecision(3);
578 
579  out << "Trajectory has " << num_points << " points over " << getDuration() << " seconds\n";
580 
581  if (variable_indexes.empty())
582  {
583  if (group_)
584  {
585  variable_indexes = group_->getVariableIndexList();
586  }
587  else
588  {
589  // use all variables
590  variable_indexes.resize(robot_model_->getVariableCount());
591  std::iota(variable_indexes.begin(), variable_indexes.end(), 0);
592  }
593  }
594 
595  for (size_t p_i = 0; p_i < num_points; ++p_i)
596  {
597  const moveit::core::RobotState& point = getWayPoint(p_i);
598  out << " waypoint " << std::setw(3) << p_i;
599  out << " time " << std::setw(5) << getWayPointDurationFromStart(p_i);
600  out << " pos ";
601  for (int index : variable_indexes)
602  {
603  out << std::setw(6) << point.getVariablePosition(index) << ' ';
604  }
605  if (point.hasVelocities())
606  {
607  out << "vel ";
608  for (int index : variable_indexes)
609  {
610  out << std::setw(6) << point.getVariableVelocity(index) << ' ';
611  }
612  }
613  if (point.hasAccelerations())
614  {
615  out << "acc ";
616  for (int index : variable_indexes)
617  {
618  out << std::setw(6) << point.getVariableAcceleration(index) << ' ';
619  }
620  }
621  if (point.hasEffort())
622  {
623  out << "eff ";
624  for (int index : variable_indexes)
625  {
626  out << std::setw(6) << point.getVariableEffort(index) << ' ';
627  }
628  }
629  out << '\n';
630  }
631 
632  out.flags(old_settings);
633  out.precision(old_precision);
634  out.flush();
635 }
636 
637 std::ostream& operator<<(std::ostream& out, const RobotTrajectory& trajectory)
638 {
639  trajectory.print(out);
640  return out;
641 }
642 
643 double pathLength(const RobotTrajectory& trajectory)
644 {
645  auto trajectory_length = 0.0;
646  for (std::size_t index = 1; index < trajectory.getWayPointCount(); ++index)
647  {
648  const auto& first = trajectory.getWayPoint(index - 1);
649  const auto& second = trajectory.getWayPoint(index);
650  trajectory_length += first.distance(second);
651  }
652  return trajectory_length;
653 }
654 
655 std::optional<double> smoothness(const RobotTrajectory& trajectory)
656 {
657  if (trajectory.getWayPointCount() > 2)
658  {
659  auto smoothness = 0.0;
660  double a = trajectory.getWayPoint(0).distance(trajectory.getWayPoint(1));
661  for (std::size_t k = 2; k < trajectory.getWayPointCount(); ++k)
662  {
663  // view the path as a sequence of segments, and look at the triangles it forms:
664  // s1
665  // /\ s4
666  // a / \ b |
667  // / \ |
668  // /......\_______|
669  // s0 c s2 s3
670 
671  // use Pythagoras generalized theorem to find the cos of the angle between segments a and b
672  double b = trajectory.getWayPoint(k - 1).distance(trajectory.getWayPoint(k));
673  double cdist = trajectory.getWayPoint(k - 2).distance(trajectory.getWayPoint(k));
674  double acos_value = (a * a + b * b - cdist * cdist) / (2.0 * a * b);
675  if (acos_value > -1.0 && acos_value < 1.0)
676  {
677  // the smoothness is actually the outside angle of the one we compute
678  double angle = (M_PI - acos(acos_value));
679 
680  // and we normalize by the length of the segments
681  double u = 2.0 * angle;
682  smoothness += u * u;
683  }
684  a = b;
685  }
686  smoothness /= static_cast<double>(trajectory.getWayPointCount());
687  return smoothness;
688  }
689  // In case the path is to short, no value is returned
690  return std::nullopt;
691 }
692 
693 std::optional<double> waypointDensity(const RobotTrajectory& trajectory)
694 {
695  // Only calculate density if more than one waypoint exists
696  if (trajectory.getWayPointCount() > 1)
697  {
698  // Calculate path length
699  const auto length = pathLength(trajectory);
700  if (length > 0.0)
701  {
702  auto density = static_cast<double>(trajectory.getWayPointCount()) / length;
703  return density;
704  }
705  }
706  // Trajectory is empty, a single point or path length is zero
707  return std::nullopt;
708 }
709 
710 } // end of namespace robot_trajectory
const std::vector< const JointModel * > & getActiveJointModels() const
Get the active joints in this group (that have controllable DOF). This does not include mimic joints.
const std::string & getName() const
Get the name of the joint group.
const std::vector< const JointModel * > & getContinuousJointModels() const
Get the array of continuous joints used in this group (may include mimic joints).
const std::vector< int > & getVariableIndexList() const
Get the index locations in the complete robot state for all the variables in this group.
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
double getVariableAcceleration(const std::string &variable) const
Get the acceleration of a particular variable. An exception is thrown if the variable is not known.
Definition: robot_state.h:395
const double * getJointPositions(const std::string &joint_name) const
Definition: robot_state.h:543
bool hasVelocities() const
By default, if velocities are never set or initialized, the state remembers that there are no velocit...
Definition: robot_state.h:229
double getVariableVelocity(const std::string &variable) const
Get the velocity of a particular variable. An exception is thrown if the variable is not known.
Definition: robot_state.h:296
bool hasAccelerations() const
By default, if accelerations are never set or initialized, the state remembers that there are no acce...
Definition: robot_state.h:322
bool hasEffort() const
By default, if effort is never set or initialized, the state remembers that there is no effort set....
Definition: robot_state.h:420
double * getVariableEffort()
Get raw access to the effort of the variables that make up this state. The values are in the same ord...
Definition: robot_state.h:428
double distance(const RobotState &other) const
Return the sum of joint distances to "other" state. An L1 norm. Only considers active joints.
Definition: robot_state.h:1354
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
Definition: robot_state.h:207
Maintain a sequence of waypoints and the time durations between these waypoints.
void swap(robot_trajectory::RobotTrajectory &other)
const std::string & getGroupName() const
void findWayPointIndicesForDurationAfterStart(double duration, int &before, int &after, double &blend) const
Finds the waypoint indices before and after a duration from start.
double getWayPointDurationFromStart(std::size_t index) const
Returns the duration after start that a waypoint will be reached.
RobotTrajectory(const moveit::core::RobotModelConstPtr &robot_model)
construct a trajectory for the whole robot
void print(std::ostream &out, std::vector< int > variable_indexes=std::vector< int >()) const
Print information about the trajectory.
bool getStateAtDurationFromStart(const double request_duration, moveit::core::RobotStatePtr &output_state) const
Gets a robot state corresponding to a supplied duration from start for the trajectory,...
void getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory &trajectory, const std::vector< std::string > &joint_filter=std::vector< std::string >()) const
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
Add a point to the trajectory.
RobotTrajectory & setRobotTrajectoryMsg(const moveit::core::RobotState &reference_state, const trajectory_msgs::msg::JointTrajectory &trajectory)
Copy the content of the trajectory message into this class. The trajectory message itself is not requ...
RobotTrajectory & append(const RobotTrajectory &source, double dt, size_t start_index=0, size_t end_index=std::numeric_limits< std::size_t >::max())
Add a specified part of a trajectory to the end of the current trajectory. The default (when start_in...
const moveit::core::RobotState & getWayPoint(std::size_t index) const
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::msg::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
Convert a robot state msg (with accompanying extra transforms) to a MoveIt robot state.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
std::optional< double > waypointDensity(const RobotTrajectory &trajectory)
Calculate the waypoint density of a trajectory.
std::optional< double > smoothness(const RobotTrajectory &trajectory)
Calculate the smoothness of a given trajectory.
double pathLength(const RobotTrajectory &trajectory)
Calculate the path length of a given trajectory based on the accumulated robot state distances....
std::ostream & operator<<(std::ostream &out, const RobotTrajectory &trajectory)
Operator overload for printing trajectory to a stream.