move_object.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 
18 import sys
19 import math
20 
21 import os
22 import copy
23 import numpy
24 
25 from optparse import OptionParser
26 
27 import rospy
28 import tf
29 from tf.transformations import euler_from_quaternion
30 from gazebo_msgs.srv import GetModelState, GetModelStateRequest, GetModelStateResponse
31 from gazebo_msgs.srv import SetModelState, SetModelStateRequest, SetModelStateResponse
32 from gazebo_msgs.msg import ModelState
33 from geometry_msgs.msg import Pose
34 
35 _usage = """usage: %prog [options]
36 %prog: Moves a model in gazebo
37 
38 %prog -h
39  Further help on options
40 """
41 
42 class move():
43  def __init__(self):
44  self.parse_options()
45  # start node
46  self.vel = float(self.options.velocity)
47  self.name = self.options.name
48  rospy.init_node('move' + self.name)
49  self.models = self.options.stop_objects.split()
50  rospy.wait_for_service('/gazebo/get_model_state')
51  self.srv_get_model_state = rospy.ServiceProxy('/gazebo/get_model_state', GetModelState)
52  rospy.wait_for_service('/gazebo/set_model_state')
53  self.srv_set_model_state = rospy.ServiceProxy('/gazebo/set_model_state', SetModelState)
54  self.pub_set_model_state = rospy.Publisher('/gazebo/set_model_state', ModelState, queue_size = 1)
55  self.node_frequency = 100.0 # hz
56  self.rate = rospy.Rate(self.node_frequency) # hz
57  self.road_block = False
58  self.timeout_respawn = rospy.Duration(60)
59 
60  # wait for all models to be available
61  while not rospy.is_shutdown():
62  rospy.sleep(1.0) # give gazebo some time to start
63  res = self.get_model_state(self.name)
64  if not res.success:
65  continue
66  else:
67  success = True
68  for model in self.models:
69  res = self.get_model_state(model)
70  if not res.success:
71  success = False
72  break # for-loop
73  if success:
74  break # while-loop
75  rospy.logerr("Not all models available yet")
76  rospy.loginfo("Ready to move object %s", self.name)
77 
78  def get_model_state(self, model_name):
79  req = GetModelStateRequest()
80  res = GetModelStateResponse()
81  req.model_name = model_name
82  try:
83  res = self.srv_get_model_state(req)
84  except rospy.service.ServiceException:
85  pass
86  return res
87 
88  def get_model_dist(self, x, y):
89  model_dist = float('inf')
90  for model in self.models:
91  res = self.get_model_state(model)
92  object_dist = math.sqrt(math.pow(res.pose.position.x-x,2) + math.pow(res.pose.position.y-y,2))
93  model_dist = min(object_dist, model_dist)
94  return model_dist
95 
96  def move_on_line(self, start, goal):
97  dx = goal[0] - start[0]
98  dy = goal[1] - start[1]
99  segment_length = math.sqrt(dx**2 + dy**2)
100  segment_time = segment_length/self.vel
101  yaw = math.atan2(dy, dx)
102 
103  segment_step_count = int(segment_time*self.node_frequency)
104 
105  if segment_step_count == 0:
106  return
107  segment_time = segment_length/self.vel/segment_step_count
108  path = numpy.linspace(0, segment_length, segment_step_count)
109 
110  idx = 0
111  last_time_in_motion = rospy.Time.now()
112  while idx < segment_step_count and not rospy.is_shutdown():
113  step = path[idx]
114 
115  step_x = start[0] + step * math.cos(yaw)
116  step_y = start[1] + step * math.sin(yaw)
117 
118  # model to close to object?
119  model_dist = self.get_model_dist(step_x, step_y)
120  if(model_dist <= float(self.options.stop_distance)):
121  rospy.logdebug("Model too close to object. Stopping!")
122  if (rospy.Time.now() - last_time_in_motion) > self.timeout_respawn:
123  rospy.logdebug("Model move to last waypoint!")
124  self.road_block = True
125  return
126  else:
127  object_new_pose = Pose()
128  object_new_pose.position.x = step_x
129  object_new_pose.position.y = step_y
130  quat = tf.transformations.quaternion_from_euler(0.0, 0.0, yaw)
131  object_new_pose.orientation.x = quat[0]
132  object_new_pose.orientation.y = quat[1]
133  object_new_pose.orientation.z = quat[2]
134  object_new_pose.orientation.w = quat[3]
135 
136  # spawn new model
137  model_state = ModelState()
138  model_state.model_name = self.name
139  model_state.pose = object_new_pose
140  model_state.reference_frame = 'world'
141 
142  # publish message
143  self.pub_set_model_state.publish(model_state)
144 
145  idx += 1
146 
147  # update last time when the model moved, before coming to halt
148  last_time_in_motion = rospy.Time.now()
149 
150  # sleep until next step
151  self.rate.sleep()
152 
153  def move_initialpose(self, initialpose):
154  object_new_pose = Pose()
155  object_new_pose.position.x = initialpose[0]
156  object_new_pose.position.y = initialpose[1]
157  object_new_pose.position.z = initialpose[2]
158  quat = tf.transformations.quaternion_from_euler(initialpose[3], initialpose[4], initialpose[5])
159  object_new_pose.orientation.x = quat[0]
160  object_new_pose.orientation.y = quat[1]
161  object_new_pose.orientation.z = quat[2]
162  object_new_pose.orientation.w = quat[3]
163 
164  # spawn new model
165  model_state = ModelState()
166  model_state.model_name = self.name
167  model_state.pose = object_new_pose
168  model_state.reference_frame = 'world'
169 
170  # publish message
171  while not rospy.is_shutdown():
172  res = self.get_model_state(self.name)
173  rospy.logdebug("desired model_state: {}".format(model_state))
174  rospy.logdebug("current model_state: {}".format(res))
175  if not numpy.any([res.twist.linear.x,res.twist.linear.y,res.twist.linear.z,res.twist.angular.x,res.twist.angular.y,res.twist.angular.z]):
176  rospy.logwarn("gazebo physics not yet started")
177  else:
178  desired_pos = object_new_pose.position
179  current_pos = res.pose.position
180  desired_ori = euler_from_quaternion([object_new_pose.orientation.x,object_new_pose.orientation.y,object_new_pose.orientation.z,object_new_pose.orientation.w])
181  current_ori = euler_from_quaternion([res.pose.orientation.x,res.pose.orientation.y,res.pose.orientation.z,res.pose.orientation.w])
182 
183  rospy.logdebug(numpy.isclose([desired_pos.x,desired_pos.y,desired_pos.z], [current_pos.x,current_pos.y,current_pos.z], atol=0.01))
184  rospy.logdebug(numpy.isclose(desired_ori, current_ori, atol=0.01))
185  if (numpy.allclose([desired_pos.x,desired_pos.y,desired_pos.z], [current_pos.x,current_pos.y,current_pos.z], atol=0.01) and
186  numpy.allclose(desired_ori, current_ori, atol=0.01)):
187  rospy.loginfo("move_initialpose reached")
188  break
189  else:
190  rospy.loginfo("publish initialpose")
191  self.pub_set_model_state.publish(model_state)
192  # try:
193  # res = self.srv_set_model_state(SetModelStateRequest(model_state=model_state))
194  # rospy.loginfo("result: {}".format(res))
195  # except rospy.service.ServiceException:
196  # pass
197 
198  # sleep until next step
199  self.rate.sleep()
200 
201  def move_polygon(self, polygon_in):
202  # move on all parts of the polygon
203  polygon = copy.deepcopy(polygon_in)
204  polygon.append(polygon_in[0])
205  while not rospy.is_shutdown():
206  if len(polygon) <= 1:
207  #rospy.loginfo("starting new round")
208  polygon = copy.deepcopy(polygon_in)
209  polygon.append(polygon_in[0])
210 
211  start = polygon[0]
212  goal = polygon[1]
213 
214  #rospy.loginfo("moving on new segment from " + str(start) + " to " + str(goal) + ".")
215  self.move_on_line(start, goal)
216  if self.road_block:
217  self.road_block = False
218  continue
219  polygon.pop(0)
220 
221  def move_circle(self, center, radius):
222  # move on all parts of the polygon
223  yaw_step = math.asin(1.0/self.node_frequency*self.vel/radius)
224  yaw = 0.0
225  last_time_in_motion = rospy.Time.now()
226  while not rospy.is_shutdown():
227  if yaw >= 2*math.pi:
228  rospy.loginfo("starting new round")
229  yaw = 0.0
230 
231  step_x = center[0] + radius * math.sin(yaw)
232  step_y = center[1] + radius * math.cos(yaw)
233 
234  # model to close to object?
235  model_dist = self.get_model_dist(step_x, step_y)
236  if(model_dist <= float(self.options.stop_distance)):
237  rospy.logdebug("Model too close to object. Stopping!")
238  if (rospy.Time.now() - last_time_in_motion) > self.timeout_respawn:
239  rospy.logdebug("Model move to start waypont of the polygone!")
240  yaw = 2*math.pi
241  continue
242  else:
243  object_new_pose = Pose()
244  object_new_pose.position.x = step_x
245  object_new_pose.position.y = step_y
246  quat = tf.transformations.quaternion_from_euler(0.0, 0.0, -yaw)
247  object_new_pose.orientation.x = quat[0]
248  object_new_pose.orientation.y = quat[1]
249  object_new_pose.orientation.z = quat[2]
250  object_new_pose.orientation.w = quat[3]
251 
252  # spawn new model
253  model_state = ModelState()
254  model_state.model_name = self.name
255  model_state.pose = object_new_pose
256  model_state.reference_frame = 'world'
257 
258  # publish message
259  self.pub_set_model_state.publish(model_state)
260 
261  yaw += yaw_step
262 
263  # update last time when the model moved, before coming to halt
264  last_time_in_motion = rospy.Time.now()
265 
266  # sleep until next step
267  self.rate.sleep()
268 
269  def parse_options(self):
270  parser = OptionParser(usage=_usage, prog=os.path.basename(sys.argv[0]))
271 
272  parser.add_option("-m", "--mode",
273  dest="mode", choices=["initialpose", "polygon", "circle"], default=None,
274  help="Mode to move model. Required.")
275 
276  parser.add_option("-n", "--name",
277  dest="name", metavar="STRING", default=None,
278  help="Name of model to be moved. Required.")
279 
280  parser.add_option("-i", "--initialpose",
281  dest="initialpose", metavar="Initialpose [x, y, z, R, P, Y]", default=None,
282  help="Cartesian Pose, only used for initialpose. Default: None")
283 
284  parser.add_option("-v", "--velocity",
285  dest="velocity", metavar="Float", default=0.5,
286  help="Velocity for movement in [m/s]. Default: 0.5")
287 
288  parser.add_option("-p", "--polygon",
289  dest="polygon", metavar="List of points [[x1,y1],[x2,y2], ...]", default=None,
290  help="List of points forming a polygon in [m], only used for polygon movement. Default: None")
291 
292  parser.add_option("-c", "--center",
293  dest="center", metavar="Point [x1,y1]", default=None,
294  help="Center point, only used for circular movement. Default: None")
295 
296  parser.add_option("-r", "--radius",
297  dest="radius", metavar="Float", default=None,
298  help="Radius, only used for circular movement. Default: None")
299 
300  parser.add_option("-o", "--stop-objects",
301  dest="stop_objects", metavar="List of objects 'object_1 object_2 ...'", default='',
302  help="List of Model-Name of objects that are to be avoided. Default: ''")
303 
304  parser.add_option("-d", "--stop-distance",
305  dest="stop_distance", metavar="Float", default=2.0,
306  help="Allowed distance to objects before stopping. Default: 2.0")
307 
308  (self.options, _) = parser.parse_args()
309 
310  if self.options.mode == None:
311  parser.error("Please provide a valid mode, see -h option.")
312  if self.options.name == None:
313  parser.error("Please provide a valid model name, see -h option.")
314 
315 
316  def run(self):
317  if self.options.mode == "initialpose":
318  if (self.options.initialpose == None):
319  rospy.logerr("Please provide a valid initialpose, see -h option. initialpose = " + str(self.options.initialpose))
320  return
321  self.move_initialpose(eval(self.options.initialpose))
322  if self.options.mode == "polygon":
323  if (self.options.polygon == None) or (type(eval(self.options.polygon)) is not list):
324  rospy.logerr("Please provide a valid polygon, see -h option. polygon = " + str(self.options.polygon))
325  return
326  self.move_polygon(eval(self.options.polygon))
327  if self.options.mode == "circle":
328  if self.options.radius == None:
329  rospy.logerr("Please provide a valid radius. radius = " + str(self.options.radius))
330  return
331  if self.options.center == None:
332  rospy.logerr("Please provide a valid center. center = " + str(self.options.center))
333  return
334  self.move_circle(eval(self.options.center),float(self.options.radius))
335 
336 
337 if __name__ == "__main__":
338  try:
339  m = move()
340  m.run()
341  except rospy.ROSInterruptException:
342  pass
def get_model_state(self, model_name)
Definition: move_object.py:78
def move_on_line(self, start, goal)
Definition: move_object.py:96
def move_polygon(self, polygon_in)
Definition: move_object.py:201
def move_circle(self, center, radius)
Definition: move_object.py:221
def parse_options(self)
Definition: move_object.py:269
def __init__(self)
Definition: move_object.py:43
def get_model_dist(self, x, y)
Definition: move_object.py:88
def move_initialpose(self, initialpose)
Definition: move_object.py:153


cob_gazebo_tools
Author(s): Felix Messmer , Nadia Hammoudeh Garcia , Florian Weisshardt
autogenerated on Sat Dec 5 2020 03:59:48