mc_rtc::Configuration general purpose configurationThe framework provides by default a few pre-implemented states intended to make it easy to take full advantage of the JSON/YAML configuration capabilities of the FSM. Using these, writing FSMs for complex robot behaviours becomes easy, and usually involves little to no handwritten C++/Python states: almost everything can be directly described in the FSM configuration. In this tutorial, we’ll explore use of the three most useful states:
JSON/YAML configurationIn the remainder of this tutorial, we’ll see how these may be used. Only YAML examples will be shown, JSON is of course also supported and similar examples may be found in the documentation. A full list of available states can be found in the “State objects” section of the JSON/YAML documentation.
The MetaTasks state (documentation) allows to load a list of tasks from their JSON/YAML configuration. The full list of available tasks is available JSON/YAML documentation.
Let’s start with an example, we will add a new MetaTasks state in the states: section of the FSM configuration.
states:
ExampleState:
base: MetaTasks
# optional: uses the output criteria of the tasks as the string output for the next transition
outputs: [CoM]
tasks:
# create a CoM task
CoM:
type: com
move_com: [0, 0, -0.05]
completion:
OR:
- timeout: 3
- AND:
- eval: 0.01
- speed: 0.005
# create a bspline_trajectory task
HandTrajectory:
type: bspline_trajectory
surface: LeftHand
stiffness: 10000.0
duration: 15.0
weight: 100
targetSurface:
# assumes that the additional object/box robot was loaded in the global fsm configuration
robot: box
surface: Left
completion:
- timeElapsed: true
This will create a new state named ExampleState that will itself loads:
CoM task named CoM that moves the com 5cm down.BSplineTrajectory task named LeftHandTrajectory that moves the left gripper of the robot to a surface Left defined on another robot.Notice the completion elements in the example above. These are what we refer to as “completion criteria”. They are a way to generate conditional statements to determine when a task has been completed. See the API documentation for mc_control::CompletionCriteria for details.
By default, every task defines the following entries:
eval: True when the norm of MetaTask::eval() is below the provided valuespeed: True when the norm of MetaTask::speed() is below the provided valuetimeout: True when the task has been added to the solver longer than the provided time (in seconds)Some tasks define additional completion criterias, such as:
SplineTrajectoryTask variants (bspline_trajectory, exact_cubic_trajectory) additionally define
timeElapsed: true: True when the task duration has elapsedwrench: True when the force applied on the robot surface is higher than the provided threshold (6d vector, NaN value ignores the reading, negative values invert the condition). Ignored if the surface has no force-sensor attached.You may add your own completion criterias by implementing mc_task::MetaTask::buildCompletionCriteria for your task. These completion criterias can be combined using the conditional constructs AND and OR as follows. The usual rules of lazy evaluation apply. For example, to check whether the hand trajectory tasks has completed if its total duration has been reached or more than 15N apply along the z direction of the hand’s surface normal after 3 seconds of delay, you could write:
# completion criteria that checks whether a trajectory task has been active for at least its specified duration
# or whether more than 15N apply along the `z` direction of the hand surface after 3 seconds of delay.
completion:
OR:
- timeElapsed: true
- AND:
- timeout: 3
- wrench: [.NaN, .NaN, .NaN, .NaN, .NaN, 15]
You may also ommit the completion criteria for tasks that you want to execute but whose completion does not matter. In that case the FSM will consider the task as always completed.
As mentioned in the previous tutorials, each state returns an output string by calling State::output("output value"). This output value is used in the transition map to determine which state should be exectuted next. By default, the MetaTasks state returns "OK" once all of its tasks have completed. If you don’t need to take branching decisions based on the tasks outputs, then your transition map would look like
transtions:
...
# transition to the next state automatically when all tasks have completed
- [ExampleState, OK, NextState, Auto]
...
You may however want to make more complex branching decisions based on why the tasks have completed. For this, the MetaTasks state defines an output configuration which may contain a list of tasks names for which the completion criteria’s string output will be used to generate the state’s output. For example:
ExampleState:
base: MetaTasks
# optional: uses the output criteria of the tasks as the string output for the next transition
outputs: [CoM]
Your transition map can now branch conditionally based on the CoM completion criteria:
# Reminder: the CoM completion criteria is defined as:
# CoM:
# completion:
# OR:
# - timeout: 3
# - AND:
# - eval: 0.01
# - speed: 0.005
- [ExampleState, "CoM=timeout", CoMHasNotConvergedState, Auto]
- [ExampleState, "CoM=eval AND speed", NextMotionState, Auto]
# state to execute by default if none of the completion patterns are matched in the transition map
# This allows to define non-exhaustive matching patterns in the transition map
- [ExampleState, "DEFAULT", DefaultState, Auto]
You may now define three new states:
CoMHasNotConvergedState to be executed if the CoM task hasn’t converged in the allowed timeNextMotionState to continue moving since the CoM convergedDefaultState to be executed in case no transition matches the generated output pattern. In this example the matching is exhaustive, and this state would never be executed.The parallel state (documentation) allows to execute multiple states in parallel. Since the FSM execution is actually single-threaded, the states are actually executed sequentially in a single controller iteration.
# Define an additional MetaTasks state that moves the right hand
RightHandState:
base: MetaTasks
HandTrajectory:
type: bspline_trajectory
surface: RightHand
stiffness: 10000.0
duration: 15.0
weight: 100
targetSurface:
robot: box
surface: Left
completion:
- timeElapsed: true
# Now say we want to move both the left and right hand and move the CoM down?
# Easy, simply put the two states in parallel:
ExampleParallelState:
base: Parallel
# At each iteration, the ExampleState will be executed, followed by the RightHandState
states: [ExampleState, RightHandState]
# optional defines which state to use as the output criteria
# By default the last state's output in the states list above is used
outputs: [ExampleState, RightHandState]
Parallel states are considered completed when all of its states have completed. As for the MetaTasks state, it is possible to specify how the state output is generated:
states list will be used.outputs is specified, these states’ outputs will be used to generate the parallel state’s output.For example:
[ExampleParallelState, "ExampleState: (CoM=timeout), RightHandState: (timeElapsed)", "StateA"]
[ExampleParallelState, "ExampleState: (CoM=eval AND speed), RightHandState: (timeElapsed)", "StateB"]
# Here the interest of having a default state becomes apparent,
# as for complex condition criterias it might be cumbersome to create an exhaustive list of possible outputs.
[ExampleParallelState, "DEFAULT", "DefaultState"]
Sometimes it is useful to be able to interact between states. For instance consider a StabilizerStandingState in parallel to a Pickup state that wants to pick-up an object on the floor. This state needs to move the CoM lower to enable the robot to reach. This kind of interaction between states can be achieved using the DataStore.
In complex scenarios, it is often useful to be able to have multiple FSMs in the same controller. That’s what the Meta state (documentation) is for, it embeds an FSM within an FSM state.
ExampleMetaState:
base: Meta
transitions:
- [StateA, OK, StateB]
- [StateB, OK, StateC]
- ...
This state can then be exectued as part of another FSM, or even as part of another Meta state.
transtions:
- [ExampleParallelState, DEFAULT, ExampleMetaState]
- [ExampleMetaState, OK, LastStateOutput]
Note that the ouput of a Meta FSM is the output of its last state without a transition within the Meta FSM (here LastStateOutput).