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

Concurrence container

Description: This tutorial teaches you how to use the Concurrence container.

Tutorial Level: BEGINNER

Next Tutorial: Sequence container

   1 from smach import Concurrence

Specify concurrence outcomes

Concurrence Outcome Map

The outcome map of a SMACH concurrence specifies the policy for determining the outcome of the concurrence based on the outcomes of its children. Specifically, the map is a dictionary where the keys are potential outcomes of the concurrence, and the values are dictionaries mapping child labels onto child outcomes. Once all the states in the concurrence have terminated, if one of these child-outcome mappings is satisfied, the concurrence will return its associated outcome. If none of the mappings are satisfied, the concurrence will return its default outcome.

   1 cc = Concurrence(outcomes = ['outcome1', 'outcome2'],
   2                  default_outcome = 'outcome1',
   3                  input_keys = ['sm_input'],
   4                  output_keys = ['sm_output'],
   5                  outcome_map = {'succeeded':{'FOO':'succeeded',
   6                                              'BAR':'outcome2'},
   7                                 'outcome3':{'FOO':'outcome2'}})
   8 with cc:
   9     Concurrence.add('FOO', Foo())
  10     Concurrence.add('BAR', Bar())

The example above specifies the following policy:

  • When 'FOO' has outcome 'succeeded' and 'BAR' has outcome 'outcome2', the state machine will exit with outcome 'succeeded'.
  • When 'FOO' has outcome 'outcome2', the state machine will exit with outcome 'outcome3', independent of the outcome of state BAR.


If you want full control over a concurrence state machine, you can use the callbacks it provides, the child_termination_cb and the outcome_cb:

   1 # gets called when ANY child state terminates
   2 def child_term_cb(outcome_map):
   4   # terminate all running states if FOO finished with outcome 'outcome3'
   5   if outcome_map['FOO'] == 'outcome3':
   6     return True
   8   # terminate all running states if BAR finished
   9   if outcome_map['BAR']:
  10     return True
  12   # in all other case, just keep running, don't terminate anything
  13   return False
  16 # gets called when ALL child states are terminated
  17 def out_cb(outcome_map):
  18    if outcome_map['FOO'] == 'succeeded':
  19       return 'outcome1'
  20    else:
  21       return 'outcome2'
  24 # creating the concurrence state machine
  25 sm = Concurrence(outcomes=['outcome1', 'outcome2'],
  26                  default_outcome='outcome1',
  27                  input_keys=['sm_input'],
  28                  output_keys=['sm_output'],
  29                  child_termination_cb = child_term_cb,
  30                  outcome_cb = out_cb)
  32 with sm:
  33    Concurrence.add('FOO', Foo(),
  34                    remapping={'foo_in':'input'})
  36    Concurrence.add('BAR', Bar(),
  37                    remapping={'bar_out':'bar_out'})

  • The child_termination_cb is called every time one of the child states terminates. In the callback function you can decide if the state machine should keep running (return False), or if it should preempt all remaining running states (return True).
  • The outcome_cb is called once when the last child state terminates. This callback returns the outcome of the concurrence state machine.

Wiki: smach/Tutorials/Concurrence container (last edited 2012-01-03 19:16:38 by SiegfriedGevatter)