moveit2
The MoveIt Motion Planning Framework for ROS 2.
planning_scene_interface.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Author: Ioan Sucan, Felix Messmer
34 
35 import rospy
36 from rosgraph.names import ns_join
37 from . import conversions
38 
39 from moveit_msgs.msg import PlanningScene, CollisionObject, AttachedCollisionObject
40 from moveit_ros_planning_interface import _moveit_planning_scene_interface
41 from geometry_msgs.msg import Pose, Point
42 from shape_msgs.msg import SolidPrimitive, Plane, Mesh, MeshTriangle
43 from .exception import MoveItCommanderException
44 from moveit_msgs.srv import ApplyPlanningScene, ApplyPlanningSceneRequest
45 
46 try:
47  from pyassimp import pyassimp
48 except:
49  # support pyassimp > 3.0
50  try:
51  import pyassimp
52  except:
53  pyassimp = False
54  print(
55  "Failed to import pyassimp, see https://github.com/ros-planning/moveit/issues/86 for more info"
56  )
57 
58 
59 class PlanningSceneInterface(object):
60  """
61  Python interface for a C++ PlanningSceneInterface.
62  Uses both C++ wrapped methods and scene manipulation topics
63  to manipulate the PlanningScene managed by the PlanningSceneMonitor.
64  See wrap_python_planning_scene_interface.cpp for the wrapped methods.
65  """
66 
67  def __init__(self, ns="", synchronous=False, service_timeout=5.0):
68  """Create a planning scene interface; it uses both C++ wrapped methods and scene manipulation topics."""
69  self._psi_psi = _moveit_planning_scene_interface.PlanningSceneInterface(ns)
70 
71  self._pub_co_pub_co = rospy.Publisher(
72  ns_join(ns, "collision_object"), CollisionObject, queue_size=100
73  )
74  self._pub_aco_pub_aco = rospy.Publisher(
75  ns_join(ns, "attached_collision_object"),
76  AttachedCollisionObject,
77  queue_size=100,
78  )
79  self.__synchronous__synchronous = synchronous
80  if self.__synchronous__synchronous:
81  self._apply_planning_scene_diff_apply_planning_scene_diff = rospy.ServiceProxy(
82  ns_join(ns, "apply_planning_scene"), ApplyPlanningScene
83  )
84  self._apply_planning_scene_diff_apply_planning_scene_diff.wait_for_service(service_timeout)
85 
86  def __submit(self, collision_object, attach=False):
87  if self.__synchronous__synchronous:
88  diff_req = self.__make_planning_scene_diff_req__make_planning_scene_diff_req(collision_object, attach)
89  self._apply_planning_scene_diff_apply_planning_scene_diff.call(diff_req)
90  else:
91  if attach:
92  self._pub_aco_pub_aco.publish(collision_object)
93  else:
94  self._pub_co_pub_co.publish(collision_object)
95 
96  def add_object(self, collision_object):
97  """Add an object to the planning scene"""
98  self.__submit__submit(collision_object, attach=False)
99 
100  def add_sphere(self, name, pose, radius=1):
101  """Add a sphere to the planning scene"""
102  co = self.__make_sphere__make_sphere(name, pose, radius)
103  self.__submit__submit(co, attach=False)
104 
105  def add_cylinder(self, name, pose, height, radius):
106  """Add a cylinder to the planning scene"""
107  co = self.__make_cylinder__make_cylinder(name, pose, height, radius)
108  self.__submit__submit(co, attach=False)
109 
110  def add_mesh(self, name, pose, filename, size=(1, 1, 1)):
111  """Add a mesh to the planning scene"""
112  co = self.__make_mesh__make_mesh(name, pose, filename, size)
113  self.__submit__submit(co, attach=False)
114 
115  def add_box(self, name, pose, size=(1, 1, 1)):
116  """Add a box to the planning scene"""
117  co = self.__make_box__make_box(name, pose, size)
118  self.__submit__submit(co, attach=False)
119 
120  def add_plane(self, name, pose, normal=(0, 0, 1), offset=0):
121  """Add a plane to the planning scene"""
122  co = CollisionObject()
123  co.operation = CollisionObject.ADD
124  co.id = name
125  co.header = pose.header
126  p = Plane()
127  p.coef = list(normal)
128  p.coef.append(offset)
129  co.planes = [p]
130  co.plane_poses = [pose.pose]
131  self.__submit__submit(co, attach=False)
132 
133  def attach_object(self, attached_collision_object):
134  """Attach an object in the planning scene"""
135  self.__submit__submit(attached_collision_object, attach=True)
136 
138  self, link, name, pose=None, filename="", size=(1, 1, 1), touch_links=[]
139  ):
140  aco = AttachedCollisionObject()
141  if (pose is not None) and filename:
142  aco.object = self.__make_mesh__make_mesh(name, pose, filename, size)
143  else:
144  aco.object = self.__make_existing__make_existing(name)
145  aco.link_name = link
146  aco.touch_links = [link]
147  if len(touch_links) > 0:
148  aco.touch_links = touch_links
149  self.__submit__submit(aco, attach=True)
150 
151  def attach_box(self, link, name, pose=None, size=(1, 1, 1), touch_links=[]):
152  aco = AttachedCollisionObject()
153  if pose is not None:
154  aco.object = self.__make_box__make_box(name, pose, size)
155  else:
156  aco.object = self.__make_existing__make_existing(name)
157  aco.link_name = link
158  if len(touch_links) > 0:
159  aco.touch_links = touch_links
160  else:
161  aco.touch_links = [link]
162  self.__submit__submit(aco, attach=True)
163 
164  def clear(self):
165  """Remove all objects from the planning scene"""
166  self.remove_attached_objectremove_attached_object()
167  self.remove_world_objectremove_world_object()
168 
169  def remove_world_object(self, name=None):
170  """
171  Remove an object from planning scene, or all if no name is provided
172  """
173  co = CollisionObject()
174  co.operation = CollisionObject.REMOVE
175  if name is not None:
176  co.id = name
177  self.__submit__submit(co, attach=False)
178 
179  def remove_attached_object(self, link=None, name=None):
180  """
181  Remove an attached object from the robot, or all objects attached to the link if no name is provided,
182  or all attached objects in the scene if neither link nor name are provided.
183 
184  Removed attached objects remain in the scene as world objects.
185  Call remove_world_object afterwards to remove them from the scene.
186  """
187  aco = AttachedCollisionObject()
188  aco.object.operation = CollisionObject.REMOVE
189  if link is not None:
190  aco.link_name = link
191  if name is not None:
192  aco.object.id = name
193  self.__submit__submit(aco, attach=True)
194 
195  def get_known_object_names(self, with_type=False):
196  """
197  Get the names of all known objects in the world. If with_type is set to true, only return objects that have a known type.
198  """
199  return self._psi_psi.get_known_object_names(with_type)
200 
202  self, minx, miny, minz, maxx, maxy, maxz, with_type=False
203  ):
204  """
205  Get the names of known objects in the world that are located within a bounding region (specified in the frame reported by
206  get_planning_frame()). If with_type is set to true, only return objects that have a known type.
207  """
208  return self._psi_psi.get_known_object_names_in_roi(
209  minx, miny, minz, maxx, maxy, maxz, with_type
210  )
211 
212  def get_object_poses(self, object_ids):
213  """
214  Get the poses from the objects identified by the given object ids list.
215  """
216  ser_ops = self._psi_psi.get_object_poses(object_ids)
217  ops = dict()
218  for key in ser_ops:
219  msg = Pose()
220  conversions.msg_from_string(msg, ser_ops[key])
221  ops[key] = msg
222  return ops
223 
224  def get_objects(self, object_ids=[]):
225  """
226  Get the objects identified by the given object ids list. If no ids are provided, return all the known objects.
227  """
228  ser_objs = self._psi_psi.get_objects(object_ids)
229  objs = dict()
230  for key in ser_objs:
231  msg = CollisionObject()
232  conversions.msg_from_string(msg, ser_objs[key])
233  objs[key] = msg
234  return objs
235 
236  def get_attached_objects(self, object_ids=[]):
237  """
238  Get the attached objects identified by the given object ids list. If no ids are provided, return all the attached objects.
239  """
240  ser_aobjs = self._psi_psi.get_attached_objects(object_ids)
241  aobjs = dict()
242  for key in ser_aobjs:
243  msg = AttachedCollisionObject()
244  conversions.msg_from_string(msg, ser_aobjs[key])
245  aobjs[key] = msg
246  return aobjs
247 
248  def apply_planning_scene(self, planning_scene_message):
249  """
250  Applies the planning scene message.
251  """
252  return self._psi_psi.apply_planning_scene(
253  conversions.msg_to_string(planning_scene_message)
254  )
255 
256  @staticmethod
257  def __make_existing(name):
258  """
259  Create an empty Collision Object. Used when the object already exists
260  """
261  co = CollisionObject()
262  co.id = name
263  return co
264 
265  @staticmethod
266  def __make_box(name, pose, size):
267  co = CollisionObject()
268  co.operation = CollisionObject.ADD
269  co.id = name
270  co.header = pose.header
271  co.pose = pose.pose
272  box = SolidPrimitive()
273  box.type = SolidPrimitive.BOX
274  box.dimensions = list(size)
275  co.primitives = [box]
276  return co
277 
278  @staticmethod
279  def __make_mesh(name, pose, filename, scale=(1, 1, 1)):
280  co = CollisionObject()
281  if pyassimp is False:
283  "Pyassimp needs patch https://launchpadlibrarian.net/319496602/patchPyassim.txt"
284  )
285  scene = pyassimp.load(filename)
286  if not scene.meshes or len(scene.meshes) == 0:
287  raise MoveItCommanderException("There are no meshes in the file")
288  if len(scene.meshes[0].faces) == 0:
289  raise MoveItCommanderException("There are no faces in the mesh")
290  co.operation = CollisionObject.ADD
291  co.id = name
292  co.header = pose.header
293  co.pose = pose.pose
294 
295  mesh = Mesh()
296  first_face = scene.meshes[0].faces[0]
297  if hasattr(first_face, "__len__"):
298  for face in scene.meshes[0].faces:
299  if len(face) == 3:
300  triangle = MeshTriangle()
301  triangle.vertex_indices = [face[0], face[1], face[2]]
302  mesh.triangles.append(triangle)
303  elif hasattr(first_face, "indices"):
304  for face in scene.meshes[0].faces:
305  if len(face.indices) == 3:
306  triangle = MeshTriangle()
307  triangle.vertex_indices = [
308  face.indices[0],
309  face.indices[1],
310  face.indices[2],
311  ]
312  mesh.triangles.append(triangle)
313  else:
315  "Unable to build triangles from mesh due to mesh object structure"
316  )
317  for vertex in scene.meshes[0].vertices:
318  point = Point()
319  point.x = vertex[0] * scale[0]
320  point.y = vertex[1] * scale[1]
321  point.z = vertex[2] * scale[2]
322  mesh.vertices.append(point)
323  co.meshes = [mesh]
324  pyassimp.release(scene)
325  return co
326 
327  @staticmethod
328  def __make_sphere(name, pose, radius):
329  co = CollisionObject()
330  co.operation = CollisionObject.ADD
331  co.id = name
332  co.header = pose.header
333  co.pose = pose.pose
334  sphere = SolidPrimitive()
335  sphere.type = SolidPrimitive.SPHERE
336  sphere.dimensions = [radius]
337  co.primitives = [sphere]
338  return co
339 
340  @staticmethod
341  def __make_cylinder(name, pose, height, radius):
342  co = CollisionObject()
343  co.operation = CollisionObject.ADD
344  co.id = name
345  co.header = pose.header
346  co.pose = pose.pose
347  cylinder = SolidPrimitive()
348  cylinder.type = SolidPrimitive.CYLINDER
349  cylinder.dimensions = [height, radius]
350  co.primitives = [cylinder]
351  return co
352 
353  @staticmethod
354  def __make_planning_scene_diff_req(collision_object, attach=False):
355  scene = PlanningScene()
356  scene.is_diff = True
357  scene.robot_state.is_diff = True
358  if attach:
359  scene.robot_state.attached_collision_objects = [collision_object]
360  else:
361  scene.world.collision_objects = [collision_object]
362  planning_scene_diff_req = ApplyPlanningSceneRequest()
363  planning_scene_diff_req.scene = scene
364  return planning_scene_diff_req
def attach_box(self, link, name, pose=None, size=(1, 1, 1), touch_links=[])
def add_plane(self, name, pose, normal=(0, 0, 1), offset=0)
def attach_mesh(self, link, name, pose=None, filename="", size=(1, 1, 1), touch_links=[])
def __init__(self, ns="", synchronous=False, service_timeout=5.0)
def get_known_object_names_in_roi(self, minx, miny, minz, maxx, maxy, maxz, with_type=False)
void print(PropagationDistanceField &pdf, int numX, int numY, int numZ)