(!) 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.

Wrapping a Container With actionlib

Description: This tutorial explains how to serve out a SMACH plan as an action over actionlib.

Tutorial Level: INTERMEDIATE

Wrapping a SMACH State

SMACH provides the top-level container called ActionServerWrapper. This class advertises an actionlib action 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.

The action server wrapper can inject the goal message received by the action server into the contained state, as well as extract the result message from that state when it terminates. When constructing the action server wrapper, the user specifies which state machine outcomes correspond to a succeeded, aborted, or preempted result.

Consider this example, which wraps a SMACH state machine as an action:

   1 import rospy
   3 from smach import StateMachine
   4 from smach_ros import ActionServerWrapper
   6 # Construct state machine
   7 sm = StateMachine(outcomes=['did_something',
   8                             'did_something_else',
   9                             'aborted',
  10                             'preempted'])
  11 with sm:
  12     ### Add states in here...
  14 # Construct action server wrapper
  15 asw = ActionServerWrapper(
  16     'my_action_server_name', MyAction,
  17     wrapped_container = sm,
  18     succeeded_outcomes = ['did_something','did_something_else'],
  19     aborted_outcomes = ['aborted'],
  20     preempted_outcomes = ['preempted'] )
  22 # Run the server in a background thread
  23 asw.run_server()
  25 # Wait for control-c
  26 rospy.spin()

Getting The Goal/Result Messages Into/Out Of The Contained State

The above code will call sm.execute(), but it will not load the goal into the contained state machine, nor will it extract a result. In order to do these things, you need to tell the action server wrapper what it should call the goal and result messages in the context of SMACH. You can replace the action server wrapper construction call with the following:

   1 # Construct action server wrapper
   2 asw = ActionServerWrapper(
   3     'my_action_server_name', MyAction, sm,
   4     ['did_something','did_something_else'], ['aborted'], ['preempted'],
   5     goal_key = 'my_awesome_goal',
   6     result_key = 'egad_its_a_result' )

The keyword arguments goal_key and result_key are the SMACH userdata keys in the context of the ActionServerWrapper. Like any other container, this means that the wrapper's contained state (in this case the state machine sm) will receive a reference to this userdata structure when its execute() method is called. Similarly to how userdata is passed between scopes in nested state machines, in this case, you need to set these key identifiers in the state machine sm as well.

In order to copy in the keys form the parent, you can replace the construction call for the state machine sm with this:

   1 # Construct state machine
   2 sm = StateMachine(
   3         outcomes=['did_something','did_something_else','aborted','preempted'],
   4         input_keys = ['my_awesome_goal'],
   5         output_keys = ['egad_its_a_result'])

Once you have done this, you can access these keys from any state added to sm. For more goal/result policies, see the ActionServerWrapper API documentation.

Wiki: smach/Tutorials/Wrapping a SMACH Container With actionlib (last edited 2017-01-12 14:19:25 by esteve)