Skip to content

Commit a10c245

Browse files
committed
Merge pull request #188 from personalrobotics/bugfix/issue153
Made IsAtTrajectoryStart work for affine DOFs.
2 parents 932398f + 152ea07 commit a10c245

File tree

3 files changed

+89
-52
lines changed

3 files changed

+89
-52
lines changed

src/prpy/base/robot.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,13 @@ def ExecuteTrajectory(self, traj, defer=False, timeout=None, period=0.01,
457457
if traj.GetNumWaypoints() <= 0:
458458
raise ValueError('Trajectory must contain at least one waypoint.')
459459

460+
# Check if this trajectory contains both affine and joint DOFs
461+
cspec = traj.GetConfigurationSpecification()
462+
needs_base = util.HasAffineDOFs(cspec)
463+
needs_joints = util.HasJointDOFs(cspec)
464+
if needs_base and needs_joints:
465+
raise ValueError('Trajectories with affine and joint DOFs are not supported')
466+
460467
# Check that the current configuration of the robot matches the
461468
# initial configuration specified by the trajectory.
462469
if not util.IsAtTrajectoryStart(self, traj):
@@ -485,9 +492,6 @@ def ExecuteTrajectory(self, traj, defer=False, timeout=None, period=0.01,
485492
' function that produced this trajectory to return a'
486493
' single-waypoint trajectory.', FutureWarning)
487494

488-
# TODO: Check if this trajectory contains the base.
489-
needs_base = util.HasAffineDOFs(traj.GetConfigurationSpecification())
490-
491495
self.GetController().SetPath(traj)
492496

493497
active_manipulators = self.GetTrajectoryManipulators(traj)

src/prpy/tsr/tsr.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,6 @@
3636
EPSILON = 0.001
3737

3838

39-
def wrap_to_interval(angles, lower=-pi):
40-
"""
41-
Wraps an angle into a semi-closed interval of width 2*pi.
42-
43-
By default, this interval is `[-pi, pi)`. However, the lower bound of the
44-
interval can be specified to wrap to the interval `[lower, lower + 2*pi)`.
45-
46-
If `lower` is an array the same length as angles, the bounds will be
47-
applied element-wise to each angle in `angles`.
48-
49-
See: http://stackoverflow.com/a/32266181
50-
51-
@param angles an angle or 1D array of angles to wrap
52-
@type angles float or numpy.array
53-
@param lower optional lower bound on wrapping interval
54-
@type lower float or numpy.array
55-
"""
56-
return (angles - lower) % (2*pi) + lower
57-
58-
5939
class TSR(object):
6040
""" A Task-Space-Region (TSR) represents a motion constraint. """
6141
def __init__(self, T0_w=None, Tw_e=None, Bw=None,
@@ -82,6 +62,7 @@ def __init__(self, T0_w=None, Tw_e=None, Bw=None,
8262
Bw_interval = Bw_cont[3:6, 1] - Bw_cont[3:6, 0]
8363
Bw_interval = numpy.minimum(Bw_interval, 2*pi)
8464

65+
from prpy.util import wrap_to_interval
8566
Bw_cont[3:6, 0] = wrap_to_interval(Bw_cont[3:6, 0])
8667
Bw_cont[3:6, 1] = Bw_cont[3:6, 0] + Bw_interval
8768

@@ -200,6 +181,7 @@ def rpy_within_bounds(rpy, Bw):
200181
@return check a (3,) vector of True if within and False if outside
201182
"""
202183
# Unwrap rpy to Bw_cont.
184+
from prpy.util import wrap_to_interval
203185
rpy = wrap_to_interval(rpy, lower=Bw[:, 0])
204186

205187
# Check bounds condition on RPY component.
@@ -398,6 +380,7 @@ def sample_xyzrpy(self, xyzrpy=NANBW):
398380
if numpy.isnan(x) else x
399381
for i, x in enumerate(xyzrpy)])
400382
# Unwrap rpy to [-pi, pi]
383+
from prpy.util import wrap_to_interval
401384
Bw_sample[3:6] = wrap_to_interval(Bw_sample[3:6])
402385
return Bw_sample
403386

src/prpy/util.py

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,23 @@ def CreatePlannerParametersString(options, params=None,
9696

9797
return lxml.etree.tostring(params_xml)
9898

99+
def HasGroup(cspec, group_name):
100+
try:
101+
cspec.GetGroupFromName(group_name)
102+
return True
103+
except openravepy.openrave_exception:
104+
return False
105+
99106
def HasAffineDOFs(cspec):
100-
def has_group(cspec, group_name):
101-
try:
102-
cspec.GetGroupFromName(group_name)
103-
return True
104-
except openravepy.openrave_exception:
105-
return False
107+
return (HasGroup(cspec, 'affine_transform')
108+
or HasGroup(cspec, 'affine_velocities')
109+
or HasGroup(cspec, 'affine_accelerations'))
106110

107-
return (has_group(cspec, 'affine_transform')
108-
or has_group(cspec, 'affine_velocities')
109-
or has_group(cspec, 'affine_accelerations'))
111+
def HasJointDOFs(cspec):
112+
return (HasGroup(cspec, 'joint_values')
113+
or HasGroup(cspec, 'joint_velocities')
114+
or HasGroup(cspec, 'joint_accelerations')
115+
or HasGroup(cspec, 'joint_torques'))
110116

111117
def GetTrajectoryIndices(traj):
112118
try:
@@ -689,28 +695,58 @@ def IsAtTrajectoryStart(robot, trajectory):
689695
@returns: True if the robot's active DOFs match the given trajectory
690696
False if one or more active DOFs differ by DOF resolution
691697
"""
692-
# Get used indices and starting configuration from trajectory.
693698
cspec = trajectory.GetConfigurationSpecification()
694-
dof_indices, _ = cspec.ExtractUsedIndices(robot)
695-
traj_values = cspec.ExtractJointValues(
696-
trajectory.GetWaypoint(0), robot, dof_indices)
697-
698-
# Get current configuration of robot for used indices.
699-
with robot.GetEnv():
700-
robot_values = robot.GetDOFValues(dof_indices)
701-
dof_resolutions = robot.GetDOFResolutions(dof_indices)
702-
703-
# Check deviation in each DOF, using OpenRAVE's SubtractValue function.
704-
dof_infos = zip(dof_indices, traj_values, robot_values, dof_resolutions)
705-
for dof_index, traj_value, robot_value, dof_resolution in dof_infos:
706-
# Look up the Joint and Axis of the DOF from the robot.
707-
joint = robot.GetJointFromDOFIndex(dof_index)
708-
axis = dof_index - joint.GetDOFIndex()
709-
710-
# If any joint deviates too much, return False.
711-
delta_value = abs(joint.SubtractValue(traj_value, robot_value, axis))
712-
if delta_value > dof_resolution:
699+
needs_base = HasAffineDOFs(cspec)
700+
needs_joints = HasJointDOFs(cspec)
701+
702+
if needs_base and needs_joints:
703+
raise ValueError('Trajectories with affine and joint DOFs are not supported')
704+
705+
if trajectory.GetEnv() != robot.GetEnv():
706+
raise ValueError('The environment attached to the trajectory does not match the environment attached to the robot')
707+
708+
if needs_base:
709+
doft = openravepy.DOFAffine.X | openravepy.DOFAffine.Y | openravepy.DOFAffine.RotationAxis
710+
current_pose = openravepy.RaveGetAffineDOFValuesFromTransform(robot.GetTransform(), doft)
711+
start_transform = numpy.eye(4)
712+
start_transform = cspec.ExtractTransform(start_transform, trajectory.GetWaypoint(0), robot)
713+
traj_start = openravepy.RaveGetAffineDOFValuesFromTransform(start_transform, doft)
714+
715+
# Compare translation distance
716+
trans_delta_value = abs(current_pose[:2] - traj_start[:2])
717+
trans_resolution = robot.GetAffineTranslationResolution()[:2]
718+
if trans_delta_value[0] > trans_resolution[0] or \
719+
trans_delta_value[1] > trans_resolution[1]:
713720
return False
721+
722+
# Compare rotation distance
723+
rot_delta_value = abs(wrap_to_interval(current_pose[2] - traj_start[2]))
724+
rot_resolution = robot.GetAffineRotationAxisResolution()[2] # Rotation about z?
725+
if rot_delta_value > rot_resolution:
726+
return False
727+
728+
else:
729+
# Get used indices and starting configuration from trajectory.
730+
dof_indices, _ = cspec.ExtractUsedIndices(robot)
731+
traj_values = cspec.ExtractJointValues(
732+
trajectory.GetWaypoint(0), robot, dof_indices)
733+
734+
# Get current configuration of robot for used indices.
735+
with robot.GetEnv():
736+
robot_values = robot.GetDOFValues(dof_indices)
737+
dof_resolutions = robot.GetDOFResolutions(dof_indices)
738+
739+
# Check deviation in each DOF, using OpenRAVE's SubtractValue function.
740+
dof_infos = zip(dof_indices, traj_values, robot_values, dof_resolutions)
741+
for dof_index, traj_value, robot_value, dof_resolution in dof_infos:
742+
# Look up the Joint and Axis of the DOF from the robot.
743+
joint = robot.GetJointFromDOFIndex(dof_index)
744+
axis = dof_index - joint.GetDOFIndex()
745+
746+
# If any joint deviates too much, return False.
747+
delta_value = abs(joint.SubtractValue(traj_value, robot_value, axis))
748+
if delta_value > dof_resolution:
749+
return False
714750

715751
# If all joints match, return True.
716752
return True
@@ -1116,3 +1152,17 @@ def BodyPointsStateFromTraj(bodypoints, traj, time, derivatives=[0, 1, 2]):
11161152
"""
11171153
return BodyPointsStatesFromTraj(bodypoints, traj, (time,), derivatives)[0]
11181154

1155+
def wrap_to_interval(angles, lower=-numpy.pi):
1156+
"""
1157+
Wraps an angle into a semi-closed interval of width 2*pi.
1158+
By default, this interval is `[-pi, pi)`. However, the lower bound of the
1159+
interval can be specified to wrap to the interval `[lower, lower + 2*pi)`.
1160+
If `lower` is an array the same length as angles, the bounds will be
1161+
applied element-wise to each angle in `angles`.
1162+
See: http://stackoverflow.com/a/32266181
1163+
@param angles an angle or 1D array of angles to wrap
1164+
@type angles float or numpy.array
1165+
@param lower optional lower bound on wrapping interval
1166+
@type lower float or numpy.array
1167+
"""
1168+
return (angles - lower) % (2*numpy.pi) + lower

0 commit comments

Comments
 (0)