1
1
#!/usr/bin/env python
2
- import math
2
+
3
3
import sys
4
- import copy
5
- from tokenize import group
4
+
6
5
import rospy
7
- import time
8
6
import moveit_commander
9
7
import moveit_msgs .msg
10
- import geometry_msgs .msg
11
- from random import random
12
- import os
13
- import subprocess , shlex , psutil
14
-
15
- from math import pi
16
-
17
- import std_msgs .msg
18
- from std_msgs .msg import String
19
- from moveit_commander .conversions import pose_to_list
20
- from tf .transformations import quaternion_from_euler
21
-
8
+ import tf
22
9
from std_msgs .msg import String
23
10
from sensor_msgs .msg import Range
24
- import csv
25
-
26
11
import numpy as np
12
+ from message_filters import ApproximateTimeSynchronizer , Subscriber
27
13
28
14
from applevision_rospkg .msg import PointWithCovarianceStamped , RegionOfInterestWithCovarianceStamped
29
15
30
- import tf
31
-
32
-
33
- class approachPlanner (object ):
34
- def __init__ (self ):
35
- super (approachPlanner , self ).__init__ ()
36
-
16
+ class SynchronizerMinTick (ApproximateTimeSynchronizer ):
17
+ """
18
+ Calls a callback if one of two conditions occur:
19
+ 1. A group of messages have timestamps within self.slop
20
+ 2. An above group has not occured within self.min_tick
21
+ In other words, this class seeks to synchronize incoming messages with the constraint that
22
+ we would also like the callback to be invoked at a minimum frequency. This behavior allows
23
+ for graceful failure when a sensor stops publishing unexpectedly.
24
+
25
+ If the min_tick timer elapses and not all messages are ready, the callback will be invoked
26
+ with the earliest message from the highest priority subscriber (in order passed to the constructor),
27
+ all topics that do not have a message recent enough will be filled with None.
28
+ """
29
+
30
+ def __init__ (self , fs , queue_size , slop , min_tick ):
31
+ super (SynchronizerMinTick , self ).__init__ (fs , queue_size , slop )
32
+ self .min_tick = rospy .Duration .from_sec (min_tick )
33
+ self .last_tick = rospy .Time ()
34
+ self .tick_timer = rospy .Timer (self .min_tick , self ._timerCallback , reset = True , oneshot = True )
35
+
36
+ def signalMessage (self , * msg ):
37
+ if self .tick_timer :
38
+ self .tick_timer .shutdown ()
39
+
40
+ self .last_tick = rospy .Time ()
41
+ self .tick_timer = rospy .Timer (self .min_tick , self ._timerCallback , reset = True , oneshot = True )
42
+ return super (SynchronizerMinTick , self ).signalMessage (* msg )
43
+
44
+ def _timerCallback (self , _ ):
45
+ # signalMessage with whatever the latest data is
46
+ with self .lock :
47
+ found_msg = None
48
+ for q in self .queues :
49
+ if not q :
50
+ continue
51
+ highest_stamp = max (q )
52
+ if rospy .Time .now () - highest_stamp <= self .min_tick :
53
+ found_msg = q [highest_stamp ]
54
+
55
+ if not found_msg :
56
+ # nothing has pinged within the tick rate
57
+ rospy .logwarn ('SynchronizerMinTick: no packets within min tick rate' )
58
+ ret = [None for _ in range (len (self .queues ))]
59
+ else :
60
+ target_stamp = found_msg .header .stamp
61
+ # collect all things that have pinged within the slop
62
+ ret = []
63
+ for q in self .queues :
64
+ if not q :
65
+ ret .append (None )
66
+ continue
67
+ best_stamp_diff , best_stamp = min ((abs (target_stamp - s ), s ) for s in q )
68
+ if best_stamp_diff <= self .slop :
69
+ ret .append (q [best_stamp ])
70
+ else :
71
+ ret .append (None )
72
+
73
+ # empty queue and return
74
+ self .signalMessage (* ret )
75
+ for q in self .queues :
76
+ q .clear ()
77
+
78
+
79
+ class ApproachPlanner ():
80
+ def __init__ (self , acSub , adSub ):
37
81
# Moveit Setup
38
-
39
82
moveit_commander .roscpp_initialize (sys .argv )
40
83
41
84
self .robot = moveit_commander .RobotCommander ()
@@ -50,6 +93,7 @@ def __init__(self):
50
93
51
94
self .tf_listener_ = tf .TransformListener ()
52
95
96
+
53
97
# CV Details
54
98
55
99
self .CAMERA_RES = np .array ((640 , 360 ))
@@ -62,7 +106,9 @@ def __init__(self):
62
106
'pixel' : 75 , # center vec
63
107
'dist' : 0.2 , # distance sensor
64
108
'dead' : 0.4 , # dead recking zone
65
- 'miss' : 20 # count dropped
109
+ 'miss' : 20 , # count dropped
110
+ 'slop' : 0.017 ,# sec timestamp off
111
+ 'tick' : 1 # sec no data without action
66
112
}
67
113
68
114
self .distMissCount = 0
@@ -73,30 +119,36 @@ def __init__(self):
73
119
self .A = None
74
120
self .B = None
75
121
76
- def toCenterCoord (self , q ):
77
- return np .array ((q .x + (q .w // 2 ), q .y + (q .h // 2 )))
78
-
79
- def aCCallback (self , res ):
80
- if self .aCLast == None :
81
- self .aCLast = res
82
- self .tryPlan ()
83
- else :
84
- if self .aDLast == None and self .distDisabled is False : self .distMissCount += 1
85
-
86
- if self .distMissCount > self .tolerances ['miss' ]: self .distDisabled = True
122
+ # synchronizer
87
123
88
- def aDCallback ( self , res ):
89
- if self .aDLast == None :
90
- self .aDLast = res
124
+ # registers tryPlan as the message callback.
125
+ self .sync = SynchronizerMinTick ([ acSub , adSub ], 10 , self . tolerances [ 'slop' ], self . tolerances [ 'min_tick' ])
126
+ self .sync . registerCallback ( self . tryPlan )
91
127
92
- if self .distDisabled == True :
93
- self .distDisabled == False
94
- self .distMissCount = 0
128
+ def toCenterCoord (self , q ):
129
+ return np .array ((q .x + (q .w // 2 ), q .y + (q .h // 2 )))
95
130
96
- self .tryPlan ()
97
- pass
131
+ # def aCCallback(self, res):
132
+ # if self.aCLast == None:
133
+ # self.aCLast = res
134
+ # self.tryPlan()
135
+ # else:
136
+ # if self.aDLast == None and self.distDisabled is False: self.distMissCount += 1
137
+ # if self.distMissCount > self.tolerances['miss']: self.distDisabled = True
138
+
139
+ # def aDCallback(self, res):
140
+ # if self.aDLast == None:
141
+ # self.aDLast = res
142
+ # if self.distDisabled == True:
143
+ # self.distDisabled == False
144
+ # self.distMissCount = 0
145
+ # self.tryPlan()
146
+ # pass
147
+
148
+ def tryPlan (self , cam_msg , dist_msg ):
149
+ # TODO: do something with cam_msg and dist_msg. Note that cam_msg and dist_msg can be None.
150
+ # TODO: refactor now that messages aren't in self.aCLast and self.aDLast
98
151
99
- def tryPlan (self ):
100
152
# Pre-Exit on Missing Sub
101
153
if self .aCLast == None and (self .aDLast == None or self .distDisabled == True ):
102
154
rospy .logwarn ("stuck doing nothing" )
@@ -249,10 +301,9 @@ def main():
249
301
rospy .init_node ('applevision_motion' )
250
302
rospy .wait_for_service ('Tf2Transform' )
251
303
252
- aP = approachPlanner ()
253
-
254
- aCpub = rospy .Subscriber ('applevision/apple_camera' , RegionOfInterestWithCovarianceStamped , aP .aCCallback , queue_size = 1 )
255
- aDPub = rospy .Subscriber ('applevision/apple_dist' , Range , aP .aDCallback , queue_size = 10 )
304
+ aCsub = Subscriber ('applevision/apple_camera' , RegionOfInterestWithCovarianceStamped )
305
+ aDsub = Subscriber ('applevision/apple_dist' , Range )
306
+ aP = ApproachPlanner (aCsub , aDsub )
256
307
257
308
rospy .spin ()
258
309
0 commit comments