node.py
Go to the documentation of this file.
00001 #
00002 # License: BSD
00003 #   https://raw.github.com/yujinrobot/yujin_ocs/license/LICENSE
00004 #
00005 ##############################################################################
00006 # Imports
00007 ##############################################################################
00008 
00009 import rospy
00010 import std_msgs.msg as std_msgs
00011 import geometry_msgs.msg as geometry_msgs
00012 import threading
00013 import tf
00014 import dynamic_reconfigure.client
00015 
00016 
00017 # Local imports
00018 from .rotate import Rotate
00019 
00020 
00021 class Node(object):
00022     '''
00023       Manages connectivity information provided by services and provides this
00024       for human interactive agent (aka remocon) connections.
00025     '''
00026     __slots__ = [
00027             '_publishers',
00028             '_subscribers',
00029             '_parameters',
00030             '_spotted_markers',
00031             '_target_frame',
00032             '_target_base_t',
00033             '_target_base_o',
00034             '_child_frame_id',
00035             '_parent_frame_id',
00036             '_thread',
00037             '_tf_thread',
00038             '_tf_broadcaster',
00039             '_rotate',
00040             '_rate',
00041             '_listener',
00042             '_running',
00043             '_controller_finished',
00044             '_stop_requested',
00045             '_dynamic_reconfigure_client'
00046         ]
00047     SPOTTED_NONE = 'none'
00048     SPOTTED_LEFT = 'left'
00049     SPOTTED_RIGHT = 'right'
00050     SPOTTED_BOTH = 'both'
00051 
00052     ##########################################################################
00053     # Initialisation
00054     ##########################################################################
00055 
00056     def __init__(self):
00057         self._rotate = Rotate('~cmd_vel')
00058         self._publishers, self._subscribers = self._setup_ros_api()
00059         self._parameters = self._setup_parameters()
00060         self._spotted_markers = Node.SPOTTED_NONE
00061         self._thread = None
00062         self._tf_thread = None
00063         self._running = False
00064         self._rate = 0.36  # this could be parameterised
00065         self._listener = tf.TransformListener()
00066         self._tf_broadcaster = tf.TransformBroadcaster()
00067         self._controller_finished = False
00068         self._stop_requested = False
00069         self._dynamic_reconfigure_client = dynamic_reconfigure.client.Client(rospy.get_param('~ar_tracker', 'ar_track_alvar'))
00070 
00071     def _setup_parameters(self):
00072         parameters = {}
00073         parameters['search_only'] = rospy.get_param('~search_only', False)
00074         parameters['base_postfix'] = rospy.get_param('~base_postfix', 'base')
00075         parameters['base_frame']  = rospy.get_param('~base_frame', 'base_footprint')
00076         parameters['odom_frame']  = rospy.get_param('~odom_frame', 'odom')
00077         return parameters
00078 
00079     def _setup_ros_api(self):
00080         '''
00081           These are all public topics. Typically that will drop them into the /concert
00082           namespace.
00083         '''
00084         publishers = {}
00085         publishers['result'] = rospy.Publisher('~result', std_msgs.Bool, queue_size=5)
00086         publishers['initial_pose_trigger'] = rospy.Publisher('~initialise', std_msgs.Empty, queue_size=5)
00087         publishers['enable_approach_controller'] = rospy.Publisher('~enable_approach_controller', std_msgs.String, queue_size=5)
00088         publishers['disable_approach_controller'] = rospy.Publisher('~disable_approach_controller', std_msgs.Empty, queue_size=5)
00089         subscribers = {}
00090         subscribers['enable'] = rospy.Subscriber('~enable', std_msgs.String, self._ros_enable_subscriber)
00091         subscribers['spotted_markers'] = rospy.Subscriber('~spotted_markers', std_msgs.String, self._ros_spotted_subscriber)
00092         subscribers['relative_target_pose'] = rospy.Subscriber('~relative_target_pose', geometry_msgs.PoseStamped, self._ros_relative_target_pose_subscriber)
00093 
00094         subscribers['approach_controller_result'] = rospy.Subscriber('~approach_pose_reached', std_msgs.Bool, self._ros_controller_result_callback)
00095         return (publishers, subscribers)
00096 
00097     def _is_running(self):
00098         return self._running
00099 
00100     ##########################################################################
00101     # Ros Api Functions
00102     ##########################################################################
00103 
00104     def _ros_enable_subscriber(self, msg):
00105         if msg.data:
00106             rospy.loginfo('enable ar pair approach')
00107             if not self._is_running():
00108                 self._running = True
00109                 self._set_target_base_transform(msg.data)
00110                 self._tf_thread =threading.Thread(target=self._broadcast_tfs)
00111                 self._tf_thread.start()
00112                 self._thread = threading.Thread(target=self.execute)
00113                 self._thread.start()
00114         else:
00115             rospy.loginfo('disable ar pair approach')
00116             if self._is_running():
00117                 self._publishers['result'].publish(std_msgs.Bool(False))
00118             self._stop()
00119             if self._thread is not None:
00120                 self._thread.join()
00121                 self._thread = None
00122             self._stop_requested = False
00123 
00124     def _ros_relative_target_pose_subscriber(self, msg):
00125         self._relative_target_pose = msg
00126 
00127     def _ros_spotted_subscriber(self, msg):
00128         self._spotted_markers = msg.data
00129         if self._spotted_markers == Node.SPOTTED_BOTH and self._rotate.is_running():
00130             self._rotate.stop()
00131 
00132     def _ros_controller_result_callback(self, msg):
00133         rospy.loginfo("AR Pair Approach : received result from the approach controller.")
00134         if msg.data:
00135             rospy.loginfo("AR Pair Approach : Controller reached the goal.")
00136             self._controller_finished = True
00137         else:
00138             rospy.loginfo("AR Pair Approach : Controller failed to reach the goal.")
00139             # TODO: handle this situation
00140 
00141     ##########################################################################
00142     # Execute
00143     ##########################################################################
00144 
00145     def _stop(self):
00146         if not self._rotate.is_stopped():
00147             self._rotate.stop()
00148         self._stop_requested = True
00149 
00150     def _post_execute(self, result):
00151         self._stop_requested = False
00152         if not rospy.is_shutdown():
00153             self._dynamic_reconfigure_client.update_configuration({'enabled': 'False'})
00154             self._publishers['result'].publish(std_msgs.Bool(result))
00155         self._running = False
00156         self._tf_thread.join()
00157 
00158     def execute(self):
00159         self._dynamic_reconfigure_client.update_configuration({'enabled': 'True'})
00160         found_markers = self._initialise_rotation()
00161         if not found_markers:
00162             result = self._rotate.execute()
00163             if not result or self._stop_requested:
00164                 self._post_execute(False)
00165                 return
00166         rospy.loginfo("AR Pair Approach : found both ar pair markers.")
00167         if self._parameters['search_only']:
00168             rospy.loginfo("AR Pair Approach : aborting initialisation and approach as requested.")
00169             return
00170         rospy.loginfo("AR Pair Approach : setting an initial pose from the global ar pair reference.")
00171         self._publishers['initial_pose_trigger'].publish(std_msgs.Empty())
00172         rospy.loginfo("AR Pair Approach : enabling the approach controller")
00173 
00174         #base_target_frame = self._target_frame + '_relative_' + self._parameters['base_postfix']
00175         base_target_frame = self._target_frame + '_' + self._parameters['base_postfix']
00176         self._publishers['enable_approach_controller'].publish(base_target_frame)
00177         while not rospy.is_shutdown() and not self._stop_requested:
00178             if self._controller_finished:
00179                 self._controller_finished = False
00180                 break
00181             rospy.sleep(0.1)
00182         rospy.loginfo("AR Pair Approach : disabling the approach controller")
00183         self._publishers['disable_approach_controller'].publish(std_msgs.Empty())
00184         if rospy.is_shutdown() or self._stop_requested:
00185             self._post_execute(False)
00186         else:
00187             self._post_execute(True)
00188 
00189     def _set_target_base_transform(self, target_frame):
00190         self._target_frame = target_frame
00191         rospy.sleep(2.0)
00192         try:
00193             # this is from odom to target frame
00194             (t, o) = self._listener.lookupTransform(self._parameters['odom_frame'], target_frame, rospy.Time(0))
00195         except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException) as unused_e:
00196             self.logwarn("couldn't get transfrom between %s and %s"%(self._parameters['odom_frame'], target_frame))
00197             return 
00198 
00199         # Transform from target_frame to base target frame
00200         p = (0.0, 0.36, 0.0)
00201         q = tf.transformations.quaternion_from_euler(1.57, -1.57, 0.0)
00202 
00203         self._target_base_t = (t[0], t[1], 0.0)
00204         self._target_base_o = tf.transformations.quaternion_multiply(o, q)
00205 
00206         # Publish relative target frame
00207         base_postfix = self._parameters['base_postfix']
00208         self._parent_frame_id = self._parameters['odom_frame']
00209         self._child_frame_id = target_frame + '_' + base_postfix
00210 
00211     def _broadcast_tfs(self):
00212         r = rospy.Rate(5)
00213 
00214         while self._running and not rospy.is_shutdown():
00215             self._publish_tf() 
00216             r.sleep()   
00217 
00218 
00219     def _publish_tf(self):
00220         self._tf_broadcaster.sendTransform(self._target_base_t, self._target_base_o, rospy.Time.now(), self._child_frame_id, self._parent_frame_id)
00221 
00222     ##########################################################################
00223     # Runtime
00224     ##########################################################################
00225 
00226     def _initialise_rotation(self):
00227         '''
00228           Do not call this if already running, you will cause self._rotate to become volatile.
00229 
00230           @return : True or false depending on if we can skip this step or not.
00231         '''
00232         direction = Rotate.CLOCKWISE
00233         rospy.loginfo("Markers : %s"%self._spotted_markers)
00234         if self._spotted_markers == Node.SPOTTED_BOTH:
00235             rospy.loginfo("AR Pair Approach : received an enable command, both spotted markers already in view!")
00236             return True
00237         elif self._spotted_markers == Node.SPOTTED_LEFT:
00238             rospy.loginfo("AR Pair Approach : received an enable command, only left in view.")
00239         elif self._spotted_markers == Node.SPOTTED_RIGHT:
00240             rospy.loginfo("AR Pair Approach : received an enable command, only right in view.")
00241             direction = Rotate.COUNTER_CLOCKWISE
00242         else:  # self._spotted_markers == Node.SPOTTED_NONE
00243             try:
00244                 rospy.logerr("AR Pair Approach : this should not happen")
00245                 # this is from global to base footprint
00246                 (unused_t, orientation) = self._listener.lookupTransform(self._target_frame, self._parameters['base_frame'], rospy.Time(0))
00247                 unused_roll, unused_pitch, yaw = tf.transformations.euler_from_quaternion(orientation)
00248                 rospy.loginfo("AR Pair Search : current yaw = %s" % str(yaw))
00249                 direction = Rotate.COUNTER_CLOCKWISE if yaw > 0 else Rotate.CLOCKWISE
00250             except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException) as unused_e:
00251                 direction = Rotate.COUNTER_CLOCKWISE
00252             rospy.loginfo("AR Pair Search: received an enable command, none in view.")
00253         self._rotate.init(yaw_absolute_rate=self._rate, yaw_direction=direction)
00254         return False
00255 
00256     def logwarn(self, msg):
00257         rospy.logwarn('AR Pair Approach : %s'%msg)
00258 
00259     def spin(self):
00260         '''
00261           Parse the set of /remocons/<name>_<uuid> connections.
00262         '''
00263 
00264 
00265         rospy.spin()
00266         if self._thread is not None:
00267             self._thread.join()


yocs_ar_pair_approach
Author(s): Daniel Stonier
autogenerated on Thu Jun 6 2019 21:47:14