Equivalents of "if" and "unless" ROS1 XML for ROS2 python launch files
Hi,
I migrate some packages to ROS2 (humble) and I don't find an equivalent of the if
and unless
arguments I had in ROS1 (noetic) XML launch files for python ROS2 launchers.
I know I could achieve that with XML in ROS2 too, but I was curious to make it in python since it seems to be a more "ROSish" way to do it, nowadays.
A very simple example ROS1 snippet XML
<launch>
<arg name="viz" default="false"/>
<node if="$(arg viz)" pkg="rviz" name="rviz" type="rviz"/>
</launch>
Now In ROS2, I have the following python file, but the rviz node run is not conditioned to the value of viz
anymore.
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import TextSubstitution
from launch_ros.actions import Node
def generate_launch_description():
launch_description = LaunchDescription()
viz_arg = DeclareLaunchArgument("viz", default_value=TextSubstitution(text="false"))
launch_description.add_action(viz_arg)
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
)
# I'd like condition the following line with an if, I guess
launch_description.add_action(rviz_node)
return launch_description
Thanks for your help.
Asked by aplat on 2022-12-05 05:42:13 UTC
Answers
This is answered is previous questions. For instance, see: https://answers.ros.org/question/382000/ros2-makes-launch-files-crazy-too-soon-to-be-migrating/?answer=382141#post-id-382141
In short, you should be able to do:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import TextSubstitution
from launch_ros.actions import Node
def generate_launch_description():
launch_description = LaunchDescription()
viz_arg = DeclareLaunchArgument("viz", default_value=TextSubstitution(text="false"))
launch_description.add_action(viz_arg)
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
condition=IfCondition(LaunchConfiguration('viz')),
)
launch_description.add_action(rviz_node)
return launch_description
Asked by christophebedard on 2022-12-05 16:30:02 UTC
Comments
Thanks for your answer and the link. I now understand that it has to be done through the condition mechanism.
My question was broader than the example I posted though, because the if
and unless
of ROS1 are usable in more than <node>
tag, for example also in <param>
and in <include>
.
Asked by aplat on 2022-12-08 17:14:33 UTC
The condition
parameter applies to all Action
s: https://github.com/ros2/launch/blob/7694235bbf00f19dafd7251f6954f7f14606ab4c/launch/launch/action.py#L40. Node
, IncludeLaunchDescription
, SetParameter
, etc. are Action
s, so they all (as well as their corresponding YAML/XML versions) support the condition
parameter.
Asked by christophebedard on 2022-12-08 17:20:35 UTC
Thanks, that was the missing part of the puzzle.
The naming is not obvious though, I did not get that all those classes inherited from Action
. Now it makes perfect sense. Thanks again!
Asked by aplat on 2022-12-08 18:00:09 UTC
Yeah, documentation could definitely be helpful here.
Asked by christophebedard on 2022-12-10 12:50:33 UTC
To mimic a if
and unless
present into an <include>
tag, you should use conditions such as pointed by Christophe in another answer.
Note that several types of conditions exists, such as IfCondition
, UnlessCondition
, LaunchConfigurationEquals
and LaunchConfigurationNotEquals
For example, to migrate the following ROS1 XML code
<include file="$(find pkg_1)/launch/my_file.launch" if="$(eval points != '/points')">
In python ROS2
from ament_index_python import get_package_share_directory
from launch import LaunchDescription
from launch.actions.include_launch_description import IncludeLaunchDescription
from launch.conditions import LaunchConfigurationNotEquals
from launch.launch_description_sources
import PythonLaunchDescriptionSource
import os
launch_description = LaunchDescription()
pkg_1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(get_package_share_directory('pkg_1'), 'launch/my_file.launch')),
condition=LaunchConfigurationNotEquals('points', "/points"),
)
launch_description.add_action(pkg_1_include)
Asked by aplat on 2022-12-08 17:53:27 UTC
Comments
Hi,
If you use the simple_launch package*, conditions boil down to a with
block as follows:
from simple_launch import SimpleLauncher
def generate_launch_description():
sl = SimpleLauncher()
sl.declare_arg('viz', default_value=False, description='Run RViz')
with sl.group(if_arg='viz'):
sl.node('rviz2', 'rviz2')
return sl.launch_description()
More complex conditions can be combined, and the opaque_function approach can always be taken when actual Python (not launch) logic is needed.
- disclaimer: I am the author of this package
Asked by Olivier Kermorgant on 2022-12-12 06:20:08 UTC
Comments
Looks great, I'll definitely give it a try. To be honest, the number of line inflation from a one liner XML snippet to a 15+ lines python equivalent is a bit hard to swallow, and your package looks very appealing.
Asked by aplat on 2022-12-16 06:15:46 UTC
I could not agree more, that is why simple_launch
was created in the first place.
The initial idea was to easily teach the principles of launch files without the syntax bloat but at the end of the day this package is useful for many cases.
Asked by Olivier Kermorgant on 2022-12-16 09:14:40 UTC
If you want to use XML, then why don't you create an XML ROS 2 launch file?
Asked by christophebedard on 2022-12-17 13:52:34 UTC
Comments