From fb163f69ea987b96229236449de46e93bfb3bbb0 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 11 Sep 2024 09:31:15 +0200 Subject: [PATCH] Imported upstream version '1.4.0' of 'upstream' --- .github/helpers/check_urls.sh | 10 +- .github/workflows/ci.yml | 1 - .markdownlint.yaml | 5 + .pre-commit-config.yaml | 11 +- CHANGELOG.rst | 11 + README.md | 322 ++++----------- doc/architecture.rst | 142 +++++++ doc/conf.py | 184 +++++++++ doc/example.rst | 12 + doc/{ => images}/dataflow.graphml | 0 doc/{ => images}/dataflow.svg | 0 doc/index.rst | 12 + doc/installation.rst | 95 +++++ ...ibility.md => polyscope_compatibility.rst} | 18 +- doc/real_time.md | 282 ------------- doc/real_time.rst | 334 ++++++++++++++++ examples/full_driver.cpp | 48 ++- include/ur_client_library/rtde/rtde_writer.h | 7 +- include/ur_client_library/ur/datatypes.h | 7 + package.xml | 3 +- resources/external_control.urscript | 204 ++++++++-- rosdoc.yaml | 7 + src/log.cpp | 2 +- src/rtde/rtde_writer.cpp | 14 +- tests/test_rtde_writer.cpp | 83 +++- tests/test_spline_interpolation.cpp | 378 +++++++++++++++++- tests/test_ur_driver.cpp | 133 +++++- 27 files changed, 1687 insertions(+), 638 deletions(-) create mode 100644 .markdownlint.yaml create mode 100644 doc/architecture.rst create mode 100644 doc/conf.py create mode 100644 doc/example.rst rename doc/{ => images}/dataflow.graphml (100%) rename doc/{ => images}/dataflow.svg (100%) create mode 100644 doc/index.rst create mode 100644 doc/installation.rst rename doc/{polyscope_compatibility.md => polyscope_compatibility.rst} (62%) delete mode 100644 doc/real_time.md create mode 100644 doc/real_time.rst create mode 100644 rosdoc.yaml diff --git a/.github/helpers/check_urls.sh b/.github/helpers/check_urls.sh index 81f73ea..2bccc8d 100755 --- a/.github/helpers/check_urls.sh +++ b/.github/helpers/check_urls.sh @@ -7,17 +7,21 @@ urls=$(grep -oP "(http|ftp|https):\/\/([a-zA-Z0-9_-]+(?:(?:\.[a-zA-Z0-9_-]+)+))( fail_counter=0 +FAILED_LINKS=() for item in $urls; do # echo $item filename=$(echo "$item" | cut -d':' -f1) url=$(echo "$item" | cut -d':' -f2-) - echo "Checking $url from file $filename" + echo -n "Checking $url from file $filename" if ! curl --head --silent --fail "$url" 2>&1 > /dev/null; then - echo "Invalid link in file $filename: $url" + echo -e " \033[0;31mNOT FOUND\033[32m\n" + FAILED_LINKS+=("$url from file $filename") ((fail_counter=fail_counter+1)) else - echo "$url ok" + printf " \033[32mok\033[0m\n" fi done +echo "Failed files:" +printf '%s\n' "${FAILED_LINKS[@]}" exit $fail_counter diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb0055c..f679a5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,6 @@ jobs: --exclude-dir=CMakeModules \ --exclude=tcp_socket.cpp \ --exclude-dir=debian \ - --exclude=real_time.md \ --exclude=dataflow.graphml \ --exclude=start_ursim.sh diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..d4d7680 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,5 @@ +--- +MD013: + line_length: 100 +MD033: + allowed_elements: [img, a] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13fb129..1d98db0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,4 +2,13 @@ repos: - repo: https://github.com/pre-commit/mirrors-clang-format rev: 'v14.0.6' hooks: - - id: clang-format + - id: clang-format + - repo: https://github.com/DavidAnson/markdownlint-cli2 + rev: v0.13.0 + hooks: + - id: markdownlint-cli2 + exclude: "include/ur_client_library/queue/LICENSE.md" + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v0.9.1 + hooks: + - id: sphinx-lint diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 436dcab..a09d1e0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,17 @@ Changelog for package ur_client_library ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.4.0 (2024-09-10) +------------------ +* Ensure that the targets are reachable within the robots limits (`#184 `_) +* Analog domain (`#211 `_) +* Fix clang compilation error (`#210 `_) +* Moved reset of speed slider to correct teardown function, so that it … (`#206 `_) + …resets between each test. +* [doc] Fix syntax in example.rst (`#207 `_) +* [doc] Migrate documentation to sphinx (`#95 `_) +* Contributors: Felix Exner, Mads Holm Peters, Remi Siffert, URJala + 1.3.7 (2024-06-03) ------------------ * [ci] Update CI diff --git a/README.md b/README.md index da1001a..bc042cf 100644 --- a/README.md +++ b/README.md @@ -1,293 +1,101 @@ # 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) - * [Logging configuration](#logging-configuration) - * [Change logging level](#change-logging-level) - * [Create new log handler](#create-new-log-handler) - * [Console_bridge](#console_bridge) - * [Acknowledgement](#acknowledgement) - A C++ library for accessing Universal Robots interfaces. With this library C++-based drivers can be implemented in order to create external applications leveraging the versatility of Universal Robots robotic manipulators. ## Requirements - * **Polyscope** (The software running on the robot controller) version **3.14.3** (for CB3-Series), - or **5.9.4** (for e-Series) or higher. If you use an older Polyscope version it is suggested to - update your robot. If for some reason (please tell us in the issues why) you cannot upgrade your - robot, please see the [version compatibility table](doc/polyscope_compatibility.md) for a - compatible tag. - * The library requires an implementation of **POSIX threads** such as the `pthread` library - * Socket communication is currently based on Linux sockets. Thus, this library will require Linux - for building and using. - * The [master](https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/master) - branch of this repository requires a C++17-compatible compiler. For building this library without - a C++17-requirement, please use the - [boost](https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/boost) branch - instead that requires the boost library. - For the C++17 features, please use those minimum compiler versions: - - | Compiler | min. version | - |-----------|--------------| - | **GCC** | 7 | - | **Clang** | 7 | +* **Polyscope** (The software running on the robot controller) version **3.14.3** (for CB3-Series), + or **5.9.4** (for e-Series) or higher. If you use an older Polyscope version it is suggested to + update your robot. If for some reason (please tell us in the issues why) you cannot upgrade your + robot, please see the [version compatibility table](doc/polyscope_compatibility.rst) for a + compatible tag. +* The library requires an implementation of **POSIX threads** such as the `pthread` library +* Socket communication is currently based on Linux sockets. Thus, this library will require Linux + for building and using. +* The [master](https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/master) + branch of this repository requires a C++17-compatible compiler. For building this library without + a C++17-requirement, please use the + [boost](https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/boost) branch + instead that requires the boost library. ## 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](doc/installation.rst) ## License + The majority of this library is licensed under the Apache-2.0 licensed. However, certain parts are licensed under different licenses: - - The queue used inside the communication structures is originally written by Cameron Desrochers - and is released under the BSD-2-Clause license. - - The semaphore implementation used inside the queue implementation is written by Jeff Preshing and - licensed under the zlib license - - The Dockerfile used for the integration tests of this repository is originally written by Arran - Hobson Sayers and released under the MIT license + +* The queue used inside the communication structures is originally written by Cameron Desrochers + and is released under the BSD-2-Clause license. +* The semaphore implementation used inside the queue implementation is written by Jeff Preshing and + licensed under the zlib license +* The Dockerfile used for the integration tests of this repository is originally written by Arran + Hobson Sayers and released under the MIT license While the main `LICENSE` file in this repository contains the Apache-2.0 license used for the majority of the work, the respective libraries of third-party components reside together with the code imported from those third parties. ## Library contents + Currently, this library contains the following components: - * **Basic primary interface:** The primary interface isn't fully implemented at the current state - and provides only basic functionality. See [A word on the primary / secondary - interface](#a-word-on-the-primary--secondary-interface) for further information about the primary - interface. - * **RTDE interface:** The [RTDE interface](https://www.universal-robots.com/articles/ur-articles/real-time-data-exchange-rtde-guide/) - is fully supported by this library. See [RTDEClient](#rtdeclient) for further information on how - to use this library as an RTDE client. - * **Dashboard interface:** The [Dashboard server](https://www.universal-robots.com/articles/ur-articles/dashboard-server-e-series-port-29999/) can be accessed directly from C++ through helper functions using this library. - * **Custom motion streaming:** This library was initially developed as part of the [Universal - Robots ROS driver](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver). Therefore, it - also contains a mechanism to do data streaming through a custom socket, e.g. to perform motion - command streaming. + +* **Basic primary interface:** The primary interface isn't fully implemented at the current state + and provides only basic functionality. See [A word on the primary / secondary + interface](#a-word-on-the-primary--secondary-interface) for further information about the primary + interface. +* **RTDE interface:** The [RTDE interface](https://www.universal-robots.com/articles/ur-articles/real-time-data-exchange-rtde-guide/) + is fully supported by this library. See + [RTDEClient](https://docs.universal-robots.com/Universal_Robots_ROS_Documentation/doc/ur_client_library/doc/architecture.html#rtdeclient) + for further information on how + to use this library as an RTDE client. +* **Dashboard interface:** The [Dashboard + server](https://www.universal-robots.com/articles/ur-articles/dashboard-server-e-series-port-29999/) + can be accessed directly from C++ through helper functions using this library. +* **Custom motion streaming:** This library was initially developed as part of the [Universal + Robots ROS driver](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver). Therefore, it + also contains a mechanism to do data streaming through a custom socket, e.g. to perform motion + command streaming. ## Example driver + In the `examples` subfolder you will find a minimal example of a running driver. It starts an instance of the `UrDriver` class and prints the RTDE values read from the controller. 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. - -## 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. +* 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. -### 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. +## Architecture -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](doc/architecture.rst) ## A word on the primary / secondary interface + Currently, this library doesn't support the primary interface very well, as the [Universal Robots ROS driver](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver) was built mainly upon the RTDE interface. Therefore, there is also no `PrimaryClient` for directly accessing the primary interface. This may change in future, though. -The `comm::URStream` class can be used to open a connection to the primary / secondary interface and -send data to it. The [producer/consumer](#producer--consumer-architecture) pipeline structure can also be used -together with the primary / secondary interface. However, package parsing isn't implemented for most -packages currently. See the [`primary_pipeline` example](examples/primary_pipeline.cpp) on details -how to set this up. Note that when running this example, most packages will just be printed as their -raw byte streams in a hex notation, as they aren't implemented in the library, yet. +The `comm::URStream` class can be used to open a connection to the primary / secondary interface +and send data to it. The [producer/consumer](#producer--consumer-architecture) pipeline structure +can also be used together with the primary / secondary interface. However, package parsing isn't +implemented for most packages currently. See the [`primary_pipeline` +example](examples/primary_pipeline.cpp) on details how to set this up. Note that when running this +example, most packages will just be printed as their raw byte streams in a hex notation, as they +aren't implemented in the library, yet. ## A word on Real-Time scheduling + As mentioned above, for a clean operation it is quite critical that arriving RTDE messages are read before the next message arrives. Due to this, both, the RTDE receive thread and the thread calling -`getDataPackage()` should be scheduled with real-time priority. See [this guide](doc/real_time.md) +`getDataPackage()` should be scheduled with real-time priority. See [this guide](doc/real_time.rst) for details on how to set this up. The RTDE receive thread will be scheduled to real-time priority automatically, if applicable. If @@ -297,6 +105,7 @@ scheduled to real-time priority by the application. See the as an example. ## Producer / Consumer architecture + Communication with the primary / secondary and RTDE interfaces is designed to use a consumer/producer pattern. The Producer reads data from the socket whenever it comes in, parses the contents and stores the parsed packages into a pipeline queue. @@ -304,17 +113,20 @@ You can write your own consumers that use the packages coming from the producer. [`comm::ShellConsumer`](include/ur_client_library/comm/shell_consumer.h) as an example. ## Logging configuration + As this library was originally designed to be included into a ROS driver but also to be used as a standalone library, it uses custom logging macros instead of direct `printf` or `std::cout` statements. The macro based interface is by default using the [`DefaultLogHandler`](include/ur_client_library/default_log_handler.h) to print the logging messages as `printf` statements. It is possible to define your own log handler -to change the behavior, [see create new log handler](#Create-new-log-handler) on how to. +to change the behavior, [see create new log handler](#create-new-log-handler) on how to. ### Change logging level + Make sure to set the logging level in your application, as by default only messages of level WARNING or higher will be printed. See below for an example: + ```c++ #include "ur_client_library/log.h" @@ -328,6 +140,7 @@ int main(int argc, char* argv[]) ``` ### Create new log handler + The logger comes with an interface [`LogHandler`](include/ur_client_library/log.h), which can be used to implement your own log handler for messages logged with this library. This can be done by inheriting from the `LogHandler class`. @@ -382,12 +195,15 @@ int main(int argc, char* argv[]) ``` ## Contributor Guidelines + * This repo supports [pre-commit](https://pre-commit.com/) e.g. for automatic code formatting. TLDR: This will prevent you from committing falsely formatted code: - ``` + + ``` bash pipx install pre-commit pre-commit install ``` + * Succeeding pipelines are a must on Pull Requests (unless there is a reason, e.g. when there have been upstream changes). * We try to increase and keep our code coverage high, so PRs with new @@ -395,11 +211,13 @@ features should also have tests covering them. * Parameters of public methods must all be documented. ## Acknowledgment + Many parts of this library are forked from the [ur_modern_driver](https://github.com/ros-industrial/ur_modern_driver). Developed in collaboration between: -[Universal Robots A/S](https://www.universal-robots.com/)   and   +[Universal Robots A/S](https://www.universal-robots.com/) +  and   [FZI Research Center for Information Technology](https://www.fzi.de).