Loading...
Searching...
No Matches
MotionBenchmakerDemo.cpp
1#include "MotionBenchmakerDemo.h"
2#include <boost/property_tree/ptree.hpp>
3#include <boost/property_tree/json_parser.hpp>
4#include <ompl/base/spaces/RealVectorStateSpace.h>
5#include <ompl/base/ProblemDefinition.h>
6#include <ompl/base/PlannerTerminationCondition.h>
7#include <ompl/geometric/planners/rrt/RRTConnect.h>
8#include <ompl/geometric/planners/rrt/RRTstar.h>
9#include <ompl/geometric/planners/fmt/FMT.h>
10#include <ompl/geometric/planners/prm/PRM.h>
11#include <ompl/geometric/planners/rrt/RRTConnect.h>
12#include <ompl/geometric/planners/rrt/RRT.h>
13#include <ompl/geometric/planners/kpiece/KPIECE1.h>
14#include <ompl/geometric/planners/kpiece/LBKPIECE1.h>
15#include <ompl/vamp/VampStateValidityChecker.h>
16#include <ompl/vamp/VampMotionValidator.h>
17#include <ompl/geometric/SimpleSetup.h>
18#include <ompl/vamp/VampStateSpace.h>
19#include <vamp/robots/panda.hh>
20#include <vamp/vector.hh>
21#include <vamp/collision/factory.hh>
22#include <ompl/tools/benchmark/Benchmark.h>
23
24#include <iostream>
25#include <iomanip>
26#include <numeric>
27#include <algorithm>
28#include <cmath>
29#include <fstream>
30
31namespace ob = ompl::base;
32namespace og = ompl::geometric;
33namespace ot = ompl::tools;
34
35MotionBenchmakerDemo::MotionBenchmakerDemo(const std::string &robotName, const std::string &problemFile,
36 const std::string &plannerName)
37 : robotName_(robotName), plannerName_(plannerName)
38{
39 loadProblemsFromJSON(problemFile);
40 initializeStateSpace();
41}
42
43void MotionBenchmakerDemo::loadProblemsFromJSON(const std::string &filename)
44{
45 using boost::property_tree::ptree;
46 ptree pt;
47
48 try
49 {
50 std::ifstream file(filename);
51 if (!file.is_open())
52 {
53 throw std::runtime_error("Failed to open JSON file: " + filename);
54 }
55 boost::property_tree::json_parser::read_json(file, pt);
56 }
57 catch (const std::exception &e)
58 {
59 throw std::runtime_error(std::string("Failed to load JSON file: ") + e.what());
60 }
61
62 // Check if problems key exists
63 auto problemsIt = pt.find("problems");
64 if (problemsIt == pt.not_found())
65 {
66 throw std::runtime_error("JSON file does not contain 'problems' key");
67 }
68
69 // Iterate through each problem type (e.g., "problem_type_1", "problem_type_2")
70 for (const auto &problemEntry : pt.get_child("problems"))
71 {
72 std::string problemName = problemEntry.first;
73 problemNames_.push_back(problemName);
74
75 const auto &problemInstances = problemEntry.second;
76 std::vector<ptree> instances;
77
78 // Each problem type contains an array of problem instances
79 for (const auto &instanceEntry : problemInstances)
80 {
81 instances.push_back(instanceEntry.second);
82 }
83
84 problems_[problemName] = instances;
85 }
86
87 std::cout << "Loaded " << problemNames_.size() << " problem types from " << filename << std::endl;
88}
89
90void MotionBenchmakerDemo::initializeStateSpace()
91{
92 // Create state space
93 using Robot = vamp::robots::Panda;
94 space_ = std::make_shared<ompl::vamp::VampStateSpace<Robot>>();
95}
96
97std::shared_ptr<ompl::base::Planner>
98MotionBenchmakerDemo::createPlanner(const std::shared_ptr<ompl::base::SpaceInformation> &si) const
99{
100 std::shared_ptr<ompl::base::Planner> planner;
101
102 if (plannerName_ == "RRTConnect" || plannerName_ == "rrtc")
103 {
104 planner = std::make_shared<og::RRTConnect>(si);
105 }
106 else if (plannerName_ == "RRTstar" || plannerName_ == "rrtstar")
107 {
108 planner = std::make_shared<og::RRTstar>(si);
109 }
110 else if (plannerName_ == "FMT" || plannerName_ == "fmt")
111 {
112 planner = std::make_shared<og::FMT>(si);
113 }
114 else if (plannerName_ == "PRM" || plannerName_ == "prm")
115 {
116 planner = std::make_shared<og::PRM>(si);
117 }
118 else if (plannerName_ == "KPIECE1" || plannerName_ == "kpiece1")
119 {
120 planner = std::make_shared<og::KPIECE1>(si);
121 }
122 else if (plannerName_ == "RRT" || plannerName_ == "rrt")
123 {
124 planner = std::make_shared<og::RRT>(si);
125 }
126 else if (plannerName_ == "LBKPIECE1" || plannerName_ == "lbkpiece1")
127 {
128 planner = std::make_shared<og::LBKPIECE1>(si);
129 }
130 else
131 {
132 std::cerr << "Unknown planner '" << plannerName_ << "', using RRTConnect" << std::endl;
133 planner = std::make_shared<og::RRTConnect>(si);
134 }
135
136 return planner;
137}
138
139bool MotionBenchmakerDemo::setupProblem(const std::string &problemName, const boost::property_tree::ptree &problemData)
140{
141 // Check if problem is valid
142 bool valid = problemData.get<bool>("valid", true);
143 if (!valid)
144 {
145 return false;
146 }
147
148 // Build environment from problem data
149 vamp::collision::Environment<float> env_float;
150 bool is_box = problemName == "box" ? true : false;
151
152 // Load spheres
153 if (problemData.find("sphere") != problemData.not_found())
154 {
155 for (const auto &sphereEntry : problemData.get_child("sphere"))
156 {
157 const auto &s = sphereEntry.second;
158 std::array<float, 3> center;
159 size_t i = 0;
160 const auto &posChild =
161 (s.find("center") != s.not_found()) ? s.get_child("center") : s.get_child("position");
162 for (const auto &coord : posChild)
163 {
164 if (i < 3)
165 center[i++] = std::stof(coord.second.data());
166 }
167 float radius = s.get<float>("radius");
168 env_float.spheres.emplace_back(vamp::collision::factory::sphere::array(center, radius));
169 }
170 }
171
172 // Load cylinders (convert to capsules)
173 if (problemData.find("cylinder") != problemData.not_found())
174 {
175 for (const auto &cylEntry : problemData.get_child("cylinder"))
176 {
177 const auto &c = cylEntry.second;
178 std::array<float, 3> center, orientation;
179 size_t i = 0;
180 const auto &posChild =
181 (c.find("center") != c.not_found()) ? c.get_child("center") : c.get_child("position");
182 for (const auto &coord : posChild)
183 {
184 if (i < 3)
185 center[i++] = std::stof(coord.second.data());
186 }
187 i = 0;
188 for (const auto &euler : c.get_child("orientation_euler_xyz"))
189 {
190 if (i < 3)
191 orientation[i++] = std::stof(euler.second.data());
192 }
193 float radius = c.get<float>("radius");
194 float length = c.get<float>("length");
195 if (is_box)
196 {
197 std::array<float, 3> halfExtents = {radius, radius, length / 2.0f};
198 env_float.cuboids.emplace_back(
199 vamp::collision::factory::cuboid::array(center, orientation, halfExtents));
200 }
201 else
202 {
203 env_float.capsules.emplace_back(
204 vamp::collision::factory::capsule::center::array(center, orientation, radius, length));
205 }
206 }
207 }
208
209 // Load boxes
210 if (problemData.find("box") != problemData.not_found())
211 {
212 for (const auto &boxEntry : problemData.get_child("box"))
213 {
214 const auto &b = boxEntry.second;
215 std::array<float, 3> center, orientation, halfExtents;
216 size_t i = 0;
217 const auto &posChild =
218 (b.find("center") != b.not_found()) ? b.get_child("center") : b.get_child("position");
219 for (const auto &coord : posChild)
220 {
221 if (i < 3)
222 center[i++] = std::stof(coord.second.data());
223 }
224 i = 0;
225 for (const auto &euler : b.get_child("orientation_euler_xyz"))
226 {
227 if (i < 3)
228 orientation[i++] = std::stof(euler.second.data());
229 }
230 i = 0;
231 for (const auto &extent : b.get_child("half_extents"))
232 {
233 if (i < 3)
234 halfExtents[i++] = std::stof(extent.second.data());
235 }
236 env_float.cuboids.emplace_back(vamp::collision::factory::cuboid::array(center, orientation, halfExtents));
237 }
238 }
239
240 using Environment = vamp::collision::Environment<vamp::FloatVector<vamp::FloatVectorWidth>>;
241 currentEnv_ = std::make_shared<Environment>(env_float);
242
243 using Robot = vamp::robots::Panda;
244 auto space_info = ss_->getSpaceInformation();
245
246 // Add VAMP-based validators using the environment
247 space_info->setStateValidityChecker(
248 std::make_shared<ompl::vamp::VampStateValidityChecker<Robot>>(space_info, *currentEnv_));
249 space_info->setMotionValidator(std::make_shared<ompl::vamp::VampMotionValidator<Robot>>(space_info, *currentEnv_));
250
251 // Set start and goal configurations
252 ob::ScopedState<> start(space_);
253 ob::ScopedState<> goal(space_);
254
255 // Get start configuration
256 std::vector<double> startVec;
257 if (problemData.find("start") != problemData.not_found())
258 {
259 for (const auto &val : problemData.get_child("start"))
260 {
261 startVec.push_back(std::stod(val.second.data()));
262 }
263 }
264
265 for (size_t i = 0; i < startVec.size() && i < space_->getDimension(); ++i)
266 {
267 start[i] = startVec[i];
268 }
269
270 // Get goal configuration
271 std::vector<double> goalVec;
272 if (problemData.find("goal") != problemData.not_found())
273 {
274 for (const auto &val : problemData.get_child("goal"))
275 {
276 goalVec.push_back(std::stod(val.second.data()));
277 }
278 }
279 else if (problemData.find("goals") != problemData.not_found())
280 {
281 // Use first goal from goals array
282 auto goalsChild = problemData.get_child("goals");
283 if (goalsChild.size() > 0)
284 {
285 auto firstGoal = goalsChild.begin()->second;
286 for (const auto &val : firstGoal)
287 {
288 goalVec.push_back(std::stod(val.second.data()));
289 }
290 }
291 }
292
293 for (size_t i = 0; i < goalVec.size() && i < space_->getDimension(); ++i)
294 {
295 goal[i] = goalVec[i];
296 }
297
298 // print start vs goal
299 // std::cout << "Start: ";
300 // for (size_t i = 0; i < startVec.size(); ++i) {
301 // std::cout << start[i] << " ";
302 // }
303 // std::cout << std::endl;
304
305 // std::cout << "Goal: ";
306 // for (size_t i = 0; i < goalVec.size(); ++i) {
307 // std::cout << goal[i] << " ";
308 // }
309 // std::cout << std::endl;
310
311 // Set the start and goal states
312 ss_->setStartAndGoalStates(start, goal);
313 return true;
314}
315
316PlanningResult MotionBenchmakerDemo::solveInstance(const std::string &problemName,
317 const boost::property_tree::ptree &problemData,
318 double timeoutSeconds)
319{
320 PlanningResult result;
321 ss_ = std::make_shared<og::SimpleSetup>(space_);
322
323 auto space_info = ss_->getSpaceInformation();
324
325 if (!setupProblem(problemName, problemData))
326 {
327 return result;
328 }
329 space_info = ss_->getSpaceInformation();
330 // Setup benchmarking
331 auto planner = createPlanner(space_info);
332 ss_->setPlanner(planner);
333
334 // Plan
335 ob::PlannerStatus status = ss_->solve(ob::timedPlannerTerminationCondition(timeoutSeconds));
336
337 result.planningTime = ss_->getLastPlanComputationTime();
338 result.simplificationTime = ss_->getLastSimplificationTime();
339
341 {
342 result.solved = true;
343 auto path = ss_->getSolutionPath();
344 result.pathVertices = path.getStateCount();
345 result.pathCost = path.length();
346 }
347
348 // Get planner data for iteration count
349 ob::PlannerData plannerData(space_info);
350 ss_->getPlanner()->getPlannerData(plannerData);
351 result.planningIterations = plannerData.numVertices();
352
353 // Clean up
354 ss_.reset();
355
356 return result;
357}
358
359PlanningResult MotionBenchmakerDemo::benchmarkInstance(const std::string &problemName,
360 const boost::property_tree::ptree &problemData,
361 unsigned int benchmarkTrials, double timeoutSeconds)
362{
363 PlanningResult result;
364 ss_ = std::make_shared<og::SimpleSetup>(space_);
365
366 if (!setupProblem(problemName, problemData))
367 {
368 return result;
369 }
370 auto space_info = ss_->getSpaceInformation();
371 // setup benchmark
372 double memoryLimit = 1000;
373
374 std::string benchmarkName = robotName_ + "_" + problemName;
375 ot::Benchmark b(*ss_, benchmarkName);
376 ot::Benchmark::Request request(timeoutSeconds, memoryLimit, benchmarkTrials);
377
378 b.addPlanner(std::make_shared<og::RRTConnect>(space_info));
379 b.addPlanner(std::make_shared<og::PRM>(space_info));
380 b.addPlanner(std::make_shared<og::KPIECE1>(space_info));
381 b.addPlanner(std::make_shared<og::LBKPIECE1>(space_info));
382
383 b.benchmark(request);
384 b.saveResultsToFile("vamp_mbm_cpp.log");
385 result.solved = true;
386 ss_.reset();
387
388 return result;
389}
390
391std::vector<PlanningResult> MotionBenchmakerDemo::benchmarkProblem(const std::string &problemName,
392 unsigned int benchmarkTrials, double timeoutSeconds)
393{
394 std::vector<PlanningResult> allResults;
395
396 if (problems_.find(problemName) == problems_.end())
397 {
398 std::cerr << "Problem '" << problemName << "' not found" << std::endl;
399 return allResults;
400 }
401
402 const auto &instances = problems_[problemName];
403 std::cout << "Benchmarking '" << problemName << "' with " << instances.size() << " instances..." << std::endl;
404
405 for (size_t i = 0; i < instances.size(); ++i)
406 {
407 std::cout << " Instance " << i + 1 << "/" << instances.size() << "... ";
408 std::cout.flush();
409
410 if (benchmarkTrials == 0)
411 {
412 auto result = solveInstance(problemName, instances[i], timeoutSeconds);
413 allResults.push_back(result);
414 }
415 else
416 {
417 auto result = benchmarkInstance(problemName, instances[i], benchmarkTrials, timeoutSeconds);
418 allResults.push_back(result);
419 break;
420 }
421 }
422
423 return allResults;
424}
425
426std::map<std::string, std::vector<PlanningResult>> MotionBenchmakerDemo::benchmarkAll(unsigned int benchmarkTrials,
427 double timeoutSeconds,
428 bool print_failures)
429{
430 std::map<std::string, std::vector<PlanningResult>> allResults;
431
432 for (const auto &problemName : problemNames_)
433 {
434 // if (problemName != "table_under_pick") {
435 // continue;
436 // }
437
438 auto results = benchmarkProblem(problemName, benchmarkTrials, timeoutSeconds);
439 allResults[problemName] = results;
440
441 if (print_failures)
442 {
443 unsigned int failures = 0;
444 for (const auto &result : results)
445 {
446 if (!result.solved)
447 {
448 failures++;
449 }
450 }
451 if (failures > 0)
452 {
453 std::cout << " Failed: " << failures << "/" << results.size() << std::endl;
454 }
455 }
456 }
457
458 return allResults;
459}
460
461void MotionBenchmakerDemo::printStatistics(const std::string &problemName, const std::vector<PlanningResult> &results)
462{
463 if (results.empty())
464 {
465 std::cout << "No results for problem '" << problemName << "'" << std::endl;
466 return;
467 }
468
469 // Count successes
470 unsigned int successes = 0;
471 std::vector<double> planningTimes;
472 std::vector<double> pathCosts;
473 std::vector<unsigned int> iterations;
474 std::vector<unsigned int> vertices;
475
476 for (const auto &result : results)
477 {
478 if (result.solved)
479 {
480 successes++;
481 planningTimes.push_back(result.planningTime); // Convert to seconds
482 pathCosts.push_back(result.pathCost);
483 iterations.push_back(result.planningIterations);
484 vertices.push_back(result.pathVertices);
485 }
486 }
487
488 std::cout << "\n=== Statistics for '" << problemName << "' ===" << std::endl;
489 std::cout << "Success Rate: " << successes << "/" << results.size() << std::endl;
490
491 if (successes > 0)
492 {
493 // Calculate statistics
494 auto minMaxTime = std::minmax_element(planningTimes.begin(), planningTimes.end());
495 double meanTime = std::accumulate(planningTimes.begin(), planningTimes.end(), 0.0) / planningTimes.size();
496
497 auto minMaxCost = std::minmax_element(pathCosts.begin(), pathCosts.end());
498 double meanCost = std::accumulate(pathCosts.begin(), pathCosts.end(), 0.0) / pathCosts.size();
499
500 std::cout << std::fixed << std::setprecision(4);
501 std::cout << "Planning Time (s): min=" << *minMaxTime.first << ", max=" << *minMaxTime.second
502 << ", mean=" << meanTime << std::endl;
503
504 std::cout << "Path Cost (m): min=" << *minMaxCost.first << ", max=" << *minMaxCost.second
505 << ", mean=" << meanCost << std::endl;
506
507 unsigned int meanIter = std::accumulate(iterations.begin(), iterations.end(), 0u) / iterations.size();
508 unsigned int meanVert = std::accumulate(vertices.begin(), vertices.end(), 0u) / vertices.size();
509
510 std::cout << "Mean Iterations: " << meanIter << std::endl;
511 std::cout << "Mean Path Vertices: " << meanVert << std::endl;
512 }
513 std::cout << std::endl;
514}
std::map< std::string, std::vector< PlanningResult > > benchmarkAll(unsigned int numTrials=1, double timeoutSeconds=5.0, bool print_failures=false)
Run benchmarks on all problems in the loaded file.
static void printStatistics(const std::string &problemName, const std::vector< PlanningResult > &results)
Print summary statistics for a set of results.
MotionBenchmakerDemo(const std::string &robotName, const std::string &problemFile, const std::string &plannerName="RRTConnect")
Initialize with a robot type and JSON problem file.
std::vector< PlanningResult > benchmarkProblem(const std::string &problemName, unsigned int numTrials=1, double timeoutSeconds=5.0)
Run a benchmark on a specific problem.
This namespace contains sampling based planning routines shared by both planning under geometric cons...
PlannerTerminationCondition timedPlannerTerminationCondition(double duration)
Return a termination condition that will become true duration seconds in the future (wall-time).
This namespace contains code that is specific to planning under geometric constraints.
Includes various tools such as self config, benchmarking, etc.
Results from a single planning trial.
@ EXACT_SOLUTION
The planner found an exact solution.
@ APPROXIMATE_SOLUTION
The planner found an approximate solution.