Skip to content

Commit 3e17aae

Browse files
committed
Merge pull request #210 from DavidB-CMU/master
Added helper functions to check if the robot is at a goal configuration.
2 parents baffdcd + 60866ca commit 3e17aae

File tree

2 files changed

+232
-47
lines changed

2 files changed

+232
-47
lines changed

src/prpy/util.py

Lines changed: 110 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -680,77 +680,141 @@ def FindCatkinResource(package, relative_path):
680680
relative_path))
681681

682682

683-
def IsAtTrajectoryStart(robot, trajectory):
683+
def IsAtTrajectoryWaypoint(robot, trajectory, waypoint_idx):
684684
"""
685-
Check if robot's DOFs match the start configuration of a trajectory.
686-
687-
This function examines the current DOF values of the specified robot and
688-
compares these values to the first waypoint of the specified trajectory.
689-
If every DOF value specified in the trajectory differs by less than the
690-
DOF resolution of the specified joint/axis then it will return True.
691-
Otherwise, it returns False.
692-
693-
@param robot: the robot whose active DOFs will be checked
694-
@param trajectory: the trajectory whose start configuration will be checked
695-
@returns: True if the robot's active DOFs match the given trajectory
696-
False if one or more active DOFs differ by DOF resolution
685+
Check if robot is at a particular waypoint in a trajectory.
686+
687+
This function examines the current DOF values of the specified
688+
robot and compares these values to the first waypoint of the
689+
specified trajectory. If the DOF values specified in the trajectory
690+
differ by less than the DOF resolution of the specified joint/axis
691+
then it will return True. Otherwise, it returns False.
692+
693+
NOTE: This is used in ExecuteTrajectory(),
694+
IsAtTrajectoryStart(), and
695+
IsAtTrajectoryEnd()
696+
697+
@param robot: The robot whose active DOFs will be checked.
698+
@param trajectory: The trajectory containing the waypoint
699+
to be checked.
700+
@returns: True The robot is at the desired position.
701+
False One or more joints differ by DOF resolution.
697702
"""
703+
if trajectory.GetNumWaypoints() == 0:
704+
raise ValueError('Trajectory has 0 waypoints!')
705+
698706
cspec = trajectory.GetConfigurationSpecification()
699707
needs_base = HasAffineDOFs(cspec)
700708
needs_joints = HasJointDOFs(cspec)
701709

702710
if needs_base and needs_joints:
703-
raise ValueError('Trajectories with affine and joint DOFs are not supported')
711+
raise ValueError('Trajectories with affine and joint DOFs are '
712+
'not supported')
704713

705714
if trajectory.GetEnv() != robot.GetEnv():
706-
raise ValueError('The environment attached to the trajectory does not match the environment attached to the robot')
707-
715+
raise ValueError('The environment attached to the trajectory '
716+
'does not match the environment attached to '
717+
'the robot in IsAtTrajectoryStart().')
708718
if needs_base:
709-
doft = openravepy.DOFAffine.X | openravepy.DOFAffine.Y | openravepy.DOFAffine.RotationAxis
710-
current_pose = openravepy.RaveGetAffineDOFValuesFromTransform(robot.GetTransform(), doft)
719+
rtf = robot.GetTransform()
720+
doft = openravepy.DOFAffine.X | \
721+
openravepy.DOFAffine.Y | \
722+
openravepy.DOFAffine.RotationAxis
723+
curr_pose = openravepy.RaveGetAffineDOFValuesFromTransform(rtf, doft)
711724
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-
725+
waypoint = trajectory.GetWaypoint(0)
726+
start_t = cspec.ExtractTransform(start_transform, waypoint, robot)
727+
traj_start = openravepy.RaveGetAffineDOFValuesFromTransform(start_t, \
728+
doft)
715729
# Compare translation distance
716-
trans_delta_value = abs(current_pose[:2] - traj_start[:2])
730+
trans_delta_value = abs(curr_pose[:2] - traj_start[:2])
717731
trans_resolution = robot.GetAffineTranslationResolution()[:2]
718732
if trans_delta_value[0] > trans_resolution[0] or \
719733
trans_delta_value[1] > trans_resolution[1]:
720734
return False
721735

722736
# 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:
737+
rot_delta_value = abs(wrap_to_interval(curr_pose[2] - traj_start[2]))
738+
rot_res = robot.GetAffineRotationAxisResolution()[2] # Rot about z?
739+
if rot_delta_value > rot_res:
726740
return False
727741

728742
else:
729-
# Get used indices and starting configuration from trajectory.
743+
# Get joint indices used in the trajectory,
744+
# and the joint positions at this waypoint
745+
waypoint = trajectory.GetWaypoint(waypoint_idx)
730746
dof_indices, _ = cspec.ExtractUsedIndices(robot)
731-
traj_values = cspec.ExtractJointValues(
732-
trajectory.GetWaypoint(0), robot, dof_indices)
747+
goal_config = cspec.ExtractJointValues(waypoint, robot, dof_indices)
748+
749+
# Return false if any joint deviates too much
750+
return IsAtConfiguration(robot, goal_config, dof_indices)
733751

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
750-
751-
# If all joints match, return True.
752752
return True
753753

754+
755+
def IsAtTrajectoryStart(robot, trajectory):
756+
"""
757+
Check if robot is at the configuration specified by
758+
the FIRST waypoint in a trajectory.
759+
"""
760+
waypoint_idx = 0
761+
return IsAtTrajectoryWaypoint(robot, trajectory, waypoint_idx)
762+
763+
764+
def IsAtTrajectoryEnd(robot, trajectory):
765+
"""
766+
Check if robot is at the configuration specified by
767+
the LAST waypoint in a trajectory.
768+
"""
769+
waypoint_idx = trajectory.GetNumWaypoints() - 1
770+
return IsAtTrajectoryWaypoint(robot, trajectory, waypoint_idx)
771+
772+
773+
def IsAtConfiguration(robot, goal_config, dof_indices=None):
774+
"""
775+
Check if robot's joints have reached a desired configuration.
776+
777+
If the DOF indices are not specified, the robot's active DOF
778+
will be used.
779+
780+
@param robot The robot object.
781+
@param goal_config The desired configuration, an array of joint
782+
positions.
783+
@param dof_indices The joint index numbers.
784+
785+
@return boolean Returns True if joints are at goal position,
786+
within DOF resolution.
787+
"""
788+
789+
# If DOF indices not specified, use the active DOF by default
790+
if dof_indices == None:
791+
dof_indices = robot.GetActiveDOFIndices()
792+
793+
# Get current position of joints
794+
with robot.GetEnv():
795+
joint_values = robot.GetDOFValues(dof_indices)
796+
dof_resolutions = robot.GetDOFResolutions(dof_indices)
797+
798+
## If any joint is not at the goal position, return False
799+
for i in xrange(0, len(goal_config)):
800+
# Get the axis index for this joint, which is 0
801+
# for revolute joints or 0-2 for spherical joints.
802+
joint = robot.GetJointFromDOFIndex(dof_indices[i])
803+
axis_idx = dof_indices[i] - joint.GetDOFIndex()
804+
# Use OpenRAVE method to check the configuration
805+
# difference value1-value2 for axis i,
806+
# taking into account joint limits and wrapping
807+
# of continuous joints.
808+
delta_value = abs(joint.SubtractValue(joint_values[i], \
809+
goal_config[i], \
810+
axis_idx))
811+
if delta_value > dof_resolutions[i]:
812+
return False
813+
814+
# If all joints match the goal, return True
815+
return True
816+
817+
754818
def IsTimedTrajectory(trajectory):
755819
"""
756820
Returns True if the trajectory is timed.

tests/test_util.py

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,78 @@
1+
from __future__ import print_function
12
import openravepy
23
import prpy.util
34
import unittest
45

5-
class IsTimedTrajectoryTests(unittest.TestCase):
6+
import os # environ, path
7+
import subprocess
8+
import sys # stderr
9+
10+
import numpy # allclose, zeros
11+
12+
13+
# Add the models included with OpenRAVE to the OPENRAVE_DATA path.
14+
# These may not be available if the user manually set the OPENRAVE_DATA
15+
# environmental variable, e.g. through openrave_catkin.
16+
try:
17+
share_path = \
18+
subprocess.check_output(['openrave-config', '--share-dir']).strip()
19+
os.environ['OPENRAVE_DATA'] = os.path.join(share_path, 'data')
20+
except subprocess.CalledProcessError as e:
21+
print('error: Failed using "openrave-config" to find the default'
22+
' OPENRAVE_DATA path. Loading assets may fail.',
23+
file=sys.stderr)
24+
25+
# Initialize OpenRAVE.
26+
openravepy.RaveInitialize(True)
27+
openravepy.misc.InitOpenRAVELogging()
28+
openravepy.RaveSetDebugLevel(openravepy.DebugLevel.Fatal)
29+
30+
31+
class TrajectoryTests(unittest.TestCase):
632
def setUp(self):
733
self.env = openravepy.Environment()
34+
self.env.Load('wamtest1.env.xml')
835
self.traj = openravepy.RaveCreateTrajectory(self.env, '')
36+
self.robot = self.env.GetRobot('BarrettWAM')
37+
self.manipulator = self.robot.GetManipulator('arm')
38+
39+
# Set all 7 DOF of the WAM arm to active
40+
with self.env:
41+
self.robot.SetActiveDOFs(self.manipulator.GetArmIndices())
42+
self.robot.SetActiveManipulator(self.manipulator)
43+
self.active_dof_indices = self.robot.GetActiveDOFIndices()
44+
45+
46+
def CreateTrajectory(self, q_start, q_goal):
47+
"""
48+
Create a trajectory between two joint configurations.
49+
(a straight line in joint space)
50+
"""
51+
env = self.env
52+
robot = self.robot
53+
dof_indices = self.active_dof_indices
54+
55+
traj = openravepy.RaveCreateTrajectory(env, '')
56+
cspec = robot.GetActiveConfigurationSpecification('linear')
57+
58+
# Add first waypoint
59+
start_waypoint = numpy.zeros(cspec.GetDOF())
60+
cspec.InsertJointValues(start_waypoint, q_start, robot, \
61+
dof_indices, False)
62+
traj.Init(cspec)
63+
traj.Insert(0, start_waypoint.ravel())
64+
65+
# Add second waypoint, if different to first
66+
if not numpy.allclose(q_start, q_goal):
67+
goal_waypoint = numpy.zeros(cspec.GetDOF())
68+
cspec.InsertJointValues(goal_waypoint, q_goal, robot, \
69+
dof_indices, False)
70+
traj.Insert(1, goal_waypoint.ravel())
71+
72+
return traj
73+
74+
75+
# IsTimedTrajectory()
976

1077
def test_IsTimed_ReturnsTrue(self):
1178
cspec = openravepy.ConfigurationSpecification()
@@ -26,3 +93,57 @@ def test_IsNotTimed_ReturnsFalse(self):
2693

2794
self.traj.Insert(0, [0.])
2895
self.assertFalse(prpy.util.IsTimedTrajectory(self.traj))
96+
97+
98+
# IsAtConfiguration()
99+
100+
def test_IsAtConfiguration_ReturnsTrue(self):
101+
curr_config = self.robot.GetDOFValues(self.active_dof_indices)
102+
self.assertTrue(prpy.util.IsAtConfiguration(self.robot, curr_config))
103+
104+
def test_IsAtConfiguration_ReturnsFalse(self):
105+
goal_config = [0.02, 0.01, 0.02, 0.01, 0.01, 0.01, 0.0];
106+
self.assertFalse(prpy.util.IsAtConfiguration(self.robot, goal_config))
107+
108+
109+
# IsAtTrajectoryStart()
110+
111+
def test_IsAtTrajectoryStart_ReturnsTrue(self):
112+
current_config = self.robot.GetDOFValues(self.active_dof_indices)
113+
goal_config = [0.02, 0.01, 0.02, 0.01, 0.01, 0.01, 0.0];
114+
q0 = current_config
115+
q1 = goal_config
116+
traj = self.CreateTrajectory(q0, q1)
117+
self.assertTrue(prpy.util.IsAtTrajectoryStart(self.robot, traj))
118+
119+
# Test with only 1 waypoint
120+
traj = self.CreateTrajectory(q0, q0)
121+
self.assertTrue(prpy.util.IsAtTrajectoryStart(self.robot, traj))
122+
123+
def test_IsAtTrajectoryStart_ReturnsFalse(self):
124+
current_config = self.robot.GetDOFValues(self.active_dof_indices)
125+
goal_config = [0.02, 0.01, 0.02, 0.01, 0.01, 0.01, 0.0];
126+
q0 = current_config
127+
q1 = goal_config
128+
traj = self.CreateTrajectory(q1, q0) # goal is 1st waypoint
129+
self.assertFalse(prpy.util.IsAtTrajectoryStart(self.robot, traj))
130+
131+
132+
# IsAtTrajectoryEnd()
133+
134+
def test_IsAtTrajectoryEnd_ReturnsTrue(self):
135+
current_config = self.robot.GetDOFValues(self.active_dof_indices)
136+
goal_config = [0.02, 0.01, 0.02, 0.01, 0.01, 0.01, 0.0];
137+
q0 = current_config
138+
q1 = goal_config
139+
# set last waypoint to current config
140+
traj = self.CreateTrajectory(q1, q0)
141+
self.assertTrue(prpy.util.IsAtTrajectoryEnd(self.robot, traj))
142+
143+
def test_IsAtTrajectoryEnd_ReturnsFalse(self):
144+
current_config = self.robot.GetDOFValues(self.active_dof_indices)
145+
goal_config = [0.02, 0.01, 0.02, 0.01, 0.01, 0.01, 0.0];
146+
q0 = current_config
147+
q1 = goal_config
148+
traj = self.CreateTrajectory(q0, q1)
149+
self.assertFalse(prpy.util.IsAtTrajectoryEnd(self.robot, traj))

0 commit comments

Comments
 (0)