Skip to content

Commit 353d054

Browse files
committed
Merge pull request #131 from personalrobotics/feature/herbpy_issue22
Feature/herbpy issue22
2 parents 7c19f04 + d520f62 commit 353d054

File tree

3 files changed

+115
-95
lines changed

3 files changed

+115
-95
lines changed

src/prpy/base/mobilebase.py

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import copy, functools, numpy, openravepy
3232
from .. import bind
3333
from prpy.clone import Clone
34-
from ..planning.retimer import OpenRAVEAffineRetimer
3534

3635
def create_affine_trajectory(robot, poses):
3736
doft = openravepy.DOFAffine.X | openravepy.DOFAffine.Y | openravepy.DOFAffine.RotationAxis
@@ -51,8 +50,6 @@ def __init__(self, sim, robot):
5150
self.simulated = sim
5251
self.robot = robot
5352

54-
self.retimer = OpenRAVEAffineRetimer()
55-
5653
def __dir__(self):
5754
# Add planning methods to the tab-completion list.
5855
method_names = set(self.__dict__.keys())
@@ -95,11 +92,11 @@ def Forward(self, meters, execute=True, direction=None, **kw_args):
9592
offset_pose[0:3, 3] = meters * direction
9693
goal_pose = numpy.dot(start_pose, offset_pose)
9794

98-
traj = create_affine_trajectory(self.robot, [ start_pose, goal_pose ])
95+
path = create_affine_trajectory(self.robot, [ start_pose, goal_pose ])
9996
if execute:
100-
return self.ExecuteBasePath(traj, **kw_args)
97+
return self.robot.ExecutePath(path, **kw_args)
10198
else:
102-
return traj
99+
return path
103100
else:
104101
raise NotImplementedError('DriveForward is not implemented')
105102

@@ -116,11 +113,11 @@ def Rotate(self, angle_rad, execute=True, **kw_args):
116113
relative_pose = openravepy.matrixFromAxisAngle([ 0., 0., angle_rad ])
117114
goal_pose = numpy.dot(start_pose, relative_pose)
118115

119-
traj = create_affine_trajectory(self.robot, [ start_pose, goal_pose ])
116+
path = create_affine_trajectory(self.robot, [ start_pose, goal_pose ])
120117
if execute:
121-
return self.ExecuteBasePath(traj, **kw_args)
118+
return self.robot.ExecutePath(path, **kw_args)
122119
else:
123-
return traj
120+
return path
124121
else:
125122
raise NotImplementedError('Rotate is not implemented')
126123

@@ -147,6 +144,7 @@ def DriveStraightUntilForce(self, direction, velocity=0.1, force_threshold=3.0,
147144
raise NotImplementedError('DriveStraightUntilForce is not implemented')
148145

149146
def _BasePlanWrapper(self, planning_method, args, kw_args):
147+
150148
robot = self.robot
151149
with Clone(robot.GetEnv()) as cloned_env:
152150
cloned_robot = cloned_env.Cloned(robot)
@@ -169,41 +167,7 @@ def _BasePlanWrapper(self, planning_method, args, kw_args):
169167

170168
# Optionally execute the trajectory.
171169
if 'execute' not in kw_args or kw_args['execute']:
172-
return self.ExecuteBasePath(traj, **kw_args)
170+
return self.robot.ExecutePath(traj, **kw_args)
173171
else:
174172
return traj
175-
176-
def ExecuteBasePath(self, path, defer=False, **kwargs):
177-
178-
def do_execute(path, **kwargs):
179-
robot = self.robot
180-
with Clone(robot.GetEnv()) as cloned_env:
181-
182-
cloned_robot = cloned_env.Cloned(robot)
183-
cloned_robot.SetActiveDOFs(
184-
[],
185-
affine=(openravepy.DOFAffine.X |
186-
openravepy.DOFAffine.Y |
187-
openravepy.DOFAffine.RotationAxis)
188-
)
189-
190-
cloned_timed_traj = self.retimer.RetimeTrajectory(cloned_robot, path, defer=False, **kwargs)
191-
192-
# Copy the trajectory back to the original environment.
193-
from ..util import CopyTrajectory
194-
timed_traj = CopyTrajectory(cloned_timed_traj, env=robot.GetEnv())
195-
196-
return robot.ExecuteTrajectory(timed_traj, defer=False, **kwargs)
197-
198-
if defer:
199-
from trollius.executor import get_default_executor
200-
from trollius.futures import wrap_future
201-
202-
executor = kwargs.get('executor') or get_default_executor()
203-
return wrap_future(
204-
executor.submit(do_execute,
205-
path, **kwargs
206-
)
207-
)
208-
else:
209-
return do_execute(path, **kwargs)
173+

src/prpy/base/robot.py

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from ..tsr.tsrlibrary import TSRLibrary
3636
from ..planning.base import Sequence
3737
from ..planning.ompl import OMPLSimplifier
38-
from ..planning.retimer import HauserParabolicSmoother, ParabolicRetimer
38+
from ..planning.retimer import HauserParabolicSmoother, OpenRAVEAffineRetimer, ParabolicRetimer
3939
from ..planning.mac_smoother import MacSmoother
4040

4141
logger = logging.getLogger('robot')
@@ -73,6 +73,7 @@ def __init__(self, robot_name=None):
7373
HauserParabolicSmoother(),
7474
self.retimer
7575
)
76+
self.affine_retimer = OpenRAVEAffineRetimer()
7677

7778
def __dir__(self):
7879
# We have to manually perform a lookup in InstanceDeduplicator because
@@ -186,45 +187,51 @@ def GetTrajectoryManipulators(self, traj):
186187
def PostProcessPath(self, path, defer=False, executor=None,
187188
constrained=None, smooth=None, default_timelimit=0.5,
188189
shortcut_options=None, smoothing_options=None,
189-
retiming_options=None, **kwargs):
190+
retiming_options=None, affine_retiming_options=None,
191+
**kwargs):
190192
""" Post-process a geometric path to prepare it for execution.
191193
192194
This method post-processes a geometric path by (optionally) optimizing
193-
it and timing it. Three different post-processing pipelines are used:
194-
195-
1. For constrained trajectories, we do not modify the geometric path
196-
and retime the path to be time-optimal. This trajectory must stop
197-
at every waypoint. The only exception is for...
198-
2. For smooth trajectories, we attempt to fit a time-optimal smooth
195+
it and timing it. Four different post-processing pipelines are used:
196+
197+
1. For base trajectories (i..e affine DOFs), we time the trajectory
198+
using self.affine_retimer. This function does not currently support
199+
trajectories that contain both regular and affine DOFs.
200+
2. For constrained trajectories, we do not modify the geometric path
201+
and retime the path to be time-optimal via self.retimer. This
202+
trajectory must stop at every waypoint. The only exception is for...
203+
3. For smooth trajectories, we attempt to fit a time-optimal smooth
199204
curve through the waypoints (not implemented). If this curve is
200205
not collision free, then we fall back on...
201-
3. By default, we run a smoother that jointly times and smooths the
202-
path. This algorithm can change the geometric path to optimize
203-
runtime.
206+
4. By default, we run a smoother that jointly times and smooths the
207+
path via self.smoother. This algorithm can change the geometric path to
208+
optimize runtime.
204209
205-
The behavior in (1) and (2) can be forced by passing constrained=True
210+
The behavior in (2) and (3) can be forced by passing constrained=True
206211
or smooth=True. By default, the case is inferred by the tag(s) attached
207212
to the trajectory: (1) is triggered by the CONSTRAINED tag and (2) is
208213
tiggered by the SMOOTH tag.
209214
210215
Options an be passed to each post-processing routine using the
211-
shortcut-options, smoothing_options, and retiming_options **kwargs
212-
dictionaries. If no "timelimit" is specified in any of these
213-
dictionaries, it defaults to default_timelimit seconds.
216+
affine_retiming_options, shortcut_options, smoothing_options, and
217+
retiming_options **kwargs dictionaries. If no "timelimit" is specified
218+
in any of these dictionaries, it defaults to default_timelimit seconds.
214219
215220
@param path un-timed OpenRAVE trajectory
216221
@param defer return immediately with a future trajectory
217222
@param executor executor to use when defer = True
218223
@param constrained the path is constrained; do not change it
219224
@param smooth the path is smooth; attempt to execute it directly
220225
@param default_timelimit timelimit for all operations, if not set
221-
@param shortcut_options kwargs to ShortcutPath for shortcutting
222-
@param smoothing_options kwargs to RetimeTrajectory for smoothing
223-
@param retiming_options kwargs to RetimeTrajectory for timing
226+
@param shortcut_options kwargs to self.simplifier
227+
@param smoothing_options kwargs to self.smoother
228+
@param retiming_options kwargs to self.retimer
229+
@param affine_retiming_options kwargs to self.affine_retimer
224230
@return trajectory ready for execution
225231
"""
226232
from ..planning.base import Tags
227233
from ..util import GetTrajectoryTags, CopyTrajectory
234+
from openravepy import DOFAffine
228235

229236
# Default parameters.
230237
if shortcut_options is None:
@@ -233,10 +240,13 @@ def PostProcessPath(self, path, defer=False, executor=None,
233240
smoothing_options = dict()
234241
if retiming_options is None:
235242
retiming_options = dict()
243+
if affine_retiming_options is None:
244+
affine_retimer_options = dict()
236245

237246
shortcut_options.setdefault('timelimit', default_timelimit)
238247
smoothing_options.setdefault('timelimit', default_timelimit)
239248
retiming_options.setdefault('timelimit', default_timelimit)
249+
affine_retimer_options.setdefault('timelimit', default_timelimit)
240250

241251
# Read default parameters from the trajectory's tags.
242252
tags = GetTrajectoryTags(path)
@@ -259,51 +269,75 @@ def do_postprocess():
259269
# in the trajectory as active.
260270
env = path.GetEnv()
261271
cspec = path.GetConfigurationSpecification()
272+
262273
used_bodies = cspec.ExtractUsedBodies(env)
263274
if self not in used_bodies:
264275
raise ValueError(
265276
'Robot "{:s}" is not in the trajectory.'.format(
266277
self.GetName()))
267278

279+
# Extract active DOFs from teh trajectory and set them as active.
268280
dof_indices, _ = cspec.ExtractUsedIndices(self)
269-
cloned_robot.SetActiveDOFs(dof_indices)
270-
logger.debug(
271-
'Setting robot "%s" DOFs %s as active for post-processing.',
272-
cloned_robot.GetName(), list(dof_indices))
273-
274-
# TODO: Handle a affine DOF trajectories for the base.
275281

276-
# Directly compute a timing of smooth trajectories.
277-
if smooth:
282+
if prpy.util.HasAffineDOFs(cspec):
283+
affine_dofs = (DOFAffine.X | DOFAffine.Y | DOFAffine.RotationAxis)
284+
285+
# Bug in OpenRAVE ExtractUsedIndices function makes
286+
# dof_indices = affine_dofs. Temporary workaround for that bug.
287+
dof_indices = []
278288
logger.warning(
279-
'Post-processing smooth paths is not supported.'
280-
' Using the default post-processing logic; this may'
281-
' significantly change the geometric path.'
289+
'Trajectory contains affine DOFs. Any regular DOFs'
290+
' will be ignored.'
282291
)
292+
else:
293+
affine_dofs = 0
294+
295+
cloned_robot.SetActiveDOFs(dof_indices, affine_dofs)
296+
logger.debug(
297+
'Setting robot "%s" DOFs %s (affine? %d) as active for'
298+
' post-processing.',
299+
cloned_robot.GetName(), list(dof_indices), affine_dofs
300+
)
283301

284-
# The trajectory is constrained. Retime it without changing the
285-
# geometric path.
286-
if constrained:
287-
logger.debug('Retiming a constrained path. The output'
288-
' trajectory will stop at every waypoint.')
289-
traj = self.retimer.RetimeTrajectory(
290-
cloned_robot, path, defer=False, **retiming_options)
302+
if len(dof_indices) and affine_dofs:
303+
raise ValueError(
304+
'Trajectory contains both affine and regular DOFs.')
305+
# Special case for timing affine-only trajectories.
306+
elif affine_dofs:
307+
traj = self.affine_retimer.RetimeTrajectory(
308+
cloned_robot, path, defer=False, **affine_retimer_options)
291309
else:
292-
# The trajectory is not constrained, so we can shortcut it
293-
# before execution.
294-
if self.simplifier is not None:
295-
logger.debug('Shortcutting an unconstrained path.')
296-
shortcut_path = self.simplifier.ShortcutPath(
297-
cloned_robot, path, defer=False, **shortcut_options)
310+
# Directly compute a timing of smooth trajectories.
311+
if smooth:
312+
logger.warning(
313+
'Post-processing smooth paths is not supported.'
314+
' Using the default post-processing logic; this may'
315+
' significantly change the geometric path.'
316+
)
317+
318+
# The trajectory is constrained. Retime it without changing the
319+
# geometric path.
320+
if constrained:
321+
logger.debug('Retiming a constrained path. The output'
322+
' trajectory will stop at every waypoint.')
323+
traj = self.retimer.RetimeTrajectory(
324+
cloned_robot, path, defer=False, **retiming_options)
325+
# The trajectory is not constrained, so we can shortcut it
326+
# before execution.
298327
else:
299-
logger.debug('Skipping shortcutting; no simplifier'
300-
' available.')
301-
shortcut_path = path
302-
303-
logger.debug('Smoothing an unconstrained path.')
304-
traj = self.smoother.RetimeTrajectory(
305-
cloned_robot, shortcut_path, defer=False,
306-
**smoothing_options)
328+
if self.simplifier is not None:
329+
logger.debug('Shortcutting an unconstrained path.')
330+
shortcut_path = self.simplifier.ShortcutPath(
331+
cloned_robot, path, defer=False, **shortcut_options)
332+
else:
333+
logger.debug('Skipping shortcutting; no simplifier'
334+
' available.')
335+
shortcut_path = path
336+
337+
logger.debug('Smoothing an unconstrained path.')
338+
traj = self.smoother.RetimeTrajectory(
339+
cloned_robot, shortcut_path, defer=False,
340+
**smoothing_options)
307341

308342
return CopyTrajectory(traj, env=self.GetEnv())
309343

src/prpy/util.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,3 +671,25 @@ def GeodesicDistance(t1, t2, r=1.0):
671671
error = GeodesicError(t1, t2)
672672
error[3] = r*error[3]
673673
return numpy.linalg.norm(error)
674+
675+
def FindCatkinResource(package, relative_path):
676+
'''
677+
Find a Catkin resource in the share directory or
678+
the package source directory. Raises IOError
679+
if resource is not found.
680+
681+
@param relative_path Path relative to share or package
682+
source directory
683+
@param package The package to search in
684+
@return Absolute path to resource
685+
'''
686+
from catkin.find_in_workspaces import find_in_workspaces
687+
688+
paths = find_in_workspaces(project=package, search_dirs=['share'],
689+
path=relative_path, first_match_only=True)
690+
691+
if paths and len(paths) == 1:
692+
return paths[0]
693+
else:
694+
raise IOError('Loading resource "{:s}" failed.'.format(
695+
relative_path))

0 commit comments

Comments
 (0)