moveit2
The MoveIt Motion Planning Framework for ROS 2.
BenchmarkExecutor.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2015, Rice University
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 the Rice University 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: Ryan Luna */
36 
39 #include <moveit/version.h>
40 #include <tf2_eigen/tf2_eigen.hpp>
41 
42 // TODO(henningkayser): Switch to boost/timer/progress_display.hpp with Boost 1.72
43 // boost/progress.hpp is deprecated and will be replaced by boost/timer/progress_display.hpp in Boost 1.72.
44 // Until then we need to suppress the deprecation warning.
45 #define BOOST_ALLOW_DEPRECATED_HEADERS
46 #include <boost/regex.hpp>
47 #include <boost/progress.hpp>
48 #undef BOOST_ALLOW_DEPRECATED_HEADERS
49 #include <boost/date_time/posix_time/posix_time.hpp>
50 #include <math.h>
51 #include <limits>
52 #include <filesystem>
53 #ifndef _WIN32
54 #include <unistd.h>
55 #else
56 #include <winsock2.h>
57 #endif
58 
59 #undef max
60 
61 using namespace moveit_ros_benchmarks;
62 
63 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit.ros.benchmarks.BenchmarkExecutor");
64 
65 template <class Clock, class Duration>
66 boost::posix_time::ptime toBoost(const std::chrono::time_point<Clock, Duration>& from)
67 {
68  typedef std::chrono::nanoseconds duration_t;
69  typedef long rep_t;
70  rep_t d = std::chrono::duration_cast<duration_t>(from.time_since_epoch()).count();
71  rep_t sec = d / 1000000000;
72  rep_t nsec = d % 1000000000;
73  namespace pt = boost::posix_time;
74 #ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
75  return pt::from_time_t(sec) + pt::nanoseconds(nsec)
76 #else
77  return pt::from_time_t(sec) + pt::microseconds(nsec / 1000);
78 #endif
79 }
80 
81 static std::string getHostname()
82 {
83  static const int BUF_SIZE = 1024;
84  char buffer[BUF_SIZE];
85  int err = gethostname(buffer, sizeof(buffer));
86  if (err != 0)
87  return std::string();
88  else
89  {
90  buffer[BUF_SIZE - 1] = '\0';
91  return std::string(buffer);
92  }
93 }
94 
95 BenchmarkExecutor::BenchmarkExecutor(const rclcpp::Node::SharedPtr& node, const std::string& robot_description_param)
96  : node_(node), dbloader(node)
97 {
98  pss_ = nullptr;
99  psws_ = nullptr;
100  rs_ = nullptr;
101  cs_ = nullptr;
102  tcs_ = nullptr;
103  psm_ = new planning_scene_monitor::PlanningSceneMonitor(node, robot_description_param);
105 }
106 
108 {
109  delete pss_;
110  delete psws_;
111  delete rs_;
112  delete cs_;
113  delete tcs_;
114  delete psm_;
115 }
116 
117 void BenchmarkExecutor::initialize(const std::vector<std::string>& planning_pipeline_names)
118 {
119  planning_pipelines_.clear();
120 
121  // ros::NodeHandle pnh("~");
122  std::string parent_node_name = node_->get_name();
123  for (const std::string& planning_pipeline_name : planning_pipeline_names)
124  {
125  // Initialize planning pipelines from configured child namespaces
126  static rclcpp::Node::SharedPtr child_node = rclcpp::Node::make_shared(planning_pipeline_name, parent_node_name);
127  planning_pipeline::PlanningPipelinePtr pipeline(new planning_pipeline::PlanningPipeline(
128  planning_scene_->getRobotModel(), child_node, "planning_plugin", "request_adapters"));
129 
130  // Verify the pipeline has successfully initialized a planner
131  if (!pipeline->getPlannerManager())
132  {
133  RCLCPP_ERROR(LOGGER, "Failed to initialize planning pipeline '%s'", planning_pipeline_name.c_str());
134  continue;
135  }
136 
137  // Disable visualizations and store pipeline
138  pipeline->displayComputedMotionPlans(false);
139  pipeline->checkSolutionPaths(false);
140  planning_pipelines_[planning_pipeline_name] = pipeline;
141  }
142 
143  // Error check
144  if (planning_pipelines_.empty())
145  RCLCPP_ERROR(LOGGER, "No planning pipelines have been loaded. Nothing to do for the benchmarking service.");
146  else
147  {
148  RCLCPP_INFO(LOGGER, "Available planning pipelines:");
149  for (const std::pair<const std::string, planning_pipeline::PlanningPipelinePtr>& entry : planning_pipelines_)
150  RCLCPP_INFO_STREAM(LOGGER, "Pipeline: " << entry.first << ", Planner: " << entry.second->getPlannerPluginName());
151  }
152 }
153 
155 {
156  if (pss_)
157  {
158  delete pss_;
159  pss_ = nullptr;
160  }
161  if (psws_)
162  {
163  delete psws_;
164  psws_ = nullptr;
165  }
166  if (rs_)
167  {
168  delete rs_;
169  rs_ = nullptr;
170  }
171  if (cs_)
172  {
173  delete cs_;
174  cs_ = nullptr;
175  }
176  if (tcs_)
177  {
178  delete tcs_;
179  tcs_ = nullptr;
180  }
181 
182  benchmark_data_.clear();
183  pre_event_fns_.clear();
184  post_event_fns_.clear();
185  planner_start_fns_.clear();
186  planner_completion_fns_.clear();
187  query_start_fns_.clear();
188  query_end_fns_.clear();
189 }
190 
192 {
193  pre_event_fns_.push_back(func);
194 }
195 
197 {
198  post_event_fns_.push_back(func);
199 }
200 
202 {
203  planner_start_fns_.push_back(func);
204 }
205 
207 {
208  planner_completion_fns_.push_back(func);
209 }
210 
212 {
213  query_start_fns_.push_back(func);
214 }
215 
217 {
218  query_end_fns_.push_back(func);
219 }
220 
222 {
223  if (planning_pipelines_.empty())
224  {
225  RCLCPP_ERROR(LOGGER, "No planning pipelines configured. Did you call BenchmarkExecutor::initialize?");
226  return false;
227  }
228 
229  std::vector<BenchmarkRequest> queries;
230  moveit_msgs::msg::PlanningScene scene_msg;
231 
232  if (initializeBenchmarks(opts, scene_msg, queries))
233  {
235  return false;
236 
237  for (std::size_t i = 0; i < queries.size(); ++i)
238  {
239  // Configure planning scene
240  if (scene_msg.robot_model_name != planning_scene_->getRobotModel()->getName())
241  {
242  // Clear all geometry from the scene
243  planning_scene_->getWorldNonConst()->clearObjects();
244  planning_scene_->getCurrentStateNonConst().clearAttachedBodies();
245  planning_scene_->getCurrentStateNonConst().setToDefaultValues();
246 
247  planning_scene_->processPlanningSceneWorldMsg(scene_msg.world);
248  }
249  else
250  planning_scene_->usePlanningSceneMsg(scene_msg);
251 
252  // Calling query start events
253  for (QueryStartEventFunction& query_start_fn : query_start_fns_)
254  query_start_fn(queries[i].request, planning_scene_);
255 
256  RCLCPP_INFO(LOGGER, "Benchmarking query '%s' (%lu of %lu)", queries[i].name.c_str(), i + 1, queries.size());
257  std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
259  std::chrono::duration<double> dt = std::chrono::system_clock::now() - start_time;
260  double duration = dt.count();
261 
262  for (QueryCompletionEventFunction& query_end_fn : query_end_fns_)
263  query_end_fn(queries[i].request, planning_scene_);
264 
265  writeOutput(queries[i], boost::posix_time::to_iso_extended_string(toBoost(start_time)), duration);
266  }
267 
268  return true;
269  }
270  return false;
271 }
272 
273 bool BenchmarkExecutor::queriesAndPlannersCompatible(const std::vector<BenchmarkRequest>& requests,
274  const std::map<std::string, std::vector<std::string>>& /*planners*/)
275 {
276  // Make sure that the planner interfaces can service the desired queries
277  for (const std::pair<const std::string, planning_pipeline::PlanningPipelinePtr>& pipeline_entry : planning_pipelines_)
278  {
279  for (const BenchmarkRequest& request : requests)
280  {
281  if (!pipeline_entry.second->getPlannerManager()->canServiceRequest(request.request))
282  {
283  RCLCPP_ERROR(LOGGER, "Interface '%s' in pipeline '%s' cannot service the benchmark request '%s'",
284  pipeline_entry.second->getPlannerPluginName().c_str(), pipeline_entry.first.c_str(),
285  request.name.c_str());
286  return false;
287  }
288  }
289  }
290 
291  return true;
292 }
293 
294 bool BenchmarkExecutor::initializeBenchmarks(const BenchmarkOptions& opts, moveit_msgs::msg::PlanningScene& scene_msg,
295  std::vector<BenchmarkRequest>& requests)
296 {
298  return false;
299 
300  std::vector<StartState> start_states;
301  std::vector<PathConstraints> path_constraints;
302  std::vector<PathConstraints> goal_constraints;
303  std::vector<TrajectoryConstraints> traj_constraints;
304  std::vector<BenchmarkRequest> queries;
305 
306  if (!loadBenchmarkQueryData(opts, scene_msg, start_states, path_constraints, goal_constraints, traj_constraints,
307  queries))
308  {
309  RCLCPP_ERROR(LOGGER, "Failed to load benchmark query data");
310  return false;
311  }
312 
313  RCLCPP_INFO(
314  LOGGER,
315  "Benchmark loaded %lu starts, %lu goals, %lu path constraints, %lu trajectory constraints, and %lu queries",
316  start_states.size(), goal_constraints.size(), path_constraints.size(), traj_constraints.size(), queries.size());
317 
318  moveit_msgs::msg::WorkspaceParameters workspace_parameters = opts.getWorkspaceParameters();
319  // Make sure that workspace_parameters are set
320  if (workspace_parameters.min_corner.x == workspace_parameters.max_corner.x &&
321  workspace_parameters.min_corner.x == 0.0 &&
322  workspace_parameters.min_corner.y == workspace_parameters.max_corner.y &&
323  workspace_parameters.min_corner.y == 0.0 &&
324  workspace_parameters.min_corner.z == workspace_parameters.max_corner.z &&
325  workspace_parameters.min_corner.z == 0.0)
326  {
327  workspace_parameters.min_corner.x = workspace_parameters.min_corner.y = workspace_parameters.min_corner.z = -5.0;
328 
329  workspace_parameters.max_corner.x = workspace_parameters.max_corner.y = workspace_parameters.max_corner.z = 5.0;
330  }
331 
332  std::vector<double> goal_offset;
333  opts.getGoalOffsets(goal_offset);
334 
335  // Create the combinations of BenchmarkRequests
336 
337  // 1) Create requests for combinations of start states,
338  // goal constraints, and path constraints
339  for (PathConstraints& goal_constraint : goal_constraints)
340  {
341  // Common benchmark request properties
342  BenchmarkRequest brequest;
343  brequest.name = goal_constraint.name;
344  brequest.request.workspace_parameters = workspace_parameters;
345  brequest.request.goal_constraints = goal_constraint.constraints;
346  brequest.request.group_name = opts.getGroupName();
347  brequest.request.allowed_planning_time = opts.getTimeout();
348  brequest.request.num_planning_attempts = 1;
349 
350  if (brequest.request.goal_constraints.size() == 1 &&
351  brequest.request.goal_constraints[0].position_constraints.size() == 1 &&
352  brequest.request.goal_constraints[0].orientation_constraints.size() == 1 &&
353  brequest.request.goal_constraints[0].visibility_constraints.empty() &&
354  brequest.request.goal_constraints[0].joint_constraints.empty())
355  shiftConstraintsByOffset(brequest.request.goal_constraints[0], goal_offset);
356 
357  std::vector<BenchmarkRequest> request_combos;
358  createRequestCombinations(brequest, start_states, path_constraints, request_combos);
359  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
360  }
361 
362  // 2) Existing queries are treated like goal constraints.
363  // Create all combos of query, start states, and path constraints
364  for (BenchmarkRequest& query : queries)
365  {
366  // Common benchmark request properties
367  BenchmarkRequest brequest;
368  brequest.name = query.name;
369  brequest.request = query.request;
370  brequest.request.group_name = opts.getGroupName();
371  brequest.request.allowed_planning_time = opts.getTimeout();
372  brequest.request.num_planning_attempts = 1;
373 
374  // Make sure that workspace_parameters are set
375  if (brequest.request.workspace_parameters.min_corner.x == brequest.request.workspace_parameters.max_corner.x &&
376  brequest.request.workspace_parameters.min_corner.x == 0.0 &&
377  brequest.request.workspace_parameters.min_corner.y == brequest.request.workspace_parameters.max_corner.y &&
378  brequest.request.workspace_parameters.min_corner.y == 0.0 &&
379  brequest.request.workspace_parameters.min_corner.z == brequest.request.workspace_parameters.max_corner.z &&
380  brequest.request.workspace_parameters.min_corner.z == 0.0)
381  {
382  // ROS_WARN("Workspace parameters are not set for request %s. Setting defaults", queries[i].name.c_str());
383  brequest.request.workspace_parameters = workspace_parameters;
384  }
385 
386  // Create all combinations of start states and path constraints
387  std::vector<BenchmarkRequest> request_combos;
388  createRequestCombinations(brequest, start_states, path_constraints, request_combos);
389  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
390  }
391 
392  // 3) Trajectory constraints are also treated like goal constraints
393  for (TrajectoryConstraints& traj_constraint : traj_constraints)
394  {
395  // Common benchmark request properties
396  BenchmarkRequest brequest;
397  brequest.name = traj_constraint.name;
398  brequest.request.trajectory_constraints = traj_constraint.constraints;
399  brequest.request.group_name = opts.getGroupName();
400  brequest.request.allowed_planning_time = opts.getTimeout();
401  brequest.request.num_planning_attempts = 1;
402 
403  if (brequest.request.trajectory_constraints.constraints.size() == 1 &&
404  brequest.request.trajectory_constraints.constraints[0].position_constraints.size() == 1 &&
405  brequest.request.trajectory_constraints.constraints[0].orientation_constraints.size() == 1 &&
406  brequest.request.trajectory_constraints.constraints[0].visibility_constraints.empty() &&
407  brequest.request.trajectory_constraints.constraints[0].joint_constraints.empty())
408  shiftConstraintsByOffset(brequest.request.trajectory_constraints.constraints[0], goal_offset);
409 
410  std::vector<BenchmarkRequest> request_combos;
411  std::vector<PathConstraints> no_path_constraints;
412  createRequestCombinations(brequest, start_states, no_path_constraints, request_combos);
413  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
414  }
415 
416  options_ = opts;
417  return true;
418 }
419 
420 bool BenchmarkExecutor::loadBenchmarkQueryData(const BenchmarkOptions& opts, moveit_msgs::msg::PlanningScene& scene_msg,
421  std::vector<StartState>& start_states,
422  std::vector<PathConstraints>& path_constraints,
423  std::vector<PathConstraints>& goal_constraints,
424  std::vector<TrajectoryConstraints>& traj_constraints,
425  std::vector<BenchmarkRequest>& queries)
426 {
427  try
428  {
429  warehouse_ros::DatabaseConnection::Ptr warehouse_connection = dbloader.loadDatabase();
430  warehouse_connection->setParams(opts.getHostName(), opts.getPort(), 20);
431  if (warehouse_connection->connect())
432  {
433  pss_ = new moveit_warehouse::PlanningSceneStorage(warehouse_connection);
434  psws_ = new moveit_warehouse::PlanningSceneWorldStorage(warehouse_connection);
435  rs_ = new moveit_warehouse::RobotStateStorage(warehouse_connection);
436  cs_ = new moveit_warehouse::ConstraintsStorage(warehouse_connection);
437  tcs_ = new moveit_warehouse::TrajectoryConstraintsStorage(warehouse_connection);
438  }
439  else
440  {
441  RCLCPP_ERROR(LOGGER, "Failed to connect to DB");
442  return false;
443  }
444  }
445  catch (std::exception& e)
446  {
447  RCLCPP_ERROR(LOGGER, "Failed to initialize benchmark server: '%s'", e.what());
448  return false;
449  }
450 
451  return loadPlanningScene(opts.getSceneName(), scene_msg) && loadStates(opts.getStartStateRegex(), start_states) &&
452  loadPathConstraints(opts.getGoalConstraintRegex(), goal_constraints) &&
454  loadTrajectoryConstraints(opts.getTrajectoryConstraintRegex(), traj_constraints) &&
455  loadQueries(opts.getQueryRegex(), opts.getSceneName(), queries);
456  return true;
457 }
458 
459 void BenchmarkExecutor::shiftConstraintsByOffset(moveit_msgs::msg::Constraints& constraints,
460  const std::vector<double>& offset)
461 {
462  Eigen::Isometry3d offset_tf(Eigen::AngleAxis<double>(offset[3], Eigen::Vector3d::UnitX()) *
463  Eigen::AngleAxis<double>(offset[4], Eigen::Vector3d::UnitY()) *
464  Eigen::AngleAxis<double>(offset[5], Eigen::Vector3d::UnitZ()));
465  offset_tf.translation() = Eigen::Vector3d(offset[0], offset[1], offset[2]);
466 
467  geometry_msgs::msg::Pose constraint_pose_msg;
468  constraint_pose_msg.position = constraints.position_constraints[0].constraint_region.primitive_poses[0].position;
469  constraint_pose_msg.orientation = constraints.orientation_constraints[0].orientation;
470  Eigen::Isometry3d constraint_pose;
471  tf2::fromMsg(constraint_pose_msg, constraint_pose);
472 
473  Eigen::Isometry3d new_pose = constraint_pose * offset_tf;
474  geometry_msgs::msg::Pose new_pose_msg;
475  new_pose_msg = tf2::toMsg(new_pose);
476 
477  constraints.position_constraints[0].constraint_region.primitive_poses[0].position = new_pose_msg.position;
478  constraints.orientation_constraints[0].orientation = new_pose_msg.orientation;
479 }
480 
482  const std::vector<StartState>& start_states,
483  const std::vector<PathConstraints>& path_constraints,
484  std::vector<BenchmarkRequest>& requests)
485 {
486  // Use default start state
487  if (start_states.empty())
488  {
489  // Adding path constraints
490  for (const PathConstraints& path_constraint : path_constraints)
491  {
492  BenchmarkRequest new_brequest = brequest;
493  new_brequest.request.path_constraints = path_constraint.constraints[0];
494  new_brequest.name = brequest.name + "_" + path_constraint.name;
495  requests.push_back(new_brequest);
496  }
497 
498  if (path_constraints.empty())
499  requests.push_back(brequest);
500  }
501  else // Create a request for each start state specified
502  {
503  for (const StartState& start_state : start_states)
504  {
505  // Skip start states that have the same name as the goal
506  if (start_state.name == brequest.name)
507  continue;
508 
509  BenchmarkRequest new_brequest = brequest;
510  new_brequest.request.start_state = start_state.state;
511 
512  // Duplicate the request for each of the path constraints
513  for (const PathConstraints& path_constraint : path_constraints)
514  {
515  new_brequest.request.path_constraints = path_constraint.constraints[0];
516  new_brequest.name = start_state.name + "_" + new_brequest.name + "_" + path_constraint.name;
517  requests.push_back(new_brequest);
518  }
519 
520  if (path_constraints.empty())
521  {
522  new_brequest.name = start_state.name + "_" + brequest.name;
523  requests.push_back(new_brequest);
524  }
525  }
526  }
527 }
528 
530  const std::map<std::string, std::vector<std::string>>& pipeline_configurations, const std::string& group_name)
531 {
532  // Make sure planner plugins exist
533  for (const std::pair<const std::string, std::vector<std::string>>& pipeline_config_entry : pipeline_configurations)
534  {
535  bool pipeline_exists = false;
536  for (const std::pair<const std::string, planning_pipeline::PlanningPipelinePtr>& pipeline_entry :
538  {
539  pipeline_exists = pipeline_entry.first == pipeline_config_entry.first;
540  if (pipeline_exists)
541  break;
542  }
543 
544  if (!pipeline_exists)
545  {
546  RCLCPP_ERROR(LOGGER, "Planning pipeline '%s' does NOT exist", pipeline_config_entry.first.c_str());
547  return false;
548  }
549  }
550 
551  // Make sure planners exist within those pipelines
552  for (const std::pair<const std::string, std::vector<std::string>>& entry : pipeline_configurations)
553  {
554  planning_interface::PlannerManagerPtr pm = planning_pipelines_[entry.first]->getPlannerManager();
555  const planning_interface::PlannerConfigurationMap& config_map = pm->getPlannerConfigurations();
556 
557  // if the planner is chomp or stomp skip this function and return true for checking planner configurations for the
558  // planning group otherwise an error occurs, because for OMPL a specific planning algorithm needs to be defined for
559  // a planning group, whereas with STOMP and CHOMP this is not necessary
560  if (pm->getDescription().compare("stomp") || pm->getDescription().compare("chomp"))
561  continue;
562 
563  for (std::size_t i = 0; i < entry.second.size(); ++i)
564  {
565  bool planner_exists = false;
566  for (const std::pair<const std::string, planning_interface::PlannerConfigurationSettings>& config_entry :
567  config_map)
568  {
569  std::string planner_name = group_name + "[" + entry.second[i] + "]";
570  planner_exists = (config_entry.second.group == group_name && config_entry.second.name == planner_name);
571  }
572 
573  if (!planner_exists)
574  {
575  RCLCPP_ERROR(LOGGER, "Planner '%s' does NOT exist for group '%s' in pipeline '%s'", entry.second[i].c_str(),
576  group_name.c_str(), entry.first.c_str());
577  std::cout << "There are " << config_map.size() << " planner entries: " << '\n';
578  for (const auto& config_map_entry : config_map)
579  std::cout << config_map_entry.second.name << '\n';
580  return false;
581  }
582  }
583  }
584 
585  return true;
586 }
587 
588 bool BenchmarkExecutor::loadPlanningScene(const std::string& scene_name, moveit_msgs::msg::PlanningScene& scene_msg)
589 {
590  bool ok = false;
591  try
592  {
593  if (pss_->hasPlanningScene(scene_name)) // whole planning scene
594  {
596  ok = pss_->getPlanningScene(pswm, scene_name);
597  scene_msg = static_cast<moveit_msgs::msg::PlanningScene>(*pswm);
598 
599  if (!ok)
600  RCLCPP_ERROR(LOGGER, "Failed to load planning scene '%s'", scene_name.c_str());
601  }
602  else if (psws_->hasPlanningSceneWorld(scene_name)) // Just the world (no robot)
603  {
605  ok = psws_->getPlanningSceneWorld(pswwm, scene_name);
606  scene_msg.world = static_cast<moveit_msgs::msg::PlanningSceneWorld>(*pswwm);
607  scene_msg.robot_model_name =
608  "NO ROBOT INFORMATION. ONLY WORLD GEOMETRY"; // this will be fixed when running benchmark
609 
610  if (!ok)
611  RCLCPP_ERROR(LOGGER, "Failed to load planning scene '%s'", scene_name.c_str());
612  }
613  else
614  RCLCPP_ERROR(LOGGER, "Failed to find planning scene '%s'", scene_name.c_str());
615  }
616  catch (std::exception& ex)
617  {
618  RCLCPP_ERROR(LOGGER, "Error loading planning scene: %s", ex.what());
619  }
620  RCLCPP_INFO(LOGGER, "Loaded planning scene successfully");
621  return ok;
622 }
623 
624 bool BenchmarkExecutor::loadQueries(const std::string& regex, const std::string& scene_name,
625  std::vector<BenchmarkRequest>& queries)
626 {
627  if (regex.empty())
628  return true;
629 
630  std::vector<std::string> query_names;
631  try
632  {
633  pss_->getPlanningQueriesNames(regex, query_names, scene_name);
634  }
635  catch (std::exception& ex)
636  {
637  RCLCPP_ERROR(LOGGER, "Error loading motion planning queries: %s", ex.what());
638  return false;
639  }
640 
641  if (query_names.empty())
642  {
643  RCLCPP_ERROR(LOGGER, "Scene '%s' has no associated queries", scene_name.c_str());
644  return false;
645  }
646 
647  for (const std::string& query_name : query_names)
648  {
650  try
651  {
652  pss_->getPlanningQuery(planning_query, scene_name, query_name);
653  }
654  catch (std::exception& ex)
655  {
656  RCLCPP_ERROR(LOGGER, "Error loading motion planning query '%s': %s", query_name.c_str(), ex.what());
657  continue;
658  }
659 
660  BenchmarkRequest query;
661  query.name = query_name;
662  query.request = static_cast<moveit_msgs::msg::MotionPlanRequest>(*planning_query);
663  queries.push_back(query);
664  }
665  RCLCPP_INFO(LOGGER, "Loaded queries successfully");
666  return true;
667 }
668 
669 bool BenchmarkExecutor::loadStates(const std::string& regex, std::vector<StartState>& start_states)
670 {
671  if (!regex.empty())
672  {
673  std::regex start_regex(regex);
674  std::vector<std::string> state_names;
675  rs_->getKnownRobotStates(state_names);
676  for (const std::string& state_name : state_names)
677  {
678  std::smatch match;
679  if (std::regex_match(state_name, match, start_regex))
680  {
682  try
683  {
684  if (rs_->getRobotState(robot_state, state_name))
685  {
686  StartState start_state;
687  start_state.state = moveit_msgs::msg::RobotState(*robot_state);
688  start_state.name = state_name;
689  start_states.push_back(start_state);
690  }
691  }
692  catch (std::exception& ex)
693  {
694  RCLCPP_ERROR(LOGGER, "Runtime error when loading state '%s': %s", state_name.c_str(), ex.what());
695  continue;
696  }
697  }
698  }
699 
700  if (start_states.empty())
701  RCLCPP_WARN(LOGGER, "No stored states matched the provided start state regex: '%s'", regex.c_str());
702  }
703  RCLCPP_INFO(LOGGER, "Loaded states successfully");
704  return true;
705 }
706 
707 bool BenchmarkExecutor::loadPathConstraints(const std::string& regex, std::vector<PathConstraints>& constraints)
708 {
709  if (!regex.empty())
710  {
711  std::vector<std::string> cnames;
712  cs_->getKnownConstraints(regex, cnames);
713 
714  for (const std::string& cname : cnames)
715  {
717  try
718  {
719  if (cs_->getConstraints(constr, cname))
720  {
721  PathConstraints constraint;
722  constraint.constraints.push_back(*constr);
723  constraint.name = cname;
724  constraints.push_back(constraint);
725  }
726  }
727  catch (std::exception& ex)
728  {
729  RCLCPP_ERROR(LOGGER, "Runtime error when loading path constraint '%s': %s", cname.c_str(), ex.what());
730  continue;
731  }
732  }
733 
734  if (constraints.empty())
735  RCLCPP_WARN(LOGGER, "No path constraints found that match regex: '%s'", regex.c_str());
736  else
737  RCLCPP_INFO(LOGGER, "Loaded path constraints successfully");
738  }
739  return true;
740 }
741 
742 bool BenchmarkExecutor::loadTrajectoryConstraints(const std::string& regex,
743  std::vector<TrajectoryConstraints>& constraints)
744 {
745  if (!regex.empty())
746  {
747  std::vector<std::string> cnames;
748  tcs_->getKnownTrajectoryConstraints(regex, cnames);
749 
750  for (const std::string& cname : cnames)
751  {
753  try
754  {
755  if (tcs_->getTrajectoryConstraints(constr, cname))
756  {
757  TrajectoryConstraints constraint;
758  constraint.constraints = *constr;
759  constraint.name = cname;
760  constraints.push_back(constraint);
761  }
762  }
763  catch (std::exception& ex)
764  {
765  RCLCPP_ERROR(LOGGER, "Runtime error when loading trajectory constraint '%s': %s", cname.c_str(), ex.what());
766  continue;
767  }
768  }
769 
770  if (constraints.empty())
771  RCLCPP_WARN(LOGGER, "No trajectory constraints found that match regex: '%s'", regex.c_str());
772  else
773  RCLCPP_INFO(LOGGER, "Loaded trajectory constraints successfully");
774  }
775  return true;
776 }
777 
779  const std::map<std::string, std::vector<std::string>>& pipeline_map, int runs)
780 {
781  benchmark_data_.clear();
782 
783  unsigned int num_planners = 0;
784  for (const std::pair<const std::string, std::vector<std::string>>& pipeline_entry : pipeline_map)
785  num_planners += pipeline_entry.second.size();
786 
787  boost::progress_display progress(num_planners * runs, std::cout);
788 
789  // Iterate through all planning pipelines
790  for (const std::pair<const std::string, std::vector<std::string>>& pipeline_entry : pipeline_map)
791  {
792  planning_pipeline::PlanningPipelinePtr planning_pipeline = planning_pipelines_[pipeline_entry.first];
793  // Use the planning context if the pipeline only contains the planner plugin
794  bool use_planning_context = planning_pipeline->getAdapterPluginNames().empty();
795  // Iterate through all planners configured for the pipeline
796  for (const std::string& planner_id : pipeline_entry.second)
797  {
798  // This container stores all of the benchmark data for this planner
799  PlannerBenchmarkData planner_data(runs);
800  // This vector stores all motion plan results for further evaluation
801  std::vector<planning_interface::MotionPlanDetailedResponse> responses(runs);
802  std::vector<bool> solved(runs);
803 
804  request.planner_id = planner_id;
805 
806  // Planner start events
807  for (PlannerStartEventFunction& planner_start_fn : planner_start_fns_)
808  planner_start_fn(request, planner_data);
809 
810  planning_interface::PlanningContextPtr planning_context;
811  if (use_planning_context)
812  planning_context = planning_pipeline->getPlannerManager()->getPlanningContext(planning_scene_, request);
813 
814  // Iterate runs
815  for (int j = 0; j < runs; ++j)
816  {
817  // Pre-run events
818  for (PreRunEventFunction& pre_event_fn : pre_event_fns_)
819  pre_event_fn(request);
820 
821  // Solve problem
822  std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
823  if (use_planning_context)
824  {
825  solved[j] = planning_context->solve(responses[j]);
826  }
827  else
828  {
829  // The planning pipeline does not support MotionPlanDetailedResponse
831  solved[j] = planning_pipeline->generatePlan(planning_scene_, request, response);
832  responses[j].error_code_ = response.error_code_;
833  if (response.trajectory_)
834  {
835  responses[j].description_.push_back("plan");
836  responses[j].trajectory_.push_back(response.trajectory_);
837  responses[j].processing_time_.push_back(response.planning_time_);
838  }
839  }
840  std::chrono::duration<double> dt = std::chrono::system_clock::now() - start;
841  double total_time = dt.count();
842 
843  // Collect data
844  start = std::chrono::system_clock::now();
845 
846  // Post-run events
847  for (PostRunEventFunction& post_event_fn : post_event_fns_)
848  post_event_fn(request, responses[j], planner_data[j]);
849  collectMetrics(planner_data[j], responses[j], solved[j], total_time);
850  dt = std::chrono::system_clock::now() - start;
851  double metrics_time = dt.count();
852  RCLCPP_DEBUG(LOGGER, "Spent %lf seconds collecting metrics", metrics_time);
853 
854  ++progress;
855  }
856 
857  computeAveragePathSimilarities(planner_data, responses, solved);
858 
859  // Planner completion events
860  for (PlannerCompletionEventFunction& planner_completion_fn : planner_completion_fns_)
861  planner_completion_fn(request, planner_data);
862 
863  benchmark_data_.push_back(planner_data);
864  }
865  }
866 }
867 
869  const planning_interface::MotionPlanDetailedResponse& mp_res, bool solved,
870  double total_time)
871 {
872  metrics["time REAL"] = moveit::core::toString(total_time);
873  metrics["solved BOOLEAN"] = solved ? "true" : "false";
874 
875  if (solved)
876  {
877  // Analyzing the trajectory(ies) geometrically
878  double traj_len = 0.0; // trajectory length
879  double clearance = 0.0; // trajectory clearance (average)
880  bool correct = true; // entire trajectory collision free and in bounds
881 
882  double process_time = total_time;
883  for (std::size_t j = 0; j < mp_res.trajectory_.size(); ++j)
884  {
885  correct = true;
886  traj_len = 0.0;
887  clearance = 0.0;
888  const robot_trajectory::RobotTrajectory& p = *mp_res.trajectory_[j];
889 
890  // compute path length
891  traj_len = robot_trajectory::path_length(p);
892 
893  // compute correctness and clearance
895  for (std::size_t k = 0; k < p.getWayPointCount(); ++k)
896  {
898  planning_scene_->checkCollisionUnpadded(req, res, p.getWayPoint(k));
899  if (res.collision)
900  correct = false;
901  if (!p.getWayPoint(k).satisfiesBounds())
902  correct = false;
903  double d = planning_scene_->distanceToCollisionUnpadded(p.getWayPoint(k));
904  if (d > 0.0) // in case of collision, distance is negative
905  clearance += d;
906  }
907  clearance /= (double)p.getWayPointCount();
908 
909  // compute smoothness
910  const auto smoothness = [&]() {
911  const auto s = robot_trajectory::smoothness(p);
912  return s.has_value() ? s.value() : 0.0;
913  }();
914 
915  metrics["path_" + mp_res.description_[j] + "_correct BOOLEAN"] = correct ? "true" : "false";
916  metrics["path_" + mp_res.description_[j] + "_length REAL"] = moveit::core::toString(traj_len);
917  metrics["path_" + mp_res.description_[j] + "_clearance REAL"] = moveit::core::toString(clearance);
918  metrics["path_" + mp_res.description_[j] + "_smoothness REAL"] = moveit::core::toString(smoothness);
919  metrics["path_" + mp_res.description_[j] + "_time REAL"] = moveit::core::toString(mp_res.processing_time_[j]);
920 
921  if (j == mp_res.trajectory_.size() - 1)
922  {
923  metrics["final_path_correct BOOLEAN"] = correct ? "true" : "false";
924  metrics["final_path_length REAL"] = moveit::core::toString(traj_len);
925  metrics["final_path_clearance REAL"] = moveit::core::toString(clearance);
926  metrics["final_path_smoothness REAL"] = moveit::core::toString(smoothness);
927  metrics["final_path_time REAL"] = moveit::core::toString(mp_res.processing_time_[j]);
928  }
929  process_time -= mp_res.processing_time_[j];
930  }
931  if (process_time <= 0.0)
932  process_time = 0.0;
933  metrics["process_time REAL"] = moveit::core::toString(process_time);
934  }
935 }
936 
938  PlannerBenchmarkData& planner_data, const std::vector<planning_interface::MotionPlanDetailedResponse>& responses,
939  const std::vector<bool>& solved)
940 {
941  RCLCPP_INFO(LOGGER, "Computing result path similarity");
942  const size_t result_count = planner_data.size();
943  size_t unsolved = std::count_if(solved.begin(), solved.end(), [](bool s) { return !s; });
944  std::vector<double> average_distances(responses.size());
945  for (size_t first_traj_i = 0; first_traj_i < result_count; ++first_traj_i)
946  {
947  // If trajectory was not solved there is no valid average distance so it's set to max double only
948  if (!solved[first_traj_i])
949  {
950  average_distances[first_traj_i] = std::numeric_limits<double>::max();
951  continue;
952  }
953  // Iterate all result trajectories that haven't been compared yet
954  for (size_t second_traj_i = first_traj_i + 1; second_traj_i < result_count; ++second_traj_i)
955  {
956  // Ignore if other result has not been solved
957  if (!solved[second_traj_i])
958  continue;
959 
960  // Get final trajectories
961  const robot_trajectory::RobotTrajectory& traj_first = *responses[first_traj_i].trajectory_.back();
962  const robot_trajectory::RobotTrajectory& traj_second = *responses[second_traj_i].trajectory_.back();
963 
964  // Compute trajectory distance
965  double trajectory_distance;
966  if (!computeTrajectoryDistance(traj_first, traj_second, trajectory_distance))
967  continue;
968 
969  // Add average distance to counters of both trajectories
970  average_distances[first_traj_i] += trajectory_distance;
971  average_distances[second_traj_i] += trajectory_distance;
972  }
973  // Normalize average distance by number of actual comparisons
974  average_distances[first_traj_i] /= result_count - unsolved - 1;
975  }
976 
977  // Store results in planner_data
978  for (size_t i = 0; i < result_count; ++i)
979  planner_data[i]["average_waypoint_distance REAL"] = moveit::core::toString(average_distances[i]);
980 }
981 
983  const robot_trajectory::RobotTrajectory& traj_second,
984  double& result_distance)
985 {
986  // Abort if trajectories are empty
987  if (traj_first.empty() || traj_second.empty())
988  return false;
989 
990  // Waypoint counter
991  size_t pos_first = 0;
992  size_t pos_second = 0;
993  const size_t max_pos_first = traj_first.getWayPointCount() - 1;
994  const size_t max_pos_second = traj_second.getWayPointCount() - 1;
995 
996  // Compute total distance between pairwise waypoints of both trajectories.
997  // The selection of waypoint pairs is based on what steps results in the minimal distance between the next pair of
998  // waypoints. We first check what steps are still possible or if we reached the end of the trajectories. Then we
999  // compute the pairwise waypoint distances of the pairs from increasing both, the first, or the second trajectory.
1000  // Finally we select the pair that results in the minimal distance, summarize the total distance and iterate
1001  // accordingly. After that we compute the average trajectory distance by normalizing over the number of steps.
1002  double total_distance = 0;
1003  size_t steps = 0;
1004  double current_distance = traj_first.getWayPoint(pos_first).distance(traj_second.getWayPoint(pos_second));
1005  while (true)
1006  {
1007  // Keep track of total distance and number of comparisons
1008  total_distance += current_distance;
1009  ++steps;
1010  if (pos_first == max_pos_first && pos_second == max_pos_second) // end reached
1011  break;
1012 
1013  // Determine what steps are still possible
1014  bool can_up_first = pos_first < max_pos_first;
1015  bool can_up_second = pos_second < max_pos_second;
1016  bool can_up_both = can_up_first && can_up_second;
1017 
1018  // Compute pair-wise waypoint distances (increasing both, first, or second trajectories).
1019  double up_both = std::numeric_limits<double>::max();
1020  double up_first = std::numeric_limits<double>::max();
1021  double up_second = std::numeric_limits<double>::max();
1022  if (can_up_both)
1023  up_both = traj_first.getWayPoint(pos_first + 1).distance(traj_second.getWayPoint(pos_second + 1));
1024  if (can_up_first)
1025  up_first = traj_first.getWayPoint(pos_first + 1).distance(traj_second.getWayPoint(pos_second));
1026  if (can_up_second)
1027  up_second = traj_first.getWayPoint(pos_first).distance(traj_second.getWayPoint(pos_second + 1));
1028 
1029  // Select actual step, store new distance value and iterate trajectory positions
1030  if (can_up_both && up_both < up_first && up_both < up_second)
1031  {
1032  ++pos_first;
1033  ++pos_second;
1034  current_distance = up_both;
1035  }
1036  else if ((can_up_first && up_first < up_second) || !can_up_second)
1037  {
1038  ++pos_first;
1039  current_distance = up_first;
1040  }
1041  else if (can_up_second)
1042  {
1043  ++pos_second;
1044  current_distance = up_second;
1045  }
1046  }
1047  // Normalize trajectory distance by number of comparison steps
1048  result_distance = total_distance / static_cast<double>(steps);
1049  return true;
1050 }
1051 
1052 void BenchmarkExecutor::writeOutput(const BenchmarkRequest& brequest, const std::string& start_time,
1053  double benchmark_duration)
1054 {
1055  const std::map<std::string, std::vector<std::string>>& pipelines = options_.getPlanningPipelineConfigurations();
1056 
1057  size_t num_planners = 0;
1058  for (const std::pair<const std::string, std::vector<std::string>>& pipeline : pipelines)
1059  num_planners += pipeline.second.size();
1060 
1061  std::string hostname = getHostname();
1062  if (hostname.empty())
1063  hostname = "UNKNOWN";
1064 
1065  std::string filename = options_.getOutputDirectory();
1066  if (!filename.empty() && filename[filename.size() - 1] != '/')
1067  filename.append("/");
1068 
1069  // Ensure directories exist
1070  std::filesystem::create_directories(filename);
1071 
1072  filename += (options_.getBenchmarkName().empty() ? "" : options_.getBenchmarkName() + "_") + brequest.name + "_" +
1073  getHostname() + "_" + start_time + ".log";
1074  std::ofstream out(filename.c_str());
1075  if (!out)
1076  {
1077  RCLCPP_ERROR(LOGGER, "Failed to open '%s' for benchmark output", filename.c_str());
1078  return;
1079  }
1080 
1081  out << "MoveIt version " << MOVEIT_VERSION_STR << '\n';
1082  out << "Experiment " << brequest.name << '\n';
1083  out << "Running on " << hostname << '\n';
1084  out << "Starting at " << start_time << '\n';
1085 
1086  // Experiment setup
1087  moveit_msgs::msg::PlanningScene scene_msg;
1088  planning_scene_->getPlanningSceneMsg(scene_msg);
1089  out << "<<<|" << '\n';
1090  // TODO(YuYan): implement stream print for the message formats
1091  // out << "Motion plan request:" << '\n' << brequest.request << '\n';
1092  // out << "Planning scene: " << '\n' << scene_msg << '\n' << "|>>>" << '\n';
1093  out << "Motion plan request:" << '\n'
1094  << " planner_id: " << brequest.request.planner_id << '\n'
1095  << " group_name: " << brequest.request.group_name << '\n'
1096  << " num_planning_attempts: " << brequest.request.num_planning_attempts << '\n'
1097  << " allowed_planning_time: " << brequest.request.allowed_planning_time << '\n';
1098  out << "Planning scene:" << '\n'
1099  << " scene_name: " << scene_msg.name << '\n'
1100  << " robot_model_name: " << scene_msg.robot_model_name << '\n'
1101  << "|>>>" << '\n';
1102 
1103  // Not writing optional cpu information
1104 
1105  // The real random seed is unknown. Writing a fake value
1106  out << "0 is the random seed" << '\n';
1107  out << brequest.request.allowed_planning_time << " seconds per run" << '\n';
1108  // There is no memory cap
1109  out << "-1 MB per run" << '\n';
1110  out << options_.getNumRuns() << " runs per planner" << '\n';
1111  out << benchmark_duration << " seconds spent to collect the data" << '\n';
1112 
1113  // No enum types
1114  out << "0 enum types" << '\n';
1115 
1116  out << num_planners << " planners" << '\n';
1117 
1118  size_t run_id = 0;
1119  for (const std::pair<const std::string, std::vector<std::string>>& pipeline : pipelines)
1120  {
1121  for (std::size_t i = 0; i < pipeline.second.size(); ++i, ++run_id)
1122  {
1123  // Write the name of the planner and the used pipeline
1124  out << pipeline.second[i] << " (" << pipeline.first << ")" << '\n';
1125 
1126  // in general, we could have properties specific for a planner;
1127  // right now, we do not include such properties
1128  out << "0 common properties" << '\n';
1129 
1130  // Create a list of the benchmark properties for this planner
1131  std::set<std::string> properties_set;
1132  for (PlannerRunData& planner_run_data : benchmark_data_[run_id]) // each run of this planner
1133  for (PlannerRunData::const_iterator pit = planner_run_data.begin(); pit != planner_run_data.end();
1134  ++pit) // each benchmark property of the given run
1135  properties_set.insert(pit->first);
1136 
1137  // Writing property list
1138  out << properties_set.size() << " properties for each run" << '\n';
1139  for (const std::string& property : properties_set)
1140  out << property << '\n';
1141 
1142  // Number of runs
1143  out << benchmark_data_[run_id].size() << " runs" << '\n';
1144 
1145  // And the benchmark properties
1146  for (PlannerRunData& planner_run_data : benchmark_data_[run_id]) // each run of this planner
1147  {
1148  // Write out properties in the order we listed them above
1149  for (const std::string& property : properties_set)
1150  {
1151  // Make sure this run has this property
1152  PlannerRunData::const_iterator runit = planner_run_data.find(property);
1153  if (runit != planner_run_data.end())
1154  out << runit->second;
1155  out << "; ";
1156  }
1157  out << '\n'; // end of the run
1158  }
1159  out << "." << '\n'; // end the planner
1160  }
1161  }
1162 
1163  out.close();
1164  RCLCPP_INFO(LOGGER, "Benchmark results saved to '%s'", filename.c_str());
1165 }
boost::posix_time::ptime toBoost(const std::chrono::time_point< Clock, Duration > &from)
double distance(const RobotState &other) const
Return the sum of joint distances to "other" state. An L1 norm. Only considers active joints.
Definition: robot_state.h:1457
bool loadPlanningScene(const std::string &scene_name, moveit_msgs::msg::PlanningScene &scene_msg)
Load the planning scene with the given name from the warehouse.
void computeAveragePathSimilarities(PlannerBenchmarkData &planner_data, const std::vector< planning_interface::MotionPlanDetailedResponse > &responses, const std::vector< bool > &solved)
virtual bool runBenchmarks(const BenchmarkOptions &opts)
bool loadQueries(const std::string &regex, const std::string &scene_name, std::vector< BenchmarkRequest > &queries)
Load all motion plan requests matching the given regular expression from the warehouse.
std::vector< PlannerRunData > PlannerBenchmarkData
Structure to hold information for a single planner's benchmark data.
virtual bool loadBenchmarkQueryData(const BenchmarkOptions &opts, moveit_msgs::msg::PlanningScene &scene_msg, std::vector< StartState > &start_states, std::vector< PathConstraints > &path_constraints, std::vector< PathConstraints > &goal_constraints, std::vector< TrajectoryConstraints > &traj_constraints, std::vector< BenchmarkRequest > &queries)
Initialize benchmark query data from start states and constraints.
void initialize(const std::vector< std::string > &plugin_classes)
planning_scene::PlanningScenePtr planning_scene_
moveit_warehouse::ConstraintsStorage * cs_
bool computeTrajectoryDistance(const robot_trajectory::RobotTrajectory &traj_first, const robot_trajectory::RobotTrajectory &traj_second, double &result_distance)
void addQueryStartEvent(const QueryStartEventFunction &func)
std::vector< PostRunEventFunction > post_event_fns_
std::vector< QueryCompletionEventFunction > query_end_fns_
moveit_warehouse::RobotStateStorage * rs_
bool plannerConfigurationsExist(const std::map< std::string, std::vector< std::string >> &planners, const std::string &group_name)
Check that the desired planner plugins and algorithms exist for the given group.
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, PlannerBenchmarkData &benchmark_data)> PlannerCompletionEventFunction
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, planning_scene::PlanningScenePtr)> QueryCompletionEventFunction
Definition of a query-end benchmark event function. Invoked after a query has finished benchmarking.
moveit_warehouse::PlanningSceneStorage * pss_
bool loadPathConstraints(const std::string &regex, std::vector< PathConstraints > &constraints)
Load all constraints matching the given regular expression from the warehouse.
void addPreRunEvent(const PreRunEventFunction &func)
planning_scene_monitor::PlanningSceneMonitor * psm_
BenchmarkExecutor(const rclcpp::Node::SharedPtr &node, const std::string &robot_description_param="robot_description")
bool queriesAndPlannersCompatible(const std::vector< BenchmarkRequest > &requests, const std::map< std::string, std::vector< std::string >> &planners)
Check that the given requests can be run on the set of planner plugins and algorithms.
std::vector< PlannerBenchmarkData > benchmark_data_
virtual void collectMetrics(PlannerRunData &metrics, const planning_interface::MotionPlanDetailedResponse &mp_res, bool solved, double total_time)
std::map< std::string, planning_pipeline::PlanningPipelinePtr > planning_pipelines_
std::vector< QueryStartEventFunction > query_start_fns_
moveit_warehouse::TrajectoryConstraintsStorage * tcs_
void addPlannerCompletionEvent(const PlannerCompletionEventFunction &func)
void addPlannerStartEvent(const PlannerStartEventFunction &func)
void addQueryCompletionEvent(const QueryCompletionEventFunction &func)
bool loadStates(const std::string &regex, std::vector< StartState > &start_states)
Load all states matching the given regular expression from the warehouse.
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, const planning_interface::MotionPlanDetailedResponse &response, PlannerRunData &run_data)> PostRunEventFunction
Definition of a post-run benchmark event function. Invoked immediately after each planner calls solve...
void runBenchmark(moveit_msgs::msg::MotionPlanRequest request, const std::map< std::string, std::vector< std::string >> &planners, int runs)
Execute the given motion plan request on the set of planners for the set number of runs.
std::function< void(moveit_msgs::msg::MotionPlanRequest &request)> PreRunEventFunction
Definition of a pre-run benchmark event function. Invoked immediately before each planner calls solve...
void createRequestCombinations(const BenchmarkRequest &brequest, const std::vector< StartState > &start_states, const std::vector< PathConstraints > &path_constraints, std::vector< BenchmarkRequest > &combos)
Duplicate the given benchmark request for all combinations of start states and path constraints.
moveit_warehouse::PlanningSceneWorldStorage * psws_
virtual void writeOutput(const BenchmarkRequest &brequest, const std::string &start_time, double benchmark_duration)
warehouse_ros::DatabaseLoader dbloader
std::vector< PlannerStartEventFunction > planner_start_fns_
std::vector< PreRunEventFunction > pre_event_fns_
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, planning_scene::PlanningScenePtr)> QueryStartEventFunction
Definition of a query-start benchmark event function. Invoked before a new query is benchmarked.
virtual bool initializeBenchmarks(const BenchmarkOptions &opts, moveit_msgs::msg::PlanningScene &scene_msg, std::vector< BenchmarkRequest > &queries)
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, PlannerBenchmarkData &benchmark_data)> PlannerStartEventFunction
bool loadTrajectoryConstraints(const std::string &regex, std::vector< TrajectoryConstraints > &constraints)
Load all trajectory constraints from the warehouse that match the given regular expression.
void addPostRunEvent(const PostRunEventFunction &func)
std::vector< PlannerCompletionEventFunction > planner_completion_fns_
void shiftConstraintsByOffset(moveit_msgs::msg::Constraints &constraints, const std::vector< double > &offset)
std::map< std::string, std::string > PlannerRunData
Structure to hold information for a single run of a planner.
const moveit_msgs::msg::WorkspaceParameters & getWorkspaceParameters() const
const std::string & getPathConstraintRegex() const
Get the regex expression for matching the names of all path constraints to plan with.
const std::map< std::string, std::vector< std::string > > & getPlanningPipelineConfigurations() const
Get all planning pipeline names mapped to their parameter configuration.
const std::string & getBenchmarkName() const
Get the reference name of the benchmark.
int getNumRuns() const
Get the specified number of benchmark query runs.
double getTimeout() const
Get the maximum timeout per planning attempt.
const std::string & getHostName() const
Set the ROS namespace the node handle should use for parameter lookup.
int getPort() const
Get the port of the warehouse database host server.
void getGoalOffsets(std::vector< double > &offsets) const
Get the constant position/orientation offset to be used for shifting all goal constraints.
const std::string & getQueryRegex() const
Get the regex expression for matching the names of all queries to run.
const std::string & getGroupName() const
Get the name of the planning group to run the benchmark with.
const std::string & getGoalConstraintRegex() const
Get the regex expression for matching the names of all goal constraints to plan to.
const std::string & getStartStateRegex() const
Get the regex expression for matching the names of all start states to plan from.
const std::string & getSceneName() const
Get the reference name of the planning scene stored inside the warehouse database.
const std::string & getOutputDirectory() const
Get the target directory for the generated benchmark result data.
const std::string & getTrajectoryConstraintRegex() const
Get the regex expression for matching the names of all trajectory constraints to plan with.
bool getConstraints(ConstraintsWithMetadata &msg_m, const std::string &name, const std::string &robot="", const std::string &group="") const
Get the constraints named name. Return false on failure.
void getKnownConstraints(std::vector< std::string > &names, const std::string &robot="", const std::string &group="") const
bool getPlanningQuery(MotionPlanRequestWithMetadata &query_m, const std::string &scene_name, const std::string &query_name)
void getPlanningQueriesNames(std::vector< std::string > &query_names, const std::string &scene_name) const
bool hasPlanningScene(const std::string &name) const
bool getPlanningScene(PlanningSceneWithMetadata &scene_m, const std::string &scene_name) const
Get the latest planning scene named scene_name.
bool hasPlanningSceneWorld(const std::string &name) const
bool getPlanningSceneWorld(PlanningSceneWorldWithMetadata &msg_m, const std::string &name) const
Get the constraints named name. Return false on failure.
void getKnownRobotStates(std::vector< std::string > &names, const std::string &robot="") const
bool getRobotState(RobotStateWithMetadata &msg_m, const std::string &name, const std::string &robot="") const
Get the constraints named name. Return false on failure.
void getKnownTrajectoryConstraints(std::vector< std::string > &names, const std::string &robot="", const std::string &group="") const
bool getTrajectoryConstraints(TrajectoryConstraintsWithMetadata &msg_m, const std::string &name, const std::string &robot="", const std::string &group="") const
Get the constraints named name. Return false on failure.
This class facilitates loading planning plugins and planning request adapted plugins....
PlanningSceneMonitor Subscribes to the topic planning_scene.
const planning_scene::PlanningScenePtr & getPlanningScene()
Avoid this function! Returns an unsafe pointer to the current planning scene.
Maintain a sequence of waypoints and the time durations between these waypoints.
const moveit::core::RobotState & getWayPoint(std::size_t index) const
locale-agnostic conversion functions from floating point numbers to strings
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
std::string toString(double d)
Convert a double to std::string using the classic C locale.
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotState >::ConstPtr RobotStateWithMetadata
Definition: state_storage.h:47
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::PlanningScene >::ConstPtr PlanningSceneWithMetadata
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::TrajectoryConstraints >::ConstPtr TrajectoryConstraintsWithMetadata
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::PlanningSceneWorld >::ConstPtr PlanningSceneWorldWithMetadata
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::MotionPlanRequest >::ConstPtr MotionPlanRequestWithMetadata
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::Constraints >::ConstPtr ConstraintsWithMetadata
p
Definition: pick.py:62
std::map< std::string, PlannerConfigurationSettings > PlannerConfigurationMap
Map from PlannerConfigurationSettings.name to PlannerConfigurationSettings.
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
Planning pipeline.
double path_length(RobotTrajectory const &trajectory)
Calculate the path length of a given trajectory based on the accumulated robot state distances....
std::optional< double > smoothness(RobotTrajectory const &trajectory)
Calculate the smoothness of a given trajectory.
d
Definition: setup.py:4
name
Definition: setup.py:7
Representation of a collision checking request.
Representation of a collision checking result.
bool collision
True if collision was found, false otherwise.
std::vector< moveit_msgs::msg::Constraints > constraints
std::vector< robot_trajectory::RobotTrajectoryPtr > trajectory_
robot_trajectory::RobotTrajectoryPtr trajectory_
moveit_msgs::msg::MoveItErrorCodes error_code_