diff --git a/docs/source/action_clients.rst b/docs/source/action_clients.rst index 07c841a8..c4b65dac 100644 --- a/docs/source/action_clients.rst +++ b/docs/source/action_clients.rst @@ -291,4 +291,4 @@ The output in each window should be something similar to [INFO] [1761753121.199914878] [move_straight_in_2d_action_client]: Received feedback distance: 0.4442135691642761. [INFO] [1761753121.211306087] [move_straight_in_2d_action_client]: Received feedback distance: 0.4342135488986969. [INFO] [1761753121.223468503] [move_straight_in_2d_action_client]: Received feedback distance: 0.42421355843544006. - [INFO] [1761753121.237240878] [move_straight_in_2d_action_client]: Final position was: geometry_msgs.msg.Point(x=0.7071067811865476, y=-0.7071067811865476, z=0.0). \ No newline at end of file + [INFO] [1761753121.237240878] [move_straight_in_2d_action_client]: Final position was: geometry_msgs.msg.Point(x=0.7071067811865476, y=-0.7071067811865476, z=0.0). diff --git a/docs/source/cmake/cmake_packages_without_sudo.rst b/docs/source/cmake/cmake_packages_without_sudo.rst index 756bf678..e3882f29 100644 --- a/docs/source/cmake/cmake_packages_without_sudo.rst +++ b/docs/source/cmake/cmake_packages_without_sudo.rst @@ -3,14 +3,14 @@ Install a CMake package without sudo privileges =============================================== -To install a CMake package or library without sudo privileges, we need to define a directory to which we have access. For instance, +To install a CMake package or library without sudo privileges, we need to define a directory to which we have access. For instance, in :code:`~/`. Create a custom folder ---------------------- -In this tutorial, we are going to create a custom folder :code:`~/opt` containing +In this tutorial, we are going to create a custom folder :code:`~/opt` containing the folders :code:`lib` and :code:`include`. This will be our directory to install all our CMake packages. Run the following commands, @@ -22,7 +22,7 @@ Run the following commands, mkdir -p include mkdir -p lib -Then, we update the LD_LIBRARY_PATH, LIBRARY_PATH, and CPATH in :code:`~/.bashrc`. +Then, we update the LD_LIBRARY_PATH, LIBRARY_PATH, and CPATH in :code:`~/.bashrc`. Do the following just once, so that all terminal windows automatically source this new workspace for you. @@ -38,7 +38,7 @@ Do the following just once, so that all terminal windows automatically source th echo "export CPATH=$CPATH:~/opt/include" >> ~/.bashrc source ~/.bashrc - + Install a CMake package ----------------------- @@ -48,27 +48,27 @@ To install a CMake package, we set the :code:`CMAKE_INSTALL_PREFIX:PATH` flag wi .. code-block:: console - cmake -DCMAKE_INSTALL_PREFIX:PATH=~/opt .. - make + cmake -DCMAKE_INSTALL_PREFIX:PATH=~/opt .. + make make install Example: Installing `qpOASES `_ -------------------------------------------------------------------------------- +------------------------------------------------------------------- This example shows how to build and install the qpOASES to be used in your CMake project. -.. note:: - Check the `official qpOASES documentation `_ for more details. +.. note:: + Check the `official qpOASES documentation `_ for more details. -.. warning:: +.. warning:: This example assumes you have git, CMake, Eigen, and a C++ compiler installed in your GNU/Linux distribution. To install qpOASES as a shared library, we use the instructions provided by the DQ Robotics in \ `cpp-interface-qpoases `_ specifying the -installation directory. +installation directory. .. code-block:: console @@ -79,23 +79,23 @@ installation directory. mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX:PATH=~/opt - make + make make install Example: include and link the qpOASES in your project -------------------------------------------------------- +----------------------------------------------------- .. tab-set:: .. tab-item:: CMakeLists.txt :download:`CMakeLists.txt <../../../cmake_tutorial_workspace/src/cpp_cmake_example_qpoases_lib/CMakeLists.txt>` - + .. literalinclude:: ../../../cmake_tutorial_workspace/src/cpp_cmake_example_qpoases_lib/CMakeLists.txt :language: cmake :linenos: - :emphasize-lines: 17 + :emphasize-lines: 17 .. tab-item:: test_qpoases.cpp @@ -104,18 +104,18 @@ Example: include and link the qpOASES in your project .. literalinclude:: ../../../cmake_tutorial_workspace/src/cpp_cmake_example_qpoases_lib/src/test_qpoases.cpp :language: cpp :linenos: - :emphasize-lines: 2,3 + :emphasize-lines: 2,3 -.. warning:: - If you have the library installed in two directories, you need to ensure you are linking the library you want. +.. warning:: + If you have the library installed in two directories, you need to ensure you are linking the library you want. -For instance, let's say you have the DQ Robotics library installed globally (i.e., :code:`/usr/local/lib/`) and locally (i.e., :code:`~/opt/lib`), +For instance, let's say you have the DQ Robotics library installed globally (i.e., :code:`/usr/local/lib/`) and locally (i.e., :code:`~/opt/lib`), and you want to use the local one. Then, you can use :code:`find_library` with the :code:`NO_DEFAULT_PATH` flag. .. literalinclude:: ../../../cmake_tutorial_workspace/src/cpp_cmake_example_qpoases_lib/examples/CMakeLists.txt :language: cmake :linenos: - :emphasize-lines: 3,4,9 + :emphasize-lines: 3,4,9 diff --git a/docs/source/cpp/cpp_library.rst b/docs/source/cpp/cpp_library.rst index 657e7bd6..58d787a6 100644 --- a/docs/source/cpp/cpp_library.rst +++ b/docs/source/cpp/cpp_library.rst @@ -4,19 +4,19 @@ Creating C++ Libraries (for :program:`ament_cmake`) .. admonition:: The C++ library block for :program:`ament_cmake` .. dropdown:: TL;DR - + When your project exports a library, you might benefit from using the following template. Note that there is, in general, no reason to define multiple libraries. A single shared library can hold all the content that you want to export from a package, hence the library named ``${PROJECT_NAME}``. - + Remember to - + #. Add all exported headers to :file:`include/` otherwise other packages cannot see it. #. Add all source files of the library to ``add_library``. #. Add all ROS2 dependencies of the library to ``ament_target_dependencies``. #. Add **ALL** dependencies for which you used ``find_package`` to ``ament_export_dependencies``, otherwise dependencies might become complex for projects that use your library. #. Add any other (**NOT ROS2**) libraries to ``target_link_libraries``. - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/CMakeLists.txt :language: cmake :lines: 14-63 @@ -37,7 +37,7 @@ resulting in the following output .. code-block:: console :emphasize-lines: 16,17,18 - + ros2 pkg create cpp_package_with_a_library \ --build-type ament_cmake \ --dependencies rclcpp @@ -57,7 +57,7 @@ resulting in the following output creating folder ./cpp_package_with_a_library/src creating folder ./cpp_package_with_a_library/include/cpp_package_with_a_library creating ./cpp_package_with_a_library/CMakeLists.txt - + [WARNING]: Unknown license 'TODO: License declaration'. This has been set in the package.xml, but no LICENSE file has been created. It is recommended to use one of the ament license identitifers: Apache-2.0 @@ -75,10 +75,10 @@ Package-related sources ----------------------- .. admonition:: In this step, we'll work on these. - + .. code-block:: console :emphasize-lines: 2,6 - + cpp_package_with_a_library ├── CMakeLists.txt ├── include @@ -103,7 +103,7 @@ The files already exist, we just need to modify them as follows .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/package.xml :language: xml - :linenos: + :linenos: .. tab-item:: CMakeLists.txt @@ -112,7 +112,7 @@ The files already exist, we just need to modify them as follows Note that, because the local Node depends on the library being exported by this project, it needs to explicitly link to it. :download:`CMakeLists.txt <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/CMakeLists.txt>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/CMakeLists.txt :language: cmake :linenos: @@ -123,10 +123,10 @@ Library sources --------------- .. admonition:: In this step, we'll work on these. - + .. code-block:: console :emphasize-lines: 5,8 - + cpp_package_with_a_library ├── CMakeLists.txt ├── include @@ -141,12 +141,12 @@ Library sources .. tab-set:: - .. tab-item:: sample_class.hpp + .. tab-item:: sample_class.hpp A class that does a bunch of nothing, but that depends on Eigen3 and Qt, as an example. :download:`sample_class.hpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/include/cpp_package_with_a_library/sample_class.hpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/include/cpp_package_with_a_library/sample_class.hpp :language: cpp :linenos: @@ -155,7 +155,7 @@ Library sources .. tab-item:: sample_class.cpp :download:`sample_class.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class.cpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class.cpp :language: cpp :linenos: @@ -166,10 +166,10 @@ Sources for a local node that uses the library ---------------------------------------------- .. admonition:: In this step, we'll work on these. - + .. code-block:: console :emphasize-lines: 9-11 - + cpp_package_with_a_library ├── CMakeLists.txt ├── include @@ -189,7 +189,7 @@ Just in case you need to have a node, in the same package, that also uses the li .. tab-item:: sample_class_local_node.cpp :download:`sample_class.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node.cpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node.cpp :language: cpp :linenos: @@ -198,7 +198,7 @@ Just in case you need to have a node, in the same package, that also uses the li .. tab-item:: sample_class_local_node.hpp :download:`sample_class_local_node.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node.hpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node.hpp :language: cpp :linenos: @@ -207,7 +207,7 @@ Just in case you need to have a node, in the same package, that also uses the li .. tab-item:: sample_class_local_node_main.cpp :download:`sample_class.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node_main.cpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_library/src/sample_class_local_node_main.cpp :language: cpp :linenos: diff --git a/docs/source/cpp/cpp_node.rst b/docs/source/cpp/cpp_node.rst index 3cace06d..a6ca1f82 100644 --- a/docs/source/cpp/cpp_node.rst +++ b/docs/source/cpp/cpp_node.rst @@ -6,25 +6,25 @@ Creating C++ Nodes (for :program:`ament_cmake`) .. dropdown:: TL;DR When adding a new Node in an existing :file:`CMakeLists.txt`, you might benefit from using the following template. - + Remember to: - + #. Add **ALL** dependencies (including ROS2 ones) with ``find_package``, if applicable. - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/CMakeLists.txt :language: cmake :lines: 8-10 - + #. Change ``print_forever_node`` to the name of your Node. #. Add all source files to ``add_executable``. #. Add all ROS2 dependencies of this binary to ``ament_target_dependencies``. #. Add any other (**NOT ROS2**) libraries to ``target_link_libraries``. - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/CMakeLists.txt :language: cmake :lines: 12-47 :emphasize-lines: 7,12,17,21 - + Create the package ------------------ @@ -46,7 +46,7 @@ which outputs .. code-block:: console :emphasize-lines: 13-16 - + going to create a new package package name: cpp_package_with_a_node destination directory: /home/murilo/ROS2_Tutorial/ros2_tutorial_workspace/src @@ -63,7 +63,7 @@ which outputs creating folder ./cpp_package_with_a_node/src creating folder ./cpp_package_with_a_node/include/cpp_package_with_a_node creating ./cpp_package_with_a_node/CMakeLists.txt - + [WARNING]: Unknown license 'TODO: License declaration'. This has been set in the package.xml, but no LICENSE file has been created. It is recommended to use one of the ament license identitifers: Apache-2.0 @@ -80,10 +80,10 @@ Package-related sources ----------------------- .. admonition:: In this step, we'll work on these. - + .. code-block:: console :emphasize-lines: 2,6 - + cpp_package_with_a_node ├── CMakeLists.txt ├── include @@ -115,7 +115,7 @@ The files already exist, we just need to modify them as follows A *one-size-fits-most* solution is shown below. For each new Node we add a block to the :file:`CMakeLists.txt` with the following format. :download:`CMakeLists.txt <../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/CMakeLists.txt>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/CMakeLists.txt :language: cmake :linenos: @@ -124,7 +124,7 @@ The files already exist, we just need to modify them as follows Making C++ ROS2 Nodes --------------------- -.. admonition:: (Murilo's) ``rclcpp`` best practices +.. admonition:: (Murilo's) ``rclcpp`` best practices For each new C++ Node, we make three files following the style below. @@ -138,7 +138,7 @@ Making C++ ROS2 Nodes .. code-block:: console :emphasize-lines: 7-10 - + cpp_package_with_a_node ├── CMakeLists.txt ├── include @@ -166,7 +166,7 @@ These files do not exist, so we'll create them. .. tab-item:: src/..._node.hpp Similar to what we did in Python, we inherit from ``rclcpp::Node``. Whatever is different is owing to differences in languages. - + :download:`print_forever_node.hpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/src/print_forever_node.hpp>` .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/src/print_forever_node.hpp @@ -177,7 +177,7 @@ These files do not exist, so we'll create them. .. tab-item:: src/..._node.cpp - The implementation has nothing special, just don't forget to initialize the parent class, ``rclcpp::Node``, with the name of the node. + The implementation has nothing special, just don't forget to initialize the parent class, ``rclcpp::Node``, with the name of the node. :download:`print_forever_node.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/src/print_forever_node.cpp>` @@ -187,13 +187,13 @@ These files do not exist, so we'll create them. :lines: 24- :emphasize-lines: 7 - .. tab-item:: src/..._main.cpp + .. tab-item:: src/..._main.cpp Given that we are using ``rclcpp::spin()``, there is nothing special here either. Just remember to not mess up the ``std::make_shared`` and always use perfect forwarding. The ``rclcpp::spin()`` handles the ``SIGINT`` when we, for example, press :kbd:`CTRL+C` on the terminal. It is not perfect, but it does the trick for simple nodes like this one. - + :download:`print_forever_node_main.cpp <../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/src/print_forever_node_main.cpp>` - + .. literalinclude:: ../../../ros2_tutorial_workspace/src/cpp_package_with_a_node/src/print_forever_node_main.cpp :language: cpp :linenos: diff --git a/docs/source/cpp/cpp_vent.rst b/docs/source/cpp/cpp_vent.rst index 56f5a942..3fc83b0a 100644 --- a/docs/source/cpp/cpp_vent.rst +++ b/docs/source/cpp/cpp_vent.rst @@ -5,7 +5,7 @@ Anything below this point is just me venting about topics that frequently come up when C++ is mentioned. But, C++ is difficult -^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++ I think C++ organically follows `Bushnell's Law `_, adjusted for the topic @@ -14,7 +14,7 @@ I think C++ organically follows `Bushnell's Law `_ and `PyTorch `_. @@ -30,18 +30,18 @@ The memefied version of this discussion is :align: center Why use C++ if it sucks?? -^^^^^^^^^^^^^^^^^^^^^^^^^ - -There's much folklore around C++. "C is faster than C++." "C++ is unsafe" (I'm looking at you, Rust). ++++++++++++++++++++++++++ + +There's much folklore around C++. "C is faster than C++." "C++ is unsafe" (I'm looking at you, Rust). Anyhow, we'd all benefit if people stopped spreading weird fallacies about the C++ language when the problems they have can usually be attributed instead to a `skill issue `_. Some quick info from `Stroustrup's FAQ `_, also known as the person who designed and implemented the C++ programming language. ```` - + What is the difference between C and C++? C++ is a direct descendant of C that retains almost all of C as a subset. C++ provides stronger type checking than C and directly supports a wider range of programming styles than C. C++ is "a better C" in the sense that it supports the styles of programming done using C with better type checking and more notational support (without loss of efficiency). In the same sense, ANSI C is a better C than K&R C. In addition, C++ supports data abstraction, object-oriented programming, and generic programming (see my books). I have never seen a program that could be expressed better in C than in C++ (and I don't think such a program could exist - every construct in C has an obvious C++ equivalent). [...] - + C++ is low-level? No. C++ offers both low-level and high-level features. C++ has low-level parts, such as pointers, arrays, and casts. These facilities are (almost identical to what C offers) are essential (in some form or other) for close-to-the-hardware work. So, if you want low-level language facilities, yes C++ provides a well-tried set of facilities for you. However, when you don't want to use low-level features, you don't need to use the C++ facilities (directly). Instead, you can rely on higher-level facilities, including libraries. For example, if you don't want to use arrays and pointers, standard library strings and containers are (better) alternatives in many cases. If you use only low-level facilities, you are almost certainly wasting time and complicating maintenance without performance advantages (see Learning Standard C++ as a New Language). You may also be laying your systems open to attacks (e.g. buffer overflows). @@ -49,12 +49,12 @@ C++ too slow for low-level work? No. If you can afford to use C, you can afford to use C++, even the higher-level facilities of C++ where you need their functionality. See Abstraction and the C++ machine model and the ISO C++ standards committee's Technical Report on Performance. C++ is useful only if you write truly object-oriented code? - No. That is, "no" for just about any reasonable definition of "object-oriented". C++ provides support for a wide variety of needs, not just for one style or for one kind of application. In fact, compared to C, C++ provides more support for very simple programming tasks. For example, the standard library and other libraries radically simplifies many otherwise tedious and error-prone tasks. C++ is widely used for huge applications but it also provides benefits for even tiny programming tasks. + No. That is, "no" for just about any reasonable definition of "object-oriented". C++ provides support for a wide variety of needs, not just for one style or for one kind of application. In fact, compared to C, C++ provides more support for very simple programming tasks. For example, the standard library and other libraries radically simplifies many otherwise tedious and error-prone tasks. C++ is widely used for huge applications but it also provides benefits for even tiny programming tasks. ```` - + But I hate pointers, and pointers hate me: The ballad of ``segmentation fault (core dumped)`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ In things entirely written in modern C++ (loosely C++11 and above, but C++14 and above for what I want to say here), you shouldn't see any ``new`` or any loose raw pointer modifiers ``*``. @@ -63,7 +63,7 @@ Use `smart pointers `_. In general, `s If only using smart pointers, you still manage to get a segmentation fault, then hats off to you. But I can get segfaults with ``std::vector`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++++++++++++++++++++++ As a successor of C, the standard library in C++ kept some of its predecessor's behavior of not generating exceptions. @@ -102,14 +102,14 @@ Instead of doing that, use the method ``.at()``, which `checks the bounds `_" -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ I see this claim all the time, and it has many `skill-issue `_\ -related causes, but basically, it shows up more frequently in the constructors of ``std::vector`` and ``std::shared_ptr``. @@ -123,9 +123,9 @@ Let's suppose that we have a class public: Potato(const double& size): size_(size) - {}; + {}; }; - + for which we want to get a ``std::shared_ptr``. Do not do this .. code-block:: cpp @@ -143,6 +143,6 @@ because that will create one instance of ``Potato(20.0)``, just to copy it when auto potato_ptr = std::make_shared(20.0); -by forwarding the argument to the constructor instead of calling it explicitly. - +by forwarding the argument to the constructor instead of calling it explicitly. + For everything else that you don't want to copy, use ``std::move()``, but you don't see it that much unless you're designing a library. diff --git a/docs/source/create_interface_package.rst b/docs/source/create_interface_package.rst index 03b9cbfe..3b9eb22c 100644 --- a/docs/source/create_interface_package.rst +++ b/docs/source/create_interface_package.rst @@ -90,7 +90,7 @@ The :file:`package.xml` dependencies Whenever the package has any type of interface, the :file:`package.xml` **must** include three specific dependencies. Namely, the ones highlighted below. Edit the :file:`package_with_interfaces/package.xml` like so -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/package.xml <../../ros2_tutorial_workspace/src/package_with_interfaces/package.xml>` +:download:`package.xml <../../ros2_tutorial_workspace/src/package_with_interfaces/package.xml>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/package.xml :language: xml @@ -100,7 +100,7 @@ Edit the :file:`package_with_interfaces/package.xml` like so The message folder ------------------ -The convention is to add all messages to a folder called :file:`msg`. Let's follow that convention +The convention is to add all messages to a folder called :file:`msg`. Let's follow that convention .. code:: console @@ -125,7 +125,7 @@ Let us create a message file to transfer inspirational quotes between Nodes. For There are many ways to represent this, but for the sake of the example let us give each message an :code:`id` and two rather obvious fields. Create a file called :file:`AmazingQuote.msg` in the folder :file:`msg` that we just created with the following contents. -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuote.msg <../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuote.msg>` +:download:`AmazingQuote.msg <../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuote.msg>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuote.msg :language: yaml @@ -141,7 +141,7 @@ Re-using a message from the same package With the :file:`AmazingQuote.msg`, we have seen how to use built-in types. Let's use another message, :file:`AmazingQuoteStamped.msg`, to learn two more possibilities, namely using messages from the same package and messages defined elsewhere. -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuoteStamped.msg <../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuoteStamped.msg>` +:download:`AmazingQuoteStamped.msg <../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuoteStamped.msg>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/msg/AmazingQuoteStamped.msg :language: yaml @@ -155,7 +155,7 @@ In many :program:`ROS2` packages, messages with the suffix ``Stamped`` exist. As The service folder ------------------ -The convention is to add all services to a folder called :file:`srv`. Let's follow that convention +The convention is to add all services to a folder called :file:`srv`. Let's follow that convention .. code:: console @@ -169,7 +169,7 @@ The service file Add the file :file:`AddPoints.srv` in the :file:`srv` folder with the following contents -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/srv/AddPoints.srv <../../ros2_tutorial_workspace/src/package_with_interfaces/srv/AddPoints.srv>` +:download:`AddPoints.srv <../../ros2_tutorial_workspace/src/package_with_interfaces/srv/AddPoints.srv>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/srv/AddPoints.srv :language: yaml @@ -192,7 +192,7 @@ The action file Add the file :file:`MoveStraightIn2D.action` in the :file:`action` folder with the following contents -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/action/MoveStraightIn2D.action <../../ros2_tutorial_workspace/src/package_with_interfaces/action/MoveStraightIn2D.action>` +:download:`MoveStraightIn2D.action <../../ros2_tutorial_workspace/src/package_with_interfaces/action/MoveStraightIn2D.action>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/action/MoveStraightIn2D.action :language: yaml @@ -202,14 +202,14 @@ Add the file :file:`MoveStraightIn2D.action` in the :file:`action` folder with t The :file:`CMakeLists.txt` directives ------------------------------------- -.. note:: +.. note:: The order of the :program:`CMake` directives is very important and getting the order wrong can result in bugs with cryptic error messages. If a package is dedicated to interfaces, there is no need to worry too much about the :program:`CMake` details. We can follow the boilerplate as shown below. Edit the :file:`package_with_interfaces/CMakeLists.txt` like so -:download:`~/ros2_tutorial_workspace/src/package_with_interfaces/CMakeLists.txt <../../ros2_tutorial_workspace/src/package_with_interfaces/CMakeLists.txt>` +:download:`CMakeLists.txt <../../ros2_tutorial_workspace/src/package_with_interfaces/CMakeLists.txt>` .. literalinclude:: ../../ros2_tutorial_workspace/src/package_with_interfaces/CMakeLists.txt :language: cmake @@ -259,7 +259,7 @@ For instance, running .. code:: console ros2 interface package package_with_interfaces - + returns .. code:: console @@ -268,7 +268,7 @@ returns package_with_interfaces/msg/AmazingQuoteStamped package_with_interfaces/action/MoveStraightIn2D package_with_interfaces/srv/AddPoints - + and we can further get more specific info on :file:`AmazingQuote` (or :file:`AmazingQuoteStamped`) .. code:: console @@ -285,7 +285,7 @@ alternatively, we can do the same for :file:`AddPoints` .. code:: console ros2 interface show package_with_interfaces/srv/AddPoints - + which returns expanded information on each field of the service .. code:: yaml @@ -331,4 +331,4 @@ which returns expanded information about all fields of the action float64 y float64 z --- - float32 error_norm \ No newline at end of file + float32 error_norm diff --git a/docs/source/create_packages.rst b/docs/source/create_packages.rst index 9fbfd3a3..ad834ef3 100644 --- a/docs/source/create_packages.rst +++ b/docs/source/create_packages.rst @@ -7,7 +7,7 @@ Everything else that :program:`ROS2` can offer rotates about the effective use o ROS2 has a tool to help create package templates. We can get all available options by running .. code-block:: console - + ros2 pkg create -h which outputs a list of handy options to populate the package template with useful files. Namely, the four emphasized ones. @@ -48,4 +48,4 @@ which outputs a list of handy options to populate the package template with usef Although it is recommended to use some of the other options as well, such as :code:`--license`, :code:`--maintainer-email`, and :code:`--maintainer-name`, they would make the commands too long for the purposes of this tutorial. Remember to choose -the most suitable values for your application. \ No newline at end of file +the most suitable values for your application. diff --git a/docs/source/create_python_library.rst b/docs/source/create_python_library.rst index 886b6945..6b207856 100644 --- a/docs/source/create_python_library.rst +++ b/docs/source/create_python_library.rst @@ -3,13 +3,13 @@ Creating a Python Library (for :program:`ament_python`) Let us start, as already recommended in this tutorial, with a template by :program:`ros2 pkg create`. -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace/src ros2 pkg create python_package_with_a_library \ --build-type ament_python \ --library-name sample_python_library - + which outputs the forever beautiful wall of text we're now used to, with a minor difference regarding the additional library template, as highlighted below. .. code-block:: console @@ -62,7 +62,7 @@ The first is the name of the :program:`ROS2` package, and the second is the name .. code-block:: console :emphasize-lines: 1,3 - + python_package_with_a_library/ |-- package.xml |-- python_package_with_a_library @@ -77,7 +77,7 @@ The first is the name of the :program:`ROS2` package, and the second is the name |-- test_copyright.py |-- test_flake8.py `-- test_pep257.py - + We learned the meaning of most of those in the preamble, namely :ref:`Python Best Practices`. To quickly clarify a few things, see the table below. .. list-table:: ROS2 Python package folders/files explained @@ -104,13 +104,13 @@ Overview of the library ----------------------- .. hint:: - If you have created the bad habit of declaring all/too many things in your :file:`__init__.py` file, take the hint and start breaking the definitions into different files and use the :file:`__init__.py` just to export the relevant parts of your library. + If you have created the bad habit of declaring all/too many things in your :file:`__init__.py` file, take the hint and start breaking the definitions into different files and use the :file:`__init__.py` just to export the relevant parts of your library. For the sake of the example, let us create a library with a Python :code:`function` and another one with a :code:`class`. To guide our next steps, we first draw a quick overview of what our :code:`python_package_with_a_library` will look like. .. code-block:: console :emphasize-lines: 6-8 - + python_package_with_a_library/ |-- package.xml |-- python_package_with_a_library @@ -127,21 +127,21 @@ For the sake of the example, let us create a library with a Python :code:`functi |-- test_copyright.py |-- test_flake8.py `-- test_pep257.py - + With respect to the highlighted files, we will #. Create the :code:`_sample_function.py`. #. Create the :code:`_sample_class.py`. -#. Modify :file:`__init__.py` to use the new function and class. - -All other files and directories will remain as-is, in the way they were generated by :program:`ros2 pkg create`. - +#. Modify :file:`__init__.py` to use the new function and class. + +All other files and directories will remain as-is, in the way they were generated by :program:`ros2 pkg create`. + Create the sample function -------------------------- Create a new file with the following contents and name. -:download:`~/ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_function.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_function.py>` +:download:`_sample_function.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_function.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_function.py :language: python @@ -153,21 +153,21 @@ The function has two parameters, :code:`a` and :code:`b`. For simplicity, we're Create the sample class ----------------------- -Create a new file with the following contents and name. +Create a new file with the following contents and name. -:download:`~/ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_class.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_class.py>` +:download:`_sample_class.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_class.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/_sample_class.py :language: python :linenos: :lines: 26- - + The class is quite simple with a `private data member `_ and a method to retrieve it. Modify the :file:`__init__.py` ------------------------------ -:download:`~/ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/__init__.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/__init__.py>` +:download:`__init__.py <../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/__init__.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_library/python_package_with_a_library/sample_python_library/__init__.py :language: python diff --git a/docs/source/create_python_node_from_scratch.rst b/docs/source/create_python_node_from_scratch.rst index 06594ae0..19c08b52 100644 --- a/docs/source/create_python_node_from_scratch.rst +++ b/docs/source/create_python_node_from_scratch.rst @@ -2,17 +2,17 @@ Creating a Python Node from scratch (for :program:`ament_python`) ================================================================= .. seealso:: - + The official API documentation: https://docs.ros.org/en/jazzy/p/rclpy/rclpy.html .. admonition:: **TL;DR** Making an :program:`ament_python` Node - + #. Modify :file:`package.xml` with any additional dependencies. #. Create the Node. #. Modify the :file:`setup.py` file. -Let us add an additional Node to our :program:`ament_python` package that actually uses ROS2 functionality. +Let us add an additional Node to our :program:`ament_python` package that actually uses ROS2 functionality. These are the steps that must be taken, in general, to add a new Node. File structure @@ -59,7 +59,7 @@ Let us add the dependency between the :code:`` and :code:` :language: xml :linenos: :emphasize-lines: 10 - + Creating the Node ----------------- @@ -71,7 +71,7 @@ In the directory :file:`src/python_package_with_a_node/python_package_with_a_nod :language: python :linenos: :lines: 24- - + .. _Making rosrun work: Making :command:`ros2 run` work @@ -81,11 +81,11 @@ We need an additional step to make it deployable in a place where :command:`ros2 To do so, we modify the :code:`console_scripts` key in the :code:`entry_points` dictionary defined in :file:`setup.py`, to have our new node, as follows -.. hint:: +.. hint:: :code:`console_scripts` expects a :code:`list` of :code:`str` in a specific format. Hence, follow the format properly and don't forget the commas to separate elements in the :code:`list`. -:download:`~/ros2_tutorial_workspace/src/python_package_with_a_node/setup.py <../../ros2_tutorial_workspace/src/python_package_with_a_node/setup.py>` +:download:`setup.py <../../ros2_tutorial_workspace/src/python_package_with_a_node/setup.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_node/setup.py :language: python @@ -116,11 +116,11 @@ And, with that, we can run .. code-block:: console ros2 run python_package_with_a_node print_forever_node - + which will output, as expected - + .. code-block:: console - + [INFO] [1753518652.646459087] [print_forever]: Printed 0 times. [INFO] [1753518653.131078795] [print_forever]: Printed 1 times. [INFO] [1753518653.632436004] [print_forever]: Printed 2 times. diff --git a/docs/source/create_python_node_with_template.rst b/docs/source/create_python_node_with_template.rst index ac927920..87c2ab1f 100644 --- a/docs/source/create_python_node_with_template.rst +++ b/docs/source/create_python_node_with_template.rst @@ -1,18 +1,18 @@ Creating a Python Node with a template (for :program:`ament_python`) ==================================================================== -It is always good to rely on the templates available in :program:`ros2 pkg create`, mostly because the best practices for packaging might change between ROS2 versions. +It is always good to rely on the templates available in :program:`ros2 pkg create`, mostly because the best practices for packaging might change between ROS2 versions. Let us use the template for creating a package with a Node, as follows. -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace/src ros2 pkg create python_package_with_a_node \ --build-type ament_python \ --node-name sample_python_node - -Which will output many things in common with the prior example, but with two major differences. + +Which will output many things in common with the prior example, but with two major differences. #. It generates a template Node #. The :file:`setup.py` has information about the Node. @@ -60,14 +60,14 @@ Which will output many things in common with the prior example, but with two maj Then, we can build the workspace as usual to consider the new package as well. -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace colcon build - -which will result in going through the package we created in the prior example and the current one. -.. code :: console +which will result in going through the package we created in the prior example and the current one. + +.. code-block:: console Starting >>> python_package_with_a_node Starting >>> the_simplest_python_package diff --git a/docs/source/create_python_package.rst b/docs/source/create_python_package.rst index 3064621b..18fe9540 100644 --- a/docs/source/create_python_package.rst +++ b/docs/source/create_python_package.rst @@ -4,7 +4,7 @@ Creating a Python package (for :program:`ament_python`) .. note:: This is **NOT** the only way to build Python packages in ROS2. -Packages in ROS2 can either rely on :program:`CMake` or directly use setup tools available in Python. +Packages in ROS2 can either rely on :program:`CMake` or directly use setup tools available in Python. For pure Python projects, it might be easier to use :program:`ament_python`, so we start this tutorial with it. Let us build the simplest of Python packages and start from there. @@ -24,9 +24,9 @@ Let us build the simplest of Python packages and start from there. which will result in the output below, meaning the package has been generated successfully. -.. code-block:: console +.. code-block:: console :emphasize-lines: 7 - + going to create a new package package name: the_simplest_python_package destination directory: /root/ros2_tutorial_workspace/src @@ -65,14 +65,14 @@ which will result in the output below, meaning the package has been generated su We can build the workspace that now has this empty package using :program:`colcon` -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace colcon build - + which will now output -.. code :: console +.. code-block:: console Starting >>> the_simplest_python_package Finished <<< the_simplest_python_package [0.49s] diff --git a/docs/source/cybersecurity/cryptography.rst b/docs/source/cybersecurity/cryptography.rst index 51149585..72248369 100644 --- a/docs/source/cybersecurity/cryptography.rst +++ b/docs/source/cybersecurity/cryptography.rst @@ -289,7 +289,7 @@ Exercises We can think of decryption and encryption exercises that help illustrate the process. Decryption -~~~~~~~~~~ ++~~~~~~~~~ Suppose that you receive the following message, which has been encrypted with your public key. It was clearly done so using :program:`age`. @@ -315,7 +315,7 @@ Anyone with this key can decode the message. If anyone can decode the message, t What are the decrypted contents of this message? Encryption -~~~~~~~~~~ ++~~~~~~~~~ Suppose that you have the following public key. diff --git a/docs/source/cybersecurity/index.rst b/docs/source/cybersecurity/index.rst index 829434ee..f8dce1e7 100644 --- a/docs/source/cybersecurity/index.rst +++ b/docs/source/cybersecurity/index.rst @@ -61,4 +61,4 @@ controls, and backup, which is a type of corrective control. .. admonition:: References - .. footbibliography:: \ No newline at end of file + .. footbibliography:: diff --git a/docs/source/docker/index.rst b/docs/source/docker/index.rst index 84be7024..57d5aed2 100644 --- a/docs/source/docker/index.rst +++ b/docs/source/docker/index.rst @@ -80,14 +80,14 @@ If executed for the first time in the machine, it should return something simila Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world - 58dee6a49ef1: Pull complete - c3bdf82c34d1: Download complete + 58dee6a49ef1: Pull complete + c3bdf82c34d1: Download complete Digest: sha256:0e760fdfbc48ba8041e7c6db999bb40bfca508b4be580ac75d32c4e29d202ce1 Status: Downloaded newer image for hello-world:latest - + Hello from Docker! This message shows that your installation appears to be working correctly. - + To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. @@ -96,13 +96,13 @@ If executed for the first time in the machine, it should return something simila executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. - + To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash - + Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ - + For more examples and ideas, visit: https://docs.docker.com/get-started/ @@ -171,7 +171,7 @@ If your host does not have :program:`ROS2` you can also have multiple containers other without any direct involvement of the host. For instance with the following compose file named :file:`compose.yml` below. -:download:`simple_example/compose.yml ` +:download:`compose.yml ` .. literalinclude:: scripts/compose/simple_example/compose.yml :language: yaml @@ -286,13 +286,13 @@ The :file:`compose.yml` For real-time performance, additional capabilities must be given to the container. -:download:`realtime_example/compose.yml ` +:download:`compose.yml ` .. literalinclude:: scripts/compose/realtime_example/compose.yml :language: yaml For this example, the relevant parameters are ``cap_add``, ``rtprio``, and ``rttime``. The first one is to add the capability of setting process `niceness `_. Then, -the other two are related to the realtime priorities. +the other two are related to the realtime priorities. The compose file does not make anything realtime. For a realtime thread you will have to set up the thread scheduling properly to ``SCHED_FIFO`` or ``SCHED_RR`` We can run one example doing so in ``sas_core`` is shown below. diff --git a/docs/source/faq.rst b/docs/source/faq.rst index dabda13c..3ac750a7 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -3,12 +3,12 @@ Frequently asked questions (FAQ) .. note:: Also known as, frequently made comments, things I'd like to mention, etc. - + You got the name wrong, it's **ROS 2** not **ROS2** --------------------------------------------------- -Besides the humorous nature of the `meme `_ below and my love for the 1993's blockbuster, this is an inconspicuous way of showing, in every single section, that these tutorials are not official. +Besides the humorous nature of the `meme `_ below and my love for the 1993's blockbuster, this is an inconspicuous way of showing, in every single section, that these tutorials are not official. .. image:: ../images/ros2_or_ros_2.jpg :align: center @@ -18,10 +18,10 @@ It's not Linux, it's GNU/Linux: Keep all grievances in :code:`#vent` The wording on these tutorials is precise as possible. Note that some terms are commonly used with loose meanings, but I hope that the message is still conveyed. This applies to the whole tutorial, given that even official sources are not uniform in their terminology. -So, to end any deep discussions that might distract you from the point of these tutorials before they even start, I'll let you with the world-renowned Linux copypasta edited with `what was actually said `_ +So, to end any deep discussions that might distract you from the point of these tutorials before they even start, I'll let you with the world-renowned Linux copypasta edited with `what was actually said `_ *I'd just like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system [...]. Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called “Linux,” and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use.* - + *Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called “Linux” distributions are really distributions of GNU/Linux.* The difference between Python *scripts* and *modules* @@ -57,4 +57,4 @@ come up with namings such as submodule *Packages are a way of structuring Python’s module namespace [...]. For example, the module name A.B designates a submodule named B in a package named A.* -What most people want to say when they mention a package is, usually, either a folder with a :file:`__init__.py` or a folder with a :code:`setup.py` that can be built into a :code:`wheel` or something similar. +What most people want to say when they mention a package is, usually, either a folder with a :file:`__init__.py` or a folder with a :code:`setup.py` that can be built into a :code:`wheel` or something similar. diff --git a/docs/source/gazebo/index.rst b/docs/source/gazebo/index.rst index c3adaf0f..de0a42bc 100644 --- a/docs/source/gazebo/index.rst +++ b/docs/source/gazebo/index.rst @@ -33,4 +33,4 @@ once for :program:`ROS2` and another for :program:`Gazebo Classic`. Although this switch might still be ongoing and some more advanced functionalities might be missing, we will work with a single ``.sdf`` whenever possible. -If some of these terms are not clear to you now, worry not. They will become clearer as we progress in the tutorials. \ No newline at end of file +If some of these terms are not clear to you now, worry not. They will become clearer as we progress in the tutorials. diff --git a/docs/source/gazebo/other_content.rst b/docs/source/gazebo/other_content.rst index c8cdd61f..1622a156 100644 --- a/docs/source/gazebo/other_content.rst +++ b/docs/source/gazebo/other_content.rst @@ -123,7 +123,7 @@ parameter_bridge \ /world/shapes/set_pose/blocking@geometry_msgs/msg/PoseStamped]gz.msgs.Pose Getting entity pose information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++++++ https://github.com/gazebosim/gz-sim/blob/gz-sim8/examples/worlds/pose_publisher.sdf diff --git a/docs/source/gazebo/usage.rst b/docs/source/gazebo/usage.rst index 9c67bfa3..ec082086 100644 --- a/docs/source/gazebo/usage.rst +++ b/docs/source/gazebo/usage.rst @@ -340,7 +340,7 @@ will be as follows. [INFO] [create-1]: process has finished cleanly [pid 14161] The package :file:`ros_gz_sim_demos` ------------------------------------ +------------------------------------ .. seealso:: diff --git a/docs/source/index.rst b/docs/source/index.rst index 3c8b059f..7392d61c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -19,7 +19,7 @@ This tutorial is licensed under `Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International `_. -The example code have their own License headers, usually `MIT Licensed `_. +The example code has their own License headers, usually `MIT Licensed `_. .. attention:: AI training with this content must follow the `license restrictions `_. In particular, please refer to the excerpt below. @@ -30,17 +30,17 @@ The example code have their own License headers, usually `MIT Licensed `_ tutorials by `Murilo M. Marinho `_, focusing on Ubuntu 24.04 x64 LTS and the programming practices of successful state-of-the-art robotics implementations such as the `SmartArmStack `_ also used in the `AISciencePlatform `_. These tutorials have been the backbone of `EEEN62021 Software for Robotics `_, one of the units of the `MSc Robotics `_ at the `University of Manchester `_. -🤟 **Using this tutorial** +🤟 **Using this tutorial** This is a tutorial that supposes that the user will follow it linearly. Some readers can skip the :doc:`Preamble ` if they are somewhat already comfortable in Python and Ubuntu. Otherwise, all steps can be considered as dependent on the prior ones, starting from :doc:`ROS2 Setup `. -It is expected that the user will be working directly on an Ubuntu machine. :program:`Docker` images are available but no compatibility with host systems other than Ubuntu have been attempted. +It is expected that the user will be working directly on an Ubuntu machine. :program:`Docker` images are available but no compatibility with host systems other than Ubuntu has been attempted. ❤️ **Ways to show love** @@ -67,7 +67,7 @@ If you enjoyed this tutorial, please Creating ``rclpy`` Nodes and figuring out what all that means. #. :doc:`ROS2 Python Library ` - Create Python librarys and importing/using it in another :program:`ament_python` package. + Create Python libraries and importing/using them in another :program:`ament_python` package. #. :doc:`ROS2 Interfaces ` ROS2 interfaces, i.e., messages ``.msg``, services ``.srv``, and actions ``.action``. Creating custom interface packages with :program:`ament_cmake`. @@ -106,7 +106,7 @@ If you enjoyed this tutorial, please preamble/python/python_best_practices preamble/python/python_asyncio preamble/python/python_packaging - + .. toctree:: :caption: ROS2 Setup (⭐start here⭐) :maxdepth: 2 @@ -131,7 +131,7 @@ If you enjoyed this tutorial, please :caption: ROS2 Python Node :maxdepth: 2 :hidden: - + create_python_node_from_scratch python_node_explained @@ -139,7 +139,7 @@ If you enjoyed this tutorial, please :caption: ROS2 Python Library :maxdepth: 2 :hidden: - + create_python_library using_python_library @@ -155,7 +155,7 @@ If you enjoyed this tutorial, please :caption: ROS2 Messages in Python :maxdepth: 2 :hidden: - + publishers_and_subscribers inspecting_topics @@ -229,7 +229,7 @@ If you enjoyed this tutorial, please :caption: ROS2 C++ Basics :maxdepth: 2 :hidden: - + cpp/cpp_node cpp/cpp_library cpp/cpp_vent @@ -238,7 +238,7 @@ If you enjoyed this tutorial, please :caption: SAS Basics :maxdepth: 2 :hidden: - + sas/index sas/installation sas/sas_robot_driver_add_new_robot @@ -268,7 +268,7 @@ By reading and/or using this tutorial in total or in part, you agree to these te .. admonition:: Disclaimer THIS TUTORIAL AND RELATED SOFTWARE ARE PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND/OR TUTORIAL, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + Changelog --------- diff --git a/docs/source/inspecting_parameters.rst b/docs/source/inspecting_parameters.rst index 0bfde24d..e3078e03 100644 --- a/docs/source/inspecting_parameters.rst +++ b/docs/source/inspecting_parameters.rst @@ -15,12 +15,12 @@ which returns :emphasize-lines: 11-15 usage: ros2 param [-h] Call `ros2 param -h` for more detailed usage. ... - + Various param related sub-commands - + options: -h, --help show this help message and exit - + Commands: delete Delete parameter describe Show descriptive information about declared parameters @@ -29,13 +29,13 @@ which returns list Output a list of available parameters load Load parameter file for a node set Set parameter - + Call `ros2 param -h` for more detailed usage. As shown in the emphasized lines above, the :program:`ros2 param` tool has a large number of useful commands to interact with parameters. Launching the Node with parameters ---------------------------------- +---------------------------------- .. hint:: If you left the Node running from the last section, just keep it that way and skip this. @@ -52,7 +52,7 @@ List-up parameters with :program:`ros2 param list` .. hint:: Remember that :ref:`Grep best friend`. -Similar to other ROS2 commands, we can get a list of currently loaded parameters with +Similar to other ROS2 commands, we can get a list of currently loaded parameters with .. code-block:: console @@ -70,7 +70,7 @@ which returns a well organized list showing the parameters of each active Node use_sim_time Obtain parameters with :program:`ros2 param get` ------------------------------------------------ +------------------------------------------------ To obtain the value of a parameter, we can do as follows @@ -109,7 +109,7 @@ to topics using a ROS2 tool, we can set a parameter with the following syntax ros2 param set \ /peanut_butter_falcon_quote_publisher_node \ quote \ - "You got a good-guy heart. You can't do shit about it, that's just who you are. You're a hero." + "You got a good-guy heart. You can't do shit about it, that's just who you are. You're a hero." If everything is correct, we'll get @@ -122,7 +122,7 @@ If everything is correct, we'll get Some errors are easy to debug, such as when we get the name of the Node wrong .. code-block:: console - + Node not found but because of the interaction between the :program:`terminal`, :program:`ros2 param` itself, and the syntax of the services, its easy to find cryptic error messages. @@ -205,7 +205,7 @@ We can start the Node with the launch file ros2 launch python_package_that_uses_parameters_and_launch_files \ peanut_butter_falcon_quote_publisher_launch.py -which, at the beginning, will have the parameters set in the :file:`_launch.py`. We can then +which, at the beginning, will have the parameters set in the :file:`_launch.py`. We can then .. code-block:: console :emphasize-lines: 3 diff --git a/docs/source/inspecting_services.rst b/docs/source/inspecting_services.rst index 972ae0be..688c5aed 100644 --- a/docs/source/inspecting_services.rst +++ b/docs/source/inspecting_services.rst @@ -52,7 +52,7 @@ To see all currently active services, we run .. code:: console ros2 service list - + which, in this case, outputs .. code-block:: console @@ -79,7 +79,7 @@ Back to our example, we can do ros2 service call /add_points \ package_with_interfaces/srv/AddPoints \ - '{ + '{ a: { x: 10, y: 11, @@ -95,7 +95,7 @@ Back to our example, we can do which results in .. code-block:: console - + requester: making request: package_with_interfaces.srv.AddPoints_Request(a=geometry_msgs.msg.Point(x=10.0, y=11.0, z=12.0), b=geometry_msgs.msg.Point(x=-10.0, y=-10.0, z=22.0)) response: @@ -216,7 +216,7 @@ It might be a bit of a handful, but we will need three (properly sourced) termin ros2 run python_package_that_uses_the_services add_points_service_client_introspection_node -Each program will have it's own output, shown below. For the purposes of this section we can focus on the output of +Each program will have its own output, shown below. For the purposes of this section we can focus on the output of :program:`ros2 service echo`. The other two outputs repeat what we have seen in the previous session, further guaranteeing that the introspection works without affecting the overall behavior of the nodes too much. diff --git a/docs/source/inspecting_topics.rst b/docs/source/inspecting_topics.rst index 796b8a48..e768e4fc 100644 --- a/docs/source/inspecting_topics.rst +++ b/docs/source/inspecting_topics.rst @@ -51,7 +51,7 @@ During the development of a publisher, it is extremely useful to be able to chec .. code:: console - ros2 run python_package_that_uses_the_messages amazing_quote_publisher_node + ros2 run python_package_that_uses_the_messages amazing_quote_publisher_node Getting all topics with :program:`ros2 topic list` -------------------------------------------------- @@ -61,7 +61,7 @@ In particular, when there are many topics, it is difficult to remember every nam .. code:: console ros2 topic list - + which, in this case, outputs .. code-block:: console @@ -83,7 +83,7 @@ showing, in particular, the :code:`/amazing_quote` topic what we were looking fo /amazing_quotes /parameter_events /rosout - + .. _Grep best friend: :program:`grep` is your new best friend @@ -98,13 +98,13 @@ When the list of topics is too large, we can use :program:`grep` to help filter .. code:: console ros2 topic list | grep quote - + which outputs only the lines that contain :code:`quote`, that is .. code:: console /amazing_quote - + Getting quick info with :program:`ros2 topic info` -------------------------------------------------- @@ -136,7 +136,7 @@ To inspect the topic whose name we already know, we run .. code:: console - ros2 topic echo /amazing_quote + ros2 topic echo /amazing_quote which outputs the following @@ -170,7 +170,7 @@ which outputs the following :program:`grep` is still your best friend ----------------------------------------- -Whenever the topic is too crowded or the messages too fast, it might be difficult to pinpoint a single field we are looking for. In that case, :program:`grep` can also help. +Whenever the topic is too crowded or the messages too fast, it might be difficult to pinpoint a single field we are looking for. In that case, :program:`grep` can also help. For example let us say that we want to see only the :code:`id` fields of the messages. We can do @@ -195,7 +195,7 @@ There are situations in which we are interested in knowing if the topics are rec .. code:: console ros2 topic hz /amazing_quote - + which will output, after some time, .. code:: console @@ -220,14 +220,14 @@ Start the subscriber and get basic info .. code:: console - ros2 run python_package_that_uses_the_messages amazing_quote_subscriber_node + ros2 run python_package_that_uses_the_messages amazing_quote_subscriber_node -When only the subscriber is running, we can still get the basic info on the topic, e.g. +When only the subscriber is running, we can still get the basic info on the topic, e.g. .. code:: console ros2 topic list - + which also outputs .. code-block:: console @@ -237,7 +237,7 @@ which also outputs /parameter_events /rosout -and +and .. code:: console @@ -257,50 +257,50 @@ Testing your subscribers with :program:`ros2 topic pub` To somewhat quickly evaluate a subscriber, we can use the :program:`ros2 topic pub`. It allows us to publish messages to check the behavior of our subscribers. -In our case, we can send an :program:`AmazingQuote` using :abbr:`YAML (YAML Ain't Markup Language)` `(More info) `_. You can also refer to the YAML Cheat Sheet at `QuickRef.ME `_. +In our case, we can send an :program:`AmazingQuote` using :abbr:`YAML (YAML Ain't Markup Language)` `(More info) `_. You can also refer to the YAML Cheat Sheet at `QuickRef.ME `_. .. code-block:: console :emphasize-lines: 4-6 ros2 topic pub /amazing_quote \ package_with_interfaces/msg/AmazingQuote \ - '{ - id: 1994, - quote: So you’re telling me there’s a chance, - philosopher_name: Lloyd + '{ + id: 1994, + quote: So you’re telling me there’s a chance, + philosopher_name: Lloyd }' .. note:: - To improve readability, the command above uses the escape character ``\``. You can see more on this at the `bash docs `_. You can also refer to the :program:`bash` Cheat Sheet at `QuickRef.ME `_. + To improve readability, the command above uses the escape character ``\``. You can see more on this at the `bash docs `_. You can also refer to the :program:`bash` Cheat Sheet at `QuickRef.ME `_. which will result in our subscriber outputting .. code:: console - [INFO] [1684222464.960446589] [amazing_quote_subscriber_node]: + [INFO] [1684222464.960446589] [amazing_quote_subscriber_node]: I have received the most amazing of quotes. It says 'So you’re telling me there’s a chance' - And was though by the following genius + And was thought by the following genius -- Lloyd This latest quote had the id=1994. - [INFO] [1684222465.953452826] [amazing_quote_subscriber_node]: + [INFO] [1684222465.953452826] [amazing_quote_subscriber_node]: I have received the most amazing of quotes. It says 'So you’re telling me there’s a chance' - And was though by the following genius + And was thought by the following genius -- Lloyd This latest quote had the id=1994. - + For complicated messages, properly writing the message on the terminal can be a handful. In that case, it might be better to make a minimal script to test the subscriber instead. Refer to :ref:`Create a publisher`. diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 30866275..8ddec0d4 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -3,16 +3,16 @@ ROS2 Installation ================= -.. note:: +.. note:: This tutorial is an abridged version of the original `ROS 2 Documentation `_. This tutorial considers a fresh Ubuntu Desktop (not Server) 24.04 LTS installation, that you have super user access and common sense. It might work in other cases, but those have not been tested in this tutorial. -.. warning:: +.. warning:: All commands must be followed to the letter, in the precise order described herein. Any deviation from what is described might cause unspecified problems and not all of them are easily solvable. Update :program:`apt` packages ------------------------------ -.. hint:: +.. hint:: You can quickly open a new terminal window by pressing :kbd:`CTRL+ATL+T`. After a fresh install, update and upgrade all :program:`apt` packages. @@ -28,7 +28,7 @@ Install a few pre-requisites .. code-block:: console sudo apt install -y software-properties-common curl terminator git - + Namely: =========================== ================================================================================================================================================ @@ -48,21 +48,21 @@ The following commands will do all that magic. sudo add-apt-repository universe export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') - curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" # If using Ubuntu derivates use $UBUNTU_CODENAME + curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" # If using Ubuntu derivatives use $UBUNTU_CODENAME sudo dpkg -i /tmp/ros2-apt-source.deb sudo apt update && sudo apt upgrade -y Install ROS2 packages --------------------- -There are plenty of ways to install ROS2, the following will suffice for now. +There are plenty of ways to install ROS2, the following will suffice for now. .. code-block:: console sudo apt install -y ros-jazzy-desktop ros-dev-tools Set up system environment to find ROS2 -------------------------------------- +-------------------------------------- ROS2 packages are implemented in such a way that they live peacefully in the :code:`/opt/ros/{ROS_DISTRO}` folder in your Ubuntu. A given terminal window or program will only know that ROS2 exists, and which version you want to use, if you run a setup file *for each terminal, every time you open a new one*. @@ -75,14 +75,14 @@ The :code:`~/.bashrc` file can be used for that exact purpose as, in Ubuntu, tha echo "# Source ROS2 Jazzy, as instructed in https://ros2-tutorial.readthedocs.io" >> ~/.bashrc echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc source ~/.bashrc - + Check if it works ----------------- If the following command .. code-block:: console - + ros2 outputs something similar to what is shown below, then it worked! Otherwise, it didn't! diff --git a/docs/source/interfaces.rst b/docs/source/interfaces.rst index 70fff5ca..eea1aa7f 100644 --- a/docs/source/interfaces.rst +++ b/docs/source/interfaces.rst @@ -28,9 +28,9 @@ Getting info on interfaces We can get information about ROS2 interfaces available in our system with :program:`ros2 interface`. Let us first get more information about the program usage with .. code:: console - + ros2 interface -h - + which results in .. code:: console @@ -50,18 +50,18 @@ which results in show Output the interface definition Call `ros2 interface -h` for more detailed usage. - + This shows that with :program:`ros2 interface list` we can get a list of all interfaces available in our workspace. That returns a huge list of interfaces, so it will not be replicated entirely here. Instead, we can run .. code:: console ros2 interface packages - + to get the list of packages with interfaces available, which returns something similar to - + .. code-block:: console :emphasize-lines: 8, 21 - + action_msgs action_tutorials_interfaces actionlib_msgs @@ -99,16 +99,16 @@ to get the list of packages with interfaces available, which returns something s From those, :file:`sensor_msgs` and :file:`geometry_msgs` are packages to always keep in mind when looking for a suitable interface. It will help to keep your Nodes compatible with the community. -.. warning:: +.. warning:: The :file:`std_msgs` package, widely used in ROS1, is deprecated in ROS2 since Foxy. The :file:`example_interfaces` somewhat takes its place, but the recommended practice is to create "semantically meaningful message types". They might remove both or either of these in future versions, so do not use them. -As an example, let us take a look into the :file:`example_interfaces` package, containing, as the name implies, example interface types. We can do so with - +As an example, let us take a look into the :file:`example_interfaces` package, containing, as the name implies, example interface types. We can do so with + .. code:: console ros2 interface package example_interfaces - + which returns .. code:: console @@ -155,7 +155,7 @@ For example, let's say that we are interested in looking up the contents of :fil .. code:: console ros2 interface show example_interfaces/msg/String - + which returns the contents of the source file used to create this message .. code-block:: yaml @@ -181,9 +181,9 @@ We run .. code:: console ros2 interface show example_interfaces/srv/AddTwoInts - + that results in - + .. code-block:: yaml :emphasize-lines: 3 @@ -220,4 +220,4 @@ that results in int32[] sequence Notice that the two :code:`---` are separators to show us the :code:`Goal`, :code:`Result`, and :code:`Feedback` components of the action. -Despite the descriptive name of :file:`Fibonacci`, the :file:`.action` file by itself does nothing. The logic must be implemented on the Node. \ No newline at end of file +Despite the descriptive name of :file:`Fibonacci`, the :file:`.action` file by itself does nothing. The logic must be implemented on the Node. diff --git a/docs/source/launch_configurable_nodes.rst b/docs/source/launch_configurable_nodes.rst index 44936564..58649349 100644 --- a/docs/source/launch_configurable_nodes.rst +++ b/docs/source/launch_configurable_nodes.rst @@ -17,16 +17,16 @@ which returns [--launch-prefix LAUNCH_PREFIX] [--launch-prefix-filter LAUNCH_PREFIX_FILTER] package_name [launch_file_name] [launch_arguments ...] - + Run a launch file - + positional arguments: package_name Name of the ROS package which contains the launch file launch_file_name Name of the launch file launch_arguments Arguments to the launch file; ':=' (for duplicates, last one wins) - + options: -h, --help show this help message and exit -n, --noninteractive Run the launch system non-interactively, with no @@ -63,7 +63,7 @@ We can call our Node, configured with our launch file, with which returns .. code-block:: console - + [INFO] [launch]: All log files can be found below /home/murilo/.ros/log/2023-06-30-17-00-07-522194-murilos-toaster-2963 [INFO] [launch]: Default logging verbosity is set to INFO [INFO] [amazing_quote_configurable_publisher_node-1]: process started with pid [2964] @@ -79,7 +79,7 @@ showing that the launch was successful. resulting in something similar to .. code-block:: console - + id: 301 quote: Yeah, you're gonna die, it's a matter of time. That ain't the question. The question's, whether they're gonna have a good story ... philosopher_name: Tyler diff --git a/docs/source/navigation/custom_nodes.rst b/docs/source/navigation/custom_nodes.rst index 55c58df4..fe8ebaa2 100644 --- a/docs/source/navigation/custom_nodes.rst +++ b/docs/source/navigation/custom_nodes.rst @@ -444,7 +444,7 @@ from the node. We receive an empty behaviour tree and that is also added to the :language: python :lines: 44-49 -Besides the slightly different action type, the process to make an action client is mostly unchanged. +Besides the slightly different action type, the process to make an action client is mostly unchanged. Adjusting the :file:`setup.py` ------------------------------ diff --git a/docs/source/navigation/installation.rst b/docs/source/navigation/installation.rst index bc46c2e8..074f6814 100644 --- a/docs/source/navigation/installation.rst +++ b/docs/source/navigation/installation.rst @@ -47,4 +47,4 @@ localisation and mapping. Both can be done in isolation too. - https://docs.nav2.org/concepts/index.html - https://docs.nav2.org/setup_guides/transformation/setup_transforms.html - https://docs.nav2.org/setup_guides/sdf/setup_sdf.html - - https://docs.nav2.org/setup_guides/odom/setup_odom_gz.html \ No newline at end of file + - https://docs.nav2.org/setup_guides/odom/setup_odom_gz.html diff --git a/docs/source/navigation/tb3_simulation_launch_disclaimer.rst b/docs/source/navigation/tb3_simulation_launch_disclaimer.rst index 2ef73acc..f3ae52d4 100644 --- a/docs/source/navigation/tb3_simulation_launch_disclaimer.rst +++ b/docs/source/navigation/tb3_simulation_launch_disclaimer.rst @@ -4,4 +4,4 @@ cleanly on my machines. It has hanged sometimes, sometimes :program:`Gazebo` does not show, and sometimes it does not shutdown properly. It is highly recommended to use a docker container, such as ``_, - to minimise disappointment. \ No newline at end of file + to minimise disappointment. diff --git a/docs/source/navigation/usage.rst b/docs/source/navigation/usage.rst index b479167e..842064c0 100644 --- a/docs/source/navigation/usage.rst +++ b/docs/source/navigation/usage.rst @@ -24,7 +24,7 @@ obstacles, and other relevant objects. In this example, a `TurtleBot3 `_ will be used. As part of ``nav2_bringup``, there is a rather complete example that we can utilize, namely :file:`tb3_simulation_launch.py`. The example can be executed -with the following command. +with the following command. .. code-block:: console diff --git a/docs/source/parameters_and_launch.rst b/docs/source/parameters_and_launch.rst index 5a1226a9..e144728f 100644 --- a/docs/source/parameters_and_launch.rst +++ b/docs/source/parameters_and_launch.rst @@ -1,17 +1,17 @@ Parameters and launch files: creating configurable Nodes ======================================================== -The Nodes we have made in the past few sections are interesting because they take advantage of the interprocess communication provided by ROS2. +The Nodes we have made in the past few sections are interesting because they take advantage of the interprocess communication provided by ROS2. -Other capabilities of ROS2 that we must take advantage of are `ROS2 parameters `_ and `ROS2 launch files `_. We can use them to modify the behavior of Nodes without having to modify their source code. +Other capabilities of ROS2 that we must take advantage of are `ROS2 parameters `_ and `ROS2 launch files `_. We can use them to modify the behavior of Nodes without having to modify their source code. For Python users, that might sound less appealing than for users of compiled languages. However, users of your package might not want nor be able to modify the source code directly, if the package is installable or part of a larger system with multiple users. - + Create the package ------------------ -First, let us create an :program:`ament_python` package that depends on our :file:`packages_with_interfaces` and build from there. +First, let us create an :program:`ament_python` package that depends on our :file:`package_with_interfaces` and build from there. .. code:: console @@ -41,7 +41,7 @@ Create the Node using parameters .. code-block:: console :emphasize-lines: 7 - + python_package_that_uses_parameters_and_launch_files/ |-- launch | `-- peanut_butter_falcon_quote_publisher_launch.py @@ -94,7 +94,7 @@ For one-off parameters, we just get them once after declaring them. Because we'r :lines: 39-50 :emphasize-lines: 2-5,9,12 -In this case, we're making the topic name and publication periodicity as one-off configurable parameters. +In this case, we're making the topic name and publication periodicity as one-off configurable parameters. Continuously-obtained parameters -------------------------------- @@ -128,10 +128,10 @@ Truly configurable: using :file:`_launch.py` files #. Create the launch file named as :file:`launch/_launch.py`. #. (Once) modify the :file:`setup.py` to correctly install launch files. -.. note: +.. note:: For a previous user of ROS1 used with the :abbr:`XML (Extensible Markup Language)`\ -based :file:`.launch` files, switching for the Python-based ones is a hassle. However, my experience with these so far has been quite positive, because when using Python we have access to an entire ecosystem of tools to make the launch files - smarter, whereas with the :abbr:`XML (Extensible Markup Language)`\ -based ones, if possible at all, we had to add hack on top of hack to achieve the same. + smarter, whereas with the :abbr:`XML (Extensible Markup Language)`\ -based ones, if possible at all, we had to add hack on top of hack to achieve the same. Differently from ROS1, in ROS2 we can use Python launch files. They are quite powerful, well documented, and mentioned first `in the official documentation `_, so we will use them instead of :abbr:`XML (Extensible Markup Language)` or :abbr:`YAML (YAML ain't markup language)` files. @@ -142,7 +142,7 @@ Differently from ROS1, in ROS2 we can use Python launch files. They are quite po .. code-block:: console :emphasize-lines: 2 - + python_package_that_uses_parameters_and_launch_files/ |-- launch | `-- peanut_butter_falcon_quote_publisher_launch.py @@ -173,7 +173,7 @@ Create the :file:`launch` file .. code-block:: console :emphasize-lines: 3 - + python_package_that_uses_parameters_and_launch_files/ |-- launch | `-- peanut_butter_falcon_quote_publisher_launch.py @@ -199,9 +199,9 @@ Suppose that we are tired of all the meme quotes and want to make our Node publi .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_parameters_and_launch_files/launch/peanut_butter_falcon_quote_publisher_launch.py :language: python - :linenos: + :linenos: -We're relying on the :code:`LaunchDescription`, which expects a list of :code:`launch_ros.actions`. +We're relying on the :code:`LaunchDescription`, which expects a list of :code:`launch_ros.actions`. .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_parameters_and_launch_files/launch/peanut_butter_falcon_quote_publisher_launch.py :language: python @@ -233,7 +233,7 @@ The :file:`setup.py` .. code-block:: console :emphasize-lines: 11 - + python_package_that_uses_parameters_and_launch_files/ |-- launch | `-- peanut_butter_falcon_quote_publisher_launch.py @@ -259,7 +259,7 @@ Modify the :file:`setup.py` to look like this :linenos: :emphasize-lines: 15,25-27 -We have already seen a :file:`setup.py` so many times we're almost calling it `Wilson `_. +We have already seen a :file:`setup.py` so many times we're almost calling it `Wilson `_. The only difference is emphasized above inside the :code:`data_files`, which is the line that will specify that launch files will be installed as well. Notice that the :file:`setup.py` looks for files with a specific pattern in the folder :file:`launch`, so be sure that your launch files have the correct name otherwise they might not be installed as expected. diff --git a/docs/source/preamble/python/installing_python.rst b/docs/source/preamble/python/installing_python.rst index f87c054a..955668c9 100644 --- a/docs/source/preamble/python/installing_python.rst +++ b/docs/source/preamble/python/installing_python.rst @@ -2,9 +2,9 @@ Installing Python on Ubuntu =========================== .. warning:: - If you change or try to tinker with the default Python version of Ubuntu, your system will most likely **BREAK COMPLETELY**. + If you change or try to tinker with the default Python version of Ubuntu, your system will most likely **BREAK COMPLETELY**. Do not play around with the default Python installation, because Ubuntu depends on it to work properly (or work at all). - + In Ubuntu, Python is already installed! In fact, Ubuntu would not work without it. Let's check its version by running @@ -12,25 +12,25 @@ In fact, Ubuntu would not work without it. Let's check its version by running python3 --version -which should output +which should output .. code-block:: console Python 3.12.3 - + If the :code:`3.12` part of your version is different, this tutorial might not work for you. Please make sure to use the default Python in your Ubuntu. .. warning:: Note that the command is :program:`python3` and not :program:`python`. In fact, the result of - + .. code-block:: console - + python - - is - + + is + .. code-block:: console - + Command 'python' not found, did you mean: command 'python3' from deb python3 command 'python' from deb python-is-python3 @@ -50,7 +50,7 @@ which should output something similar to Python 3.12.3 (main, Jun 18 2025, 17:59:45) [GCC 13.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. - >>> + >>> in particular, if the :code:`GCC 13` is different, then this tutorial might not work for you. @@ -71,7 +71,7 @@ For the purposes of this tutorial, let us install :code:`pip` and :code:`venv` sudo apt update sudo apt install -y python3-pip python3-venv - + .. _Isolate your environment with a venv: When you want to isolate your environment, use :program:`venv` @@ -79,23 +79,23 @@ When you want to isolate your environment, use :program:`venv` .. warning:: At the time of this writing, there was no support for :program:`venv` on ROS2 `(More info) `_. - Until that is handled, we are not going to use :program:`venv` for the ROS2 tutorials. + Until that is handled, we are not going to use :program:`venv` for the ROS2 tutorials. However, we will use :program:`venv` to protect our ROS2 environment from these Python preamble tutorials. - + Using :program:`venv` (`More info `_) is quite straightforward. Create a :file:`venv` -^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++ .. code-block:: console cd ~ python3 -m venv ros2tutorial_venv - + where the only argument, :code:`ros2tutorial_venv`, is the name of the folder in which the :code:`venv` will be created. Activate a :file:`venv` -^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++ Whenever we want to use a :file:`venv`, it must be explicitly activated. @@ -103,15 +103,15 @@ Whenever we want to use a :file:`venv`, it must be explicitly activated. cd ~ source ros2tutorial_venv/bin/activate - + The terminal will change to have the prefix :code:`(ros2tutorial_venv)` to let us know that we are using a :file:`venv`, as follows .. code-block:: console - (ros2tutorial_venv) murilo@murilos-toaster:~$ - + (ros2tutorial_venv) murilo@murilos-toaster:~$ + Deactivate a :file:`venv` -^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++ To deactivate, run @@ -119,25 +119,25 @@ To deactivate, run deactivate -We'll know that we're no longer using the :code:`ros2tutorial_venv` because the prefix will disappear back to +We'll know that we're no longer using the :code:`ros2tutorial_venv` because the prefix will disappear back to .. code-block:: console - murilo@murilos-toaster:~$ + murilo@murilos-toaster:~$ Installing libraries -------------------- .. warning:: - In these tutorials, we rely either on :program:`apt` or :program:`pip` to install packages. + In these tutorials, we rely either on :program:`apt` or :program:`pip` to install packages. There are other package managers for Python and plenty of other ways to install and manage packages. They are, in general, not compatible with each other so, like cleaning products, **DO NOT** mix them. - + .. hint:: Using :code:`python3 -m pip` instead of calling just :code:`pip` allows more control over which version of :program:`pip` is being called. The need for this becomes more evident when several Python versions have to coexist in a system. - -As an example, let us install the best robot modeling and control library ever conceived, `DQ Robotics `_. + +As an example, let us install the best robot modeling and control library ever conceived, `DQ Robotics `_. First, we activate the virtual environment @@ -151,7 +151,7 @@ then, we install .. code-block:: console python3 -m pip install dqrobotics --break-system-packages - + which will result in something similar to (might change depending on future versions) .. code-block:: console @@ -177,7 +177,7 @@ We can remove the library we just installed with resulting in .. code-block:: console - + Found existing installation: dqrobotics 25.4.0a7 Uninstalling dqrobotics-25.4.0a7: Would remove: diff --git a/docs/source/preamble/python/python_asyncio.rst b/docs/source/preamble/python/python_asyncio.rst index acf56d9e..94cfa01f 100644 --- a/docs/source/preamble/python/python_asyncio.rst +++ b/docs/source/preamble/python/python_asyncio.rst @@ -113,7 +113,7 @@ Using :code:`await` ------------------- .. admonition:: **TL;DR** Using :code:`await` - + #. Run multiple :code:`Task`\ s. #. Use :code:`await` for them, **after they were executed**. @@ -176,7 +176,7 @@ Ok, enough with the explanation, let's go to the endorphin rush of actually runn cd ~/ros2_tutorials_preamble/python/minimalist_package/ python3 -m minimalist_package.minimalist_async.async_await_example -Which will result in something like shown below. The function is stochastic, so it might take more or less time to +Which will result in something like shown below. The function is stochastic, so it might take more or less time to return and the order of the tasks ending might also be different. However, in the :code:`await` framework, the results will **ALWAYS** be processed in the order that was specified @@ -189,28 +189,28 @@ We can also see that both tasks are running concurrently until :code:`task2` fin :emphasize-lines: 13,20,21,22 Awaiting results... - task1 retry needed (roll = 0.36896762068176037 > 0.1). - task2 retry needed (roll = 0.8429002838770375 > 0.1). - task1 retry needed (roll = 0.841018521652675 > 0.1). - task2 retry needed (roll = 0.1351152094825686 > 0.1). - task1 retry needed (roll = 0.9484654265361889 > 0.1). - task2 retry needed (roll = 0.3167046796566366 > 0.1). - task1 retry needed (roll = 0.7519672365071198 > 0.1). - task2 retry needed (roll = 0.38440407016827005 > 0.1). - task1 retry needed (roll = 0.23155484384953284 > 0.1). - task2 retry needed (roll = 0.6418306170261009 > 0.1). - task1 retry needed (roll = 0.532161975008607 > 0.1). + task1 retry needed (roll = 0.36896762068176037 > 0.1). + task2 retry needed (roll = 0.8429002838770375 > 0.1). + task1 retry needed (roll = 0.841018521652675 > 0.1). + task2 retry needed (roll = 0.1351152094825686 > 0.1). + task1 retry needed (roll = 0.9484654265361889 > 0.1). + task2 retry needed (roll = 0.3167046796566366 > 0.1). + task1 retry needed (roll = 0.7519672365071198 > 0.1). + task2 retry needed (roll = 0.38440407016827005 > 0.1). + task1 retry needed (roll = 0.23155484384953284 > 0.1). + task2 retry needed (roll = 0.6418306170261009 > 0.1). + task1 retry needed (roll = 0.532161975008607 > 0.1). task2 Done. - task1 retry needed (roll = 0.448132225703992 > 0.1). - task1 retry needed (roll = 0.13504700640433664 > 0.1). - task1 retry needed (roll = 0.7404815278498079 > 0.1). - task1 retry needed (roll = 0.9830081693068259 > 0.1). - task1 retry needed (roll = 0.4070546146764875 > 0.1). - task1 retry needed (roll = 0.7474267487174882 > 0.1). + task1 retry needed (roll = 0.448132225703992 > 0.1). + task1 retry needed (roll = 0.13504700640433664 > 0.1). + task1 retry needed (roll = 0.7404815278498079 > 0.1). + task1 retry needed (roll = 0.9830081693068259 > 0.1). + task1 retry needed (roll = 0.4070546146764875 > 0.1). + task1 retry needed (roll = 0.7474267487174882 > 0.1). task1 Done. The result of task=task1 was 0.038934769861482144. The result of task=task2 was 0.06380247590535493. - + Process finished with exit code 0 Hooray! May there be concurrency! @@ -219,7 +219,7 @@ Using :code:`callback` ---------------------- .. admonition:: **TL;DR** Using :code:`callbacks` - + #. Run multiple :code:`Task`\ s. #. Add a :code:`callback` to handle the result **as soon as it is ready**. #. Use :code:`await` for each :code:`Task` just so that the main loop does not return prematurely. @@ -261,13 +261,13 @@ Enough diplomacy, let's make a file called :file:`async_callback_example.py` in In the :code:`callback` paradigm, besides the function that does the actual task, as in the prior example, we have to make a, to no one's surprise, callback function to process the results as they come. -We do so with +We do so with .. literalinclude:: ../../../../preamble/python/minimalist_package/minimalist_package/minimalist_async/async_callback_example.py :language: python :lines: 29-40 -In this case, the :code:`callback` must receive a :code:`asyncio.Future` and process it. Test the future for :code:`None` in +In this case, the :code:`callback` must receive a :code:`asyncio.Future` and process it. Test the future for :code:`None` in case the task fails for any reason. Aside from that, there are only two key differences with the :code:`await` logic example we showed before, @@ -296,13 +296,13 @@ ends before the first one, it will be automatically processed by its :code:`call Awaiting results... task1 retry needed (roll = 0.6248308966234916 > 0.1). task2 retry needed (roll = 0.24259714032999036 > 0.1). - task1 retry needed (roll = 0.1996764883575476 > 0.1). + task1 retry needed (roll = 0.1996764883575476 > 0.1). task2 Done. The result of task=task2 was 0.09069407383542283. - task1 retry needed (roll = 0.6700777523785147 > 0.1). - task1 retry needed (roll = 0.7344216907108979 > 0.1). - task1 retry needed (roll = 0.4907223062034761 > 0.1). - task1 retry needed (roll = 0.20026037098687932 > 0.1). + task1 retry needed (roll = 0.6700777523785147 > 0.1). + task1 retry needed (roll = 0.7344216907108979 > 0.1). + task1 retry needed (roll = 0.4907223062034761 > 0.1). + task1 retry needed (roll = 0.20026037098687932 > 0.1). task1 Done. The result of task=task1 was 0.09676678954317675. diff --git a/docs/source/preamble/python/python_best_practices.rst b/docs/source/preamble/python/python_best_practices.rst index 5986009f..cbb63fbe 100644 --- a/docs/source/preamble/python/python_best_practices.rst +++ b/docs/source/preamble/python/python_best_practices.rst @@ -53,7 +53,7 @@ Minimalist package: something to start with .. code-block:: console :emphasize-lines: 1-4 - + python/ `-- minimalist_package |-- minimalist_package @@ -197,7 +197,7 @@ resulting in Note that for this second execution strategy to work, we **MUST** have the `#!`, called `shebang `_, at the beginning of the first line. The path after the shebang specifies what program will be used to interpret that file. In general, differently from Windows, Ubuntu does not guess the file type by the extension -when running it. +when running it. .. literalinclude:: ../../../../preamble/python/minimalist_package/minimalist_package/minimalist_script.py :language: python @@ -313,7 +313,7 @@ following contents .. note:: Relative imports such as :code:`from . import ` might work in some cases, and that is fine. It is a supported - and valid way to import. However, don't be surprised when it doesn't work in ROS2, PyPI packages, etc., and `generates + and valid way to import. However, don't be surprised when it doesn't work in ROS2, PyPI packages, etc., and `generates a lot of frustration `_. Not a matter of taste: Code style @@ -393,7 +393,7 @@ When documenting your code, obviously, the documentation should be about what th .. hint:: - Ideally, all documentation is perfect from the start. In reality, however, that rarely ever happens so some documentation is always better than none. + Ideally, all documentation is perfect from the start. In reality, however, that rarely ever happens so some documentation is always better than none. My advice would be to write something as it goes and possibly adjust it to more stable or cleaner documentation when the need arises. Unit tests: always test your code @@ -423,15 +423,15 @@ Unit tests: always test your code `-- test `-- test_minimalist_class.py -`Unit testing `_ is a flag that has been waved by programming enthusiasts +`Unit testing `_ is a flag that has been waved by programming enthusiasts and is often a good measurement of code maturity. The elephant in the room is that writing unit tests is **boring**. Yes, we know, *very* boring. Unit tests are boring because they are an *investment*. Unit testing won't necessarily make your -code `[...] better, faster, [...] `_ *right now*. However, without tests, don't +code `[...] better, faster, [...] `_ *right now*. However, without tests, don't be surprised after some point if your implementations make you drown in `tech debt `_. -Dedicating a couple of minutes now to make a couple of tests when your codebase is still in its infancy makes it more manageable +Dedicating a couple of minutes now to make a couple of tests when your codebase is still in its infancy makes it more manageable and less boresome. Back to the example, a good practice is to create a folder name :file:`test` at the same level as the packages to be tested, like so @@ -444,7 +444,7 @@ Back to the example, a good practice is to create a folder name :file:`test` at Then, we create a file named :file:`test_minimalist_class.py` with the contents below in the :file:`test` folder. .. note:: - + The prefix :file:`test_` is important as it is used by some frameworks to automatically discover tests. So it is better not to use that prefix if that file does not contain a unit test. @@ -456,7 +456,7 @@ Then, we create a file named :file:`test_minimalist_class.py` with the contents :lines: 1- Running the tests -^^^^^^^^^^^^^^^^^ ++++++++++++++++++ For a quick jolt of instant gratification, let's run the tests before we proceed with the explanation. @@ -470,22 +470,22 @@ There are many ways to run tests written with :code:`unittest`. The following wi which will output .. code-block:: console - + test_attribute (test_minimalist_class.TestMinimalistClass) ... ok test_get_set_private_attribute (test_minimalist_class.TestMinimalistClass) ... ok test_method (test_minimalist_class.TestMinimalistClass) ... ok test_private_attribute (test_minimalist_class.TestMinimalistClass) ... ok test_static_method (test_minimalist_class.TestMinimalistClass) ... ok - + ---------------------------------------------------------------------- Ran 5 tests in 0.000s - + OK Yay! We've done it! Start with use :code:`unittest` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++++++++ .. note:: @@ -501,18 +501,18 @@ We import the :code:`unittest` module along with the class that we want to test, :lines: 1-2 Test them all -^^^^^^^^^^^^^ ++++++++++++++ .. note:: Good unit tests will not only let you know when something broke but also *where* it broke. A failed test of a high-level function might not give you too much information, whereas a failed test of a lower-level (more fundamental) function will allow you to pinpoint the issue. -Unit tests are somewhat like insurance. The more coverage you have, the better. In this example, we test all the +Unit tests are somewhat like insurance. The more coverage you have, the better. In this example, we test all the elements in the class. Each test will be based on one or more asserts. For more info check the `unittest docs `_. In a few words, we make a subclass of :code:`unittest.TestCase` and create methods within it that test one part -of the code, hence the name unit tests. +of the code, hence the name unit tests. .. literalinclude:: ../../../../preamble/python/minimalist_package/test/test_minimalist_class.py :language: python @@ -521,7 +521,7 @@ of the code, hence the name unit tests. If one of the :code:`asserts` fails, then the related test will fail, and the test framework will let us know which one. The test's main function -^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++ Generally, a test script based on `unittest` will have the following main function. It will run all available tests in our test class. For more info and alternatives check the `unittest docs `_. diff --git a/docs/source/preamble/python/python_packaging.rst b/docs/source/preamble/python/python_packaging.rst index 9e1877bb..3cc51cea 100644 --- a/docs/source/preamble/python/python_packaging.rst +++ b/docs/source/preamble/python/python_packaging.rst @@ -27,7 +27,7 @@ The :file:`setup.py` .. code-block:: console :emphasize-lines: 12 - + python/ `-- minimalist_package |-- minimalist_package @@ -73,7 +73,7 @@ Installing :file:`wheel` .. code-block:: console - DEPRECATION: minimalist-package is being installed using the legacy 'setup.py install' method because it does not have a 'pyproject.toml' + DEPRECATION: minimalist-package is being installed using the legacy 'setup.py install' method because it does not have a 'pyproject.toml' and the 'wheel' package is not installed. pip 23.1 will enforce this behaviour change. A possible replacement is to enable the '--use-pep517' option. Discussion can be found at https://github.com/pypa/pip/issues/8559 @@ -138,16 +138,16 @@ which will return something similar to .. code-block:: console Awaiting results... - task1 retry needed (roll = 0.1534174185325745 > 0.1). - task2 retry needed (roll = 0.35338687437350913 > 0.1). + task1 retry needed (roll = 0.1534174185325745 > 0.1). + task2 retry needed (roll = 0.35338687437350913 > 0.1). task1 Done. - task2 retry needed (roll = 0.3877920607121429 > 0.1). + task2 retry needed (roll = 0.3877920607121429 > 0.1). The result of task=task1 was 0.07646509818952207. - task2 retry needed (roll = 0.7010015915930288 > 0.1). - task2 retry needed (roll = 0.8907576123834621 > 0.1). - task2 retry needed (roll = 0.4233577578392548 > 0.1). - task2 retry needed (roll = 0.7512028176843422 > 0.1). - task2 retry needed (roll = 0.33501957024540663 > 0.1). + task2 retry needed (roll = 0.7010015915930288 > 0.1). + task2 retry needed (roll = 0.8907576123834621 > 0.1). + task2 retry needed (roll = 0.4233577578392548 > 0.1). + task2 retry needed (roll = 0.7512028176843422 > 0.1). + task2 retry needed (roll = 0.33501957024540663 > 0.1). task2 Done. The result of task=task2 was 0.09239734738421612. @@ -158,7 +158,7 @@ We first run an interactive session with .. code-block:: console - python3 + python3 we can then interact with it as any other installed package @@ -192,7 +192,7 @@ which will return something similar to /home/murilo/ros2tutorial_venv/bin/minimalist_script /home/murilo/ros2tutorial_venv/lib/python3.10/site-packages/minimalist_package-23.6.0.dist-info/* /home/murilo/ros2tutorial_venv/lib/python3.10/site-packages/minimalist_package/* - Proceed (Y/n)? + Proceed (Y/n)? and just press :kbd:`ENTER`, resulting in the package being uninstalled diff --git a/docs/source/preamble/ubuntu.rst b/docs/source/preamble/ubuntu.rst index 8223d317..a2907c71 100644 --- a/docs/source/preamble/ubuntu.rst +++ b/docs/source/preamble/ubuntu.rst @@ -12,9 +12,9 @@ Who cares about the :program:`terminal` anyways, are you like 100 years old or s ----------------------------------------------------------------------------------------- Besides the unintended upside that if you're typing into a terminal fast enough with a black hoodie, you're cosplaying `Mr. Robot `_ at a very low cost, there wouldn't be another way to make a tutorial like this within -the current age of the Universe without relying on Ubuntu's :program:`terminal`. +the current age of the Universe without relying on Ubuntu's :program:`terminal`. -:abbr:`GUIs (Graphical User Interfaces)` change faster than long tutorials like this one can keep up with and :program:`terminal` +:abbr:`GUIs (Graphical User Interfaces)` change faster than long tutorials like this one can keep up with and :program:`terminal` is our reliable partner in crime and unlikely to change much in the foreseeable future. For the whole tutorial, you can copy and paste the commands in :program:`terminal`. If it doesn't work, it's either your fault or mine, @@ -27,7 +27,7 @@ The :program:`terminal` Check out `Canonical's Tutorial `_ on :program:`terminal` for the complete story. -.. hint:: +.. hint:: You can open a new terminal window by pressing :kbd:`CTRL+ALT+T`. @@ -50,7 +50,7 @@ For all intents and purposes, Tom Hanks is not stuck in this terminal. Instead, - Outputs the absolute path to the current directory. * - :program:`mkdir` - :code:`mkdir a_folder` - - **M**\ a\ **k**\ es a **dir**\ ectory called :file:`a_folder` in the current directory. + - **M**\ a\ **k**\ es a **dir**\ ectory called :file:`a_folder` in the current directory. * - :program:`cd` - :code:`cd a_folder` - **C**\ hanges **d**\ irectory to a specified target. @@ -94,7 +94,7 @@ To warm up, let's start by creating an empty file inside a new directory, as fol The path :file:`~` stands for the currently logged-in user's home folder. Not every command is able to expand it. In those cases, use ``$USER`` instead. -.. hint:: +.. hint:: You can open a new terminal window by pressing :kbd:`CTRL+ALT+T`. @@ -115,7 +115,7 @@ Then, we can use :program:`nano` to create another file with some contents nano file_with_stuff.txt -Then, :program:`nano` will run. At this point we can start typing, so let's just type +Then, :program:`nano` will run. At this point we can start typing, so let's just type .. code-block:: console @@ -208,7 +208,7 @@ Tab completion Whenever I have to look at a novice's shoulders while they interact with the terminal it gives me a certain level of anxiety. That is because they are trying to perfectly type even the longest and meanest paths for files, directories, and programs. The terminal has :kbd:`TAB` completion, so use it extensively. -You can press :kbd:`TAB` at any time to complete the name of a program, folder, file, or pretty much anything. +You can press :kbd:`TAB` at any time to complete the name of a program, folder, file, or pretty much anything. For example, we can move to a folder @@ -245,7 +245,7 @@ Be careful even when not using :program:`sudo` ---------------------------------------------- With regular user privileges, the `major `_ system folders will be protected from tampering. However, our home folder, e.g. :file:`/home/` will not. -In our home folder, we are the lords, so a mistake can be fatal for your files/directories. +In our home folder, we are the lords, so a mistake can be fatal for your files/directories. File permissions ---------------- @@ -264,7 +264,7 @@ I will not get into detail here about programs to change permissions because we To some extent similar to :program:`explorer` in Windows and :program:`finder` in macOS, :program:`nautilus` is `the default file manager in Ubuntu `_. One tip is that it can be opened from the :program:`terminal` as well, so that you don't have to find whatever folder you are again. -For example, +For example, .. hint:: diff --git a/docs/source/publishers_and_subscribers.rst b/docs/source/publishers_and_subscribers.rst index ba60ea07..06fc8562 100644 --- a/docs/source/publishers_and_subscribers.rst +++ b/docs/source/publishers_and_subscribers.rst @@ -1,5 +1,5 @@ Publishers and Subscribers: using messages -=========================================== +========================================== Finally, we reached the point where :program:`ROS2` becomes appealing. As you saw in the last section, we can easily create complex interface types using an easy and generic description. We can use those to provide `interprocess communication `_, i.e. two different programs talking to each other, which otherwise can be error-prone and very difficult to implement. @@ -56,7 +56,7 @@ This will be the structure of the package. The main elements are highlighted. Create the package ------------------ -First, let us create an :program:`ament_python` package that depends on our newly developed :file:`packages_with_interfaces` and build from there. +First, let us create an :program:`ament_python` package that depends on our newly developed :file:`package_with_interfaces` and build from there. .. code:: console @@ -128,15 +128,15 @@ Create the Node with a publisher #. Add new dependencies to :file:`package.xml` #. Import new messages :code:`from .msg import ` #. In a subclass of :code:`Node` - + #. Create a publisher with :code:`self.publisher = self.create_publisher(...)` #. Send messages with :code:`self.publisher.publish(....)` - + #. Add the new Node to :file:`setup.py` For the publisher, create a file called :file:`amazing_quote_publisher_node.py`, with the following contents -:download:`~/ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_publisher_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_publisher_node.py>` +:download:`amazing_quote_publisher_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_publisher_node.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_publisher_node.py :language: python @@ -178,7 +178,7 @@ Then, each message is handled much like any other class in Python. We instantiat :language: python :lines: 48-51 -Lastly, the message needs to be published using :code:`Node.publish(msg)`. +Lastly, the message needs to be published using :code:`Node.publish(msg)`. .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_publisher_node.py :language: python @@ -196,22 +196,22 @@ Create the Node with a subscriber #. Add new dependencies to :file:`package.xml` #. Import new messages :code:`from .msg import ` #. In a subclass of :code:`Node` - + #. Create a callback :code:`def callback(self, msg):` #. Create a subscriber :code:`self.subscriber = self.create_subscription(...)` - + #. Add the new Node to :file:`setup.py` For the subscriber Node, create a file in :file:`python_package_that_uses_the_messages/python_package_that_uses_the_messages` called :file:`amazing_quote_subscriber_node.py`, with the following contents -:download:`~/ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py>` +:download:`amazing_quote_subscriber_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py :language: python :linenos: :lines: 24- :emphasize-lines: 3, 11-15, 17-31 - + Similarly to the publisher, in the subscriber, we start by importing the message in question .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py @@ -225,7 +225,7 @@ Then, in our subclass of :code:`Node`, we call :code:`Node.create_subscription(. :language: python :lines: 34-38 :emphasize-lines: 4 - + where the only difference with respect to the publisher is the third argument, namely :code:`callback`, in which a method that receives a :code:`msg_type` and returns nothing is expected. For example, the :code:`amazing_quote_subscriber_callback`. .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/python_package_that_uses_the_messages/amazing_quote_subscriber_node.py @@ -244,7 +244,7 @@ Update the :file:`setup.py` As we already learned in :ref:`Making rosrun work`, we must adjust the :file:`setup.py` to refer to the Nodes we just created. -:download:`~/ros2_tutorial_workspace/src/python_package_that_uses_the_messages/setup.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/setup.py>` +:download:`setup.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/setup.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_messages/setup.py :language: python @@ -278,7 +278,7 @@ Hence, in the second terminal, we run .. code:: console ros2 run python_package_that_uses_the_messages amazing_quote_subscriber_node - + which outputs .. code:: console @@ -312,9 +312,9 @@ which outputs .. note:: If there are any issues with either the publisher or the subscriber, this connection will not work. In the next section, we'll see strategies to help us troubleshoot and understand communication through topics. - + .. warning:: Unless instructed otherwise, the publisher does **NOT** wait for a subscriber to connect before it starts publishing the messages. As shown in the case above, the first message we received started with `id>0`. If we delayed longer to start the publisher, we would have received later messages only. - + Let's close each node with :kbd:`CTRL+C` on each terminal before we proceed to the next tutorial. diff --git a/docs/source/python_node_explained.rst b/docs/source/python_node_explained.rst index 0155d628..704fe8f8 100644 --- a/docs/source/python_node_explained.rst +++ b/docs/source/python_node_explained.rst @@ -2,7 +2,7 @@ The Python Node, explained ========================== .. note:: - + The way that a Python Node in ROS2 works, i.e. the explanation in this section, does not depend on the building with :program:`ament_python` or :program:`ament_cmake`. In a strict sense, the :file:`print_forever_node.py` is not a minimal Node, but it does showcase most good practices in a Node that actually does something. @@ -13,19 +13,19 @@ The imports .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_node/python_package_with_a_node/print_forever_node.py :language: python :lines: 24-25 - + As in any Python code, we have to import the libraries that we will use and specific modules/classes within those libraries. With :code:`rclpy`, there is no difference. Making a subclass of :code:`Node` --------------------------------- -The current version of ROS2 behaves better when your custom Node is a subclass of :code:`rclpy.node.Node`. That is achieved with +The current version of ROS2 behaves better when your custom Node is a subclass of :code:`rclpy.node.Node`. That is achieved with .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_node/python_package_with_a_node/print_forever_node.py :language: python :lines: 28-33 :emphasize-lines: 1,5 - + About inheritance in Python, you can check the official documentation on `inheritance `_ and on `super() `_. In more advanced nodes, inheritance does not cut it, but that is an advanced topic to be covered some other time. @@ -39,7 +39,7 @@ Use a :code:`Timer` for periodic work (when using :code:`rclpy.spin()`) If the code relies on :code:`rclpy.spin()`, a Timer must be used for periodic work. -In its most basic usage, periodic tasks in ROS2 must be handled by a `Timer `_. +In its most basic usage, periodic tasks in ROS2 must be handled by a `Timer `_. To do so, have the node create it with the :code:`create_timer()` method, as follows. @@ -54,11 +54,11 @@ The method that is periodically called by the Timer is, in this case, as follows :language: python :lines: 37-39 :emphasize-lines: 1 - + In ROS2, the logging methods, i.e. :code:`self.get_logger().info()`, are methods of the Node itself. So, the capability to log (print to the terminal) using ROS2 Nodes is dependent on the scope in which that Node exists. - -Where the ROS2 magic happens: :code:`rclpy.init()` and :code:`rclpy.spin()` ---------------------------------------------------------------------------- + +Where the ROS2 magic happens: :code:`rclpy.init()` and :code:`rclpy.spin()` +---------------------------------------------------------------------------- All the ROS2 magic happens in some sort of :code:`spin()` method. It is called this way because the :code:`spin()` method will constantly loop (or spin) through **items of work**, e.g. scheduled Timer callbacks. All the **items of work** will only be effectively executed when an **executor** runs through it. For simple Nodes, such as the one in this example, the **global** executor is implicitly used. You can read a bit more about that `here `_. @@ -68,11 +68,11 @@ Anyhow, the point is that nothing related to ROS2 will happen unless the two fol :language: python :lines: 43-58 :emphasize-lines: 8,12 - + Have a :code:`try-catch` block for :code:`KeyboardInterrupt` ------------------------------------------------------------ -.. note: +.. note:: You can see more about this topic at :ref:`Python try catch`, in the preamble. @@ -116,7 +116,7 @@ Example of a class: :language: python :lines: 28-29 :emphasize-lines: 2 - + Example of a method: .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_with_a_node/python_package_with_a_node/print_forever_node.py diff --git a/docs/source/running_node.rst b/docs/source/running_node.rst index 94dad9c1..338aba72 100644 --- a/docs/source/running_node.rst +++ b/docs/source/running_node.rst @@ -7,10 +7,10 @@ The most basic way of running a Node is using the ROS2 tool :program:`ros2 run`. More information on it can be obtained through -.. code :: console +.. code-block:: console ros2 run -h - + which returns the most relevant arguments :code:`package_name` and :code:`executable_name`. .. code-block:: console @@ -32,13 +32,13 @@ which returns the most relevant arguments :code:`package_name` and :code:`execut Back to our example, with a properly sourced terminal, the example node can be executed with -.. code :: console +.. code-block:: console ros2 run python_package_with_a_node sample_python_node which will now correctly output -.. code :: console +.. code-block:: console Hi from python_package_with_a_node. @@ -48,15 +48,15 @@ Troubleshooting tips If :program:`ROS2` is unable to find the node, but it is able to find the package, then you can rely on :program:`ros2 pkg executables`. For instance, you can run as follows. -.. code :: console +.. code-block:: console ros2 pkg executables python_package_with_a_node The command, at this stage, should output the following. -.. code :: console +.. code-block:: console python_package_with_a_node sample_python_node This shows that :program:`ROS2` has been correctly able to find :file:`sample_python_node` within :file:`python_package_with_a_node`. -If the command outputs nothing, this means that no nodes were found. \ No newline at end of file +If the command outputs nothing, this means that no nodes were found. diff --git a/docs/source/sas/index.rst b/docs/source/sas/index.rst index ca57e27e..0593760e 100644 --- a/docs/source/sas/index.rst +++ b/docs/source/sas/index.rst @@ -18,5 +18,5 @@ Quick overview #. :doc:`Installation ` Setting up your system to use `SAS`. -#. :doc:`Creating a new SASRobotDriver ` +#. :doc:`Creating a new SASRobotDriver ` Creating a new subclass of `SASRobotDriver` and use it in `SAS`. First shown in `SAS` Workshop 2. diff --git a/docs/source/sas/sas_robot_driver_add_new_robot.rst b/docs/source/sas/sas_robot_driver_add_new_robot.rst index 3be2b1cf..2e582e31 100644 --- a/docs/source/sas/sas_robot_driver_add_new_robot.rst +++ b/docs/source/sas/sas_robot_driver_add_new_robot.rst @@ -33,7 +33,7 @@ which outputs .. dropdown:: ros2 pkg create output .. code-block:: console - + going to create a new package package name: sas_robot_driver_myrobot destination directory: /home/murilo/Downloads/pycharm-community-2024.3.5/bin diff --git a/docs/source/service_servers_and_clients.rst b/docs/source/service_servers_and_clients.rst index 704907f8..b9155179 100644 --- a/docs/source/service_servers_and_clients.rst +++ b/docs/source/service_servers_and_clients.rst @@ -124,19 +124,19 @@ Create the Node with a Service Server ------------------------------------- .. admonition:: **TL;DR** Creating a service server - + #. Add new dependencies to :file:`package.xml` #. Import new services :code:`from .srv import ` #. In a subclass of :code:`Node` - + #. create a callback :code:`def callback(self, request, response):` #. create a service server with :code:`self.service_server = self.create_service(...)` - + #. Add the new Node to :file:`setup.py` Let's start by creating a :file:`add_points_service_server_node.py`. -:download:`~/ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services/add_points_service_server_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services/add_points_service_server_node.py>` +:download:`add_points_service_server_node.py <../../ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services/add_points_service_server_node.py>` .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services/add_points_service_server_node.py :language: python @@ -194,16 +194,16 @@ Create the Node with a Service Client (using a :code:`callback`) #. Add new dependencies to :file:`package.xml` #. Import new services :code:`from .srv import ` #. In a subclass of :code:`Node` - + #. (*recommended*) wait for service to be available :code:`service_client.wait_for_service(...)`. #. (*if periodic*) add a :code:`Timer` with a proper :code:`timer_callback()` #. create a callback for the future :code:`def service_future_callback(self, future: Future):` #. create a Service Client with :code:`self.service_client = self.create_client(...)` - + #. Add the new Node to :file:`setup.py` The Node -^^^^^^^^ +++++++++ .. note:: This example deviates somewhat from what is done in the `official examples `_. @@ -220,9 +220,9 @@ We start by adding a :file:`add_points_service_client_node.py` at :file:`python_ :lines: 24- Imports -^^^^^^^ ++++++++ -To have access to the service, we import it with :code:`from .srv import `. +To have access to the service, we import it with :code:`from .srv import `. .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services/add_points_service_client_node.py :language: python @@ -230,7 +230,7 @@ To have access to the service, we import it with :code:`from .srv impor :emphasize-lines: 5,7 Instantiate a Service Client -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++++++ We instantiate a Service Client with :code:`Node.create_client()`. The values of :code:`srv_type` and :code:`srv_name` must match the ones used in the Service Server. @@ -239,7 +239,7 @@ We instantiate a Service Client with :code:`Node.create_client()`. The values of :lines: 39-41 (Recommended) Wait for the Service Server to be available -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .. warning:: The order of execution and speed of Nodes depend on a complicated web of relationships between ROS2, the operating system, and the workload of the machine. It would be naive to expect the server to always be active before the client, even if the server Node is started before the client Node. @@ -251,7 +251,7 @@ In many cases, having the result of the service is of particular importance (hen :lines: 43,44 Instantiate a :code:`Future` as a class attribute -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++++++++++++++++++++++++++ As part of the :code:`async` framework, we instantiate a :code:`Future` (`More info `_). In this example it is important to have it as an attribute of the class so that we do not lose the reference to it after the callback. @@ -260,7 +260,7 @@ As part of the :code:`async` framework, we instantiate a :code:`Future` (`More i :lines: 46 Instantiate a Timer -^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++ Whenever periodic work must be done, it is recommended to use a :code:`Timer`, as we already learned in :ref:`Use a Timer for periodic work`. @@ -275,7 +275,7 @@ The need for a callback for the :code:`Timer`, should also be no surprise. :lines: 53-54 Service Clients use :code:`.Request()` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++++++++++++++++++++++ Given that services work in a request-response model, the Service Client must instantiate a suitable :code:`.Request()` and populate its fields before making the service call, as shown below. To make the example more interesting, it randomly switches between two possible quotes. @@ -284,15 +284,15 @@ Given that services work in a request-response model, the Service Client must in :lines: 56-64 Make service calls with :code:`call_async()` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++++++++++++++++++++++ The :code:`async` framework in ROS2 is based on Python's :code:`asyncio` that we already saw in :ref:`Asyncio`. - + .. note:: At first glance, it might feel that all this trouble to use :code:`async` is unjustified. However, Nodes in practice will hardly ever do one service call and be done. Many Nodes in a complex system will have a composition of many service servers, service clients, publishers, and subscribers. Blocking the entire Node while it waits for the result of a service is, in most cases, a bad design. -The recommended way to call a service is through :code:`call_async()`, which is the reason why we are working with :code:`async` logic. In general, the result of :code:`call_async()`, a :code:`Future`, will not have the result of the service call at the next line of our program. +The recommended way to call a service is through :code:`call_async()`, which is the reason why we are working with :code:`async` logic. In general, the result of :code:`call_async()`, a :code:`Future`, will not have the result of the service call at the next line of our program. There are many ways to address the use of a :code:`Future`. One of them, specially tailored to interface :code:`async` with callback-based frameworks is the :code:`Future.add_done_callback()`. If the :code:`Future` is already done by the time we call :code:`add_done_callback()`, it is supposed to `call the callback for us `_. @@ -311,7 +311,7 @@ Given that we are periodically calling the service, before replace the class :co :emphasize-lines: 1-4 The Future callback -^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++ The callback for the :code:`Future` must receive a :code:`Future` as an argument. Having it as an attribute of the Node's class allows us to access ROS2 method such as :code:`get_logger()` and other contextual information. diff --git a/docs/source/source_after_build.rst b/docs/source/source_after_build.rst index 492bb600..d4e100a0 100644 --- a/docs/source/source_after_build.rst +++ b/docs/source/source_after_build.rst @@ -8,20 +8,20 @@ workspace is re-built and re-sourced. For example, if we try the following in the terminal window we used to first build this example package -.. code :: console +.. code-block:: console ros2 run python_package_with_a_node sample_python_node it will not work and will output -.. code :: console +.. code-block:: console Package 'python_package_with_a_node' not found - + As the workspace grows bigger and the packages more complex, figuring out such errors becomes a considerable hassle. My suggestion is to always source after a build, so that sourcing errors can always be ruled out. -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace colcon build @@ -83,7 +83,7 @@ Package not found One important tool to assist in case your package is not found is :program:`ros2 pkg list`. It can be called as follows. -.. code :: console +.. code-block:: console ros2 pkg list @@ -91,13 +91,13 @@ It will output a large number of packages even for the most basic installations a particular package, you can use :program:`grep` which is more actively used (and explained) in other parts of this tutorial. For instance, if you are looking for :file:`python_package_with_a_node` you can do as follows. -.. code :: console +.. code-block:: console ros2 pkg list | grep python_package_with_a_node This will either output nothing if the package is not found or it will output the name of the package, as follows. -.. code :: console +.. code-block:: console python_package_with_a_node @@ -120,7 +120,7 @@ To fix this, you must #. Deactivate the :program:`venv`. #. Remove the :file:`build`, :file:`install`, and :file:`log` folders. -#. Rebuild and resource in a clean terminal, without a :program:`venv`. +#. Rebuild and re-source in a clean terminal, without a :program:`venv`. In this tutorial, this would be equivalent to doing @@ -129,7 +129,7 @@ In this tutorial, this would be equivalent to doing Remember that :program:`rm` can cause *permanent* loss of data. Please understand the following command and its implications *before* executing it. -.. code :: console +.. code-block:: console deactivate cd ~/ros2_tutorial_workspace diff --git a/docs/source/terminator.rst b/docs/source/terminator.rst index 1e8d89c2..28b4a504 100644 --- a/docs/source/terminator.rst +++ b/docs/source/terminator.rst @@ -6,8 +6,8 @@ Terminator is life .. note:: You can refer to the `project's documentation `_ for more info. - - + + After installing :program:`terminator` as instructed in the last section, the default terminal window will be automatically updated to use it. Shortcuts @@ -35,7 +35,7 @@ Will result in three terminal windows that look like so. OK, but what if shortcuts scare me ---------------------------------- -Instead of using shortcuts, a context menu can be opened by right-clicking a terminal window. +Instead of using shortcuts, a context menu can be opened by right-clicking a terminal window. Then, you can choose to `Split Horizontally` or `Split Vertically` to achieve the same results. .. image:: ../images/terminator_right_click_menu.png diff --git a/docs/source/the_canonical_build_command.rst b/docs/source/the_canonical_build_command.rst index ff57b1bc..e828f151 100644 --- a/docs/source/the_canonical_build_command.rst +++ b/docs/source/the_canonical_build_command.rst @@ -1,4 +1,4 @@ -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace colcon build @@ -10,4 +10,4 @@ .. warning:: - :program:`colcon` will *not* work properly if your terminal has an active :program:`venv`. \ No newline at end of file + :program:`colcon` will *not* work properly if your terminal has an active :program:`venv`. diff --git a/docs/source/transformations/index.rst b/docs/source/transformations/index.rst index daedf099..a20b761b 100644 --- a/docs/source/transformations/index.rst +++ b/docs/source/transformations/index.rst @@ -164,10 +164,10 @@ Let's rip the trademarked plaster brand out. The following equation represents t :name: eq:rotation_formation \boldsymbol{r} \triangleq \cos\left(\frac{\phi}{2}\right) + \boldsymbol{v}\sin\left(\frac{\phi}{2}\right), - + where :math:`\boldsymbol{v}^2=-1`. This means that the rotation axis :math:`\boldsymbol{v}` can be any imaginary number such that :math:`||\boldsymbol{v}||=1`. - + The easiest way to think about a rotation using quaternions is to think about the axis of rotation :math:`\boldsymbol{v}` and the angle of rotation :math:`\phi`. Then, you construct the quaternion with the :ref:`rotation quaternion formation law `. @@ -323,7 +323,7 @@ Lastly, we have, for a ``rotation``, Create the package ------------------ -We will create a package to showcase the transformations from the previous section. We use ``TransformedStamped`` as +We will create a package to showcase the transformations from the previous section. We use ``TransformStamped`` as it will be useful right away, when we talk about ``tf2``. To see how this would work, programmatically, we start by creating the :file:`python_package_that_uses_geometry_msgs` package. diff --git a/docs/source/transformations/nottf2.rst b/docs/source/transformations/nottf2.rst index 2c579345..cf80fbb6 100644 --- a/docs/source/transformations/nottf2.rst +++ b/docs/source/transformations/nottf2.rst @@ -155,4 +155,4 @@ The result will be as follows. Note that the results are reasonably close to the ones we calculated mathematically. However, given the limitations on current computers related to floating point accuracy, you can **always** expect a level of inaccuracy. This is **not** -limited or affected by the use of quaternions, this is an inherent limitation of our computers. \ No newline at end of file +limited or affected by the use of quaternions, this is an inherent limitation of our computers. diff --git a/docs/source/transformations/rviz.rst b/docs/source/transformations/rviz.rst index 16d98372..2302aedc 100644 --- a/docs/source/transformations/rviz.rst +++ b/docs/source/transformations/rviz.rst @@ -123,4 +123,4 @@ The integrated sample can be executed as follows. ros2 run rqt_image_view rqt_image_view /images It should work by showing color variations of the controversial image. We will use the visualiser, not the publisher, -in some examples in future sections. \ No newline at end of file +in some examples in future sections. diff --git a/docs/source/transformations/tf2.rst b/docs/source/transformations/tf2.rst index eb143cf1..5e2a064c 100644 --- a/docs/source/transformations/tf2.rst +++ b/docs/source/transformations/tf2.rst @@ -224,7 +224,7 @@ We can show the integrated example as follows. Our broadcaster won't output anything to the screen. The output of the listener will be as long as you allow it to be. The first line indicates a normal behavior, in which -subscribers to not have instant access to topics. That is properly handled by our listener. When available, you can see +subscribers do not have instant access to topics. That is properly handled by our listener. When available, you can see the output. .. code-block:: console @@ -320,4 +320,4 @@ This should result in the following, where the important line is highlighted. There will be a degree of pointlessness when attempting to parse through the ``/tf`` output in the terminal. We will also not attempt to do that here. To help you with that, we will have visualisation and simulation tools shown in -following sections. \ No newline at end of file +following sections. diff --git a/docs/source/using_python_library.rst b/docs/source/using_python_library.rst index df0513ff..966334ba 100644 --- a/docs/source/using_python_library.rst +++ b/docs/source/using_python_library.rst @@ -1,20 +1,20 @@ Using a Python Library from another package (for :program:`ament_python`) -====================================================================== +======================================================================== -Let us create a package with a Node that uses the library we created in the prior example. +Let us create a package with a Node that uses the library we created in the prior example. Note that we must add the :code:`python_package_with_a_library` as a dependency to our new package. The easiest way to do so is through :program:`ros2 pkg create`. We also add :code:`rclpy` as a dependency so that our Node can do something useful. -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace/src ros2 pkg create python_package_that_uses_the_library \ --dependencies rclpy python_package_with_a_library \ --build-type ament_python \ --node-name node_that_uses_the_library - + resulting in yet another version of our favorite wall of text .. code-block:: console @@ -98,7 +98,7 @@ Indeed, the most difficult part is to make and configure the library itself. Aft :language: python :lines: 24-29 :emphasize-lines: 6 - + And then use the symbols we imported as we would with any other Python library. .. literalinclude:: ../../ros2_tutorial_workspace/src/python_package_that_uses_the_library/python_package_that_uses_the_library/node_that_uses_the_library.py @@ -119,13 +119,13 @@ Run .. hint:: Remember that you can stop the node at any time with :kbd:`CTRL+C`. -.. code :: console +.. code-block:: console ros2 run python_package_that_uses_the_library node_that_uses_the_library - + Which outputs something similar to the shown below, but with different numbers and strings as they are randomized. -.. code :: console +.. code-block:: console [INFO] [1753585839.509922172] [node_that_uses_the_library]: sample_function_for_square_of_sum(0.9787232004970391,1.7320908702316369) returned 7.348512926060575. [INFO] [1753585839.510400755] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned GQkUZgSkje. @@ -135,4 +135,4 @@ Which outputs something similar to the shown below, but with different numbers a [INFO] [1753585840.495950422] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned ITLIHPOMgv. [INFO] [1753585840.994468589] [node_that_uses_the_library]: sample_function_for_square_of_sum(0.5244531764161572,1.7524376840394509) returned 5.184231990426279. [INFO] [1753585840.994695797] [node_that_uses_the_library]: sample_class_with_random_name.get_name() returned LGtybBngKv. - + diff --git a/docs/source/workspace_setup.rst b/docs/source/workspace_setup.rst index 758a2c03..b084a016 100644 --- a/docs/source/workspace_setup.rst +++ b/docs/source/workspace_setup.rst @@ -12,13 +12,13 @@ In ROS2, a workspace is nothing more than a folder in which all your packages ar No, really, you just need to make a folder, e.g. the one we will use throughout these tutorials. -.. code :: console +.. code-block:: console cd ~ mkdir -p ros2_tutorial_workspace/src - + It is common practice to have all source files inside the :code:`src` folder, so we will also do so for these tutorials. Nonetheless, it is not a strict requirement. - + First build ----------- @@ -27,30 +27,30 @@ The program :program:`colcon` is the build system of ROS2 and will be described For now, run -.. code :: console +.. code-block:: console cd ~/ros2_tutorial_workspace colcon build - + for which the output will be something similar to -.. code :: console +.. code-block:: console Summary: 0 packages finished [0.08s] - + given that we have an empty workspace, no surprise here. The folders :code:`build`, :code:`install`, and :code:`log` have been generated automatically by :program:`colcon`. The project structure becomes as follows. .. code-block:: console :emphasize-lines: 2-4 - + ros2_tutorial_workspace/ |-- build |-- install |-- log `-- src - + Inside the :code:`install` folder lie everything in the project that can be accessed by the users. .. note:: @@ -60,11 +60,11 @@ Inside the :code:`install` folder lie everything in the project that can be acce Do the following just once, so that all terminal windows automatically source this new workspace for you. -.. code :: console +.. code-block:: console echo "# Source the ROS2 overlay, as instructed in https://ros2-tutorial.readthedocs.io" >> ~/.bashrc echo "source ~/ros2_tutorial_workspace/install/setup.bash" >> ~/.bashrc source ~/.bashrc - + However, since our workspace is currently empty, there's not much we can do with it. Let's add some content. - +