ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
4

ROS2 Launch - How to Concatenate LaunchConfiguration with String

asked 2021-08-19 18:27:42 -0600

shonigmann gravatar image

Is there a preferred way to concatenate a LaunchConfiguration with a string (e.g. a file extension, prefix, or suffix) that can be used in a similar way to PathJoinSubstitution?

e.g. given:

DeclareLaunchArgument('my_robot', default_value='my_robot_name')

a path could be made to my_robot using:

PathJoinSubstitution(['path', 'to', LaunchConfiguration('my_robot')])

which would evaluate as:

/path/to/my_robot_name

But what if I want to create a path to my_robot_name.config? Is there an equivalent way to combine strings and LaunchConfigurations?

As a workaround, I've used Command or PythonExpression substitutions, e.g.:

Command(['echo -n ', LaunchConfiguration('robot_name'), '.config'])  # or
PythonExpression(["'", LaunchConfiguration('robot_name'), ".config'"])

But I think it would be great if there was a cleaner, more readable alternative that I've completely overlooked.

edit retag flag offensive close merge delete

3 Answers

Sort by ยป oldest newest most voted
0

answered 2023-08-08 11:43:49 -0600

shonigmann gravatar image

@Fetullah Atas' answer got me most of the way there. Moving my comment to an answer to hopefully improve visibility. As a potential correction to his example, using a secondary launch argument that references the first is one possible workaround.

Example:

joy_config = LaunchConfiguration('joy_config')
joy_config_dec= DeclareLaunchArgument('joy_config', default_value='ps3')
final_string_dec= DeclareLaunchArgument('final_string', default_value=[LaunchConfiguration('joy_config'), '.config.yaml'])

Which would result in LaunchConfiguration('final_string') evaluating to: "ps3.config.yaml".

I'd be surprised if there wasn't a more direct solution since this question was originally asked.

edit flag offensive delete link more
1

answered 2021-08-20 03:52:35 -0600

updated 2021-08-20 03:58:56 -0600

Hi for this particular case, launch.substitutions.TextSubstitution might be what you are looking for.

You can see see example usage of the case here;

 joy_config = launch.substitutions.LaunchConfiguration('joy_config')
 joy_config_dec= launch.actions.DeclareLaunchArgument('joy_config', default_value='ps3'),

 final_string = os.path.join(joy_config, launch.substitutions.TextSubstitution(text='.config.yaml'))

The final string should be "ps3.config.yaml"

You can see complete example and reference at here ; https://github.com/ros2/teleop_twist_...

edit flag offensive delete link more

Comments

2

It seems the example you linked uses a list of strings and TextSubstitutions in the context of setting the default argument. Using substitutions directly within os.path.join will result in an error, as join() expects a string. In Foxy it results in:

Caught exception when trying to load file of format [py]: expected str, bytes or os.PathLike object, not LaunchConfiguration

In any case, thank you! The method demonstrated in the linked example (creating a new launch argument with a default value based on a previous argument) is a better approach than what I was doing!

shonigmann gravatar image shonigmann  ( 2021-08-20 11:49:56 -0600 )edit
1

As a potential correction to your example, the following works as expected:

joy_config = LaunchConfiguration('joy_config')
joy_config_dec= DeclareLaunchArgument('joy_config', default_value='ps3')
final_string_dec= DeclareLaunchArgument('final_string', default_value=[LaunchConfiguration('joy_config'), '.config.yaml'])

Which would result in LaunchConfiguration('final_string') evaluating to: "ps3.config.yaml" without the explicit use of TextSubstitution. In fact, I am still unclear what TextSubstitution's purpose is - perhaps it is a legacy function from before ROS2 was able to parse a mixed list of strings and LaunchConfigurations?

shonigmann gravatar image shonigmann  ( 2021-08-20 11:53:32 -0600 )edit
0

answered 2023-05-05 06:20:26 -0600

hacker1024 gravatar image

I did not want to declare another launch argument, so I generalized the PythonExpression solution above in a function.

from launch import SomeSubstitutionsType
from launch.substitutions import PythonExpression
from launch.utilities import normalize_to_list_of_substitutions

def cat_substitutions(substitutions: SomeSubstitutionsType) -> Substitution:
    return PythonExpression(["'", *normalize_to_list_of_substitutions(substitutions), "'"])
edit flag offensive delete link more

Comments

using a = PythonExpression(["'map", LaunchConfiguration('map_number'), ".world'"]) How can I add the formatted PythonExpression to my DeclareLaunchArgument

return LaunchDescription([
    DeclareLaunchArgument(
        'world',
        default_value=[os.path.join(
            # Change dir below if you want to load different world
            get_package_share_directory('my_bot'), 'worlds', a), ''],
        description='SDF world file'),
    gazebo
])

Or should this be a separate question?

i1Cps gravatar image i1Cps  ( 2023-05-09 11:41:03 -0600 )edit
1

what you currently have will fail because os.path.join has no knowledge of ROS 2 launch substitutions or the launch context, and only supports string inputs.

if memory serves, you can either:

  • append the PythonExpression to the default_value list (e.g. default_value=[os.path.join(...), '/', a] and DeclareLaunchArgument will handle the substitution in the launch context at launch time. It will fail when you try to add it. This is a bit sloppier as you need to hard code a path separator
  • use PathJoinSubstitution (from launch.substitutions import PathJoinSubstitution) which functions like os.path.join but supports substitutions. e.g.: default_value=PathJoinSubstitution([get_package_share_directory('my_bot'), 'worlds', a]). This approach is probably preferred as it is more portable
shonigmann gravatar image shonigmann  ( 2023-05-10 10:42:40 -0600 )edit

Thanks, It worked perfectly. When trying to use the LaunchConfiguration('map_number') as a condition. E.G.: map_number = LaunchConfiguration('map_number') if map_number == '1': # Do something How should we handle that? As it wont compare a LaunchConfiguration to a string. Or is this a seperate question?

i1Cps gravatar image i1Cps  ( 2023-05-15 10:09:02 -0600 )edit

Question Tools

2 followers

Stats

Asked: 2021-08-19 18:27:42 -0600

Seen: 4,114 times

Last updated: Aug 08 '23