Skip to content

Commit

Permalink
Introduce blackboard support (#71)
Browse files Browse the repository at this point in the history
* Implemented BT blackboard support for input and output ports
* Introduced Math.random() function support for assignments
* Implemented new grid_world test for comparison with SOTA

---------

Signed-off-by: Marco Lampacrescia <[email protected]>
Signed-off-by: Christian Henkel <[email protected]>
  • Loading branch information
MarcoLm993 authored Dec 16, 2024
1 parent b26a411 commit 1ecf24a
Show file tree
Hide file tree
Showing 61 changed files with 2,127 additions and 238 deletions.
107 changes: 107 additions & 0 deletions docs/source/graphics/blackboard_to_scxml.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 69 additions & 42 deletions docs/source/scxml-jani-conversion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,99 @@ SCXML and JANI

In CONVINCE, we expect developers to use Behavior Trees and SCXML to model the different parts of a robotic systems.

SCXML (Scope XML) is a high level format that describes a single state machine, and allows it to exchange information with other state machines using events. Each SCXML file defines its variables (datamodel), states, and transitions.
SCXML (State Chart XML) is an XML format that describes a single state machine, and allows it to exchange information with other SCXML state machines using events. Each SCXML file defines its variables (datamodel), states, and transitions.

With SCXML, the system consists of a set of state machines, each one represented by an SCXML file, which are synchronized together using events. Operations are carried out when the execution of a state machine receives an event, enters a state, or exits a state.
Using SCXML, the system can be modeled as a set of state machines, each one represented by an SCXML file, which are synchronized together using events. Operations are carried out when the execution of a state machine receives an event, enters a state, or exits a state.

With JANI, the whole system model is contained in a single JSON file, consisting of a set of global variables, automata (equivalent to state machines) with their edges (equivalent to transitions), and a composition description, specifying how the automata should be synchronized by advancing specific edges at the same time synchronously.
Using AS2FM, we can convert the model described using SCXML to JANI, that is a JSON-based format for describing a system as a formal model. With JANI, the whole system model is contained in a single JSON file, consisting of a set of global variables, automata (equivalent to state machines) with their edges (equivalent to transitions), and a composition description, specifying how the automata should be synchronized by advancing specific edges at the same time synchronously.

The main difference between SCXML and JANI is that in JANI there is no concept of events, so synchronization must be achieved using the global variables and composition description.

High-Level (ROS) SCXML Implementation
---------------------------------------

In CONVINCE, we extended the standard SCXML format defined `here <https://www.w3.org/TR/scxml/>`_ with ROS specific features, to make it easier for ROS developers to model ROS-based systems.
In CONVINCE, we extended the standard SCXML format defined `here <https://www.w3.org/TR/scxml/>`_ with ROS and Behavior Tree (BT) specific features, to make it easier for robot developers to model their systems using both ROS and BT.

In this guide we will refer to the extended SCXML format as high-level SCXML and to the standard SCXML format as low-level SCXML.

Currently, the supported ROS-features are:

* ROS Topics
* ROS Timers (Rate-callbacks)

TODO: Example of Topic and Timer declaration + usage.
* ROS Service
* ROS Actions
* BT Ticks
* BT Responses
* BT Ports
* BT Blackboard

Low-Level SCXML Conversion
----------------------------

Low-Level SCXML is the standard SCXML format defined `here <https://www.w3.org/TR/scxml/>`_.

Our converter is able to transform high-level SCXML to low-level SCXML by translating the ROS specific features to standard SCXML features.
In case of timers, we need additional information that cannot be encoded in SCXML, such that information is generated at runtime.
Our converter is able to transform High-Level (HL) SCXML to Low-Level (LL) SCXML by translating ROS and BT specific features to standard SCXML features.
This applies also for timers: we generate an additional SCXML FSM encoding a global clock, sending out events to trigger the timers defined in the model at the correct rate.

The next subsections describe our conversion strategy from HL-SCXML to LL-SCXML.
The entry-point for the conversion is implemented in ScxmlRoot.as_plain_scxml(). TODO: Link to API.

Handling of (ROS) Timers
__________________________

TODO

Handling of (ROS) Services
_____________________________

ROS services, as well as ROS topics, can be handled directly in the conversion from HL-SCXML to LL-SCXML.

The main structure of the SCXML related state machines can be inspected in the diagram below:

.. image:: graphics/ros_service_to_scxml.drawio.svg
:alt: Handling of ROS Services
:align: center

The automata of clients and services are converted directly from the existing ROS-SCXML files, while the "Extra Service Handler" is autogenerated starting from the provided clients' and services' declarations.


Handling of (ROS) Actions
_____________________________

The conversion between the two SCXML formats is implemented in ScxmlRoot.as_plain_scxml(). TODO: Link to API.
ROS actions are handled similarly to ROS Services: a HL-SCXML description of the system is converted to LL-SCXML, and an additional automaton is generated to handle the synchronization between the clients and the server.

TODO: Describe how we translate the high-level SCXML to the low-level SCXML.
The structure of a client-server communication through actions and additional threads looks as follows:

.. image:: graphics/ros_action_to_scxml.drawio.svg
:alt: Handling of ROS Actions
:align: center


Handling the BT Blackboard
_____________________________

The Blackboard is a container that shares variables across different BT plugins. The value of those variables normally changes over time, and is expected to be updated at each tick.

In LL-SCXML, this is handled by an autogenerated SCXML FSM, that receives the updates from the various plugins and, upon request, provides the data.
This diagram summarizes the FSM structure.

.. image:: graphics/blackboard_to_scxml.drawio.svg
:alt: Blackboard FSM
:align: center

TODO: Timers are useful for SCAN as well: instead of keeping them in a runtime object, we can consider to list them in an intermediary XML file.

Setting Blackboard Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This can be done using the tag `bt_set_output` in HL-SCXML. In LL-SCXML this translates to a send event, that is received by the Blackboard FSM to update the internal data.

Reading Blackboard Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

At the current state, to read the internal variables there needs to be some message exchange between the plugin FSM and the Blackboard FSM.
This needs to happen for each and every Blackboard variable that is read.

Optimization is nevertheless possible (sending the whole set of blackboard variables each time), but this would diverge from the Blackboard.CPP implementation, so it should rather be an automatic conversion.

JANI Conversion
----------------
Expand Down Expand Up @@ -114,34 +172,3 @@ The JANI model resulting from applying the conversion strategies we just describ
:align: center

It can be seen how new self loop edges are added in the `A_B_receiver` automaton (the dashed ones) and how the `ev_a_on_send` is now duplicated in the composition table, one advancing the `A sender` automaton and the other one advancing the `A_B sender` automaton.


Handling of (ROS) Timers
__________________________

TODO

Handling of (ROS) Services
_____________________________

ROS services, as well as ROS topics, can be handled directly in the ROS to plain SCXML conversion, without the need of adding JANI-specific features, as for the ROS timers.

The main structure of the SCXML related state machines can be inspected in the diagram below:

.. image:: graphics/ros_service_to_scxml.drawio.svg
:alt: Handling of ROS Services
:align: center

The automata of clients and services are converted directly from the existing ROS-SCXML files, while the "Extra Service Handler" is autogenerated starting from the provided clients and services.


Handling of (ROS) Actions
_____________________________

ROS actions are handled similarly to ROS Services: a ROS-SCXML description of the system is converted to plain SCXML, and an additional automaton is generated to handle the synchronization between the clients and the server.

The structure of a client-server communication through actions and additional threads looks as follows:

.. image:: graphics/ros_action_to_scxml.drawio.svg
:alt: Handling of ROS Actions
:align: center
42 changes: 42 additions & 0 deletions ros_support_interfaces/grid_robot_interfaces/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.5)
project(grid_robot_interfaces)

# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
find_package(std_msgs REQUIRED)

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Int2D.msg"
DEPENDENCIES std_msgs
)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
1 change: 1 addition & 0 deletions ros_support_interfaces/grid_robot_interfaces/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Used in `test/jani_generator/_test_data/grid_robot_blackboard`
2 changes: 2 additions & 0 deletions ros_support_interfaces/grid_robot_interfaces/msg/Int2D.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int32 x
int32 y
22 changes: 22 additions & 0 deletions ros_support_interfaces/grid_robot_interfaces/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>grid_robot_interfaces</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="[email protected]">root</maintainer>
<license>TODO: License declaration</license>

<buildtool_depend>ament_cmake</buildtool_depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
5 changes: 4 additions & 1 deletion src/as2fm/as2fm_common/ecmascript_interpretation.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ def interpret_ecma_script_expr(
msg_addition = ""
if expr in ("True", "False"):
msg_addition = "Did you mean to use 'true' or 'false' instead?"
raise RuntimeError(f"Failed to interpret JS expression: 'result = {expr}'. {msg_addition}")
raise RuntimeError(
f"Failed to interpret JS expression using variables {variables}: ",
f"'result = {expr}'. {msg_addition}",
)
expr_result = context.result
if isinstance(expr_result, BasicJsTypes):
return expr_result
Expand Down
Loading

0 comments on commit 1ecf24a

Please sign in to comment.