Loading...
Searching...
No Matches
HyRRT.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/rrt/HyRRT.h"
38#include "ompl/base/Planner.h"
39#include "ompl/base/goals/GoalState.h"
40#include "ompl/base/spaces/RealVectorStateSpace.h"
41#include "ompl/tools/config/SelfConfig.h"
42#include "ompl/control/spaces/RealVectorControlSpace.h"
43
44namespace base = ompl::base;
45namespace tools = ompl::tools;
46namespace control = ompl::control;
47
48using namespace std::chrono;
49
50ompl::control::HyRRT::HyRRT(const control::SpaceInformationPtr &si_) : base::Planner(si_, "HyRRT")
51{
52 specs_.approximateSolutions = false;
53 siC_ = si_.get();
54}
55
60
62{
63 // get input states with PlannerInputStates helper, pis_
64 while (const base::State *st = pis_.nextStart())
65 {
66 auto *motion = new Motion(siC_);
67 si_->copyState(motion->state, st);
68 siC_->nullControl(motion->control);
69 motion->root = motion->state;
72 // Add start motion to the tree
73 nn_->add(motion);
74 }
75
76 if (!sampler_)
77 sampler_ = siC_->allocStateSampler();
78 if (!controlSampler_)
79 controlSampler_ = siC_->allocDirectedControlSampler();
80}
81
83{
84 sampler_->sampleUniform(randomMotion->state);
85}
86
88{
89 // Initialization
90 // Make sure the planner is configured correctly
91 // Ensures that there is at least one input state and a goal object specified
95 initTree();
96
97 // Main Planning Loop
98 // Periodically check if the termination condition is met
99 // If it is, terminate planning
100 while (!ptc())
101 {
102 nextIteration:
103
104 // Allocate memory for a random motion
105 auto *randomMotion = new Motion(siC_);
106 randomSample(randomMotion); // Randomly sample a state from the planning space
107
108 auto *solution = new Motion(siC_);
109
110 // Generate random maximum flow time
111 double random = rand();
112 double randomFlowTimeMax = random / RAND_MAX * tM_;
113 double tFlow = 0; // Tracking variable for the amount of flow time used in a given continuous simulation step
114
115 bool collision = false; // Set collision to false initially
116
117 // Sample and instantiate parent vertices and states in solutionPairs
118 auto *parentMotion = nn_->nearest(randomMotion);
119 base::State *previousState = si_->allocState();
120 si_->copyState(previousState, parentMotion->state);
121 auto *collisionParentMotion = parentMotion;
122
123 // Choose whether to begin growing the tree in the flow or jump regime
124 bool in_jump = jumpSet_(parentMotion);
125 bool in_flow = flowSet_(parentMotion);
126 bool priority = in_jump && in_flow ? random / RAND_MAX > 0.5 :
127 in_jump; // If both are true, equal chance of being in flow or jump set.
128
129 // Allocate memory for the new solutionPair
130 std::vector<base::State *> *intermediateStates = new std::vector<base::State *>;
131
132 // Fill the solutionPair with the starting vertex
133 base::State *parentState = si_->allocState();
134 si_->copyState(parentState, previousState);
135
136 control::Control *compoundControl = siC_->allocControl();
137 siC_->allocControlSampler()->sample(compoundControl);
138
139 // Simulate in either the jump or flow regime
140 if (!priority)
141 { // Flow
142 while (tFlow < randomFlowTimeMax && flowSet_(parentMotion))
143 {
144 tFlow += flowStepDuration_;
145
146 // Find new state with continuous simulation
147 base::State *intermediateState = si_->allocState();
148
149 intermediateState = this->continuousSimulator_(getFlowControl(compoundControl), previousState,
150 flowStepDuration_, intermediateState);
152 intermediateState, ompl::base::HybridStateSpace::getStateTime(previousState) + flowStepDuration_);
155
156 // Add new intermediate state to solutionPair
157 intermediateStates->push_back(intermediateState);
158
159 // Create motion to add to tree
160 auto *motion = new Motion(siC_);
161 si_->copyState(motion->state, intermediateState);
162 motion->parent = parentMotion;
163 motion->solutionPair = intermediateStates; // Set the new motion solutionPair
164
165 motion->control = compoundControl;
166
167 // Discard state if it is in the unsafe set
168 if (unsafeSet_(motion))
169 goto nextIteration;
170
171 double *collisionTime = new double(-1.0);
172 collision = collisionChecker_(motion, jumpSet_, intermediateState, collisionTime);
173
174 if (*collisionTime != -1.0)
175 {
176 ompl::base::HybridStateSpace::setStateTime(motion->state, *collisionTime);
177 ompl::base::HybridStateSpace::setStateTime(intermediateState, *collisionTime);
178 }
179
180 // State has passed all tests so update parent, solutionPair, and temporary states
181 si_->copyState(previousState, intermediateState);
182
183 // Add motion to tree or handle collision/goal
184 bool inGoalSet = pdef_->getGoal()->isSatisfied(previousState, &dist_);
185
186 // If maximum flow time has been reached, a collision has occured, or a solution has been found, exit
187 // the loop
188 if (tFlow >= randomFlowTimeMax || collision || inGoalSet)
189 {
190 if (inGoalSet)
191 {
192 solution = motion;
193 nn_->add(solution);
194 }
195 else if (collision)
196 {
197 collisionParentMotion = motion;
198 priority = true; // If collision has occurred, continue to jump regime
199 }
200 else
201 {
202 nn_->add(motion);
203 }
204 break;
205 }
206 }
207 }
208
209 if (priority)
210 { // Jump
211 // Instantiate and find new state with discrete simulator
212 base::State *newState = si_->allocState();
213 newState = this->discreteSimulator_(previousState, getJumpControl(compoundControl), newState);
214
215 // Create motion to add to tree
216 auto *motion = new Motion(siC_);
217 si_->copyState(motion->state, newState);
218
219 motion->parent = collisionParentMotion;
220 motion->control = compoundControl;
225
226 // If generated state is in the unsafe set, then continue on to the next iteration
227 if (unsafeSet_(motion))
228 goto nextIteration;
229
230 // State has passed all tests so update parent, solutionPair, and temporary states
231 si_->copyState(previousState, newState);
232
233 // Add motions to tree, and free up memory allocated to newState
234 nn_->add(motion);
235
236 // Add motion to tree or handle collision/goal
237 bool inGoalSet = pdef_->getGoal()->isSatisfied(previousState, &dist_);
238
239 if (inGoalSet)
240 {
241 solution = motion;
242 }
243 }
244
245 // If state is within goal set, construct path
246 if (pdef_->getGoal()->isSatisfied(previousState, &dist_))
247 {
248 vector<Motion *> trajectory;
249 return constructSolution(solution);
250 }
251 }
253}
255{
256 vector<Motion *> trajectory;
257 nn_->list(trajectory);
258 std::vector<Motion *> mpath;
259
260 double finalDistance = dist_;
261 Motion *solution = last_motion;
262
263 // Construct the path from the goal to the start by following the parent pointers
264 while (solution != nullptr)
265 {
266 mpath.push_back(solution);
267 solution = solution->parent;
268 }
269
270 // Create a new path object to store the solution path
271 auto path(std::make_shared<control::PathControl>(si_));
272
273 // Add the states to the path in reverse order (from start to goal)
274 for (std::size_t i = mpath.size() - 1; i < mpath.size(); --i)
275 {
276 // Append all intermediate states to the path, including starting state,
277 // excluding end vertex
278 if (mpath[i]->solutionPair != nullptr)
279 { // A jump motion does not contain an edge
280 for (unsigned int j = 0; j < mpath[i]->solutionPair->size(); j++)
281 {
282 if (i == mpath.size() - 1 && j == 0) // Starting state has no control
283 {
284 path->append(mpath[i]->solutionPair->at(j));
285 continue;
286 }
287 path->append(mpath[i]->solutionPair->at(j), mpath[i]->control,
288 siC_->getPropagationStepSize()); // Need to make a new motion to append to trajectory
289 // matrix
290 }
291 }
292 else
293 { // If a jump motion
294 if (i == mpath.size() - 1)
295 path->append(mpath[i]->state);
296 else
297 path->append(mpath[i]->state, mpath[i]->control, 0);
298 }
299 }
300
301 // Add the solution path to the problem definition
302 pdef_->addSolutionPath(path, finalDistance > 0.0, finalDistance, getName());
303 OMPL_INFORM("%s: Created %u states", getName().c_str(), nn_->size());
304
305 // Return a status indicating that an exact solution has been found
306 if (finalDistance > 0.0)
308 else
310}
311
313{
314 Planner::clear();
315 sampler_.reset();
316 controlSampler_.reset();
317 freeMemory();
318 if (nn_)
319 nn_->clear();
320}
321
323{
324 Planner::setup();
326 if (!nn_)
328 nn_->setDistanceFunction([this](const Motion *a, const Motion *b)
329 { return ompl::control::HyRRT::distanceFunc_(a->state, b->state); });
330}
331
333{
334 if (nn_)
335 {
336 std::vector<Motion *> motions;
337 nn_->list(motions);
338 for (auto &motion : motions)
339 {
340 if (motion->state)
341 si_->freeState(motion->state);
342 if (motion->control)
343 siC_->freeControl(motion->control);
344 if (motion->solutionPair)
345 {
346 for (base::State *state : *motion->solutionPair)
347 si_->freeState(state);
348 }
349 delete motion;
350 }
351 }
352}
353
355{
356 Planner::getPlannerData(data);
357
358 vector<Motion *> motions;
359 if (nn_)
360 nn_->list(motions);
361
362 if (lastGoalMotion_ != nullptr)
364
365 for (auto &motion : motions)
366 {
367 if (motion->parent == nullptr)
368 data.addStartVertex(base::PlannerDataVertex(motion->state));
369 else
370 data.addEdge(base::PlannerDataVertex(motion->parent->state), base::PlannerDataVertex(motion->state));
371 }
372}
static void setStateTime(ompl::base::State *state, double position)
Set the time position value of the given state.
static double getStateTime(const ompl::base::State *state)
The time value of the given state.
static void setStateJumps(ompl::base::State *state, int jumps)
Set the jumps value of the given state.
static int getStateJumps(const ompl::base::State *state)
The jumps value of the given state.
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition PlannerData.h:59
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
unsigned int addStartVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
unsigned int addGoalVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
virtual bool addEdge(unsigned int v1, unsigned int v2, const PlannerDataEdge &edge=PlannerDataEdge(), Cost weight=Cost(1.0))
Adds a directed edge between the given vertex indexes. An optional edge structure and weight can be s...
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
PlannerInputStates pis_
Utility class to extract valid input states.
Definition Planner.h:407
PlannerSpecs specs_
The specifications of the planner (its capabilities).
Definition Planner.h:413
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition Planner.h:404
const std::string & getName() const
Get the name of the planner.
Definition Planner.cpp:56
SpaceInformationPtr si_
The space information for which planning is done.
Definition Planner.h:401
virtual void checkValidity()
Check to see if the planner is in a working state (setup has been called, a goal was set,...
Definition Planner.cpp:106
Definition of an abstract state.
Definition State.h:50
Definition of an abstract control.
Definition Control.h:48
Representation of a motion in the search tree.
Definition HyRRT.h:100
base::State * state
The state contained by the motion.
Definition HyRRT.h:114
Motion * parent
The parent motion in the exploration tree.
Definition HyRRT.h:117
void clear() override
Clear all allocated memory.
Definition HyRRT.cpp:312
std::function< base::State *(const control::Control *u, base::State *curState, double tFlowMax, base::State *newState)> continuousSimulator_
Simulator for propagation under flow regime.
Definition HyRRT.h:403
base::PlannerStatus constructSolution(Motion *lastMotion)
Construct the path, starting at the last edge.
Definition HyRRT.cpp:254
std::function< double(base::State *state1, base::State *state2)> distanceFunc_
Compute distance between states, default is Euclidean distance.
Definition HyRRT.h:353
void randomSample(Motion *randomMotion)
Sample the random motion.
Definition HyRRT.cpp:82
void checkMandatoryParametersSet(void) const
Check if all required parameters have been set.
Definition HyRRT.h:255
Motion * lastGoalMotion_
The most recent goal motion. Used for PlannerData computation.
Definition HyRRT.h:294
void setup() override
Set the problem instance to solve.
Definition HyRRT.cpp:322
base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc) override
Function that can solve the motion planning problem. This function can be called multiple times on th...
Definition HyRRT.cpp:87
std::function< bool(Motion *motion)> jumpSet_
Function that returns true if a motion intersects with the jump set, and false if not.
Definition HyRRT.h:377
std::function< base::State *(base::State *curState, const control::Control *u, base::State *newState)> discreteSimulator_
Simulator for propagation under jump regime.
Definition HyRRT.h:370
base::StateSamplerPtr sampler_
State sampler.
Definition HyRRT.h:413
std::function< bool(Motion *motion)> flowSet_
Function that returns true if a motion intersects with the flow set, and false if not.
Definition HyRRT.h:384
std::shared_ptr< NearestNeighbors< Motion * > > nn_
A nearest-neighbors datastructure containing the tree of motions.
Definition HyRRT.h:306
~HyRRT() override
Destructor.
Definition HyRRT.cpp:56
control::SpaceInformation * siC_
The base::SpaceInformation cast as control::SpaceInformation, for convenience.
Definition HyRRT.h:345
void getPlannerData(base::PlannerData &data) const override
Get the PlannerData object associated with this planner.
Definition HyRRT.cpp:354
void setContinuousSimulator(std::function< base::State *(const control::Control *u, base::State *curState, double tFlowMax, base::State *newState)> function)
Define the continuous dynamics simulator.
Definition HyRRT.h:276
std::function< ompl::base::State *(const control::Control *control, ompl::base::State *x_cur, double tFlow, ompl::base::State *new_state)> continuousSimulator
Simulates the dynamics of the multicopter when in flow regime, with no nonnegligble forces other than...
Definition HyRRT.h:224
control::DirectedControlSamplerPtr controlSampler_
Control Sampler.
Definition HyRRT.h:342
std::function< bool(Motion *motion, std::function< bool(Motion *motion)> obstacleSet, base::State *newState, double *collisionTime)> collisionChecker_
Collision checker. Default is point-by-point collision checking using the jump set.
Definition HyRRT.h:327
double flowStepDuration_
The flow time for a given integration step, within a flow propagation step. Must be set by user.
Definition HyRRT.h:360
std::function< bool(Motion *motion)> unsafeSet_
Function that returns true if a motion intersects with the unsafe set, and false if not.
Definition HyRRT.h:391
void initTree(void)
Runs the initial setup tasks for the tree.
Definition HyRRT.cpp:61
void freeMemory()
Free the memory allocated by this planner.
Definition HyRRT.cpp:332
double tM_
The maximum flow time for a given flow propagation step. Must be set by the user.
Definition HyRRT.h:357
HyRRT(const control::SpaceInformationPtr &si)
Constructor.
Definition HyRRT.cpp:50
double dist_
Minimum distance from goal to final vertex of generated trajectories.
Definition HyRRT.h:416
This class contains methods that automatically configure various parameters for motion planning....
Definition SelfConfig.h:59
static NearestNeighbors< _T > * getDefaultNearestNeighbors(const base::Planner *planner)
Select a default nearest neighbor datastructure for the given space.
Definition SelfConfig.h:105
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
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
Includes various tools such as self config, benchmarking, etc.
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.
@ UNKNOWN
Uninitialized status.