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>
41 
42 #include <boost/regex.hpp>
43 #include <boost/progress.hpp>
44 #include <boost/math/constants/constants.hpp>
45 #include <boost/filesystem.hpp>
46 #include <boost/date_time/posix_time/posix_time.hpp>
47 #include <unistd.h>
48 
49 using namespace moveit_ros_benchmarks;
50 
51 static std::string getHostname()
52 {
53  static const int BUF_SIZE = 1024;
54  char buffer[BUF_SIZE];
55  int err = gethostname(buffer, sizeof(buffer));
56  if (err != 0)
57  return std::string();
58  else
59  {
60  buffer[BUF_SIZE - 1] = '\0';
61  return std::string(buffer);
62  }
63 }
64 
65 BenchmarkExecutor::BenchmarkExecutor(const std::string& robot_description_param)
66 {
67  pss_ = NULL;
68  psws_ = NULL;
69  rs_ = NULL;
70  cs_ = NULL;
71  tcs_ = NULL;
72  psm_ = new planning_scene_monitor::PlanningSceneMonitor(robot_description_param);
74 
75  // Initialize the class loader for planner plugins
76  try
77  {
79  "moveit_core", "planning_interface::PlannerManager"));
80  }
82  {
83  ROS_FATAL_STREAM("Exception while creating planning plugin loader " << ex.what());
84  }
85 }
86 
88 {
89  if (pss_)
90  delete pss_;
91  if (psws_)
92  delete psws_;
93  if (rs_)
94  delete rs_;
95  if (cs_)
96  delete cs_;
97  if (tcs_)
98  delete tcs_;
99  delete psm_;
100 }
101 
102 void BenchmarkExecutor::initialize(const std::vector<std::string>& plugin_classes)
103 {
104  planner_interfaces_.clear();
105 
106  // Load the planning plugins
107  const std::vector<std::string>& classes = planner_plugin_loader_->getDeclaredClasses();
108 
109  for (std::size_t i = 0; i < plugin_classes.size(); ++i)
110  {
111  std::vector<std::string>::const_iterator it = std::find(classes.begin(), classes.end(), plugin_classes[i]);
112  if (it == classes.end())
113  {
114  ROS_ERROR("Failed to find plugin_class %s", plugin_classes[i].c_str());
115  return;
116  }
117 
118  try
119  {
120  planning_interface::PlannerManagerPtr p = planner_plugin_loader_->createUniqueInstance(plugin_classes[i]);
121  p->initialize(planning_scene_->getRobotModel(), "");
122 
123  p->getPlannerConfigurations();
124  planner_interfaces_[plugin_classes[i]] = p;
125  }
127  {
128  ROS_ERROR_STREAM("Exception while loading planner '" << plugin_classes[i] << "': " << ex.what());
129  }
130  }
131 
132  // error check
133  if (planner_interfaces_.empty())
134  ROS_ERROR("No planning plugins have been loaded. Nothing to do for the benchmarking service.");
135  else
136  {
137  std::stringstream ss;
138  for (std::map<std::string, planning_interface::PlannerManagerPtr>::const_iterator it = planner_interfaces_.begin();
139  it != planner_interfaces_.end(); ++it)
140  ss << it->first << " ";
141  ROS_INFO("Available planner instances: %s", ss.str().c_str());
142  }
143 }
144 
146 {
147  if (pss_)
148  {
149  delete pss_;
150  pss_ = NULL;
151  }
152  if (psws_)
153  {
154  delete psws_;
155  psws_ = NULL;
156  }
157  if (rs_)
158  {
159  delete rs_;
160  rs_ = NULL;
161  }
162  if (cs_)
163  {
164  delete cs_;
165  cs_ = NULL;
166  }
167  if (tcs_)
168  {
169  delete tcs_;
170  tcs_ = NULL;
171  }
172 
173  benchmark_data_.clear();
174  pre_event_fns_.clear();
175  post_event_fns_.clear();
176  planner_start_fns_.clear();
177  planner_completion_fns_.clear();
178  query_start_fns_.clear();
179  query_end_fns_.clear();
180 }
181 
183 {
184  pre_event_fns_.push_back(func);
185 }
186 
188 {
189  post_event_fns_.push_back(func);
190 }
191 
193 {
194  planner_start_fns_.push_back(func);
195 }
196 
198 {
199  planner_completion_fns_.push_back(func);
200 }
201 
203 {
204  query_start_fns_.push_back(func);
205 }
206 
208 {
209  query_end_fns_.push_back(func);
210 }
211 
213 {
214  if (planner_interfaces_.size() == 0)
215  {
216  ROS_ERROR("No planning interfaces configured. Did you call BenchmarkExecutor::initialize?");
217  return false;
218  }
219 
220  std::vector<BenchmarkRequest> queries;
221  moveit_msgs::PlanningScene scene_msg;
222 
223  if (initializeBenchmarks(opts, scene_msg, queries))
224  {
226  return false;
227 
228  for (std::size_t i = 0; i < queries.size(); ++i)
229  {
230  // Configure planning scene
231  if (scene_msg.robot_model_name != planning_scene_->getRobotModel()->getName())
232  {
233  // Clear all geometry from the scene
234  planning_scene_->getWorldNonConst()->clearObjects();
235  planning_scene_->getCurrentStateNonConst().clearAttachedBodies();
236  planning_scene_->getCurrentStateNonConst().setToDefaultValues();
237 
238  planning_scene_->processPlanningSceneWorldMsg(scene_msg.world);
239  }
240  else
241  planning_scene_->usePlanningSceneMsg(scene_msg);
242 
243  // Calling query start events
244  for (std::size_t j = 0; j < query_start_fns_.size(); ++j)
245  query_start_fns_[j](queries[i].request, planning_scene_);
246 
247  ROS_INFO("Benchmarking query '%s' (%lu of %lu)", queries[i].name.c_str(), i + 1, queries.size());
248  ros::WallTime start_time = ros::WallTime::now();
250  double duration = (ros::WallTime::now() - start_time).toSec();
251 
252  for (std::size_t j = 0; j < query_end_fns_.size(); ++j)
253  query_end_fns_[j](queries[i].request, planning_scene_);
254 
255  writeOutput(queries[i], boost::posix_time::to_iso_extended_string(start_time.toBoost()), duration);
256  }
257 
258  return true;
259  }
260  return false;
261 }
262 
263 bool BenchmarkExecutor::queriesAndPlannersCompatible(const std::vector<BenchmarkRequest>& requests,
264  const std::map<std::string, std::vector<std::string>>& planners)
265 {
266  // Make sure that the planner interfaces can service the desired queries
267  for (std::map<std::string, planning_interface::PlannerManagerPtr>::const_iterator it = planner_interfaces_.begin();
268  it != planner_interfaces_.end(); ++it)
269  {
270  for (std::size_t i = 0; i < requests.size(); ++i)
271  {
272  if (!it->second->canServiceRequest(requests[i].request))
273  {
274  ROS_ERROR("Interface '%s' cannot service the benchmark request '%s'", it->first.c_str(),
275  requests[i].name.c_str());
276  return false;
277  }
278  }
279  }
280 
281  return true;
282 }
283 
284 bool BenchmarkExecutor::initializeBenchmarks(const BenchmarkOptions& opts, moveit_msgs::PlanningScene& scene_msg,
285  std::vector<BenchmarkRequest>& requests)
286 {
288  return false;
289 
290  try
291  {
293  conn->setParams(opts.getHostName(), opts.getPort(), 20);
294  if (conn->connect())
295  {
301  }
302  else
303  {
304  ROS_ERROR("Failed to connect to DB");
305  return false;
306  }
307  }
308  catch (std::exception& e)
309  {
310  ROS_ERROR("Failed to initialize benchmark server: '%s'", e.what());
311  return false;
312  }
313 
314  std::vector<StartState> start_states;
315  std::vector<PathConstraints> path_constraints;
316  std::vector<PathConstraints> goal_constraints;
317  std::vector<TrajectoryConstraints> traj_constraints;
318  std::vector<BenchmarkRequest> queries;
319 
320  bool ok = loadPlanningScene(opts.getSceneName(), scene_msg) && loadStates(opts.getStartStateRegex(), start_states) &&
321  loadPathConstraints(opts.getGoalConstraintRegex(), goal_constraints) &&
322  loadPathConstraints(opts.getPathConstraintRegex(), path_constraints) &&
323  loadTrajectoryConstraints(opts.getTrajectoryConstraintRegex(), traj_constraints) &&
324  loadQueries(opts.getQueryRegex(), opts.getSceneName(), queries);
325 
326  if (!ok)
327  {
328  ROS_ERROR("Failed to load benchmark stuff");
329  return false;
330  }
331 
332  ROS_INFO("Benchmark loaded %lu starts, %lu goals, %lu path constraints, %lu trajectory constraints, and %lu queries",
333  start_states.size(), goal_constraints.size(), path_constraints.size(), traj_constraints.size(),
334  queries.size());
335 
336  moveit_msgs::WorkspaceParameters workspace_parameters = opts.getWorkspaceParameters();
337  // Make sure that workspace_parameters are set
338  if (workspace_parameters.min_corner.x == workspace_parameters.max_corner.x &&
339  workspace_parameters.min_corner.x == 0.0 &&
340  workspace_parameters.min_corner.y == workspace_parameters.max_corner.y &&
341  workspace_parameters.min_corner.y == 0.0 &&
342  workspace_parameters.min_corner.z == workspace_parameters.max_corner.z &&
343  workspace_parameters.min_corner.z == 0.0)
344  {
345  workspace_parameters.min_corner.x = workspace_parameters.min_corner.y = workspace_parameters.min_corner.z = -5.0;
346 
347  workspace_parameters.max_corner.x = workspace_parameters.max_corner.y = workspace_parameters.max_corner.z = 5.0;
348  }
349 
350  std::vector<double> goal_offset;
351  opts.getGoalOffsets(goal_offset);
352 
353  // Create the combinations of BenchmarkRequests
354 
355  // 1) Create requests for combinations of start states,
356  // goal constraints, and path constraints
357  for (std::size_t i = 0; i < goal_constraints.size(); ++i)
358  {
359  // Common benchmark request properties
360  BenchmarkRequest brequest;
361  brequest.name = goal_constraints[i].name;
362  brequest.request.workspace_parameters = workspace_parameters;
363  brequest.request.goal_constraints = goal_constraints[i].constraints;
364  brequest.request.group_name = opts.getGroupName();
365  brequest.request.allowed_planning_time = opts.getTimeout();
366  brequest.request.num_planning_attempts = 1;
367 
368  if (brequest.request.goal_constraints.size() == 1 &&
369  brequest.request.goal_constraints[0].position_constraints.size() == 1 &&
370  brequest.request.goal_constraints[0].orientation_constraints.size() == 1 &&
371  brequest.request.goal_constraints[0].visibility_constraints.size() == 0 &&
372  brequest.request.goal_constraints[0].joint_constraints.size() == 0)
373  shiftConstraintsByOffset(brequest.request.goal_constraints[0], goal_offset);
374 
375  std::vector<BenchmarkRequest> request_combos;
376  createRequestCombinations(brequest, start_states, path_constraints, request_combos);
377  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
378  }
379 
380  // 2) Existing queries are treated like goal constraints.
381  // Create all combos of query, start states, and path constraints
382  for (std::size_t i = 0; i < queries.size(); ++i)
383  {
384  // Common benchmark request properties
385  BenchmarkRequest brequest;
386  brequest.name = queries[i].name;
387  brequest.request = queries[i].request;
388  brequest.request.group_name = opts.getGroupName();
389  brequest.request.allowed_planning_time = opts.getTimeout();
390  brequest.request.num_planning_attempts = 1;
391 
392  // Make sure that workspace_parameters are set
393  if (brequest.request.workspace_parameters.min_corner.x == brequest.request.workspace_parameters.max_corner.x &&
394  brequest.request.workspace_parameters.min_corner.x == 0.0 &&
395  brequest.request.workspace_parameters.min_corner.y == brequest.request.workspace_parameters.max_corner.y &&
396  brequest.request.workspace_parameters.min_corner.y == 0.0 &&
397  brequest.request.workspace_parameters.min_corner.z == brequest.request.workspace_parameters.max_corner.z &&
398  brequest.request.workspace_parameters.min_corner.z == 0.0)
399  {
400  // ROS_WARN("Workspace parameters are not set for request %s. Setting defaults", queries[i].name.c_str());
401  brequest.request.workspace_parameters = workspace_parameters;
402  }
403 
404  // Create all combinations of start states and path constraints
405  std::vector<BenchmarkRequest> request_combos;
406  createRequestCombinations(brequest, start_states, path_constraints, request_combos);
407  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
408  }
409 
410  // 3) Trajectory constraints are also treated like goal constraints
411  for (std::size_t i = 0; i < traj_constraints.size(); ++i)
412  {
413  // Common benchmark request properties
414  BenchmarkRequest brequest;
415  brequest.name = traj_constraints[i].name;
416  brequest.request.trajectory_constraints = traj_constraints[i].constraints;
417  brequest.request.group_name = opts.getGroupName();
418  brequest.request.allowed_planning_time = opts.getTimeout();
419  brequest.request.num_planning_attempts = 1;
420 
421  if (brequest.request.trajectory_constraints.constraints.size() == 1 &&
422  brequest.request.trajectory_constraints.constraints[0].position_constraints.size() == 1 &&
423  brequest.request.trajectory_constraints.constraints[0].orientation_constraints.size() == 1 &&
424  brequest.request.trajectory_constraints.constraints[0].visibility_constraints.size() == 0 &&
425  brequest.request.trajectory_constraints.constraints[0].joint_constraints.size() == 0)
426  shiftConstraintsByOffset(brequest.request.trajectory_constraints.constraints[0], goal_offset);
427 
428  std::vector<BenchmarkRequest> request_combos;
429  std::vector<PathConstraints> no_path_constraints;
430  createRequestCombinations(brequest, start_states, no_path_constraints, request_combos);
431  requests.insert(requests.end(), request_combos.begin(), request_combos.end());
432  }
433 
434  options_ = opts;
435  return true;
436 }
437 
438 void BenchmarkExecutor::shiftConstraintsByOffset(moveit_msgs::Constraints& constraints,
439  const std::vector<double> offset)
440 {
441  Eigen::Affine3d offset_tf(Eigen::AngleAxis<double>(offset[3], Eigen::Vector3d::UnitX()) *
442  Eigen::AngleAxis<double>(offset[4], Eigen::Vector3d::UnitY()) *
443  Eigen::AngleAxis<double>(offset[5], Eigen::Vector3d::UnitZ()));
444  offset_tf.translation() = Eigen::Vector3d(offset[0], offset[1], offset[2]);
445 
446  geometry_msgs::Pose constraint_pose_msg;
447  constraint_pose_msg.position = constraints.position_constraints[0].constraint_region.primitive_poses[0].position;
448  constraint_pose_msg.orientation = constraints.orientation_constraints[0].orientation;
449  Eigen::Affine3d constraint_pose;
450  tf::poseMsgToEigen(constraint_pose_msg, constraint_pose);
451 
452  Eigen::Affine3d new_pose = constraint_pose * offset_tf;
453  geometry_msgs::Pose new_pose_msg;
454  tf::poseEigenToMsg(new_pose, new_pose_msg);
455 
456  constraints.position_constraints[0].constraint_region.primitive_poses[0].position = new_pose_msg.position;
457  constraints.orientation_constraints[0].orientation = new_pose_msg.orientation;
458 }
459 
461  const std::vector<StartState>& start_states,
462  const std::vector<PathConstraints>& path_constraints,
463  std::vector<BenchmarkRequest>& requests)
464 {
465  // Use default start state
466  if (start_states.empty())
467  {
468  // Adding path constraints
469  for (std::size_t k = 0; k < path_constraints.size(); ++k)
470  {
471  BenchmarkRequest new_brequest = brequest;
472  new_brequest.request.path_constraints = path_constraints[k].constraints[0];
473  new_brequest.name = brequest.name + "_" + path_constraints[k].name;
474  requests.push_back(new_brequest);
475  }
476 
477  if (path_constraints.empty())
478  requests.push_back(brequest);
479  }
480  else // Create a request for each start state specified
481  {
482  for (std::size_t j = 0; j < start_states.size(); ++j)
483  {
484  BenchmarkRequest new_brequest = brequest;
485  new_brequest.request.start_state = start_states[j].state;
486 
487  // Duplicate the request for each of the path constraints
488  for (std::size_t k = 0; k < path_constraints.size(); ++k)
489  {
490  new_brequest.request.path_constraints = path_constraints[k].constraints[0];
491  new_brequest.name = start_states[j].name + "_" + new_brequest.name + "_" + path_constraints[k].name;
492  requests.push_back(new_brequest);
493  }
494 
495  if (path_constraints.empty())
496  {
497  new_brequest.name = start_states[j].name + "_" + brequest.name;
498  requests.push_back(new_brequest);
499  }
500  }
501  }
502 }
503 
504 bool BenchmarkExecutor::plannerConfigurationsExist(const std::map<std::string, std::vector<std::string>>& planners,
505  const std::string& group_name)
506 {
507  // Make sure planner plugins exist
508  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
509  ++it)
510  {
511  bool plugin_exists = false;
512  for (std::map<std::string, planning_interface::PlannerManagerPtr>::const_iterator planner_it =
513  planner_interfaces_.begin();
514  planner_it != planner_interfaces_.end() && !plugin_exists; ++planner_it)
515  {
516  plugin_exists = planner_it->first == it->first;
517  }
518 
519  if (!plugin_exists)
520  {
521  ROS_ERROR("Planning plugin '%s' does NOT exist", it->first.c_str());
522  return false;
523  }
524  }
525 
526  // Make sure planning algorithms exist within those plugins
527  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
528  ++it)
529  {
530  planning_interface::PlannerManagerPtr pm = planner_interfaces_[it->first];
531  const planning_interface::PlannerConfigurationMap& config_map = pm->getPlannerConfigurations();
532 
533  // if the planner is chomp or stomp skip this function and return true for checking planner configurations for the
534  // planning group otherwise an error occurs, because for OMPL a specific planning algorithm needs to be defined for
535  // a planning group, whereas with STOMP and CHOMP this is not necessary
536  if (pm->getDescription().compare("stomp") || pm->getDescription().compare("chomp"))
537  continue;
538 
539  for (std::size_t i = 0; i < it->second.size(); ++i)
540  {
541  bool planner_exists = false;
542  for (planning_interface::PlannerConfigurationMap::const_iterator map_it = config_map.begin();
543  map_it != config_map.end() && !planner_exists; ++map_it)
544  {
545  std::string planner_name = group_name + "[" + it->second[i] + "]";
546  planner_exists = (map_it->second.group == group_name && map_it->second.name == planner_name);
547  }
548 
549  if (!planner_exists)
550  {
551  ROS_ERROR("Planner '%s' does NOT exist for group '%s' in pipeline '%s'", it->second[i].c_str(),
552  group_name.c_str(), it->first.c_str());
553  std::cout << "There are " << config_map.size() << " planner entries: " << std::endl;
554  for (planning_interface::PlannerConfigurationMap::const_iterator map_it = config_map.begin();
555  map_it != config_map.end() && !planner_exists; ++map_it)
556  std::cout << map_it->second.name << std::endl;
557  return false;
558  }
559  }
560  }
561 
562  return true;
563 }
564 
565 bool BenchmarkExecutor::loadPlanningScene(const std::string& scene_name, moveit_msgs::PlanningScene& scene_msg)
566 {
567  bool ok = false;
568  try
569  {
570  if (pss_->hasPlanningScene(scene_name)) // whole planning scene
571  {
573  ok = pss_->getPlanningScene(pswm, scene_name);
574  scene_msg = static_cast<moveit_msgs::PlanningScene>(*pswm);
575 
576  if (!ok)
577  ROS_ERROR("Failed to load planning scene '%s'", scene_name.c_str());
578  }
579  else if (psws_->hasPlanningSceneWorld(scene_name)) // Just the world (no robot)
580  {
582  ok = psws_->getPlanningSceneWorld(pswwm, scene_name);
583  scene_msg.world = static_cast<moveit_msgs::PlanningSceneWorld>(*pswwm);
584  scene_msg.robot_model_name =
585  "NO ROBOT INFORMATION. ONLY WORLD GEOMETRY"; // this will be fixed when running benchmark
586 
587  if (!ok)
588  ROS_ERROR("Failed to load planning scene '%s'", scene_name.c_str());
589  }
590  else
591  ROS_ERROR("Failed to find planning scene '%s'", scene_name.c_str());
592  }
593  catch (std::exception& ex)
594  {
595  ROS_ERROR("Error loading planning scene: %s", ex.what());
596  }
597  ROS_INFO("Loaded planning scene successfully");
598  return ok;
599 }
600 
601 bool BenchmarkExecutor::loadQueries(const std::string& regex, const std::string& scene_name,
602  std::vector<BenchmarkRequest>& queries)
603 {
604  if (regex.empty())
605  return true;
606 
607  std::vector<std::string> query_names;
608  try
609  {
610  pss_->getPlanningQueriesNames(regex, query_names, scene_name);
611  }
612  catch (std::exception& ex)
613  {
614  ROS_ERROR("Error loading motion planning queries: %s", ex.what());
615  return false;
616  }
617 
618  if (query_names.empty())
619  {
620  ROS_ERROR("Scene '%s' has no associated queries", scene_name.c_str());
621  return false;
622  }
623 
624  for (std::size_t i = 0; i < query_names.size(); ++i)
625  {
627  try
628  {
629  pss_->getPlanningQuery(planning_query, scene_name, query_names[i]);
630  }
631  catch (std::exception& ex)
632  {
633  ROS_ERROR("Error loading motion planning query '%s': %s", query_names[i].c_str(), ex.what());
634  continue;
635  }
636 
637  BenchmarkRequest query;
638  query.name = query_names[i];
639  query.request = static_cast<moveit_msgs::MotionPlanRequest>(*planning_query);
640  queries.push_back(query);
641  }
642  ROS_INFO("Loaded queries successfully");
643  return true;
644 }
645 
646 bool BenchmarkExecutor::loadStates(const std::string& regex, std::vector<StartState>& start_states)
647 {
648  if (regex.size())
649  {
650  boost::regex start_regex(regex);
651  std::vector<std::string> state_names;
652  rs_->getKnownRobotStates(state_names);
653  for (std::size_t i = 0; i < state_names.size(); ++i)
654  {
655  boost::cmatch match;
656  if (boost::regex_match(state_names[i].c_str(), match, start_regex))
657  {
659  try
660  {
661  if (rs_->getRobotState(robot_state, state_names[i]))
662  {
663  StartState start_state;
664  start_state.state = moveit_msgs::RobotState(*robot_state);
665  start_state.name = state_names[i];
666  start_states.push_back(start_state);
667  }
668  }
669  catch (std::exception& ex)
670  {
671  ROS_ERROR("Runtime error when loading state '%s': %s", state_names[i].c_str(), ex.what());
672  continue;
673  }
674  }
675  }
676 
677  if (start_states.empty())
678  ROS_WARN("No stored states matched the provided start state regex: '%s'", regex.c_str());
679  }
680  ROS_INFO("Loaded states successfully");
681  return true;
682 }
683 
684 bool BenchmarkExecutor::loadPathConstraints(const std::string& regex, std::vector<PathConstraints>& constraints)
685 {
686  if (regex.size())
687  {
688  std::vector<std::string> cnames;
689  cs_->getKnownConstraints(regex, cnames);
690 
691  for (std::size_t i = 0; i < cnames.size(); ++i)
692  {
694  try
695  {
696  if (cs_->getConstraints(constr, cnames[i]))
697  {
698  PathConstraints constraint;
699  constraint.constraints.push_back(*constr);
700  constraint.name = cnames[i];
701  constraints.push_back(constraint);
702  }
703  }
704  catch (std::exception& ex)
705  {
706  ROS_ERROR("Runtime error when loading path constraint '%s': %s", cnames[i].c_str(), ex.what());
707  continue;
708  }
709  }
710 
711  if (constraints.empty())
712  ROS_WARN("No path constraints found that match regex: '%s'", regex.c_str());
713  else
714  ROS_INFO("Loaded path constraints successfully");
715  }
716  return true;
717 }
718 
719 bool BenchmarkExecutor::loadTrajectoryConstraints(const std::string& regex,
720  std::vector<TrajectoryConstraints>& constraints)
721 {
722  if (regex.size())
723  {
724  std::vector<std::string> cnames;
725  tcs_->getKnownTrajectoryConstraints(regex, cnames);
726 
727  for (std::size_t i = 0; i < cnames.size(); ++i)
728  {
730  try
731  {
732  if (tcs_->getTrajectoryConstraints(constr, cnames[i]))
733  {
734  TrajectoryConstraints constraint;
735  constraint.constraints = *constr;
736  constraint.name = cnames[i];
737  constraints.push_back(constraint);
738  }
739  }
740  catch (std::exception& ex)
741  {
742  ROS_ERROR("Runtime error when loading trajectory constraint '%s': %s", cnames[i].c_str(), ex.what());
743  continue;
744  }
745  }
746 
747  if (constraints.empty())
748  ROS_WARN("No trajectory constraints found that match regex: '%s'", regex.c_str());
749  else
750  ROS_INFO("Loaded trajectory constraints successfully");
751  }
752  return true;
753 }
754 
755 void BenchmarkExecutor::runBenchmark(moveit_msgs::MotionPlanRequest request,
756  const std::map<std::string, std::vector<std::string>>& planners, int runs)
757 {
758  benchmark_data_.clear();
759 
760  unsigned int num_planners = 0;
761  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
762  ++it)
763  num_planners += it->second.size();
764 
765  boost::progress_display progress(num_planners * runs, std::cout);
766 
767  // Iterate through all planner plugins
768  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
769  ++it)
770  {
771  // Iterate through all planners associated with the plugin
772  for (std::size_t i = 0; i < it->second.size(); ++i)
773  {
774  // This container stores all of the benchmark data for this planner
775  PlannerBenchmarkData planner_data(runs);
776 
777  request.planner_id = it->second[i];
778 
779  // Planner start events
780  for (std::size_t j = 0; j < planner_start_fns_.size(); ++j)
781  planner_start_fns_[j](request, planner_data);
782 
783  planning_interface::PlanningContextPtr context =
784  planner_interfaces_[it->first]->getPlanningContext(planning_scene_, request);
785  for (int j = 0; j < runs; ++j)
786  {
787  // Pre-run events
788  for (std::size_t k = 0; k < pre_event_fns_.size(); ++k)
789  pre_event_fns_[k](request);
790 
791  // Solve problem
794  bool solved = context->solve(mp_res);
795  double total_time = (ros::WallTime::now() - start).toSec();
796 
797  // Collect data
798  start = ros::WallTime::now();
799 
800  // Post-run events
801  for (std::size_t k = 0; k < post_event_fns_.size(); ++k)
802  post_event_fns_[k](request, mp_res, planner_data[j]);
803  collectMetrics(planner_data[j], mp_res, solved, total_time);
804  double metrics_time = (ros::WallTime::now() - start).toSec();
805  ROS_DEBUG("Spent %lf seconds collecting metrics", metrics_time);
806 
807  ++progress;
808  }
809 
810  // Planner completion events
811  for (std::size_t j = 0; j < planner_completion_fns_.size(); ++j)
812  planner_completion_fns_[j](request, planner_data);
813 
814  benchmark_data_.push_back(planner_data);
815  }
816  }
817 }
818 
820  const planning_interface::MotionPlanDetailedResponse& mp_res, bool solved,
821  double total_time)
822 {
823  metrics["time REAL"] = moveit::core::toString(total_time);
824  metrics["solved BOOLEAN"] = boost::lexical_cast<std::string>(solved);
825 
826  if (solved)
827  {
828  // Analyzing the trajectory(ies) geometrically
829  double L = 0.0; // trajectory length
830  double clearance = 0.0; // trajectory clearance (average)
831  double smoothness = 0.0; // trajectory smoothness (average)
832  bool correct = true; // entire trajectory collision free and in bounds
833 
834  double process_time = total_time;
835  for (std::size_t j = 0; j < mp_res.trajectory_.size(); ++j)
836  {
837  correct = true;
838  L = 0.0;
839  clearance = 0.0;
840  smoothness = 0.0;
841  const robot_trajectory::RobotTrajectory& p = *mp_res.trajectory_[j];
842 
843  // compute path length
844  for (std::size_t k = 1; k < p.getWayPointCount(); ++k)
845  L += p.getWayPoint(k - 1).distance(p.getWayPoint(k));
846 
847  // compute correctness and clearance
849  for (std::size_t k = 0; k < p.getWayPointCount(); ++k)
850  {
852  planning_scene_->checkCollisionUnpadded(req, res, p.getWayPoint(k));
853  if (res.collision)
854  correct = false;
855  if (!p.getWayPoint(k).satisfiesBounds())
856  correct = false;
857  double d = planning_scene_->distanceToCollisionUnpadded(p.getWayPoint(k));
858  if (d > 0.0) // in case of collision, distance is negative
859  clearance += d;
860  }
861  clearance /= (double)p.getWayPointCount();
862 
863  // compute smoothness
864  if (p.getWayPointCount() > 2)
865  {
866  double a = p.getWayPoint(0).distance(p.getWayPoint(1));
867  for (std::size_t k = 2; k < p.getWayPointCount(); ++k)
868  {
869  // view the path as a sequence of segments, and look at the triangles it forms:
870  // s1
871  // /\ s4
872  // a / \ b |
873  // / \ |
874  // /......\_______|
875  // s0 c s2 s3
876  //
877 
878  // use Pythagoras generalized theorem to find the cos of the angle between segments a and b
879  double b = p.getWayPoint(k - 1).distance(p.getWayPoint(k));
880  double cdist = p.getWayPoint(k - 2).distance(p.getWayPoint(k));
881  double acosValue = (a * a + b * b - cdist * cdist) / (2.0 * a * b);
882  if (acosValue > -1.0 && acosValue < 1.0)
883  {
884  // the smoothness is actually the outside angle of the one we compute
885  double angle = (boost::math::constants::pi<double>() - acos(acosValue));
886 
887  // and we normalize by the length of the segments
888  double u = 2.0 * angle;
889  smoothness += u * u;
890  }
891  a = b;
892  }
893  smoothness /= (double)p.getWayPointCount();
894  }
895  metrics["path_" + mp_res.description_[j] + "_correct BOOLEAN"] = boost::lexical_cast<std::string>(correct);
896  metrics["path_" + mp_res.description_[j] + "_length REAL"] = moveit::core::toString(L);
897  metrics["path_" + mp_res.description_[j] + "_clearance REAL"] = moveit::core::toString(clearance);
898  metrics["path_" + mp_res.description_[j] + "_smoothness REAL"] = moveit::core::toString(smoothness);
899  metrics["path_" + mp_res.description_[j] + "_time REAL"] = moveit::core::toString(mp_res.processing_time_[j]);
900  process_time -= mp_res.processing_time_[j];
901  }
902  if (process_time <= 0.0)
903  process_time = 0.0;
904  metrics["process_time REAL"] = moveit::core::toString(process_time);
905  }
906 }
907 
908 void BenchmarkExecutor::writeOutput(const BenchmarkRequest& brequest, const std::string& start_time,
909  double benchmark_duration)
910 {
911  const std::map<std::string, std::vector<std::string>>& planners = options_.getPlannerConfigurations();
912 
913  size_t num_planners = 0;
914  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
915  ++it)
916  num_planners += it->second.size();
917 
918  std::string hostname = getHostname();
919  if (hostname.empty())
920  hostname = "UNKNOWN";
921 
922  std::string filename = options_.getOutputDirectory();
923  if (filename.size() && filename[filename.size() - 1] != '/')
924  filename.append("/");
925 
926  // Ensure directories exist
927  boost::filesystem::create_directories(filename);
928 
929  filename += (options_.getBenchmarkName().empty() ? "" : options_.getBenchmarkName() + "_") + brequest.name + "_" +
930  getHostname() + "_" + start_time + ".log";
931  std::ofstream out(filename.c_str());
932  if (!out)
933  {
934  ROS_ERROR("Failed to open '%s' for benchmark output", filename.c_str());
935  return;
936  }
937 
938  out << "MoveIt! version " << MOVEIT_VERSION << std::endl;
939  out << "Experiment " << brequest.name << std::endl;
940  out << "Running on " << hostname << std::endl;
941  out << "Starting at " << start_time << std::endl;
942 
943  // Experiment setup
944  moveit_msgs::PlanningScene scene_msg;
945  planning_scene_->getPlanningSceneMsg(scene_msg);
946  out << "<<<|" << std::endl;
947  out << "Motion plan request:" << std::endl << brequest.request << std::endl;
948  out << "Planning scene: " << std::endl << scene_msg << std::endl << "|>>>" << std::endl;
949 
950  // Not writing optional cpu information
951 
952  // The real random seed is unknown. Writing a fake value
953  out << "0 is the random seed" << std::endl;
954  out << brequest.request.allowed_planning_time << " seconds per run" << std::endl;
955  // There is no memory cap
956  out << "-1 MB per run" << std::endl;
957  out << options_.getNumRuns() << " runs per planner" << std::endl;
958  out << benchmark_duration << " seconds spent to collect the data" << std::endl;
959 
960  // No enum types
961  out << "0 enum types" << std::endl;
962 
963  out << num_planners << " planners" << std::endl;
964 
965  size_t run_id = 0;
966  for (std::map<std::string, std::vector<std::string>>::const_iterator it = planners.begin(); it != planners.end();
967  ++it)
968  {
969  for (std::size_t i = 0; i < it->second.size(); ++i, ++run_id)
970  {
971  // Write the name of the planner.
972  out << it->second[i] << std::endl;
973 
974  // in general, we could have properties specific for a planner;
975  // right now, we do not include such properties
976  out << "0 common properties" << std::endl;
977 
978  // Create a list of the benchmark properties for this planner
979  std::set<std::string> properties_set;
980  for (std::size_t j = 0; j < benchmark_data_[run_id].size(); ++j) // each run of this planner
981  for (PlannerRunData::const_iterator pit = benchmark_data_[run_id][j].begin();
982  pit != benchmark_data_[run_id][j].end(); ++pit) // each benchmark property of the given run
983  properties_set.insert(pit->first);
984 
985  // Writing property list
986  out << properties_set.size() << " properties for each run" << std::endl;
987  for (std::set<std::string>::const_iterator pit = properties_set.begin(); pit != properties_set.end(); ++pit)
988  out << *pit << std::endl;
989 
990  // Number of runs
991  out << benchmark_data_[run_id].size() << " runs" << std::endl;
992 
993  // And the benchmark properties
994  for (std::size_t j = 0; j < benchmark_data_[run_id].size(); ++j) // each run of this planner
995  {
996  // Write out properties in the order we listed them above
997  for (std::set<std::string>::const_iterator pit = properties_set.begin(); pit != properties_set.end(); ++pit)
998  {
999  // Make sure this run has this property
1000  PlannerRunData::const_iterator runit = benchmark_data_[run_id][j].find(*pit);
1001  if (runit != benchmark_data_[run_id][j].end())
1002  out << runit->second;
1003  out << "; ";
1004  }
1005  out << std::endl; // end of the run
1006  }
1007  out << "." << std::endl; // end the planner
1008  }
1009  }
1010 
1011  out.close();
1012  ROS_INFO("Benchmark results saved to '%s'", filename.c_str());
1013 }
d
static std::string getHostname()
BenchmarkExecutor(const std::string &robot_description_param="robot_description")
void addPostRunEvent(PostRunEventFunction func)
filename
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...
const std::string & getSceneName() const
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.
const std::string & getOutputDirectory() const
DatabaseConnection::Ptr loadDatabase()
void poseEigenToMsg(const Eigen::Affine3d &e, geometry_msgs::Pose &m)
bool hasPlanningScene(const std::string &name) const
moveit_warehouse::PlanningSceneStorage * pss_
void poseMsgToEigen(const geometry_msgs::Pose &m, Eigen::Affine3d &e)
const std::string & getGroupName() const
bool loadStates(const std::string &regex, std::vector< StartState > &start_states)
Load all states matching the given regular expression from the warehouse.
const std::string & getPathConstraintRegex() const
const std::string & getHostName() const
planning_scene::PlanningScenePtr planning_scene_
warehouse_ros::DatabaseLoader dbloader
virtual void writeOutput(const BenchmarkRequest &brequest, const std::string &start_time, double benchmark_duration)
bool getPlanningSceneWorld(PlanningSceneWorldWithMetadata &msg_m, const std::string &name) const
void getKnownRobotStates(std::vector< std::string > &names, const std::string &robot="") const
moveit_warehouse::TrajectoryConstraintsStorage * tcs_
void addPreRunEvent(PreRunEventFunction func)
std::map< std::string, PlannerConfigurationSettings > PlannerConfigurationMap
std::vector< PreRunEventFunction > pre_event_fns_
virtual void collectMetrics(PlannerRunData &metrics, const planning_interface::MotionPlanDetailedResponse &mp_res, bool solved, double total_time)
boost::function< void(const moveit_msgs::MotionPlanRequest &request, PlannerBenchmarkData &benchmark_data)> PlannerCompletionEventFunction
const std::string & getGoalConstraintRegex() const
std::vector< PlannerStartEventFunction > planner_start_fns_
void addQueryCompletionEvent(QueryCompletionEventFunction func)
virtual bool runBenchmarks(const BenchmarkOptions &opts)
std::shared_ptr< pluginlib::ClassLoader< planning_interface::PlannerManager > > planner_plugin_loader_
#define ROS_WARN(...)
moveit_warehouse::ConstraintsStorage * cs_
void runBenchmark(moveit_msgs::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...
const planning_scene::PlanningScenePtr & getPlanningScene()
boost::function< void(const moveit_msgs::MotionPlanRequest &request, planning_scene::PlanningScenePtr)> QueryCompletionEventFunction
Definition of a query-end benchmark event function. Invoked after a query has finished benchmarking...
std::vector< PlannerCompletionEventFunction > planner_completion_fns_
const std::string & getStartStateRegex() const
void getGoalOffsets(std::vector< double > &offsets) const
bool hasPlanningSceneWorld(const std::string &name) const
bool loadPathConstraints(const std::string &regex, std::vector< PathConstraints > &constraints)
Load all constraints matching the given regular expression from the warehouse.
#define ROS_FATAL_STREAM(args)
std::map< std::string, std::string > PlannerRunData
Structure to hold information for a single run of a planner.
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
#define ROS_INFO(...)
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.
planning_scene_monitor::PlanningSceneMonitor * psm_
std::vector< QueryCompletionEventFunction > query_end_fns_
void addPlannerCompletionEvent(PlannerCompletionEventFunction func)
bool loadTrajectoryConstraints(const std::string &regex, std::vector< TrajectoryConstraints > &constraints)
Load all trajectory constraints from the warehouse that match the given regular expression.
boost::function< void(moveit_msgs::MotionPlanRequest &request)> PreRunEventFunction
Definition of a pre-run benchmark event function. Invoked immediately before each planner calls solve...
bool loadPlanningScene(const std::string &scene_name, moveit_msgs::PlanningScene &scene_msg)
Load the planning scene with the given name from the warehouse.
std::vector< QueryStartEventFunction > query_start_fns_
void getKnownConstraints(std::vector< std::string > &names, const std::string &robot="", const std::string &group="") const
std::vector< moveit_msgs::Constraints > constraints
std::map< std::string, planning_interface::PlannerManagerPtr > planner_interfaces_
std::string toString(double d)
bool getPlanningQuery(MotionPlanRequestWithMetadata &query_m, const std::string &scene_name, const std::string &query_name)
bool getConstraints(ConstraintsWithMetadata &msg_m, const std::string &name, const std::string &robot="", const std::string &group="") const
moveit_warehouse::PlanningSceneWorldStorage * psws_
const robot_state::RobotState & getWayPoint(std::size_t index) const
const std::string & getTrajectoryConstraintRegex() const
void initialize(const std::vector< std::string > &plugin_classes)
std::size_t getWayPointCount() const
bool getPlanningScene(PlanningSceneWithMetadata &scene_m, const std::string &scene_name) const
moveit_warehouse::RobotStateStorage * rs_
bool getRobotState(RobotStateWithMetadata &msg_m, const std::string &name, const std::string &robot="") const
INLINE Rall1d< T, V, S > acos(const Rall1d< T, V, S > &x)
static WallTime now()
std::vector< robot_trajectory::RobotTrajectoryPtr > trajectory_
void getPlanningQueriesNames(std::vector< std::string > &query_names, const std::string &scene_name) const
void addPlannerStartEvent(PlannerStartEventFunction func)
const std::string & getQueryRegex() const
boost::function< void(const moveit_msgs::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...
std::vector< PlannerRunData > PlannerBenchmarkData
Structure to hold information for a single planner&#39;s benchmark data.
const std::string & getBenchmarkName() const
std::vector< PlannerBenchmarkData > benchmark_data_
boost::function< void(const moveit_msgs::MotionPlanRequest &request, PlannerBenchmarkData &benchmark_data)> PlannerStartEventFunction
virtual bool initializeBenchmarks(const BenchmarkOptions &opts, moveit_msgs::PlanningScene &scene_msg, std::vector< BenchmarkRequest > &queries)
const moveit_msgs::WorkspaceParameters & getWorkspaceParameters() const
#define ROS_ERROR_STREAM(args)
void addQueryStartEvent(QueryStartEventFunction func)
void shiftConstraintsByOffset(moveit_msgs::Constraints &constraints, const std::vector< double > offset)
#define ROS_ERROR(...)
const std::map< std::string, std::vector< std::string > > & getPlannerConfigurations() const
boost::function< void(const moveit_msgs::MotionPlanRequest &request, planning_scene::PlanningScenePtr)> QueryStartEventFunction
Definition of a query-start benchmark event function. Invoked before a new query is benchmarked...
std::vector< PostRunEventFunction > post_event_fns_
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.
#define ROS_DEBUG(...)


benchmarks
Author(s): Ryan Luna
autogenerated on Wed Jul 10 2019 04:04:08