Skip to content

Commit

Permalink
use state id consistently to avoid long path strings; modify preempt …
Browse files Browse the repository at this point in the history
…and published outcome to improve sync; add flexbe_outcome_listener node for simple monitoring; this breaks API with flexbe_app and requires version 4.1.0+ of the FlexBE WebUI API
  • Loading branch information
David Conner committed Aug 25, 2024
1 parent a25a118 commit 9580cab
Show file tree
Hide file tree
Showing 36 changed files with 726 additions and 383 deletions.
55 changes: 32 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
FlexBE is a high-level behavior engine coordinating the capabilities of a robot in order to solve complex tasks.
Behaviors are modeled as hierarchical state machines (HFSM) where states correspond to active actions
and transitions describe the reaction to outcomes.
Main advantage over similar approaches is the good operator integration and an
intuitive user interface.

The main advantage of FlexBE over similar approaches is the good operator integration and an intuitive user interface.
Besides executing behaviors in full autonomy, the operator can restrict execution of certain transitions or trigger them manually.
Furthermore, FlexBE supports modifying the whole structure of a behavior during its execution without restarting it.
The user interface features a runtime control interface as well as a graphical editor for state machines.
Expand All @@ -23,64 +23,73 @@ Rolling ![ROS Build Farm](https://build.ros2.org/job/Rdev__flexbe_behavior_engin

## Installation

For released versions, FlexBE is available as ` apt install` package `ros-<DISTRO>-flexbe-*`
For released versions, FlexBE is available as `apt install` package `ros-<DISTRO>-flexbe-*`

To build from source, execute the following commands to install FlexBE for ROS 2 systems:

cd "ros2_ws"/src
git clone https://github.com/FlexBE/flexbe_behavior_engine.git
`cd "ros2_ws"/src`

Next, navigate to the "ros2_ws" top-level directory and build FlexBE:
`git clone https://github.com/FlexBE/flexbe_behavior_engine.git`

colcon build
Next, navigate to the "ros2_ws" top-level directory and build FlexBE:

`colcon build`

## Creating new FlexBE Behavior packages

To begin, create your own repository for behavior development in the `${WORKSPACE_ROOT}/src` folder:

`ros2 run flexbe_widget create_repo [your_project_name] <meta_package_name> <--non-interactive>`
`ros2 run flexbe_widget create_repo [your_project_name] <meta_package_name> <--non-interactive>`

This will clone a project template (requires internet access) that contains examples and proper package definitions,
and create the ROS 2 package structure and three subfolders.

For example, running

`ros2 run flexbe_widget create_repo my_project my_flexbe_project`

from the `${WORKSPACE_ROOT}/src` folder will create:
* `${WORKSPACE_ROOT}/src/my_flexbe_project`
* `${WORKSPACE_ROOT}/src/my_flexbe_project/my_flexbe_project` - the ROS meta package
* `${WORKSPACE_ROOT}/src/my_flexbe_project/my_project_flexbe_behaviors`
* `${WORKSPACE_ROOT}/src/my_flexbe_project/my_project_flexbe_states`

- `${WORKSPACE_ROOT}/src/my_flexbe_project`
- `${WORKSPACE_ROOT}/src/my_flexbe_project/my_flexbe_project` - the ROS meta package
- `${WORKSPACE_ROOT}/src/my_flexbe_project/my_project_flexbe_behaviors`
- `${WORKSPACE_ROOT}/src/my_flexbe_project/my_project_flexbe_states`

These are intended to contain your custom FlexBE state implementations and HFSM-based behaviors.

This version of the flexbe_behavior_engine requires version 4.0+ of the FlexBE user interface.
This release of the FlexBE Behavior Engine requires version 4.1+ of the FlexBE UI.
This breaks compatability with the older FlexBE App and now requires use of the FlexBE WebUI tool.

It is recommended to install the FlexBE WebUI user interface:

It is recommended to install the FlexBE user interface by following one of these steps:
* https://github.com/FlexBE/flexbe_webui.git - new Python-based webserver version (preferred)
* https://github.com/FlexBE/flexbe_app.git - classic FlexBE App (iron or ros2-devel branches)
[FlexBE WebUI](https://github.com/FlexBE/flexbe_webui.git) - Python-based webserver version


## Usage

Use the following launch file for running the onboard engine:

ros2 launch flexbe_onboard behavior_onboard.launch.py
`ros2 launch flexbe_onboard behavior_onboard.launch.py`

Use the following launch file for running the operator control station (requires the FlexBE App or WebUI):

ros2 launch flexbe_webui flexbe_ocs.launch.py
`ros2 launch flexbe_webui flexbe_ocs.launch.py`

> Note: replace `flexbe_webui` with `flexbe_app` to run the "classic" UI (after `ros2 run flexbe_app nwjs_install`).
During testing is is recommended to start the base nodes and the UI client separately:

Use the following launch file to run both of the above, e.g., for testing on a single computer:
`ros2 launch flexbe_webui flexbe_ocs.launch.py headless:=True`

ros2 launch flexbe_webui flexbe_full.launch.py
`ros2 run flexbe_webui webui_client`

See the `flexbe_webui` README for more details.


Use the following launch file to run the entire FlexBE system, both onboard and OCS, e.g., for testing on a single computer:

`ros2 launch flexbe_webui flexbe_full.launch.py`

For running tests use:
`colcon test --ctest-args --packages-select <flexbe_package>`

`colcon test --ctest-args --packages-select <flexbe_package>`

## Next Steps

Expand Down
2 changes: 1 addition & 1 deletion flexbe_core/flexbe_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from .logger import Logger # noqa: F401
from .state_logger import StateLogger # noqa: F401

MIN_UI_VERSION = '4.0.0' # Minimum FlexBE App or UI version required to interact with this version of flexbe_core
MIN_UI_VERSION = '4.1.0' # Minimum FlexBE UI version required to interact with this version of flexbe_core

# pylint: disable=R0903

Expand Down
35 changes: 23 additions & 12 deletions flexbe_core/flexbe_core/behavior.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


"""This defines the superclass for all implemented behaviors."""
from flexbe_core.core import LockableStateMachine, OperatableStateMachine, PreemptableState
from flexbe_core.core import LockableStateMachine, OperatableStateMachine, PreemptableState, StateMap
from flexbe_core.logger import Logger

from flexbe_msgs.msg import BehaviorSync
Expand All @@ -44,14 +44,15 @@ def __init__(self):
self._state_machine = None
self.name = 'unnamed behavior'
self.beh_id = 0 # Behavior id checksum assigned by processing the file contents
self._state_map = None

self.contains = {}
self._behaviors = {}

self._autonomy_level = 3
self._debug = False

self.requested_state_path = None
self.requested_state_id = None

# Please implement this abstract method:
def create(self):
Expand Down Expand Up @@ -178,15 +179,25 @@ def set_parameter(self, name, value):

def confirm(self):
"""Confirm that this behavior is ready for execution."""
LockableStateMachine.path_for_switch = self.requested_state_path

self._state_machine.confirm(self.name, self.beh_id)

# def define_structure(self):
# """
# Calculate all state ids and prepare the ContainerStructure message
# """
# self._state_machine.define_structure()
self._state_map = StateMap()
self._state_machine.confirm(self.name, self.beh_id, self._state_map)
LockableStateMachine.path_for_switch = None
if self.requested_state_id is not None:
requested_state = self._state_map[self.requested_state_id]
LockableStateMachine.path_for_switch = requested_state.path

@property
def state_map_items(self):
"""Return two lists of keys and values from state map."""
if self._state_machine is not None:
return list(zip(*self._state_map.items))
return [], []

def get_state_by_id(self, st_id):
"""Return state reference from state map by id."""
if self._state_map is not None:
return self._state_map.get(st_id)
return None

def execute(self):
"""
Expand Down Expand Up @@ -225,7 +236,7 @@ def prepare_for_switch(self, state):
sm.replace_userdata(state_container.userdata)
state_container = state_container._parent
states[1].replace_state(state) # add to new state machine
self.requested_state_path = state.path # set start after switch
self.requested_state_id = state.state_id # set start after switch

def get_current_states(self):
"""Get all currently active (sub-)states."""
Expand Down
32 changes: 20 additions & 12 deletions flexbe_core/flexbe_core/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@

from .concurrency_container import ConcurrencyContainer # noqa: F401
from .event_state import EventState # noqa: F401
from .exceptions import StateError # noqa: F401
from .exceptions import StateMachineError # noqa: F401
from .exceptions import UserDataError # noqa: F401
from .lockable_state import LockableState # noqa: F401
from .lockable_state_machine import LockableStateMachine # noqa: F401
from .manually_transitionable_state import ManuallyTransitionableState # noqa: F401
Expand All @@ -44,23 +47,28 @@
from .state import State # noqa: F401
from .state_machine import StateMachine # noqa: F401
from .state_map import StateMap # noqa: F401
from .topics import Topics
from .user_data import UserData # noqa: F401

__all__ = [
'PreemptableStateMachine',
'OperatableStateMachine',
'LockableStateMachine',
'RosStateMachine',
'StateMachine',
'ConcurrencyContainer',
'PriorityContainer',
'State',
'RosState',
'ManuallyTransitionableState',
'EventState',
'LockableState',
'PreemptableState',
'LockableStateMachine',
'ManuallyTransitionableState',
'OperatableState',
'EventState',
'OperatableStateMachine',
'PreemptableState',
'PreemptableStateMachine',
'PriorityContainer',
'RosState',
'RosStateMachine',
'State',
'StateError',
'StateMachine',
'StateMachineError',
'StateMap',
'UserData'
'Topics',
'UserData',
'UserDataError'
]
Loading

0 comments on commit 9580cab

Please sign in to comment.