42 #include <moveit/version.h>
43 #include <tf2_eigen/tf2_eigen.hpp>
49 #define BOOST_ALLOW_DEPRECATED_HEADERS
50 #include <boost/regex.hpp>
51 #include <boost/progress.hpp>
52 #undef BOOST_ALLOW_DEPRECATED_HEADERS
53 #include <boost/date_time/posix_time/posix_time.hpp>
68 template <
class Clock,
class Duration>
69 boost::posix_time::ptime
toBoost(
const std::chrono::time_point<Clock, Duration>& from)
71 typedef std::chrono::nanoseconds duration_t;
73 rep_t
d = std::chrono::duration_cast<duration_t>(from.time_since_epoch()).count();
74 rep_t sec =
d / 1000000000;
75 rep_t nsec =
d % 1000000000;
76 namespace pt = boost::posix_time;
77 #ifdef BOOST_DATE_TIME_HAS_NANOSECONDS
78 return pt::from_time_t(sec) + pt::nanoseconds(nsec)
80 return pt::from_time_t(sec) + pt::microseconds(nsec / 1000);
86 robot_descriptionparam) }
87 , planning_scene_storage_{ nullptr }
88 , planning_scene_world_storage_{ nullptr }
89 , robot_state_storage_{ nullptr }
90 , constraints_storage_{ nullptr }
91 , trajectory_constraints_storage_{ nullptr }
107 for (
const std::string& planning_pipeline_name : planning_pipeline_names)
109 if (
moveit_cpp_->getPlanningPipelines().find(planning_pipeline_name) ==
moveit_cpp_->getPlanningPipelines().end())
111 RCLCPP_ERROR(
getLogger(),
"Cannot find pipeline '%s'", planning_pipeline_name.c_str());
115 const auto& pipeline =
moveit_cpp_->getPlanningPipelines().at(planning_pipeline_name);
117 if (!pipeline->getPlannerManager())
119 RCLCPP_ERROR(
getLogger(),
"Failed to initialize planning pipeline '%s'", planning_pipeline_name.c_str());
127 RCLCPP_ERROR(
getLogger(),
"No planning pipelines have been loaded. Nothing to do for the benchmarking service.");
131 RCLCPP_INFO(
getLogger(),
"Available planning pipelines:");
132 for (
const std::pair<const std::string, planning_pipeline::PlanningPipelinePtr>& entry :
136 "Pipeline: " << entry.first <<
", Planner: " << entry.second->getPlannerPluginName());
208 RCLCPP_ERROR(
getLogger(),
"No planning pipelines configured. Did you call BenchmarkExecutor::initialize?");
212 std::vector<BenchmarkRequest> queries;
213 moveit_msgs::msg::PlanningScene scene_msg;
217 for (std::size_t i = 0; i < queries.size(); ++i)
220 if (scene_msg.robot_model_name !=
planning_scene_->getRobotModel()->getName())
240 RCLCPP_INFO(
getLogger(),
"Benchmarking query '%s' (%lu of %lu)", queries[i].
name.c_str(), i + 1, queries.size());
241 std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
243 std::chrono::duration<double> dt = std::chrono::system_clock::now() - start_time;
244 double duration = dt.count();
260 moveit_msgs::msg::PlanningScene& scene_msg,
261 std::vector<BenchmarkRequest>& requests)
268 std::vector<StartState> start_states;
269 std::vector<PathConstraints> path_constraints;
270 std::vector<PathConstraints> goal_constraints;
271 std::vector<TrajectoryConstraints> traj_constraints;
272 std::vector<BenchmarkRequest> queries;
277 RCLCPP_ERROR(
getLogger(),
"Failed to load benchmark query data");
283 "Benchmark loaded %lu starts, %lu goals, %lu path constraints, %lu trajectory constraints, and %lu queries",
284 start_states.size(), goal_constraints.size(), path_constraints.size(), traj_constraints.size(), queries.size());
286 moveit_msgs::msg::WorkspaceParameters workspace_parameters =
options.workspace;
288 if (workspace_parameters.min_corner.x == workspace_parameters.max_corner.x &&
289 workspace_parameters.min_corner.x == 0.0 &&
290 workspace_parameters.min_corner.y == workspace_parameters.max_corner.y &&
291 workspace_parameters.min_corner.y == 0.0 &&
292 workspace_parameters.min_corner.z == workspace_parameters.max_corner.z &&
293 workspace_parameters.min_corner.z == 0.0)
295 workspace_parameters.min_corner.x = workspace_parameters.min_corner.y = workspace_parameters.min_corner.z = -5.0;
297 workspace_parameters.max_corner.x = workspace_parameters.max_corner.y = workspace_parameters.max_corner.z = 5.0;
308 benchmark_request.
name = goal_constraint.name;
309 benchmark_request.
request.workspace_parameters = workspace_parameters;
310 benchmark_request.
request.goal_constraints = goal_constraint.constraints;
312 benchmark_request.
request.allowed_planning_time =
options.timeout;
313 benchmark_request.
request.num_planning_attempts = 1;
315 if (benchmark_request.
request.goal_constraints.size() == 1 &&
316 benchmark_request.
request.goal_constraints.at(0).position_constraints.size() == 1 &&
317 benchmark_request.
request.goal_constraints.at(0).orientation_constraints.size() == 1 &&
318 benchmark_request.
request.goal_constraints.at(0).visibility_constraints.empty() &&
319 benchmark_request.
request.goal_constraints.at(0).joint_constraints.empty())
324 std::vector<BenchmarkRequest> request_combos;
326 requests.insert(requests.end(), request_combos.begin(), request_combos.end());
335 benchmark_request.
name = query.name;
336 benchmark_request.
request = query.request;
338 benchmark_request.
request.allowed_planning_time =
options.timeout;
339 benchmark_request.
request.num_planning_attempts = 1;
342 if (benchmark_request.
request.workspace_parameters.min_corner.x ==
343 benchmark_request.
request.workspace_parameters.max_corner.x &&
344 benchmark_request.
request.workspace_parameters.min_corner.x == 0.0 &&
345 benchmark_request.
request.workspace_parameters.min_corner.y ==
346 benchmark_request.
request.workspace_parameters.max_corner.y &&
347 benchmark_request.
request.workspace_parameters.min_corner.y == 0.0 &&
348 benchmark_request.
request.workspace_parameters.min_corner.z ==
349 benchmark_request.
request.workspace_parameters.max_corner.z &&
350 benchmark_request.
request.workspace_parameters.min_corner.z == 0.0)
353 benchmark_request.
request.workspace_parameters = workspace_parameters;
357 std::vector<BenchmarkRequest> request_combos;
359 requests.insert(requests.end(), request_combos.begin(), request_combos.end());
367 benchmark_request.
name = traj_constraint.name;
368 benchmark_request.
request.trajectory_constraints = traj_constraint.constraints;
370 benchmark_request.
request.allowed_planning_time =
options.timeout;
371 benchmark_request.
request.num_planning_attempts = 1;
373 if (benchmark_request.
request.trajectory_constraints.constraints.size() == 1 &&
374 benchmark_request.
request.trajectory_constraints.constraints.at(0).position_constraints.size() == 1 &&
375 benchmark_request.
request.trajectory_constraints.constraints.at(0).orientation_constraints.size() == 1 &&
376 benchmark_request.
request.trajectory_constraints.constraints.at(0).visibility_constraints.empty() &&
377 benchmark_request.
request.trajectory_constraints.constraints.at(0).joint_constraints.empty())
382 std::vector<BenchmarkRequest> request_combos;
383 std::vector<PathConstraints> no_path_constraints;
385 requests.insert(requests.end(), request_combos.begin(), request_combos.end());
391 const BenchmarkOptions&
options, moveit_msgs::msg::PlanningScene& scene_msg, std::vector<StartState>& start_states,
392 std::vector<PathConstraints>& path_constraints, std::vector<PathConstraints>& goal_constraints,
393 std::vector<TrajectoryConstraints>& traj_constraints, std::vector<BenchmarkRequest>& queries)
397 warehouse_ros::DatabaseConnection::Ptr warehouse_connection =
db_loader_.loadDatabase();
398 warehouse_connection->setParams(
options.hostname,
options.port, 20);
399 if (warehouse_connection->connect())
403 std::make_shared<moveit_warehouse::PlanningSceneWorldStorage>(warehouse_connection);
404 robot_state_storage_ = std::make_shared<moveit_warehouse::RobotStateStorage>(warehouse_connection);
405 constraints_storage_ = std::make_shared<moveit_warehouse::ConstraintsStorage>(warehouse_connection);
407 std::make_shared<moveit_warehouse::TrajectoryConstraintsStorage>(warehouse_connection);
408 RCLCPP_INFO(
getLogger(),
"Connected to DB");
412 RCLCPP_ERROR(
getLogger(),
"Failed to connect to DB");
416 catch (std::exception& e)
418 RCLCPP_ERROR(
getLogger(),
"Failed to initialize benchmark server: '%s'", e.what());
424 RCLCPP_ERROR(
getLogger(),
"Failed to load the planning scene");
429 RCLCPP_ERROR(
getLogger(),
"Failed to load the states");
434 RCLCPP_ERROR(
getLogger(),
"Failed to load the goal constraints");
438 RCLCPP_ERROR(
getLogger(),
"Failed to load the path constraints");
442 RCLCPP_ERROR(
getLogger(),
"Failed to load the trajectory constraints");
446 RCLCPP_ERROR(
getLogger(),
"Failed to get a query regex");
452 const std::vector<double>& offset)
454 Eigen::Isometry3d offset_tf(Eigen::AngleAxis<double>(offset.at(3), Eigen::Vector3d::UnitX()) *
455 Eigen::AngleAxis<double>(offset.at(4), Eigen::Vector3d::UnitY()) *
456 Eigen::AngleAxis<double>(offset.at(5), Eigen::Vector3d::UnitZ()));
457 offset_tf.translation() =
Eigen::Vector3d(offset.at(0), offset.at(1), offset.at(2));
459 geometry_msgs::msg::Pose constraint_pose_msg;
460 constraint_pose_msg.position =
461 constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).position;
462 constraint_pose_msg.orientation = constraints.orientation_constraints.at(0).orientation;
463 Eigen::Isometry3d constraint_pose;
464 tf2::fromMsg(constraint_pose_msg, constraint_pose);
466 Eigen::Isometry3d new_pose = constraint_pose * offset_tf;
467 geometry_msgs::msg::Pose new_pose_msg;
468 new_pose_msg = tf2::toMsg(new_pose);
470 constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).position = new_pose_msg.position;
471 constraints.orientation_constraints.at(0).orientation = new_pose_msg.orientation;
475 const std::vector<StartState>& start_states,
476 const std::vector<PathConstraints>& path_constraints,
477 std::vector<BenchmarkRequest>& requests)
480 if (start_states.empty())
486 new_benchmark_request.
request.path_constraints = path_constraint.constraints.at(0);
487 new_benchmark_request.
name = benchmark_request.
name +
"_" + path_constraint.name;
488 requests.push_back(new_benchmark_request);
491 if (path_constraints.empty())
493 requests.push_back(benchmark_request);
498 for (
const StartState& start_state : start_states)
501 if (start_state.name == benchmark_request.
name)
505 new_benchmark_request.
request.start_state = start_state.state;
510 new_benchmark_request.
request.path_constraints = path_constraint.constraints.at(0);
511 new_benchmark_request.
name = start_state.name +
"_" + new_benchmark_request.
name +
"_" + path_constraint.name;
512 requests.push_back(new_benchmark_request);
515 if (path_constraints.empty())
517 new_benchmark_request.
name = start_state.name +
"_" + benchmark_request.
name;
518 requests.push_back(new_benchmark_request);
525 const std::map<std::string, std::vector<std::string>>& pipeline_configurations,
const std::string& group_name)
528 for (
const std::pair<
const std::string, std::vector<std::string>>& pipeline_config_entry : pipeline_configurations)
530 bool pipeline_exists =
false;
531 for (
const std::pair<const std::string, planning_pipeline::PlanningPipelinePtr>& pipeline_entry :
534 pipeline_exists = pipeline_entry.first == pipeline_config_entry.first;
539 if (!pipeline_exists)
541 RCLCPP_ERROR(
getLogger(),
"Planning pipeline '%s' does NOT exist", pipeline_config_entry.first.c_str());
547 auto planning_pipelines =
moveit_cpp_->getPlanningPipelines();
548 for (
const std::pair<
const std::string, std::vector<std::string>>& entry : pipeline_configurations)
550 planning_interface::PlannerManagerPtr pm = planning_pipelines[entry.first]->getPlannerManager();
556 if (pm->getDescription().compare(
"stomp") || pm->getDescription().compare(
"chomp"))
559 for (std::size_t i = 0; i < entry.second.size(); ++i)
561 bool planner_exists =
false;
562 for (
const std::pair<const std::string, planning_interface::PlannerConfigurationSettings>& config_entry :
565 std::string planner_name = group_name +
"[" + entry.second[i] +
"]";
566 planner_exists = (config_entry.second.group == group_name && config_entry.second.name == planner_name);
571 RCLCPP_ERROR(
getLogger(),
"Planner '%s' does NOT exist for group '%s' in pipeline '%s'",
572 entry.second[i].c_str(), group_name.c_str(), entry.first.c_str());
573 std::cout <<
"There are " << config_map.size() <<
" planner entries: " <<
'\n';
574 for (
const auto& config_map_entry : config_map)
575 std::cout << config_map_entry.second.name <<
'\n';
594 RCLCPP_ERROR(
getLogger(),
"Failed to load planning scene '%s'", scene_name.c_str());
597 scene_msg =
static_cast<moveit_msgs::msg::PlanningScene
>(*planning_scene_w_metadata);
604 RCLCPP_ERROR(
getLogger(),
"Failed to load planning scene world '%s'", scene_name.c_str());
607 scene_msg.world =
static_cast<moveit_msgs::msg::PlanningSceneWorld
>(*pswwm);
608 scene_msg.robot_model_name =
609 "NO ROBOT INFORMATION. ONLY WORLD GEOMETRY";
613 RCLCPP_ERROR(
getLogger(),
"Failed to find planning scene '%s'", scene_name.c_str());
617 catch (std::exception& ex)
619 RCLCPP_ERROR(
getLogger(),
"Error loading planning scene: %s", ex.what());
622 RCLCPP_INFO(
getLogger(),
"Loaded planning scene successfully");
627 std::vector<BenchmarkRequest>& queries)
631 RCLCPP_WARN(
getLogger(),
"No query regex provided, don't load any queries from the database");
635 std::vector<std::string> query_names;
640 catch (std::exception& ex)
642 RCLCPP_ERROR(
getLogger(),
"Error loading motion planning queries: %s", ex.what());
646 if (query_names.empty())
648 RCLCPP_ERROR(
getLogger(),
"Scene '%s' has no associated queries", scene_name.c_str());
652 for (
const std::string& query_name : query_names)
659 catch (std::exception& ex)
661 RCLCPP_ERROR(
getLogger(),
"Error loading motion planning query '%s': %s", query_name.c_str(), ex.what());
666 query.
name = query_name;
668 queries.push_back(query);
670 RCLCPP_INFO(
getLogger(),
"Loaded queries successfully");
678 std::regex start_regex(regex);
679 std::vector<std::string> state_names;
682 if (state_names.empty())
684 RCLCPP_WARN(
getLogger(),
"Database does not contain any named states");
687 for (
const std::string& state_name : state_names)
690 if (std::regex_match(state_name, match, start_regex))
698 start_state.
state = moveit_msgs::msg::RobotState(*robot_state);
699 start_state.
name = state_name;
700 start_states.push_back(start_state);
703 catch (std::exception& ex)
705 RCLCPP_ERROR(
getLogger(),
"Runtime error when loading state '%s': %s", state_name.c_str(), ex.what());
711 if (start_states.empty())
713 RCLCPP_WARN(
getLogger(),
"No stored states matched the provided start state regex: '%s'", regex.c_str());
716 RCLCPP_INFO(
getLogger(),
"Loaded states successfully");
724 std::vector<std::string> cnames;
727 for (
const std::string& cname : cnames)
736 constraint.
name = cname;
737 constraints.push_back(constraint);
740 catch (std::exception& ex)
742 RCLCPP_ERROR(
getLogger(),
"Runtime error when loading path constraint '%s': %s", cname.c_str(), ex.what());
747 if (constraints.empty())
749 RCLCPP_WARN(
getLogger(),
"No path constraints found that match regex: '%s'", regex.c_str());
753 RCLCPP_INFO(
getLogger(),
"Loaded path constraints successfully");
760 std::vector<TrajectoryConstraints>& constraints)
764 std::vector<std::string> cnames;
767 for (
const std::string& cname : cnames)
776 constraint.
name = cname;
777 constraints.push_back(constraint);
780 catch (std::exception& ex)
782 RCLCPP_ERROR(
getLogger(),
"Runtime error when loading trajectory constraint '%s': %s", cname.c_str(), ex.what());
787 if (constraints.empty())
789 RCLCPP_WARN(
getLogger(),
"No trajectory constraints found that match regex: '%s'", regex.c_str());
793 RCLCPP_INFO(
getLogger(),
"Loaded trajectory constraints successfully");
803 auto num_planners = 0;
804 for (
const std::pair<
const std::string, std::vector<std::string>>& pipeline_entry :
options.planning_pipelines)
806 num_planners += pipeline_entry.second.size();
808 num_planners +=
options.parallel_planning_pipelines.size();
810 boost::progress_display progress(num_planners *
options.runs, std::cout);
813 auto planning_pipelines =
moveit_cpp_->getPlanningPipelines();
814 for (
const std::pair<
const std::string, std::vector<std::string>>& pipeline_entry :
options.planning_pipelines)
817 for (
const std::string& planner_id : pipeline_entry.second)
822 std::vector<planning_interface::MotionPlanDetailedResponse> responses(
options.runs);
823 std::vector<bool> solved(
options.runs);
825 request.planner_id = planner_id;
830 planner_start_function(request, planner_data);
835 .planning_pipeline = pipeline_entry.first,
836 .planning_attempts = request.num_planning_attempts,
837 .planning_time = request.allowed_planning_time,
838 .max_velocity_scaling_factor = request.max_velocity_scaling_factor,
839 .max_acceleration_scaling_factor = request.max_acceleration_scaling_factor
843 for (
int j = 0; j <
options.runs; ++j)
847 pre_event_function(request);
850 auto planning_component = std::make_shared<moveit_cpp::PlanningComponent>(request.group_name,
moveit_cpp_);
854 planning_component->setStartState(start_state);
855 planning_component->setGoal(request.goal_constraints);
856 planning_component->setPathConstraints(request.path_constraints);
857 planning_component->setTrajectoryConstraints(request.trajectory_constraints);
860 std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
863 const auto response = planning_component->plan(plan_req_params,
planning_scene_);
865 solved[j] = bool(response.error_code);
867 responses[j].error_code = response.error_code;
868 if (response.trajectory)
870 responses[j].description.push_back(
"plan");
871 responses[j].trajectory.push_back(response.trajectory);
872 responses[j].processing_time.push_back(response.planning_time);
875 std::chrono::duration<double> dt = std::chrono::system_clock::now() - start;
876 double total_time = dt.count();
879 start = std::chrono::system_clock::now();
884 post_event_fn(request, responses[j], planner_data[j]);
886 collectMetrics(planner_data[j], responses[j], solved[j], total_time);
887 dt = std::chrono::system_clock::now() - start;
888 double metriconstraints_storage_time = dt.count();
889 RCLCPP_DEBUG(
getLogger(),
"Spent %lf seconds collecting metrics", metriconstraints_storage_time);
899 planner_completion_fn(request, planner_data);
906 if (!
options.parallel_planning_pipelines.empty())
909 for (
const std::pair<
const std::string, std::vector<std::pair<std::string, std::string>>>& parallel_pipeline_entry :
910 options.parallel_planning_pipelines)
915 std::vector<planning_interface::MotionPlanDetailedResponse> responses(
options.runs);
916 std::vector<bool> solved(
options.runs);
921 planner_start_function(request, planner_data);
926 for (
const auto& pipeline_planner_id_pair : parallel_pipeline_entry.second)
929 .
planner_id = pipeline_planner_id_pair.second,
930 .planning_pipeline = pipeline_planner_id_pair.first,
931 .planning_attempts = request.num_planning_attempts,
932 .planning_time = request.allowed_planning_time,
933 .max_velocity_scaling_factor = request.max_velocity_scaling_factor,
934 .max_acceleration_scaling_factor = request.max_acceleration_scaling_factor
940 for (
int j = 0; j <
options.runs; ++j)
945 pre_event_function(request);
949 auto planning_component = std::make_shared<moveit_cpp::PlanningComponent>(request.group_name,
moveit_cpp_);
953 planning_component->setStartState(start_state);
954 planning_component->setGoal(request.goal_constraints);
955 planning_component->setPathConstraints(request.path_constraints);
956 planning_component->setTrajectoryConstraints(request.trajectory_constraints);
959 std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
961 const auto t1 = std::chrono::system_clock::now();
962 const auto response = planning_component->plan(multi_pipeline_plan_request,
965 const auto t2 = std::chrono::system_clock::now();
967 solved[j] = bool(response.error_code);
969 responses[j].error_code = response.error_code;
970 if (response.trajectory)
972 responses[j].description.push_back(
"plan");
973 responses[j].trajectory.push_back(response.trajectory);
974 responses[j].processing_time.push_back(std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count());
977 std::chrono::duration<double> dt = std::chrono::system_clock::now() - start;
978 double total_time = dt.count();
981 start = std::chrono::system_clock::now();
985 post_event_fn(request, responses[j], planner_data[j]);
988 collectMetrics(planner_data[j], responses[j], solved[j], total_time);
989 dt = std::chrono::system_clock::now() - start;
990 double metriconstraints_storage_time = dt.count();
991 RCLCPP_DEBUG(
getLogger(),
"Spent %lf seconds collecting metrics", metriconstraints_storage_time);
1001 planner_completion_fn(request, planner_data);
1011 bool solved,
double total_time)
1014 metrics[
"solved BOOLEAN"] = solved ?
"true" :
"false";
1019 double traj_len = 0.0;
1020 double clearance = 0.0;
1021 bool correct =
true;
1023 double process_time = total_time;
1024 for (std::size_t j = 0; j < motion_plan_response.
trajectory.size(); ++j)
1053 return s.has_value() ? s.value() : 0.0;
1056 metrics[
"path_" + motion_plan_response.
description[j] +
"_correct BOOLEAN"] = correct ?
"true" :
"false";
1060 metrics[
"path_" + motion_plan_response.
description[j] +
"_time REAL"] =
1063 if (j == motion_plan_response.
trajectory.size() - 1)
1065 metrics[
"final_path_correct BOOLEAN"] = correct ?
"true" :
"false";
1073 if (process_time <= 0.0)
1080 PlannerBenchmarkData& planner_data,
const std::vector<planning_interface::MotionPlanDetailedResponse>& responses,
1081 const std::vector<bool>& solved)
1083 RCLCPP_INFO(
getLogger(),
"Computing result path similarity");
1084 const size_t result_count = planner_data.size();
1085 size_t unsolved = std::count_if(solved.begin(), solved.end(), [](
bool s) { return !s; });
1086 std::vector<double> average_distances(responses.size());
1087 for (
size_t first_traj_i = 0; first_traj_i < result_count; ++first_traj_i)
1090 if (!solved[first_traj_i])
1092 average_distances[first_traj_i] = std::numeric_limits<double>::max();
1096 for (
size_t second_traj_i = first_traj_i + 1; second_traj_i < result_count; ++second_traj_i)
1099 if (!solved[second_traj_i])
1107 double trajectory_distance;
1112 average_distances[first_traj_i] += trajectory_distance;
1113 average_distances[second_traj_i] += trajectory_distance;
1116 average_distances[first_traj_i] /= result_count - unsolved - 1;
1120 for (
size_t i = 0; i < result_count; ++i)
1126 double& result_distance)
1129 if (traj_first.
empty() || traj_second.
empty())
1133 size_t pos_first = 0;
1134 size_t pos_second = 0;
1144 double total_distance = 0;
1150 total_distance += current_distance;
1152 if (pos_first == max_pos_first && pos_second == max_pos_second)
1156 bool can_up_first = pos_first < max_pos_first;
1157 bool can_up_second = pos_second < max_pos_second;
1158 bool can_up_both = can_up_first && can_up_second;
1161 double up_both = std::numeric_limits<double>::max();
1162 double up_first = std::numeric_limits<double>::max();
1163 double up_second = std::numeric_limits<double>::max();
1172 if (can_up_both && up_both < up_first && up_both < up_second)
1176 current_distance = up_both;
1178 else if ((can_up_first && up_first < up_second) || !can_up_second)
1181 current_distance = up_first;
1183 else if (can_up_second)
1186 current_distance = up_second;
1190 result_distance = total_distance /
static_cast<double>(steps);
1198 size_t num_planners = 0;
1199 for (
const std::pair<
const std::string, std::vector<std::string>>& pipeline :
options.planning_pipelines)
1201 num_planners += pipeline.second.size();
1203 num_planners +=
options.parallel_planning_pipelines.size();
1205 std::string hostname = [&]() {
1206 static const int BUF_SIZE = 1024;
1207 char buffer[BUF_SIZE];
1208 int err = gethostname(buffer,
sizeof(buffer));
1211 return std::string();
1215 buffer[BUF_SIZE - 1] =
'\0';
1216 return std::string(buffer);
1219 if (hostname.empty())
1221 hostname =
"UNKNOWN";
1225 std::string filename =
options.output_directory;
1226 if (!filename.empty() && filename[filename.size() - 1] !=
'/')
1228 filename.append(
"/");
1232 std::filesystem::create_directories(filename);
1235 filename += (
options.benchmark_name.empty() ?
"" :
options.benchmark_name +
"_") + benchmark_request.
name +
"_" +
1236 hostname +
"_" + start_time +
".log";
1239 std::ofstream out(filename.c_str());
1242 RCLCPP_ERROR(
getLogger(),
"Failed to open '%s' for benchmark output", filename.c_str());
1247 out <<
"MoveIt version " << MOVEIT_VERSION_STR <<
'\n';
1248 out <<
"Experiment " << benchmark_request.
name <<
'\n';
1249 out <<
"Running on " << hostname <<
'\n';
1250 out <<
"Starting at " << start_time <<
'\n';
1253 moveit_msgs::msg::PlanningScene scene_msg;
1255 out <<
"<<<|" <<
'\n';
1256 out <<
"Motion plan request:" <<
'\n'
1257 <<
" planner_id: " << benchmark_request.
request.planner_id <<
'\n'
1258 <<
" group_name: " << benchmark_request.
request.group_name <<
'\n'
1259 <<
" num_planning_attempts: " << benchmark_request.
request.num_planning_attempts <<
'\n'
1260 <<
" allowed_planning_time: " << benchmark_request.
request.allowed_planning_time <<
'\n';
1261 out <<
"Planning scene:" <<
'\n'
1262 <<
" scene_name: " << scene_msg.name <<
'\n'
1263 <<
" robot_model_name: " << scene_msg.robot_model_name <<
'\n'
1267 out <<
"0 is the random seed" <<
'\n';
1268 out << benchmark_request.
request.allowed_planning_time <<
" seconds per run" <<
'\n';
1270 out <<
"-1 MB per run" <<
'\n';
1271 out <<
options.runs <<
" runs per planner" <<
'\n';
1272 out << benchmark_duration <<
" seconds spent to collect the data" <<
'\n';
1275 out <<
"0 enum types" <<
'\n';
1277 out << num_planners <<
" planners" <<
'\n';
1283 for (
const std::pair<
const std::string, std::vector<std::string>>& pipeline :
options.planning_pipelines)
1285 for (std::size_t i = 0; i < pipeline.second.size(); ++i, ++run_id)
1288 out << pipeline.second[i] <<
" (" << pipeline.first <<
')' <<
'\n';
1292 out <<
"0 common properties" <<
'\n';
1295 std::set<std::string> properties_set;
1298 for (PlannerRunData::const_iterator pit = planner_run_data.begin(); pit != planner_run_data.end();
1300 properties_set.insert(pit->first);
1304 out << properties_set.size() <<
" properties for each run" <<
'\n';
1305 for (
const std::string& property : properties_set)
1306 out <<
property <<
'\n';
1315 for (
const std::string& property : properties_set)
1318 PlannerRunData::const_iterator runit = planner_run_data.find(property);
1319 if (runit != planner_run_data.end())
1320 out << runit->second;
1330 for (
const std::pair<
const std::string, std::vector<std::pair<std::string, std::string>>>& parallel_pipeline :
1331 options.parallel_planning_pipelines)
1334 out << parallel_pipeline.first <<
" (" << parallel_pipeline.first <<
")" <<
'\n';
1338 out <<
"0 common properties" <<
'\n';
1341 std::set<std::string> properties_set;
1345 for (PlannerRunData::const_iterator pit = planner_run_data.begin(); pit != planner_run_data.end(); ++pit)
1347 properties_set.insert(pit->first);
1352 out << properties_set.size() <<
" properties for each run" <<
'\n';
1353 for (
const std::string& property : properties_set)
1354 out <<
property <<
'\n';
1363 for (
const std::string& property : properties_set)
1366 PlannerRunData::const_iterator runit = planner_run_data.find(property);
1367 if (runit != planner_run_data.end())
1368 out << runit->second;
1380 RCLCPP_INFO(
getLogger(),
"Benchmark results saved to '%s'", filename.c_str());
boost::posix_time::ptime toBoost(const std::chrono::time_point< Clock, Duration > &from)
Representation of a robot's state. This includes position, velocity, acceleration and effort.
double distance(const RobotState &other) const
Return the sum of joint distances to "other" state. An L1 norm. Only considers active joints.
bool satisfiesBounds(double margin=0.0) const
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)
bool loadQueries(const std::string ®ex, const std::string &scene_name, std::vector< BenchmarkRequest > &queries)
Load all motion plan requests matching the given regular expression from the warehouse.
void createRequestCombinations(const BenchmarkRequest &benchmark_request, 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.
std::vector< PlannerRunData > PlannerBenchmarkData
Structure to hold information for a single planner's benchmark data.
rclcpp::Node::SharedPtr node_
planning_scene::PlanningScenePtr planning_scene_
std::vector< PlannerStartEventFunction > planner_start_functions_
warehouse_ros::DatabaseLoader db_loader_
bool computeTrajectoryDistance(const robot_trajectory::RobotTrajectory &traj_first, const robot_trajectory::RobotTrajectory &traj_second, double &result_distance)
void addQueryStartEvent(const QueryStartEventFunction &func)
std::shared_ptr< moveit_warehouse::ConstraintsStorage > constraints_storage_
std::shared_ptr< moveit_warehouse::PlanningSceneStorage > planning_scene_storage_
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::vector< QueryCompletionEventFunction > query_end_functions_
std::shared_ptr< moveit_warehouse::TrajectoryConstraintsStorage > trajectory_constraints_storage_
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.
std::shared_ptr< moveit_cpp::MoveItCpp > moveit_cpp_
virtual void collectMetrics(PlannerRunData &metrics, const planning_interface::MotionPlanDetailedResponse &motion_plan_response, bool solved, double total_time)
std::vector< QueryStartEventFunction > query_start_functions_
bool loadPathConstraints(const std::string ®ex, std::vector< PathConstraints > &constraints)
Load all constraints matching the given regular expression from the warehouse.
std::vector< PreRunEventFunction > pre_event_functions_
void runBenchmark(moveit_msgs::msg::MotionPlanRequest request, const BenchmarkOptions &options)
Execute the given motion plan request on the set of planners for the set number of runs.
void addPreRunEvent(const PreRunEventFunction &func)
BenchmarkExecutor(const rclcpp::Node::SharedPtr &node, const std::string &robot_description_param="robot_description")
virtual bool runBenchmarks(const BenchmarkOptions &options)
std::vector< PlannerBenchmarkData > benchmark_data_
virtual bool loadBenchmarkQueryData(const BenchmarkOptions &options, 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.
std::vector< PostRunEventFunction > post_event_functions_
virtual bool initializeBenchmarks(const BenchmarkOptions &options, moveit_msgs::msg::PlanningScene &scene_msg, std::vector< BenchmarkRequest > &queries)
void addPlannerCompletionEvent(const PlannerCompletionEventFunction &func)
std::shared_ptr< planning_scene_monitor::PlanningSceneMonitor > planning_scene_monitor_
void addPlannerStartEvent(const PlannerStartEventFunction &func)
void addQueryCompletionEvent(const QueryCompletionEventFunction &func)
bool loadStates(const std::string ®ex, 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...
std::function< void(moveit_msgs::msg::MotionPlanRequest &request)> PreRunEventFunction
Definition of a pre-run benchmark event function. Invoked immediately before each planner calls solve...
std::vector< PlannerCompletionEventFunction > planner_completion_functions_
std::shared_ptr< moveit_warehouse::RobotStateStorage > robot_state_storage_
virtual ~BenchmarkExecutor()
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 void writeOutput(const BenchmarkRequest &benchmark_request, const std::string &start_time, double benchmark_duration, const BenchmarkOptions &options)
bool initialize(const std::vector< std::string > &plugin_classes)
std::shared_ptr< moveit_warehouse::PlanningSceneWorldStorage > planning_scene_world_storage_
std::function< void(const moveit_msgs::msg::MotionPlanRequest &request, PlannerBenchmarkData &benchmark_data)> PlannerStartEventFunction
bool loadTrajectoryConstraints(const std::string ®ex, std::vector< TrajectoryConstraints > &constraints)
Load all trajectory constraints from the warehouse that match the given regular expression.
void addPostRunEvent(const PostRunEventFunction &func)
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.
Maintain a sequence of waypoints and the time durations between these waypoints.
std::size_t getWayPointCount() const
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
std::string toString(double d)
Convert a double to std::string using the classic C locale.
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::msg::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
Convert a robot state msg (with accompanying extra transforms) to a MoveIt robot state.
::planning_interface::MotionPlanResponse getShortestSolution(const std::vector<::planning_interface::MotionPlanResponse > &solutions)
Function that returns the shortest solution out of a vector of solutions based on robot_trajectory::p...
warehouse_ros::MessageWithMetadata< moveit_msgs::msg::RobotState >::ConstPtr RobotStateWithMetadata
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
const rclcpp::Logger & getLogger()
std::map< std::string, PlannerConfigurationSettings > PlannerConfigurationMap
Map from PlannerConfigurationSettings.name to PlannerConfigurationSettings.
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
std::optional< double > smoothness(const RobotTrajectory &trajectory)
Calculate the smoothness of a given trajectory.
double pathLength(const RobotTrajectory &trajectory)
Calculate the path length of a given trajectory based on the accumulated robot state distances....
Representation of a collision checking request.
Representation of a collision checking result.
bool collision
True if collision was found, false otherwise.
Planner parameters provided with the MotionPlanRequest.
std::vector< PlanRequestParameters > plan_request_parameter_vector
Planner parameters provided with the MotionPlanRequest.
moveit_msgs::msg::MotionPlanRequest request
std::vector< moveit_msgs::msg::Constraints > constraints
moveit_msgs::msg::RobotState state
moveit_msgs::msg::TrajectoryConstraints constraints
Options to configure a benchmark experiment. The configuration is provided via ROS2 parameters.
std::vector< std::string > description
std::vector< double > processing_time
std::vector< robot_trajectory::RobotTrajectoryPtr > trajectory