moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
trajectory_cache.cpp
Go to the documentation of this file.
1// Copyright 2024 Intrinsic Innovation LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
20#include <memory>
21#include <vector>
22
26#include <warehouse_ros/message_collection.h>
27
28#include <rclcpp/rclcpp.hpp>
29#include <rclcpp/logging.hpp>
31
32namespace moveit_ros
33{
34namespace trajectory_cache
35{
36
37using warehouse_ros::MessageWithMetadata;
38using warehouse_ros::Metadata;
39using warehouse_ros::Query;
40
41// Utils =======================================================================
42
43// Ensure we always have a workspace frame ID.
44//
45// It makes sense to use getModelFrame() in the absence of a workspace parameter frame ID because the same method is
46// used to populate the workspace header frame ID in the MoveGroupInterface's setWorkspace() method, which this function
47// is associated with.
49 const moveit_msgs::msg::WorkspaceParameters& workspace_parameters)
50{
51 if (workspace_parameters.header.frame_id.empty())
52 {
53 return move_group.getRobotModel()->getModelFrame();
54 }
55 else
56 {
57 return workspace_parameters.header.frame_id;
58 }
59}
60
61// Ensure we always have a cartesian path request frame ID.
62//
63// It makes sense to use getPoseReferenceFrame() in the absence of a frame ID in the request because the same method is
64// used to populate the header frame ID in the MoveGroupInterface's computeCartesianPath() method, which this function is associated with.
66 const moveit_msgs::srv::GetCartesianPath::Request& path_request)
67{
68 if (path_request.header.frame_id.empty())
69 {
70 return move_group.getPoseReferenceFrame();
71 }
72 else
73 {
74 return path_request.header.frame_id;
75 }
76}
77
78// Append a range inclusive query with some tolerance about some center value.
79void queryAppendRangeInclusiveWithTolerance(Query& query, const std::string& name, double center, double tolerance)
80{
81 query.appendRangeInclusive(name, center - tolerance / 2, center + tolerance / 2);
82}
83
84// Sort constraint components by joint or link name.
85void sortConstraints(std::vector<moveit_msgs::msg::JointConstraint>& joint_constraints,
86 std::vector<moveit_msgs::msg::PositionConstraint>& position_constraints,
87 std::vector<moveit_msgs::msg::OrientationConstraint>& orientation_constraints)
88{
89 std::sort(joint_constraints.begin(), joint_constraints.end(),
90 [](const moveit_msgs::msg::JointConstraint& l, const moveit_msgs::msg::JointConstraint& r) {
91 return l.joint_name < r.joint_name;
92 });
93
94 std::sort(position_constraints.begin(), position_constraints.end(),
95 [](const moveit_msgs::msg::PositionConstraint& l, const moveit_msgs::msg::PositionConstraint& r) {
96 return l.link_name < r.link_name;
97 });
98
99 std::sort(orientation_constraints.begin(), orientation_constraints.end(),
100 [](const moveit_msgs::msg::OrientationConstraint& l, const moveit_msgs::msg::OrientationConstraint& r) {
101 return l.link_name < r.link_name;
102 });
103}
104
105// Trajectory Cache ============================================================
106
107TrajectoryCache::TrajectoryCache(const rclcpp::Node::SharedPtr& node)
108 : node_(node), logger_(moveit::getLogger("moveit.ros.trajectory_cache"))
109{
110 tf_buffer_ = std::make_unique<tf2_ros::Buffer>(node_->get_clock());
111 tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
112}
113
115{
116 RCLCPP_DEBUG(logger_, "Opening trajectory cache database at: %s (Port: %d, Precision: %f)", options.db_path.c_str(),
117 options.db_port, options.exact_match_precision);
118
119 // If the `warehouse_plugin` parameter isn't set, defaults to warehouse_ros'
120 // default.
122 options_ = options;
123
124 db_->setParams(options.db_path, options.db_port);
125 return db_->connect();
126}
127
128unsigned TrajectoryCache::countTrajectories(const std::string& cache_namespace)
129{
130 auto coll = db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_trajectory_cache", cache_namespace);
131 return coll.count();
132}
133
134unsigned TrajectoryCache::countCartesianTrajectories(const std::string& cache_namespace)
135{
136 auto coll =
137 db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_cartesian_trajectory_cache", cache_namespace);
138 return coll.count();
139}
140
141// =============================================================================
142// GETTERS AND SETTERS
143// =============================================================================
144
145std::string TrajectoryCache::getDbPath() const
146{
147 return options_.db_path;
148}
149
151{
152 return options_.db_port;
153}
154
156{
157 return options_.exact_match_precision;
158}
159
160void TrajectoryCache::setExactMatchPrecision(double exact_match_precision)
161{
162 options_.exact_match_precision = exact_match_precision;
163}
164
169
171 size_t num_additional_trajectories_to_preserve_when_deleting_worse)
172{
174 num_additional_trajectories_to_preserve_when_deleting_worse;
175}
176
177// =============================================================================
178// MOTION PLAN TRAJECTORY CACHING
179// =============================================================================
180
181std::vector<MessageWithMetadata<moveit_msgs::msg::RobotTrajectory>::ConstPtr>
183 const std::string& cache_namespace,
184 const moveit_msgs::msg::MotionPlanRequest& plan_request,
185 double start_tolerance, double goal_tolerance, bool metadata_only,
186 const std::string& sort_by, bool ascending) const
187{
188 auto coll = db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_trajectory_cache", cache_namespace);
189
190 Query::Ptr query = coll.createQuery();
191
192 bool start_ok = this->extractAndAppendTrajectoryStartToQuery(*query, move_group, plan_request, start_tolerance);
193 bool goal_ok = this->extractAndAppendTrajectoryGoalToQuery(*query, move_group, plan_request, goal_tolerance);
194
195 if (!start_ok || !goal_ok)
196 {
197 RCLCPP_ERROR(logger_, "Could not construct trajectory query.");
198 return {};
199 }
200
201 return coll.queryList(query, metadata_only, sort_by, ascending);
202}
203
204MessageWithMetadata<moveit_msgs::msg::RobotTrajectory>::ConstPtr TrajectoryCache::fetchBestMatchingTrajectory(
205 const moveit::planning_interface::MoveGroupInterface& move_group, const std::string& cache_namespace,
206 const moveit_msgs::msg::MotionPlanRequest& plan_request, double start_tolerance, double goal_tolerance,
207 bool metadata_only, const std::string& sort_by, bool ascending) const
208{
209 // First find all matching, but metadata only.
210 // Then use the ID metadata of the best plan to pull the actual message.
211 auto matching_trajectories = this->fetchAllMatchingTrajectories(
212 move_group, cache_namespace, plan_request, start_tolerance, goal_tolerance, true, sort_by, ascending);
213
214 if (matching_trajectories.empty())
215 {
216 RCLCPP_DEBUG(logger_, "No matching trajectories found.");
217 return nullptr;
218 }
219
220 auto coll = db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_trajectory_cache", cache_namespace);
221
222 // Best plan is at first index, since the lookup query was sorted by
223 // execution_time.
224 int best_trajectory_id = matching_trajectories.at(0)->lookupInt("id");
225 Query::Ptr best_query = coll.createQuery();
226 best_query->append("id", best_trajectory_id);
227
228 return coll.findOne(best_query, metadata_only);
229}
230
232 const std::string& cache_namespace,
233 const moveit_msgs::msg::MotionPlanRequest& plan_request,
234 const moveit_msgs::msg::RobotTrajectory& trajectory, double execution_time_s,
235 double planning_time_s, bool prune_worse_trajectories)
236{
237 std::string workspace_frame_id = getWorkspaceFrameId(move_group, plan_request.workspace_parameters);
238
239 // Check pre-conditions
240 if (!trajectory.multi_dof_joint_trajectory.points.empty())
241 {
242 RCLCPP_ERROR(logger_, "Skipping plan insert: Multi-DOF trajectory plans are not supported.");
243 return false;
244 }
245 if (workspace_frame_id.empty())
246 {
247 RCLCPP_ERROR(logger_, "Skipping plan insert: Workspace frame ID cannot be empty.");
248 return false;
249 }
250 if (trajectory.joint_trajectory.header.frame_id.empty())
251 {
252 RCLCPP_ERROR(logger_, "Skipping plan insert: Trajectory frame ID cannot be empty.");
253 return false;
254 }
255 if (workspace_frame_id != trajectory.joint_trajectory.header.frame_id)
256 {
257 RCLCPP_ERROR(logger_,
258 "Skipping plan insert: "
259 "Plan request frame (%s) does not match plan frame (%s).",
260 workspace_frame_id.c_str(), trajectory.joint_trajectory.header.frame_id.c_str());
261 return false;
262 }
263
264 auto coll = db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_trajectory_cache", cache_namespace);
265
266 // Pull out trajectories "exactly" keyed by request in cache.
267 Query::Ptr exact_query = coll.createQuery();
268
269 bool start_query_ok = this->extractAndAppendTrajectoryStartToQuery(*exact_query, move_group, plan_request, 0);
270 bool goal_query_ok = this->extractAndAppendTrajectoryGoalToQuery(*exact_query, move_group, plan_request, 0);
271
272 if (!start_query_ok || !goal_query_ok)
273 {
274 RCLCPP_ERROR(logger_, "Skipping plan insert: Could not construct lookup query.");
275 return false;
276 }
277
278 auto exact_matches =
279 coll.queryList(exact_query, /*metadata_only=*/true, /*sort_by=*/"execution_time_s", /*ascending=*/true);
280
281 double best_execution_time = std::numeric_limits<double>::infinity();
282 if (!exact_matches.empty())
283 {
284 best_execution_time = exact_matches.at(0)->lookupDouble("execution_time_s");
285
286 if (prune_worse_trajectories)
287 {
288 size_t preserved_count = 0;
289 for (auto& match : exact_matches)
290 {
291 double match_execution_time_s = match->lookupDouble("execution_time_s");
292 if (++preserved_count > options_.num_additional_trajectories_to_preserve_when_deleting_worse &&
293 execution_time_s < match_execution_time_s)
294 {
295 int delete_id = match->lookupInt("id");
296 RCLCPP_DEBUG(logger_,
297 "Overwriting plan (id: %d): "
298 "execution_time (%es) > new trajectory's execution_time (%es)",
299 delete_id, match_execution_time_s, execution_time_s);
300
301 Query::Ptr delete_query = coll.createQuery();
302 delete_query->append("id", delete_id);
303 coll.removeMessages(delete_query);
304 }
305 }
306 }
307 }
308
309 // Insert if candidate is best seen.
310 if (execution_time_s < best_execution_time)
311 {
312 Metadata::Ptr insert_metadata = coll.createMetadata();
313
314 bool start_meta_ok = this->extractAndAppendTrajectoryStartToMetadata(*insert_metadata, move_group, plan_request);
315 bool goal_meta_ok = this->extractAndAppendTrajectoryGoalToMetadata(*insert_metadata, move_group, plan_request);
316 insert_metadata->append("execution_time_s", execution_time_s);
317 insert_metadata->append("planning_time_s", planning_time_s);
318
319 if (!start_meta_ok || !goal_meta_ok)
320 {
321 RCLCPP_ERROR(logger_, "Skipping plan insert: Could not construct insert metadata.");
322 return false;
323 }
324
325 RCLCPP_DEBUG(logger_,
326 "Inserting trajectory: New trajectory execution_time (%es) "
327 "is better than best trajectory's execution_time (%es)",
328 execution_time_s, best_execution_time);
329
330 coll.insert(trajectory, insert_metadata);
331 return true;
332 }
333
334 RCLCPP_DEBUG(logger_,
335 "Skipping plan insert: New trajectory execution_time (%es) "
336 "is worse than best trajectory's execution_time (%es)",
337 execution_time_s, best_execution_time);
338 return false;
339}
340
341// =============================================================================
342// CARTESIAN TRAJECTORY CACHING
343// =============================================================================
344
345moveit_msgs::srv::GetCartesianPath::Request
347 const std::vector<geometry_msgs::msg::Pose>& waypoints,
348 double max_step, double jump_threshold, bool avoid_collisions)
349{
350 moveit_msgs::srv::GetCartesianPath::Request out;
351
352 move_group.constructRobotState(out.start_state);
353
354 out.group_name = move_group.getName();
355 out.max_velocity_scaling_factor = move_group.getMaxVelocityScalingFactor();
356 out.max_acceleration_scaling_factor = move_group.getMaxVelocityScalingFactor();
357
358 out.header.frame_id = move_group.getPoseReferenceFrame();
359 out.waypoints = waypoints;
360 out.max_step = max_step;
361 out.jump_threshold = jump_threshold;
362 out.path_constraints = moveit_msgs::msg::Constraints();
363 out.avoid_collisions = avoid_collisions;
364 out.link_name = move_group.getEndEffectorLink();
365 out.header.stamp = move_group.getNode()->now();
366
367 return out;
368}
369
370std::vector<MessageWithMetadata<moveit_msgs::msg::RobotTrajectory>::ConstPtr>
372 const std::string& cache_namespace,
373 const moveit_msgs::srv::GetCartesianPath::Request& plan_request,
374 double min_fraction, double start_tolerance,
375 double goal_tolerance, bool metadata_only,
376 const std::string& sort_by, bool ascending) const
377{
378 auto coll =
379 db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_cartesian_trajectory_cache", cache_namespace);
380
381 Query::Ptr query = coll.createQuery();
382
383 bool start_ok =
384 this->extractAndAppendCartesianTrajectoryStartToQuery(*query, move_group, plan_request, start_tolerance);
385 bool goal_ok = this->extractAndAppendCartesianTrajectoryGoalToQuery(*query, move_group, plan_request, goal_tolerance);
386
387 if (!start_ok || !goal_ok)
388 {
389 RCLCPP_ERROR(logger_, "Could not construct cartesian trajectory query.");
390 return {};
391 }
392
393 query->appendGTE("fraction", min_fraction);
394 return coll.queryList(query, metadata_only, sort_by, ascending);
395}
396
397MessageWithMetadata<moveit_msgs::msg::RobotTrajectory>::ConstPtr TrajectoryCache::fetchBestMatchingCartesianTrajectory(
398 const moveit::planning_interface::MoveGroupInterface& move_group, const std::string& cache_namespace,
399 const moveit_msgs::srv::GetCartesianPath::Request& plan_request, double min_fraction, double start_tolerance,
400 double goal_tolerance, bool metadata_only, const std::string& sort_by, bool ascending) const
401{
402 // First find all matching, but metadata only.
403 // Then use the ID metadata of the best plan to pull the actual message.
404 auto matching_trajectories =
405 this->fetchAllMatchingCartesianTrajectories(move_group, cache_namespace, plan_request, min_fraction,
406 start_tolerance, goal_tolerance, true, sort_by, ascending);
407
408 if (matching_trajectories.empty())
409 {
410 RCLCPP_DEBUG(logger_, "No matching cartesian trajectories found.");
411 return nullptr;
412 }
413
414 auto coll =
415 db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_cartesian_trajectory_cache", cache_namespace);
416
417 // Best plan is at first index, since the lookup query was sorted by
418 // execution_time.
419 int best_trajectory_id = matching_trajectories.at(0)->lookupInt("id");
420 Query::Ptr best_query = coll.createQuery();
421 best_query->append("id", best_trajectory_id);
422
423 return coll.findOne(best_query, metadata_only);
424}
425
427 const std::string& cache_namespace,
428 const moveit_msgs::srv::GetCartesianPath::Request& plan_request,
429 const moveit_msgs::msg::RobotTrajectory& trajectory,
430 double execution_time_s, double planning_time_s, double fraction,
431 bool prune_worse_trajectories)
432{
433 std::string path_request_frame_id = getCartesianPathRequestFrameId(move_group, plan_request);
434
435 // Check pre-conditions
436 if (!trajectory.multi_dof_joint_trajectory.points.empty())
437 {
438 RCLCPP_ERROR(logger_, "Skipping cartesian trajectory insert: "
439 "Multi-DOF trajectory plans are not supported.");
440 return false;
441 }
442 if (path_request_frame_id.empty())
443 {
444 RCLCPP_ERROR(logger_, "Skipping cartesian trajectory insert: Path request frame ID cannot be empty.");
445 return false;
446 }
447 if (trajectory.joint_trajectory.header.frame_id.empty())
448 {
449 RCLCPP_ERROR(logger_, "Skipping cartesian trajectory insert: Trajectory frame ID cannot be empty.");
450 return false;
451 }
452
453 auto coll =
454 db_->openCollection<moveit_msgs::msg::RobotTrajectory>("move_group_cartesian_trajectory_cache", cache_namespace);
455
456 // Pull out trajectories "exactly" keyed by request in cache.
457 Query::Ptr exact_query = coll.createQuery();
458
459 bool start_query_ok =
460 this->extractAndAppendCartesianTrajectoryStartToQuery(*exact_query, move_group, plan_request, 0);
461 bool goal_query_ok = this->extractAndAppendCartesianTrajectoryGoalToQuery(*exact_query, move_group, plan_request, 0);
462 exact_query->append("fraction", fraction);
463
464 if (!start_query_ok || !goal_query_ok)
465 {
466 RCLCPP_ERROR(logger_, "Skipping cartesian trajectory insert: Could not construct lookup query.");
467 return false;
468 }
469
470 auto exact_matches =
471 coll.queryList(exact_query, /*metadata_only=*/true, /*sort_by=*/"execution_time_s", /*ascending=*/true);
472 double best_execution_time = std::numeric_limits<double>::infinity();
473 if (!exact_matches.empty())
474 {
475 best_execution_time = exact_matches.at(0)->lookupDouble("execution_time_s");
476
477 if (prune_worse_trajectories)
478 {
479 size_t preserved_count = 0;
480 for (auto& match : exact_matches)
481 {
482 double match_execution_time_s = match->lookupDouble("execution_time_s");
483 if (++preserved_count > options_.num_additional_trajectories_to_preserve_when_deleting_worse &&
484 execution_time_s < match_execution_time_s)
485 {
486 int delete_id = match->lookupInt("id");
487 RCLCPP_DEBUG(logger_,
488 "Overwriting cartesian trajectory (id: %d): "
489 "execution_time (%es) > new trajectory's execution_time (%es)",
490 delete_id, match_execution_time_s, execution_time_s);
491
492 Query::Ptr delete_query = coll.createQuery();
493 delete_query->append("id", delete_id);
494 coll.removeMessages(delete_query);
495 }
496 }
497 }
498 }
499
500 // Insert if candidate is best seen.
501 if (execution_time_s < best_execution_time)
502 {
503 Metadata::Ptr insert_metadata = coll.createMetadata();
504
505 bool start_meta_ok =
506 this->extractAndAppendCartesianTrajectoryStartToMetadata(*insert_metadata, move_group, plan_request);
507 bool goal_meta_ok =
508 this->extractAndAppendCartesianTrajectoryGoalToMetadata(*insert_metadata, move_group, plan_request);
509 insert_metadata->append("execution_time_s", execution_time_s);
510 insert_metadata->append("planning_time_s", planning_time_s);
511 insert_metadata->append("fraction", fraction);
512
513 if (!start_meta_ok || !goal_meta_ok)
514 {
515 RCLCPP_ERROR(logger_, "Skipping cartesian trajectory insert: "
516 "Could not construct insert metadata.");
517 return false;
518 }
519
520 RCLCPP_DEBUG(logger_,
521 "Inserting cartesian trajectory: New trajectory execution_time (%es) "
522 "is better than best trajectory's execution_time (%es) at fraction (%es)",
523 execution_time_s, best_execution_time, fraction);
524
525 coll.insert(trajectory, insert_metadata);
526 return true;
527 }
528
529 RCLCPP_DEBUG(logger_,
530 "Skipping cartesian trajectory insert: New trajectory execution_time (%es) "
531 "is worse than best trajectory's execution_time (%es) at fraction (%es)",
532 execution_time_s, best_execution_time, fraction);
533 return false;
534}
535
536// =============================================================================
537// MOTION PLAN TRAJECTORY QUERY AND METADATA CONSTRUCTION
538// =============================================================================
539
540// MOTION PLAN TRAJECTORY CACHING: QUERY CONSTRUCTION ==========================
541
542bool TrajectoryCache::extractAndAppendTrajectoryStartToQuery(
544 const moveit_msgs::msg::MotionPlanRequest& plan_request, double match_tolerance) const
545{
546 std::string workspace_frame_id = getWorkspaceFrameId(move_group, plan_request.workspace_parameters);
547 match_tolerance += options_.exact_match_precision;
548
549 // Make ignored members explicit
550 if (!plan_request.start_state.multi_dof_joint_state.joint_names.empty())
551 {
552 RCLCPP_WARN(logger_, "Ignoring start_state.multi_dof_joint_states: Not supported.");
553 }
554 if (!plan_request.start_state.attached_collision_objects.empty())
555 {
556 RCLCPP_WARN(logger_, "Ignoring start_state.attached_collision_objects: Not supported.");
557 }
558
559 query.append("group_name", plan_request.group_name);
560
561 // Workspace params
562 // Match anything within our specified workspace limits.
563 query.append("workspace_parameters.header.frame_id", workspace_frame_id);
564 query.appendGTE("workspace_parameters.min_corner.x", plan_request.workspace_parameters.min_corner.x);
565 query.appendGTE("workspace_parameters.min_corner.y", plan_request.workspace_parameters.min_corner.y);
566 query.appendGTE("workspace_parameters.min_corner.z", plan_request.workspace_parameters.min_corner.z);
567 query.appendLTE("workspace_parameters.max_corner.x", plan_request.workspace_parameters.max_corner.x);
568 query.appendLTE("workspace_parameters.max_corner.y", plan_request.workspace_parameters.max_corner.y);
569 query.appendLTE("workspace_parameters.max_corner.z", plan_request.workspace_parameters.max_corner.z);
570
571 // Joint state
572 // Only accounts for joint_state position. Ignores velocity and effort.
573 if (plan_request.start_state.is_diff)
574 {
575 // If plan request states that the start_state is_diff, then we need to get
576 // the current state from the move_group.
577
578 // NOTE: methyldragon -
579 // I think if is_diff is on, the joint states will not be populated in all
580 // of our motion plan requests? If this isn't the case we might need to
581 // apply the joint states as offsets as well.
582 //
583 // TODO: Since MoveIt also potentially does another getCurrentState() call
584 // when planning, there is a chance that the current state in the cache
585 // differs from the state used in MoveIt's plan.
586 //
587 // When upstreaming this class to MoveIt, this issue should go away once
588 // the class is used within the move group's Plan call.
589 moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
590 if (!current_state)
591 {
592 RCLCPP_WARN(logger_, "Skipping start query append: Could not get robot state.");
593 // NOTE: methyldragon -
594 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
595 // supported.
596 return false;
597 }
598
599 moveit_msgs::msg::RobotState current_state_msg;
600 robotStateToRobotStateMsg(*current_state, current_state_msg);
601
602 for (size_t i = 0; i < current_state_msg.joint_state.name.size(); i++)
603 {
604 query.append("start_state.joint_state.name_" + std::to_string(i), current_state_msg.joint_state.name.at(i));
605 queryAppendRangeInclusiveWithTolerance(query, "start_state.joint_state.position_" + std::to_string(i),
606 current_state_msg.joint_state.position.at(i), match_tolerance);
607 }
608 }
609 else
610 {
611 for (size_t i = 0; i < plan_request.start_state.joint_state.name.size(); i++)
612 {
613 query.append("start_state.joint_state.name_" + std::to_string(i), plan_request.start_state.joint_state.name.at(i));
614 queryAppendRangeInclusiveWithTolerance(query, "start_state.joint_state.position_" + std::to_string(i),
615 plan_request.start_state.joint_state.position.at(i), match_tolerance);
616 }
617 }
618 return true;
619}
620
621bool TrajectoryCache::extractAndAppendTrajectoryGoalToQuery(
623 const moveit_msgs::msg::MotionPlanRequest& plan_request, double match_tolerance) const
624{
625 std::string workspace_frame_id = getWorkspaceFrameId(move_group, plan_request.workspace_parameters);
626 match_tolerance += options_.exact_match_precision;
627
628 // Make ignored members explicit
629 bool emit_position_constraint_warning = false;
630 for (auto& constraint : plan_request.goal_constraints)
631 {
632 for (auto& position_constraint : constraint.position_constraints)
633 {
634 if (!position_constraint.constraint_region.primitives.empty())
635 {
636 emit_position_constraint_warning = true;
637 break;
638 }
639 }
640 if (emit_position_constraint_warning)
641 {
642 break;
643 }
644 }
645 if (emit_position_constraint_warning)
646 {
647 RCLCPP_WARN(logger_, "Ignoring goal_constraints.position_constraints.constraint_region: "
648 "Not supported.");
649 }
650
651 queryAppendRangeInclusiveWithTolerance(query, "max_velocity_scaling_factor", plan_request.max_velocity_scaling_factor,
652 match_tolerance);
653 queryAppendRangeInclusiveWithTolerance(query, "max_acceleration_scaling_factor",
654 plan_request.max_acceleration_scaling_factor, match_tolerance);
655 queryAppendRangeInclusiveWithTolerance(query, "max_cartesian_speed", plan_request.max_cartesian_speed,
656 match_tolerance);
657
658 // Extract constraints (so we don't have cardinality on goal_constraint idx.)
659 std::vector<moveit_msgs::msg::JointConstraint> joint_constraints;
660 std::vector<moveit_msgs::msg::PositionConstraint> position_constraints;
661 std::vector<moveit_msgs::msg::OrientationConstraint> orientation_constraints;
662 for (auto& constraint : plan_request.goal_constraints)
663 {
664 for (auto& joint_constraint : constraint.joint_constraints)
665 {
666 joint_constraints.push_back(joint_constraint);
667 }
668 for (auto& position_constraint : constraint.position_constraints)
669 {
670 position_constraints.push_back(position_constraint);
671 }
672 for (auto& orientation_constraint : constraint.orientation_constraints)
673 {
674 orientation_constraints.push_back(orientation_constraint);
675 }
676
677 // Also sort for even less cardinality.
678 sortConstraints(joint_constraints, position_constraints, orientation_constraints);
679 }
680
681 // Joint constraints
682 size_t joint_idx = 0;
683 for (auto& constraint : joint_constraints)
684 {
685 std::string meta_name = "goal_constraints.joint_constraints_" + std::to_string(joint_idx++);
686
687 query.append(meta_name + ".joint_name", constraint.joint_name);
688 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".position", constraint.position, match_tolerance);
689 query.appendGTE(meta_name + ".tolerance_above", constraint.tolerance_above);
690 query.appendLTE(meta_name + ".tolerance_below", constraint.tolerance_below);
691 }
692
693 // Position constraints
694 // All offsets will be "frozen" and computed wrt. the workspace frame
695 // instead.
696 if (!position_constraints.empty())
697 {
698 query.append("goal_constraints.position_constraints.header.frame_id", workspace_frame_id);
699
700 size_t pos_idx = 0;
701 for (auto& constraint : position_constraints)
702 {
703 std::string meta_name = "goal_constraints.position_constraints_" + std::to_string(pos_idx++);
704
705 // Compute offsets wrt. to workspace frame.
706 double x_offset = 0;
707 double y_offset = 0;
708 double z_offset = 0;
709
710 if (workspace_frame_id != constraint.header.frame_id)
711 {
712 try
713 {
714 auto transform =
715 tf_buffer_->lookupTransform(constraint.header.frame_id, workspace_frame_id, tf2::TimePointZero);
716
717 x_offset = transform.transform.translation.x;
718 y_offset = transform.transform.translation.y;
719 z_offset = transform.transform.translation.z;
720 }
721 catch (tf2::TransformException& ex)
722 {
723 RCLCPP_WARN(logger_,
724 "Skipping goal query append: "
725 "Could not get goal transform for translation %s to %s: %s",
726 workspace_frame_id.c_str(), constraint.header.frame_id.c_str(), ex.what());
727
728 // NOTE: methyldragon -
729 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
730 // supported.
731 return false;
732 }
733 }
734
735 query.append(meta_name + ".link_name", constraint.link_name);
736
737 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.x",
738 x_offset + constraint.target_point_offset.x, match_tolerance);
739 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.y",
740 y_offset + constraint.target_point_offset.y, match_tolerance);
741 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.z",
742 z_offset + constraint.target_point_offset.z, match_tolerance);
743 }
744 }
745
746 // Orientation constraints
747 // All offsets will be "frozen" and computed wrt. the workspace frame
748 // instead.
749 if (!orientation_constraints.empty())
750 {
751 query.append("goal_constraints.orientation_constraints.header.frame_id", workspace_frame_id);
752
753 size_t ori_idx = 0;
754 for (auto& constraint : orientation_constraints)
755 {
756 std::string meta_name = "goal_constraints.orientation_constraints_" + std::to_string(ori_idx++);
757
758 // Compute offsets wrt. to workspace frame.
759 geometry_msgs::msg::Quaternion quat_offset;
760 quat_offset.x = 0;
761 quat_offset.y = 0;
762 quat_offset.z = 0;
763 quat_offset.w = 1;
764
765 if (workspace_frame_id != constraint.header.frame_id)
766 {
767 try
768 {
769 auto transform =
770 tf_buffer_->lookupTransform(constraint.header.frame_id, workspace_frame_id, tf2::TimePointZero);
771
772 quat_offset = transform.transform.rotation;
773 }
774 catch (tf2::TransformException& ex)
775 {
776 RCLCPP_WARN(logger_,
777 "Skipping goal query append: "
778 "Could not get goal transform for orientation %s to %s: %s",
779 workspace_frame_id.c_str(), constraint.header.frame_id.c_str(), ex.what());
780
781 // NOTE: methyldragon -
782 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
783 // supported.
784 return false;
785 }
786 }
787
788 query.append(meta_name + ".link_name", constraint.link_name);
789
790 // Orientation of constraint frame wrt. workspace frame
791 tf2::Quaternion tf2_quat_frame_offset(quat_offset.x, quat_offset.y, quat_offset.z, quat_offset.w);
792
793 // Added offset on top of the constraint frame's orientation stated in
794 // the constraint.
795 tf2::Quaternion tf2_quat_goal_offset(constraint.orientation.x, constraint.orientation.y, constraint.orientation.z,
796 constraint.orientation.w);
797
798 tf2_quat_frame_offset.normalize();
799 tf2_quat_goal_offset.normalize();
800
801 auto final_quat = tf2_quat_goal_offset * tf2_quat_frame_offset;
802 final_quat.normalize();
803
804 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.x", final_quat.getX(),
805 match_tolerance);
806 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.y", final_quat.getY(),
807 match_tolerance);
808 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.z", final_quat.getZ(),
809 match_tolerance);
810 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".target_point_offset.w", final_quat.getW(),
811 match_tolerance);
812 }
813 }
814
815 return true;
816}
817
818// MOTION PLAN TRAJECTORY CACHING: METADATA CONSTRUCTION =======================
819
820bool TrajectoryCache::extractAndAppendTrajectoryStartToMetadata(
822 const moveit_msgs::msg::MotionPlanRequest& plan_request) const
823{
824 std::string workspace_frame_id = getWorkspaceFrameId(move_group, plan_request.workspace_parameters);
825
826 // Make ignored members explicit
827 if (!plan_request.start_state.multi_dof_joint_state.joint_names.empty())
828 {
829 RCLCPP_WARN(logger_, "Ignoring start_state.multi_dof_joint_states: Not supported.");
830 }
831 if (!plan_request.start_state.attached_collision_objects.empty())
832 {
833 RCLCPP_WARN(logger_, "Ignoring start_state.attached_collision_objects: Not supported.");
834 }
835
836 metadata.append("group_name", plan_request.group_name);
837
838 // Workspace params
839 metadata.append("workspace_parameters.header.frame_id", workspace_frame_id);
840 metadata.append("workspace_parameters.min_corner.x", plan_request.workspace_parameters.min_corner.x);
841 metadata.append("workspace_parameters.min_corner.y", plan_request.workspace_parameters.min_corner.y);
842 metadata.append("workspace_parameters.min_corner.z", plan_request.workspace_parameters.min_corner.z);
843 metadata.append("workspace_parameters.max_corner.x", plan_request.workspace_parameters.max_corner.x);
844 metadata.append("workspace_parameters.max_corner.y", plan_request.workspace_parameters.max_corner.y);
845 metadata.append("workspace_parameters.max_corner.z", plan_request.workspace_parameters.max_corner.z);
846
847 // Joint state
848 // Only accounts for joint_state position. Ignores velocity and effort.
849 if (plan_request.start_state.is_diff)
850 {
851 // If plan request states that the start_state is_diff, then we need to get
852 // the current state from the move_group.
853
854 // NOTE: methyldragon -
855 // I think if is_diff is on, the joint states will not be populated in all
856 // of our motion plan requests? If this isn't the case we might need to
857 // apply the joint states as offsets as well.
858 //
859 // TODO: Since MoveIt also potentially does another getCurrentState() call
860 // when planning, there is a chance that the current state in the cache
861 // differs from the state used in MoveIt's plan.
862 //
863 // When upstreaming this class to MoveIt, this issue should go away once
864 // the class is used within the move group's Plan call.
865 moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
866 if (!current_state)
867 {
868 RCLCPP_WARN(logger_, "Skipping start metadata append: Could not get robot state.");
869 // NOTE: methyldragon -
870 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
871 // supported.
872 return false;
873 }
874
875 moveit_msgs::msg::RobotState current_state_msg;
876 robotStateToRobotStateMsg(*current_state, current_state_msg);
877
878 for (size_t i = 0; i < current_state_msg.joint_state.name.size(); i++)
879 {
880 metadata.append("start_state.joint_state.name_" + std::to_string(i), current_state_msg.joint_state.name.at(i));
881 metadata.append("start_state.joint_state.position_" + std::to_string(i),
882 current_state_msg.joint_state.position.at(i));
883 }
884 }
885 else
886 {
887 for (size_t i = 0; i < plan_request.start_state.joint_state.name.size(); i++)
888 {
889 metadata.append("start_state.joint_state.name_" + std::to_string(i),
890 plan_request.start_state.joint_state.name.at(i));
891 metadata.append("start_state.joint_state.position_" + std::to_string(i),
892 plan_request.start_state.joint_state.position.at(i));
893 }
894 }
895
896 return true;
897}
898
899bool TrajectoryCache::extractAndAppendTrajectoryGoalToMetadata(
901 const moveit_msgs::msg::MotionPlanRequest& plan_request) const
902{
903 std::string workspace_frame_id = getWorkspaceFrameId(move_group, plan_request.workspace_parameters);
904
905 // Make ignored members explicit
906 bool emit_position_constraint_warning = false;
907 for (auto& constraint : plan_request.goal_constraints)
908 {
909 for (auto& position_constraint : constraint.position_constraints)
910 {
911 if (!position_constraint.constraint_region.primitives.empty())
912 {
913 emit_position_constraint_warning = true;
914 break;
915 }
916 }
917 if (emit_position_constraint_warning)
918 {
919 break;
920 }
921 }
922 if (emit_position_constraint_warning)
923 {
924 RCLCPP_WARN(logger_, "Ignoring goal_constraints.position_constraints.constraint_region: "
925 "Not supported.");
926 }
927
928 metadata.append("max_velocity_scaling_factor", plan_request.max_velocity_scaling_factor);
929 metadata.append("max_acceleration_scaling_factor", plan_request.max_acceleration_scaling_factor);
930 metadata.append("max_cartesian_speed", plan_request.max_cartesian_speed);
931
932 // Extract constraints (so we don't have cardinality on goal_constraint idx.)
933 std::vector<moveit_msgs::msg::JointConstraint> joint_constraints;
934 std::vector<moveit_msgs::msg::PositionConstraint> position_constraints;
935 std::vector<moveit_msgs::msg::OrientationConstraint> orientation_constraints;
936 for (auto& constraint : plan_request.goal_constraints)
937 {
938 for (auto& joint_constraint : constraint.joint_constraints)
939 {
940 joint_constraints.push_back(joint_constraint);
941 }
942 for (auto& position_constraint : constraint.position_constraints)
943 {
944 position_constraints.push_back(position_constraint);
945 }
946 for (auto& orientation_constraint : constraint.orientation_constraints)
947 {
948 orientation_constraints.push_back(orientation_constraint);
949 }
950
951 // Also sort for even less cardinality.
952 sortConstraints(joint_constraints, position_constraints, orientation_constraints);
953 }
954
955 // Joint constraints
956 size_t joint_idx = 0;
957 for (auto& constraint : joint_constraints)
958 {
959 std::string meta_name = "goal_constraints.joint_constraints_" + std::to_string(joint_idx++);
960
961 metadata.append(meta_name + ".joint_name", constraint.joint_name);
962 metadata.append(meta_name + ".position", constraint.position);
963 metadata.append(meta_name + ".tolerance_above", constraint.tolerance_above);
964 metadata.append(meta_name + ".tolerance_below", constraint.tolerance_below);
965 }
966
967 // Position constraints
968 if (!position_constraints.empty())
969 {
970 // All offsets will be "frozen" and computed wrt. the workspace frame
971 // instead.
972 metadata.append("goal_constraints.position_constraints.header.frame_id", workspace_frame_id);
973
974 size_t position_idx = 0;
975 for (auto& constraint : position_constraints)
976 {
977 std::string meta_name = "goal_constraints.position_constraints_" + std::to_string(position_idx++);
978
979 // Compute offsets wrt. to workspace frame.
980 double x_offset = 0;
981 double y_offset = 0;
982 double z_offset = 0;
983
984 if (workspace_frame_id != constraint.header.frame_id)
985 {
986 try
987 {
988 auto transform =
989 tf_buffer_->lookupTransform(constraint.header.frame_id, workspace_frame_id, tf2::TimePointZero);
990
991 x_offset = transform.transform.translation.x;
992 y_offset = transform.transform.translation.y;
993 z_offset = transform.transform.translation.z;
994 }
995 catch (tf2::TransformException& ex)
996 {
997 RCLCPP_WARN(logger_,
998 "Skipping goal metadata append: "
999 "Could not get goal transform for translation %s to %s: %s",
1000 workspace_frame_id.c_str(), constraint.header.frame_id.c_str(), ex.what());
1001
1002 // NOTE: methyldragon -
1003 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1004 // supported.
1005 return false;
1006 }
1007 }
1008
1009 metadata.append(meta_name + ".link_name", constraint.link_name);
1010
1011 metadata.append(meta_name + ".target_point_offset.x", x_offset + constraint.target_point_offset.x);
1012 metadata.append(meta_name + ".target_point_offset.y", y_offset + constraint.target_point_offset.y);
1013 metadata.append(meta_name + ".target_point_offset.z", z_offset + constraint.target_point_offset.z);
1014 }
1015 }
1016
1017 // Orientation constraints
1018 if (!orientation_constraints.empty())
1019 {
1020 // All offsets will be "frozen" and computed wrt. the workspace frame
1021 // instead.
1022 metadata.append("goal_constraints.orientation_constraints.header.frame_id", workspace_frame_id);
1023
1024 size_t ori_idx = 0;
1025 for (auto& constraint : orientation_constraints)
1026 {
1027 std::string meta_name = "goal_constraints.orientation_constraints_" + std::to_string(ori_idx++);
1028
1029 // Compute offsets wrt. to workspace frame.
1030 geometry_msgs::msg::Quaternion quat_offset;
1031 quat_offset.x = 0;
1032 quat_offset.y = 0;
1033 quat_offset.z = 0;
1034 quat_offset.w = 1;
1035
1036 if (workspace_frame_id != constraint.header.frame_id)
1037 {
1038 try
1039 {
1040 auto transform =
1041 tf_buffer_->lookupTransform(constraint.header.frame_id, workspace_frame_id, tf2::TimePointZero);
1042
1043 quat_offset = transform.transform.rotation;
1044 }
1045 catch (tf2::TransformException& ex)
1046 {
1047 RCLCPP_WARN(logger_,
1048 "Skipping goal metadata append: "
1049 "Could not get goal transform for orientation %s to %s: %s",
1050 workspace_frame_id.c_str(), constraint.header.frame_id.c_str(), ex.what());
1051
1052 // NOTE: methyldragon -
1053 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1054 // supported.
1055 return false;
1056 }
1057 }
1058
1059 metadata.append(meta_name + ".link_name", constraint.link_name);
1060
1061 // Orientation of constraint frame wrt. workspace frame
1062 tf2::Quaternion tf2_quat_frame_offset(quat_offset.x, quat_offset.y, quat_offset.z, quat_offset.w);
1063
1064 // Added offset on top of the constraint frame's orientation stated in
1065 // the constraint.
1066 tf2::Quaternion tf2_quat_goal_offset(constraint.orientation.x, constraint.orientation.y, constraint.orientation.z,
1067 constraint.orientation.w);
1068
1069 tf2_quat_frame_offset.normalize();
1070 tf2_quat_goal_offset.normalize();
1071
1072 auto final_quat = tf2_quat_goal_offset * tf2_quat_frame_offset;
1073 final_quat.normalize();
1074
1075 metadata.append(meta_name + ".target_point_offset.x", final_quat.getX());
1076 metadata.append(meta_name + ".target_point_offset.y", final_quat.getY());
1077 metadata.append(meta_name + ".target_point_offset.z", final_quat.getZ());
1078 metadata.append(meta_name + ".target_point_offset.w", final_quat.getW());
1079 }
1080 }
1081
1082 return true;
1083}
1084
1085// =============================================================================
1086// CARTESIAN TRAJECTORY QUERY AND METADATA CONSTRUCTION
1087// =============================================================================
1088
1089// CARTESIAN TRAJECTORY CACHING: QUERY CONSTRUCTION ============================
1090
1091bool TrajectoryCache::extractAndAppendCartesianTrajectoryStartToQuery(
1093 const moveit_msgs::srv::GetCartesianPath::Request& plan_request, double match_tolerance) const
1094{
1095 std::string path_request_frame_id = getCartesianPathRequestFrameId(move_group, plan_request);
1096 match_tolerance += options_.exact_match_precision;
1097
1098 // Make ignored members explicit
1099 if (!plan_request.start_state.multi_dof_joint_state.joint_names.empty())
1100 {
1101 RCLCPP_WARN(logger_, "Ignoring start_state.multi_dof_joint_states: Not supported.");
1102 }
1103 if (!plan_request.start_state.attached_collision_objects.empty())
1104 {
1105 RCLCPP_WARN(logger_, "Ignoring start_state.attached_collision_objects: Not supported.");
1106 }
1107
1108 query.append("group_name", plan_request.group_name);
1109 query.append("path_request.header.frame_id", path_request_frame_id);
1110
1111 // Joint state
1112 // Only accounts for joint_state position. Ignores velocity and effort.
1113 if (plan_request.start_state.is_diff)
1114 {
1115 // If plan request states that the start_state is_diff, then we need to get
1116 // the current state from the move_group.
1117
1118 // NOTE: methyldragon -
1119 // I think if is_diff is on, the joint states will not be populated in all
1120 // of our motion plan requests? If this isn't the case we might need to
1121 // apply the joint states as offsets as well.
1122 //
1123 // TODO: Since MoveIt also potentially does another getCurrentState() call
1124 // when planning, there is a chance that the current state in the cache
1125 // differs from the state used in MoveIt's plan.
1126 //
1127 // When upstreaming this class to MoveIt, this issue should go away once
1128 // the class is used within the move group's Plan call.
1129 moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
1130 if (!current_state)
1131 {
1132 RCLCPP_WARN(logger_, "Skipping start metadata append: Could not get robot state.");
1133 // NOTE: methyldragon -
1134 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1135 // supported.
1136 return false;
1137 }
1138
1139 moveit_msgs::msg::RobotState current_state_msg;
1140 robotStateToRobotStateMsg(*current_state, current_state_msg);
1141
1142 for (size_t i = 0; i < current_state_msg.joint_state.name.size(); i++)
1143 {
1144 query.append("start_state.joint_state.name_" + std::to_string(i), current_state_msg.joint_state.name.at(i));
1145 queryAppendRangeInclusiveWithTolerance(query, "start_state.joint_state.position_" + std::to_string(i),
1146 current_state_msg.joint_state.position.at(i), match_tolerance);
1147 }
1148 }
1149 else
1150 {
1151 for (size_t i = 0; i < plan_request.start_state.joint_state.name.size(); i++)
1152 {
1153 query.append("start_state.joint_state.name_" + std::to_string(i), plan_request.start_state.joint_state.name.at(i));
1154 queryAppendRangeInclusiveWithTolerance(query, "start_state.joint_state.position_" + std::to_string(i),
1155 plan_request.start_state.joint_state.position.at(i), match_tolerance);
1156 }
1157 }
1158
1159 return true;
1160}
1161
1162bool TrajectoryCache::extractAndAppendCartesianTrajectoryGoalToQuery(
1164 const moveit_msgs::srv::GetCartesianPath::Request& plan_request, double match_tolerance) const
1165{
1166 std::string path_request_frame_id = getCartesianPathRequestFrameId(move_group, plan_request);
1167 match_tolerance += options_.exact_match_precision;
1168
1169 // Make ignored members explicit
1170 if (!plan_request.path_constraints.joint_constraints.empty() ||
1171 !plan_request.path_constraints.position_constraints.empty() ||
1172 !plan_request.path_constraints.orientation_constraints.empty() ||
1173 !plan_request.path_constraints.visibility_constraints.empty())
1174 {
1175 RCLCPP_WARN(logger_, "Ignoring path_constraints: Not supported.");
1176 }
1177 if (plan_request.avoid_collisions)
1178 {
1179 RCLCPP_WARN(logger_, "Ignoring avoid_collisions: Not supported.");
1180 }
1181
1182 queryAppendRangeInclusiveWithTolerance(query, "max_velocity_scaling_factor", plan_request.max_velocity_scaling_factor,
1183 match_tolerance);
1184 queryAppendRangeInclusiveWithTolerance(query, "max_acceleration_scaling_factor",
1185 plan_request.max_acceleration_scaling_factor, match_tolerance);
1186 queryAppendRangeInclusiveWithTolerance(query, "max_step", plan_request.max_step, match_tolerance);
1187 queryAppendRangeInclusiveWithTolerance(query, "jump_threshold", plan_request.jump_threshold, match_tolerance);
1188
1189 // Waypoints
1190 // Restating them in terms of the robot model frame (usually base_link)
1191 std::string base_frame = move_group.getRobotModel()->getModelFrame();
1192
1193 // Compute offsets.
1194 double x_offset = 0;
1195 double y_offset = 0;
1196 double z_offset = 0;
1197
1198 geometry_msgs::msg::Quaternion quat_offset;
1199 quat_offset.x = 0;
1200 quat_offset.y = 0;
1201 quat_offset.z = 0;
1202 quat_offset.w = 1;
1203
1204 if (base_frame != path_request_frame_id)
1205 {
1206 try
1207 {
1208 auto transform = tf_buffer_->lookupTransform(path_request_frame_id, base_frame, tf2::TimePointZero);
1209
1210 x_offset = transform.transform.translation.x;
1211 y_offset = transform.transform.translation.y;
1212 z_offset = transform.transform.translation.z;
1213 quat_offset = transform.transform.rotation;
1214 }
1215 catch (tf2::TransformException& ex)
1216 {
1217 RCLCPP_WARN(logger_,
1218 "Skipping goal metadata append: "
1219 "Could not get goal transform for %s to %s: %s",
1220 base_frame.c_str(), path_request_frame_id.c_str(), ex.what());
1221
1222 // NOTE: methyldragon -
1223 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1224 // supported.
1225 return false;
1226 }
1227 }
1228
1229 tf2::Quaternion tf2_quat_frame_offset(quat_offset.x, quat_offset.y, quat_offset.z, quat_offset.w);
1230 tf2_quat_frame_offset.normalize();
1231
1232 size_t waypoint_idx = 0;
1233 for (auto& waypoint : plan_request.waypoints)
1234 {
1235 std::string meta_name = "waypoints_" + std::to_string(waypoint_idx++);
1236
1237 // Apply offsets
1238 // Position
1239 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".position.x", x_offset + waypoint.position.x,
1240 match_tolerance);
1241 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".position.y", y_offset + waypoint.position.y,
1242 match_tolerance);
1243 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".position.z", z_offset + waypoint.position.z,
1244 match_tolerance);
1245
1246 // Orientation
1247 tf2::Quaternion tf2_quat_goal_offset(waypoint.orientation.x, waypoint.orientation.y, waypoint.orientation.z,
1248 waypoint.orientation.w);
1249 tf2_quat_goal_offset.normalize();
1250
1251 auto final_quat = tf2_quat_goal_offset * tf2_quat_frame_offset;
1252 final_quat.normalize();
1253
1254 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".orientation.x", final_quat.getX(), match_tolerance);
1255 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".orientation.y", final_quat.getY(), match_tolerance);
1256 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".orientation.z", final_quat.getZ(), match_tolerance);
1257 queryAppendRangeInclusiveWithTolerance(query, meta_name + ".orientation.w", final_quat.getW(), match_tolerance);
1258 }
1259
1260 query.append("link_name", plan_request.link_name);
1261 query.append("header.frame_id", base_frame);
1262
1263 return true;
1264}
1265
1266// CARTESIAN TRAJECTORY CACHING: METADATA CONSTRUCTION =========================
1267
1268bool TrajectoryCache::extractAndAppendCartesianTrajectoryStartToMetadata(
1270 const moveit_msgs::srv::GetCartesianPath::Request& plan_request) const
1271{
1272 std::string path_request_frame_id = getCartesianPathRequestFrameId(move_group, plan_request);
1273
1274 // Make ignored members explicit
1275 if (!plan_request.start_state.multi_dof_joint_state.joint_names.empty())
1276 {
1277 RCLCPP_WARN(logger_, "Ignoring start_state.multi_dof_joint_states: Not supported.");
1278 }
1279 if (!plan_request.start_state.attached_collision_objects.empty())
1280 {
1281 RCLCPP_WARN(logger_, "Ignoring start_state.attached_collision_objects: Not supported.");
1282 }
1283
1284 metadata.append("group_name", plan_request.group_name);
1285 metadata.append("path_request.header.frame_id", path_request_frame_id);
1286
1287 // Joint state
1288 // Only accounts for joint_state position. Ignores velocity and effort.
1289 if (plan_request.start_state.is_diff)
1290 {
1291 // If plan request states that the start_state is_diff, then we need to get
1292 // the current state from the move_group.
1293
1294 // NOTE: methyldragon -
1295 // I think if is_diff is on, the joint states will not be populated in all
1296 // of our motion plan requests? If this isn't the case we might need to
1297 // apply the joint states as offsets as well.
1298 //
1299 // TODO: Since MoveIt also potentially does another getCurrentState() call
1300 // when planning, there is a chance that the current state in the cache
1301 // differs from the state used in MoveIt's plan.
1302 //
1303 // When upstreaming this class to MoveIt, this issue should go away once
1304 // the class is used within the move group's Plan call.
1305 moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
1306 if (!current_state)
1307 {
1308 RCLCPP_WARN(logger_, "Skipping start metadata append: Could not get robot state.");
1309 // NOTE: methyldragon -
1310 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1311 // supported.
1312 return false;
1313 }
1314
1315 moveit_msgs::msg::RobotState current_state_msg;
1316 robotStateToRobotStateMsg(*current_state, current_state_msg);
1317
1318 for (size_t i = 0; i < current_state_msg.joint_state.name.size(); i++)
1319 {
1320 metadata.append("start_state.joint_state.name_" + std::to_string(i), current_state_msg.joint_state.name.at(i));
1321 metadata.append("start_state.joint_state.position_" + std::to_string(i),
1322 current_state_msg.joint_state.position.at(i));
1323 }
1324 }
1325 else
1326 {
1327 for (size_t i = 0; i < plan_request.start_state.joint_state.name.size(); i++)
1328 {
1329 metadata.append("start_state.joint_state.name_" + std::to_string(i),
1330 plan_request.start_state.joint_state.name.at(i));
1331 metadata.append("start_state.joint_state.position_" + std::to_string(i),
1332 plan_request.start_state.joint_state.position.at(i));
1333 }
1334 }
1335
1336 return true;
1337}
1338
1339bool TrajectoryCache::extractAndAppendCartesianTrajectoryGoalToMetadata(
1341 const moveit_msgs::srv::GetCartesianPath::Request& plan_request) const
1342{
1343 std::string path_request_frame_id = getCartesianPathRequestFrameId(move_group, plan_request);
1344
1345 // Make ignored members explicit
1346 if (!plan_request.path_constraints.joint_constraints.empty() ||
1347 !plan_request.path_constraints.position_constraints.empty() ||
1348 !plan_request.path_constraints.orientation_constraints.empty() ||
1349 !plan_request.path_constraints.visibility_constraints.empty())
1350 {
1351 RCLCPP_WARN(logger_, "Ignoring path_constraints: Not supported.");
1352 }
1353 if (plan_request.avoid_collisions)
1354 {
1355 RCLCPP_WARN(logger_, "Ignoring avoid_collisions: Not supported.");
1356 }
1357
1358 metadata.append("max_velocity_scaling_factor", plan_request.max_velocity_scaling_factor);
1359 metadata.append("max_acceleration_scaling_factor", plan_request.max_acceleration_scaling_factor);
1360 metadata.append("max_step", plan_request.max_step);
1361 metadata.append("jump_threshold", plan_request.jump_threshold);
1362
1363 // Waypoints
1364 // Restating them in terms of the robot model frame (usually base_link)
1365 std::string base_frame = move_group.getRobotModel()->getModelFrame();
1366
1367 // Compute offsets.
1368 double x_offset = 0;
1369 double y_offset = 0;
1370 double z_offset = 0;
1371
1372 geometry_msgs::msg::Quaternion quat_offset;
1373 quat_offset.x = 0;
1374 quat_offset.y = 0;
1375 quat_offset.z = 0;
1376 quat_offset.w = 1;
1377
1378 if (base_frame != path_request_frame_id)
1379 {
1380 try
1381 {
1382 auto transform = tf_buffer_->lookupTransform(path_request_frame_id, base_frame, tf2::TimePointZero);
1383
1384 x_offset = transform.transform.translation.x;
1385 y_offset = transform.transform.translation.y;
1386 z_offset = transform.transform.translation.z;
1387 quat_offset = transform.transform.rotation;
1388 }
1389 catch (tf2::TransformException& ex)
1390 {
1391 RCLCPP_WARN(logger_,
1392 "Skipping goal metadata append: "
1393 "Could not get goal transform for %s to %s: %s",
1394 base_frame.c_str(), path_request_frame_id.c_str(), ex.what());
1395
1396 // NOTE: methyldragon -
1397 // Ideally we would restore the original state here and undo our changes, however copy of the query is not
1398 // supported.
1399 return false;
1400 }
1401 }
1402
1403 tf2::Quaternion tf2_quat_frame_offset(quat_offset.x, quat_offset.y, quat_offset.z, quat_offset.w);
1404 tf2_quat_frame_offset.normalize();
1405
1406 size_t waypoint_idx = 0;
1407 for (auto& waypoint : plan_request.waypoints)
1408 {
1409 std::string meta_name = "waypoints_" + std::to_string(waypoint_idx++);
1410
1411 // Apply offsets
1412 // Position
1413 metadata.append(meta_name + ".position.x", x_offset + waypoint.position.x);
1414 metadata.append(meta_name + ".position.y", y_offset + waypoint.position.y);
1415 metadata.append(meta_name + ".position.z", z_offset + waypoint.position.z);
1416
1417 // Orientation
1418 tf2::Quaternion tf2_quat_goal_offset(waypoint.orientation.x, waypoint.orientation.y, waypoint.orientation.z,
1419 waypoint.orientation.w);
1420 tf2_quat_goal_offset.normalize();
1421
1422 auto final_quat = tf2_quat_goal_offset * tf2_quat_frame_offset;
1423 final_quat.normalize();
1424
1425 metadata.append(meta_name + ".orientation.x", final_quat.getX());
1426 metadata.append(meta_name + ".orientation.y", final_quat.getY());
1427 metadata.append(meta_name + ".orientation.z", final_quat.getZ());
1428 metadata.append(meta_name + ".orientation.w", final_quat.getW());
1429 }
1430
1431 metadata.append("link_name", plan_request.link_name);
1432 metadata.append("header.frame_id", base_frame);
1433
1434 return true;
1435}
1436
1437} // namespace trajectory_cache
1438} // namespace moveit_ros
Client class to conveniently use the ROS interfaces provided by the move_group node.
bool insertTrajectory(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::msg::MotionPlanRequest &plan_request, const moveit_msgs::msg::RobotTrajectory &trajectory, double execution_time_s, double planning_time_s, bool prune_worse_trajectories=true)
Inserts a trajectory into the database if it is the best matching trajectory seen so far.
unsigned countCartesianTrajectories(const std::string &cache_namespace)
Count the number of cartesian trajectories for a particular cache namespace.
unsigned countTrajectories(const std::string &cache_namespace)
Count the number of non-cartesian trajectories for a particular cache namespace.
void setNumAdditionalTrajectoriesToPreserveWhenDeletingWorse(size_t num_additional_trajectories_to_preserve_when_deleting_worse)
Set the number of additional trajectories to preserve when deleting worse trajectories.
TrajectoryCache(const rclcpp::Node::SharedPtr &node)
Constructs a TrajectoryCache.
double getExactMatchPrecision() const
Gets the exact match precision.
size_t getNumAdditionalTrajectoriesToPreserveWhenDeletingWorse() const
Get the number of trajectories to preserve when deleting worse trajectories.
void setExactMatchPrecision(double exact_match_precision)
Sets the exact match precision.
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr fetchBestMatchingCartesianTrajectory(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::srv::GetCartesianPath::Request &plan_request, double min_fraction, double start_tolerance, double goal_tolerance, bool metadata_only=false, const std::string &sort_by="execution_time_s", bool ascending=true) const
Fetches the best cartesian trajectory that fits within the requested tolerances for start and goal co...
std::vector< warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr > fetchAllMatchingTrajectories(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::msg::MotionPlanRequest &plan_request, double start_tolerance, double goal_tolerance, bool metadata_only=false, const std::string &sort_by="execution_time_s", bool ascending=true) const
Fetches all plans that fit within the requested tolerances for start and goal conditions,...
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr fetchBestMatchingTrajectory(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::msg::MotionPlanRequest &plan_request, double start_tolerance, double goal_tolerance, bool metadata_only=false, const std::string &sort_by="execution_time_s", bool ascending=true) const
Fetches the best trajectory that fits within the requested tolerances for start and goal conditions,...
moveit_msgs::srv::GetCartesianPath::Request constructGetCartesianPathRequest(moveit::planning_interface::MoveGroupInterface &move_group, const std::vector< geometry_msgs::msg::Pose > &waypoints, double max_step, double jump_threshold, bool avoid_collisions=true)
Constructs a GetCartesianPath request.
std::string getDbPath() const
Gets the database path.
std::vector< warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr > fetchAllMatchingCartesianTrajectories(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::srv::GetCartesianPath::Request &plan_request, double min_fraction, double start_tolerance, double goal_tolerance, bool metadata_only=false, const std::string &sort_by="execution_time_s", bool ascending=true) const
Fetches all cartesian trajectories that fit within the requested tolerances for start and goal condit...
bool init(const Options &options)
Initializes the TrajectoryCache.
bool insertCartesianTrajectory(const moveit::planning_interface::MoveGroupInterface &move_group, const std::string &cache_namespace, const moveit_msgs::srv::GetCartesianPath::Request &plan_request, const moveit_msgs::msg::RobotTrajectory &trajectory, double execution_time_s, double planning_time_s, double fraction, bool prune_worse_trajectories=true)
Inserts a cartesian trajectory into the database if it is the best matching cartesian trajectory seen...
uint32_t getDbPort() const
Gets the database port.
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::msg::RobotState &robot_state, bool copy_attached_bodies=true)
Convert a MoveIt robot state to a robot state message.
void queryAppendRangeInclusiveWithTolerance(Query &query, const std::string &name, double center, double tolerance)
std::string getWorkspaceFrameId(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::msg::WorkspaceParameters &workspace_parameters)
std::string getCartesianPathRequestFrameId(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::srv::GetCartesianPath::Request &path_request)
void sortConstraints(std::vector< moveit_msgs::msg::JointConstraint > &joint_constraints, std::vector< moveit_msgs::msg::PositionConstraint > &position_constraints, std::vector< moveit_msgs::msg::OrientationConstraint > &orientation_constraints)
warehouse_ros::DatabaseConnection::Ptr loadDatabase(const rclcpp::Node::SharedPtr &node)
Load a database connection.
Main namespace for MoveIt.
Definition exceptions.h:43
Fuzzy-Matching Trajectory Cache.