better_launch: a replacement for the ROS2 launch system - intuitive, simple, memorable

Just to note it here: After looking into it, I’m pretty sure the <timer> action is used intentionally (i.e., not due to execution order) here. See my comment.

This looks like great work. My own team has been considering building a TUI to do better node / param management, but perhaps you have filled the gap here. That said, introducing a new syntax for launch is brittle.. I’m not quite ready to move all of our launch files over.

Besides stopping and starting nodes, does the TUI monitor node liveness (e.g. saying if a node that should be up is dead and able to restart)? Does it also list the current node parameters / launch injected parameters?

I don’t think you should go out on a limb too far here. Your code also uses a lot of python classes, and you have a completely different problem. As far as I can see, large parts of better launch are based on rclpy, the Python interface used to build ROS 2 nodes. There are good reasons why ros2 launch is not based on the node interfaces. One example is that ros2 launch files not only handles nodes, but also the parameterisation of urdf files, for example. If you work with ros2 control, you do this regularly.

I don’t use python classes to imitate a basic “if” condition. You may have noticed that python already has that…

I also don’t see how having a dependency on rcl, the one central library of ROS2, is an issue when writing a package for ROS2. And while better_launch creates a ROS node in order to make e.g. service calls, this node is only created once it’s actually needed. I.e. if you never do anything that directly interacts with ROS this node will never be created. In that case the rclpy import is just for type hints.

I think that’s an antipattern; this isn’t a launch issue, it’s an application issue. Your application shouldn’t rely on a strict orchestration (manually ros2 running nodes or ros2 launching them) timing to properly work.

I would argue it’s an anti-pattern because the ROS2 design philosophy doesn’t accommodate it. It’s this design philosophy that resulted in ROS2 being such a huge downgrade in usability and user friendliness.

As it stands, better_launch just works AND is more userfriendly than anything the ROS2 launch system could come up with so far. If that’s an anti-pattern I’m completely fine with that.

1 Like

Thanks for your interest :slight_smile: I’m not planning to drop better_launch, but of course only time can tell.

Besides stopping and starting nodes, does the TUI monitor node liveness (e.g. saying if a node that should be up is dead and able to restart)? Does it also list the current node parameters / launch injected parameters?

Regarding the TUI, the nodes menu will color all nodes the launch file has started as either green or red depending on whether the process is still running. From there you can kill and restart any node (even if it’s not a lifecycle node) and also request lifecycle transitions if the node supports it.

Colors should also be updated while the menu is open, so it should work as a simple live visualization (please open an issue if it doesn’t!). I’m thinking of a better way to do this, but for now that’s what I can offer.

1 Like

This is a foundational concept in ros 2 launch. ros 2 launch is designed to be declarative and execute things at launch time that it may not know about when the code describing them is interpreted by the Python interpreter. That’s why it uses a class to provide an if condition, for example: it needs to calculate the result of that condition at some time in the future when the information it needs has become available.

I know. My point is that it’s bad design and an even worse user experience.

1 Like

Can you explain why?

I think the problem is that 90% of launch files do not need the delayed execution. But because of the remaining 10%, everyone is forced to use the non-intuitive declarative style…

But this is a very common pattern I see at many ROS 2 places (my paint points list has a great deal of issues on this topic). And it’s great to see some of them being slowly addressed! So maybe it’s also time to rethink the launch system to be simple and intuitive by default? XML launch files might be one option, and I’m glad to see them becoming the default in many repos.

2 Likes

When I first learned that ROS2 would use Python for its launch system, I (wrongly, as it turned out) assumed the choice was made to enable a more declarative syntax and the flexibility of writing normal Python code. In practice, however, launch files aren’t so much written in Python as they are constructed using the launch and launch_ros libraries, which effectively constrain you to a declarative approach.

That said, I’ve come to understand this choice over time, and I agree with @christophebedard. Whenever a node required strict orchestration to launch properly, it usually pointed to a deeper design issue within the node itself.

Still, I wish we never had to write launch files in Python. Using launch/launch_ros often feels like interacting with a backend that was meant to be fronted by XML or YAML. Perhaps a system similar to Ansible could work, where most things are declared in YAML, but custom Python modules (using standard Python) can be plugged in as needed. In a way, better_launch already offers this possibility, since its launch files can be included in standard launch files.

2 Likes

Just wanted to hop in and share my more point of view. I really like the discussion that’s ongoing.

When I was some years ago migrating from ROS 1 to ROS 2 for the first time, I got really excited to hear that ROS 2 uses Python for launch files. At that time, we had a need for many if-conditions, loops, launch-time parameter evaluation, and custom logic in our launch files. Xml was pretty clunky when trying to do all this, so I had high hopes that Python launch files would simplify the logic.

Python launch files allow doing all this, but we ended up using a lot of opaque functions to read the launch argument values, and to use the “normal” python syntax.

I feel like this requirement for the launch files being declarative comes with a huge cost of the launch files not being intuitive, or easy to understand. I’d expect to see native Python syntax: ifs, loops, direct launch parameter access, etc., but now it feels like new users need to learn a completely new language to be able to understand and write Python launch files. This can be a huge barrier, which is why XML files currently feel simpler.

Python launch files had a great potential, so I was really happy to learn about the better_launch, addressing many things I’d have expected from the Python launch files originally.

4 Likes

Because that’s exactly what it is :slight_smile:

XML/YAML are the frontends and Python is the underlying backend. XML/YAML frontend implementation lagged behind a bit (IIUC), so people started using mostly Python. This meant that the real frontends didn’t get as much love (e.g., bugfixes, new features, useful examples out in the wild) over the years, although we’re slowly working on it (recent example!).

The call for a “user-friendly” Python frontend (not backend) makes sense to me, like @emersonknapp’s launch_frontend_py or potentially better_launch (although it’s a reimplementation).

7 Likes

Because Python is an imperative language, adding declarative capabilities requires that use of libraries. That is the only way to evaluate conditions at the appropriate time, or evaluate parameters when the source for them is actually available. Yes, you can do this in Python without all those libraries and using its language-level statements, but then you’re just doing imperative programming, not declarative. If imperative programming is how you want to orchestrate your system, then that’s fine, but that wasn’t the choice made for ROS’s new launch system when it was started.

Most places where a declarative approach is desired will produce their own DSL - look at Nix and QML for excellent examples. That can be very hit-or-miss. We chose to produce a Python back end for our behaviour to gain the power of Python immediately, and rely on frontends to provide the nice UX - such as XML, or a user-friendly Python API.

ROS 1 launch had developed a quite successful DSL :smiley:

Right, I see. Thanks @christophebedard and @gbiggs for the clarifications!

I have never actually realized that Python launch files are actually the backend, and not meant to be the front-end of the launch. I think this is a general misconception and one source of frustration with Python launch files. So what we are basically missing in the official launch implementation is the Python front-end for the launch?

What are the benefits of having the launch implemented as declarative? I’m not sure if I’ve seen a place where I would’ve needed this feature, and @peci1 also was wondering how big portion of the users actually need it. I’m all in for the declarative approach, but if in most of the cases it isn’t needed, couldn’t we have a simple, imperative way of launching as the default implementation, and then declarative available for advanced usage?

Right now better_launch provides this option, which is amazing. I haven’t yet had a chance to try it out, but if it works, It’s something that I’d love to see in the official launch implementation.

This document has details. Here’s a key paragraph:

The goal of the system description is to capture the intentions of the user describing the system to be launched, with as few side effects as possible. The reason for doing this is so that a launch description can be visualized and statically analyzed without actually launching the described system. Having a tool that can allow a developer to visualize and modify the launch description in a WYSIWYG (what you see is what you get) editor is an important use case for the system description.

Basically, it’s harder to statically analyse what system you will get from a launch description if it’s not declarative.

Notably, configuration management tools such as Terraform, Chef, and Puppet use a declarative approach.

@wjwwood can give more information if there are gaps in that document.

a launch description can be visualized and statically analyzed without actually launching the described system

That sounds super cool.

Yet I still miss the roslaunch-checker we had in ROS1. I gave it a try to recreate it multiple times but never found a way without actually launching things.

But from this statement I conclude this should actually be possible. So I have new hopes :slight_smile:

edit: closest I got was mocking subprocess.popen and launching it. Which is exactly the opposite of the quoted section above, because that would work for any python implementation

3 Likes

Yeah, the ability to visualize and analyze the system without launching the system is indeed a really cool feature!

@ndahn, do you think better_launch would also be able to do this? By for example doing a “dry-run” to see which nodes are launched, without actually launching them.

@gbiggs and others, do you know if there is something existing that can currently visualize the launch, or otherwise utilize the declarative language? Or is this declarativeness currently more of a dead feature?

I often hear that ROS 2 has a high learning curve and is difficult for newcomers to jump into. I think the current state of the launch contributes to this issue. I’m still not convinced that the declarative style is something we want, if it makes the launch file syntax overly complicated.

I’m now wondering how the declarative approach / ability to visualize launch plays in with tools such as @emersonknapp’s graph_monitor, that can show continuously the system state and which nodes are up and running. If we have something to monitor the system state while it is running, is there a need for launch-time visualization and analysis?

I’d really want ROS 2 to move into a direction of simplifying the launch. In my opinion, supporting multiple formats (python, xml, yaml) just adds up to the complexity and brings in a huge maintenance burden. There will be differences in what features are available in each. I’d much rather just choose the best and simplest launch format, and go forward only with that.

I wouldn’t be happy to move back to XML launch files. In my eyes, the Python ones have the most flexibility and allow doing things that isn’t possible in XML. Currently just the complexity of the Python syntax is an issue. I think having a user-friendly Python front-end is the right way to go!

3 Likes

It’s not a dead feature, but, as with everything else, someone needs to step up to build the tool.

I don’t think it does, as the XML and YAML frontends demonstrate. Trying to use the backend as a frontend (in part because the frontends were not produced for quite some time) makes things unnecessarily difficult.

The popularity of declarative approaches in configuration management tools is justified, I think, just as it is for ROS.

We did just have the XML frontend, but there was a lot of support for also having a YAML one.

Yes. Safety-critical systems require such static analysis. Non-safety-critical production systems benefit from it greatly.

1 Like

I think it is!

See Why does the ROS2 Python launch files look like XML written in Python? - #7 by wjwwood

Agreed, that’s definitely a documentation issue. For some context, we started by only having the Python API due to time constraints, so we documented how to use it for making launch files a bit, in order to have something. Later we finished the frontends which let you use XML and YAML. I’ve always believed these should be what most people should use most of the time.

Honestly in my experience launch is still pretty XML-friendly and the YAML can be a little weirdly XML-like in some cases. Unfortunately I never have had time to try to learn the launch internals enough to tackle this, for example:

I think for now the best thing to do is raise awareness and start using XML as much as possible.

For those things that really are easier and better to do in imperative Python,OpaqueFunction is always an option.