diff --git a/.github/helpers/check_urls.sh b/.github/helpers/check_urls.sh new file mode 100755 index 000000000..f7222d115 --- /dev/null +++ b/.github/helpers/check_urls.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -e + +IGNORE_FILES="" +IGNORE_PATTERNS="" + +while getopts ":f:d:p:" opt; do + case "${opt}" in + f) + IGNORE_FILES="${OPTARG}";; + d) + IGNORE_DIRS="${OPTARG}";; + p) + IGNORE_PATTERNS="${OPTARG}";; + \?) + echo "Invalid option -$OPTARG" + exit;; + esac +done + +read -r -a ignore_files <<<"$IGNORE_FILES" +read -r -a ignore_dirs <<<"$IGNORE_DIRS" +read -r -a ignore_patterns <<<"$IGNORE_PATTERNS" + +IGNORE_FILES_ARG="" +for item in "${ignore_files[@]}"; do + IGNORE_FILES_ARG="$IGNORE_FILES_ARG --exclude=$item" +done +IGNORE_DIRS_ARG="" +for item in "${ignore_dirs[@]}"; do + IGNORE_DIRS_ARG="$IGNORE_DIRS_ARG --exclude-dir=$item" +done + +#Find URLs in code: +urls=$(grep -oP '(http|ftp|https):\/\/([a-zA-Z0-9_-]+(?:(?:\.[a-zA-Z0-9_-]+)+))([a-zA-Z0-9_.,@?^=%&:\/~+#-]*[a-zA-Z0-9_@?^=%&\/~+#-])?' -rI $IGNORE_FILES_ARG $IGNORE_DIRS_ARG) + +fail_counter=0 + +FAILED_LINKS=() +for item in $urls; do +# echo $item + skip=0 + for pattern in "${ignore_patterns[@]}"; do + [[ "$item" =~ $pattern ]] && skip=1 + done + + if [[ $skip == 1 ]]; then + echo "SKIPPING $item" + continue + fi + + filename=$(echo "$item" | cut -d':' -f1) + url=$(echo "$item" | cut -d':' -f2-) + echo -n "Checking $url from file $filename" + if ! curl --head --silent --fail "$url" 2>&1 > /dev/null; then + echo -e " \033[0;31mNOT FOUND\033[32m\n" + FAILED_LINKS+=("$url from file $filename") + ((fail_counter=fail_counter+1)) + else + printf " \033[32mok\033[0m\n" + fi +done + +echo "Failed files:" +printf '%s\n' "${FAILED_LINKS[@]}" +exit $fail_counter diff --git a/.github/workflows/check_links.yml b/.github/workflows/check_links.yml new file mode 100644 index 000000000..dc662f7b2 --- /dev/null +++ b/.github/workflows/check_links.yml @@ -0,0 +1,19 @@ +name: Check Links +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + +jobs: + check_links: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check URLs + run: | + .github/helpers/check_urls.sh \ + -d ".git build CMakeModules debian" \ + -f "package.xml ursim_docker.rst architecture_coarse.svg" \ + -p "vnc\.html opensource\.org\/licenses\/BSD-3-Clause kernel\.org\/pub\/linux\/kernel" diff --git a/README.md b/README.md index 4a5f1e1e7..665854712 100644 --- a/README.md +++ b/README.md @@ -97,23 +97,23 @@ For getting started, you'll basically need three steps: ```bash sudo apt-get install ros-humble-ur ``` - See the [installation instructions](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/installation/installation.html) for more details and source-build instructions. + See the [installation instructions](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/installation/installation.html) for more details and source-build instructions. 2. **Start & Setup the robot**. Once you've installed the driver, [setup the - robot](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/installation/robot_setup.html) + robot](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/installation/robot_setup.html) and [create a program for external - control](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/installation/install_urcap_e_series.html). + control](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/installation/install_urcap_e_series.html). Please do this step carefully and extract the calibration as explained - [here](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/installation/robot_setup.html#extract-calibration-information). + [here](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/installation/robot_setup.html#extract-calibration-information). Otherwise the TCP's pose will not be correct inside the ROS ecosystem. If no real robot is required, you can [use a simulated - robot](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/usage.html#usage-with-official-ur-simulator) + robot](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/usage.html#usage-with-official-ur-simulator) that will behave almost exactly like the real robot. 3. **Start the driver**. See the [usage - documentation](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/usage.html) for + documentation](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/usage.html) for details. ```bash @@ -122,7 +122,7 @@ For getting started, you'll basically need three steps: ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=192.168.56.101 ``` -4. Unless started in [headless mode](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/ROS_INTERFACE.html#headless-mode): Run the external_control program by **pressing `play` on the teach pendant**. +4. Unless started in [headless mode](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/ROS_INTERFACE.html#headless-mode): Run the external_control program by **pressing `play` on the teach pendant**. ## MoveIt! support @@ -135,8 +135,8 @@ Watch MoveIt in action with the Universal Robots ROS2 driver: *The video shows free-space trajectory planning around a modeled collision scene object using the MoveIt2 MotionPlanning widget for Rviz2.* See the [MoveIt! -section](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/usage.html#using-moveit) -of the [Usage guide](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/usage.html) +section](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/usage.html#using-moveit) +of the [Usage guide](https://docs.ros.org/en/ros2_packages/humble/api/ur_robot_driver/doc/usage.html) for details. diff --git a/ur_bringup/scripts/start_ursim.sh b/ur_bringup/scripts/start_ursim.sh index a8dbe9bf1..3c60f1ed2 100755 --- a/ur_bringup/scripts/start_ursim.sh +++ b/ur_bringup/scripts/start_ursim.sh @@ -28,102 +28,16 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -PERSISTENT_BASE="${HOME}/.ursim" -URCAP_VERSION="1.0.5" echo -e "\033[0;31mDEPRECATION WARNING: " \ "Launch files from the ur_bringup package are deprecated and will be removed from Iron " \ "Irwini on. Please use the same launch files from the ur_robot_driver package." \ "\033[0m" -help() -{ - # Display Help - echo "Starts URSim inside a docker container" - echo - echo "Syntax: `basename "$0"` [-m|s|h]" - echo "options:" - echo " -m Robot model. One of [ur3, ur3e, ur5, ur5e, ur10, ur10e, ur16e]. Defaults to ur5e." - echo " -h Print this Help." - echo -} +ORANGE='\033[0;33m' +NC='\033[0m' # No Color -ROBOT_MODEL=UR5 -ROBOT_SERIES=e-series +URSIM_CMD="ros2 run ur_client_library start_ursim.sh" -validate_model() -{ - case $ROBOT_MODEL in - ur3|ur5|ur10) - ROBOT_MODEL=${ROBOT_MODEL^^} - ROBOT_SERIES=cb3 - ;; - ur3e|ur5e|ur10e|ur16e) - ROBOT_MODEL=${ROBOT_MODEL^^} - ROBOT_MODEL=$(echo ${ROBOT_MODEL:0:$((${#ROBOT_MODEL}-1))}) - ROBOT_SERIES=e-series - ;; - *) - echo "Not a valid robot model: $ROBOT_MODEL" - exit - ;; - esac -} - - -while getopts ":hm:s:" option; do - case $option in - h) # display Help - help - exit;; - m) # robot model - ROBOT_MODEL=${OPTARG} - validate_model - ;; - \?) # invalid option - echo "Error: Invalid option" - help - exit;; - esac -done - -URCAP_STORAGE="${PERSISTENT_BASE}/${ROBOT_SERIES}/urcaps" -PROGRAM_STORAGE="${PERSISTENT_BASE}/${ROBOT_SERIES}/programs" - -# Create local storage for programs and URCaps -mkdir -p "${URCAP_STORAGE}" -mkdir -p "${PROGRAM_STORAGE}" - -# Download external_control URCap -if [[ ! -f "${URCAP_STORAGE}/externalcontrol-${URCAP_VERSION}.jar" ]]; then - curl -L -o "${URCAP_STORAGE}/externalcontrol-${URCAP_VERSION}.jar" \ - "https://github.com/UniversalRobots/Universal_Robots_ExternalControl_URCap/releases/download/v${URCAP_VERSION}/externalcontrol-${URCAP_VERSION}.jar" -fi - -# Check whether network already exists -docker network inspect ursim_net > /dev/null -if [ $? -eq 0 ]; then - echo "ursim_net already exists" -else - echo "Creating ursim_net" - docker network create --subnet=192.168.56.0/24 ursim_net -fi - -# run docker container -docker run --rm -d --net ursim_net --ip 192.168.56.101\ - -v "${URCAP_STORAGE}":/urcaps \ - -v "${PROGRAM_STORAGE}":/ursim/programs \ - -e ROBOT_MODEL="${ROBOT_MODEL}" \ - --name ursim \ - universalrobots/ursim_${ROBOT_SERIES} || exit - -trap "echo killing; docker container kill ursim; exit" SIGINT SIGTERM - -echo "Docker URSim is running" -printf "\nTo access Polyscope, open the following URL in a web browser.\n\thttp://192.168.56.101:6080/vnc.html\n\n" -echo "To exit, press CTRL+C" - -while : -do - sleep 1 -done +echo -e "${ORANGE} DEPRECATION WARNING: The script starting URSim was moved to the ur_client_library package. This script here does still work, but will be removed with ROS Jazzy. Please use `${URSIM_CMD}` to start URSim in future." +$URSIM_CMD diff --git a/ur_robot_driver/README.md b/ur_robot_driver/README.md index 4f520eb63..69234b2ed 100644 --- a/ur_robot_driver/README.md +++ b/ur_robot_driver/README.md @@ -33,7 +33,7 @@ request. **Custom script snippets** can be sent to the robot on a topic basis. By default, they will interrupt other programs (such as the one controlling the robot). For a certain subset of functions, it is however possible to send them as secondary programs. See [UR -documentation](https://www.universal-robots.com/how-tos-and-faqs/how-to/ur-how-tos/secondary-program-17257/) +documentation](https://www.universal-robots.com/articles/ur/programming/secondary-program/) on details.
**Note to e-Series users:** diff --git a/ur_robot_driver/doc/installation/real_time.md b/ur_robot_driver/doc/installation/real_time.md deleted file mode 100644 index e55a9e638..000000000 --- a/ur_robot_driver/doc/installation/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/ur_robot_driver/doc/installation/real_time.rst b/ur_robot_driver/doc/installation/real_time.rst new file mode 100644 index 000000000..deb347c05 --- /dev/null +++ b/ur_robot_driver/doc/installation/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:: linuxconfig + + @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:: text + + "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 `_.