diff --git a/README.md b/README.md index da1001a0..7b919de6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,11 @@ # Universal Robots Client Library * [Universal Robots Client Library](#universal-robots-client-library) - * [Requirements](#requirements) * [Build instructions](#build-instructions) - * [Plain cmake](#plain-cmake) - * [Inside a ROS workspace](#inside-a-ros-workspace) * [Use this library in other projects](#use-this-library-in-other-projects) * [License](#license) * [Library contents](#library-contents) * [Example driver](#example-driver) * [Architecture](#architecture) - * [RTDEClient](#rtdeclient) - * [RTDEWriter](#rtdewriter) - * [ReverseInterface](#reverseinterface) - * [ScriptSender](#scriptsender) - * [Other public interface functions](#other-public-interface-functions) - * [check_calibration()](#check_calibration) - * [sendScript()](#sendscript) - * [DashboardClient](#dashboardclient) * [A word on the primary / secondary interface](#a-word-on-the-primary--secondary-interface) * [A word on Real-Time scheduling](#a-word-on-real-time-scheduling) * [Producer / Consumer architecture](#producer--consumer-architecture) @@ -54,67 +43,7 @@ robotic manipulators. ## Build instructions -### Plain cmake -To build this library standalone so that you can build you own applications using this library, -follow the usual cmake procedure: -```bash -cd -mkdir build && cd build -cmake .. -make -sudo make install -``` - -This will install the library into your system so that it can be used by other cmake projects -directly. - -### Inside a ROS workspace -If you want to build this library inside a ROS workspace, e.g. because you want to build the -[Universal Robots ROS driver](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver) from -source, you cannot use `catkin_make` directly, as this library is not a catkin package. Instead, you -will have to use -[`catkin_make_isolated`](http://docs.ros.org/independent/api/rep/html/rep-0134.html) or [catkin -build](https://catkin-tools.readthedocs.io/en/latest/verbs/catkin_build.html) to build your -workspace. - -## Use this library in other projects -When you want to use this library in other cmake projects, make sure to - * Add `find_package(ur_client_library REQUIRED)` to your `CMakeLists.txt` - * add `ur_client_library::urcl` to the list of `target_link_libraries(...)` commands inside your - CMakeLists.txt file - -As a minimal example, take the following "project": -```c++ -/*main.cpp*/ - -#include -#include - -int main(int argc, char* argv[]) -{ - urcl::DashboardClient my_client("192.168.56.101"); - bool connected = my_client.connect(); - if (connected) - { - std::string answer = my_client.sendAndReceive("PolyscopeVersion\n"); - std::cout << answer << std::endl; - my_client.disconnect(); - } - return 0; -} - -``` - -```cmake -# CMakeLists.txt - -cmake_minimum_required(VERSION 3.0.2) -project(minimal_example) - -find_package(ur_client_library REQUIRED) -add_executable(db_client main.cpp) -target_link_libraries(db_client ur_client_library::urcl) -``` +See [Build / installation](docs/installation.rst) ## License The majority of this library is licensed under the Apache-2.0 licensed. However, certain parts are @@ -155,121 +84,7 @@ sure to simplicity reasons it doesn't use any sophisticated method to locate the required files. ## Architecture -The image below shows a rough architecture overview that should help developers to use the different -modules present in this library. Note that this is an incomplete view on the classes involved. - -[![Data flow](doc/dataflow.svg "Data flow")](doc/dataflow.svg) - -The core of this library is the `UrDriver` class which creates a -fully functioning robot interface. For details on how to use it, please see the [Example -driver](#example-driver) section. - -The `UrDriver`'s modules will be explained in the following. - -### RTDEClient -The `RTDEClient` class serves as a standalone -[RTDE](https://www.universal-robots.com/articles/ur-articles/real-time-data-exchange-rtde-guide/) -client. To use the RTDE-Client, you'll have to initialize and start it separately: - -```c++ -rtde_interface::RTDEClient my_client(ROBOT_IP, notifier, OUTPUT_RECIPE, INPUT_RECIPE); -my_client.init(); -my_client.start(); -while (true) -{ - std::unique_ptr data_pkg = my_client.getDataPackage(READ_TIMEOUT); - if (data_pkg) - { - std::cout << data_pkg->toString() << std::endl; - } -} -``` - -Upon construction, two recipe files have to be given, one for the RTDE inputs, one for the RTDE -outputs. Please refer to the [RTDE -guide](https://www.universal-robots.com/articles/ur-articles/real-time-data-exchange-rtde-guide/) -on which elements are available. - -Inside the `RTDEclient` data is received in a separate thread, parsed by the `RTDEParser` and added -to a pipeline queue. - -Right after calling `my_client.start()`, it should be made sure to read the buffer from the -`RTDEClient` by calling `getDataPackage()` frequently. The Client's queue can only contain 1 item -at a time, so a `Pipeline producer overflowed!` error will be raised if the buffer isn't read before -the next package arrives. - -For writing data to the RTDE interface, use the `RTDEWriter` member of the `RTDEClient`. It can be -retrieved by calling `getWriter()` method. The `RTDEWriter` provides convenience methods to write -all data available at the RTDE interface. Make sure that the required keys are configured inside the -input recipe, as otherwise the send-methods will return `false` if the data field is not setup in -the recipe. - -An example of a standalone RTDE-client can be found in the `examples` subfolder. To run it make -sure to - * have an instance of a robot controller / URSim running at the configured IP address (or adapt the - address to your needs) - * run it from the package's main folder (the one where this README.md file is stored), as for - simplicity reasons it doesn't use any sophisticated method to locate the required files. - -#### RTDEWriter -The `RTDEWriter` class provides an interface to write data to the RTDE interface. Data fields that -should be written have to be defined inside the `INPUT_RECIPE` as noted above. - -The class offers specific methods for every RTDE input possible to write. - -Data is sent asynchronously to the RTDE interface. - -### ReverseInterface -The `ReverseInterface` opens a TCP port on which a custom protocol is implemented between the -robot and the control PC. The port can be specified in the class constructor. - -It's basic functionality is to send a vector of floating point data together with a mode. It is -meant to send joint positions or velocities together with a mode that tells the robot how to -interpret those values (e.g. `SERVOJ`, `SPEEDJ`). Therefore, this interface can be used to do motion -command streaming to the robot. - -In order to use this class in an application together with a robot, make sure that a corresponding -URScript is running on the robot that can interpret the commands sent. See [this example -script](resources/external_control.urscript) for reference. - -Also see the [ScriptSender](#scriptsender) for a way to define the corresponding URScript on the -control PC and sending it to the robot upon request. - -### ScriptSender - -The `ScriptSender` class opens a tcp socket on the remote PC whose single purpose it is to answer -with a URScript code snippet on a "*request_program*" request. The script code itself has to be -given to the class constructor. - -Use this class in conjunction with the [**External Control** -URCap](https://github.com/UniversalRobots/Universal_Robots_ExternalControl_URCap) which will make -the corresponding request when starting a program on the robot that contains the **External -Control** program node. In order to work properly, make sure that the IP address and script sender -port are configured correctly on the robot. - -### Other public interface functions -This section shall explain the public interface functions that haven't been covered above - -#### `check_calibration()` -This function opens a connection to the primary interface where it will receive a calibration -information as the first message. The checksum from this calibration info is compared to the one -given to this function. Connection to the primary interface is dropped afterwards. - -#### `sendScript()` -This function sends given URScript code directly to the secondary interface. The -`sendRobotProgram()` function is a special case that will send the script code given in the -`RTDEClient` constructor. - -### DashboardClient -The `DashboardClient` wraps the calls on the [Dashboard server](https://www.universal-robots.com/articles/ur-articles/dashboard-server-e-series-port-29999/) directly into C++ functions. - -After connecting to the dashboard server by using the `connect()` function, dashboard calls can be -sent using the `sendAndReceive()` function. Answers from the dashboard server will be returned as -string from this function. If no answer is received, a `UrException` is thrown. - -Note: In order to make this more useful developers are expected to wrap this bare interface into -something that checks the returned string for something that is expected. See the -[DashboardClientROS](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver/blob/master/ur_robot_driver/include/ur_robot_driver/dashboard_client_ros.h) as an example. +See [Architecture documentation](docs/architecture.rst) ## A word on the primary / secondary interface Currently, this library doesn't support the primary interface very well, as the [Universal Robots diff --git a/doc/.static b/doc/.static new file mode 100644 index 00000000..e69de29b diff --git a/doc/.templates b/doc/.templates new file mode 100644 index 00000000..e69de29b diff --git a/doc/architecture.rst b/doc/architecture.rst new file mode 100644 index 00000000..45307b1f --- /dev/null +++ b/doc/architecture.rst @@ -0,0 +1,142 @@ +Library architecture +==================== + +The image below shows a rough architecture overview that should help developers to use the different +modules present in this library. Note that this is an incomplete view on the classes involved. + +.. image:: images/dataflow.svg + :width: 100% + :alt: Data flow + + +The core of this library is the ``UrDriver`` class which creates a +fully functioning robot interface. For details on how to use it, please see the [Example +driver](#example-driver) section. + +The ``UrDriver``'s modules will be explained in the following. + +RTDEClient +---------- + +The ``RTDEClient`` class serves as a standalone +`RTDE `_ +client. To use the RTDE-Client, you'll have to initialize and start it separately: + +.. code-block:: c++ + + rtde_interface::RTDEClient my_client(ROBOT_IP, notifier, OUTPUT_RECIPE, INPUT_RECIPE); + my_client.init(); + my_client.start(); + while (true) + { + std::unique_ptr data_pkg = my_client.getDataPackage(READ_TIMEOUT); + if (data_pkg) + { + std::cout << data_pkg->toString() << std::endl; + } + } + +Upon construction, two recipe files have to be given, one for the RTDE inputs, one for the RTDE +outputs. Please refer to the `RTDE +guide `_ +on which elements are available. + +Inside the ``RTDEclient`` data is received in a separate thread, parsed by the ``RTDEParser`` and +added to a pipeline queue. + +Right after calling ``my_client.start()``, it should be made sure to read the buffer from the +``RTDEClient`` by calling ``getDataPackage()`` frequently. The Client's queue can only contain 1 +item at a time, so a ``Pipeline producer overflowed!`` error will be raised if the buffer isn't read +before the next package arrives. + +For writing data to the RTDE interface, use the ``RTDEWriter`` member of the ``RTDEClient``. It can be +retrieved by calling ``getWriter()`` method. The ``RTDEWriter`` provides convenience methods to write +all data available at the RTDE interface. Make sure that the required keys are configured inside the +input recipe, as otherwise the send-methods will return ``false`` if the data field is not setup in +the recipe. + +An example of a standalone RTDE-client can be found in the ``examples`` subfolder. To run it make +sure to + +* have an instance of a robot controller / URSim running at the configured IP address (or adapt the + address to your needs) +* run it from the package's main folder, as for simplicity reasons it doesn't use any sophisticated + method to locate the required files. + + +RTDEWriter +^^^^^^^^^^ + +The ``RTDEWriter`` class provides an interface to write data to the RTDE interface. Data fields that +should be written have to be defined inside the ``INPUT_RECIPE`` as noted above. + +The class offers specific methods for every RTDE input possible to write. + +Data is sent asynchronously to the RTDE interface. + +ReverseInterface +---------------- + +The ``ReverseInterface`` opens a TCP port on which a custom protocol is implemented between the +robot and the control PC. The port can be specified in the class constructor. + +It's basic functionality is to send a vector of floating point data together with a mode. It is +meant to send joint positions or velocities together with a mode that tells the robot how to +interpret those values (e.g. ``SERVOJ``, ``SPEEDJ``). Therefore, this interface can be used to do +motion command streaming to the robot. + +In order to use this class in an application together with a robot, make sure that a corresponding +URScript is running on the robot that can interpret the commands sent. See `this example +script <../resources/external_control.urscript>`_ for reference. + +Also see the :ref:`ScriptSender` for a way to define the corresponding URScript on the +control PC and sending it to the robot upon request. + +.. _ScriptSender: + +ScriptSender +------------ + +The ``ScriptSender`` class opens a tcp socket on the remote PC whose single purpose it is to answer +with a URScript code snippet on a "*request_program*" request. The script code itself has to be +given to the class constructor. + +Use this class in conjunction with the `External Control URCap +`_ which will make the +corresponding request when starting a program on the robot that contains the **External Control** +program node. In order to work properly, make sure that the IP address and script sender port are +configured correctly on the robot. + +Other public interface functions +-------------------------------- + +This section shall explain the public interface functions that haven't been covered above + + +check_calibration() +^^^^^^^^^^^^^^^^^^^ + +This function opens a connection to the primary interface where it will receive a calibration +information as the first message. The checksum from this calibration info is compared to the one +given to this function. Connection to the primary interface is dropped afterwards. + +sendScript() +^^^^^^^^^^^^ + +This function sends given URScript code directly to the secondary interface. The +``sendRobotProgram()`` function is a special case that will send the script code given in the +``RTDEClient`` constructor. + +DashboardClient +--------------- + +The ``DashboardClient`` wraps the calls on the `Dashboard server `_ +directly into C++ functions. + +After connecting to the dashboard server by using the ``connect()`` function, dashboard calls can be +sent using the ``sendAndReceive()`` function. Answers from the dashboard server will be returned as +string from this function. If no answer is received, a ``UrException`` is thrown. + +Note: In order to make this more useful developers are expected to wrap this bare interface into +something that checks the returned string for something that is expected. See the +`DashboardClientROS `_ as an example. diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 00000000..257f406f --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +import os +import catkin_pkg.package + +catkin_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +catkin_package = catkin_pkg.package.parse_package( + os.path.join(catkin_dir, catkin_pkg.package.PACKAGE_MANIFEST_FILENAME) +) + +# -- Project information ----------------------------------------------------- + +project = "ur_client_library" +copyright = "2022, Felix Exner" +author = "Felix Exner" + +# The short X.Y version +version = catkin_package.version +# The full version, including alpha/beta/rc tags +release = catkin_package.version + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = [".templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "agogo" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [".static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "ur_client_librarydoc" + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + master_doc, + "ur_client_library.tex", + "ur\\_client\\_library Documentation", + "Felix Exner", + "manual", + ) +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, "ur_client_library", "ur_client_library Documentation", [author], 1)] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + "ur_client_library", + "ur_client_library Documentation", + author, + "ur_client_library", + "One line description of project.", + "Miscellaneous", + ) +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] diff --git a/doc/dataflow.graphml b/doc/images/dataflow.graphml similarity index 100% rename from doc/dataflow.graphml rename to doc/images/dataflow.graphml diff --git a/doc/dataflow.svg b/doc/images/dataflow.svg similarity index 100% rename from doc/dataflow.svg rename to doc/images/dataflow.svg diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 00000000..3da73f9e --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,11 @@ +ur_client_library +================= + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + installation + polyscope_compatibility + architecture + real_time diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 00000000..9f9ec223 --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,95 @@ +Build / installation +==================== + +Plain cmake +----------- +To build this library standalone so that you can build you own +applications using this library, follow the usual cmake procedure: + +.. code:: console + + $ cd + $ mkdir build && cd build + $ cmake .. + $ make + $ sudo make install + +This will install the library into your system so that it can be used by +other cmake projects directly. + + +Inside a ROS / ROS 2 workspace +------------------------------ + +The ``ur_client_library`` is available in all maintained ROS distribution and can be installed +using + +.. code-block:: console + + $ sudo apt install ros--ur-client-library + +Unless you explicitly want to contribute to this library we recommend using the binary installation +instead of building from source as explained below. + +ROS noetic +^^^^^^^^^^ + +If you want to build this library inside a ROS workspace, e.g. because +you want to build the `Universal Robots ROS +driver `__ +from source, you cannot use ``catkin_make`` directly, as this library is +not a catkin package. Instead, you will have to use +`catkin_make_isolated `_ +or `catkin +build `__ +to build your workspace. + +ROS 2 +^^^^^ + +If you want to build this library inside a ROS 2 workspace, e.g. because +you want to build the `Universal Robots ROS2 +driver `__ +from source, simply clone this project into your workspace and build your workspace as usual. + +Use this library in other projects +---------------------------------- + +When you want to use this library in other cmake projects, make sure to + +* Add ``find_package(ur_client_library REQUIRED)`` to your ``CMakeLists.txt`` +* add ``ur_client_library::urcl`` to the list of ``target_link_libraries(...)`` commands inside your + ``CMakeLists.txt`` file + +As a minimal example, take the following “project”: + +.. code:: cpp + + /*main.cpp*/ + + #include + #include + + int main(int argc, char* argv[]) + { + urcl::DashboardClient my_client("192.168.56.101"); + bool connected = my_client.connect(); + if (connected) + { + std::string answer = my_client.sendAndReceive("PolyscopeVersion\n"); + std::cout << answer << std::endl; + my_client.disconnect(); + } + return 0; + } + +.. code:: cmake + + # CMakeLists.txt + + cmake_minimum_required(VERSION 3.0.2) + project(minimal_example) + + find_package(ur_client_library REQUIRED) + add_executable(db_client main.cpp) + target_link_libraries(db_client ur_client_library::urcl) diff --git a/doc/polyscope_compatibility.md b/doc/polyscope_compatibility.rst similarity index 62% rename from doc/polyscope_compatibility.md rename to doc/polyscope_compatibility.rst index b4e023b2..65bc23dd 100644 --- a/doc/polyscope_compatibility.md +++ b/doc/polyscope_compatibility.rst @@ -1,4 +1,6 @@ -# Polyscope version compatibility +Polyscope version compatibility +=============================== + The table below shows breaking changes in the library compared to Polyscope versions. Compatibility is listed for CB3 robots (versions 3.x.y) and e-Series robots (versions 5.x.y) respectively. @@ -11,7 +13,15 @@ If your Polyscope version is less than the minimum required version for the late we suggest to upgrade your robot's software. Please refer to the robot's user manual how to update your robot. +.. list-table:: + :header-rows: 1 + + * - Polyscope version + - Max. version + - Max. tag + - Breaking changes + * - < 3.14.3 / 5.9.4 + - 1.1.0 + - `polyscope_compat_break_1 `_ + - `tcp_offset in RTDE interface `_ -|Polyscope version | Maximum tag | Breaking changes | -|------------------|-------------|------------------| -| < 3.14.3 / 5.9.4 | [polyscope_compat_break_1](https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/polyscope_compat_break_1) | [tcp_offset in RTDE interface](https://github.com/UniversalRobots/Universal_Robots_Client_Library/pull/110)| diff --git a/doc/real_time.md b/doc/real_time.md deleted file mode 100644 index c7d02207..00000000 --- a/doc/real_time.md +++ /dev/null @@ -1,282 +0,0 @@ -# Setting up Ubuntu with a PREEMPT_RT kernel -In order to run the `universal_robot_driver`, we highly recommend to setup a ubuntu system with -real-time capabilities. Especially with a robot from the e-Series the higher control frequency -might lead to non-smooth trajectory execution if not run using a real-time-enabled system. - -You might still be able to control the robot using a non-real-time system. This is, however, not recommended. - -To get real-time support into a ubuntu system, the following steps have to be performed: - 1. Get the sources of a real-time kernel - 2. Compile the real-time kernel - 3. Setup user privileges to execute real-time tasks - -This guide will help you setup your system with a real-time kernel. - -## Preparing -To build the kernel, you will need a couple of tools available on your system. You can install them -using - -``` bash -$ sudo apt-get install build-essential bc ca-certificates gnupg2 libssl-dev wget gawk flex bison -``` - -Before you download the sources of a real-time-enabled kernel, check the kernel version that is currently installed: - -```bash -$ uname -r -4.15.0-62-generic -``` - -To continue with this tutorial, please create a temporary folder and navigate into it. You should -have sufficient space (around 25GB) there, as the extracted kernel sources take much space. After -the new kernel is installed, you can delete this folder again. - -In this example we will use a temporary folder inside our home folder: - -```bash -$ mkdir -p ${HOME}/rt_kernel_build -$ cd ${HOME}/rt_kernel_build -``` - -All future commands are expected to be run inside this folder. If the folder is different, the `$` -sign will be prefixed with a path relative to the above folder. - -## Getting the sources for a real-time kernel -To build a real-time kernel, we first need to get the kernel sources and the real-time patch. - -First, we must decide on the kernel version that we want to use. Above, we -determined that our system has a 4.15 kernel installed. However, real-time -patches exist only for selected kernel versions. Those can be found on the -[linuxfoundation wiki](https://wiki.linuxfoundation.org/realtime/preempt_rt_versions). - -In this example, we will select a 4.14 kernel. Select a kernel version close to the -one installed on your system. - -Go ahead and download the kernel sources, patch sources and their signature files: - -```bash -$ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.xz -$ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.sign -$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.xz -$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.sign -``` - -To unzip the downloaded files do -```bash -$ xz -dk patch-4.14.139-rt66.patch.xz -$ xz -d linux-4.14.139.tar.xz -``` - -### Verification -Technically, you can skip this section, it is however highly recommended to verify the file -integrity of such a core component of your system! - -To verify file integrity, you must first import public keys by the kernel developers and the patch -author. For the kernel sources use (as suggested on -[kernel.org](https://www.kernel.org/signature.html)) - -```bash -$ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org -``` - -and for the patch search for a key of the author listed on -[linuxfoundation wiki](https://wiki.linuxfoundation.org/realtime/preempt_rt_versions). - -```bash -$ gpg2 --keyserver hkp://keys.gnupg.net --search-keys zanussi -gpg: data source: http://51.38.91.189:11371 -(1) German Daniel Zanussi - 4096 bit RSA key 0x537F98A9D92CEAC8, created: 2019-07-24, expires: 2023-07-24 -(2) Michael Zanussi - 4096 bit RSA key 0x7C7F76A2C1E3D9EB, created: 2019-05-08 -(3) Tom Zanussi - Tom Zanussi - Tom Zanussi - 4096 bit RSA key 0xDE09826778A38521, created: 2017-12-15 -(4) Riccardo Zanussi - 2048 bit RSA key 0xD299A06261D919C3, created: 2014-08-27, expires: 2018-08-27 (expired) -(5) Zanussi Gianni - 1024 bit DSA key 0x78B89CB020D1836C, created: 2004-04-06 -(6) Michael Zanussi - Michael Zanussi - Michael Zanussi - Michael Zanussi - 1024 bit DSA key 0xB3E952DCAC653064, created: 2000-09-05 -(7) Michael Zanussi - 1024 bit DSA key 0xEB10BBD9BA749318, created: 1999-05-31 -(8) Michael B. Zanussi - 1024 bit DSA key 0x39EE4EAD7BBB1E43, created: 1998-07-16 -Keys 1-8 of 8 for "zanussi". Enter number(s), N)ext, or Q)uit > 3 -``` - -Now we can verify the downloaded sources: -```bash -$ gpg2 --verify linux-4.14.139.tar.sign -gpg: assuming signed data in 'linux-4.14.139.tar' -gpg: Signature made Fr 16 Aug 2019 10:15:17 CEST -gpg: using RSA key 647F28654894E3BD457199BE38DBBDC86092693E -gpg: Good signature from "Greg Kroah-Hartman " [unknown] -gpg: WARNING: This key is not certified with a trusted signature! -gpg: There is no indication that the signature belongs to the owner. -Primary key fingerprint: 647F 2865 4894 E3BD 4571 99BE 38DB BDC8 6092 693E - -$ gpg2 --verify patch-4.14.139-rt66.patch.sign -gpg: assuming signed data in 'patch-4.14.139-rt66.patch' -gpg: Signature made Fr 23 Aug 2019 21:09:20 CEST -gpg: using RSA key 0x0129F38552C38DF1 -gpg: Good signature from "Tom Zanussi " [unknown] -gpg: aka "Tom Zanussi " [unknown] -gpg: aka "Tom Zanussi " [unknown] -gpg: WARNING: This key is not certified with a trusted signature! -gpg: There is no indication that the signature belongs to the owner. -Primary key fingerprint: 5BDF C45C 2ECC 5387 D50C E5EF DE09 8267 78A3 8521 - Subkey fingerprint: ACF8 5F98 16A8 D5F0 96AE 1FD2 0129 F385 52C3 8DF1 -``` - -## Compilation -Before we can compile the sources, we have to extract the tar archive and apply the patch - -```bash -$ tar xf linux-4.14.139.tar -$ cd linux-4.14.139 -linux-4.14.139$ xzcat ../patch-4.14.139-rt66.patch.xz | patch -p1 -``` - -Now to configure your kernel, just type -```bash -linux-4.14.139$ make oldconfig -``` - -This will ask for kernel options. For everything else then the `Preemption Model` use the default -value (just press Enter) or adapt to your preferences. For the preemption model select `Fully Preemptible Kernel`: - -```bash -Preemption Model - 1. No Forced Preemption (Server) (PREEMPT_NONE) -> 2. Voluntary Kernel Preemption (Desktop) (PREEMPT_VOLUNTARY) - 3. Preemptible Kernel (Low-Latency Desktop) (PREEMPT__LL) (NEW) - 4. Preemptible Kernel (Basic RT) (PREEMPT_RTB) (NEW) - 5. Fully Preemptible Kernel (RT) (PREEMPT_RT_FULL) (NEW) -choice[1-5]: 5 -``` - -Now you can build the kernel. This will take some time... - -```bash -linux-4.14.139$ make -j `getconf _NPROCESSORS_ONLN` deb-pkg -``` - -After building, install the `linux-headers` and `linux-image` packages in the parent folder (only -the ones without the `-dbg` in the name) - -```bash -linux-4.14.139$ sudo apt install ../linux-headers-4.14.139-rt66_*.deb ../linux-image-4.14.139-rt66_*.deb -``` - -## Setup user privileges to use real-time scheduling -To be able to schedule threads with user privileges (what the driver will do) you'll have to change -the user's limits by changing `/etc/security/limits.conf` (See [the manpage](https://manpages.ubuntu.com/manpages/bionic/man5/limits.conf.5.html) for details) - -We recommend to setup a group for real-time users instead of writing a fixed username into the config -file: - -```bash -$ sudo groupadd realtime -$ sudo usermod -aG realtime $(whoami) -``` - -Then, make sure `/etc/security/limits.conf` contains -``` -@realtime soft rtprio 99 -@realtime soft priority 99 -@realtime soft memlock 102400 -@realtime hard rtprio 99 -@realtime hard priority 99 -@realtime hard memlock 102400 -``` - -Note: You will have to log out and log back in (Not only close your terminal window) for these -changes to take effect. No need to do this now, as we will reboot later on, anyway. - -## Setup GRUB to always boot the real-time kernel -To make the new kernel the default kernel that the system will boot into every time, you'll have to -change the grub config file inside `/etc/default/grub`. - -Note: This works for ubuntu, but might not be working for other linux systems. It might be necessary -to use another menuentry name there. - -But first, let's find out the name of the entry that we will want to make the default. You can list -all available kernels using - -```bash -$ awk -F\' '/menuentry |submenu / {print $1 $2}' /boot/grub/grub.cfg - -menuentry Ubuntu -submenu Advanced options for Ubuntu - menuentry Ubuntu, with Linux 4.15.0-62-generic - menuentry Ubuntu, with Linux 4.15.0-62-generic (recovery mode) - menuentry Ubuntu, with Linux 4.15.0-60-generic - menuentry Ubuntu, with Linux 4.15.0-60-generic (recovery mode) - menuentry Ubuntu, with Linux 4.15.0-58-generic - menuentry Ubuntu, with Linux 4.15.0-58-generic (recovery mode) - menuentry Ubuntu, with Linux 4.14.139-rt66 - menuentry Ubuntu, with Linux 4.14.139-rt66 (recovery mode) -menuentry Memory test (memtest86+) -menuentry Memory test (memtest86+, serial console 115200) -menuentry Windows 7 (on /dev/sdc2) -menuentry Windows 7 (on /dev/sdc3) -``` - -From the output above, we'll need to generate a string with the pattern `"submenu_name>entry_name"`. In our case this would be - -``` -"Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66" -``` -**The double quotes and no spaces around the `>` are important!** - -With this, we can setup the default grub entry and then update the grub menu entries. Don't forget this last step! - -```bash -$ sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66"/' /etc/default/grub -$ sudo update-grub -``` - -## Reboot the PC -After having performed the above mentioned steps, reboot the PC. It should boot into the correct -kernel automatically. - -## Check for preemption capabilities -Make sure that the kernel does indeed support real-time scheduling: - -```bash -$ uname -v | cut -d" " -f1-4 -#1 SMP PREEMPT RT -``` - -## Optional: Disable CPU speed scaling -Many modern CPUs support changing their clock frequency dynamically depending on the currently -requested computation resources. In some cases this can lead to small interruptions in execution. -While the real-time scheduled controller thread should be unaffected by this, any external -components such as a visual servoing system might be interrupted for a short period on scaling -changes. - -To check and modify the power saving mode, install cpufrequtils: -```bash -$ sudo apt install cpufrequtils -``` - -Run `cpufreq-info` to check available "governors" and the current CPU Frequency (`current CPU -frequency is XXX MHZ`). In the following we will set the governor to "performance". - -```bash -$ sudo systemctl disable ondemand -$ sudo systemctl enable cpufrequtils -$ sudo sh -c 'echo "GOVERNOR=performance" > /etc/default/cpufrequtils' -$ sudo systemctl daemon-reload && sudo systemctl restart cpufrequtils -``` - -This disables the `ondemand` CPU scaling daemon, creates a `cpufrequtils` config file and restarts -the `cpufrequtils` service. Check with `cpufreq-info`. - -For further information about governors, please see the [kernel -documentation](https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt). diff --git a/doc/real_time.rst b/doc/real_time.rst new file mode 100644 index 00000000..0471d47c --- /dev/null +++ b/doc/real_time.rst @@ -0,0 +1,334 @@ +.. _real time setup: + +Setup for real-time scheduling +============================== + +In order to run the ``universal_robot_driver``, we highly recommend to setup a ubuntu system with +real-time capabilities. Especially with a robot from the e-Series the higher control frequency +might lead to non-smooth trajectory execution if not run using a real-time-enabled system. + +You might still be able to control the robot using a non-real-time system. This is, however, not recommended. + +While the best-performing strategy would be to use a real-time enabled kernel, using a lowlatency +kernel has shown to be sufficient in many situations which is why this is also shown as an option +here. + +Installing a lowlatency-kernel +------------------------------ + +Installing a lowlatency kernel is pretty straightforward: + +.. code-block:: console + + $ sudo apt install linux-lowlatency + +Setting up Ubuntu with a PREEMPT_RT kernel +------------------------------------------ + +To get real-time support into a ubuntu system, the following steps have to be performed: + +#. Get the sources of a real-time kernel +#. Compile the real-time kernel +#. Setup user privileges to execute real-time tasks + +This guide will help you setup your system with a real-time kernel. + +Preparing +^^^^^^^^^ + +To build the kernel, you will need a couple of tools available on your system. You can install them +using + +.. code-block:: console + + $ sudo apt-get install build-essential bc ca-certificates gnupg2 libssl-dev wget gawk flex bison libelf-dev dwarves + + +.. note:: + + For different kernel versions the dependencies might be different than that. If you experience + problems such as ``fatal error: liXYZ.h: No such file or directory`` during compilation, try to + install the library's corresponding ``dev``-package. + +Before you download the sources of a real-time-enabled kernel, check the kernel version that is currently installed: + +.. code-block:: console + + $ uname -r + 5.15.0-107-generic + +To continue with this tutorial, please create a temporary folder and navigate into it. You should +have sufficient space (around 25GB) there, as the extracted kernel sources take much space. After +the new kernel is installed, you can delete this folder again. + +In this example we will use a temporary folder inside our home folder: + +.. code-block:: console + + $ mkdir -p ${HOME}/rt_kernel_build + $ cd ${HOME}/rt_kernel_build + +All future commands are expected to be run inside this folder. If the folder is different, the ``$`` +sign will be prefixed with a path relative to the above folder. + +Getting the sources for a real-time kernel +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To build a real-time kernel, we first need to get the kernel sources and the real-time patch. + +First, we must decide on the kernel version that we want to use. Above, we +determined that our system has a 5.15 kernel installed. However, real-time +patches exist only for selected kernel versions. Those can be found on the +`linuxfoundation wiki `_. + +In this example, we will select a 5.15.158 kernel with RT patch version 76. Select a kernel version close to the +one installed on your system. For easier reference later on we will export version information to +our shell environment. Make sure to execute all following commands in this shell. + +.. code-block:: console + + $ export KERNEL_MAJOR_VERSION=5 + $ export KERNEL_MINOR_VERSION=15 + $ export KERNEL_PATCH_VERSION=158 + $ export RT_PATCH_VERSION=76 + $ export KERNEL_VERSION="$KERNEL_MAJOR_VERSION.$KERNEL_MINOR_VERSION.$KERNEL_PATCH_VERSION" + +Go ahead and download the kernel sources, patch sources and their signature files: + +.. code-block:: console + + $ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/$KERNEL_MAJOR_VERSION.$KERNEL_MINOR_VERSION/patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.xz + $ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/$KERNEL_MAJOR_VERSION.$KERNEL_MINOR_VERSION/patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.sign + $ wget https://www.kernel.org/pub/linux/kernel/v$KERNEL_MAJOR_VERSION.x/linux-$KERNEL_VERSION.tar.xz + $ wget https://www.kernel.org/pub/linux/kernel/v$KERNEL_MAJOR_VERSION.x/linux-$KERNEL_VERSION.tar.sign + +To unzip the downloaded files do + +.. code-block:: console + + $ xz -dk patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.xz + $ xz -d linux-$KERNEL_VERSION.tar.xz + +Verification +~~~~~~~~~~~~ + +Technically, you can skip this section, it is however highly recommended to verify the file +integrity of such a core component of your system! + +To verify file integrity, you must first import public keys by the kernel developers and the patch +author. For the kernel sources use (as suggested on +`kernel.org `_\ ) + +.. code-block:: console + + $ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org + +and for the patch view the gpg information + +.. code-block:: console + + $ gpg2 --verify patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.sign + gpg: assuming signed data in 'patch-5.15.158-rt76.patch' + gpg: Signature made Fri May 3 17:12:45 2024 UTC + gpg: using RSA key AD85102A6BE1CDFE9BCA84F36CEF3D27CA5B141E + gpg: Can't check signature: No public key + +So, we need to import the key using + +.. code-block:: console + + gpg2 --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys AD85102A6BE1CDFE9BCA84F36CEF3D27CA5B141E + + +Now we can verify the downloaded sources: + +.. code-block:: console + + $ gpg2 --verify linux-$KERNEL_VERSION.tar.sign + gpg: assuming signed data in 'linux-5.15.158.tar' + gpg: Signature made Thu May 2 14:28:07 2024 UTC + gpg: using RSA key 647F28654894E3BD457199BE38DBBDC86092693E + gpg: Good signature from "Greg Kroah-Hartman " [unknown] + gpg: WARNING: This key is not certified with a trusted signature! + gpg: There is no indication that the signature belongs to the owner. + Primary key fingerprint: 647F 2865 4894 E3BD 4571 99BE 38DB BDC8 6092 693E + +and + +.. code-block:: console + + $ gpg2 --verify patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.sign + gpg: assuming signed data in 'patch-5.15.158-rt76.patch' + gpg: Signature made Fri May 3 17:12:45 2024 UTC + gpg: using RSA key AD85102A6BE1CDFE9BCA84F36CEF3D27CA5B141E + gpg: Good signature from "Joseph Salisbury " [unknown] + gpg: aka "Joseph Salisbury " [unknown] + gpg: aka "Joseph Salisbury " [unknown] + gpg: WARNING: This key is not certified with a trusted signature! + gpg: There is no indication that the signature belongs to the owner. + Primary key fingerprint: AD85 102A 6BE1 CDFE 9BCA 84F3 6CEF 3D27 CA5B 141E + + +Compilation +^^^^^^^^^^^ + +Before we can compile the sources, we have to extract the tar archive and apply the patch + +.. code-block:: console + + $ tar xf linux-$KERNEL_VERSION.tar + $ cd linux-$KERNEL_VERSION + $ xzcat ../patch-$KERNEL_VERSION-rt$RT_PATCH_VERSION.patch.xz | patch -p1 + +Now to configure your kernel, just type + +.. code-block:: console + + $ make oldconfig + +This will ask for kernel options. For everything else then the ``Preemption Model`` use the default +value (just press Enter) or adapt to your preferences. For the preemption model select ``Fully Preemptible Kernel``\ : + +.. code-block:: console + + Preemption Model + 1. No Forced Preemption (Server) (PREEMPT_NONE) + > 2. Voluntary Kernel Preemption (Desktop) (PREEMPT_VOLUNTARY) + 3. Preemptible Kernel (Low-Latency Desktop) (PREEMPT) + 4. Fully Preemptible Kernel (Real-Time) (PREEMPT_RT) (NEW) + choice[1-4?]: 4 + +On newer kernels you need to disable some key checking: + +.. code-block:: console + + $ scripts/config --disable SYSTEM_TRUSTED_KEYS + $ scripts/config --disable SYSTEM_REVOCATION_KEYS + +Now you can build the kernel. This will take some time... + +.. code-block:: console + + $ make -j `getconf _NPROCESSORS_ONLN` deb-pkg + +After building, install the ``linux-headers`` and ``linux-image`` packages in the parent folder (only +the ones without the ``-dbg`` in the name) + +.. code-block:: console + + $ sudo apt install ../linux-headers-$KERNEL_VERSION-rt$RT_PATCH_VERSION*.deb \ + ../linux-image-$KERNEL_VERSION-rt$RT_PATCH_VERSION*.deb + +Setup user privileges to use real-time scheduling +------------------------------------------------- + +To be able to schedule threads with user privileges (what the driver will do) you'll have to change +the user's limits by changing ``/etc/security/limits.conf`` (See `the manpage `_ for details) + +We recommend to setup a group for real-time users instead of writing a fixed username into the config +file: + +.. code-block:: console + + $ sudo groupadd realtime + $ sudo usermod -aG realtime $(whoami) + +Then, make sure ``/etc/security/limits.conf`` contains + +.. code-block:: + + @realtime soft rtprio 99 + @realtime soft priority 99 + @realtime soft memlock 102400 + @realtime hard rtprio 99 + @realtime hard priority 99 + @realtime hard memlock 102400 + +Note: You will have to log out and log back in (Not only close your terminal window) for these +changes to take effect. No need to do this now, as we will reboot later on, anyway. + +Setup GRUB to always boot the lowlatency / real-time kernel +----------------------------------------------------------- + +To make the new kernel the default kernel that the system will boot into every time, you'll have to +change the grub config file inside ``/etc/default/grub``. + +Note: This works for ubuntu, but might not be working for other linux systems. It might be necessary +to use another menuentry name there. + +But first, let's find out the name of the entry that we will want to make the default. You can list +all available kernels using + +.. code-block:: console + + $ awk -F\' '/menuentry |submenu / {print $1 $2}' /boot/grub/grub.cfg + menuentry Ubuntu + submenu Advanced options for Ubuntu + menuentry Ubuntu, with Linux 5.15.158-rt76 + menuentry Ubuntu, with Linux 5.15.158-rt76 (recovery mode) + menuentry Ubuntu, with Linux 5.15.0-107-lowlatency + menuentry Ubuntu, with Linux 5.15.0-107-lowlatency (recovery mode) + menuentry Ubuntu, with Linux 5.15.0-107-generic + menuentry Ubuntu, with Linux 5.15.0-107-generic (recovery mode) + +From the output above, we'll need to generate a string with the pattern ``"submenu_name>entry_name"``. In our case this would be + +.. code-block:: + + "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.158-rt76" + +**The double quotes and no spaces around the** ``>`` **are important!** + +With this, we can setup the default grub entry and then update the grub menu entries. Don't forget this last step! + +.. code-block:: console + + $ sudo sed -i "s/^GRUB_DEFAULT=.*/GRUB_DEFAULT=\"Advanced options for Ubuntu>Ubuntu, with Linux ${KERNEL_VERSION}-rt${RT_PATCH_VERSION}\"/" /etc/default/grub + $ sudo update-grub + +Reboot the PC +------------- + +After having performed the above mentioned steps, reboot the PC. It should boot into the correct +kernel automatically. + +Check for preemption capabilities +--------------------------------- + +Make sure that the kernel does indeed support real-time scheduling: + +.. code-block:: console + + $ uname -v | cut -d" " -f1-4 + #1 SMP PREEMPT_RT Tue + +Optional: Disable CPU speed scaling +----------------------------------- + +Many modern CPUs support changing their clock frequency dynamically depending on the currently +requested computation resources. In some cases this can lead to small interruptions in execution. +While the real-time scheduled controller thread should be unaffected by this, any external +components such as a visual servoing system might be interrupted for a short period on scaling +changes. + +To check and modify the power saving mode, install cpufrequtils: + +.. code-block:: console + + $ sudo apt install cpufrequtils + +Run ``cpufreq-info`` to check available "governors" and the current CPU Frequency (\ ``current CPU +frequency is XXX MHZ``\ ). In the following we will set the governor to "performance". + +.. code-block:: console + + $ sudo systemctl disable ondemand + $ sudo systemctl enable cpufrequtils + $ sudo sh -c 'echo "GOVERNOR=performance" > /etc/default/cpufrequtils' + $ sudo systemctl daemon-reload && sudo systemctl restart cpufrequtils + +This disables the ``ondemand`` CPU scaling daemon, creates a ``cpufrequtils`` config file and restarts +the ``cpufrequtils`` service. Check with ``cpufreq-info``. + +For further information about governors, please see the `kernel +documentation `_. diff --git a/package.xml b/package.xml index 6f63fbe7..89530e17 100644 --- a/package.xml +++ b/package.xml @@ -25,6 +25,7 @@ cmake + diff --git a/rosdoc.yaml b/rosdoc.yaml new file mode 100644 index 00000000..f68ad833 --- /dev/null +++ b/rosdoc.yaml @@ -0,0 +1,7 @@ +- builder: sphinx + sphinx_root_dir: docs +- builder: doxygen + name: C++ API + output_dir: c++ + file_patterns: '*.c *.cpp *.h *.cc *.hh *.dox *.md' + use_mdfile_as_mainpage: README.md diff --git a/src/log.cpp b/src/log.cpp index 29c35de0..54fd6b12 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -40,7 +40,7 @@ class Logger public: Logger() { - log_level_ = LogLevel::WARN; + log_level_ = LogLevel::INFO; log_handler_.reset(new DefaultLogHandler()); }