## For instruction on writing tutorials ## http://www.ros.org/wiki/WritingTutorials #################################### ##FILL ME IN #################################### ## for a custom note with links: ## note = ## for the canned note of "This tutorial assumes that you have completed the previous tutorials:" just add the links ## note.0= ## descriptive title for the tutorial ## title = Getting Started with smach ## multi-line description to be displayed in search ## description = This tutorial guides you through the very first steps of using smach. ## the next tutorial description (optional) ## next = ## links to next tutorial (optional) ## next.0.link=[[smach/Tutorials/User Data|Passing User Data between States]] ## next.1.link= ## what level user is this tutorial for ## level= BeginnerCategory ## keywords = #################################### <> <> == Why learn Smach? == Smach, which stands for "State Machine", is a powerful and scalable Python-based library for hierarchical state machines. The Smach library does not depend on ROS, and can be used in any Python project. The [[executive_smach]] stack however provides very nice integration with ROS, including smooth [[actionlib]] integration and a powerful [[smach_viewer|Smach viewer]] to visualize and introspect state machines. It is very easy to write a simple Smach state machine, while at the same time Smach allows you to design, maintain and debug large, complex hierarchical state machines. The image below shows an example state machine used to coordinate [[actionlib]] actions that allow the PR2 robot to [[pr2_plugs|charge itself at a standard outlet]]. {{attachment:pr2_plugs_executive/smach.png||width="750"}} == Installing Smach == {{{ $ sudo apt-get install ros-noetic-smach-ros }}} == Creating a State Machine == To create a Smach state machine, you first create a number of states, and then add those states to a State Machine container. === Creating a state === To create a state, you simply inherit from the `State` base class, and implement the `State.execute(userdata)` method: {{{#!python class Foo(smach.State): def __init__(self, outcomes=['outcome1', 'outcome2']): # Your state initialization goes here def execute(self, userdata): # Your state execution goes here if xxxx: return 'outcome1' else: return 'outcome2' }}} * In the '''init''' method you initialize your state class. Make sure to never block in the init method! If you need to wait for other parts of your system to start up, do this from a separate thread. * In the '''execute''' method of a state the actual work is done. Here you can execute any code you want. It is okay to block in this method as long as you like. Once you return from this method, the current state is finished. * When a state finishes, it returns an '''outcome'''. Each state has a number of possible outcomes associated with it. An outcome is a user-defined string that describes how a state finishes. A set of possible outcomes could for example be ['succeeded', 'failed', 'awesome']. The transition to the next state will be specified based on the outcome of the previous state. === Adding states to a state machine === A state machine is a container that holds a number of states. When adding a state to a state machine container, you specify the transitions between the states. {{{#!python sm = smach.StateMachine(outcomes=['outcome4','outcome5']) with sm: smach.StateMachine.add('FOO', Foo(), transitions={'outcome1':'BAR', 'outcome2':'outcome4'}) smach.StateMachine.add('BAR', Bar(), transitions={'outcome2':'FOO'}) }}} The resulting state machine looks like this: {{attachment:simple.png||width="350"}} * The red boxes show the possible '''outcomes''' of the state machine container: outcome4 and outcome5, as specified in line 1. * In line 3-5 we '''add''' the first state to the container, and call it FOO. The convention is to name states with all caps. If the outcome of state FOO is 'outcome1', then we transition to state BAR. If the outcome of state FOO is 'outcome2', then the whole state machine will exit with 'outcome4'. * Every state machine container is also a state. So you can '''nest state machines''' by adding a state machine container to another state machine container. === Example === This is a complete runnable example you can find in the [[executive_smach_tutorials]] package. {{{#!wiki <> }}} Running the example: For ROS Kinetic (and newer) versions just clone the git repository, cd into the folder "executive_smach_tutorials" and run the example (inside your ros environment) {{{ $ git clone https://github.com/eacousineau/executive_smach_tutorials.git $ cd executive_smach_tutorials $ ./examples/state_machine_simple.py }}} For older ROS-versions you can install the smach_tutorials package using rosdep. {{{ $ roscd smach_tutorials $ ./examples/state_machine_simple.py }}} This should give you the following output: {{{ [INFO] 1279835117.234563: Executing state FOO [INFO] 1279835117.234849: State machine transitioning 'FOO':'outcome1'-->'BAR' [INFO] 1279835117.235114: Executing state BAR [INFO] 1279835117.235360: State machine transitioning 'BAR':'outcome2'-->'FOO' [INFO] 1279835117.235633: Executing state FOO [INFO] 1279835117.235884: State machine transitioning 'FOO':'outcome1'-->'BAR' [INFO] 1279835117.236143: Executing state BAR [INFO] 1279835117.236387: State machine transitioning 'BAR':'outcome2'-->'FOO' [INFO] 1279835117.236644: Executing state FOO [INFO] 1279835117.236891: State machine transitioning 'FOO':'outcome1'-->'BAR' [INFO] 1279835117.237149: Executing state BAR [INFO] 1279835117.237394: State machine transitioning 'BAR':'outcome2'-->'FOO' [INFO] 1279835117.237652: Executing state FOO [INFO] 1279835117.237898: State machine terminating 'FOO':'outcome2':'outcome4' }}} == Pre-defined States and Containers == === State library === The example above shows how you can implement your own states. However, Smach comes with a whole library of pre-implemented states that cover many common usecases: * '''!SimpleActionState''': automatically call [[actionlib]] actions. * '''!ServiceState''': automatically call ROS services * '''!MonitorState''' * ... The 'Smach States' section on the [[smach/Tutorials|tutorials page]] gives an overview of all available states. === Container library === Similarly, Smach also comes with a set of useful containers: * '''!StateMachine''': the generic state machine container * '''Concurrence''': a state machine that can run multiple states in parallel. * '''Sequence''': a state machine that makes it easy to execute a set of states in sequence. The 'Smach Containers' section on the [[smach/Tutorials|tutorials page]] gives an overview of all available containers. {{attachment:containers.png|width="350"}} The [[smach/Tutorials/User Data|next tutorial]] will teach you how to pass user data between different states and state machines. ## AUTOGENERATED DO NOT DELETE ## TutorialCategory ## FILL IN THE STACK TUTORIAL CATEGORY HERE ## LearningSMACHCategory