(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.


Description: Example of a state machine that implements preemption, using a concurrence container and monitor state.

Tutorial Level: ADVANCED

NB - This is just what I coded up while trying to understand how to add preemption into a state machine. Please don't take it for best practices or anything, until JonBohren or somebody with more experience takes a look at it. -LauraLindzey


The goal here is to have a state within a state machine that terminates either after a calculation has been finished OR after a particular condition has been met. We use a concurrence container to join together a simple state that implements preemptionand a monitor state. The monitor state checks if a message has been received to the /sm_reset topic.

The sm starts in SETUP, transitions to FOO, and then loops between FOO and BAR. FOO is a Concurrence Container containing FOO_RESET, a MonitorState, and FOO_CALC, a simple state performing a long calculation. If the machine is in state FOO and a message is sent to /sm_reset, FOO_RESET will return, FOO_CALC will be preempted, and the maachine transitions to SETUP. Otherwise, if FOO_CALC finishes, FOO_RESET is preempted and the machine transitions to BAR.


   1 #!/usr/bin/env python
   2 import roslib; roslib.load_manifest('smach_preemption_example')
   3 import rospy
   4 import smach
   5 import smach_ros
   7 from std_msgs.msg import Empty
   9 class setup(smach.State):
  10     def __init__(self):
  11         smach.State.__init__(self, outcomes=['setup_done'])
  12     def execute(self, userdata):
  13         rospy.sleep(3.5)
  14         return 'setup_done'
  16 class foo(smach.State):
  17     def __init__(self):
  18         smach.State.__init__(self, outcomes=['foo_succeeded', 'preempted'])
  19     def execute(self, userdata):
  20         for idx in range(5):
  21             if self.preempt_requested():
  22                 print "state foo is being preempted!!!"
  23                 self.service_preempt()
  24                 return 'preempted'
  25             rospy.sleep(1.0)
  26         return 'foo_succeeded'
  28 def child_term_cb(outcome_map):
  29     if outcome_map['FOO_CALC'] == 'foo_succeeded':
  30         return True
  31     elif outcome_map['FOO_RESET'] == 'invalid':
  32         return True
  33     else:
  34         return False
  36 def out_cb(outcome_map):
  37     if outcome_map['FOO_RESET'] == 'invalid':
  38         return 'foo_reset'
  39     elif outcome_map['FOO_CALC'] == 'foo_succeeded':
  40         return 'foo_done'
  41     else:
  42         return 'foo_reset'
  44 def monitor_cb(ud, msg):
  45     return False     
  47 def main():
  48     rospy.init_node("preemption_example")
  50     foo_concurrence = smach.Concurrence(outcomes=['foo_done', 'foo_reset'],
  51                                         default_outcome='foo_done',
  52                                         child_termination_cb=child_term_cb,
  53                                         outcome_cb=out_cb)
  55     with foo_concurrence:
  56         smach.Concurrence.add('FOO_CALC', foo())
  57         smach.Concurrence.add('FOO_RESET', smach_ros.MonitorState("/sm_reset", Empty, monitor_cb))
  59     sm = smach.StateMachine(outcomes=['DONE'])
  60     with sm:
  61         smach.StateMachine.add('SETUP', setup(), transitions={'setup_done':'FOO'})
  62         smach.StateMachine.add('FOO', foo_concurrence, transitions={'foo_done':'BAR', 'foo_reset':'SETUP'}) 
  63         smach.StateMachine.add('BAR', foo(), transitions={'foo_succeeded':'FOO', 'preempted':'SETUP'})
  65     sis = smach_ros.IntrospectionServer('smach_server', sm, '/SM_ROOT')
  66     sis.start()
  67     sm.execute()
  68     rospy.spin()
  69     sis.stop()
  71 if __name__=="__main__":
  72     main()

Code with Notes

Wiki: smach/Tutorials/State Machine Preemption with MonitorState (last edited 2012-06-14 15:35:43 by LauraLindzey)