moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
best_seen_execution_time_policy.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
24#include <limits>
25#include <memory>
26#include <sstream>
27#include <string>
28
29#include <warehouse_ros/message_collection.h>
31
32#include <moveit_msgs/msg/motion_plan_request.hpp>
33#include <moveit_msgs/msg/robot_trajectory.hpp>
34#include <moveit_msgs/srv/get_cartesian_path.hpp>
35
41
42namespace moveit_ros
43{
44namespace trajectory_cache
45{
46
47using ::warehouse_ros::MessageCollection;
48using ::warehouse_ros::MessageWithMetadata;
49using ::warehouse_ros::Metadata;
50using ::warehouse_ros::Query;
51
52using ::moveit::core::MoveItErrorCode;
53using ::moveit::planning_interface::MoveGroupInterface;
54
55using ::moveit_msgs::msg::MotionPlanRequest;
56using ::moveit_msgs::msg::RobotTrajectory;
57using ::moveit_msgs::srv::GetCartesianPath;
58
59using ::moveit_ros::trajectory_cache::FeaturesInterface;
60
61namespace
62{
63
64const std::string EXECUTION_TIME = "execution_time_s";
65const std::string FRACTION = "fraction";
66const std::string PLANNING_TIME = "planning_time_s";
67
68} // namespace
69
70// =================================================================================================
71// BestSeenExecutionTimePolicy.
72// =================================================================================================
73// moveit_msgs::msg::MotionPlanRequest <=> moveit::planning_interface::MoveGroupInterface::Plan
74
76 : name_("BestSeenExecutionTimePolicy"), best_seen_execution_time_(std::numeric_limits<double>::infinity())
77{
78 exact_matching_supported_features_ = BestSeenExecutionTimePolicy::getSupportedFeatures(/*start_tolerance=*/0.0,
79 /*goal_tolerance=*/0.0);
80}
81
82std::vector<std::unique_ptr<FeaturesInterface<MotionPlanRequest>>>
83BestSeenExecutionTimePolicy::getSupportedFeatures(double start_tolerance, double goal_tolerance)
84{
85 std::vector<std::unique_ptr<FeaturesInterface<MotionPlanRequest>>> out;
86 out.reserve(6);
87
88 // Start.
89 out.push_back(std::make_unique<WorkspaceFeatures>());
90 out.push_back(std::make_unique<StartStateJointStateFeatures>(start_tolerance));
91
92 // Goal.
93 out.push_back(std::make_unique<MaxSpeedAndAccelerationFeatures>());
94 out.push_back(std::make_unique<GoalConstraintsFeatures>(goal_tolerance));
95 out.push_back(std::make_unique<PathConstraintsFeatures>(goal_tolerance));
96 out.push_back(std::make_unique<TrajectoryConstraintsFeatures>(goal_tolerance));
97
98 return out;
99}
100
102{
103 return name_;
104}
105
106MoveItErrorCode BestSeenExecutionTimePolicy::checkCacheInsertInputs(const MoveGroupInterface& move_group,
107 const MessageCollection<RobotTrajectory>& /*coll*/,
108 const MotionPlanRequest& key,
109 const MoveGroupInterface::Plan& value)
110{
111 std::string workspace_frame_id = getWorkspaceFrameId(move_group, key.workspace_parameters);
112
113 // Check key.
114 if (workspace_frame_id.empty())
115 {
116 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
117 name_ + ": Skipping insert: Workspace frame ID cannot be empty.");
118 }
119 if (key.goal_constraints.empty())
120 {
121 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, name_ + ": Skipping insert: No goal.");
122 }
123
124 // Check value.
125 if (value.trajectory.joint_trajectory.points.empty())
126 {
127 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, name_ + ": Empty joint trajectory points.");
128 }
129 if (value.trajectory.joint_trajectory.joint_names.empty())
130 {
131 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
132 name_ + ": Skipping insert: Empty joint trajectory joint names.");
133 }
134 if (!value.trajectory.multi_dof_joint_trajectory.points.empty())
135 {
136 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
137 name_ + ": Skipping insert: Multi-DOF trajectory plans are not supported.");
138 }
139 if (value.trajectory.joint_trajectory.header.frame_id.empty())
140 {
141 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
142 name_ + ": Skipping insert: Trajectory frame ID cannot be empty.");
143 }
144 if (workspace_frame_id != value.trajectory.joint_trajectory.header.frame_id)
145 {
146 std::stringstream ss;
147 ss << "Skipping insert: Plan request frame (" << workspace_frame_id << ") does not match plan frame ("
148 << value.trajectory.joint_trajectory.header.frame_id << ").";
149 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, ss.str());
150 }
151
152 return MoveItErrorCode::SUCCESS;
153}
154
155std::vector<MessageWithMetadata<RobotTrajectory>::ConstPtr> BestSeenExecutionTimePolicy::fetchMatchingEntries(
156 const MoveGroupInterface& move_group, const MessageCollection<RobotTrajectory>& coll, const MotionPlanRequest& key,
157 const MoveGroupInterface::Plan& /*value*/, double exact_match_precision)
158{
159 Query::Ptr query = coll.createQuery();
160 for (const auto& feature : exact_matching_supported_features_)
161 {
162 if (MoveItErrorCode ret = feature->appendFeaturesAsExactFetchQuery(*query, key, move_group, exact_match_precision);
163 !ret)
164 {
165 return {};
166 }
167 }
168
169 std::vector<MessageWithMetadata<RobotTrajectory>::ConstPtr> out =
170 coll.queryList(query, /*metadata_only=*/true, /*sort_by=*/EXECUTION_TIME, /*ascending=*/true);
171 if (!out.empty())
172 {
173 best_seen_execution_time_ = out[0]->lookupDouble(EXECUTION_TIME);
174 }
175
176 return out;
177}
178
180 const MoveGroupInterface& /*move_group*/, const MotionPlanRequest& /*key*/, const MoveGroupInterface::Plan& value,
181 const MessageWithMetadata<RobotTrajectory>::ConstPtr& matching_entry, std::string* reason)
182{
183 double matching_entry_execution_time_s = matching_entry->lookupDouble(EXECUTION_TIME);
184 double candidate_execution_time_s = getExecutionTime(value.trajectory);
185
186 if (matching_entry_execution_time_s >= candidate_execution_time_s)
187 {
188 if (reason != nullptr)
189 {
190 std::stringstream ss;
191 ss << "Matching trajectory execution_time_s `" << matching_entry_execution_time_s << "s` "
192 << "is worse than candidate trajectory's execution_time_s `" << candidate_execution_time_s << "s`";
193 *reason = ss.str();
194 }
195 return true;
196 }
197 else
198 {
199 if (reason != nullptr)
200 {
201 std::stringstream ss;
202 ss << "Matching trajectory execution_time_s `" << matching_entry_execution_time_s << "s` "
203 << "is better than candidate trajectory's execution_time_s `" << candidate_execution_time_s << "s`";
204 *reason = ss.str();
205 }
206 return false;
207 }
208}
209
210bool BestSeenExecutionTimePolicy::shouldInsert(const MoveGroupInterface& /*move_group*/,
211 const MotionPlanRequest& /*key*/, const MoveGroupInterface::Plan& value,
212 std::string* reason)
213{
214 double execution_time_s = getExecutionTime(value.trajectory);
215
216 if (execution_time_s < best_seen_execution_time_)
217 {
218 if (reason != nullptr)
219 {
220 std::stringstream ss;
221 ss << "New trajectory execution_time_s `" << execution_time_s << "s` "
222 << "is better than best trajectory's execution_time_s `" << best_seen_execution_time_ << "s`";
223 *reason = ss.str();
224 }
225 return true;
226 }
227 else
228 {
229 if (reason != nullptr)
230 {
231 std::stringstream ss;
232 ss << "New trajectory execution_time `" << execution_time_s << "s` "
233 << "is worse than best trajectory's execution_time `" << best_seen_execution_time_ << "s`";
234 *reason = ss.str();
235 }
236 return false;
237 }
238}
239
240MoveItErrorCode BestSeenExecutionTimePolicy::appendInsertMetadata(Metadata& metadata,
241 const MoveGroupInterface& move_group,
242 const MotionPlanRequest& key,
243 const MoveGroupInterface::Plan& value)
244{
245 for (const auto& feature : exact_matching_supported_features_)
246 {
247 if (MoveItErrorCode ret = feature->appendFeaturesAsInsertMetadata(metadata, key, move_group); !ret)
248 {
249 return ret;
250 }
251 }
252
253 // Append ValueT metadata.
254 metadata.append(EXECUTION_TIME, getExecutionTime(value.trajectory));
255 metadata.append(PLANNING_TIME, value.planning_time);
256
257 return MoveItErrorCode::SUCCESS;
258}
259
261{
262 best_seen_execution_time_ = std::numeric_limits<double>::infinity();
263 return;
264}
265
266// =================================================================================================
267// CartesianBestSeenExecutionTimePolicy.
268// =================================================================================================
269// moveit_msgs::srv::GetCartesianPath::Request <=> moveit_msgs::srv::GetCartesianPath::Response
270
272 : name_("CartesianBestSeenExecutionTimePolicy"), best_seen_execution_time_(std::numeric_limits<double>::infinity())
273{
274 exact_matching_supported_features_ =
276 /*goal_tolerance=*/0.0, /*min_fraction=*/0.0);
277}
278
279std::vector<std::unique_ptr<FeaturesInterface<GetCartesianPath::Request>>>
280CartesianBestSeenExecutionTimePolicy::getSupportedFeatures(double start_tolerance, double goal_tolerance,
281 double min_fraction)
282{
283 std::vector<std::unique_ptr<FeaturesInterface<GetCartesianPath::Request>>> out;
284 out.reserve(7);
285
286 // Start.
287 out.push_back(std::make_unique<CartesianWorkspaceFeatures>());
288 out.push_back(std::make_unique<CartesianStartStateJointStateFeatures>(start_tolerance));
289
290 // Goal.
291 out.push_back(std::make_unique<CartesianMaxSpeedAndAccelerationFeatures>());
292 out.push_back(std::make_unique<CartesianMaxStepAndJumpThresholdFeatures>());
293 out.push_back(std::make_unique<CartesianWaypointsFeatures>(goal_tolerance));
294 out.push_back(std::make_unique<CartesianPathConstraintsFeatures>(goal_tolerance));
295
296 // Min fraction.
297 out.push_back(std::make_unique<QueryOnlyGTEFeature<double, GetCartesianPath::Request>>(FRACTION, min_fraction));
298
299 return out;
300}
301
303{
304 return name_;
305}
306
308 const MoveGroupInterface& move_group, const MessageCollection<RobotTrajectory>& /*coll*/,
309 const GetCartesianPath::Request& key, const GetCartesianPath::Response& value)
310{
311 std::string frame_id = getCartesianPathRequestFrameId(move_group, key);
312
313 // Check key.
314 if (frame_id.empty())
315 {
316 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
317 name_ + ": Skipping insert: Workspace frame ID cannot be empty.");
318 }
319 if (key.waypoints.empty())
320 {
321 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, name_ + ": Skipping insert: No waypoints.");
322 }
323
324 // Check value.
325 if (value.solution.joint_trajectory.points.empty())
326 {
327 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, name_ + ": Empty joint trajectory points.");
328 }
329 if (value.solution.joint_trajectory.joint_names.empty())
330 {
331 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
332 name_ + ": Skipping insert: Empty joint trajectory joint names.");
333 }
334 if (!value.solution.multi_dof_joint_trajectory.points.empty())
335 {
336 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
337 name_ + ": Skipping insert: Multi-DOF trajectory plans are not supported.");
338 }
339 if (value.solution.joint_trajectory.header.frame_id.empty())
340 {
341 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN,
342 name_ + ": Skipping insert: Trajectory frame ID cannot be empty.");
343 }
344 if (frame_id != value.solution.joint_trajectory.header.frame_id)
345 {
346 std::stringstream ss;
347 ss << "Skipping insert: Plan request frame `" << frame_id << "` does not match plan frame `"
348 << value.solution.joint_trajectory.header.frame_id << "`.";
349 return MoveItErrorCode(MoveItErrorCode::INVALID_MOTION_PLAN, ss.str());
350 }
351
352 return MoveItErrorCode::SUCCESS;
353}
354
355std::vector<MessageWithMetadata<RobotTrajectory>::ConstPtr> CartesianBestSeenExecutionTimePolicy::fetchMatchingEntries(
356 const MoveGroupInterface& move_group, const MessageCollection<RobotTrajectory>& coll,
357 const GetCartesianPath::Request& key, const GetCartesianPath::Response& /*value*/, double exact_match_precision)
358{
359 Query::Ptr query = coll.createQuery();
360 for (const auto& feature : exact_matching_supported_features_)
361 {
362 if (MoveItErrorCode ret = feature->appendFeaturesAsExactFetchQuery(*query, key, move_group, exact_match_precision);
363 !ret)
364 {
365 return {};
366 }
367 }
368
369 std::vector<MessageWithMetadata<RobotTrajectory>::ConstPtr> out =
370 coll.queryList(query, /*metadata_only=*/true, /*sort_by=*/EXECUTION_TIME, /*ascending=*/true);
371 if (!out.empty())
372 {
373 best_seen_execution_time_ = out[0]->lookupDouble(EXECUTION_TIME);
374 }
375
376 return out;
377}
378
380 const MoveGroupInterface& /*move_group*/, const GetCartesianPath::Request& /*key*/,
381 const GetCartesianPath::Response& value, const MessageWithMetadata<RobotTrajectory>::ConstPtr& matching_entry,
382 std::string* reason)
383{
384 double matching_entry_execution_time_s = matching_entry->lookupDouble(EXECUTION_TIME);
385 double candidate_execution_time_s = getExecutionTime(value.solution);
386
387 if (matching_entry_execution_time_s >= candidate_execution_time_s)
388 {
389 if (reason != nullptr)
390 {
391 std::stringstream ss;
392 ss << "Matching trajectory execution_time_s `" << matching_entry_execution_time_s << "s` "
393 << "is worse than candidate trajectory's execution_time_s `" << candidate_execution_time_s << "s`";
394 *reason = ss.str();
395 }
396 return true;
397 }
398 else
399 {
400 if (reason != nullptr)
401 {
402 std::stringstream ss;
403 ss << "Matching trajectory execution_time_s `" << matching_entry_execution_time_s << "s` "
404 << "is better than candidate trajectory's execution_time_s `" << candidate_execution_time_s << "s`";
405 *reason = ss.str();
406 }
407 return false;
408 }
409}
410
411bool CartesianBestSeenExecutionTimePolicy::shouldInsert(const MoveGroupInterface& /*move_group*/,
412 const GetCartesianPath::Request& /*key*/,
413 const GetCartesianPath::Response& value, std::string* reason)
414{
415 double execution_time_s = getExecutionTime(value.solution);
416
417 if (execution_time_s < best_seen_execution_time_)
418 {
419 if (reason != nullptr)
420 {
421 std::stringstream ss;
422 ss << "New cartesian trajectory execution_time_s `" << execution_time_s << "s` "
423 << "is better than best cartesian trajectory's execution_time_s `" << best_seen_execution_time_ << "s`";
424 *reason = ss.str();
425 }
426 return true;
427 }
428 else
429 {
430 if (reason != nullptr)
431 {
432 std::stringstream ss;
433 ss << "New cartesian trajectory execution_time `" << execution_time_s << "s` "
434 << "is worse than best cartesian trajectory's execution_time `" << best_seen_execution_time_ << "s`";
435 *reason = ss.str();
436 }
437 return false;
438 }
439}
440
442 const MoveGroupInterface& move_group,
443 const GetCartesianPath::Request& key,
444 const GetCartesianPath::Response& value)
445{
446 for (const auto& feature : exact_matching_supported_features_)
447 {
448 if (MoveItErrorCode ret = feature->appendFeaturesAsInsertMetadata(metadata, key, move_group); !ret)
449 {
450 return ret;
451 }
452 }
453
454 // Append ValueT metadata.
455 metadata.append(EXECUTION_TIME, getExecutionTime(value.solution));
456 metadata.append(FRACTION, value.fraction);
457
458 return MoveItErrorCode::SUCCESS;
459}
460
462{
463 best_seen_execution_time_ = std::numeric_limits<double>::infinity();
464 return;
465}
466
467} // namespace trajectory_cache
468} // namespace moveit_ros
A cache insertion policy that only decides to insert if the motion plan is the one with the shortest ...
std::string getName() const override
Gets the name of the cache insert policy.
moveit::core::MoveItErrorCode appendInsertMetadata(warehouse_ros::Metadata &metadata, const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::msg::MotionPlanRequest &key, const moveit::planning_interface::MoveGroupInterface::Plan &value) override
Appends the insert metadata with the features supported by the policy.
moveit::core::MoveItErrorCode checkCacheInsertInputs(const moveit::planning_interface::MoveGroupInterface &move_group, const warehouse_ros::MessageCollection< moveit_msgs::msg::RobotTrajectory > &coll, const moveit_msgs::msg::MotionPlanRequest &key, const moveit::planning_interface::MoveGroupInterface::Plan &value) override
Checks inputs to the cache insert call to see if we should abort instead.
static std::vector< std::unique_ptr< FeaturesInterface< moveit_msgs::msg::MotionPlanRequest > > > getSupportedFeatures(double start_tolerance, double goal_tolerance)
Configures and returns a vector of feature extractors that can be used with this policy.
bool shouldInsert(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::msg::MotionPlanRequest &key, const moveit::planning_interface::MoveGroupInterface::Plan &value, std::string *reason=nullptr) override
Returns whether the insertion candidate should be inserted into the cache.
std::vector< warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr > fetchMatchingEntries(const moveit::planning_interface::MoveGroupInterface &move_group, const warehouse_ros::MessageCollection< moveit_msgs::msg::RobotTrajectory > &coll, const moveit_msgs::msg::MotionPlanRequest &key, const moveit::planning_interface::MoveGroupInterface::Plan &value, double exact_match_precision) override
Fetches all "matching" cache entries for comparison for pruning.
bool shouldPruneMatchingEntry(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::msg::MotionPlanRequest &key, const moveit::planning_interface::MoveGroupInterface::Plan &value, const warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr &matching_entry, std::string *reason=nullptr) override
moveit::core::MoveItErrorCode appendInsertMetadata(warehouse_ros::Metadata &metadata, const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::srv::GetCartesianPath::Request &key, const moveit_msgs::srv::GetCartesianPath::Response &value) override
Appends the insert metadata with the features supported by the policy.
static std::vector< std::unique_ptr< FeaturesInterface< moveit_msgs::srv::GetCartesianPath::Request > > > getSupportedFeatures(double start_tolerance, double goal_tolerance, double min_fraction)
Configures and returns a vector of feature extractors that can be used with this policy.
moveit::core::MoveItErrorCode checkCacheInsertInputs(const moveit::planning_interface::MoveGroupInterface &move_group, const warehouse_ros::MessageCollection< moveit_msgs::msg::RobotTrajectory > &coll, const moveit_msgs::srv::GetCartesianPath::Request &key, const moveit_msgs::srv::GetCartesianPath::Response &value) override
Checks inputs to the cache insert call to see if we should abort instead.
std::vector< warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr > fetchMatchingEntries(const moveit::planning_interface::MoveGroupInterface &move_group, const warehouse_ros::MessageCollection< moveit_msgs::msg::RobotTrajectory > &coll, const moveit_msgs::srv::GetCartesianPath::Request &key, const moveit_msgs::srv::GetCartesianPath::Response &value, double exact_match_precision) override
Fetches all "matching" cache entries for comparison for pruning.
std::string getName() const override
Gets the name of the cache insert policy.
bool shouldInsert(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::srv::GetCartesianPath::Request &key, const moveit_msgs::srv::GetCartesianPath::Response &value, std::string *reason=nullptr) override
Returns whether the insertion candidate should be inserted into the cache.
bool shouldPruneMatchingEntry(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::srv::GetCartesianPath::Request &key, const moveit_msgs::srv::GetCartesianPath::Response &value, const warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotTrajectory >::ConstPtr &matching_entry, std::string *reason=nullptr) override
User-specified constant features to key the trajectory cache on.
moveit_msgs::srv::GetCartesianPath::Request features to key the trajectory cache on.
moveit_msgs::msg::MotionPlanRequest features to key the trajectory cache on.
Utilities used by the trajectory_cache package.
std::string getWorkspaceFrameId(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::msg::WorkspaceParameters &workspace_parameters)
Gets workspace frame ID. If workspace_parameters has no frame ID, fetch it from move_group.
double getExecutionTime(const moveit_msgs::msg::RobotTrajectory &trajectory)
Returns the execution time of the trajectory in double seconds.
Definition utils.cpp:146
std::string getCartesianPathRequestFrameId(const moveit::planning_interface::MoveGroupInterface &move_group, const moveit_msgs::srv::GetCartesianPath::Request &path_request)
Gets cartesian path request frame ID. If path_request has no frame ID, fetch it from move_group.