Loading...
Searching...
No Matches
PlanarManipulatorDemo.cpp
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 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
37#include <fstream>
38#include <boost/math/constants/constants.hpp>
39
40#include "boost/program_options.hpp"
41#include "PolyWorld.h"
42#include "PlanarManipulatorPolyWorld.h"
43#include "PlanarManipulator.h"
44#include "PlanarManipulatorStateSpace.h"
45#include "PlanarManipulatorStateValidityChecker.h"
46#include "PlanarManipulatorIKGoal.h"
47#include "PlanarManipulatorTSRRTConfig.h"
48#include "PlanarManipulatorXXLDecomposition.h"
49
50#include <ompl/geometric/SimpleSetup.h>
51
52// planners
53#include <ompl/geometric/planners/xxl/XXL.h>
54#include <ompl/geometric/planners/rrt/RRT.h>
55#include <ompl/geometric/planners/kpiece/KPIECE1.h>
56#include <ompl/geometric/planners/stride/STRIDE.h>
57#include <ompl/geometric/planners/rrt/TSRRT.h>
58#include <ompl/geometric/planners/rlrt/RLRT.h>
59
60// bi-directional
61#include <ompl/geometric/planners/rrt/RRTConnect.h>
62#include <ompl/geometric/planners/kpiece/BKPIECE1.h>
63#include <ompl/geometric/planners/rlrt/BiRLRT.h>
64
65#include <ompl/tools/benchmark/Benchmark.h>
66
67// Input arguments to this binary.
68struct Arguments
69{
70 int numLinks;
71 int numRuns;
72 double timeout;
73 std::string problem;
74 bool viz;
75};
76
77// Minimal setup for a planar manipulation problem.
78struct Problem
79{
80 Problem(int links, const std::string &problemName, PolyWorld &&world, const Eigen::Affine2d &baseFrame,
81 const Eigen::Affine2d &goalFrame)
82 : name(problemName), manipulator(links, 1.0 / links), world(std::move(world)), goalFrame(goalFrame)
83 {
84 manipulator.setBaseFrame(baseFrame);
85 }
86
87 std::string name;
88 PlanarManipulator manipulator;
89 PolyWorld world;
90 Eigen::Affine2d goalFrame;
91};
92
93// Creates a problem from the input arguments.
94Problem CreateProblem(const Arguments &args)
95{
96 if (args.problem == "corridor")
97 {
98 Eigen::Affine2d baseFrame;
99 Eigen::Affine2d goalFrame;
100 PolyWorld world = createCorridorProblem(args.numLinks, baseFrame, goalFrame);
101 return Problem(args.numLinks, args.problem, std::move(world), baseFrame, goalFrame);
102 }
103 else if (args.problem == "constricted")
104 {
105 Eigen::Affine2d baseFrame;
106 Eigen::Affine2d goalFrame;
107 PolyWorld world = createConstrictedProblem(args.numLinks, baseFrame, goalFrame);
108 return Problem(args.numLinks, args.problem, std::move(world), baseFrame, goalFrame);
109 }
110 else
111 {
112 throw ompl::Exception("Unknown problem name: " + args.problem);
113 }
114}
115
116// Initialize OMPL for the given planar manipulator problem.
117ompl::geometric::SimpleSetupPtr setupOMPL(Problem &problem)
118{
119 const unsigned int numLinks = problem.manipulator.getNumLinks();
120 // Create the state space for the manipulator.
121 ompl::base::StateSpacePtr space(new PlanarManipulatorStateSpace(numLinks));
122 ompl::base::RealVectorBounds bounds(numLinks);
123 bounds.setLow(-boost::math::constants::pi<double>());
124 bounds.setHigh(boost::math::constants::pi<double>());
125
126 // Bound the joints of the manipulator between [-PI, PI]
127 space->as<PlanarManipulatorStateSpace>()->setBounds(bounds);
128 problem.manipulator.setBounds(bounds.low, bounds.high);
129
130 ompl::geometric::SimpleSetupPtr setup(new ompl::geometric::SimpleSetup(space));
131
132 // Create the collision checker.
133 setup->setStateValidityChecker(std::make_shared<PlanarManipulatorCollisionChecker>(
134 setup->getSpaceInformation(), problem.manipulator, &problem.world));
135
136 // Increase motion validator resolution.
137 setup->getSpaceInformation()->setStateValidityCheckingResolution(0.001);
138
139 // Set the start and goal.
140 ompl::base::State *start = setup->getStateSpace()->allocState();
141 double *start_angles = start->as<PlanarManipulatorStateSpace::StateType>()->values;
142 for (size_t i = 0; i < numLinks; ++i)
143 start_angles[i] = 1e-7; // zero is dangerous for numerical reasons.
144 setup->getProblemDefinition()->addStartState(start);
145 setup->getStateSpace()->freeState(start);
146
147 ompl::base::GoalPtr goal(new PlanarManipulatorIKGoal(setup->getSpaceInformation(), problem.goalFrame,
148 &problem.manipulator,
149 false)); // orientation is not fixed
150 goal->as<PlanarManipulatorIKGoal>()->setThreshold(1e-3);
151 setup->setGoal(goal);
152
153 return setup;
154}
155
156// Returns the bounds on the reachable part of the world for the chain.
157ompl::base::RealVectorBounds GetReachableWorkspaceBounds(Point origin, double chainLength, const Problem &problem)
158{
159 const auto xBounds = problem.world.xBounds();
160 const auto yBounds = problem.world.yBounds();
161
162 // Clip the world bounds based on reachable workspace of the chain.
163 double xMin = std::max(origin.first - chainLength, xBounds.first);
164 double xMax = std::min(origin.first + chainLength, xBounds.second);
165 double yMin = std::max(origin.second - chainLength, yBounds.first);
166 double yMax = std::min(origin.second + chainLength, yBounds.second);
167 ompl::base::RealVectorBounds reachable_bounds(2);
168 reachable_bounds.setLow(0, xMin);
169 reachable_bounds.setHigh(0, xMax);
170 reachable_bounds.setLow(1, yMin);
171 reachable_bounds.setHigh(1, yMax);
172
173 return reachable_bounds;
174}
175
176// Returns the task-space projection for the planar manipulator when planning
177// using TSRRT. Projects the end-effector position into the workspace.
178ompl::geometric::TaskSpaceConfigPtr getTaskSpaceConfig(const Problem &problem)
179{
180 const PlanarManipulator &manip = problem.manipulator;
181 unsigned int numLinks = manip.getNumLinks();
182 double linkLength = 1.0 / numLinks;
183 double chainLength = numLinks * linkLength;
184
185 const Eigen::Affine2d &baseFrame = manip.getBaseFrame();
186 Point origin = {baseFrame.translation()(0), baseFrame.translation()(1)};
187
188 const ompl::base::RealVectorBounds reachable_bounds = GetReachableWorkspaceBounds(origin, chainLength, problem);
189
190 ompl::geometric::TaskSpaceConfigPtr task_space_ptr(new PlanarManipTaskSpaceConfig(&manip, reachable_bounds));
191 return task_space_ptr;
192}
193
194// Returns the XXL decomposition for the planar manipulator. Splits the
195// 2D workspace into a grid with numXYSlices in the x and y dimensions.
196// Projects the end-effector position of the chain into the decomposition.
197// For chains with more than six links, the midpoint is also projected.
198ompl::geometric::XXLDecompositionPtr getXXLDecomp(const ompl::base::SpaceInformationPtr &si, const Problem &problem,
199 int numXYSlices)
200{
201 const PlanarManipulator &manip = problem.manipulator;
202 unsigned int numLinks = manip.getNumLinks();
203 double linkLength = 1.0 / numLinks; // TODO: pass this in
204 double chainLength = numLinks * linkLength;
205
206 // Creating decomposition for XXL
207 const Eigen::Affine2d &baseFrame = manip.getBaseFrame();
208 Point origin = {baseFrame.translation()(0), baseFrame.translation()(1)};
209
210 const ompl::base::RealVectorBounds reachable_bounds = GetReachableWorkspaceBounds(origin, chainLength, problem);
211
212 std::vector<int> xySlices(2, numXYSlices);
213 const int thetaSlices = 1;
214 // Select point(s) on the manipulator for projection.
215 // For short chains, we only pick the end-effector position.
216 std::vector<int> projLinks;
217 if (numLinks > 6)
218 {
219 // midpoint
220 projLinks.push_back((numLinks / 2) - 1);
221 }
222 // end-effector.
223 projLinks.push_back(numLinks - 1);
224
225 ompl::geometric::XXLDecompositionPtr decomp(new PMXXLDecomposition(
226 si, &manip, reachable_bounds, xySlices, thetaSlices, projLinks, true)); // diagonal edges
227 return decomp;
228}
229
230// Computes the Cartesian distance traveled by each joint in the chain
231// on the solution path that is computed.
232void postRunEvent(const ompl::base::PlannerPtr &planner, ompl::tools::Benchmark::RunProperties &run,
233 const PlanarManipulator *manip)
234{
235 if (!planner->getProblemDefinition()->hasSolution())
236 return;
237
238 double cartesianDist = 0.0;
239 const auto &path = static_cast<const ompl::geometric::PathGeometric &>(
240 *(planner->getProblemDefinition()->getSolutionPath().get()));
241 for (size_t i = 0; i < path.getStateCount() - 1; ++i)
242 {
243 std::vector<Eigen::Affine2d> startFrames, endFrames;
244 manip->FK(path.getState(i)->as<PlanarManipulatorStateSpace::StateType>()->values, startFrames);
245 manip->FK(path.getState(i + 1)->as<PlanarManipulatorStateSpace::StateType>()->values, endFrames);
246
247 for (size_t j = 1; j < endFrames.size(); ++j)
248 cartesianDist += (endFrames[j].translation() - startFrames[j].translation()).norm();
249 }
250
251 run["Cartesian Distance REAL"] = boost::lexical_cast<std::string>(cartesianDist);
252}
253
254void BenchmarkProblem(ompl::geometric::SimpleSetupPtr setup, const Problem &problem, int runs, double timeout)
255{
256 ompl::base::PlannerPtr kpiece(new ompl::geometric::KPIECE1(setup->getSpaceInformation()));
257 ompl::base::PlannerPtr rrt(new ompl::geometric::RRT(setup->getSpaceInformation()));
258 ompl::base::PlannerPtr rlrt(new ompl::geometric::RLRT(setup->getSpaceInformation()));
259 ompl::base::PlannerPtr stride(new ompl::geometric::STRIDE(setup->getSpaceInformation()));
260 ompl::base::PlannerPtr tsrrt(new ompl::geometric::TSRRT(setup->getSpaceInformation(), getTaskSpaceConfig(problem)));
261
262 ompl::base::PlannerPtr rrtc(new ompl::geometric::RRTConnect(setup->getSpaceInformation()));
263 ompl::base::PlannerPtr bkpiece(new ompl::geometric::BKPIECE1(setup->getSpaceInformation()));
264 ompl::base::PlannerPtr birlrt(new ompl::geometric::BiRLRT(setup->getSpaceInformation()));
265
266 const int numLinks = problem.manipulator.getNumLinks();
267 const int xySlices = std::max(2, numLinks / 3);
268 ompl::base::PlannerPtr xxl(new ompl::geometric::XXL(setup->getSpaceInformation(),
269 getXXLDecomp(setup->getSpaceInformation(), problem, xySlices)));
270 ompl::base::PlannerPtr xxl1(new ompl::geometric::XXL(
271 setup->getSpaceInformation(), getXXLDecomp(setup->getSpaceInformation(), problem, /*xySlices*/ 1)));
272 xxl1->setName("XXL1");
273
274 std::string name = "PlanarManipulator - " + problem.name;
275 ompl::tools::Benchmark benchmark(*setup, name);
276
277 benchmark.addPlanner(rrt);
278 benchmark.addPlanner(rrtc);
279 benchmark.addPlanner(rlrt);
280 benchmark.addPlanner(birlrt);
281 benchmark.addPlanner(stride);
282 benchmark.addPlanner(kpiece);
283 benchmark.addPlanner(bkpiece);
284 benchmark.addPlanner(xxl);
285 benchmark.addPlanner(xxl1);
286 benchmark.addPlanner(tsrrt);
287
288 benchmark.setPostRunEvent([&](const ompl::base::PlannerPtr &planner, ompl::tools::Benchmark::RunProperties &run)
289 { postRunEvent(planner, run, &problem.manipulator); });
290 benchmark.addExperimentParameter("num_links", "INTEGER", boost::lexical_cast<std::string>(numLinks));
291 benchmark.addExperimentParameter("cells", "INTEGER", boost::lexical_cast<std::string>(xySlices));
292
293 double memoryLimit = 8192.0; // MB. XXL requires an XXL amount of memory
294 ompl::tools::Benchmark::Request request(timeout, memoryLimit, runs);
295 benchmark.benchmark(request);
296
297 benchmark.saveResultsToFile();
298}
299
300void WriteVisualization(const Problem &problem, const ompl::geometric::PathGeometric &path, int xySlices)
301{
302 const int numLinks = problem.manipulator.getNumLinks();
303
304 const char *world_file = "world.yaml";
305 OMPL_INFORM("Writing world to %s", world_file);
306 problem.world.writeWorld(world_file);
307
308 const double linkLength = 1.0 / numLinks;
309 const Eigen::Affine2d &basePose = problem.manipulator.getBaseFrame();
310
311 const char *path_file = "manipulator_path.txt";
312 OMPL_INFORM("Writing path to %s", path_file);
313 std::ofstream fout;
314
315 // This is a proprietary format for the python visualization script.
316 fout.open(path_file);
317 // Preamble.
318 fout << numLinks << " " << linkLength << " " << basePose.translation()(0) << " " << basePose.translation()(1) << " "
319 << xySlices << std::endl;
320 // Write each state on the interpolated path.
321 for (size_t i = 0; i < path.getStateCount(); ++i)
322 {
323 const double *angles = path.getState(i)->as<PlanarManipulatorStateSpace::StateType>()->values;
324 for (size_t j = 0; j < problem.manipulator.getNumLinks(); ++j)
325 fout << angles[j] << " ";
326 fout << std::endl;
327 }
328 fout.close();
329}
330
331void SolveProblem(ompl::geometric::SimpleSetupPtr setup, const Problem &problem, double timeout, bool write_viz_out)
332{
333 // Solve the problem with XXL.
334 const int numLinks = problem.manipulator.getNumLinks();
335 // The number of grid cells in each dimension of the workspace decomposition.
336 const int xySlices = std::max(2, numLinks / 3);
337 ompl::base::PlannerPtr xxl(new ompl::geometric::XXL(setup->getSpaceInformation(),
338 getXXLDecomp(setup->getSpaceInformation(), problem, xySlices)));
339 setup->setPlanner(xxl);
340
341 // SOLVE!
342 ompl::base::PlannerStatus status = setup->solve(timeout);
343
346 {
347 ompl::geometric::PathGeometric &pgeo = setup->getSolutionPath();
348 OMPL_INFORM("Solution path has %d states", pgeo.getStateCount());
349
350 if (write_viz_out)
351 {
352 pgeo.interpolate(250);
353 WriteVisualization(problem, pgeo, xySlices);
354 }
355 }
356 else
357 {
358 OMPL_WARN("Planning failed");
359 }
360}
361
362void PlanarManipulatorPlanning(const Arguments &args)
363{
364 ompl::msg::setLogLevel(ompl::msg::LOG_INFO);
365
366 Problem problem = CreateProblem(args);
367 ompl::geometric::SimpleSetupPtr setup = setupOMPL(problem);
368
369 if (args.numRuns == 1)
370 SolveProblem(setup, problem, args.timeout, args.viz);
371 else
372 BenchmarkProblem(setup, problem, args.numRuns, args.timeout);
373}
374
375int main(int argc, char **argv)
376{
377 Arguments args;
378
379 // Read args
380 namespace po = boost::program_options;
381 po::options_description desc("Allowed options");
382 // clang-format off
383 desc.add_options()
384 ("help,?", "Show this message")
385 ("links,l", po::value<int>(&args.numLinks)->default_value(10),
386 "Set the number of links in the chain")
387 ("runs,r", po::value<int>(&args.numRuns)->default_value(1),
388 "The number of times to execute the query. >1 implies benchmarking")
389 ("timeout,t", po::value<double>(&args.timeout)->default_value(60.0),
390 "The maximum time (seconds) before failure is declared")
391 ("problem,p", po::value<std::string>(&args.problem)->default_value("corridor"),
392 "The name of the problem [corridor,constricted] to solve")
393 ("viz,v", po::bool_switch(&args.viz)->default_value(false),
394 "Write visualization output to disk. Only works when runs = 1");
395 // clang-format on
396
397 po::variables_map vm;
398 po::store(po::parse_command_line(argc, argv, desc), vm);
399 po::notify(vm);
400
401 if (vm.count("help"))
402 {
403 std::cout << desc << std::endl;
404 return 0;
405 }
406 PlanarManipulatorPlanning(args);
407}
The exception type for ompl.
Definition Exception.h:47
The lower and upper bounds for an Rn space.
double * values
The value of the actual vector in Rn.
Definition of an abstract state.
Definition State.h:50
const T * as() const
Cast this instance to a desired type.
Definition State.h:66
Bi-directional KPIECE with one level of discretization.
Definition BKPIECE1.h:74
Bi-directional Range-Limited Random Tree (Ryan Luna's Random Tree).
Definition BiRLRT.h:64
Kinematic Planning by Interior-Exterior Cell Exploration.
Definition KPIECE1.h:72
Definition of a geometric path.
std::size_t getStateCount() const
Get the number of states (way-points) that make up this path.
void interpolate(unsigned int count)
Insert a number of states in a path so that the path is made up of exactly count states....
Range-Limited Random Tree (Ryan Luna's Random Tree).
Definition RLRT.h:64
RRT-Connect (RRTConnect).
Definition RRTConnect.h:62
Rapidly-exploring Random Trees.
Definition RRT.h:66
Search Tree with Resolution Independent Density Estimation.
Definition STRIDE.h:79
Create the set of classes typically needed to solve a geometric problem.
Definition SimpleSetup.h:63
Task-space Rapidly-exploring Random Trees.
Definition TSRRT.h:88
Benchmark a set of planners on a problem instance.
Definition Benchmark.h:49
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition Console.h:66
void setLogLevel(LogLevel level)
Set the minimum level of logging data to output. Messages with lower logging levels will not be recor...
Definition Console.cpp:136
A class to store the exit status of Planner::solve().
@ EXACT_SOLUTION
The planner found an exact solution.
@ APPROXIMATE_SOLUTION
The planner found an approximate solution.
Representation of a benchmark request.
Definition Benchmark.h:152
The data collected from a run of a planner is stored as key-value pairs.
Definition Benchmark.h:73