Hi all - wanted to share some work we’ve been doing internally to help with static recovery and visualization of our ROS graph. We did this by hijacking some other tools we’ve been using to standardize our package structure and simplify our launch files.
We ended up creating and open sourcing three packages:
- cake ( GitHub - greenforge-labs/cake: Declarative code generation for ROS 2 nodes )
- clingwrap ( GitHub - greenforge-labs/clingwrap: A ROS2 launch wrapper to simplify writing python launch files )
- breadcrumb ( GitHub - greenforge-labs/breadcrumb: A ROS graph static analysis tool that works with cake and clingwrap )
cake is a concept that started as trying to simplify the boilerplate required to set up a C++ node by using a more functional approach to node initialization. For the purposes of static graph analysis, we extended it to include a declarative interface file (publishers, subscribers, etc) which would be consumed at build time to generate the ROS code required for these interfaces. A lot of inspiration was taken from the picknik robotics generate_parameter_library (in fact we wrapped this package and included parameters in the node interface file). If you follow the folder structure suggested, cake also provides an automatic cmake macro, which uses ament_cmake_auto under the hood.
clingwrap is yet another python launch wrapper. Arguments about reinventing the launch wrapper wheel aside, it was convenient for us as we were already using it in all our launch files so it provided a good way to instrument all our launch files to statically extract node launch details. clingwrap provides a LaunchBuilder object which is a subclass of LaunchDescription. The idea is that LaunchBuilder is a mutable object which will track all launch actions as it gets mutated, meaning you just have to return it at the end of generate_launch_description. This lets us add extra tracking logic inside the LaunchBuilder class and expose a get_static_information method on it which lets the user call generate_launch_description and then get_static_information on the resulting object - which returns a dataclass of node information such as package and executable name, remappings, etc. We explored parsing the underlying actions that come out of the base launchfile system, but this got complicated quickly (especially recovering composable node information!) so we fell back to this simpler solution.
breadcrumb is a cli tool that uses the static interfaces from cake and the launch information from clingwrap to generate the final runtime ROS graph, based on a launchfile provided (without executing a ROS launch). It then spits out the graph as a json file or a graphviz dot file.
The breadcrumb repo also has an example cartpole project that shows how all three packages work together in one system: breadcrumb/breadcrumb_example at main · greenforge-labs/breadcrumb · GitHub
All of these packages are still very fresh - we are rolling them out in our codebase currently, and expect to extend them where we find extra usecases / corner cases.
Whilst they are somewhat specific to our current system (i.e. you can’t use breadcrumb without rewriting all your launchfiles with clingwrap) I thought it was worth sharing what we’ve come up with for moving our ROS codebase towards being more declarative and statically analyzable.
(the statically generated ROS graph diagram from the breadcrumb example project)
