ROS Resources: Documentation | Support | Discussion Forum | Service Status | Q&A answers.ros.org

Introducing the SMACC State Machine Library

I’m curious how SMACC differs from [boost].SML (https://boost-ext.github.io/sml/). I’ve been using SML in many projects recently and I really like it’s declarative syntax. I see that SMACC is built on Statecharts which for all the nice features it has I found the syntax much more cumbersome than SML. What are the features that SMACC offers that would make it worth using over SML?

I notice on your website you compare Statecharts and MSM (http://smacc.ninja/statechart-vs-msm/) and to do that you use a slide from a talk by the the author of SML and yet don’t seem to address it as an alternative to Statecharts or MSM. Looking through your website I found it hard to understand how I would use SMACC and what benefit it would offer over SML.

I re-watched the talk that the slide on your website is from(https://www.youtube.com/watch?v=yZVby-PuXM0) and it reinforced all the reasons I wouldn’t want to use Statecharts.

On your website you seem to dismiss performance concerns by saying that if you are building a “real robot” you don’t care about determinism and high loop rates and if you actually do care (your examples are an icbm and low cost uC), you will just write it in c99. I think this is a bit of a strawman argument by using such extreme examples. If I’m using C++ I do care about performance and the state machine library you chose has known issues with runtime performance. Called out in that cppcon talk are dynamic allocations, dynamic dispatch, and a high memory footprint.

You also seem to care allot that you can put variables in your states as a supported feature of the state machine library you chose. I don’t understand why you have this requirement and why the type based system in SML is worse.

If I’m using C++ I care about performance (determinism, utilization, etc) even if I’m not running on a micro-controller. Why would I want to use an old state machine library (Statecharts) that has known performance problems compared to SML?

We generally are working on complex robot systems using manipulators with moveit and have found SML to be really nice for our applications.

1 Like

Thanks @brettpac for contributing to the ros community with the SMACC library and excellent documentation !!!

Hi @tylerweaver

Thank you for bringing up a very interesting topic. You’ve got a lot a questions here, so I’m responding inline to make sure that I get to all of them.

Anyways, let’s get down to it…

I’m curious how SMACC differs from [boost].SML (https://boost-ext.github.io/sml/ 4). I’ve been using SML in many projects recently and I really like it’s declarative syntax. I see that SMACC is built on Statecharts which for all the nice features it has I found the syntax much more cumbersome than SML. What are the features that SMACC offers that would make it worth using over SML?

Well, SMACC offers a number of features beyond just SML or Boost Statechart.

  1. SMACC provides tight integration with ROS and users are able to generate events using ROS topics, services and actions; right out of the box.
  2. SMACC offers a library of reference state machines, so that you don’t have to start your application from scratch; right out of the box
  3. SMACC offers a library of clients (such as MoveBaseZ and MoveItZ) that allow ROS users to control ROS packages like MoveBase, MoveIt!, ros_control, generic ROS publishers, and many more; right out of the box.
  4. SMACC comes with a SMACC Viewer that allows you to view the complete layout of your state machine and monitor performance during runtime, which is particularly helpful for debugging.

I notice on your website you compare Statecharts and MSM (http://smacc.ninja/statechart-vs-msm/) and to do that you use a slide from a talk by the the author of SML and yet don’t seem to address it as an alternative to Statecharts or MSM. Looking through your website I found it hard to understand how I would use SMACC and what benefit it would offer over SML.

For the development of SMACC, we researched the two Boost libraries, Boost Statechart and MSM which were both written in the same era. SML came later. But SML is basically an updated, and stripped down version of MSM. At the time I wrote that page, less information was available for SML.

But let’s talk a little about the differences between Boost Statechart and SML…

Asynchronicity

And while it’s true that SML allows you to define your own thread policies, as we’ll see later that can get real complicated for the user (if not impossible) once he has to take asynchronicity, hierarchy, orthogonals within hierarchy and multiple complex orthogonals into account, along with normal things like logging.

References
See https://boost-ext.github.io/sml/benchmarks.html under “Non-UML features”
See https://boost-ext.github.io/sml/overview.html under “Thread Safety”
See https://boost-ext.github.io/sml/user_guide.html#policies under “Thread Safe Policy”

Complexity

Boost Statechart allows you to build state machines that are an order of magnitude more complex than those possible with SML.

  • Boost Statechart allows you to have per-state transition tables, whereas Boost SML has a only a single global transiton table.

    state_machines_battlefield_naioost__kris_jusiak__cppcon_2018-1722

  • Boost Statechart allows you to spread your state machine over multiple translation units (Each state is a file, each orthogonal is a file, etc).

  • SML doesn’t really support Hierarchal State Machines, see below…

References
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/rationale.html - Item #6
See http://smacc.ninja/statechart-vs-msm/ First Chart, Column 3

Hierarchy

• Boost Statechart allows you to build hierarchal state machines, where states are classes and have parent states, with theoretically unlimited levels hierarchy.
• Boost Statechart also supports deep history (as opposed to shallow history) which enables advanced recovery procedures across different nested states.
• In SML when you want to introduce a hierarchal relationship into your state machine, you have to nest a new state machine inside of your current state machine.

And this approach taken by SML creates numerous problems, with data, transitions, event handling, and more. And because of this, I’m of the opinion that SML does not truly support hierarchal state machines.

Further, a quick view of the Open Issues on the SML Github page shows a substantial amount of open tickets related to hierarchal state machines that illustrate the numerous problems resulting from SML’s approach, some being open now for over 4 years…

See… https://github.com/boost-ext/sml/issues/

https://github.com/boost-ext/sml/issues/326 - (3/20/2020) - Status: OPEN
c_str() overload for sub state machines

https://github.com/boost-ext/sml/issues/289 - (8/2/2019) - Status: OPEN
Advanced hierarchal state machine support

https://github.com/boost-ext/sml/issues/266 - (4/29/2019) - Status: OPEN
How to access data members in a submachine when entering & exiting from it’s substates

https://github.com/boost-ext/sml/issues/185 - (7/26/2018) - Status: OPEN
How to model hierarchal state machines with Boost.SML

https://github.com/boost-ext/sml/issues/122 - (6/28/2017) - Status: OPEN
Substate entry action is not actioned on entry

https://github.com/boost-ext/sml/issues/111 - (6/21/2017) - Status: OPEN
BOOST SML handling Hierarchal State Machine

https://github.com/boost-ext/sml/issues/84 - (12/27/2016) - Status: OPEN
Handling events in nested and outer state machines

https://github.com/boost-ext/sml/issues/83 - (12/14/2016) – Status: OPEN
Composing finite state machines recursively

https://github.com/boost-ext/sml/issues/44 - (6/2/2016) - OPEN
Marking initial state in transition that originates from its nested state

**Issue 185 is particularly worth checking out. Scroll to the bottom of the (still open) issue and you’ll see the following entry by the user erikzenker…

In contrast, hierarchy is easy in Boost Statechart and SMACC. Here are some examples from the SMACC Library…

smacc_state_machine_20200513-165801 - shrunk copy

And here
smacc_state_machine_20200222-122223 - Shrunk .

References
See https://boost-ext.github.io/sml/tutorial.html “A state machine may be a state inself”
See https://boost-ext.github.io/sml/examples.html#composite “in sub sm”
See https://boost-ext.github.io/sml/examples.html#nested

State Local Storage

  • In Boost Statechart, states are classes, and like normal classes state can have member functions and member variables. This is referred to has state local storage, and this makes Boost Statechart better suited for behavioral state machine applications, where you actually want your state do something, as opposed just having the state machine do things on the transitions between states.
  • In SML, states are basically just labels, and all resource handles and variables are stored in the state machine.

More on this below…

References
See http://smacc.ninja/behavioral-vs-protocol-state-machines/

I re-watched the talk that the slide on your website is from(https://www.youtube.com/watch?v=yZVby-PuXM0) and it reinforced all the reasons I wouldn’t want to use Statecharts.

The video you are referring to was from a talk given by Kris Jusiak, who is the author of Boost.SML, and unsurprisingly he thinks Boost SML is the best ( and I have no problem with authors thinking their library is the best) but…

The benchmark tests that he uses as the basis of his arguments, in my opinion don’t really represent likely real world scenarios. For instance, in all of his performance tests, the SM processes 1 million events, something I think would be very unlikely for an asynchronous robot running ROS.

But more on this below…

On your website you seem to dismiss performance concerns by saying that if you are building a “real robot” you don’t care about determinism and high loop rates and if you actually do care (your examples are an icbm and low cost uC), you will just write it in c99. I think this is a bit of a strawman argument by using such extreme examples. If I’m using C++ I do care about performance and the state machine library you chose has known issues with runtime performance. Called out in that cppcon talk are dynamic allocations, dynamic dispatch, and a high memory footprint.

I’ve never said that I don’t care about determinism, because I do care about it, and I’m going to instrument the library with LTTNG to ensure that SMACC is completely deterministic.

And maybe I’m being a little triggered by the word “strawman” but it is not a strawman argument to say that you don’t recommend someone using your library for certain applications, but I digress…

Anyways, let’s get into the issues Kris Jusiak raises of dynamic allocations, dynamic dispatch and high memory footprint…

Memory Footprint

While the Memory Footprint of Boost Statechart is higher than SML, it’s not high enough to be concerned with in a ROS based system. In the features/benchmark page of the Boost SML site, even the “Complex State Machine” benchmark test (the largest state machine tested, where the state machine consisted of 50 States, 50 Events and 50 transitions) the memory footprint only came to 200 bytes.

Dynamic Dispatch

The ability to build state machines with Boost Statechart that are an order of magnitude more complex than with SML (or MSM) comes with a cost…

In Boost SML, event dispatch is handled via function pointers. This is extremely fast, but requires that the entire state machine be confined to one file/translation (compilation) unit*.

In Boost Statechart, event dispatch is handled via a virtual call is followed by a linear search for the appropriate reaction, using one RTTI comparison per reaction. Although this technique is not as fast as the one used by SML (and MSM), it is this method allows the state machine to be distributed over multiple translation units.

For more information on this,
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/performance.html#DoubleDispatch
See https://lists.boost.org/Archives/boost/2010/01/160835.php

*Note: While it may be theoretically is possible for a developer to distribute a state machine written in SML over several translation units, the developer would then have to somehow manually connect all the pieces of the state machine together, which does not scale and is prone to error.

Dynamic Allocation

This is most important factor for state machine performance, and is related to Boost Statechart being a behavioral state machine library and providing State Local Storage.

In Boost Statechart, states are classes that are instantiated upon state entry, and destroyed upon state exit. In SML by contrast, because the states are basically labels, there is no time spent on memory allocation.

Performance

And it is true, that these factors (dynamic dispatch and dynamic allocation), make Boost Statechart an order of magnitude slower than SML at processing events, when benchmarked inside of the very simple state machines that SML can actually support. But it doesn’t really matter, because Boost Statechart is still really fast.

For instance, on the features/benchmarks page, in the test involving the “Composite State Machine” compiled with GCC-5.2, despite being slower than Boost SML, Boost Statechart still processed one million events in 404ms. This comes to a speed of approximately 2.5 MHz.

Even for the test involving the “Complex State Machine” (where as mentioned before the state machine consisted of 50 States, 50 Events and 50 transitions), Boost Statechart was able to process one million events in 5.52 seconds. This comes to a speed of approximately 181 KHz. – Still easily fast enough for ROS applications.

And of course the tests on this page do not specify what hardware these machines were running on, so one should not look at this and think that Boost Statechart is somehow limited to these speeds.

So to summarize. It’s true that Boost Statechart is slower than Boost SML, but it’s fast enough.

And if you’re operating in an asynchronous environment, you probably aren’t processing a million events per second. And then you are probably concerned with other performance metrics not covered in the tests referenced above.

One last important point is that in future development, we (SMACC) plan to really maximize our performance by utilizing our ability to use custom allocators, where we would give applications that require super high performance the ability to do things like pre-allocate states at the beginning of the program, cache states, etc. As mentioned before, this (state destruction and construction) is the main cause of the performance differences between Boost Statechart and SML…

References
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/performance.html “State entry and exit: … roughly 3 quarters of total event processing time is spent destructing the exited state and constructing the entered state”
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/performance.html#DoubleDispatch
See https://boost-ext.github.io/sml/benchmarks.html

You also seem to care allot that you can put variables in your states as a supported feature of the state machine library you chose. I don’t understand why you have this requirement and why the type based system in SML is worse.

It’s worse because you don’t have the ability to scope resource handles or variables, to the lifetimes of the states that use them. Leaving you with only the option of using essentially global variables, all stored in one place (like the “data” class shown here https://boost-ext.github.io/sml/examples.html#data ) and making sure you don’t screw that up somehow.

And as your state machine gets larger, you wind up with a large number of resource handles and variables stored in that data class, most of which are only needed by some small portion of the state machine at any one time.

And you’re right that I do care a lot about the ability to put variables (and resource handles, transitions, behaviors, reactors, etc.) inside of states. Because it allows you to build behavioral state machines as opposed to simply protocol state machines.

In SML, and other protocol state machines, you’re limited to doing everything (via actions) on the transition. Which I submit is awkward for when you want to express complex robotic behaviors for a given state.

Maybe you want variables in a superstate so that you can loop through a sequence a number of times like we did here

Or you might want to synthesize information from multiple orthogonals in the state and act on it like we did here…

Another fact that I think illustrates that it is more natural for programmers to scope variables and resource handles used by a state to the lifetime of the state that uses them, is that that all of the python-based state machine libraries such as SMACH and Flexbe (which have their own problems) also provide state local storage with what they refer to as userdata.

And here is an open SML issue with a user complaining about exactly this issue…
https://github.com/boost-ext/sml/issues/259 - Examples of states with data (4/4/2019) - Status: OPEN

References
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/rationale.html#StateLocalStorage
See https://www.boost.org/doc/libs/1_73_0/libs/statechart/doc/faq.html#StateLocalStorage
See https://lists.boost.org/Archives/boost/2005/02/80382.php
See https://lists.boost.org/Archives/boost/2005/02/80329.php
See https://lists.boost.org/Archives/boost/2009/12/159594.php

If I’m using C++ I care about performance (determinism, utilization, etc) even if I’m not running on a micro-controller. Why would I want to use an old state machine library (Statecharts) that has known performance problems compared to SML?

As I mentioned above, because you can build state machines that are far more complex and are still more than fast enough.

It’s true that Boost Statechart is an older library, which is both good and bad. On the good side, Boost Statechart has been used in industry now for 16 years. And let’s also remember that [boost].SML is not actually a Boost library. It simply aspires to be one, and is trying to get accepted by Boost….

But on the bad side, it is true that Boost Statechart is getting a little long in the tooth, and will probably need an upgrade in the next 5 years. We (SMACC) are aware of this and are working on plans to support and maybe add some updates to the library.

We generally are working on complex robot systems using manipulators with moveit and have found SML to be really nice for our applications

That’s cool. I’m glad you like SML for your applications.
But in the interest of furthering the discussion, do you have some code we could see?

That way we could compare code side by side.
For myself, I would be very curious to find out things like…

How did you handle the asychronicity issue? Did you just use a bunch of sleep() functions?
Or did you use multiple threads? If so what thread policy did you use?
How did you handle data?
How did you handle orthogonals/different subsystems?
How would you handle, SM’s with both orthogonals, and hierarchy?

It would also be very interesting if you could share what some of performance requirements were for these projects such as …

How fast did they run (in Hz)?

I’m going to guess that it was somewhere between 10Hz – 50Hz. Maybe I’m way off and they ran at 500Hz. Either way, these speeds are way below the performance limitations of Boost Statechart as described above.

Also, how many events were you typically responding to per second?

I’m going to guess that it’s less than 100. Which leaves some room between there and the 1 million events used in the SML-Boost Statechart performance benchmarks.

Conclusion

At their core, SML and Boost Statechart, have distinctly different models of state machines, with Boost Statechart being more focused on the states and with SML placing almost all of the functionality into the state machine. This gives SML the edge in event processing speed while Boost Statechart has the ability to build far more complex state machines. And between these two, I believe that the the advantages accrue to Boost Statechart, in the realm of robotics, due to it’s asynchronous nature, as…

  • The number of orthogonals/physical subsystems on the robot increases…
  • The complexity of the orthogonals/physical subsystems themselves increase (think the NavStack vs a low battery sensor)…
  • As the frequency of modifications made to the orthogonals/physical subsystems in order to meet varying mission requirements increases (think of the different F-16 missile payloads for different missions)…
  • The mission complexity of the robot increase (demanding more states, hierarchy, advanced recovery sequences, etc.)

Anyways, I hope this post shed some light on SMACC, and the differences between SML and Boost Statechart.

Cheers.

1 Like

Wow, thank you for the detailed and thoughtful response. As to your list of questions at the end sadly I don’t have any example projects I can share as they all are client projects. I can however try to answer your questions.

How did you handle the asynchronicity issue? Did you just use a bunch of sleep() functions?

I’m not sure what you mean by asynchronicity issue. But in one case where I wanted to have a pool of threads planning the next possible moves by the robot I used an action (transition) to create futures that I added to (globally scoped) pool of futures that I then executed using a thread pool (again globally scoped, however the cpu can only reasonably deal with so many threads, so a global scoped thread pool seems reasonable to me) as they became available. This approach is more of just a normal async programming technique and the state machine did relatively little other than have guards to check if/when there was a plan ready to execute and actions add new ones to the list of ones to work on.

Or did you use multiple threads? If so what thread policy did you use?

I’m not sure what you mean here too. I’ve used SCHED_DEADLINE whenever I had to tightly control the loop rate of something controlling hardware. Other than that I’m normally just using the default thread policy and using profiling and testing to try to confirm I’m meeting my requirements. I’ve often found the biggest offender of meeting loop rates is lots of calls to malloc, eventually one is going to take a long time and if it is the critical path it will cause you to fail. To avoid that in cases where I need to do things like send/receive ros messages I’ve had really good success using boost memory pool allocators.

How did you handle data?

My practice is to create classes that represent various pieces of hardware / compute and have methods that can be called on them to perform some sort of behavior. As to how I pass data through SML. I put references to objects that have state in the event. In SML the way you trigger it to do anything is with an event. The guard tests and the actions (or transitions that cause actions) that result all have access to this event struct (and can modify it). By doing this I hand the sm both references to the objects it needs to perform actions, and a place to store the result of those actions (so guards can test on those results and trigger various transitions).

How did you handle orthogonals/different subsystems?

Generally I think I’m using a state machine in a less fine-grained way than you are. For me the state machine has been a useful way to define system level behavior. As to different subsystems, recently I’ve been working on a project where there are machine level divisions of subsystems (need too much compute to do it on one machine) so each machine has a state machine and on transitions they send ros messages to indicate their state. Using these messages we trigger actions in the other state machines where appropriate. I’m not sure I like this approach but it is working and has been the best solution I’ve found so far.

How would you handle, SM’s with both orthogonals, and hierarchy?

Again I don’t think I have even tried to have orthogonal behavior in a single state machine (see above). For hierarchy using the way I’m doing dependency injection through the event struct I’ve had no trouble having nested state machines in SML. Although I will admit it took me a while to figure out the syntax for nesting state machines (and the compiler errors aren’t helpful). I have not tried spreading them across compilation units.

How fast did they run (in Hz)?

If we are talking about the state machine itself, I’m clearly in the 10-50Hz range for actions on the state machine. I realize how silly my complaints about performance are at this rate.

For the loop rates controlling behaviors in the inner loops, the one where I was using SCHED_DEADLINE was at 4kHz. The state machine’s responsibility related to that behavior was on another thread and communicated with the inner loop by putting a struct in a boost:spsc_queue that would be checked in the inner loop and acted upon. I was leery of putting any logic in that loop I could do elsewhere.

Also, how many events were you typically responding to per second?

100 is about the upper limit of what I’ve done with the state machine itself. Normally much less.

At this point I need to walk back my statement about complex robotics systems. While what we’ve been working with are complex, the complexity of their complete behavior is not represented in the state machine and is instead represented by code outside of the state machine in most cases.

I have to admit a large reason behind my use of SML is I like the syntax of it. That is a purely aesthetic opinion. On the other hand when I read through Statecharts code (including some of your examples) I find it hard to reason about the state machine as a whole. This is probably why you created a UI for representing the state machine so you could reason about it. Again, this is another place where I have to admit aesthetics is affecting me. I generally am biased against anything that the easiest way to represent it is using a translation away from the code itself. I want code that is readable and easy to grok without any external tools.

The whole reason I started using SML in the first place was I was having a hard time reasoning about the behavior of the whole system where I was trying to add async planning and execution. I found that by forcing the large behaviors of the system through SML it helped me refactor the code where doing async planning and execution became easy to understand and reason about.

To have the nice syntax of SML there are obviously trade-offs in capability (with some performance benefits that may not really affect me). I should find sometime to try SMACC to see if I can understand how someone would use it in a project.

As to tight integration with ROS, I’ve found it fairly easy to have subscription callbacks fire events. The behavior inside an action is represented by a SML state machine and depending on how it exits the state machine is how it returns from the action with state transitions resulting in feedback messages.

As to performance monitoring (represented by time spent in states). I have had good success doing that just using nodelet_rosbag and subscribing to the above transition messages. This may not be very performant (however I doubt it is worse than your UI) but it is reasonable considering the low rate that my state machine are running at.

One potential advantage I can see from Statecharts is it would would be a non-ros way for me to combine all my existing state machines into one large one. Right now I’ve got SML state machines behind any ROS Action and at the global state of the system. I generally reason about them using diagrams I create myself. Can Statecharts deal with state machines that exist across processes or machines in some clever way?

Thank you again for such a detailed response. I did not expect that.

Hi Everyone,
Here is a link from Mr. Faconti that he feels shows a fair example of Behavior Tree’s logical syntax…
https://www.behaviortree.dev/bt_basics/

As I said before, I will be incorporating this onto the smacc.ninja developer site.

Thank you Mr. Faconti.