Skip to content

Commit 8ce67f7

Browse files
author
Gilwoo Lee
committed
Modified SnapPlanner and GreedyIKPlanner to raise collision-specific errors. Added tests for collision-specific errors in PlantoConfiguration and PlanToEndEffectorOffset
1 parent 6eaabad commit 8ce67f7

File tree

7 files changed

+145
-12
lines changed

7 files changed

+145
-12
lines changed

src/prpy/planning/snap.py

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ def PlanToEndEffectorPose(self, robot, goal_pose, **kw_args):
8080
@param goal_pose desired end-effector pose
8181
@return traj
8282
"""
83+
84+
from .exceptions import CollisionPlanningError,SelfCollisionPlanningError
85+
from openravepy import CollisionReport
86+
8387
ikp = openravepy.IkParameterizationType
8488
ikfo = openravepy.IkFilterOptions
8589

@@ -92,13 +96,33 @@ def PlanToEndEffectorPose(self, robot, goal_pose, **kw_args):
9296
ik_param, ikfo.CheckEnvCollisions,
9397
ikreturn=False, releasegil=True
9498
)
95-
96-
if ik_solution is None:
99+
100+
if ik_solution is None:
101+
# FindIKSolutions is slower than FindIKSolution,
102+
# so call this only to identify and raise error when there is no solution
103+
ik_solutions = manipulator.FindIKSolutions(
104+
ik_param,
105+
ikfo.IgnoreSelfCollisions,
106+
ikreturn=False,
107+
releasegil=True
108+
)
109+
110+
for q in ik_solutions:
111+
robot.SetActiveDOFValues(q)
112+
report = CollisionReport()
113+
if self.env.CheckCollision(robot, report=report):
114+
raise CollisionPlanningError.FromReport(report)
115+
elif robot.CheckSelfCollision(report=report):
116+
raise SelfCollisionPlanningError.FromReport(report)
117+
97118
raise PlanningError('There is no IK solution at the goal pose.')
98119

99120
return self._Snap(robot, ik_solution, **kw_args)
100121

101122
def _Snap(self, robot, goal, **kw_args):
123+
from .exceptions import CollisionPlanningError,SelfCollisionPlanningError,JointLimitError
124+
from openravepy import CollisionReport
125+
102126
Closed = openravepy.Interval.Closed
103127

104128
start = robot.GetActiveDOFValues()
@@ -110,13 +134,45 @@ def _Snap(self, robot, goal, **kw_args):
110134
params = openravepy.Planner.PlannerParameters()
111135
params.SetRobotActiveJoints(robot)
112136
params.SetGoalConfig(goal)
113-
check = params.CheckPathAllConstraints(start, goal, [], [], 0., Closed)
114-
115-
# The function returns a bitmask of ConstraintFilterOptions flags,
116-
# indicating which constraints are violated. We'll abort if any
117-
# constraints are violated.
118-
if check != 0:
119-
raise PlanningError('Straight line trajectory is not valid.')
137+
check = params.CheckPathAllConstraints(start, goal, [], [], 0., Closed,
138+
options = 15, returnconfigurations = True)
139+
140+
# If valid, above function returns (0,None, None, 0)
141+
# if not, it returns (1, invalid_config ,[], time_when_invalid),
142+
# in which case we identify and raise appropriate error.
143+
if check[0] != 0:
144+
q = check[1]
145+
146+
# Check for joint limit violation
147+
q_limit_min, q_limit_max = robot.GetActiveDOFLimits()
148+
149+
lower_position_violations = (q < q_limit_min)
150+
if lower_position_violations.any():
151+
index = lower_position_violations.nonzero()[0][0]
152+
raise JointLimitError(robot,
153+
dof_index=active_indices[index],
154+
dof_value=q[index],
155+
dof_limit=q_limit_min[index],
156+
description='position')
157+
158+
upper_position_violations = (q> q_limit_max)
159+
if upper_position_violations.any():
160+
index = upper_position_violations.nonzero()[0][0]
161+
raise JointLimitError(robot,
162+
dof_index=active_indices[index],
163+
dof_value=q[index],
164+
dof_limit=q_limit_max[index],
165+
description='position')
166+
167+
# Check for collision
168+
robot.SetActiveDOFValues(q)
169+
report = CollisionReport()
170+
if self.env.CheckCollision(robot, report=report):
171+
raise CollisionPlanningError.FromReport(report)
172+
elif robot.CheckSelfCollision(report=report):
173+
raise SelfCollisionPlanningError.FromReport(report)
174+
175+
raise PlanningError('Straight line trajectory is not valid.' )
120176

121177
# Create a two-point trajectory that starts at our current
122178
# configuration and takes us to the goal.

src/prpy/planning/workspace.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,14 @@ def PlanWorkspacePath(self, robot, traj, timelimit=5.0,
154154
@param timelimit timeout in seconds
155155
@return qtraj configuration space path
156156
"""
157-
from .exceptions import TimeoutPlanningError
157+
from .exceptions import (
158+
TimeoutPlanningError,
159+
CollisionPlanningError,
160+
SelfCollisionPlanningError,
161+
JointLimitError)
162+
from openravepy import CollisionReport
163+
from numpy import linalg as LA
164+
158165

159166
with robot:
160167
manip = robot.GetActiveManipulator()
@@ -171,17 +178,20 @@ def PlanWorkspacePath(self, robot, traj, timelimit=5.0,
171178

172179
# Smallest CSpace step at which to give up
173180
min_step = min(robot.GetActiveDOFResolutions())/100.
174-
ik_options = openravepy.IkFilterOptions.CheckEnvCollisions
175-
181+
ik_options = openravepy.IkFilterOptions.CheckEnvCollisions
176182
start_time = time.time()
177183
epsilon = 1e-6
178184

185+
179186
try:
187+
collision_error = None
180188
while t < traj.GetDuration() + epsilon:
181189
# Check for a timeout.
182190
current_time = time.time()
183191
if (timelimit is not None and
184192
current_time - start_time > timelimit):
193+
if collision_error is not None:
194+
raise collision_error
185195
raise TimeoutPlanningError(timelimit)
186196

187197
# Hypothesize new configuration as closest IK to current
@@ -193,12 +203,33 @@ def PlanWorkspacePath(self, robot, traj, timelimit=5.0,
193203
releasegil=True
194204
)
195205

206+
# FindIKSolutions is slower than FindIKSolution,
207+
# so call this only to identify error when there is no solution
208+
if qnew is None:
209+
ik_solutions = manip.FindIKSolutions(
210+
openravepy.matrixFromPose(traj.Sample(t+dt)[0:7]),
211+
openravepy.IkFilterOptions.IgnoreSelfCollisions,
212+
ikreturn=False,
213+
releasegil=True
214+
)
215+
216+
# update collision_error to contain collision info.
217+
for q in ik_solutions:
218+
robot.SetActiveDOFValues(q)
219+
report = CollisionReport()
220+
if self.env.CheckCollision(robot, report=report):
221+
collision_error = CollisionPlanningError.FromReport(report)
222+
elif robot.CheckSelfCollision(report=report):
223+
collision_error = SelfCollisionPlanningError.FromReport(report)
224+
196225
# Check if the step was within joint DOF resolution.
197226
infeasible_step = True
198227
if qnew is not None:
199228
# Found an IK
200229
step = abs(qnew - qcurr)
201230
if (max(step) < min_step) and qtraj:
231+
if collision_error is not None:
232+
raise collision_error
202233
raise PlanningError('Not making progress.')
203234
infeasible_step = any(step > robot.GetActiveDOFResolutions())
204235
if infeasible_step:

tests/planning/methods/PlanToConfiguration.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from prpy.clone import CloneException
22
from prpy.planning.base import PlanningError
3+
from prpy.planning.exceptions import (
4+
CollisionPlanningError,
5+
SelfCollisionPlanningError,
6+
JointLimitError)
37

48
class PlanToConfigurationTest(object):
59
def test_PlanToConfiguration_StartInCollision_Throws(self):
@@ -80,3 +84,17 @@ def test_PlanToConfiguration_GoalIsFeasible_FindsSolution(self):
8084
self.config_feasible_start, self.config_feasible_goal)
8185

8286

87+
class PlanToConfigurationTestCollisionTest(object):
88+
""" Expects collision-specific error"""
89+
90+
def test_PlanToConfiguration_GoalInCollision_Throws_Collision(self):
91+
# Setup
92+
with self.env:
93+
self.robot.SetActiveDOFValues(self.config_feasible_start)
94+
95+
# Test/Assert
96+
with self.assertRaises((CollisionPlanningError,
97+
SelfCollisionPlanningError,
98+
JointLimitError)):
99+
self.planner.PlanToConfiguration(
100+
self.robot, self.config_env_collision)

tests/planning/methods/PlanToEndEffectorOffset.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import numpy
22
from prpy.clone import CloneException
33
from prpy.planning.base import PlanningError
4+
from prpy.planning.exceptions import (
5+
CollisionPlanningError,
6+
SelfCollisionPlanningError,
7+
JointLimitError)
48

59

610
class PlanToEndEffectorOffsetTest(object):
@@ -106,3 +110,21 @@ def test_PlanToEndEffectorOffset_GoalInCollision_Throws(self):
106110
self.planner.PlanToEndEffectorOffset(
107111
self.robot, direction=numpy.array([0., 0., -1.]),
108112
distance=0.1)
113+
114+
class PlanToEndEffectorOffsetCollisionTest(object):
115+
""" Expects collision-specific error"""
116+
117+
def test_PlanToEndEffectorOffset_StartInCollision_Throws_CollisionError(self):
118+
# Setup
119+
with self.env:
120+
self.robot.SetActiveDOFValues(self.config_feasible_goal)
121+
goal_ik = self.manipulator.GetEndEffectorTransform()
122+
123+
self.robot.SetActiveDOFValues(self.config_env_collision)
124+
125+
# Test/Assert
126+
with self.assertRaises((CollisionPlanningError,
127+
SelfCollisionPlanningError,
128+
JointLimitError)):
129+
self.planner.PlanToEndEffectorOffset(
130+
self.robot, direction=self.direction, distance=self.distance)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from methods import PlanToEndEffectorOffsetTest
2+
from methods.PlanToEndEffectorOffset import PlanToEndEffectorOffsetCollisionTest
23
from planning_helpers import BasePlannerTest
34
from prpy.planning.workspace import GreedyIKPlanner
45
from unittest import TestCase
56

67

78
class GreedyIKPlannerTest(BasePlannerTest,
89
PlanToEndEffectorOffsetTest,
10+
PlanToEndEffectorOffsetCollisionTest,
911
TestCase):
1012
planner_factory = GreedyIKPlanner

tests/planning/test_SnapPlanner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
PlanToConfigurationTest,
33
PlanToConfigurationStraightLineTest,
44
)
5+
from methods.PlanToConfiguration import PlanToConfigurationTestCollisionTest
56
from planning_helpers import BasePlannerTest
67
from prpy.planning.snap import SnapPlanner
78
from unittest import TestCase
@@ -10,6 +11,7 @@
1011
class SnapPlannerTest(BasePlannerTest,
1112
PlanToConfigurationTest,
1213
PlanToConfigurationStraightLineTest,
14+
PlanToConfigurationTestCollisionTest,
1315
TestCase):
1416
planner_factory = SnapPlanner
1517

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from methods import PlanToEndEffectorOffsetTest
2+
from methods.PlanToEndEffectorOffset import PlanToEndEffectorOffsetCollisionTest
23
from planning_helpers import BasePlannerTest
34
from prpy.planning.vectorfield import VectorFieldPlanner
45
from unittest import TestCase
56

67

78
class VectorFieldPlannerTest(BasePlannerTest,
89
PlanToEndEffectorOffsetTest,
10+
PlanToEndEffectorOffsetCollisionTest,
911
TestCase):
1012
planner_factory = VectorFieldPlanner

0 commit comments

Comments
 (0)