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