26
26
from play_motion_msgs .msg import PlayMotionAction
27
27
from play_motion_msgs .msg import PlayMotionGoal
28
28
29
- from lasr_vision_msgs .srv import DetectWaveRequest
29
+ from lasr_vision_msgs .srv import DetectWaveRequest , DetectWaveResponse
30
30
31
31
from math import atan2
32
32
import numpy as np
@@ -79,17 +79,17 @@ def __init__(
79
79
self ,
80
80
start_following_radius : float = 2.0 ,
81
81
start_following_angle : float = 45.0 ,
82
- n_secs_static_finished : float = 8.0 ,
83
- n_secs_static_plan_close : float = 10.0 ,
84
82
new_goal_threshold : float = 2.0 ,
85
83
stopping_distance : float = 1.0 ,
86
84
static_speed : float = 0.0015 ,
85
+ max_speed : float = 0.5 ,
87
86
):
88
87
self ._start_following_radius = start_following_radius
89
88
self ._start_following_angle = start_following_angle
90
89
self ._new_goal_threshold = new_goal_threshold
91
90
self ._stopping_distance = stopping_distance
92
91
self ._static_speed = static_speed
92
+ self ._max_speed = max_speed
93
93
94
94
self ._track_id = None
95
95
@@ -147,7 +147,9 @@ def _tf_pose(self, pose: PoseStamped, target_frame: str):
147
147
def _robot_pose_in_odom (self ) -> Union [PoseStamped , None ]:
148
148
try :
149
149
current_pose : PoseWithCovarianceStamped = rospy .wait_for_message (
150
- "/robot_pose" , PoseWithCovarianceStamped
150
+ "/robot_pose" ,
151
+ PoseWithCovarianceStamped ,
152
+ timeout = rospy .Duration .from_sec (2.0 ),
151
153
)
152
154
except rospy .ROSException :
153
155
return None
@@ -208,10 +210,10 @@ def begin_tracking(self) -> bool:
208
210
209
211
def _recover_track (self , say ) -> bool :
210
212
if not say :
211
- self ._tts ("I SAW A person waving" , wait = True )
213
+ self ._tts ("I see you are waving" , wait = True )
212
214
213
215
if self ._tts_client_available and say :
214
- self ._tts ("I lost track of you, please come back" , wait = True )
216
+ self ._tts ("Please could you come back... " , wait = True )
215
217
216
218
while not self .begin_tracking () and not rospy .is_shutdown ():
217
219
rospy .loginfo ("Recovering track..." )
@@ -221,57 +223,62 @@ def _recover_track(self, say) -> bool:
221
223
222
224
return True
223
225
224
- # recover with vision, look up and check if person is waving
225
- def _recover_vision (self , prev_pose : PoseStamped ) -> bool :
226
- # look up with playmotion and detect wave service
227
- # if detected, begin tracking
228
-
229
- # use play motion to look up
230
- self ._cancel_goal ()
231
-
232
- goal = PlayMotionGoal ()
233
- goal .motion_name = "look_centre"
234
- self ._play_motion .send_goal_and_wait (goal )
235
-
236
- self ._tts ("Can you wave at me so that i can try to find you easily" , wait = True )
237
-
238
- # detect wave
226
+ def _detect_waving_person (self ) -> DetectWaveResponse :
239
227
try :
240
228
pcl = rospy .wait_for_message ("/xtion/depth_registered/points" , PointCloud2 )
241
229
req = DetectWaveRequest ()
242
230
req .pcl_msg = pcl
243
231
req .dataset = "resnet50"
244
232
req .confidence = 0.1
245
233
response = self ._detect_wave (req )
246
- if response .wave_detected :
247
- rospy .loginfo ("Wave detected, beginning tracking" )
248
- if np .isnan (response .wave_position .point .x ) or np .isnan (
249
- response .wave_position .point .y
250
- ):
251
- return False
252
- goal_pose = self ._tf_pose (
253
- PoseStamped (
254
- pose = Pose (
255
- position = Point (
256
- x = response .wave_position .point .x ,
257
- y = response .wave_position .point .y ,
258
- z = response .wave_position .point .z ,
259
- ),
260
- orientation = Quaternion (0 , 0 , 0 , 1 ),
261
- ),
262
- header = pcl .header ,
263
- ),
264
- "map" ,
265
- )
266
- rospy .loginfo (goal_pose .pose .position )
267
- goal_pose .pose .orientation = self ._compute_face_quat (
268
- prev_pose .pose , goal_pose .pose
269
- )
270
- self ._move_base (goal_pose )
271
- return True
234
+ return response
272
235
except rospy .ServiceException as e :
273
236
rospy .loginfo (f"Error detecting wave: { e } " )
274
- return False
237
+ return DetectWaveResponse ()
238
+
239
+ # recover with vision, look around and check if person is waving
240
+ def _recover_vision (self , prev_pose : PoseStamped ) -> bool :
241
+
242
+ # cancel current goal
243
+ self ._cancel_goal ()
244
+
245
+ self ._tts ("Can you wave at me so that i can try to find you easily" , wait = True )
246
+
247
+ for motion in self ._vision_recovery_motions :
248
+ rospy .loginfo (f"Performing motion: { motion } " )
249
+ goal = PlayMotionGoal ()
250
+ goal .motion_name = motion
251
+ self ._play_motion .send_goal_and_wait (goal )
252
+ for _ in range (self ._vision_recovery_attempts ):
253
+ response = self ._detect_waving_person ()
254
+ if response .wave_detected :
255
+ if np .isnan (response .wave_position .point .x ) or np .isnan (
256
+ response .wave_position .point .y
257
+ ):
258
+ continue
259
+ else :
260
+ goal_pose = self ._tf_pose (
261
+ PoseStamped (
262
+ pose = Pose (
263
+ position = Point (
264
+ x = response .wave_position .point .x ,
265
+ y = response .wave_position .point .y ,
266
+ z = response .wave_position .point .z ,
267
+ ),
268
+ orientation = Quaternion (0 , 0 , 0 , 1 ),
269
+ ),
270
+ header = prev_pose .header ,
271
+ ),
272
+ "map" ,
273
+ )
274
+ goal_pose .pose .orientation = self ._compute_face_quat (
275
+ prev_pose .pose , goal_pose .pose
276
+ )
277
+ rospy .loginfo (goal_pose .pose .position )
278
+ self ._move_base (goal_pose )
279
+ return True
280
+
281
+ return False
275
282
276
283
def _euclidian_distance (self , p1 : Pose , p2 : Pose ) -> float :
277
284
return np .linalg .norm (
@@ -375,7 +382,7 @@ def follow(self) -> None:
375
382
prev_goal : Union [None , PoseStamped ] = None
376
383
last_goal_time : Union [None , rospy .Time ] = None
377
384
going_to_person : bool = False
378
- track_vels : [float ] = []
385
+ track_vels : List [float ] = []
379
386
380
387
while not rospy .is_shutdown ():
381
388
@@ -395,8 +402,27 @@ def follow(self) -> None:
395
402
if track is None :
396
403
rospy .loginfo ("Lost track of person, recovering..." )
397
404
person_trajectory = PoseArray ()
398
- recovery = self ._recover_vision (prev_goal )
399
- self ._recover_track (say = not recovery )
405
+ ask_back : bool = False
406
+
407
+ if prev_track is not None :
408
+ robot_pose : PoseStamped = self ._robot_pose_in_odom ()
409
+ if robot_pose :
410
+ dist : float = self ._euclidian_distance (
411
+ robot_pose .pose , prev_track .pose
412
+ )
413
+ rospy .loginfo (f"Distance to last known position: { dist } " )
414
+ if dist >= MAX_VISION_DIST :
415
+ ask_back = True
416
+ else :
417
+ ask_back = True
418
+ else :
419
+ ask_back = True
420
+
421
+ if not ask_back :
422
+ self ._recover_track (say = not self ._recover_vision (prev_goal ))
423
+ else :
424
+ self ._recover_track (say = True )
425
+
400
426
prev_track = None
401
427
continue
402
428
@@ -418,6 +444,9 @@ def follow(self) -> None:
418
444
last_goal_time = rospy .Time .now ()
419
445
prev_track = track
420
446
447
+ if np .mean ([np .linalg .norm (vel ) for vel in track_vels ]) > self ._max_speed :
448
+ self ._tts ("Please walk slower, I am struggling to keep up" , wait = False )
449
+
421
450
# Distance to the previous pose
422
451
dist_to_prev = (
423
452
self ._euclidian_distance (track .pose , prev_track .pose )
@@ -448,10 +477,51 @@ def follow(self) -> None:
448
477
elif (
449
478
np .mean ([np .linalg .norm (vel ) for vel in track_vels ])
450
479
< self ._static_speed
451
- ):
452
-
453
- rospy .logwarn ("Person has been static for too long, stopping" )
480
+ ) and len (track_vels ) == 10 :
481
+ rospy .logwarn (
482
+ "Person has been static for too long, going to them and stopping"
483
+ )
484
+ # cancel current goal
454
485
self ._cancel_goal ()
486
+
487
+ # clear velocity buffer
488
+ track_vels = []
489
+
490
+ robot_pose : PoseStamped = self ._robot_pose_in_odom ()
491
+ dist : float = self ._euclidian_distance (track .pose , robot_pose .pose )
492
+ rospy .loginfo (f"Distance to person: { dist } " )
493
+
494
+ # If the person is too far away, go to them
495
+ if dist > self ._stopping_distance :
496
+ goal_pose = self ._get_pose_on_path (
497
+ self ._tf_pose (robot_pose , "map" ),
498
+ self ._tf_pose (
499
+ PoseStamped (
500
+ pose = track .pose ,
501
+ header = tracks .header ,
502
+ ),
503
+ "map" ,
504
+ ),
505
+ self ._stopping_distance ,
506
+ )
507
+ # If we can't find a path, face them
508
+ if goal_pose is None :
509
+ rospy .logwarn ("Could not find a path to the person" )
510
+ goal_pose = robot_pose
511
+ goal_pose .pose .orientation = self ._compute_face_quat (
512
+ robot_pose .pose , track .pose
513
+ )
514
+ goal_pose = self ._tf_pose (goal_pose , "map" )
515
+ # Otherwise, face them
516
+ else :
517
+ goal_pose = robot_pose
518
+ goal_pose .pose .orientation = self ._compute_face_quat (
519
+ robot_pose .pose , track .pose
520
+ )
521
+ goal_pose = self ._tf_pose (goal_pose , "map" )
522
+
523
+ self ._move_base (goal_pose )
524
+
455
525
if self ._check_finished ():
456
526
rospy .loginfo ("Finished following person" )
457
527
break
0 commit comments