ros2 / launch

Tools for launching multiple processes and for writing tests involving multiple processes.
Apache License 2.0
125 stars 139 forks source link

RegisterEventHandler: action_matcher must be an 'ExecuteProcess' instance or a callable #784

Closed RobinHeitz closed 3 months ago

RobinHeitz commented 3 months ago

Bug report

Required Info:

Steps to reproduce issue

In a launch file, I want to only include some other launch file, when gazebo has started. I want to then spawn a robot.

If tried many combinations for using an RegisterEventHandler, even those from the humble tutorials, None worked:

[ERROR] [launch]: Caught exception in launch (see debug for traceback): Caught multiple exceptions when trying to load file of format [py]:

  • TypeError: action_matcher must be an 'ExecuteProcess' instance or a callable
  • InvalidFrontendLaunchFileError: The launch file may have a syntax error, or its format is unknown
    start_gazebo_client_cmd = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(pkg_gazebo_ros, "launch", "gzclient.launch.py")
        ),
        condition=IfCondition(PythonExpression([use_simulator, " and not ", headless])),
    )

    event_handler_launch_robot = RegisterEventHandler(
        OnProcessStart(
            target_action=start_gazebo_server_cmd,
            on_start=[LogInfo(msg='=== Gazebo finished launching.')]
        )
    )

    return LaunchDescription(
        [
        declare_launch_robot,
        declare_simulator_cmd, 
        declare_use_sim_time_cmd,
        declare_use_simulator_cmd,
        declare_world_cmd,
        start_gazebo_server_cmd,
        start_gazebo_client_cmd,
        include_robot_launch,
    ])

Expected behavior

It should not thrown an err.

Actual behavior

When launching this launch file, it catches exceptions.

Additional information

Here is the log from -d:

[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription'
[INFO] [launch]: All log files can be found below /home/ros/.ros/log/2024-06-11-16-01-20-212890-rosai-2204-59517
[INFO] [launch]: Default logging verbosity is set to DEBUG
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x716004a0f700>'
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x716004a0f700>' ✓ '<launch.event_handlers.on_include_launch_description.OnIncludeLaunchDescription object at 0x716004a0fdc0>'
=== World path:  /home/ros/ros_ws/install/sim_bringup_masterly/share/sim_bringup_masterly/worlds/decathlon.world
[DEBUG] [launch]: An exception was raised in an async action/event
[DEBUG] [launch]: Traceback (most recent call last):
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_description_sources/any_launch_file_utilities.py", line 54, in get_launch_description_from_any_launch_file
    return loader(launch_file_path)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_description_sources/python_launch_file_utilities.py", line 68, in get_launch_description_from_python_launch_file
    return getattr(launch_file_module, 'generate_launch_description')()
  File "/home/ros/ros_ws/install/sim_bringup_masterly/share/sim_bringup_masterly/launch/decathlon_robot.launch.py", line 170, in generate_launch_description
    OnProcessStart(
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/event_handlers/on_process_start.py", line 63, in __init__
    super().__init__(
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/event_handlers/on_action_event_base.py", line 69, in __init__
    raise TypeError(
TypeError: action_matcher must be an 'ExecuteProcess' instance or a callable

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_service.py", line 336, in run_async
    raise completed_tasks_exceptions[0]
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_service.py", line 230, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_service.py", line 250, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/actions/include_launch_description.py", line 148, in execute
    launch_description = self.__launch_description_source.get_launch_description(context)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_description_source.py", line 84, in get_launch_description
    self._get_launch_description(self.__expanded_location)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_description_sources/any_launch_description_source.py", line 53, in _get_launch_description
    return get_launch_description_from_any_launch_file(location)
  File "/opt/ros/humble/lib/python3.10/site-packages/launch/launch_description_sources/any_launch_file_utilities.py", line 57, in get_launch_description_from_any_launch_file
    raise InvalidLaunchFileError(extension, likely_errors=exceptions)
launch.invalid_launch_file_error.InvalidLaunchFileError: Caught multiple exceptions when trying to load file of format [py]:
 - TypeError: action_matcher must be an 'ExecuteProcess' instance or a callable
 - InvalidFrontendLaunchFileError: The launch file may have a syntax error, or its format is unknown

[ERROR] [launch]: Caught exception in launch (see debug for traceback): Caught multiple exceptions when trying to load file of format [py]:
 - TypeError: action_matcher must be an 'ExecuteProcess' instance or a callable
 - InvalidFrontendLaunchFileError: The launch file may have a syntax error, or its format is unknown
[DEBUG] [launch.launch_context]: emitting event: 'launch.events.Shutdown'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x71600479dab0>'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x71600479dab0>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x716004a0f670>'
wjwwood commented 3 months ago

I believe this is expected behavior, so I'm going to close this issue, but I will provide more information below.


In your example what you're passing to the target_action argument of the OnProcessStart event handler is an IncludeLaunchDescription action, which cannot be a target for that event handler. The target_action needs to point to an action that represents a single process, e.g. an ExecuteProcess action, or a callable (like a function) that takes the Action instance that created the event and returns true if it matches or false otherwise. The IncludeLaunchDescription action you're passing isn't something that the OnProcessStart can use to match to a process, hence this error:

TypeError: action_matcher must be an 'ExecuteProcess' instance or a callable

Instead, you'll need to figure out how to get access to the exact ExecuteProcess instance you want to match (which will not be possible through an include statement I think) or you'll need to write a function that can match it. I think making a custom matcher is probably your best bet, but you can also use some of the built-in matchers:

https://github.com/ros2/launch/blob/651f57451e4d0e93d055669764fcd588e2d9b1e6/launch/launch/events/process/process_matchers.py#L25-L61

If none of those are what you need to match the process you want, then you can use one of those as a starting point for making your own matcher to pass to the target_action.


On a higher level, I don't think this event is a reliable way to know when Gazebo is "done launch" and/or "ready for use". This process started event happens pretty much immediately, and simply means the process has been started by the OS and stdout/stderr/stdin are attached to launch. It does not, however, mean that gazebo has loaded your world file, opened the ui, finished loading the server, is running the simulation, is providing ROS topics or services, etc...

Instead, you probably want to wait for some specific IO event (like a particular string to appear in stdout) or for something else that implies the system is working, like the availability of a ros topic or maybe clock messages being produced by the simulation.

But I'll leave that up to you, since I'm not sure exactly what you're trying to detect.

wjwwood commented 3 months ago

Oh and with respect to:

If tried many combinations for using an RegisterEventHandler, even those from the humble tutorials, None worked:

If you can link to me the tutorials you tried, I can verify that they're working, or maybe help you understand them.

Cheers!