moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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>
48
49namespace robot_trajectory
50{
51namespace
52{
53rclcpp::Logger getLogger()
54{
55 return moveit::getLogger("moveit.core.robot_trajectory");
56}
57} // namespace
58
59RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model)
60 : robot_model_(robot_model), group_(nullptr)
61{
62}
63
64RobotTrajectory::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
69RobotTrajectory::RobotTrajectory(const moveit::core::RobotModelConstPtr& robot_model,
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
88const 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
132RobotTrajectory& 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
265void 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 const double* accelerations = waypoints_[i]->getJointAccelerations(mdof[j]);
377
378 geometry_msgs::msg::Twist point_velocity;
379 geometry_msgs::msg::Twist point_acceleration;
380
381 for (std::size_t k = 0; k < names.size(); ++k)
382 {
383 if (names[k].find("/x") != std::string::npos)
384 {
385 point_velocity.linear.x = velocities[k];
386 point_acceleration.linear.x = accelerations[k];
387 }
388 else if (names[k].find("/y") != std::string::npos)
389 {
390 point_velocity.linear.y = velocities[k];
391 point_acceleration.linear.y = accelerations[k];
392 }
393 else if (names[k].find("/z") != std::string::npos)
394 {
395 point_velocity.linear.z = velocities[k];
396 point_acceleration.linear.z = accelerations[k];
397 }
398 else if (names[k].find("/theta") != std::string::npos)
399 {
400 point_velocity.angular.z = velocities[k];
401 point_acceleration.angular.z = accelerations[k];
402 }
403 }
404 trajectory.multi_dof_joint_trajectory.points[i].velocities.push_back(point_velocity);
405 trajectory.multi_dof_joint_trajectory.points[i].accelerations.push_back(point_acceleration);
406 }
407 }
408 if (duration_from_previous_.size() > i)
409 {
410 trajectory.multi_dof_joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
411 }
412 else
413 {
414 trajectory.multi_dof_joint_trajectory.points[i].time_from_start = ZERO_DURATION;
415 }
416 }
417 }
418}
419
421 const trajectory_msgs::msg::JointTrajectory& trajectory)
422{
423 // make a copy just in case the next clear() removes the memory for the reference passed in
424 const moveit::core::RobotState copy(reference_state); // NOLINT(performance-unnecessary-copy-initialization)
425 clear();
426 std::size_t state_count = trajectory.points.size();
427 rclcpp::Time last_time_stamp = trajectory.header.stamp;
428 rclcpp::Time this_time_stamp = last_time_stamp;
429
430 for (std::size_t i = 0; i < state_count; ++i)
431 {
432 this_time_stamp = rclcpp::Time(trajectory.header.stamp) + trajectory.points[i].time_from_start;
433 auto st = std::make_shared<moveit::core::RobotState>(copy);
434 st->setVariablePositions(trajectory.joint_names, trajectory.points[i].positions);
435 if (!trajectory.points[i].velocities.empty())
436 st->setVariableVelocities(trajectory.joint_names, trajectory.points[i].velocities);
437 if (!trajectory.points[i].accelerations.empty())
438 st->setVariableAccelerations(trajectory.joint_names, trajectory.points[i].accelerations);
439 if (!trajectory.points[i].effort.empty())
440 st->setVariableEffort(trajectory.joint_names, trajectory.points[i].effort);
441 addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
442 last_time_stamp = this_time_stamp;
443 }
444
445 return *this;
446}
447
449 const moveit_msgs::msg::RobotTrajectory& trajectory)
450{
451 // make a copy just in case the next clear() removes the memory for the reference passed in
452 const moveit::core::RobotState& copy = reference_state;
453 clear();
454
455 std::size_t state_count =
456 std::max(trajectory.joint_trajectory.points.size(), trajectory.multi_dof_joint_trajectory.points.size());
457 rclcpp::Time last_time_stamp = trajectory.joint_trajectory.points.empty() ?
458 trajectory.multi_dof_joint_trajectory.header.stamp :
459 trajectory.joint_trajectory.header.stamp;
460 rclcpp::Time this_time_stamp = last_time_stamp;
461
462 for (std::size_t i = 0; i < state_count; ++i)
463 {
464 auto st = std::make_shared<moveit::core::RobotState>(copy);
465 if (trajectory.joint_trajectory.points.size() > i)
466 {
467 st->setVariablePositions(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].positions);
468 if (!trajectory.joint_trajectory.points[i].velocities.empty())
469 {
470 st->setVariableVelocities(trajectory.joint_trajectory.joint_names,
471 trajectory.joint_trajectory.points[i].velocities);
472 }
473 if (!trajectory.joint_trajectory.points[i].accelerations.empty())
474 {
475 st->setVariableAccelerations(trajectory.joint_trajectory.joint_names,
476 trajectory.joint_trajectory.points[i].accelerations);
477 }
478 if (!trajectory.joint_trajectory.points[i].effort.empty())
479 st->setVariableEffort(trajectory.joint_trajectory.joint_names, trajectory.joint_trajectory.points[i].effort);
480 this_time_stamp = rclcpp::Time(trajectory.joint_trajectory.header.stamp) +
481 trajectory.joint_trajectory.points[i].time_from_start;
482 }
483 if (trajectory.multi_dof_joint_trajectory.points.size() > i)
484 {
485 for (std::size_t j = 0; j < trajectory.multi_dof_joint_trajectory.joint_names.size(); ++j)
486 {
487 Eigen::Isometry3d t = tf2::transformToEigen(trajectory.multi_dof_joint_trajectory.points[i].transforms[j]);
488 st->setJointPositions(trajectory.multi_dof_joint_trajectory.joint_names[j], t);
489 }
490 this_time_stamp = rclcpp::Time(trajectory.multi_dof_joint_trajectory.header.stamp) +
491 trajectory.multi_dof_joint_trajectory.points[i].time_from_start;
492 }
493
494 addSuffixWayPoint(st, (this_time_stamp - last_time_stamp).seconds());
495 last_time_stamp = this_time_stamp;
496 }
497 return *this;
498}
499
501 const moveit_msgs::msg::RobotState& state,
502 const moveit_msgs::msg::RobotTrajectory& trajectory)
503{
504 moveit::core::RobotState st(reference_state);
506 return setRobotTrajectoryMsg(st, trajectory);
507}
508
509void RobotTrajectory::findWayPointIndicesForDurationAfterStart(double duration, int& before, int& after,
510 double& blend) const
511{
512 if (duration < 0.0)
513 {
514 before = 0;
515 after = 0;
516 blend = 0;
517 return;
518 }
519
520 // Find indices
521 std::size_t index = 0, num_points = waypoints_.size();
522 double running_duration = 0.0;
523 for (; index < num_points; ++index)
524 {
525 running_duration += duration_from_previous_[index];
526 if (running_duration >= duration)
527 break;
528 }
529 before = std::max<int>(index - 1, 0);
530 after = std::min<int>(index, num_points - 1);
531
532 // Compute duration blend
533 double before_time = running_duration - duration_from_previous_[index];
534 if (after == before)
535 {
536 blend = 1.0;
537 }
538 else
539 {
540 blend = (duration - before_time) / duration_from_previous_[index];
541 }
542}
543
545{
546 if (duration_from_previous_.empty())
547 return 0.0;
548 if (index >= duration_from_previous_.size())
549 index = duration_from_previous_.size() - 1;
550
551 double time = 0.0;
552 for (std::size_t i = 0; i <= index; ++i)
553 time += duration_from_previous_[i];
554 return time;
555}
556
557bool RobotTrajectory::getStateAtDurationFromStart(const double request_duration,
558 moveit::core::RobotStatePtr& output_state) const
559{
560 // If there are no waypoints we can't do anything
561 if (getWayPointCount() == 0)
562 return false;
563
564 int before = 0, after = 0;
565 double blend = 1.0;
566 findWayPointIndicesForDurationAfterStart(request_duration, before, after, blend);
567 // ROS_DEBUG_NAMED("robot_trajectory", "Interpolating %.3f of the way between index %d and %d.", blend, before,
568 // after);
569 waypoints_[before]->interpolate(*waypoints_[after], blend, *output_state);
570 return true;
571}
572
573void RobotTrajectory::print(std::ostream& out, std::vector<int> variable_indexes) const
574{
575 size_t num_points = getWayPointCount();
576 if (num_points == 0)
577 {
578 out << "Empty trajectory.";
579 return;
580 }
581
582 std::ios::fmtflags old_settings = out.flags();
583 int old_precision = out.precision();
584 out << std::fixed << std::setprecision(3);
585
586 out << "Trajectory has " << num_points << " points over " << getDuration() << " seconds\n";
587
588 if (variable_indexes.empty())
589 {
590 if (group_)
591 {
592 variable_indexes = group_->getVariableIndexList();
593 }
594 else
595 {
596 // use all variables
597 variable_indexes.resize(robot_model_->getVariableCount());
598 std::iota(variable_indexes.begin(), variable_indexes.end(), 0);
599 }
600 }
601
602 for (size_t p_i = 0; p_i < num_points; ++p_i)
603 {
604 const moveit::core::RobotState& point = getWayPoint(p_i);
605 out << " waypoint " << std::setw(3) << p_i;
606 out << " time " << std::setw(5) << getWayPointDurationFromStart(p_i);
607 out << " pos ";
608 for (int index : variable_indexes)
609 {
610 out << std::setw(6) << point.getVariablePosition(index) << ' ';
611 }
612 if (point.hasVelocities())
613 {
614 out << "vel ";
615 for (int index : variable_indexes)
616 {
617 out << std::setw(6) << point.getVariableVelocity(index) << ' ';
618 }
619 }
620 if (point.hasAccelerations())
621 {
622 out << "acc ";
623 for (int index : variable_indexes)
624 {
625 out << std::setw(6) << point.getVariableAcceleration(index) << ' ';
626 }
627 }
628 if (point.hasEffort())
629 {
630 out << "eff ";
631 for (int index : variable_indexes)
632 {
633 out << std::setw(6) << point.getVariableEffort(index) << ' ';
634 }
635 }
636 out << '\n';
637 }
638
639 out.flags(old_settings);
640 out.precision(old_precision);
641 out.flush();
642}
643
644std::ostream& operator<<(std::ostream& out, const RobotTrajectory& trajectory)
645{
646 trajectory.print(out);
647 return out;
648}
649
650double pathLength(const RobotTrajectory& trajectory)
651{
652 auto trajectory_length = 0.0;
653 for (std::size_t index = 1; index < trajectory.getWayPointCount(); ++index)
654 {
655 const auto& first = trajectory.getWayPoint(index - 1);
656 const auto& second = trajectory.getWayPoint(index);
657 trajectory_length += first.distance(second);
658 }
659 return trajectory_length;
660}
661
662std::optional<double> smoothness(const RobotTrajectory& trajectory)
663{
664 if (trajectory.getWayPointCount() > 2)
665 {
666 auto smoothness = 0.0;
667 double a = trajectory.getWayPoint(0).distance(trajectory.getWayPoint(1));
668 for (std::size_t k = 2; k < trajectory.getWayPointCount(); ++k)
669 {
670 // view the path as a sequence of segments, and look at the triangles it forms:
671 // s1
672 // /\ s4
673 // a / \ b |
674 // / \ |
675 // /......\_______|
676 // s0 c s2 s3
677
678 // use Pythagoras generalized theorem to find the cos of the angle between segments a and b
679 double b = trajectory.getWayPoint(k - 1).distance(trajectory.getWayPoint(k));
680 double cdist = trajectory.getWayPoint(k - 2).distance(trajectory.getWayPoint(k));
681 double acos_value = (a * a + b * b - cdist * cdist) / (2.0 * a * b);
682 if (acos_value > -1.0 && acos_value < 1.0)
683 {
684 // the smoothness is actually the outside angle of the one we compute
685 double angle = (M_PI - acos(acos_value));
686
687 // and we normalize by the length of the segments
688 double u = 2.0 * angle;
689 smoothness += u * u;
690 }
691 a = b;
692 }
693 smoothness /= static_cast<double>(trajectory.getWayPointCount());
694 return smoothness;
695 }
696 // In case the path is to short, no value is returned
697 return std::nullopt;
698}
699
700std::optional<double> waypointDensity(const RobotTrajectory& trajectory)
701{
702 // Only calculate density if more than one waypoint exists
703 if (trajectory.getWayPointCount() > 1)
704 {
705 // Calculate path length
706 const auto length = pathLength(trajectory);
707 if (length > 0.0)
708 {
709 auto density = static_cast<double>(trajectory.getWayPointCount()) / length;
710 return density;
711 }
712 }
713 // Trajectory is empty, a single point or path length is zero
714 return std::nullopt;
715}
716
717std::optional<trajectory_msgs::msg::JointTrajectory> toJointTrajectory(const RobotTrajectory& trajectory,
718 bool include_mdof_joints,
719 const std::vector<std::string>& joint_filter)
720{
721 const auto group = trajectory.getGroup();
722 const auto& robot_model = trajectory.getRobotModel();
723 const std::vector<const moveit::core::JointModel*>& jnts =
724 group ? group->getActiveJointModels() : robot_model->getActiveJointModels();
725
726 if (trajectory.empty() || jnts.empty())
727 return std::nullopt;
728
729 trajectory_msgs::msg::JointTrajectory joint_trajectory;
730 std::vector<const moveit::core::JointModel*> onedof;
731 std::vector<const moveit::core::JointModel*> mdof;
732
733 for (const moveit::core::JointModel* active_joint : jnts)
734 {
735 // only consider joints listed in joint_filter
736 if (!joint_filter.empty() &&
737 std::find(joint_filter.begin(), joint_filter.end(), active_joint->getName()) == joint_filter.end())
738 continue;
739
740 if (active_joint->getVariableCount() == 1)
741 {
742 onedof.push_back(active_joint);
743 }
744 else if (include_mdof_joints)
745 {
746 mdof.push_back(active_joint);
747 }
748 }
749
750 for (const auto& joint : onedof)
751 {
752 joint_trajectory.joint_names.push_back(joint->getName());
753 }
754 for (const auto& joint : mdof)
755 {
756 for (const auto& name : joint->getVariableNames())
757 {
758 joint_trajectory.joint_names.push_back(name);
759 }
760 }
761
762 if (!onedof.empty() || !mdof.empty())
763 {
764 joint_trajectory.header.frame_id = robot_model->getModelFrame();
765 joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
766 joint_trajectory.points.resize(trajectory.getWayPointCount());
767 }
768
769 static const auto ZERO_DURATION = rclcpp::Duration::from_seconds(0);
770 double total_time = 0.0;
771 for (std::size_t i = 0; i < trajectory.getWayPointCount(); ++i)
772 {
773 total_time += trajectory.getWayPointDurationFromPrevious(i);
774 joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
775 const auto& waypoint = trajectory.getWayPoint(i);
776
777 if (!onedof.empty())
778 {
779 joint_trajectory.points[i].positions.resize(onedof.size());
780 joint_trajectory.points[i].velocities.reserve(onedof.size());
781
782 for (std::size_t j = 0; j < onedof.size(); ++j)
783 {
784 joint_trajectory.points[i].positions[j] = waypoint.getVariablePosition(onedof[j]->getFirstVariableIndex());
785 // if we have velocities/accelerations/effort, copy those too
786 if (waypoint.hasVelocities())
787 {
788 joint_trajectory.points[i].velocities.push_back(
789 waypoint.getVariableVelocity(onedof[j]->getFirstVariableIndex()));
790 }
791 if (waypoint.hasAccelerations())
792 {
793 joint_trajectory.points[i].accelerations.push_back(
794 waypoint.getVariableAcceleration(onedof[j]->getFirstVariableIndex()));
795 }
796 if (waypoint.hasEffort())
797 {
798 joint_trajectory.points[i].effort.push_back(waypoint.getVariableEffort(onedof[j]->getFirstVariableIndex()));
799 }
800 }
801 // clear velocities if we have an incomplete specification
802 if (joint_trajectory.points[i].velocities.size() != onedof.size())
803 joint_trajectory.points[i].velocities.clear();
804 // clear accelerations if we have an incomplete specification
805 if (joint_trajectory.points[i].accelerations.size() != onedof.size())
806 joint_trajectory.points[i].accelerations.clear();
807 // clear effort if we have an incomplete specification
808 if (joint_trajectory.points[i].effort.size() != onedof.size())
809 joint_trajectory.points[i].effort.clear();
810 }
811
812 if (!mdof.empty())
813 {
814 for (const auto joint : mdof)
815 {
816 // Add variable placeholders
817 const std::vector<std::string> names = joint->getVariableNames();
818 joint_trajectory.points[i].positions.reserve(joint_trajectory.points[i].positions.size() + names.size());
819
820 joint_trajectory.points[i].velocities.reserve(joint_trajectory.points[i].velocities.size() + names.size());
821
822 for (const auto& name : names)
823 {
824 joint_trajectory.points[i].positions.push_back(waypoint.getVariablePosition(name));
825
826 if (waypoint.hasVelocities())
827 {
828 joint_trajectory.points[i].velocities.push_back(waypoint.getVariableVelocity(name));
829 }
830 if (waypoint.hasAccelerations())
831 {
832 joint_trajectory.points[i].accelerations.push_back(waypoint.getVariableAcceleration(name));
833 }
834 }
835 }
836 }
837 }
838
839 return joint_trajectory;
840}
841
842} // end of namespace robot_trajectory
const std::string & getName() const
Get the name of the joint group.
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::vector< int > & getVariableIndexList() const
Get the index locations in the complete robot state for all the variables in this group.
const std::vector< const JointModel * > & getContinuousJointModels() const
Get the array of continuous joints used in this group (may include mimic joints).
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
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.
bool hasVelocities() const
By default, if velocities are never set or initialized, the state remembers that there are no velocit...
const double * getJointPositions(const std::string &joint_name) const
double getVariableVelocity(const std::string &variable) const
Get the velocity of a particular variable. An exception is thrown if the variable is not known.
bool hasAccelerations() const
By default, if accelerations are never set or initialized, the state remembers that there are no acce...
bool hasEffort() const
By default, if effort is never set or initialized, the state remembers that there is no effort set....
double distance(const RobotState &other) const
Return the sum of joint distances to "other" state. An L1 norm. Only considers active joints.
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
double * getVariableEffort()
Get raw access to the effort of the variables that make up this state. The values are in the same ord...
Maintain a sequence of waypoints and the time durations between these waypoints.
const std::string & getGroupName() const
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
Add a point to the trajectory.
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.
void swap(robot_trajectory::RobotTrajectory &other) noexcept
RobotTrajectory(const moveit::core::RobotModelConstPtr &robot_model)
construct a trajectory for the whole robot
const moveit::core::RobotModelConstPtr & getRobotModel() const
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
const moveit::core::JointModelGroup * getGroup() const
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...
double getWayPointDurationFromPrevious(std::size_t index) const
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< trajectory_msgs::msg::JointTrajectory > toJointTrajectory(const RobotTrajectory &trajectory, bool include_mdof_joints=false, const std::vector< std::string > &joint_filter={})
Converts a RobotTrajectory to a JointTrajectory message.
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.