Loading...
Searching...
No Matches
SSTPinballPlanning.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2025, University of Santa Cruz Hybrid Systems Laboratory
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 University of Santa Cruz nor the names of
18 * its 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: Beverly Xu */
36
37#include "ompl/control/planners/sst/HySST.h"
38#include "ompl/base/StateSpace.h"
39#include "ompl/control/spaces/RealVectorControlSpace.h"
40#include "ompl/base/spaces/HybridStateSpace.h"
41#include "ompl/control/ODESolver.h"
42#include "ompl/base/objectives/StateCostIntegralObjective.h"
43
44struct PinballSetup
45{
46 double paddleLength = 1;
47 double paddleWidth = 0.25;
48 std::vector<std::vector<double>> paddleCoords = {{1, -1}, {2.5, -4}, {1.5, -5}, {3, -7}, {1, -8}};
49};
50
52double distanceFunc(ompl::base::State *state1, ompl::base::State *state2)
53{
54 double dist = 0;
55 dist = hypot(state1->as<ompl::base::HybridStateSpace::StateType>()
57 ->values[0] -
60 ->values[0],
63 ->values[1] -
66 ->values[1]);
67 return fabs(dist);
68}
69
71bool inPaddle(ompl::control::HySST::Motion *motion, std::vector<double> paddleCoord)
72{
73 PinballSetup pinballSetup;
74
75 auto *motion_state =
76 motion->state->as<ompl::base::CompoundState>()->as<ompl::base::RealVectorStateSpace::StateType>(0);
77 double paddleX = paddleCoord[0];
78 double paddleY = paddleCoord[1];
79 double x1 = motion_state->values[0];
80 double x2 = motion_state->values[1];
81 double v1 = motion_state->values[2];
82 double v2 = motion_state->values[3];
83
84 bool inPaddle = false;
85
86 if ((x1 >= paddleX && x1 <= paddleX + pinballSetup.paddleLength) &&
87 (x2 >= paddleY && x2 <= paddleY + pinballSetup.paddleWidth / 2) && v2 < 0)
88 inPaddle = true; // In jump set if both velocity and position vectors are directed toward top of paddle
89 else if ((x1 >= paddleX && x1 <= paddleX + pinballSetup.paddleLength) &&
90 (x2 >= paddleY + pinballSetup.paddleWidth / 2 && x2 <= paddleY + pinballSetup.paddleWidth) && v2 > 0)
91 inPaddle = true; // In jump set if both velocity and position vectors are directed toward bottom of paddle
92 else if ((x1 >= paddleX && x1 <= paddleX + pinballSetup.paddleLength / 4) &&
93 (x2 >= paddleY && x2 <= paddleY + pinballSetup.paddleWidth) && v1 > 0)
94 inPaddle = true; // In jump set if both velocity and position vectors are directed toward left side of paddle
95 else if ((x1 >= paddleX + (3 / 4) * pinballSetup.paddleLength && x1 <= paddleX + pinballSetup.paddleLength) &&
96 (x2 >= paddleY && x2 <= paddleY + pinballSetup.paddleWidth) && v1 < 0)
97 inPaddle = true; // In jump set if both velocity and position vectors are directed toward left side of paddle
98
99 return inPaddle;
100}
101
103bool jumpSet(ompl::control::HySST::Motion *motion)
104{
105 PinballSetup pinballSetup;
106
107 auto *motion_state =
108 motion->state->as<ompl::base::CompoundState>()->as<ompl::base::RealVectorStateSpace::StateType>(0);
109 double x1 = motion_state->values[0];
110 double v1 = motion_state->values[2];
111
112 for (std::vector<double> paddleCoord : pinballSetup.paddleCoords) // If ball is in any of the paddles
113 {
114 if (inPaddle(motion, paddleCoord))
115 return true;
116 }
117
118 if ((x1 <= 0 && v1 < 0) || (x1 >= 5 && v1 > 0)) // If ball is in either side wall
119 return true;
120
121 return false;
122}
123
125bool flowSet(ompl::control::HySST::Motion *motion)
126{
127 return !jumpSet(motion);
128}
129
131bool unsafeSet(ompl::control::HySST::Motion *motion)
132{
133 PinballSetup pinballSetup;
134 auto *motion_state =
135 motion->state->as<ompl::base::CompoundState>()->as<ompl::base::RealVectorStateSpace::StateType>(0);
136 double x1 = motion_state->values[0];
137 double x2 = motion_state->values[1];
138
139 if (((x1 >= 0 && x1 < 1) || (x1 > 4 && x1 <= 5)) && x2 <= -10)
140 return true;
141
142 return false;
143}
144
147void flowODE(const ompl::control::ODESolver::StateType &x_cur, const ompl::control::Control *u,
149{
150 (void)u; // No control is applied when a state is in the flow set
151
152 // Retrieve the current orientation of the pinball.
153 const double v_1 = x_cur[2];
154 const double v_2 = x_cur[3];
155 const double a_1 = x_cur[4];
156 const double a_2 = x_cur[5];
157
158 // Ensure qdot is the same size as q. Zero out all values.
159 x_new.resize(x_cur.size(), 0);
160
161 x_new[0] = v_1;
162 x_new[1] = v_2;
163 x_new[2] = a_1;
164 x_new[3] = a_2;
165 x_new[4] = 0; // No change to acceleration
166 x_new[5] = 0; // No change to acceleration
167}
168
174ompl::base::State *discreteSimulator(ompl::base::State *x_cur, const ompl::control::Control *u,
175 ompl::base::State *new_state)
176{
177 // Retrieve control values.
179 const double u_x = control[0];
180
181 auto *state_values = x_cur->as<ompl::base::HybridStateSpace::StateType>()
182 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
183 ->values;
184 double x1 = state_values[0];
185 double x2 = state_values[1];
186 double v1 = state_values[2];
187 double v2 = state_values[3];
188 double a1 = state_values[4];
189 double a2 = state_values[5];
190
191 if ((x1 <= 0 && v1 < 0) || (x1 >= 5 && v1 > 0)) // If state is colliding with the wall
192 {
194 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
195 ->values[2] = -v1 * 0.8;
197 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
198 ->values[3] = v2;
199 }
200 else if (v2 < 0) // If state is colliding with the top of the paddle
201 {
203 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
204 ->values[2] = v1 + u_x;
206 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
207 ->values[3] = -v2 * 0.6;
208 }
209 else if (v2 > 0) // If state is colliding with the bottom of the paddle
210 {
212 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
213 ->values[2] = v1;
215 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
216 ->values[3] = -v2 * 0.6;
217 }
218 else // If state is colliding with the side of the paddle
219 {
221 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
222 ->values[2] = -(v1 * 0.6 + std::copysign(u_x, v1)); // Input is in the same direction as rebound
224 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
225 ->values[3] = v2;
226 }
227
228 // The position and acceleration doesn't change
230 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
231 ->values[0] = x1;
233 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
234 ->values[1] = x2;
236 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
237 ->values[4] = a1;
239 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
240 ->values[5] = a2;
241 return new_state;
242}
243
244// Define goal region as [1, 4] x (-inf, -10]
245class EuclideanGoalRegion : public ompl::base::Goal
246{
247public:
248 EuclideanGoalRegion(const ompl::base::SpaceInformationPtr &si) : ompl::base::Goal(si)
249 {
250 }
251
252 virtual bool isSatisfied(const ompl::base::State *st, double *distance) const
253 {
254 // perform any operations and return a truth value
255 std::vector<double> goalRegion = {1, 4, -10}; // x-min, x-max, y
256 auto *values = st->as<ompl::base::CompoundState>()->as<ompl::base::RealVectorStateSpace::StateType>(0)->values;
257
258 if (values[0] > goalRegion[1])
259 *distance = hypot(values[1] + 10, values[0] - goalRegion[1]);
260 else if (values[0] < goalRegion[0])
261 *distance = hypot(values[1] + 10, values[0] - goalRegion[0]);
262 else
263 *distance = values[1] + 10;
264
265 return values[0] >= goalRegion[0] && values[0] <= goalRegion[1] && values[1] <= goalRegion[2];
266 }
267
268 virtual bool isSatisfied(const ompl::base::State *st) const
269 {
270 double distance = -1.0;
271 return isSatisfied(st, &distance);
272 }
273};
274
275// Optimizes for lower velocity
276class VelocityObjective : public ompl::base::StateCostIntegralObjective
277{
278public:
279 VelocityObjective(const ompl::base::SpaceInformationPtr &si) : ompl::base::StateCostIntegralObjective(si, false)
280 {
281 }
282
283 // Return the magnitude of the horizontal component of the velocity vector
284 ompl::base::Cost stateCost(const ompl::base::State *s) const
285 {
286 return ompl::base::Cost(
287 -abs(s->as<ompl::base::CompoundState>()->as<ompl::base::RealVectorStateSpace::StateType>(0)->values[2]));
288 }
289
290 // Return the difference in magnitude of the horizontal components of the between the previous and current velocity
291 // vector
292 ompl::base::Cost motionCost(const ompl::base::State *s1, const ompl::base::State *s2) const
293 {
294 return ompl::base::Cost(
295 (stateCost(s1).value() + stateCost(s2).value()) * 0.5 *
297 }
298};
299
300int main()
301{
302 // Set the bounds of space
304 statespace->addDimension(0, 5);
305 statespace->addDimension(-10, 0);
306 statespace->addDimension(-10, 10);
307 statespace->addDimension(-10, 10);
308 statespace->addDimension(-10, 10);
309 statespace->addDimension(-10, 10);
310 ompl::base::StateSpacePtr stateSpacePtr(statespace);
311
312 ompl::base::HybridStateSpace *hybridSpace = new ompl::base::HybridStateSpace(stateSpacePtr);
313 ompl::base::StateSpacePtr hybridSpacePtr(hybridSpace);
314
315 // Define control space
316 ompl::control::RealVectorControlSpace *flowControlSpace =
317 new ompl::control::RealVectorControlSpace(hybridSpacePtr, 1);
318 ompl::control::RealVectorControlSpace *jumpControlSpace =
319 new ompl::control::RealVectorControlSpace(hybridSpacePtr, 1);
320
321 ompl::base::RealVectorBounds flowBounds(1);
322 flowBounds.setLow(0, 0);
323 flowBounds.setHigh(0, 0);
324 flowControlSpace->setBounds(flowBounds);
325
326 ompl::base::RealVectorBounds jumpBounds(1);
327 jumpBounds.setLow(0, -4);
328 jumpBounds.setHigh(0, 4);
329 jumpControlSpace->setBounds(jumpBounds);
330
331 ompl::control::RealVectorControlUniformSampler flowControlSampler(flowControlSpace);
332 flowControlSpace->setControlSamplerAllocator(
333 [](const ompl::control::ControlSpace *space) -> ompl::control::ControlSamplerPtr
334 { return std::make_shared<ompl::control::RealVectorControlUniformSampler>(space); });
335
337 jumpControlSpace); // Doesn't do anything because the bounds for jump input are just [0, 0], but here for
338 // demonstration
339 jumpControlSpace->setControlSamplerAllocator(
340 [](const ompl::control::ControlSpace *space) -> ompl::control::ControlSamplerPtr
341 { return std::make_shared<ompl::control::RealVectorControlUniformSampler>(space); });
342
343 ompl::control::ControlSpacePtr flowControlSpacePtr(flowControlSpace);
344 ompl::control::ControlSpacePtr jumpControlSpacePtr(jumpControlSpace);
345
347 controlSpace->addSubspace(flowControlSpacePtr);
348 controlSpace->addSubspace(jumpControlSpacePtr);
349
350 ompl::control::ControlSpacePtr controlSpacePtr(controlSpace);
351
352 // Construct a space information instance for this state space
353 ompl::control::SpaceInformationPtr si(new ompl::control::SpaceInformation(hybridSpacePtr, controlSpacePtr));
354 ompl::control::ODESolverPtr odeSolver(new ompl::control::ODEBasicSolver<>(si, &flowODE));
355
356 si->setStatePropagator(ompl::control::ODESolver::getStatePropagator(odeSolver));
357 si->setPropagationStepSize(0.01);
358 si->setup();
359
360 // Create a problem instance
361 ompl::base::ProblemDefinitionPtr pdef(new ompl::base::ProblemDefinition(si));
362
363 ompl::base::OptimizationObjectivePtr obj(new VelocityObjective(si));
364 pdef->setOptimizationObjective(obj);
365
366 std::vector<ompl::base::ScopedState<>> startStates;
367 std::vector<double> startXs = {0.5, 1, 2, 3.5, 4, 4.5};
368
369 for (std::size_t i = 0; i < startXs.size(); i++)
370 {
371 startStates.push_back(ompl::base::ScopedState<>(hybridSpacePtr));
372 startStates.back()
374 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
375 ->values[0] = startXs[i];
376 startStates.back()
378 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
379 ->values[1] = 0;
380 startStates.back()
382 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
383 ->values[2] = 0;
384 startStates.back()
386 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
387 ->values[3] = 0;
388 startStates.back()
390 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
391 ->values[4] = 0;
392 startStates.back()
394 ->as<ompl::base::RealVectorStateSpace::StateType>(0)
395 ->values[5] = -9.81;
396 pdef->addStartState(startStates.back());
397 }
398
399 // Set goal state to be on floor with a zero velocity, and tolerance of 0.1
400 std::shared_ptr<EuclideanGoalRegion> goal = std::make_shared<EuclideanGoalRegion>(si);
401
402 // Set the goal region
403 pdef->setGoal(goal);
404
405 // Create velocity optimization objective instance
406 ompl::base::OptimizationObjectivePtr velObj(new VelocityObjective(si));
407 // pdef->setOptimizationObjective(velObj);
408
409 ompl::control::HySST cHySST(si);
410
411 // Set the problem instance for our planner to solve
412 cHySST.setProblemDefinition(pdef);
413 cHySST.setup();
414
415 // Set parameters
416 cHySST.setDiscreteSimulator(discreteSimulator);
417 cHySST.setDistanceFunction(distanceFunc);
418 cHySST.setFlowSet(flowSet);
419 cHySST.setJumpSet(jumpSet);
420 cHySST.setTm(0.5);
421 cHySST.setFlowStepDuration(0.01);
422 cHySST.setUnsafeSet(unsafeSet);
423 cHySST.setSelectionRadius(0.8);
424 cHySST.setPruningRadius(0.2);
425 cHySST.setBatchSize(1);
426
427 // attempt to solve the planning problem within 30 seconds
430 double planTime = ompl::time::seconds(ompl::time::now() - t0);
431
432 if (solved) // If either approximate or exact solution has beenf ound
433 OMPL_INFORM("Solution found in %f seconds", planTime);
434 OMPL_INFORM("Solution status: %s", solved.asString().c_str());
435
436 // print path
437 pdef->getSolutionPath()->as<ompl::control::PathControl>()->printAsMatrix(std::cout);
438}
Definition of a compound state.
Definition State.h:87
Abstract definition of goals.
Definition Goal.h:63
T * as()
Cast this instance to a desired type.
Definition Goal.h:77
A state space consisting of a space and a time component.
static double getStateTime(const ompl::base::State *state)
The time value of the given state.
Definition of a problem to be solved. This includes the start state(s) for the system and a goal spec...
The lower and upper bounds for an Rn space.
double * values
The value of the actual vector in Rn.
A state space representing Rn. The distance function is the L2 norm.
void addDimension(double minBound=0.0, double maxBound=0.0)
Increase the dimensionality of the state space by 1. Optionally, bounds can be specified for this add...
Definition of a scoped state.
Definition ScopedState.h:57
Defines optimization objectives where path cost can be represented as a path integral over a cost fun...
StateCostIntegralObjective(const SpaceInformationPtr &si, bool enableMotionCostInterpolation=false)
If enableMotionCostInterpolation is set to true, then calls to motionCost() will divide the motion se...
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
A control space to allow the composition of control spaces.
virtual void addSubspace(const ControlSpacePtr &component)
Adds a control space as a component of the compound control space.
A control space representing the space of applicable controls.
void setControlSamplerAllocator(const ControlSamplerAllocator &csa)
Set the sampler allocator to use.
Definition of an abstract control.
Definition Control.h:48
const T * as() const
Cast this instance to a desired type.
Definition Control.h:64
Representation of a motion.
Definition HySST.h:80
base::State * state
The state contained by the motion.
Definition HySST.h:109
Basic solver for ordinary differential equations of the type q' = f(q, u), where q is the current sta...
Definition ODESolver.h:200
std::vector< double > StateType
Portable data type for the state values.
Definition ODESolver.h:77
static StatePropagatorPtr getStatePropagator(ODESolverPtr solver, const PostPropagationEvent &postEvent=nullptr)
Retrieve a StatePropagator object that solves a system of ordinary differential equations defined by ...
Definition ODESolver.h:127
Definition of a control path.
Definition PathControl.h:61
A control space representing Rn.
void setBounds(const base::RealVectorBounds &bounds)
Set the bounds (min max values for each dimension) for the control.
Uniform sampler for the Rn state space.
Space information containing necessary information for planning with controls. setup() needs to be ca...
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
PlannerTerminationCondition timedPlannerTerminationCondition(double duration)
Return a termination condition that will become true duration seconds in the future (wall-time).
This namespace contains sampling based planning routines used by planning under differential constrai...
Definition Control.h:45
std::chrono::system_clock::time_point point
Representation of a point in time.
Definition Time.h:52
point now()
Get the current time point.
Definition Time.h:58
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition Time.h:64
A class to store the exit status of Planner::solve().
std::string asString() const
Return a string representation.