node.py
Go to the documentation of this file.
1 #
2 # License: BSD
3 # https://raw.github.com/yujinrobot/yujin_ocs/license/LICENSE
4 #
5 ##############################################################################
6 # Imports
7 ##############################################################################
8 
9 import rospy
10 import std_msgs.msg as std_msgs
11 import geometry_msgs.msg as geometry_msgs
12 import threading
13 import tf
14 import dynamic_reconfigure.client
15 
16 
17 # Local imports
18 from .rotate import Rotate
19 
20 
21 class Node(object):
22  '''
23  Manages connectivity information provided by services and provides this
24  for human interactive agent (aka remocon) connections.
25  '''
26  __slots__ = [
27  '_publishers',
28  '_subscribers',
29  '_parameters',
30  '_spotted_markers',
31  '_target_frame',
32  '_target_base_t',
33  '_target_base_o',
34  '_child_frame_id',
35  '_parent_frame_id',
36  '_thread',
37  '_tf_thread',
38  '_tf_broadcaster',
39  '_rotate',
40  '_rate',
41  '_listener',
42  '_running',
43  '_controller_finished',
44  '_stop_requested',
45  '_dynamic_reconfigure_client'
46  ]
47  SPOTTED_NONE = 'none'
48  SPOTTED_LEFT = 'left'
49  SPOTTED_RIGHT = 'right'
50  SPOTTED_BOTH = 'both'
51 
52  ##########################################################################
53  # Initialisation
54  ##########################################################################
55 
56  def __init__(self):
57  self._rotate = Rotate('~cmd_vel')
58  self._publishers, self._subscribers = self._setup_ros_api()
60  self._spotted_markers = Node.SPOTTED_NONE
61  self._thread = None
62  self._tf_thread = None
63  self._running = False
64  self._rate = 0.36 # this could be parameterised
67  self._controller_finished = False
68  self._stop_requested = False
69  self._dynamic_reconfigure_client = dynamic_reconfigure.client.Client(rospy.get_param('~ar_tracker', 'ar_track_alvar'))
70 
71  def _setup_parameters(self):
72  parameters = {}
73  parameters['search_only'] = rospy.get_param('~search_only', False)
74  parameters['base_postfix'] = rospy.get_param('~base_postfix', 'base')
75  parameters['base_frame'] = rospy.get_param('~base_frame', 'base_footprint')
76  parameters['odom_frame'] = rospy.get_param('~odom_frame', 'odom')
77  return parameters
78 
79  def _setup_ros_api(self):
80  '''
81  These are all public topics. Typically that will drop them into the /concert
82  namespace.
83  '''
84  publishers = {}
85  publishers['result'] = rospy.Publisher('~result', std_msgs.Bool, queue_size=5)
86  publishers['initial_pose_trigger'] = rospy.Publisher('~initialise', std_msgs.Empty, queue_size=5)
87  publishers['enable_approach_controller'] = rospy.Publisher('~enable_approach_controller', std_msgs.String, queue_size=5)
88  publishers['disable_approach_controller'] = rospy.Publisher('~disable_approach_controller', std_msgs.Empty, queue_size=5)
89  subscribers = {}
90  subscribers['enable'] = rospy.Subscriber('~enable', std_msgs.String, self._ros_enable_subscriber)
91  subscribers['spotted_markers'] = rospy.Subscriber('~spotted_markers', std_msgs.String, self._ros_spotted_subscriber)
92  subscribers['relative_target_pose'] = rospy.Subscriber('~relative_target_pose', geometry_msgs.PoseStamped, self._ros_relative_target_pose_subscriber)
93 
94  subscribers['approach_controller_result'] = rospy.Subscriber('~approach_pose_reached', std_msgs.Bool, self._ros_controller_result_callback)
95  return (publishers, subscribers)
96 
97  def _is_running(self):
98  return self._running
99 
100  ##########################################################################
101  # Ros Api Functions
102  ##########################################################################
103 
104  def _ros_enable_subscriber(self, msg):
105  if msg.data:
106  rospy.loginfo('enable ar pair approach')
107  if not self._is_running():
108  self._running = True
109  self._set_target_base_transform(msg.data)
110  self._tf_thread =threading.Thread(target=self._broadcast_tfs)
111  self._tf_thread.start()
112  self._thread = threading.Thread(target=self.execute)
113  self._thread.start()
114  else:
115  rospy.loginfo('disable ar pair approach')
116  if self._is_running():
117  self._publishers['result'].publish(std_msgs.Bool(False))
118  self._stop()
119  if self._thread is not None:
120  self._thread.join()
121  self._thread = None
122  self._stop_requested = False
123 
126 
127  def _ros_spotted_subscriber(self, msg):
128  self._spotted_markers = msg.data
129  if self._spotted_markers == Node.SPOTTED_BOTH and self._rotate.is_running():
130  self._rotate.stop()
131 
133  rospy.loginfo("AR Pair Approach : received result from the approach controller.")
134  if msg.data:
135  rospy.loginfo("AR Pair Approach : Controller reached the goal.")
136  self._controller_finished = True
137  else:
138  rospy.loginfo("AR Pair Approach : Controller failed to reach the goal.")
139  # TODO: handle this situation
140 
141  ##########################################################################
142  # Execute
143  ##########################################################################
144 
145  def _stop(self):
146  if not self._rotate.is_stopped():
147  self._rotate.stop()
148  self._stop_requested = True
149 
150  def _post_execute(self, result):
151  self._stop_requested = False
152  if not rospy.is_shutdown():
153  self._dynamic_reconfigure_client.update_configuration({'enabled': 'False'})
154  self._publishers['result'].publish(std_msgs.Bool(result))
155  self._running = False
156  self._tf_thread.join()
157 
158  def execute(self):
159  self._dynamic_reconfigure_client.update_configuration({'enabled': 'True'})
160  found_markers = self._initialise_rotation()
161  if not found_markers:
162  result = self._rotate.execute()
163  if not result or self._stop_requested:
164  self._post_execute(False)
165  return
166  rospy.loginfo("AR Pair Approach : found both ar pair markers.")
167  if self._parameters['search_only']:
168  rospy.loginfo("AR Pair Approach : aborting initialisation and approach as requested.")
169  return
170  rospy.loginfo("AR Pair Approach : setting an initial pose from the global ar pair reference.")
171  self._publishers['initial_pose_trigger'].publish(std_msgs.Empty())
172  rospy.loginfo("AR Pair Approach : enabling the approach controller")
173 
174  #base_target_frame = self._target_frame + '_relative_' + self._parameters['base_postfix']
175  base_target_frame = self._target_frame + '_' + self._parameters['base_postfix']
176  self._publishers['enable_approach_controller'].publish(base_target_frame)
177  while not rospy.is_shutdown() and not self._stop_requested:
178  if self._controller_finished:
179  self._controller_finished = False
180  break
181  rospy.sleep(0.1)
182  rospy.loginfo("AR Pair Approach : disabling the approach controller")
183  self._publishers['disable_approach_controller'].publish(std_msgs.Empty())
184  if rospy.is_shutdown() or self._stop_requested:
185  self._post_execute(False)
186  else:
187  self._post_execute(True)
188 
189  def _set_target_base_transform(self, target_frame):
190  self._target_frame = target_frame
191  rospy.sleep(2.0)
192  try:
193  # this is from odom to target frame
194  (t, o) = self._listener.lookupTransform(self._parameters['odom_frame'], target_frame, rospy.Time(0))
196  self.logwarn("couldn't get transfrom between %s and %s"%(self._parameters['odom_frame'], target_frame))
197  return
198 
199  # Transform from target_frame to base target frame
200  p = (0.0, 0.36, 0.0)
201  q = tf.transformations.quaternion_from_euler(1.57, -1.57, 0.0)
202 
203  self._target_base_t = (t[0], t[1], 0.0)
204  self._target_base_o = tf.transformations.quaternion_multiply(o, q)
205 
206  # Publish relative target frame
207  base_postfix = self._parameters['base_postfix']
208  self._parent_frame_id = self._parameters['odom_frame']
209  self._child_frame_id = target_frame + '_' + base_postfix
210 
211  def _broadcast_tfs(self):
212  r = rospy.Rate(5)
213 
214  while self._running and not rospy.is_shutdown():
215  self._publish_tf()
216  r.sleep()
217 
218 
219  def _publish_tf(self):
220  self._tf_broadcaster.sendTransform(self._target_base_t, self._target_base_o, rospy.Time.now(), self._child_frame_id, self._parent_frame_id)
221 
222  ##########################################################################
223  # Runtime
224  ##########################################################################
225 
227  '''
228  Do not call this if already running, you will cause self._rotate to become volatile.
229 
230  @return : True or false depending on if we can skip this step or not.
231  '''
232  direction = Rotate.CLOCKWISE
233  rospy.loginfo("Markers : %s"%self._spotted_markers)
234  if self._spotted_markers == Node.SPOTTED_BOTH:
235  rospy.loginfo("AR Pair Approach : received an enable command, both spotted markers already in view!")
236  return True
237  elif self._spotted_markers == Node.SPOTTED_LEFT:
238  rospy.loginfo("AR Pair Approach : received an enable command, only left in view.")
239  elif self._spotted_markers == Node.SPOTTED_RIGHT:
240  rospy.loginfo("AR Pair Approach : received an enable command, only right in view.")
241  direction = Rotate.COUNTER_CLOCKWISE
242  else: # self._spotted_markers == Node.SPOTTED_NONE
243  try:
244  rospy.logerr("AR Pair Approach : this should not happen")
245  # this is from global to base footprint
246  (unused_t, orientation) = self._listener.lookupTransform(self._target_frame, self._parameters['base_frame'], rospy.Time(0))
247  unused_roll, unused_pitch, yaw = tf.transformations.euler_from_quaternion(orientation)
248  rospy.loginfo("AR Pair Search : current yaw = %s" % str(yaw))
249  direction = Rotate.COUNTER_CLOCKWISE if yaw > 0 else Rotate.CLOCKWISE
251  direction = Rotate.COUNTER_CLOCKWISE
252  rospy.loginfo("AR Pair Search: received an enable command, none in view.")
253  self._rotate.init(yaw_absolute_rate=self._rate, yaw_direction=direction)
254  return False
255 
256  def logwarn(self, msg):
257  rospy.logwarn('AR Pair Approach : %s'%msg)
258 
259  def spin(self):
260  '''
261  Parse the set of /remocons/<name>_<uuid> connections.
262  '''
263 
264 
265  rospy.spin()
266  if self._thread is not None:
267  self._thread.join()
def __init__(self)
Initialisation.
Definition: node.py:56
def _set_target_base_transform(self, target_frame)
Definition: node.py:189
def _ros_relative_target_pose_subscriber(self, msg)
Definition: node.py:124
def logwarn(self, msg)
Definition: node.py:256
def _initialise_rotation(self)
Runtime.
Definition: node.py:226
def _ros_controller_result_callback(self, msg)
Definition: node.py:132
def _ros_spotted_subscriber(self, msg)
Definition: node.py:127
def _ros_enable_subscriber(self, msg)
Ros Api Functions.
Definition: node.py:104
def _post_execute(self, result)
Definition: node.py:150
def _stop(self)
Execute.
Definition: node.py:145


yocs_ar_pair_approach
Author(s): Daniel Stonier
autogenerated on Mon Jun 10 2019 15:53:27