forked from krishauser/Klampt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRealTimePlanner.h
More file actions
executable file
·334 lines (293 loc) · 11.6 KB
/
RealTimePlanner.h
File metadata and controls
executable file
·334 lines (293 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#ifndef REAL_TIME_PLANNER_H
#define REAL_TIME_PLANNER_H
#include "RobotCSpace.h"
#include "PlannerSettings.h"
#include "PlannerObjective.h"
#include "RampCSpace.h"
#include "Modeling/DynamicPath.h"
#include <KrisLibrary/utils/StatCollector.h>
#include <KrisLibrary/utils/threadutils.h>
#include <KrisLibrary/Timer.h>
class MotionQueueInterface;
/** @brief A base class for a motion planner that generates dynamic paths.
* The output should always respect joint, velocity, and acceleration limits
* and end in a zero-velocity terminal states.
*
* Important note: the objects pointed to by CSpace* space, Robot *robot,
* and WorldPlannerSettings *settings must be owned by an outside source.
* Make sure to pass a SmartPointer to SetGoal if you are going to use the
* objective elsewhere.
*/
class DynamicMotionPlannerBase
{
public:
enum { Failure=0, Success=1, Timeout=2 };
DynamicMotionPlannerBase();
virtual ~DynamicMotionPlannerBase();
virtual void Init(CSpace* space,Robot* robot,WorldPlannerSettings* settings);
virtual void SetGoal(SmartPointer<PlannerObjectiveBase> newgoal);
virtual void SetTime(Real tstart);
virtual void SetDefaultLimits();
virtual void SetLimits(Real qScale=1.0,Real vScale=1.0,Real aScale=1.0);
bool LogBegin(const char* fn="realtimeplanner.log");
bool LogEnd();
/** DynamicMotionPlanner subclasses should override this.
* Plans from the start of 'path', and returns the result in 'path'.
* The planning duration should not exceed 'cutoff', if possible.
*
* Must return Success if successful, Failure if planning fails.
*
* Subroutines may maintain a timer, and stop and return Timeout
* planning time exceeds the cutoff. This is not strictly necessary;
* the caller should check whether the planning time exceeds the cutoff.
*/
virtual int PlanFrom(ParabolicRamp::DynamicPath& path,Real cutoff)
{
return Failure;
}
///Plans from a start configuration
int PlanFrom(const Config& qstart,Real cutoff,ParabolicRamp::DynamicPath& result)
{
result.ramps.resize(1);
result.ramps[0].SetConstant(qstart);
return PlanFrom(result,cutoff);
}
/** Tells the planner to stop. Can be called from an external thread.
*
* Subclasses should detect if stopPlanning is set to true and return
* Timeout.
*/
bool StopPlanning();
///Performs shortcutting up until the time limit
int Shortcut(ParabolicRamp::DynamicPath& path,Real timeLimit);
///Performs shortcuts that reduce the objective function, only on the
///portion of the path after time tstart
int SmartShortcut(Real tstart,ParabolicRamp::DynamicPath& path,Real timeLimit);
///Helper
bool GetMilestoneRamp(const Config& q0,const Vector& dq0,const Config& q1,ParabolicRamp::DynamicPath& ramp) const;
///Helper
bool GetMilestoneRamp(const ParabolicRamp::DynamicPath& curPath,const Config& q,ParabolicRamp::DynamicPath& ramp) const;
//returns true if the ramp from the current config to q is collision free
bool CheckMilestoneRamp(const ParabolicRamp::DynamicPath& curPath,const Config& q,ParabolicRamp::DynamicPath& ramp) const;
///returns the cost of going straight to q (assuming no collision detection)
Real EvaluateDirectPathCost(const ParabolicRamp::DynamicPath& curPath,const Config& q);
///returns the cost of using the given path. If tStart is not given, this
///uses the previously set tstart
Real EvaluatePathCost(const ParabolicRamp::DynamicPath& path,Real tStart=-1.0);
///returns the terminal cost for a path ending at q at time tEnd
Real EvaluateTerminalCost(const Config& q,Real tEnd);
Robot* robot;
WorldPlannerSettings* settings;
CSpace* cspace;
//objective function
SmartPointer<PlannerObjectiveBase> goal;
//configuration, velocity, and acceleration limits
ParabolicRamp::Vector qMin,qMax,velMax,accMax;
//planning start time
Real tstart;
//flag: another thread may set this to true when the planner should
//stop during a long planning cycle. The subclass needs to continually
//monitor this flag
bool stopPlanning;
//log file
FILE* flog;
};
/** @brief A base class for the path sending callback. Send is called by the
* planner to update the path used by the execution thread.
*/
class SendPathCallbackBase
{
public:
virtual ~SendPathCallbackBase() {}
/** Splice in path at time tcut, where tcut is relative to the time at
* which planning was initiated, and tplanstart is the time the planning
* started.
*
* If the update isn't received at a valid time < tplanstart + tcut, then
* Send should return false.
*/
virtual bool Send(Real tplanstart,Real tcut,const ParabolicRamp::DynamicPath& path) =0;
};
/** @ingroup Planning
* @brief A real-time planner. Supports constant time-stepping or
* adaptive time-stepping
*
* Users must set up the underlying planner, the planning
* problem and may add in path sending hooks.
* @sa DynamicMotionPlannerBase
* @sa SendPathCallbackBase
* @sa RealTimePlanningThread
*
* Setup: Given a world and a robotIndex (typically 0), assuming the robot
* model is set to its current configuration ...
*
* @code
* RealTimePlanner planner;
* planner.planner = new MyDynamicMotionPlanner;
* (try, for example, DynamicIKPlanner, DynamicHybridTreePlanner)
* SingleRobotCSpace cspace(...); (initialize cspace as necessary)
* //set planning problem
* planner.SetSpace(&cspace);
* //set current path -- just stationary
* planner.SetConstantPath(planner.robot->q);
* //set objective
* IKObjective* objective = new IKObjective(robot);
* objective->ikGoal = ... // setup initial IK objective
* planner.Reset(objective);
* planner.sendPathCallback = new MySendPathCallback;
* @endcode
*
* Online: The planner should be launched in a parallel thread to the
* execution thread. The planning thread should maintain a global timer,
* synchronized with the execution thread.
*
* @code
* while(true) {
* Real t = currentGlobalTime();
* if(objectiveChanged) {
* //set new objective if needed
* IKObjective* objective = new IKObjective(robot);
* objective->ikGoal = ... // setup IK objective
* planner.Reset(objective);
* }
*
* Real splitTime, planTime;
* bool changed = planner.PlanUpdate(t,splitTime,planTime);
* //planner.sendPathCallback will be called if the planner succeeded
*
* if(changed) {
* printf("Path was successfully updated, split time %g, plan time %g\n",splitTime,planTime);
* }
* else {
* printf("Path update was unsuccessful, split time %g, plan time %g\n",splitTime,planTime);
* }
* }
* @endcode
*/
class RealTimePlanner
{
public:
RealTimePlanner();
virtual ~RealTimePlanner();
///Convenience fn: will set up the planner's robot, space, settings pointers
void SetSpace(SingleRobotCSpace* space);
///Should be called at the start to initialize the start configuration
void SetConstantPath(const Config& q);
///If the robot's path has changed for a reason outside of the planner's
///control, call this before planning
void SetCurrentPath(Real tglobal,const ParabolicRamp::DynamicPath& path);
/// Set the objective function.
virtual void Reset(SmartPointer<PlannerObjectiveBase> newgoal);
/// Gets the objective function
SmartPointer<PlannerObjectiveBase> Objective() const;
/** Calls the planner, returns the splitting time and planning time.
* tglobal is a global clock synchronized between the planning and
* execution threads.
* Default implementation: fix the split time, call planner->PlanFrom
* Returns true if the path changed and planTime < splitTime
*/
virtual bool PlanUpdate(Real tglobal,Real& splitTime,Real& planTime);
/// Tells the planner to stop, when called from an external thread.
bool StopPlanning() {
if(!planner) return true;
return planner->StopPlanning();
}
/// Called whenever the sendPathCallback returned false on a planned path
/// (e.g., the padding was too short)
virtual void MarkSendFailure();
/// Users must set these members before planning
/// The underlying planing algorithm
SmartPointer<DynamicMotionPlannerBase> planner;
/// Set the current path before planning, using SetConstantPath or SetCurrentPath
Real pathStartTime;
ParabolicRamp::DynamicPath currentPath;
/// Users should set this up to capture the outputted path
SmartPointer<SendPathCallbackBase> sendPathCallback;
/// Use only in simulation: multiplies the effective computational power
/// of this machine (i.e., simulate a machine that's x times faster)
Real cognitiveMultiplier;
/// Use only in simulation: set to true if you want to allow PlanFrom
/// instances that overrun their alloted time to still update the path.
bool acceptTimeOverruns;
enum SplitUpdateProtocol { Constant, ExponentialBackoff, Learning };
SplitUpdateProtocol protocol;
Real currentSplitTime,currentPadding,currentExternalPadding;
Real maxPadding;
///Statistics captured on planning times, depending on PlanMore output.
StatCollector planFailTimeStats,planSuccessTimeStats,planTimeoutTimeStats;
};
/** @brief An interface to a planning thread.
*
* All methods are thread-safe and meant to be called by the execution
* thread.
*
* Example code is as follows:
*
* MotionQueueInterface* queue = new MyMotionQueueInterface(robot);
*
* RealTimePlanningThread thread;
* thread.SetPlanner(planner)
* thread.SetStartConfig(qstart);
* thread.Start();
* while(want to continue planning) {
* if(newObjectiveAvailable()) {
* thread.BreakPlanning(); //optional, if this is not called the
* //planner will finish an internal planning
* //cycle on the old objective
* thread.SetObjective(getObjective());
* //change the cspace or planner here if needed
* }
* if(thread.SendUpdate(queue)) {
* printf("Planner had an update\n")
* }
* ///advance the motion queue and do whatever else you need to do here...
* }
* thread.Stop()
*/
class RealTimePlanningThread
{
public:
RealTimePlanningThread();
~RealTimePlanningThread();
/// Initializes the planning thread with a start configuration
void SetStartConfig(const Config& qstart);
/// Initializes the planning thread with a CSpace
void SetCSpace(SingleRobotCSpace* space);
/// Sets the planner
void SetPlanner(const SmartPointer<DynamicMotionPlannerBase>& planner);
void SetPlanner(const SmartPointer<RealTimePlanner>& planner);
/// Set the objective function.
void SetObjective(SmartPointer<PlannerObjectiveBase> newgoal);
/// Gets the objective function. (You must not delete the pointer or assign
/// it to a SmartPointer)
PlannerObjectiveBase* GetObjective() const;
///If the robot's path has changed for a reason outside of the planner's
///control, call this
void ResetCurrentPath(Real tglobal,const ParabolicRamp::DynamicPath& path);
/// Starts planning. Returns false if there was some problem, e.g., the
/// Initial config was not set.
bool Start();
/// Stops the planning thread.
void Stop();
/// Returns true if a planning cycle is happening
bool IsPlanning();
/// Pauses planning after the current planning cycle
void PausePlanning();
/// Breaks an internal planning cycle on the existing objective
void BreakPlanning();
/// Stops planning. Equivalent to break and pause
void StopPlanning();
/// Resumes planning after a pause or stop
void ResumePlanning();
/// Returns true if a trajectory update is available
bool HasUpdate();
/// Send the trajectory update, if one exists
bool SendUpdate(MotionQueueInterface* interface);
/// Returns the objective function value of the current trajectory
/// update
Real ObjectiveValue();
void* internal;
SmartPointer<RealTimePlanner> planner;
Thread thread;
};
#endif