arcmove_globalpath_follower.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 """
4 on any new /initialpose, do full rotation, then delay (to hone in amcl)
5 
6 requires dwa_base_controller, global path updated continuously as bot moves
7 
8 """
9 
10 
11 import rospy, tf
12 import oculusprimesocket
13 from nav_msgs.msg import Odometry
14 import math, re
15 from nav_msgs.msg import Path
16 from geometry_msgs.msg import PoseWithCovarianceStamped
17 from actionlib_msgs.msg import GoalStatusArray
18 from move_base_msgs.msg import MoveBaseActionGoal
19 
20 listentime = 0.6 # allows odom + amcl to catch up
21 nextmove = 0
22 odomx = 0
23 odomy = 0
24 odomth = 0
25 gptargetx = 0
26 gptargety = 0
27 gptargetth = 0
28 lptargetx = 0
29 lptargety = 0
30 lptargetth = 0
31 followpath = False
32 pathid = None
33 goalx = 0
34 goaly = 0
35 goalth = 0
36 initialturn = False
37 waitonaboutface = 0
38 minturn = math.radians(8) # (was 6) -- 0.21 minimum for pwm 255
39 minlinear = 0.08 # was 0.05
40 maxlinear = 0.5
41 # maxarclinear = 0.75
42 lastpath = 0 # refers to localpath
43 goalpose = False
44 goalseek = False
45 
46 # TODO: get these two values from java
47 meterspersec = 0.33 # linear speed
48 radianspersec = 1.496 # 1.496 = 0.0857degrees per ms
49 
50 dpmthreshold = 1.2 # maximum to allow arcturn, radians per meter
51 tfx = 0
52 tfy = 0
53 tfth = 0
54 globalpathposenum = 20
55 listener = None
56 
57 
58 def pathCallback(data): # local path
59  global goalpose, lastpath, followpath
60  global lptargetx, lptargety
61 
62  lastpath = rospy.get_time()
63  goalpose = False
64 
65  p = data.poses[len(data.poses)-1] # get last pose in path
66  lptargetx = p.pose.position.x
67  lptargety = p.pose.position.y
68  quaternion = ( p.pose.orientation.x, p.pose.orientation.y,
69  p.pose.orientation.z, p.pose.orientation.w )
70  lptargetth = tf.transformations.euler_from_quaternion(quaternion)[2]
71 
73  global gptargetx, gptargety, gptargetth, followpath, pathid
74 
75  n = len(data.poses)
76  if n < 5:
77  return
78 
79  if n-1 < globalpathposenum:
80  p = data.poses[n-1]
81  else:
82  p = data.poses[globalpathposenum]
83 
84  gptargetx = p.pose.position.x
85  gptargety = p.pose.position.y
86  quaternion = ( p.pose.orientation.x, p.pose.orientation.y,
87  p.pose.orientation.z, p.pose.orientation.w )
88  gptargetth = tf.transformations.euler_from_quaternion(quaternion)[2]
89 
90  pathid = data.header.seq
91  followpath = True
92 
93 
94 def odomCallback(data):
95  global odomx, odomy, odomth
96  odomx = data.pose.pose.position.x
97  odomy = data.pose.pose.position.y
98  quaternion = ( data.pose.pose.orientation.x, data.pose.pose.orientation.y,
99  data.pose.pose.orientation.z, data.pose.pose.orientation.w )
100  odomth = tf.transformations.euler_from_quaternion(quaternion)[2]
101 
102 
104  if data.pose.pose.position.x == 0 and data.pose.pose.position.y == 0:
105  return
106  # do full rotation on pose estimate, to hone-in amcl (if not docked)
107  rospy.sleep(0.5) # let amcl settle
109  oculusprimesocket.sendString("right 360")
110  oculusprimesocket.waitForReplySearch("<state> direction stop")
111 
112 
114  global goalx, goaly, goalth, goalpose, lastpath, initialturn, followpath, nextmove
115 
116  # to prevent immediately rotating wrongly towards new goal direction
117  lastpath = rospy.get_time()
118  goalpose = False
119 
120  # set goal angle
121  data = d.goal.target_pose
122  goalx = data.pose.position.x
123  goaly = data.pose.position.y
124  quaternion = ( data.pose.orientation.x, data.pose.orientation.y,
125  data.pose.orientation.z, data.pose.orientation.w )
126  goalth = tf.transformations.euler_from_quaternion(quaternion)[2]
127  initialturn = True
128  followpath = False
129  nextmove = lastpath + 2 # sometimes globalpath still points at previoius goal
130 
132  global goalseek
133  goalseek = False
134  if len(data.status_list) == 0:
135  return
136  status = data.status_list[len(data.status_list)-1] # get latest status
137  if status.status == 1:
138  goalseek = True
139 
140 def arcmove(ox, oy, oth, gpx, gpy, gpth, gth, lpx, lpy, lpth):
141 
142  global initialturn, waitonaboutface, nextmove
143 
144  # global path targets
145  gpdx = gpx - ox
146  gpdy = gpy - oy
147  gpdistance = math.sqrt( pow(gpdx,2) + pow(gpdy,2) )
148  if not gpdistance == 0:
149  gpth = math.acos(gpdx/gpdistance)
150  if gpdy <0:
151  gpth = -gpth
152 
153  # local path targets
154  lpdx = lpx - ox
155  lpdy = lpy - oy
156  lpdistance = math.sqrt( pow(lpdx,2) + pow(lpdy,2) )
157  if not lpdistance == 0:
158  lpth = math.acos(lpdx/lpdistance)
159  if lpdy <0:
160  lpth = -lpth
161 
162  # find arclength if any, and turn radians, depending on scenario
163  arclength = 0
164  goalrotate = False
165  if followpath and not ox==gpx and not oy==gpy and not initialturn: # normal arc move
166 
167  if abs(lpth-gpth) > dpmthreshold: # 90 degrees local path disparity, use global instead
168  dth = gpth
169  distance = gpdistance/2 # prevent oscillation, better than distance = 0
170  # distance = 0
171  # print("using global path")
172  else:
173  dth = lpth
174  distance = lpdistance
175 
176  dth = dth - oth
177  if dth > math.pi:
178  dth = -math.pi*2 + dth
179  elif dth < -math.pi:
180  dth = math.pi*2 + dth
181 
182  radius = (distance/2)/math.sin(dth/2)
183  arclength = radius * dth # *should* work out to always be > 0
184  if not arclength == 0:
185  if abs(dth/arclength) > dpmthreshold: # 1.57:
186  arclength = 0
187  # print ("high dpm")
188 
189  elif goalpose: # final goal rotate move
190  try:
191  (trans,rot) = listener.lookupTransform('/map', '/odom', rospy.Time(0))
192  quaternion = (rot[0], rot[1], rot[2], rot[3])
193  dth = (gth - tf.transformations.euler_from_quaternion(quaternion)[2]) - oth
194  goalrotate = True
196  dth = lpth
197 
198  else: # initial turn move (always?)
199  dth = gpth - oth # point to global path
200 
201  # determine angle delta for move TODO: cleanup
202  if dth > math.pi:
203  dth = -math.pi*2 + dth
204  elif dth < -math.pi:
205  dth = math.pi*2 + dth
206 
207  # if turning more than 120 deg, inch forward, make sure not transient obstacle (like door transfer)
208  if abs(dth) > 1.57 and not goalrotate and not initialturn and waitonaboutface < 1: # was 2.094 120 deg
209  if goalDistance() > 0.9: # skip if close to goal
211  oculusprimesocket.sendString("forward 0.25")
212  oculusprimesocket.waitForReplySearch("<state> direction stop")
213  waitonaboutface += 1 # only do this once
214  rospy.sleep(1.5)
215  nextmove = rospy.get_time() + listentime
216  # print("pausing...")
217  return
218  waitonaboutface = 0
219 
220  initialturn = False
221 
222  if arclength > 0: # arcmove
223  if arclength < minlinear:
224  arclength = minlinear
225 
226  oculusprimesocket.sendString("arcmove " + str(arclength) + " " + str(int(math.degrees(dth))) )
227  rospy.sleep(arclength/0.35)
228  nextmove = rospy.get_time()
229  return
230 
231 
232  # rotate only
233 
234  # force minimum
235  if dth > 0 and dth < minturn:
236  dth = minturn
237  elif dth < 0 and dth > -minturn:
238  dth = -minturn
239 
241 
242  oculusprimesocket.sendString("move stop")
243  # below waits forever with new java stopbetweenmoves rotate code!! state never arrives
244  # oculusprimesocket.waitForReplySearch("<state> direction stop")
245  rospy.sleep(0.5)
246 
247  if dth > 0:
248  oculusprimesocket.sendString("left " + str(int(math.degrees(dth))) )
249  oculusprimesocket.waitForReplySearch("<state> direction stop")
250  elif dth < 0:
251  oculusprimesocket.sendString("right " +str(int(math.degrees(-dth))) )
252  oculusprimesocket.waitForReplySearch("<state> direction stop")
253 
254  nextmove = rospy.get_time() + listentime
255 
256 
257 def move(ox, oy, oth, tx, ty, tth, gth):
258  global followpath, initialturn, waitonaboutface, nextmove
259 
260  currentpathid = pathid
261 
262  # determine xy deltas for move
263  distance = 0
264  if followpath:
265  dx = tx - ox
266  dy = ty - oy
267  distance = math.sqrt( pow(dx,2) + pow(dy,2) )
268 
269  goalrotate = False
270  if distance > 0:
271  th = math.acos(dx/distance)
272  if dy <0:
273  th = -th
274 
275  elif goalpose:
276  try:
277  (trans,rot) = listener.lookupTransform('/map', '/odom', rospy.Time(0))
278  quaternion = (rot[0], rot[1], rot[2], rot[3])
279  th = gth - tf.transformations.euler_from_quaternion(quaternion)[2]
280  goalrotate = True
282  th = tth
283 
284  # th = gth - tfth
285 
286  else: # does this ever get called? just use th = math.acos(dx/distance), same?
287  th = tth
288 
289  # determine angle delta for move
290  dth = th - oth
291  if dth > math.pi:
292  dth = -math.pi*2 + dth
293  elif dth < -math.pi:
294  dth = math.pi*2 + dth
295 
296  # force minimums
297  if distance > 0 and distance < minlinear:
298  distance = minlinear
299 
300  if distance > maxlinear:
301  distance = maxlinear
302 
303  # supposed to reduce zig zagging (was 0.3)
304  if dth < minturn*0.5 and dth > -minturn*0.5:
305  dth = 0
306  elif dth >= minturn*0.5 and dth < minturn:
307  dth = minturn
308  elif dth <= -minturn*0.5 and dth > -minturn:
309  dth = -minturn
310 
312 
313  # if turning more than 120 deg, inch forward, make sure not transient obstacle (like door transfer)
314  if abs(dth) > 1.57 and not goalrotate and not initialturn and waitonaboutface < 1:
315  if goalDistance() > 0.9:
316  oculusprimesocket.sendString("forward 0.26")
317  oculusprimesocket.waitForReplySearch("<state> direction stop")
318  waitonaboutface += 1 # only do this once
319  rospy.sleep(1.5)
320  nextmove = rospy.get_time() + listentime
321  return
322 
323  waitonaboutface = 0
324  initialturn = False
325 
326  if not pathid == currentpathid:
327  nextmove = rospy.get_time() + listentime
328  return
329 
330  if dth > 0:
331  oculusprimesocket.sendString("left " + str(int(math.degrees(dth))) )
332  oculusprimesocket.waitForReplySearch("<state> direction stop")
333  elif dth < 0:
334  oculusprimesocket.sendString("right " +str(int(math.degrees(-dth))) )
335  oculusprimesocket.waitForReplySearch("<state> direction stop")
336 
337  if distance > 0:
338  oculusprimesocket.sendString("forward "+str(distance))
339  rospy.sleep(distance/meterspersec)
340  # initialturn = False
341 
342  nextmove = rospy.get_time() + listentime
343 
344 
346 
347  try:
348  (trans,rot) = listener.lookupTransform('/map', '/base_link', rospy.Time(0))
350  return 99
351 
352  gdx = goalx - trans[0]
353  gdy = goaly - trans[1]
354  distance = math.sqrt( pow(gdx,2) + pow(gdy,2) )
355  # print ("goaldistance: "+str(distance)+", tfx: "+str(tfx)+", tfy: "+str(tfy))
356  return distance
357 
358 
359 def cleanup():
360  oculusprimesocket.sendString("log arcmove_globalpath_follower.py disconnecting")
361 
362 
363 # MAIN
364 
365 # rospy.init_node('dwa_base_controller', anonymous=False)
366 rospy.init_node('arcmove_globalpath_follower', anonymous=False)
367 listener = tf.TransformListener()
369 rospy.on_shutdown(cleanup)
370 
371 rospy.Subscriber("odom", Odometry, odomCallback)
372 rospy.Subscriber("move_base/DWAPlannerROS/local_plan", Path, pathCallback)
373 rospy.Subscriber("move_base/goal", MoveBaseActionGoal, goalCallback)
374 rospy.Subscriber("move_base/status", GoalStatusArray, goalStatusCallback)
375 rospy.Subscriber("move_base/DWAPlannerROS/global_plan", Path, globalPathCallback)
376 rospy.Subscriber("initialpose", PoseWithCovarianceStamped, intialPoseCallback)
377 
378 oculusprimesocket.sendString("log arcmove_globalpath_follower.py connected")
379 oculusprimesocket.sendString("readsetting usearcmoves")
380 s = oculusprimesocket.waitForReplySearch("setting usearcmoves")
381 if re.search("true", s):
382  oculusprimesocket.sendString("state rosarcmove true")
383 else:
384  oculusprimesocket.sendString("state rosarcmove false")
385 
386 
387 while not rospy.is_shutdown():
388  t = rospy.get_time()
389 
390  if t >= nextmove:
391  if goalseek and (followpath or goalpose):
392 
393  oculusprimesocket.sendString("state rosarcmove")
394  s = oculusprimesocket.waitForReplySearch("<state> rosarcmove")
395  if re.search("true", s):
396  rosarcmove = True
397  else:
398  rosarcmove = False
399 
400  if rosarcmove and goalDistance() > 0.9:
401  arcmove(odomx, odomy, odomth, gptargetx, gptargety, gptargetth, goalth, lptargetx, lptargety, lptargetth) # blocking
402  else:
403  # print ("using move() global path")
404  move(odomx, odomy, odomth, gptargetx, gptargety, gptargetth, goalth) # blocking
405 
406  followpath = False
407 
408  if t - lastpath > 3 and goalseek:
409  if goalDistance() <= 0.9:
410  goalpose = True
411  else:
412  rospy.sleep(0.5)
413  # goalpose = True
414 
415  rospy.sleep(0.01)
416 
def waitForReplySearch(pattern)
def move(ox, oy, oth, tx, ty, tth, gth)
def arcmove(ox, oy, oth, gpx, gpy, gpth, gth, lpx, lpy, lpth)


oculusprime
Author(s): Colin Adamson
autogenerated on Wed Mar 10 2021 03:14:59