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 
48 namespace robot_trajectory
49 {
50 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model)
51  : robot_model_(robot_model), group_(nullptr)
52 {
53 }
54 
55 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model, const std::string& group)
56  : robot_model_(robot_model), group_(group.empty() ? nullptr : robot_model->getJointModelGroup(group))
57 {
58 }
59 
60 RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model,
62  : robot_model_(robot_model), group_(group)
63 {
64 }
65 
67 {
68  *this = other; // default assignment operator performs a shallow copy
69  if (deepcopy)
70  {
71  waypoints_.clear();
72  for (const auto& waypoint : other.waypoints_)
73  {
74  waypoints_.emplace_back(std::make_shared<moveit::core::RobotState>(*waypoint));
75  }
76  }
77 }
78 
79 const std::string& RobotTrajectory::getGroupName() const
80 {
81  if (group_)
82  return group_->getName();
83  static const std::string EMPTY;
84  return EMPTY;
85 }
86 
88 {
89  return std::accumulate(duration_from_previous_.begin(), duration_from_previous_.end(), 0.0);
90 }
91 
93 {
94  if (duration_from_previous_.empty())
95  {
96  RCLCPP_WARN(rclcpp::get_logger("RobotTrajectory"), "Too few waypoints to calculate a duration. Returning 0.");
97  return 0.0;
98  }
99 
100  // If the initial segment has a duration of 0, exclude it from the average calculation
101  if (duration_from_previous_[0] == 0)
102  {
103  if (duration_from_previous_.size() <= 1)
104  {
105  RCLCPP_WARN(rclcpp::get_logger("RobotTrajectory"), "First and only waypoint has a duration of 0.");
106  return 0.0;
107  }
108  else
109  return getDuration() / static_cast<double>(duration_from_previous_.size() - 1);
110  }
111  else
112  return getDuration() / static_cast<double>(duration_from_previous_.size());
113 }
114 
116 {
117  robot_model_.swap(other.robot_model_);
118  std::swap(group_, other.group_);
119  waypoints_.swap(other.waypoints_);
120  duration_from_previous_.swap(other.duration_from_previous_);
121 }
122 
123 RobotTrajectory& RobotTrajectory::append(const RobotTrajectory& source, double dt, size_t start_index, size_t end_index)
124 {
125  end_index = std::min(end_index, source.waypoints_.size());
126  if (start_index >= end_index)
127  return *this;
128  waypoints_.insert(waypoints_.end(), std::next(source.waypoints_.begin(), start_index),
129  std::next(source.waypoints_.begin(), end_index));
130  std::size_t index = duration_from_previous_.size();
131  duration_from_previous_.insert(duration_from_previous_.end(),
132  std::next(source.duration_from_previous_.begin(), start_index),
133  std::next(source.duration_from_previous_.begin(), end_index));
134  if (duration_from_previous_.size() > index)
135  duration_from_previous_[index] = dt;
136 
137  return *this;
138 }
139 
141 {
142  std::reverse(waypoints_.begin(), waypoints_.end());
143  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
144  {
145  // reversing the trajectory implies inverting the velocity profile
146  waypoint->invertVelocity();
147  }
148  if (!duration_from_previous_.empty())
149  {
150  duration_from_previous_.push_back(duration_from_previous_.front());
151  std::reverse(duration_from_previous_.begin(), duration_from_previous_.end());
152  duration_from_previous_.pop_back();
153  }
154 
155  return *this;
156 }
157 
159 {
160  if (waypoints_.empty())
161  return *this;
162 
163  const std::vector<const moveit::core::JointModel*>& cont_joints =
164  group_ ? group_->getContinuousJointModels() : robot_model_->getContinuousJointModels();
165 
166  for (const moveit::core::JointModel* cont_joint : cont_joints)
167  {
168  // unwrap continuous joints
169  double running_offset = 0.0;
170  double last_value = waypoints_[0]->getJointPositions(cont_joint)[0];
171  cont_joint->enforcePositionBounds(&last_value);
172  waypoints_[0]->setJointPositions(cont_joint, &last_value);
173 
174  for (std::size_t j = 1; j < waypoints_.size(); ++j)
175  {
176  double current_value = waypoints_[j]->getJointPositions(cont_joint)[0];
177  cont_joint->enforcePositionBounds(&current_value);
178  if (last_value > current_value + M_PI)
179  {
180  running_offset += 2.0 * M_PI;
181  }
182  else if (current_value > last_value + M_PI)
183  {
184  running_offset -= 2.0 * M_PI;
185  }
186 
187  last_value = current_value;
188  current_value += running_offset;
189  waypoints_[j]->setJointPositions(cont_joint, &current_value);
190  }
191  }
192  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
193  {
194  waypoint->update();
195  }
196 
197  return *this;
198 }
199 
201 {
202  if (waypoints_.empty())
203  return *this;
204 
205  const std::vector<const moveit::core::JointModel*>& cont_joints =
206  group_ ? group_->getContinuousJointModels() : robot_model_->getContinuousJointModels();
207 
208  for (const moveit::core::JointModel* cont_joint : cont_joints)
209  {
210  double reference_value0 = state.getJointPositions(cont_joint)[0];
211  double reference_value = reference_value0;
212  cont_joint->enforcePositionBounds(&reference_value);
213 
214  // unwrap continuous joints
215  double running_offset = reference_value0 - reference_value;
216 
217  double last_value = waypoints_[0]->getJointPositions(cont_joint)[0];
218  cont_joint->enforcePositionBounds(&last_value);
219  if (last_value > reference_value + M_PI)
220  {
221  running_offset -= 2.0 * M_PI;
222  }
223  else if (last_value < reference_value - M_PI)
224  {
225  running_offset += 2.0 * M_PI;
226  }
227  double current_start_value = last_value + running_offset;
228  waypoints_[0]->setJointPositions(cont_joint, &current_start_value);
229 
230  for (std::size_t j = 1; j < waypoints_.size(); ++j)
231  {
232  double current_value = waypoints_[j]->getJointPositions(cont_joint)[0];
233  cont_joint->enforcePositionBounds(&current_value);
234  if (last_value > current_value + M_PI)
235  {
236  running_offset += 2.0 * M_PI;
237  }
238  else if (current_value > last_value + M_PI)
239  {
240  running_offset -= 2.0 * M_PI;
241  }
242 
243  last_value = current_value;
244  current_value += running_offset;
245  waypoints_[j]->setJointPositions(cont_joint, &current_value);
246  }
247  }
248  for (moveit::core::RobotStatePtr& waypoint : waypoints_)
249  {
250  waypoint->update();
251  }
252 
253  return *this;
254 }
255 
256 void RobotTrajectory::getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory& trajectory,
257  const std::vector<std::string>& joint_filter) const
258 {
259  trajectory = moveit_msgs::msg::RobotTrajectory();
260  if (waypoints_.empty())
261  return;
262  const std::vector<const moveit::core::JointModel*>& jnts =
263  group_ ? group_->getActiveJointModels() : robot_model_->getActiveJointModels();
264 
265  std::vector<const moveit::core::JointModel*> onedof;
266  std::vector<const moveit::core::JointModel*> mdof;
267  trajectory.joint_trajectory.joint_names.clear();
268  trajectory.multi_dof_joint_trajectory.joint_names.clear();
269 
270  for (const moveit::core::JointModel* active_joint : jnts)
271  {
272  // only consider joints listed in joint_filter
273  if (!joint_filter.empty() &&
274  std::find(joint_filter.begin(), joint_filter.end(), active_joint->getName()) == joint_filter.end())
275  continue;
276 
277  if (active_joint->getVariableCount() == 1)
278  {
279  trajectory.joint_trajectory.joint_names.push_back(active_joint->getName());
280  onedof.push_back(active_joint);
281  }
282  else
283  {
284  trajectory.multi_dof_joint_trajectory.joint_names.push_back(active_joint->getName());
285  mdof.push_back(active_joint);
286  }
287  }
288 
289  if (!onedof.empty())
290  {
291  trajectory.joint_trajectory.header.frame_id = robot_model_->getModelFrame();
292  trajectory.joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
293  trajectory.joint_trajectory.points.resize(waypoints_.size());
294  }
295 
296  if (!mdof.empty())
297  {
298  trajectory.multi_dof_joint_trajectory.header.frame_id = robot_model_->getModelFrame();
299  trajectory.multi_dof_joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
300  trajectory.multi_dof_joint_trajectory.points.resize(waypoints_.size());
301  }
302 
303  static const auto ZERO_DURATION = rclcpp::Duration::from_seconds(0);
304  double total_time = 0.0;
305  for (std::size_t i = 0; i < waypoints_.size(); ++i)
306  {
307  if (duration_from_previous_.size() > i)
308  total_time += duration_from_previous_[i];
309 
310  if (!onedof.empty())
311  {
312  trajectory.joint_trajectory.points[i].positions.resize(onedof.size());
313  trajectory.joint_trajectory.points[i].velocities.reserve(onedof.size());
314 
315  for (std::size_t j = 0; j < onedof.size(); ++j)
316  {
317  trajectory.joint_trajectory.points[i].positions[j] =
318  waypoints_[i]->getVariablePosition(onedof[j]->getFirstVariableIndex());
319  // if we have velocities/accelerations/effort, copy those too
320  if (waypoints_[i]->hasVelocities())
321  {
322  trajectory.joint_trajectory.points[i].velocities.push_back(
323  waypoints_[i]->getVariableVelocity(onedof[j]->getFirstVariableIndex()));
324  }
325  if (waypoints_[i]->hasAccelerations())
326  {
327  trajectory.joint_trajectory.points[i].accelerations.push_back(
328  waypoints_[i]->getVariableAcceleration(onedof[j]->getFirstVariableIndex()));
329  }
330  if (waypoints_[i]->hasEffort())
331  {
332  trajectory.joint_trajectory.points[i].effort.push_back(
333  waypoints_[i]->getVariableEffort(onedof[j]->getFirstVariableIndex()));
334  }
335  }
336  // clear velocities if we have an incomplete specification
337  if (trajectory.joint_trajectory.points[i].velocities.size() != onedof.size())
338  trajectory.joint_trajectory.points[i].velocities.clear();
339  // clear accelerations if we have an incomplete specification
340  if (trajectory.joint_trajectory.points[i].accelerations.size() != onedof.size())
341  trajectory.joint_trajectory.points[i].accelerations.clear();
342  // clear effort if we have an incomplete specification
343  if (trajectory.joint_trajectory.points[i].effort.size() != onedof.size())
344  trajectory.joint_trajectory.points[i].effort.clear();
345 
346  if (duration_from_previous_.size() > i)
347  {
348  trajectory.joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
349  }
350  else
351  {
352  trajectory.joint_trajectory.points[i].time_from_start = ZERO_DURATION;
353  }
354  }
355  if (!mdof.empty())
356  {
357  trajectory.multi_dof_joint_trajectory.points[i].transforms.resize(mdof.size());
358  for (std::size_t j = 0; j < mdof.size(); ++j)
359  {
360  geometry_msgs::msg::TransformStamped ts = tf2::eigenToTransform(waypoints_[i]->getJointTransform(mdof[j]));
361  trajectory.multi_dof_joint_trajectory.points[i].transforms[j] = ts.transform;
362  // TODO: currently only checking for planar multi DOF joints / need to add check for floating
363  if (waypoints_[i]->hasVelocities() && (mdof[j]->getType() == moveit::core::JointModel::JointType::PLANAR))
364  {
365  const std::vector<std::string> names = mdof[j]->getVariableNames();
366  const double* velocities = waypoints_[i]->getJointVelocities(mdof[j]);
367 
368  geometry_msgs::msg::Twist point_velocity;
369 
370  for (std::size_t k = 0; k < names.size(); ++k)
371  {
372  if (names[k].find("/x") != std::string::npos)
373  {
374  point_velocity.linear.x = velocities[k];
375  }
376  else if (names[k].find("/y") != std::string::npos)
377  {
378  point_velocity.linear.y = velocities[k];
379  }
380  else if (names[k].find("/z") != std::string::npos)
381  {
382  point_velocity.linear.z = velocities[k];
383  }
384  else if (names[k].find("/theta") != std::string::npos)
385  {
386  point_velocity.angular.z = velocities[k];
387  }
388  }
389  trajectory.multi_dof_joint_trajectory.points[i].velocities.push_back(point_velocity);
390  }
391  }
392  if (duration_from_previous_.size() > i)
393  {
394  trajectory.multi_dof_joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
395  }
396  else
397  {
398  trajectory.multi_dof_joint_trajectory.points[i].time_from_start = ZERO_DURATION;
399  }
400  }
401  }
402 }
403 
405  const trajectory_msgs::msg::JointTrajectory& trajectory)
406 {
407  // make a copy just in case the next clear() removes the memory for the reference passed in
408  const moveit::core::RobotState copy(reference_state); // NOLINT(performance-unnecessary-copy-initialization)
409  clear();
410  std::size_t state_count = trajectory.points.size();
411  rclcpp::Time last_time_stamp = trajectory.header.stamp;
412  rclcpp::Time this_time_stamp = last_time_stamp;
413 
414  for (std::size_t i = 0; i < state_count; ++i)
415  {
416  this_time_stamp = rclcpp::Time(trajectory.header.stamp) + trajectory.points[i].time_from_start;
417  auto st = std::make_shared<moveit::core::RobotState>(copy);
418  st->setVariablePositions(trajectory.joint_names, trajectory.points[i].positions);
419  if (!trajectory.points[i].velocities.empty())
420  st->setVariableVelocities(trajectory.joint_names, trajectory.points[i].velocities);
421  if (!trajectory.points[i].accelerations.empty())
422  st->setVariableAccelerations(trajectory.joint_names, trajectory.points[i].accelerations);
423  if (!trajectory.points[i].effort.empty())
424  st->setVariableEffort(trajectory.joint_names, trajectory.points[i].effort);
425  addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
426  last_time_stamp = this_time_stamp;
427  }
428 
429  return *this;
430 }
431 
433  const moveit_msgs::msg::RobotTrajectory& trajectory)
434 {
435  // make a copy just in case the next clear() removes the memory for the reference passed in
436  const moveit::core::RobotState& copy = reference_state;
437  clear();
438 
439  std::size_t state_count =
440  std::max(trajectory.joint_trajectory.points.size(), trajectory.multi_dof_joint_trajectory.points.size());
441  rclcpp::Time last_time_stamp = trajectory.joint_trajectory.points.empty() ?
442  trajectory.multi_dof_joint_trajectory.header.stamp :
443  trajectory.joint_trajectory.header.stamp;
444  rclcpp::Time this_time_stamp = last_time_stamp;
445 
446  for (std::size_t i = 0; i < state_count; ++i)
447  {
448  auto st = std::make_shared<moveit::core::RobotState>(copy);
449  if (trajectory.joint_trajectory.points.size() > i)
450  {
451  st->setVariablePositions(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].positions);
452  if (!trajectory.joint_trajectory.points[i].velocities.empty())
453  {
454  st->setVariableVelocities(trajectory.joint_trajectory.joint_names,
455  trajectory.joint_trajectory.points[i].velocities);
456  }
457  if (!trajectory.joint_trajectory.points[i].accelerations.empty())
458  {
459  st->setVariableAccelerations(trajectory.joint_trajectory.joint_names,
460  trajectory.joint_trajectory.points[i].accelerations);
461  }
462  if (!trajectory.joint_trajectory.points[i].effort.empty())
463  st->setVariableEffort(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].effort);
464  this_time_stamp = rclcpp::Time(trajectory.joint_trajectory.header.stamp) +
465  trajectory.joint_trajectory.points[i].time_from_start;
466  }
467  if (trajectory.multi_dof_joint_trajectory.points.size() > i)
468  {
469  for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.joint_names.size(); ++j)
470  {
471  Eigen::Isometry3d t = tf2::transformToEigen(trajectory.multi_dof_joint_trajectory.points[i].transforms[j]);
472  st->setJointPositions(trajectory.multi_dof_joint_trajectory.joint_names[j], t);
473  }
474  this_time_stamp = rclcpp::Time(trajectory.multi_dof_joint_trajectory.header.stamp) +
475  trajectory.multi_dof_joint_trajectory.points[i].time_from_start;
476  }
477 
478  addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
479  last_time_stamp = this_time_stamp;
480  }
481  return *this;
482 }
483 
485  const moveit_msgs::msg::RobotState& state,
486  const moveit_msgs::msg::RobotTrajectory& trajectory)
487 {
488  moveit::core::RobotState st(reference_state);
490  return setRobotTrajectoryMsg(st, trajectory);
491 }
492 
493 void RobotTrajectory::findWayPointIndicesForDurationAfterStart(double duration, int& before, int& after,
494  double& blend) const
495 {
496  if (duration < 0.0)
497  {
498  before = 0;
499  after = 0;
500  blend = 0;
501  return;
502  }
503 
504  // Find indices
505  std::size_t index = 0, num_points = waypoints_.size();
506  double running_duration = 0.0;
507  for (; index < num_points; ++index)
508  {
509  running_duration += duration_from_previous_[index];
510  if (running_duration >= duration)
511  break;
512  }
513  before = std::max<int>(index - 1, 0);
514  after = std::min<int>(index, num_points - 1);
515 
516  // Compute duration blend
517  double before_time = running_duration - duration_from_previous_[index];
518  if (after == before)
519  {
520  blend = 1.0;
521  }
522  else
523  {
524  blend = (duration - before_time) / duration_from_previous_[index];
525  }
526 }
527 
528 double RobotTrajectory::getWayPointDurationFromStart(std::size_t index) const
529 {
530  if (duration_from_previous_.empty())
531  return 0.0;
532  if (index >= duration_from_previous_.size())
533  index = duration_from_previous_.size() - 1;
534 
535  double time = 0.0;
536  for (std::size_t i = 0; i <= index; ++i)
537  time += duration_from_previous_[i];
538  return time;
539 }
540 
541 bool RobotTrajectory::getStateAtDurationFromStart(const double request_duration,
542  moveit::core::RobotStatePtr& output_state) const
543 {
544  // If there are no waypoints we can't do anything
545  if (getWayPointCount() == 0)
546  return false;
547 
548  int before = 0, after = 0;
549  double blend = 1.0;
550  findWayPointIndicesForDurationAfterStart(request_duration, before, after, blend);
551  // ROS_DEBUG_NAMED("robot_trajectory", "Interpolating %.3f of the way between index %d and %d.", blend, before,
552  // after);
553  waypoints_[before]->interpolate(*waypoints_[after], blend, *output_state);
554  return true;
555 }
556 
557 void RobotTrajectory::print(std::ostream& out, std::vector<int> variable_indexes) const
558 {
559  size_t num_points = getWayPointCount();
560  if (num_points == 0)
561  {
562  out << "Empty trajectory.";
563  return;
564  }
565 
566  std::ios::fmtflags old_settings = out.flags();
567  int old_precision = out.precision();
568  out << std::fixed << std::setprecision(3);
569 
570  out << "Trajectory has " << num_points << " points over " << getDuration() << " seconds\n";
571 
572  if (variable_indexes.empty())
573  {
574  if (group_)
575  {
576  variable_indexes = group_->getVariableIndexList();
577  }
578  else
579  {
580  // use all variables
581  variable_indexes.resize(robot_model_->getVariableCount());
582  std::iota(variable_indexes.begin(), variable_indexes.end(), 0);
583  }
584  }
585 
586  for (size_t p_i = 0; p_i < num_points; ++p_i)
587  {
588  const moveit::core::RobotState& point = getWayPoint(p_i);
589  out << " waypoint " << std::setw(3) << p_i;
590  out << " time " << std::setw(5) << getWayPointDurationFromStart(p_i);
591  out << " pos ";
592  for (int index : variable_indexes)
593  {
594  out << std::setw(6) << point.getVariablePosition(index) << ' ';
595  }
596  if (point.hasVelocities())
597  {
598  out << "vel ";
599  for (int index : variable_indexes)
600  {
601  out << std::setw(6) << point.getVariableVelocity(index) << ' ';
602  }
603  }
604  if (point.hasAccelerations())
605  {
606  out << "acc ";
607  for (int index : variable_indexes)
608  {
609  out << std::setw(6) << point.getVariableAcceleration(index) << ' ';
610  }
611  }
612  if (point.hasEffort())
613  {
614  out << "eff ";
615  for (int index : variable_indexes)
616  {
617  out << std::setw(6) << point.getVariableEffort(index) << ' ';
618  }
619  }
620  out << '\n';
621  }
622 
623  out.flags(old_settings);
624  out.precision(old_precision);
625  out.flush();
626 }
627 
628 std::ostream& operator<<(std::ostream& out, const RobotTrajectory& trajectory)
629 {
630  trajectory.print(out);
631  return out;
632 }
633 
634 double pathLength(const RobotTrajectory& trajectory)
635 {
636  auto trajectory_length = 0.0;
637  for (std::size_t index = 1; index < trajectory.getWayPointCount(); ++index)
638  {
639  const auto& first = trajectory.getWayPoint(index - 1);
640  const auto& second = trajectory.getWayPoint(index);
641  trajectory_length += first.distance(second);
642  }
643  return trajectory_length;
644 }
645 
646 std::optional<double> smoothness(const RobotTrajectory& trajectory)
647 {
648  if (trajectory.getWayPointCount() > 2)
649  {
650  auto smoothness = 0.0;
651  double a = trajectory.getWayPoint(0).distance(trajectory.getWayPoint(1));
652  for (std::size_t k = 2; k < trajectory.getWayPointCount(); ++k)
653  {
654  // view the path as a sequence of segments, and look at the triangles it forms:
655  // s1
656  // /\ s4
657  // a / \ b |
658  // / \ |
659  // /......\_______|
660  // s0 c s2 s3
661 
662  // use Pythagoras generalized theorem to find the cos of the angle between segments a and b
663  double b = trajectory.getWayPoint(k - 1).distance(trajectory.getWayPoint(k));
664  double cdist = trajectory.getWayPoint(k - 2).distance(trajectory.getWayPoint(k));
665  double acos_value = (a * a + b * b - cdist * cdist) / (2.0 * a * b);
666  if (acos_value > -1.0 && acos_value < 1.0)
667  {
668  // the smoothness is actually the outside angle of the one we compute
669  double angle = (M_PI - acos(acos_value));
670 
671  // and we normalize by the length of the segments
672  double u = 2.0 * angle;
673  smoothness += u * u;
674  }
675  a = b;
676  }
677  smoothness /= static_cast<double>(trajectory.getWayPointCount());
678  return smoothness;
679  }
680  // In case the path is to short, no value is returned
681  return std::nullopt;
682 }
683 
684 std::optional<double> waypointDensity(const RobotTrajectory& trajectory)
685 {
686  // Only calculate density if more than one waypoint exists
687  if (trajectory.getWayPointCount() > 1)
688  {
689  // Calculate path length
690  const auto length = pathLength(trajectory);
691  if (length > 0.0)
692  {
693  auto density = static_cast<double>(trajectory.getWayPointCount()) / length;
694  return density;
695  }
696  }
697  // Trajectory is empty, a single point or path length is zero
698  return std::nullopt;
699 }
700 
701 } // 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:557
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:1389
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.
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.