Skip to content

Commit dad2cd9

Browse files
committed
python version of the planning scene tutorial
1 parent 6391622 commit dad2cd9

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import sys
2+
3+
import moveit_commander
4+
import numpy as np
5+
import rospy
6+
from geometry_msgs.msg import PoseStamped
7+
from moveit_msgs.msg import DisplayRobotState
8+
from pymoveit import kinematic_constraints, collision_detection, robot_model, random_numbers
9+
from pymoveit.planning_scene import PlanningScene
10+
11+
12+
# BEGIN_SUB_TUTORIAL stateFeasibilityTestExample
13+
#
14+
# User defined constraints can also be specified to the PlanningScene
15+
# class. This is done by specifying a callback using the
16+
# setStateFeasibilityPredicate function. Here's a simple example of a
17+
# user-defined callback that checks whether the "panda_joint1" of
18+
# the Panda robot is at a positive or negative angle:
19+
def stateFeasibilityTestExample(kinematic_state, verbose):
20+
joint_values = kinematic_state.getJointPositions("panda_joint1")
21+
return joint_values[0] > 0.0
22+
23+
24+
# END_SUB_TUTORIAL
25+
26+
def main():
27+
rospy.init_node("planning_scene_tutorial")
28+
moveit_commander.roscpp_initialize(sys.argv)
29+
30+
robot_state_pub = rospy.Publisher("display_robot_state", DisplayRobotState, queue_size=10)
31+
32+
# BEGIN_TUTORIAL
33+
#
34+
# Setup
35+
# ^^^^^
36+
#
37+
# The :planning_scene:`PlanningScene` class can be easily setup and
38+
# configured using a :moveit_core:`RobotModel` or a URDF and
39+
# SRDF. This is, however, not the recommended way to instantiate a
40+
# PlanningScene. The :planning_scene_monitor:`PlanningSceneMonitor`
41+
# is the recommended method to create and maintain the current
42+
# planning scene (and is discussed in detail in the next tutorial)
43+
# using data from the robot's joints and the sensors on the robot. In
44+
# this tutorial, we will instantiate a PlanningScene class directly,
45+
# but this method of instantiation is only intended for illustration.
46+
47+
urdf_path = '/opt/ros/noetic/share/moveit_resources_panda_description/urdf/panda.urdf'
48+
srdf_path = '/opt/ros/noetic/share/moveit_resources_panda_moveit_config/config/panda.srdf'
49+
kinematic_model = robot_model.load_robot_model(urdf_path, srdf_path)
50+
planning_scene = PlanningScene(kinematic_model, collision_detection.World())
51+
current_state = planning_scene.getCurrentStateNonConst()
52+
joint_model_group = current_state.getJointModelGroup("panda_arm")
53+
54+
# Collision Checking
55+
# ^^^^^^^^^^^^^^^^^^
56+
#
57+
# Self-collision checking
58+
# ~~~~~~~~~~~~~~~~~~~~~~~
59+
#
60+
# The first thing we will do is check whether the robot in its
61+
# current state is in *self-collision*, i.e. whether the current
62+
# configuration of the robot would result in the robot's parts
63+
# hitting each other. To do this, we will construct a
64+
# :collision_detection_struct:`CollisionRequest` object and a
65+
# :collision_detection_struct:`CollisionResult` object and pass them
66+
# into the collision checking function. Note that the result of
67+
# whether the robot is in self-collision or not is contained within
68+
# the result. Self collision checking uses an *unpadded* version of
69+
# the robot, i.e. it directly uses the collision meshes provided in
70+
# the URDF with no extra padding added on.
71+
72+
collision_request = collision_detection.CollisionRequest()
73+
collision_result = collision_detection.CollisionResult()
74+
planning_scene.checkSelfCollision(collision_request, collision_result)
75+
print(f"Test 1: Current state is {'in' if collision_result.collision else 'not in'} self collision")
76+
77+
# Change the state
78+
# ~~~~~~~~~~~~~~~~
79+
#
80+
# Now, let's change the current state of the robot. The planning
81+
# scene maintains the current state internally. We can get a
82+
# reference to it and change it and then check for collisions for the
83+
# new robot configuration. Note in particular that we need to clear
84+
# the collision_result before making a new collision checking
85+
# request.
86+
87+
rng = random_numbers.RandomNumbers(0)
88+
current_state.setToRandomPositions(joint_model_group, rng)
89+
90+
collision_result.clear()
91+
planning_scene.checkSelfCollision(collision_request, collision_result)
92+
print(f"Test 2: Current state is {'in' if collision_result.collision else 'not in'} self collision")
93+
94+
# Checking for a group
95+
# ~~~~~~~~~~~~~~~~~~~~
96+
#
97+
# Now, we will do collision checking only for the hand of the
98+
# Panda, i.e. we will check whether there are any collisions between
99+
# the hand and other parts of the body of the robot. We can ask
100+
# for this specifically by adding the group name "hand" to the
101+
# collision request.
102+
103+
collision_request.group_name = "hand"
104+
current_state.setToRandomPositions(joint_model_group, rng)
105+
collision_result.clear()
106+
planning_scene.checkSelfCollision(collision_request, collision_result)
107+
print(f"Test 3: Current state is {'in' if collision_result.collision else 'not in'} self collision")
108+
109+
# Getting Contact Information
110+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~
111+
#
112+
# First, manually set the Panda arm to a position where we know
113+
# internal (self) collisions do happen. Note that this state is now
114+
# actually outside the joint limits of the Panda, which we can also
115+
# check for directly.
116+
117+
joint_values = np.array([0.0, 0.0, 0.0, -2.9, 0.0, 1.4, 0.0])
118+
joint_model_group = current_state.getJointModelGroup("panda_arm")
119+
current_state.setJointGroupPositions(joint_model_group, joint_values)
120+
print(
121+
f"Test 4: Current state is {'valid' if current_state.satisfiesBounds(joint_model_group) else 'not valid'}")
122+
123+
# Now, we can get contact information for any collisions that might
124+
# have happened at a given configuration of the Panda arm. We can ask
125+
# for contact information by filling in the appropriate field in the
126+
# collision request and specifying the maximum number of contacts to
127+
# be returned as a large number.
128+
129+
collision_request.contacts = True
130+
collision_request.max_contacts = 1000
131+
132+
collision_result.clear()
133+
planning_scene.checkSelfCollision(collision_request, collision_result)
134+
print(f"Test 5: Current state is {'in' if collision_result.collision else 'not in'} self collision")
135+
for (first_name, second_name), contacts in collision_result.contacts:
136+
print(f"Contact between {first_name} and {second_name}")
137+
138+
# Modifying the Allowed Collision Matrix
139+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140+
#
141+
# The :collision_detection_class:`AllowedCollisionMatrix` (ACM)
142+
# provides a mechanism to tell the collision world to ignore
143+
# collisions between certain object: both parts of the robot and
144+
# objects in the world. We can tell the collision checker to ignore
145+
# all collisions between the links reported above, i.e. even though
146+
# the links are actually in collision, the collision checker will
147+
# ignore those collisions and return not in collision for this
148+
# particular state of the robot.
149+
#
150+
# Note also in this example how we are making copies of both the
151+
# allowed collision matrix and the current state and passing them in
152+
# to the collision checking function.
153+
154+
acm = planning_scene.getAllowedCollisionMatrix()
155+
copied_state = planning_scene.getCurrentState()
156+
157+
# for (it2 = collision_result.contacts.begin() it2 != collision_result.contacts.end() + +it2)
158+
for (first_name, second_name), contacts in collision_result.contacts:
159+
acm.setEntry(first_name, second_name, True)
160+
collision_result.clear()
161+
planning_scene.checkSelfCollision(collision_request, collision_result, copied_state, acm)
162+
print(f"Test 6: Current state is {'in' if collision_result.collision else 'not in'} self collision")
163+
164+
# Full Collision Checking
165+
# ~~~~~~~~~~~~~~~~~~~~~~~
166+
#
167+
# While we have been checking for self-collisions, we can use the
168+
# checkCollision functions instead which will check for both
169+
# self-collisions and for collisions with the environment (which is
170+
# currently empty). This is the set of collision checking
171+
# functions that you will use most often in a planner. Note that
172+
# collision checks with the environment will use the padded version
173+
# of the robot. Padding helps in keeping the robot further away
174+
# from obstacles in the environment.
175+
collision_result.clear()
176+
planning_scene.checkCollision(collision_request, collision_result, copied_state, acm)
177+
print(f"Test 7: Current state is {'in' if collision_result.collision else 'not in'} self collision")
178+
179+
# Constraint Checking
180+
# ^^^^^^^^^^^^^^^^^^^
181+
#
182+
# The PlanningScene class also includes easy to use function calls
183+
# for checking constraints. The constraints can be of two types:
184+
# (a) constraints chosen from the
185+
# :kinematic_constraints:`KinematicConstraint` set:
186+
# i.e. :kinematic_constraints:`JointConstraint`,
187+
# :kinematic_constraints:`PositionConstraint`,
188+
# :kinematic_constraints:`OrientationConstraint` and
189+
# :kinematic_constraints:`VisibilityConstraint` and (b) user
190+
# defined constraints specified through a callback. We will first
191+
# look at an example with a simple KinematicConstraint.
192+
#
193+
# Checking Kinematic Constraints
194+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195+
#
196+
# We will first define a simple position and orientation constraint
197+
# on the end-effector of the panda_arm group of the Panda robot. Note the
198+
# use of convenience functions for filling up the constraints
199+
# (these functions are found in the :moveit_core_files:`utils.h<utils_8h>` file from the
200+
# kinematic_constraints directory in moveit_core).
201+
202+
end_effector_name = joint_model_group.getLinkModelNames()[-1]
203+
204+
desired_pose = PoseStamped()
205+
desired_pose.pose.orientation.w = 1.0
206+
desired_pose.pose.position.x = 0.3
207+
desired_pose.pose.position.y = -0.185
208+
desired_pose.pose.position.z = 0.5
209+
desired_pose.header.frame_id = "panda_link0"
210+
goal_constraint = kinematic_constraints.constructGoalConstraints(end_effector_name, desired_pose)
211+
212+
# Now, we can check a state against this constraint using the
213+
# isStateConstrained functions in the PlanningScene class.
214+
copied_state.setToRandomPositions(joint_model_group, rng)
215+
copied_state.update()
216+
constrained = planning_scene.isStateConstrained(copied_state, goal_constraint)
217+
print(f"Test 8: Random state is {'constrained' if constrained else 'not constrained'}")
218+
219+
# There's a more efficient way of checking constraints (when you want
220+
# to check the same constraint over and over again, e.g. inside a
221+
# planner). We first construct a KinematicConstraintSet which
222+
# pre-processes the ROS Constraints messages and sets it up for quick
223+
# processing.
224+
225+
kinematic_constraint_set = kinematic_constraints.KinematicConstraintSet(kinematic_model)
226+
kinematic_constraint_set.add(goal_constraint, planning_scene.getTransforms())
227+
constrained_2 = planning_scene.isStateConstrained(copied_state, kinematic_constraint_set)
228+
print(f"Test 9: Random state is {'constrained' if constrained_2 else 'not constrained'}")
229+
230+
# There's a direct way to do this using the KinematicConstraintSet
231+
# class.
232+
233+
constraint_eval_result = kinematic_constraint_set.decide(copied_state)
234+
print(
235+
f"Test 10: Random state is {'constrained' if constraint_eval_result.satisfied else 'not constrained'}")
236+
237+
# User-defined constraints
238+
# ~~~~~~~~~~~~~~~~~~~~~~~~
239+
#
240+
# CALL_SUB_TUTORIAL stateFeasibilityTestExample
241+
242+
# Now, whenever isStateFeasible is called, this user-defined callback
243+
# will be called.
244+
245+
# planning_scene.setStateFeasibilityPredicate(stateFeasibilityTestExample)
246+
# state_feasible = planning_scene.isStateFeasible(copied_state)
247+
# print(f"Test 11: Random state is {'feasible' if state_feasible else 'not feasible'}")
248+
249+
# Whenever isStateValid is called, three checks are conducted: (a)
250+
# collision checking (b) constraint checking and (c) feasibility
251+
# checking using the user-defined callback.
252+
253+
state_valid = planning_scene.isStateValid(copied_state, kinematic_constraint_set, "panda_arm")
254+
print(f"Test 12: Random state is {'valid' if state_valid else 'not valid'}")
255+
256+
# Note that all the planners available through MoveIt and OMPL will
257+
# currently perform collision checking, constraint checking and
258+
# feasibility checking using user-defined callbacks.
259+
# END_TUTORIAL
260+
261+
moveit_commander.roscpp_shutdown()
262+
263+
264+
if __name__ == '__main__':
265+
main()

0 commit comments

Comments
 (0)