Python-learning-SMACH-Notes

  • Userdata transfer from State to sub State Machine
    If we would like to transfer data from state in the top State machine to the sub state machine, we can remap the outkey of state with the input key of sub state machine , which could also be used in any transformation from state to state machine,but also we could create a userdata in the top state machine and remap it from outkey of state when adding the state and remap it to input key of sub state machine when adding the sub state machine, which means that we create an userdata as a bridge between state and sub state machine.
    If we want to use this input key of sub state machine, for example, passing the input key to an user data in sub state machine which will be used for other states in this sub state machine, then we need rewrite the class of this sub state machine to overriding the function of execute where we can set the user data according to the input key.
  • Concurrence
     This state allows for simple split-join concurrency. The user adds a set of
        states which are all executed simultaneously. The concurrent split state
        can only transition once all contained states are ready to transition.
        
        This container can be configured to return a given outcome as a function of
        the outcomes of the contained states. This is specified in the constructor
        of the class, or after construction with L{Concurrence.add_outcome_map}.

        While a concurrence will not terminate until all if its children terminate,
        it is possible for it to preempt a subset of states 
         - All child states terminate
         - At least one child state terminates
         - A user-defined callback signals termination

        Given these causes of termination, the outcome can be determined in four ways:
         - A user-defined callback returns an outcome
         - A child-outcome map which requires ALL states to terminate is satisfied
         - A child-outcome map which requires ONE state to terminate is satisfied
         - No  maps are satisfied, so the default outcome is returned

        The specification of the outcome maps and the outcome callback are
        described in the constructor documentation below. More than one policy can
        be supplied, and each policy has the potential to not be satisfied. In the
        situation in which multiple policies are provided, and a given policy is
        not satisfied, the outcome choice precedence is as follows:
         - Outcome callback
         - First-triggered outcome map
         - last-triggered outcome map
         - Default outcome

        In practive it is best to try to accomplish your task with just ONE outcome
        policy
    def   __init__ ( self ,
                 outcomes ,
                 default_outcome ,
                 input_keys  = [],
                 output_keys  = [],
                 outcome_map  = {},
                 outcome_cb  =  None ,
                 child_termination_cb  =  None
                ):
             """Constructor for smach Concurrent Split.

            @type outcomes: list of strings
            @param outcomes: The potential outcomes of this state machine.

            @type default_outcome: string
            @param default_outcome: The outcome of this state if no elements in the 
            outcome map are satisfied by the outcomes of the contained states.


            @type outcome_map: list
            @param outcome_map: This is an outcome map for determining the
            outcome of this container. Each outcome of the container is mapped
            to a dictionary mapping child labels onto outcomes. If none of the
            child-outcome maps is satisfied, the concurrence will terminate
            with thhe default outcome.
            
            For example, if the and_outcome_map is:
                {'succeeded' : {'FOO':'succeeded', 'BAR':'done'},
                 'aborted' : {'FOO':'aborted'}}
            Then the concurrence will terimate with outcome 'succeeded' only if
            BOTH states 'FOO' and 'BAR' have terminated
            with outcomes 'succeeded' and 'done', respectively. The outcome
            'aborted' will be returned by the concurrence if the state 'FOO'
            returns the outcome 'aborted'. 

            If the outcome of a state is not specified, it will be treated as
            irrelevant to the outcome of the concurrence

            If the criteria for one outcome is the subset of another outcome,
            the container will choose the outcome which has more child outcome
            criteria satisfied. If both container outcomes have the same
            number of satisfied criteria, the behavior is undefined.

            If a more complex outcome policy is required, see the user can
            provide an outcome callback. See outcome_cb, below.

            @type child_termination_cb: callale
            @param child_termination_cb: This callback gives the user the ability
            to force the concurrence to preempt running states given the
            termination of some other set of states. This is useful when using
            a concurrence as a monitor container. 

            This callback is called each time a child state terminates. It is
            passed a single argument, a dictionary mapping child state labels
            onto their outcomes. If a state has not yet terminated, it's dict
            value will be None.

            This function can return three things:
             - False: continue blocking on the termination of all other states
             - True: Preempt all other states
             - list of state labels: Preempt only the specified states

            I{If you just want the first termination to cause the other children
            to terminate, the callback (lamda so: True) will always return True.}

            @type outcome_cb: callable
            @param outcome_cb: If the outcome policy needs to be more complicated
            than just a conjunction of state outcomes, the user can supply
            a callback for specifying the outcome of the container.

            This callback is called only once all child states have terminated,
            and it is passed the dictionary mapping state labels onto their
            respective outcomes.

            If the callback returns a string, it will treated as the outcome of
            the container.

            If the callback returns None, the concurrence will first check the
            outcome_map, and if no outcome in the outcome_map is satisfied, it
            will return the default outcome.

            B{ NOTE : This callback should be a function ONLY of the outcomes of
            the child states. It should not access any other resources.} 
  • Simple Action State (ROS)
    class   SimpleActionState ( State ):
         """Simple action client state.
        
        Use this class to represent an actionlib as a state in a state machine.
        """

         # Meta-states for this action
        WAITING_FOR_SERVER =  0
        INACTIVE =  1
        ACTIVE =  2
        PREEMPTING =  3
        COMPLETED =  4

         def   __init__ ( self ,
                 # Action info
                 action_name ,
                 action_spec
                 # Default goal 
                 goal  =  None ,
                 goal_key  =  None ,
                 goal_slots  = [],
                 goal_cb  =  None ,
                 goal_cb_args  = [],
                 goal_cb_kwargs  = {},
                 # Result modes
                 result_key  =  None ,
                 result_slots  = [],
                 result_cb  =  None ,
                 result_cb_args  = [],
                 result_cb_kwargs  = {},
                 # Keys
                 input_keys  = [],
                 output_keys  = [],
                 outcomes  = [],
                 # Timeouts
                 exec_timeout  =  None ,
                 preempt_timeout  = rospy.Duration( 60.0 ),
                 server_wait_timeout  = rospy.Duration( 60.0 )
                ):
             """Constructor for SimpleActionState action client wrapper.
            
            @type action_name: string
            @param action_name: The name of the action as it will be broadcast over ros.

            @type action_spec: actionlib action msg (.action file to generate automatically)
            @param action_spec: The type of action to which this client will connect.

            @type goal: actionlib goal msg
            @param goal: If the goal for this action does not need to be generated at
            runtime, it can be passed to this state on construction.

            @type goal_key: string
            @param goal_key: Pull the goal message from a key in the userdata.
            This will be done before calling the goal_cb if goal_cb is defined.

            @type goal_slots: list of string (as input_keys remapping to userdata)
            @param goal_slots: Pull the goal fields (__slots__) from like-named
            keys in userdata. This will be done before calling the goal_cb if 
            goal_cb is defined.

            @type goal_cb: callable
            @param goal_cb: If the goal for this action needs to be generated at
            runtime, a callback can be stored which modifies the default goal
            object. The callback is passed two parameters:
                - userdata
                - current stored goal
            The callback  returns a goal message.

            @type result_key: string
            @param result_key: Put the result message into the userdata with
            the given key. This will be done after calling the result_cb
            if result_cb is defined.

            @type result_slots: list of strings (as output_keys directly remapping to userdata)
            @param result_slots: Put the result message fields (__slots__)
            into the userdata with keys like the field names. This will be done
            after calling the result_cb if result_cb is defined.

            @type result_cb: callable
            @param result_cb: If result information from this action needs to be
            stored or manipulated on reception of a result from this action, a
            callback can be stored which is passed this information. The callback
            is passed three parameters:
                - userdata (L{UserData<smach.user_data.UserData>})
                - result status (C{actionlib.GoalStatus})
                - result (actionlib result msg)

            @type exec_timeout: C{rospy.Duration}
            @param exec_timeout: This is the timeout used for sending a preempt message
            to the delegate action. This is C{None} by default, which implies no
            timeout. 

            @type preempt_timeout: C{rospy.Duration}
            @param preempt_timeout: This is the timeout used for aborting after a
            preempt has been sent to the action and no result has been received. This
            timeout begins counting after a preempt message has been sent.

            @type server_wait_timeout: C{rospy.Duration}
            @param server_wait_timeout: This is the timeout used for aborting while
            waiting for an action server to become active.
            """
  • Sequence container
    sq = Sequence(
            outcomes = ['succeeded','aborted','preempted'],
            connector_outcome = 'succeeded')
    with sq:
        Sequence.add('MOVE_ARM_GRAB_PRE', MoveVerticalGripperPoseActionState())
        Sequence.add('MOVE_GRIPPER_OPEN', MoveGripperState(GRIPPER_MAX_WIDTH))
        Sequence.add('MOVE_ARM_GRAB',     MoveVerticalGripperPoseActionState())
        Sequence.add('MOVE_GRIPPER_CLOSE', MoveGripperState(grab_width))
        Sequence.add('MOVE_ARM_GRAB_POST', MoveVerticalGripperPoseActionState())

    class   Sequence ( smach . state_machine . StateMachine ):
         """Sequence Container

        This container inherits functionality from L{smach.StateMachine} and adds
        some auto-generated transitions that create a sequence of states from the
        order in which said states are added to the container.
        """
         def   __init__ ( self ,
                 outcomes ,
                 connector_outcome ,
                 input_keys =[],
                 output_keys =[]):
             """Constructor.

            @type outcomes: list of string
            @param outcomes: The potential outcomes of this container.

            @type connector_outcome: string
            @param connector_outcome: The outcome used to connect states in the
            sequence.
            """
            smach.state_machine.StateMachine. __init__ ( self , outcomes, input_keys, output_keys)

             self ._last_added_seq_label =  None
             self ._connector_outcome = connector_outcome

         ### Construction Methods
         @ staticmethod
         def   add ( label state transitions  =  None remapping  =  None ):
             """Add a state to the sequence.
            Each state added will receive an additional transition from it to the
            state which is added after it. The transition will follow the outcome
            specified at construction of this container.
            
            @type label: string
            @param label: The label of the state being added.
            
            @param state: An instance of a class implementing the L{State} interface.
            
            @param transitions: A dictionary mapping state outcomes to other state
            labels. If one of these transitions follows the connector outcome
            specified in the constructor, the provided transition will override
            the automatically generated connector transition.
            """
  • Iterator container
    class   Iterator ( smach . container . Container ):
         """Sequence Container

        This container inherits functionality from L{smach.StateMachine} and adds
        some auto-generated transitions that create a sequence of states from the
        order in which said states are added to the container.
        """
         def   __init__ ( self ,
                 outcomes ,
                 input_keys ,
                 output_keys ,
                 it  = [],
                 it_label  =  'it_data' ,
                 exhausted_outcome  =  'exhausted' ):
             """Constructor.

            @type outcomes: list of string
            @param outcomes: The potential outcomes of this container.

            @type it: iterable
            @param iteritems: Items to iterate over on each cycle

            @type it_label: string
            @param iteritems_label: The label that the item in the current
            iteration will be given when it is put into the container's local
            userdata.
            """
             if  exhausted_outcome not in outcomes:
                outcomes.append(exhausted_outcome)
            smach.container.Container. __init__ ( self , outcomes, input_keys, output_keys)

             self ._items = it
             self ._items_label = it_label

             self ._is_running =  False

             self ._state_label =  ''
             self ._state =  None
             self ._loop_outcomes = []
             self ._break_outcomes = []
             self ._final_outcome_map = {}
             self ._exhausted_outcome = exhausted_outcome


         ### Construction Methods
         @ staticmethod
         def   set_iteritems ( it it_label = 'it_data' ):
             """Set the list or generator for the iterator to iterate over.
            
            @type it: iterable
            @param iteritems: Items to iterate over on each cycle

            @type it_label: string
            @param iteritems_label: The label that the item in the current
            iteration will be given when it is put into the container's local
            userdata.

            @type exhausted_outcome: string
            @param exhausted_outcome: If the iterable is exhausted without a break
            condition this outcome is emitted by the container.
            """
             # Get currently opened container
             self  = Iterator._currently_opened_container()
             self ._items = it
             self ._items_label = it_label

         @ staticmethod
         def   set_contained_state (
                 label ,
                 state ,
                 loop_outcomes  = [],
                 break_outcomes  = [],
                 final_outcome_map  = {}):
             """Set the contained state
            
            @type label: string
            @param label: The label of the state being added.
            
            @type state: L{smach.State}
            @param state: An instance of a class implementing the L{smach.State} interface.

            @param loop_outcomes: List of contained state outcomes that should cause
            the iterator to continue. If this is empty, all outcomes that are not
            in the break_outcomes list will cause the iterator to continue to
            iterate.  NOTE : loop_outcomes will be overriden by break_outcomes if both
            parameters are used.

            @param break_outcomes: List of contained state outcomes that should
            cause the iterator to break. When the contained state emits an outcome
            in this list, the container will terminate and return either that
            outcome or the outcome it is mapped to in final_outcome_map.  NOTE :
            loop_outcomes will be overriden by break_outcomes if both
            parameters are used.

            @param final_outcome_map: A map from contained state outcomes to container
            outcomes. On termination of the iterator (either from finishing or from
            a break) this map will be used to translate contained state outcomes to
            container outcomes.
            Unspecified contained state outcomes will fall through as
            container outcomes.
            """

  • ActionServerWrapper (Wrapper a container with Actionlib)


    # Construct state machine
    sm = StateMachine(
            outcomes=['did_something','did_something_else','aborted','preempted'],
            input_keys = ['my_awesome_goal'],
            output_keys = ['egad_its_a_result'])
    
    
    # Construct action server wrapperasw = ActionServerWrapper( 'my_action_server_name', MyAction, sm,
    succeeded_outcomes =['did_something','did_something_else'],
    
    aborted_outcomes = ['aborted'], 
    
    preempted_outcomes = ['preempted'],
        goal_key = 'my_awesome_goal',
        result_key = 'egad_its_a_result' )

    SMACH provides the top-level container called ActionServerWrapper. This class advertises anactionlibaction server. Instead of being executed by a parent,its contained state goes active when the action server receives a goal. Accordingly, this container does not inherit from the smach.State base class and cannot be put inside of another container

    class   ActionServerWrapper ():
         """SMACH container wrapper with actionlib ActionServer.

        Use this class to associate an action server with a smach
        L{StateMachine<smach.state_machine.StateMachine>}. This allows invocation
        of the state machine over the actionlib API/protocol.

        This class delegates to a provided SMACH container and associates it with an
        action server. The user can specify lists of outcomes which correspond to
        different action result statuses (SUCCEEDED, ABORTED, PREEMPTED). Once the
        delegate state machine leaves one of these outcomes, this wrapper class will
        cause the state machine to terminate, and cause the action server to return
        a result.

        Note that this class does not inherit from L{smach.State<smach.State>} and
        can only be used as a top-level container.
        """

         def   __init__ ( self ,
                 server_name action_spec ,
                 wrapped_container ,
                 succeeded_outcomes  = [],
                 aborted_outcomes  = [],
                 preempted_outcomes  = [],
                 goal_key  =  'action_goal' ,
                 feedback_key  =  'action_feedback' ,
                 result_key  =  'action_result' ,
                 goal_slots_map  = {},
                 feedback_slots_map  = {},
                 result_slots_map  = {},
                 expand_goal_slots  =  False ,
                 pack_result_slots  =  False
                ):
             """Constructor.

            @type server_name: string
            @param server_name: The name of the action server that this container will
            present.

            @type action_spec: actionlib action msg
            @param action_spec: The type of action this server will present

            @type wrapped_container: L{StateMachine}
            @param wrapped_container: The state machine to manipulate

            @type succeeded_outcomes: array of strings
            @param succeeded_outcomes: Array of terminal state labels which, when left,
            should cause the action server to return SUCCEEDED as a result status.

            @type aborted_outcomes: array of strings
            @param aborted_outcomes: Array of terminal state labels which, when left,
            should cause the action server to return ABORTED as a result status.

            @type preempted_outcomes: array of strings
            @param preempted_outcomes: Array of terminal state labels which, when left,
            should cause the action server to return PREEMPTED as a result status.

            @type goal_key: string
            @param goal_key: The userdata key into which the action goal should be
            stuffed when the action server receives one.

            @type feedback_key: string
            @param feedback_key: The userdata key into which the SMACH container
            can put feedback information relevant to the action.

            @type result_key: string
            @param result_key: The userdata key into which the SMACH container
            can put result information from this action.
            """

  • ServiceState (ROS)

    You could simply call any service from ageneric state, but SMACH has specific support to call services, saving you a lot of code! SMACH provides a state class that acts as a proxy to aROS service. The instantiation of the state takes a service name, service type, and some policy for generating a service request. The possible outcomes of the service state are 'succeeded', 'preempted' and 'aborted'.

    /!\ The service state is almost identical to the simple action state. Just replace 'goal' with 'request', and 'result' with 'response'.
  • MonitorState(ROS)


    class   MonitorState ( smach . State ):
         """A state that will check a given ROS topic with a condition function. 
        """
         def   __init__ ( self topic msg_type cond_cb max_checks =- 1 , input_keys  = [], output_keys =[]):
            smach.State. __init__ ( self , outcomes =[ 'valid' , 'invalid' , 'preempted' ],  input_keys  = input_keys, output_keys  = output_keys)

             self ._topic = topic
             self ._msg_type = msg_type
             self ._cond_cb = cond_cb                    
             self ._max_checks = max_checks
             self ._n_checks =  0

             self ._trigger_event = threading.Event()

         def   execute ( self ud ):
             # If prempted before even getting a chance, give up.
             if   self .preempt_requested():
                 self .service_preempt()
                 return   'preempted'
            
             self ._n_checks =  0
             self ._trigger_event.clear()
            
             self ._sub = rospy.Subscriber( self ._topic,  self ._msg_type,  self ._cb,  callback_args =ud)

             self ._trigger_event.wait()
             self ._sub.unregister()

             if   self .preempt_requested():
                 self .service_preempt()
                 return   'preempted'

             if   self ._max_checks >  0  and  self ._n_checks >=  self ._max_checks:
                 return   'valid'

             return   'invalid'

         def   _cb ( self , msg , ud ) :
             self ._n_checks +=  1
             try :
                 if  ( self ._max_checks >  0  and  self ._n_checks >=  self ._max_checks) or not  self ._cond_cb(ud, msg):
                     self ._trigger_event.set()
             except   Exception   as  e:
                rospy.logerr( "Error thrown while executing condition callback %s: %s"  % ( str ( self ._cond_cb), e))
                 self ._trigger_event.set()

         def   request_preempt ( self ):
            smach.State.request_preempt( self )
             self ._trigger_event.set()


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值