From c2c6f19d1086a6a338dedf3052ee68f045bf7e15 Mon Sep 17 00:00:00 2001 From: Yannick Goumaz <61198661+ygoumaz@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:08:19 +0200 Subject: [PATCH] Sync fork (#17) * Update launch files to declare WebotsController node (#11) (#3763) * change launch files * Update Setting-Up-Simulation-Webots-Basic.rst * update minimal version tutorial 2 * add upcoming versions * New Reset Handler tutorial (#12) (#3764) * new tutorial Co-authored-by: Olivier Michel * Update macOS-Development-Setup.rst (#3758) - add Xcode App Store link - BASH -> ZSH (default) - make homebrew paths generic Co-authored-by: Philipp Dittmann * Change confusing title between Debian package and Debian platform (#3771) Currently Debian does not offer ROS2 packages. Change the title that can be confusing. * Make the calendar more mobile-friendly. (#3774) * Make the calendar more mobile-friendly. When looking at some of the analytics, it looks like this page is particularly not mobile friendly. That's because the Google calendar embedded on the bottom of the page is of fixed with 800x600, which doesn't work well on many mobile devices. What we do here is to see if a device has 767 or less pixels. If it does, then we just show the agenda, which fits in just 280 pixels. If we have more than that, we have room for the full monthly agenda, so we show the full thing. Signed-off-by: Chris Lalancette Co-authored-by: Tomoya Fujita --------- Signed-off-by: Chris Lalancette Co-authored-by: Olivier Michel Co-authored-by: PhDittmann Co-authored-by: Philipp Dittmann Co-authored-by: Quentin Quadrat Co-authored-by: Chris Lalancette Co-authored-by: Tomoya Fujita --- .../Alternatives/macOS-Development-Setup.rst | 14 +- source/Installation/RHEL-Install-RPMs.rst | 4 +- .../Installation/Ubuntu-Install-Debians.rst | 4 +- source/The-ROS2-Project/Governance.rst | 35 +++- .../Simulators/Webots/Code/robot_launch.py | 15 +- .../Webots/Code/robot_launch_sensor.py | 16 +- .../Setting-Up-Simulation-Webots-Advanced.rst | 10 +- .../Setting-Up-Simulation-Webots-Basic.rst | 21 +- .../Webots/Simulation-Reset-Handler.rst | 193 ++++++++++++++++++ .../Simulators/Webots/Simulation-Webots.rst | 1 + 10 files changed, 264 insertions(+), 49 deletions(-) create mode 100644 source/Tutorials/Advanced/Simulators/Webots/Simulation-Reset-Handler.rst diff --git a/source/Installation/Alternatives/macOS-Development-Setup.rst b/source/Installation/Alternatives/macOS-Development-Setup.rst index e8121b2aaf..6dd60e54f7 100644 --- a/source/Installation/Alternatives/macOS-Development-Setup.rst +++ b/source/Installation/Alternatives/macOS-Development-Setup.rst @@ -29,7 +29,7 @@ You need the following things installed to build ROS 2: #. **Xcode** - * If you don't already have it installed, install Xcode. + * If you don't already have it installed, install [Xcode](https://apps.apple.com/app/xcode/id497799835). * Note: Versions of Xcode later than 11.3.1 can no longer be installed on macOS Mojave, so you will need to install an older version manually, see: https://stackoverflow.com/a/61046761 * Also, if you don't already have it installed, install the Command Line Tools: @@ -71,12 +71,12 @@ You need the following things installed to build ROS 2: .. code-block:: bash # Add the openssl dir for DDS-Security - # if you are using ZSH, then replace '.bashrc' with '.zshrc' - echo "export OPENSSL_ROOT_DIR=$(brew --prefix openssl)" >> ~/.bashrc + # if you are using BASH, then replace '.zshrc' with '.bashrc' + echo "export OPENSSL_ROOT_DIR=$(brew --prefix openssl)" >> ~/.zshrc # Add the Qt directory to the PATH and CMAKE_PREFIX_PATH - export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/usr/local/opt/qt@5 - export PATH=$PATH:/usr/local/opt/qt@5/bin + export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$(brew --prefix qt@5) + export PATH=$PATH:$(brew --prefix qt@5)/bin #. Use ``python3 -m pip`` (just ``pip`` may install Python3 or Python2) to install more stuff: @@ -92,7 +92,7 @@ You need the following things installed to build ROS 2: nose pep8 psutil pydocstyle pydot pygraphviz pyparsing==2.4.7 \ pytest-mock rosdep rosdistro setuptools==59.6.0 vcstool - Please ensure that the ``$PATH`` environment variable contains the install location of the binaries (default: ``$HOME/Library/Python//bin``) + Please ensure that the ``$PATH`` environment variable contains the install location of the binaries (``$(brew --prefix)/bin``) #. *Optional*: if you want to build the ROS 1<->2 bridge, then you must also install ROS 1: @@ -156,7 +156,7 @@ Source the ROS 2 setup file: .. code-block:: bash - . ~/ros2_{DISTRO}/install/setup.bash + . ~/ros2_{DISTRO}/install/setup.zsh This will automatically set up the environment for any DDS vendors that support was built for. diff --git a/source/Installation/RHEL-Install-RPMs.rst b/source/Installation/RHEL-Install-RPMs.rst index 76f158a9a8..e3eaaf4ff9 100644 --- a/source/Installation/RHEL-Install-RPMs.rst +++ b/source/Installation/RHEL-Install-RPMs.rst @@ -1,5 +1,5 @@ -RHEL (RPM) -========== +RHEL (RPM packages) +=================== .. contents:: Table of Contents :depth: 2 diff --git a/source/Installation/Ubuntu-Install-Debians.rst b/source/Installation/Ubuntu-Install-Debians.rst index f878bc43dc..45f2724128 100644 --- a/source/Installation/Ubuntu-Install-Debians.rst +++ b/source/Installation/Ubuntu-Install-Debians.rst @@ -2,8 +2,8 @@ Installation/Linux-Install-Debians -Ubuntu (Debian) -=============== +Ubuntu (Debian packages) +======================== .. contents:: Table of Contents :depth: 2 diff --git a/source/The-ROS2-Project/Governance.rst b/source/The-ROS2-Project/Governance.rst index cab0167bdc..7f106ea244 100644 --- a/source/The-ROS2-Project/Governance.rst +++ b/source/The-ROS2-Project/Governance.rst @@ -333,8 +333,37 @@ It can be accessed via `iCal - - + + +
+
+ +
+
+
+
If you have an individual event or series of events that you'd like to post please contact info@openrobotics.org diff --git a/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch.py b/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch.py index 574c6a3e0d..1da5355010 100644 --- a/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch.py +++ b/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch.py @@ -1,28 +1,23 @@ import os -import pathlib import launch -from launch_ros.actions import Node from launch import LaunchDescription from ament_index_python.packages import get_package_share_directory from webots_ros2_driver.webots_launcher import WebotsLauncher -from webots_ros2_driver.utils import controller_url_prefix +from webots_ros2_driver.webots_controller import WebotsController def generate_launch_description(): package_dir = get_package_share_directory('my_package') - robot_description = pathlib.Path(os.path.join(package_dir, 'resource', 'my_robot.urdf')).read_text() + robot_description_path = os.path.join(package_dir, 'resource', 'my_robot.urdf') webots = WebotsLauncher( world=os.path.join(package_dir, 'worlds', 'my_world.wbt') ) - my_robot_driver = Node( - package='webots_ros2_driver', - executable='driver', - output='screen', - additional_env={'WEBOTS_CONTROLLER_URL': controller_url_prefix() + 'my_robot'}, + my_robot_driver = WebotsController( + robot_name='my_robot', parameters=[ - {'robot_description': robot_description}, + {'robot_description': robot_description_path}, ] ) diff --git a/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch_sensor.py b/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch_sensor.py index 3644f32e18..fe72ea6a09 100644 --- a/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch_sensor.py +++ b/source/Tutorials/Advanced/Simulators/Webots/Code/robot_launch_sensor.py @@ -1,28 +1,24 @@ import os -import pathlib import launch from launch_ros.actions import Node from launch import LaunchDescription from ament_index_python.packages import get_package_share_directory -from webots_ros2_driver.webots_launcher import WebotsLauncher, Ros2SupervisorLauncher -from webots_ros2_driver.utils import controller_url_prefix +from webots_ros2_driver.webots_launcher import WebotsLauncher +from webots_ros2_driver.webots_controller import WebotsController def generate_launch_description(): package_dir = get_package_share_directory('my_package') - robot_description = pathlib.Path(os.path.join(package_dir, 'resource', 'my_robot.urdf')).read_text() + robot_description_path = os.path.join(package_dir, 'resource', 'my_robot.urdf') webots = WebotsLauncher( world=os.path.join(package_dir, 'worlds', 'my_world.wbt') ) - my_robot_driver = Node( - package='webots_ros2_driver', - executable='driver', - output='screen', - additional_env={'WEBOTS_CONTROLLER_URL': controller_url_prefix() + 'my_robot'}, + my_robot_driver = WebotsController( + robot_name='my_robot', parameters=[ - {'robot_description': robot_description}, + {'robot_description': robot_description_path}, ] ) diff --git a/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Advanced.rst b/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Advanced.rst index da3871b0fc..cac545fa34 100644 --- a/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Advanced.rst +++ b/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Advanced.rst @@ -24,6 +24,8 @@ Prerequisites This is a continuation of the first part of the tutorial: :doc:`./Setting-Up-Simulation-Webots-Basic`. It is mandatory to start with the first part to set up the custom packages and necessary files. +This tutorial is compatible with version 2023.1.0 of ``webots_ros2`` and Webots R2023b, as well as upcoming versions. + Tasks ----- @@ -153,7 +155,7 @@ Go to the file ``robot_launch.py`` and replace ``def generate_launch_description .. literalinclude:: Code/robot_launch_sensor.py :language: python - :lines: 11-44 + :lines: 10-40 This will create an ``obstacle_avoider`` node that will be included in the ``LaunchDescription``. @@ -221,8 +223,6 @@ Next steps ---------- You might want to improve the plugin or create new nodes to change the behavior of the robot. -Taking inspiration from these previous tutorials could be a starting point: - -* :doc:`../../Recording-A-Bag-From-Your-Own-Node-Py`. +You can also implement a reset handler to automatically restart your ROS nodes when the simulation is reset from the Webots interface: -* :doc:`../../../Intermediate/Tf2/Tf2-Main`. +* :doc:`./Simulation-Reset-Handler`. diff --git a/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Basic.rst b/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Basic.rst index 4f9de6cec3..d92ca272ab 100644 --- a/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Basic.rst +++ b/source/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Basic.rst @@ -49,6 +49,8 @@ In particular, :doc:`../../../Beginner-CLI-Tools/Introducing-Turtlesim/Introduci The Linux and ROS commands of this tutorial must be run in a pre-configured Linux Virtual Machine (VM). The following page :doc:`./Installation-MacOS` explains how to install the ``webots_ros2`` package on macOS. +This tutorial is compatible with version 2023.1.0 of ``webots_ros2`` and Webots R2023b, as well as upcoming versions. + Tasks ----- @@ -344,10 +346,10 @@ You have to specify in the constructor which world file the simulator will open. .. literalinclude:: Code/robot_launch.py :language: python :dedent: 4 - :lines: 15-17 + :lines: 13-15 Then, the ROS node interacting with the simulated robot is created. -This node, named ``driver``, is located in the ``webots_ros2_driver`` package. +This node, named ``WebotsController``, is located in the ``webots_ros2_driver`` package. .. tabs:: @@ -366,33 +368,32 @@ This node, named ``driver``, is located in the ``webots_ros2_driver`` package. In your case, you need to run a single instance of this node, because you have a single robot in the simulation. But if you had more robots in the simulation, you would have to run one instance of this node per robot. -``WEBOTS_CONTROLLER_URL`` is used to define the name of the robot the driver should connect to. -The ``controller_url_prefix()`` method is mandatory, as it allows ``webots_ros2_driver`` to add the correct protocol prefix depending on your platform. -The ``robot_description`` parameter holds the contents of the URDF file which refers to the ``MyRobotDriver`` plugin. -You can see the ``driver`` node as the interface that connects your controller plugin to the target robot. +The ``robot_name`` parameter is used to define the name of the robot the driver should connect to. +The ``robot_description`` parameter holds the path to the URDF file which refers to the ``MyRobotDriver`` plugin. +You can see the ``WebotsController`` node as the interface that connects your controller plugin to the target robot. .. literalinclude:: Code/robot_launch.py :language: python :dedent: 4 - :lines: 19-27 + :lines: 17-22 After that, the two nodes are set to be launched in the ``LaunchDescription`` constructor: .. literalinclude:: Code/robot_launch.py :language: python :dedent: 4 - :lines: 29-31 + :lines: 24-26 Finally, an optional part is added in order to shutdown all the nodes once Webots terminates (e.g., when it gets closed from the graphical user interface). .. literalinclude:: Code/robot_launch.py :language: python :dedent: 8 - :lines: 32-37 + :lines: 27-32 .. note:: - More details on ``webots_ros2_driver`` and ``WebotsLauncher`` arguments can be found `on the nodes reference page `_. + More details on ``WebotsController`` and ``WebotsLauncher`` arguments can be found `on the nodes reference page `_. 6 Edit additional files ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/source/Tutorials/Advanced/Simulators/Webots/Simulation-Reset-Handler.rst b/source/Tutorials/Advanced/Simulators/Webots/Simulation-Reset-Handler.rst new file mode 100644 index 0000000000..4a20c78117 --- /dev/null +++ b/source/Tutorials/Advanced/Simulators/Webots/Simulation-Reset-Handler.rst @@ -0,0 +1,193 @@ +Setting up a Reset Handler +========================== + +**Goal:** Extend a robot simulation with a reset handler to restart nodes when the reset button of Webots is pressed. + +**Tutorial level:** Advanced + +**Time:** 10 minutes + +.. contents:: Contents + :depth: 2 + :local: + +Background +---------- + +In this tutorial, you will learn how to implement a reset handler in a robot simulation using Webots. +The Webots reset button reverts the world to the initial state and restarts controllers. +It is convenient as it quickly resets the simulation, but in the context of ROS 2, robot controllers are not started again making the simulation stop. +The reset handler allows you to restart specific nodes or perform additional actions when the reset button in Webots is pressed. +This can be useful for scenarios where you need to reset the state of your simulation or restart specific components without completely restarting the complete ROS system. + +Prerequisites +------------- + +Before proceeding with this tutorial, make sure you have completed the following: + +- Understanding of ROS 2 nodes and topics covered in the beginner :doc:`../../../../Tutorials`. +- Knowledge of Webots and ROS 2 and its interface package. +- Familiarity with :doc:`./Setting-Up-Simulation-Webots-Basic`. + + +Reset Handler for Simple Cases (Controllers Only) +------------------------------------------------- + +In the launch file of your package, add the ``respawn`` parameter. + +.. code-block:: python + + def generate_launch_description(): + robot_driver = WebotsController( + robot_name='my_robot', + parameters=[ + {'robot_description': robot_description_path} + ], + + # Every time one resets the simulation the controller is automatically respawned + respawn=True + ) + + # Starts Webots + webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world])) + + return LaunchDescription([ + webots, + robot_driver + ]) + +On reset, Webots kills all driver nodes. +Therefore, to start them again after reset, you should set the ``respawn`` property of the driver node to ``True``. +It will ensure driver nodes are up and running after the reset. + +Reset Handler for Multiple Nodes (No Shutdown Required) +------------------------------------------------------- + +If you have some other nodes that have to be started along with the driver node (e.g. ``ros2_control`` nodes), then you can use the ``OnProcessExit`` event handler: + +.. code-block:: python + + def get_ros2_control_spawners(*args): + # Declare here all nodes that must be restarted at simulation reset + ros_control_node = Node( + package='controller_manager', + executable='spawner', + arguments=['diffdrive_controller'] + ) + return [ + ros_control_node + ] + + def generate_launch_description(): + robot_driver = WebotsController( + robot_name='my_robot', + parameters=[ + {'robot_description': robot_description_path} + ], + + # Every time one resets the simulation the controller is automatically respawned + respawn=True + ) + + # Starts Webots + webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world])) + + # Declare the reset handler that respawns nodes when robot_driver exits + reset_handler = launch.actions.RegisterEventHandler( + event_handler=launch.event_handlers.OnProcessExit( + target_action=robot_driver, + on_exit=get_ros2_control_spawners, + ) + ) + + return LaunchDescription([ + webots, + robot_driver, + reset_handler + ] + get_ros2_control_spawners()) + +It is not possible to use the ``respawn`` property on the ``ros2_control`` node, as the spawner exits during launch time and not when the simulation is reset. +Instead we should declare a list of nodes in a function (e.g. ``get_ros2_control_spawners``). +The nodes of this list are started along other nodes when executing the launch file. +With the ``reset_handler``, the function is also declared as action to start when the ``robot_driver`` node exits, which corresponds to the moment when the simulation is reset in the Webots interface. +The ``robot_driver`` node still has the ``respawn`` property set to ``True``, so that it gets restarted along with ``ros2_control`` nodes. + +Reset Handler Requiring Node Shutdown +------------------------------------- + +With the current ROS 2 launch API, there is no way to make the reset work in launch files where nodes need to be shutdown before the restart (e.g. ``Nav2`` or ``RViz``). +The reason is that currently, ROS 2 doesn't allow to shutdown specific nodes from a launch file. +There is a solution, but it requires to manually restart nodes after pushing the reset button. + +Webots needs to be started in a specific launch file without other nodes. + +.. code-block:: python + + def generate_launch_description(): + # Starts Webots + webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world])) + + return LaunchDescription([ + webots + ]) + + +A second launch file must be started from another process. +This launch file contains all other nodes, including robot controllers/plugins, Navigation2 nodes, RViz, state publishers, etc. + +.. code-block:: python + + def generate_launch_description(): + robot_driver = WebotsController( + robot_name='my_robot', + parameters=[ + {'robot_description': robot_description_path} + ] + ) + + ros_control_node = Node( + package='controller_manager', + executable='spawner', + arguments=['diffdrive_controller'] + ) + + nav2_node = IncludeLaunchDescription( + PythonLaunchDescriptionSource(os.path.join( + get_package_share_directory('nav2_bringup'), 'launch', 'bringup_launch.py')), + launch_arguments=[ + ('map', nav2_map), + ('params_file', nav2_params), + ], + ) + + rviz = Node( + package='rviz2', + executable='rviz2', + output='screen' + ) + + # Declare the handler that shuts all nodes down when robot_driver exits + shutdown_handler = launch.actions.RegisterEventHandler( + event_handler=launch.event_handlers.OnProcessExit( + target_action=robot_driver, + on_exit=[launch.actions.EmitEvent(event=launch.events.Shutdown())], + ) + ) + + return LaunchDescription([ + robot_driver, + ros_control_node, + nav2_node, + rviz, + shutdown_handler + ]) + +The second launch file contains a handler that triggers a shutdown event when the driver node exits (which is the case when the simulation is reset). +This second launch file must be manually restarted from the command line after pressing the reset button. + +Summary +------- + +In this tutorial, you learned how to implement a reset handler in a robot simulation using Webots. +The reset handler allows you to restart specific nodes or perform additional actions when the reset button in Webots is pressed. +You explored different approaches based on the complexity of your simulation and the requirements of your nodes. diff --git a/source/Tutorials/Advanced/Simulators/Webots/Simulation-Webots.rst b/source/Tutorials/Advanced/Simulators/Webots/Simulation-Webots.rst index 108662f238..cff39992de 100644 --- a/source/Tutorials/Advanced/Simulators/Webots/Simulation-Webots.rst +++ b/source/Tutorials/Advanced/Simulators/Webots/Simulation-Webots.rst @@ -15,3 +15,4 @@ This set of tutorials will teach you how to configure the Webots simulator with Installation-MacOS Setting-Up-Simulation-Webots-Basic Setting-Up-Simulation-Webots-Advanced + Simulation-Reset-Handler