37 #include <ompl/util/PPM.h>
38 #include <boost/filesystem.hpp>
40 #include "ConstrainedPlanningCommon.h"
42 static const double pi2 = 2 * boost::math::constants::pi<double>();
51 TorusConstraint(
const double outer,
const double inner,
const std::string &maze)
52 :
ompl::base::
Constraint(3, 1), outer(outer), inner(inner), file_(maze)
54 ppm_.loadFile(maze.c_str());
57 void getStartAndGoalStates(Eigen::Ref<Eigen::VectorXd> start, Eigen::Ref<Eigen::VectorXd> goal)
const
59 const double h = ppm_.getHeight() - 1;
60 const double w = ppm_.getWidth() - 1;
62 for (
unsigned int x = 0; x <= w; ++x)
63 for (
unsigned int y = 0; y <= h; ++y)
65 Eigen::Vector2d p = {x / w, y / h};
67 auto &c = ppm_.getPixel(x, y);
68 if (c.red == 255 && c.blue == 0 && c.green == 0)
69 mazeToAmbient(p, start);
71 else if (c.green == 255 && c.blue == 0 && c.red == 0)
72 mazeToAmbient(p, goal);
76 void function(
const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::VectorXd> out)
const override
78 Eigen::Vector3d c = {x[0], x[1], 0};
79 out[0] = (x - outer * c.normalized()).norm() - inner;
82 void jacobian(
const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::MatrixXd> out)
const override
84 const double xySquaredNorm = x[0] * x[0] + x[1] * x[1];
85 const double xyNorm = std::sqrt(xySquaredNorm);
86 const double denom = std::sqrt(x[2] * x[2] + (xyNorm - outer) * (xyNorm - outer));
87 const double c = (xyNorm - outer) * (xyNorm * xySquaredNorm) / (xySquaredNorm * xySquaredNorm * denom);
90 out(0, 2) = x[2] / denom;
93 void ambientToMaze(
const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::VectorXd> out)
const
95 Eigen::Vector3d c = {x[0], x[1], 0};
97 const double h = ppm_.getHeight();
98 const double w = ppm_.getWidth();
100 out[0] = std::atan2(x[2], c.norm() - outer) / pi2;
101 out[0] += (out[0] < 0);
103 out[1] = std::atan2(x[1], x[0]) / pi2;
104 out[1] += (out[1] < 0);
108 void mazeToAmbient(
const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::VectorXd> out)
const
110 Eigen::Vector2d a = x * pi2;
112 Eigen::Vector3d b = {std::cos(a[0]), 0, std::sin(a[0])};
116 double norm = std::sqrt(b[0] * b[0] + b[1] * b[1]);
117 out << std::cos(a[1]), std::sin(a[1]), 0;
122 bool mazePixel(
const Eigen::Ref<const Eigen::VectorXd> &x)
const
124 const double h = ppm_.getHeight();
125 const double w = ppm_.getWidth();
127 if (x[0] < 0 || x[0] >= w || x[1] < 0 || x[1] >= h)
131 return !(c.red == 0 && c.blue == 0 && c.green == 0);
137 Eigen::Vector2d coords;
138 ambientToMaze(x, coords);
140 return mazePixel(coords);
143 void dump(std::ofstream &file)
const
145 file << outer << std::endl;
146 file << inner << std::endl;
148 boost::filesystem::path path(file_);
149 file << boost::filesystem::canonical(path).string() << std::endl;
153 const std::string file_;
157 bool torusPlanningOnce(
ConstrainedProblem &cp,
enum PLANNER_TYPE planner,
bool output)
159 cp.setPlanner(planner);
166 OMPL_INFORM(
"Dumping problem information to `torus_info.txt`.");
167 std::ofstream infofile(
"torus_info.txt");
168 infofile << cp.type << std::endl;
169 dynamic_cast<TorusConstraint *
>(cp.constraint.get())->dump(infofile);
176 cp.dumpGraph(
"torus");
181 bool torusPlanningBench(
ConstrainedProblem &cp, std::vector<enum PLANNER_TYPE> &planners)
183 cp.setupBenchmark(planners,
"torus");
188 bool torusPlanning(
bool output,
enum SPACE_TYPE space, std::vector<enum PLANNER_TYPE> &planners,
190 const std::string &maze)
193 auto rvss = std::make_shared<ob::RealVectorStateSpace>(3);
196 bounds.setLow(-(outer + inner));
197 bounds.setHigh(outer + inner);
199 rvss->setBounds(bounds);
202 auto constraint = std::make_shared<TorusConstraint>(outer, inner, maze);
205 cp.setConstrainedOptions(c_opt);
206 cp.setAtlasOptions(a_opt);
208 Eigen::Vector3d start, goal;
209 constraint->getStartAndGoalStates(start, goal);
211 cp.setStartAndGoalStates(start, goal);
212 cp.ss->setStateValidityChecker(std::bind(&TorusConstraint::isValid, constraint, std::placeholders::_1));
215 return torusPlanningOnce(cp, planners[0], output);
217 return torusPlanningBench(cp, planners);
220 auto help_msg =
"Shows this help message.";
221 auto output_msg =
"Dump found solution path (if one exists) in plain text and planning graph in GraphML to "
222 "`torus_path.txt` and `torus_graph.graphml` respectively.";
223 auto bench_msg =
"Do benchmarking on provided planner list.";
224 auto outer_msg =
"Outer radius of torus.";
225 auto inner_msg =
"Inner radius of torus.";
226 auto maze_msg =
"Filename of maze image (in .ppm format) to use as obstacles on the surface of the torus.";
228 int main(
int argc,
char **argv)
231 enum SPACE_TYPE space = PJ;
232 std::vector<enum PLANNER_TYPE> planners = {RRT};
238 boost::filesystem::path path(__FILE__);
239 std::string maze = (path.parent_path() /
"mazes/thick.ppm").
string();
241 po::options_description desc(
"Options");
242 desc.add_options()(
"help,h", help_msg);
243 desc.add_options()(
"output,o", po::bool_switch(&output)->default_value(
false), output_msg);
244 desc.add_options()(
"bench", po::bool_switch(&bench)->default_value(
false), bench_msg);
245 desc.add_options()(
"outer", po::value<double>(&outer)->default_value(2), outer_msg);
246 desc.add_options()(
"inner", po::value<double>(&inner)->default_value(1), inner_msg);
247 desc.add_options()(
"maze,m", po::value<std::string>(&maze), maze_msg);
249 addSpaceOption(desc, &space);
250 addPlannerOption(desc, &planners);
251 addConstrainedOptions(desc, &c_opt);
252 addAtlasOptions(desc, &a_opt);
254 po::variables_map vm;
255 po::store(po::parse_command_line(argc, argv, desc), vm);
258 if (vm.count(
"help"))
260 std::cout << desc << std::endl;
270 return static_cast<int>(torusPlanning(output, space, planners, c_opt, a_opt, bench, outer, inner, maze));