move_patterns.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 
3 """
4 Copyright (c) 2020, Ubiquity Robotics
5 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice, this
9  list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice,
11  this list of conditions and the following disclaimer in the documentation
12  and/or other materials provided with the distribution.
13 * Neither the name of display_node nor the names of its
14  contributors may be used to endorse or promote products derived from
15  this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 """
27 
28 """
29 Example client program for sending multiple move_basic commands
30 """
31 
32 import rospy
33 
34 # our custom messages for the commands we will be using
35 import getopt, sys
36 python3 = True if sys.hexversion > 0x03000000 else False
37 import genpy
38 import struct
39 
40 import geometry_msgs.msg
41 import actionlib
42 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
43 import tf
44 
45 import traceback
46 import time
47 
48 
49 
50 def printUsage():
51  print """-h --help - This help menu
52  -c --continue - Continuous operation, no prompts to continue
53  -w --waypoints - Select a waypoint list by name as below
54  line A simple line
55  box A square box
56  octagon An octagon pattern
57  figure8 A figure 8 dual octagon
58  -s --scale - A different scale for the pattern
59  -x --offsetX - An offset for X map placement of the pattern
60  -y --offsetY - An offset for Y map placement of the pattern """
61 
62 
63 class Controller:
64 
65  #######################################################################################
66  # Define lists of waypoints in terms of X,Y values in meters and robot angular yaw pose
67  # The entries on each waypoint are as follows:
68  # 1) X coordinate in the map in meters
69  # 2) Y coordinate in the map in meters
70  # 3) Angle in radians that the robot assumes once at X,Y
71  # 4) A simple text comment
72 
73  # A simple line of one meter length
74  figureLine = [
75  [ 0.00, 0.00, 0.000, "MOVE: Leg A" ],
76  [ 0.50, 0.00, 0.000, "MOVE: Leg B" ]
77  ]
78 
79  # A box pattern
80  figureBox = [
81  [ 0.00, 0.00, 0.000, "MOVE: Leg A" ],
82  [ 0.50, 0.00, 1.570, "MOVE: Leg B" ],
83  [ 0.50, 0.50, 3.140, "MOVE: Leg C" ],
84  [ 0.00, 0.50, -1.570, "MOVE: Leg D" ]
85  ]
86 
87 
88  # An octagon pattern which is a simplified way to do a circle type of pattern
89  figureOctagon = [
90  [ 0.40, 0.30, 0.000, "MOVE: Leg A" ],
91  [ 0.60, 0.10, -0.785, "MOVE: Leg B" ],
92  [ 0.60, -0.10, -1.571, "MOVE: Leg C" ],
93  [ 0.40, -0.30, -2.356, "MOVE: Leg D" ],
94  [ 0.20, -0.30, -3.141, "Move: Leg E" ],
95  [ 0.00, -0.10, 2.356, "Move: Leg F" ],
96  [ 0.00, 0.10, 1.571, "Move: Leg G" ],
97  [ 0.20, 0.30, 0.785, "Move: Leg H" ]
98  ]
99 
100  # Two octagon connected on one edge for a figure 8
101  figure8octagon = [
102  [ 0.40, 0.30, 0.000, "MOVE: Leg A" ],
103  [ 0.60, 0.10, -0.785, "MOVE: Leg B" ],
104  [ 0.60, -0.10, -1.571, "MOVE: Leg C" ],
105  [ 0.40, -0.30, -2.356, "MOVE: Leg D" ],
106  [ 0.20, -0.30, -3.141, "Move: Leg E" ],
107  [ 0.00, -0.10, 2.356, "Move: Leg F" ],
108  [ 0.00, 0.10, 1.571, "Move: Leg G" ],
109  [-0.20, 0.30, 2.356, "Move: Leg H" ],
110  [-0.40, 0.30, 3.141, "Move: Leg I" ],
111  [-0.60, 0.10, -2.356, "Move: Leg J" ],
112  [-0.60, -0.10, -1.571, "Move: Leg K" ],
113  [-0.40, -0.30, -0.785, "Move: Leg L" ],
114  [-0.20, -0.30, 0.000, "Move: Leg M" ],
115  [ 0.00, -0.10, 0.785, "Move: Leg N" ],
116  [ 0.00, 0.10, 1.571, "Move: Leg O" ],
117  [ 0.20, 0.30, 0.785, "Move: Leg P" ]
118  ]
119 
120 
121  #######################################################################################
122 
123  """
124  Constructor for our class
125  """
126  def __init__(self):
127 
128  rospy.init_node('controller')
129 
130  # Time per loop for the main control
131  self.loop_msec = 50
132 
133  # Define waitAtEachVertex as 0 for continual moves or 1 for pause each vertex
135 
136  self.waypointName = 'line'
137  self.scaleX = 1.0
138  self.scaleY = 1.0
139  self.offsetX = 0.0
140  self.offsetY = 0.0
141 
142  # read commandline arguments and place them in array
143  # Only the -w option seems to work, something is wrong with getopt usage here
144  try:
145  opts, args = getopt.getopt(sys.argv[1:], 'hcw:s:x:y:h', \
146  ['help','continue', 'waypoints=','scale=','offsetX=','offsetY='])
147  except getopt.GetoptError as err:
148  # will print something like "option -a not recognized"
149  print "Error in recognized options"
150  printUsage()
151  sys.exit(2)
152 
153  for o, a in opts:
154  # evaluate given options
155  if o in ("-h", "--help"):
156  print ("displaying help")
157  printUsage()
158  sys.exit(2)
159  elif o in ("-c", "--continue"):
160  # continuous operation, do not require ENTER at each vertex
161  self.waitAtEachVertex = 0
162  elif o in ("-w", "--waypoints"):
163  # define a waypoint list for waypoints
164  waypointName = a
165  if (a == "line"):
166  waypointList = self.figureLine
167  elif (a == "box"):
168  waypointList = self.figureBox
169  elif (a == "figure8"):
170  waypointList = self.figure8octagon
171  elif (a == "octagon"):
172  waypointList = self.figureOctagon
173  else:
174  print ("Invalid choice of waypoint list")
175  sys.exit(2)
176  print "Waypoint list will be '%s'", waypointList
177  elif o in ("-s", "--scaleX"):
178  self.scaleX = float(a)
179  self.scaleY = float(a)
180  elif o in ("-x", "--offsetX"):
181  self.offsetX = float(a)
182  elif o in ("-y", "--offsetY"):
183  self.offsetY = float(a)
184 
185  print ("WaypointsName: %s scaleX %f scaleY %f offsetX %f offsetY %f" \
186  %(self.waypointName,self.scaleX,self.scaleY,self.offsetX,self.offsetY))
187 
188  print "A total of %d waypoints is in the list " % (len(waypointList))
189 
190 
191  # A publisher for sending commands to the follower node
192  # self.moveBaseGoalPub = rospy.Publisher("/move_base/goal", \
193  # move_base_msgs/MoveBaseActionGoal, queue_size=1)
194 
195  # Publish the desired waypoint to navigate on top of now
196  # For fiducial nav use frame_id of "map"
197  # For odom nav use frame_id of "odom"
198  def publishMoveBaseGoalWaitForReply(self, x, y, yaw, comment):
199  goal = MoveBaseGoal()
200  goal.target_pose.header.frame_id = "map"
201  goal.target_pose.header.stamp = rospy.Time.now()
202  goal.target_pose.pose.position.x = x
203  goal.target_pose.pose.position.y = y
204 
205  # to send orientation with a yaw we need quaternion transform
206  x , y, z, w = tf.transformations.quaternion_from_euler(0, 0, yaw)
207  goal.target_pose.pose.orientation.x = x
208  goal.target_pose.pose.orientation.y = y
209  goal.target_pose.pose.orientation.z = z
210  goal.target_pose.pose.orientation.w = w
211  now = rospy.get_rostime()
212  print "[%i.%i] PubMove: %s x,y,z,w of %f %f %f %f yaw %f" \
213  % (now.secs,now.nsecs,comment,x,y,z,w,yaw)
214 
215  client = actionlib.SimpleActionClient('move_base', MoveBaseAction)
216  client.wait_for_server()
217 
218  # publish the goal to the topic
219  client.send_goal(goal)
220 
221  now = rospy.get_rostime()
222  print "[%i.%i] Waiting for result ..." % (now.secs, now.nsecs)
223  wait = client.wait_for_result()
224  if not wait:
225  rospy.logerr("Action server not available!")
226  rospy.signal_shutdown("Action server not available!")
227  else:
228  now = rospy.get_rostime()
229  print "[%i.%i] Received result" % (now.secs, now.nsecs)
230  return client.get_result()
231 
232  """
233  Main loop
234  """
235  def run(self):
236 
237  print "ROS publisher publishing goals to move basic"
238  waypointList = self.figureLine
239 
240  # continue going through waypoints over and over.
241  # If you only want to do list once exit after first for loop
242  while (True):
243  for waypoint in waypointList:
244  x,y,yaw,comment = waypoint
245  x = (x * self.scaleX) + self.offsetX
246  y = (y * self.scaleY) + self.offsetY
247  now = rospy.get_rostime()
248  print "[%i.%i] Waypoint: %s X %f Y %f yaw %f will be published now" \
249  % (now.secs,now.nsecs,comment,x, y, yaw)
250 
251  # now publish the waypoint
252  moveResult = self.publishMoveBaseGoalWaitForReply( x, y, yaw, comment)
253  if moveResult == True:
254  print "ERROR: MoveBasic Bad Status! for Waypoint: X %f Y %f yaw %f " % (x, y, yaw)
255  else:
256  print "[%i.%i] Waypoint: %s X %f Y %f yaw %f has been reached" \
257  % (now.secs,now.nsecs,comment,x, y, yaw)
258 
259  # optionally wait at each vertex before going to next one
260  if (self.waitAtEachVertex == 1):
261  raw_input("Hit ENTER to go to next waypoint ... ")
262 
263 if __name__ == "__main__":
264  # Create an instance of our goal class
265  node = Controller()
266  # run it
267  node.run()
list figureLine
Define lists of waypoints in terms of X,Y values in meters and robot angular yaw pose The entries on ...
def publishMoveBaseGoalWaitForReply(self, x, y, yaw, comment)


move_basic
Author(s): Jim Vaughan
autogenerated on Fri Mar 26 2021 02:46:58