Loading...
Searching...
No Matches
LTLWithTriangulation.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2013, 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: Matt Maly */
36
37#include <ompl/control/SpaceInformation.h>
38#include <ompl/base/spaces/SE2StateSpace.h>
39#include <ompl/control/spaces/RealVectorControlSpace.h>
40#include <ompl/control/SimpleSetup.h>
41#include <ompl/config.h>
42#include <iostream>
43#include <vector>
44
45#include <ompl/extensions/triangle/PropositionalTriangularDecomposition.h>
46#include <ompl/control/planners/ltl/PropositionalDecomposition.h>
47#include <ompl/control/planners/ltl/Automaton.h>
48#include <ompl/control/planners/ltl/ProductGraph.h>
49#include <ompl/control/planners/ltl/LTLPlanner.h>
50#include <ompl/control/planners/ltl/LTLProblemDefinition.h>
51
52namespace ob = ompl::base;
53namespace oc = ompl::control;
54
55using Polygon = oc::PropositionalTriangularDecomposition::Polygon;
56using Vertex = oc::PropositionalTriangularDecomposition::Vertex;
57
58// a decomposition is only needed for SyclopRRT and SyclopEST
59// use TriangularDecomp
60class MyDecomposition : public oc::PropositionalTriangularDecomposition
61{
62public:
63 MyDecomposition(const ob::RealVectorBounds &bounds) : oc::PropositionalTriangularDecomposition(bounds)
64 {
65 }
66 ~MyDecomposition() override = default;
67
68 void project(const ob::State *s, std::vector<double> &coord) const override
69 {
70 coord.resize(2);
71 coord[0] = s->as<ob::SE2StateSpace::StateType>()->getX();
72 coord[1] = s->as<ob::SE2StateSpace::StateType>()->getY();
73 }
74
75 void sampleFullState(const ob::StateSamplerPtr &sampler, const std::vector<double> &coord,
76 ob::State *s) const override
77 {
78 sampler->sampleUniform(s);
79 auto *ws = s->as<ob::SE2StateSpace::StateType>();
80 ws->setXY(coord[0], coord[1]);
81 }
82};
83
84void addObstaclesAndPropositions(std::shared_ptr<oc::PropositionalTriangularDecomposition> &decomp)
85{
86 Polygon obstacle(4);
87 obstacle.pts[0] = Vertex(0., .9);
88 obstacle.pts[1] = Vertex(1.1, .9);
89 obstacle.pts[2] = Vertex(1.1, 1.1);
90 obstacle.pts[3] = Vertex(0., 1.1);
91 decomp->addHole(obstacle);
92
93 Polygon p0(4);
94 p0.pts[0] = Vertex(.9, .3);
95 p0.pts[1] = Vertex(1.1, .3);
96 p0.pts[2] = Vertex(1.1, .5);
97 p0.pts[3] = Vertex(.9, .5);
98 decomp->addProposition(p0);
99
100 Polygon p1(4);
101 p1.pts[0] = Vertex(1.5, 1.6);
102 p1.pts[1] = Vertex(1.6, 1.6);
103 p1.pts[2] = Vertex(1.6, 1.7);
104 p1.pts[3] = Vertex(1.5, 1.7);
105 decomp->addProposition(p1);
106
107 Polygon p2(4);
108 p2.pts[0] = Vertex(.2, 1.7);
109 p2.pts[1] = Vertex(.3, 1.7);
110 p2.pts[2] = Vertex(.3, 1.8);
111 p2.pts[3] = Vertex(.2, 1.8);
112 decomp->addProposition(p2);
113}
114
115/* Returns whether a point (x,y) is within a given polygon.
116 We are assuming that the polygon is a axis-aligned rectangle, with vertices stored
117 in counter-clockwise order, beginning with the bottom-left vertex. */
118bool polyContains(const Polygon &poly, double x, double y)
119{
120 return x >= poly.pts[0].x && x <= poly.pts[2].x && y >= poly.pts[0].y && y <= poly.pts[2].y;
121}
122
123/* Our state validity checker queries the decomposition for its obstacles,
124 and checks for collisions against them.
125 This is to prevent us from having to redefine the obstacles in multiple places. */
126bool isStateValid(const oc::SpaceInformation *si,
127 const std::shared_ptr<oc::PropositionalTriangularDecomposition> &decomp, const ob::State *state)
128{
129 if (!si->satisfiesBounds(state))
130 return false;
131 const auto *se2 = state->as<ob::SE2StateSpace::StateType>();
132
133 double x = se2->getX();
134 double y = se2->getY();
135 const std::vector<Polygon> &obstacles = decomp->getHoles();
136 for (const auto &obstacle : obstacles)
137 {
138 if (polyContains(obstacle, x, y))
139 return false;
140 }
141 return true;
142}
143
144void propagate(const ob::State *start, const oc::Control *control, const double duration, ob::State *result)
145{
146 const auto *se2 = start->as<ob::SE2StateSpace::StateType>();
147 const auto *rctrl = control->as<oc::RealVectorControlSpace::ControlType>();
148
149 double xout = se2->getX() + rctrl->values[0] * duration * cos(se2->getYaw());
150 double yout = se2->getY() + rctrl->values[0] * duration * sin(se2->getYaw());
151 double yawout = se2->getYaw() + rctrl->values[1];
152
153 auto *se2out = result->as<ob::SE2StateSpace::StateType>();
154 se2out->setXY(xout, yout);
155 se2out->setYaw(yawout);
156
157 auto *so2out = se2out->as<ob::SO2StateSpace::StateType>(1);
159 SO2.enforceBounds(so2out);
160}
161
162void plan()
163{
164 // construct the state space we are planning in
165 auto space(std::make_shared<ob::SE2StateSpace>());
166
167 // set the bounds for the R^2 part of SE(2)
168 ob::RealVectorBounds bounds(2);
169 bounds.setLow(0);
170 bounds.setHigh(2);
171
172 space->setBounds(bounds);
173
174 // create triangulation that ignores obstacle and respects propositions
175 std::shared_ptr<oc::PropositionalTriangularDecomposition> ptd = std::make_shared<MyDecomposition>(bounds);
176 // helper method that adds an obstacle, as well as three propositions p0,p1,p2
177 addObstaclesAndPropositions(ptd);
178 ptd->setup();
179
180 // create a control space
181 auto cspace(std::make_shared<oc::RealVectorControlSpace>(space, 2));
182
183 // set the bounds for the control space
184 ob::RealVectorBounds cbounds(2);
185 cbounds.setLow(-.5);
186 cbounds.setHigh(.5);
187
188 cspace->setBounds(cbounds);
189
190 oc::SpaceInformationPtr si(std::make_shared<oc::SpaceInformation>(space, cspace));
191 si->setStateValidityChecker([&si, ptd](const ob::State *state) { return isStateValid(si.get(), ptd, state); });
192 si->setStatePropagator(propagate);
193 si->setPropagationStepSize(0.025);
194
195 // LTL co-safety sequencing formula: visit p2,p0 in that order
196#if OMPL_HAVE_SPOT
197 // This shows off the capability to construct an automaton from LTL-cosafe formula using Spot
198 auto cosafety = std::make_shared<oc::Automaton>(3, "! p0 U ((p2 & !p0) & XF p0)");
199#else
200 auto cosafety = oc::Automaton::SequenceAutomaton(3, {2, 0});
201#endif
202 // LTL safety avoidance formula: never visit p1
203#if OMPL_HAVE_SPOT
204 // This shows off the capability to construct an automaton from LTL-safe formula using Spot
205 auto safety = std::make_shared<oc::Automaton>(3, "G ! p1", false);
206#else
207 auto safety = oc::Automaton::AvoidanceAutomaton(3, {1});
208#endif
209
210 // construct product graph (propDecomp x A_{cosafety} x A_{safety})
211 auto product(std::make_shared<oc::ProductGraph>(ptd, cosafety, safety));
212
213 // LTLSpaceInformation creates a hybrid space of robot state space x product graph.
214 // It takes the validity checker from SpaceInformation and expands it to one that also
215 // rejects any hybrid state containing rejecting automaton states.
216 // It takes the state propagator from SpaceInformation and expands it to one that
217 // follows continuous propagation with setting the next decomposition region
218 // and automaton states accordingly.
219 //
220 // The robot state space, given by SpaceInformation, is referred to as the "lower space".
221 auto ltlsi(std::make_shared<oc::LTLSpaceInformation>(si, product));
222
223 // LTLProblemDefinition creates a goal in hybrid space, corresponding to any
224 // state in which both automata are accepting
225 auto pdef(std::make_shared<oc::LTLProblemDefinition>(ltlsi));
226
227 // create a start state
229 start->setX(0.2);
230 start->setY(0.2);
231 start->setYaw(0.0);
232
233 // addLowerStartState accepts a state in lower space, expands it to its
234 // corresponding hybrid state (decomposition region containing the state, and
235 // starting states in both automata), and adds that as an official start state.
236 pdef->addLowerStartState(start.get());
237
238 // LTL planner (input: LTL space information, product automaton)
239 oc::LTLPlanner ltlPlanner(ltlsi, product);
240 ltlPlanner.setProblemDefinition(pdef);
241
242 // attempt to solve the problem within thirty seconds of planning time
243 // considering the above cosafety/safety automata, a solution path is any
244 // path that visits p2 followed by p0 while avoiding obstacles and avoiding p1.
245 ob::PlannerStatus solved = ltlPlanner.ob::Planner::solve(30.0);
246
247 if (solved)
248 {
249 std::cout << "Found solution:" << std::endl;
250 // The path returned by LTLProblemDefinition is through hybrid space.
251 // getLowerSolutionPath() projects it down into the original robot state space
252 // that we handed to LTLSpaceInformation.
253 static_cast<oc::PathControl &>(*pdef->getLowerSolutionPath()).printAsMatrix(std::cout);
254 }
255 else
256 std::cout << "No solution found" << std::endl;
257}
258
259int main(int /*argc*/, char ** /*argv*/)
260{
261 plan();
262 return 0;
263}
The lower and upper bounds for an Rn space.
A state in SE(2): (x, y, yaw).
void setXY(double x, double y)
Set the X and Y components of the state.
The definition of a state in SO(2).
A state space representing SO(2). The distance function and interpolation take into account angle wra...
void enforceBounds(State *state) const override
Normalize the value of the state to the interval [-Pi, Pi).
Definition of a scoped state.
Definition ScopedState.h:57
ompl::base::State StateType
Define the type of state allocated by this space.
Definition StateSpace.h:78
Definition of an abstract state.
Definition State.h:50
const T * as() const
Cast this instance to a desired type.
Definition State.h:66
static AutomatonPtr AvoidanceAutomaton(unsigned int numProps, const std::vector< unsigned int > &avoidProps)
Returns an avoidance automaton, which rejects when any one of the given list of propositions becomes ...
static AutomatonPtr SequenceAutomaton(unsigned int numProps, const std::vector< unsigned int > &seqProps)
Helper function to return a sequence automaton. Assumes all propositions are mutually exclusive.
Definition of an abstract control.
Definition Control.h:48
A planner for generating system trajectories to satisfy a logical specification given by an automaton...
Definition LTLPlanner.h:59
Definition of a control path.
Definition PathControl.h:61
virtual void printAsMatrix(std::ostream &out) const
Print the path as a real-valued matrix where the i-th row represents the i-th state along the path,...
A PropositionalTriangularDecomposition is a triangulation that ignores obstacles and respects proposi...
PropositionalTriangularDecomposition(const base::RealVectorBounds &bounds, const std::vector< Polygon > &holes=std::vector< Polygon >(), const std::vector< Polygon > &props=std::vector< Polygon >())
Creates a PropositionalTriangularDecomposition over the given bounds, which must be 2-dimensional....
Space information containing necessary information for planning with controls. setup() needs to be ca...
This namespace contains sampling based planning routines shared by both planning under geometric cons...
This namespace contains sampling based planning routines used by planning under differential constrai...
Definition Control.h:45
std::chrono::system_clock::duration duration
Representation of a time duration.
Definition Time.h:55
A class to store the exit status of Planner::solve().