ROS 2 Humble is the latest LTS of the Robot Operating System (ROS), the de facto framework for robot application development. Yocto and ROS 2 allow you to build custom Linux-based production-grade operating systems for robots that obtain best performance for your use case. This article provides a walkthrough on how to build a Linux operating system with ROS 2 Humble for the AMD ZCU104 board and showcase our contribution to the ROS 2 community bringing ROS 2 Humble support for Yocto Honister release. Lastly, we show how to get better performance with ROS 2 Humble using ROBOTCORE hardware acceleration framework for ROS.

Why Yocto/PetaLinux instead of Ubuntu? production-grade robotic systems

Most people these days develop robots in Ubuntu, so it's only normal that many engineers demand Ubuntu (a developer-known rootfs) for their robotic creations. This however comes with a technical debt that often new roboticists don't consider. There're two aspects of special relevance that are often misunderstood in robotics:

  • security: cybersecurity is a process that requires a periodic effort including monitoring, research and updates of your robot's root file system (rootfs). Keeping it (your rootfs) minimal and monitored is key for a security-driven service. Regardless of whether you're cooking your own Linux rootfs or you're paying for it (someone else building it, and you pay either via telemetry data or via direct payment), a first-class security response requires you to take ownership of cybersecurity. You cannot fully externalize cybersecurity to a third party if you want your users to have a first-class security response. Also, cybersecurity needs to be observed from an attacker's perspective. While doing so, one would understand that attackers are mostly driven by financial opportunities. A common rootfs for robots is certainly a very interesting target. Besides the economics behind an attacker's mindset, from a technical perspective, compromising an operating system that's shared and common across multiple robots with common defaults (e.g. Ubuntu) is indeed a much easier target than "your own Linux distro" cooked for your specific use case and with a minimalistic attack surface (that you or someone else should study by threat modeling it). In a nutshell, you might be exposing yourself by picking a "popular" development-oriented rootfs for your robot.
  • real-time: robots are inherently deterministic machines. Information is shared across robot networks between sensors and actuators in real-time. Skipping deadlines can lead to catastrophic missbehaviors. Ensuring an OS is real-time requires all its components to execute in a time-bounded manner. General purpose development-oriented OSs like Ubuntu are generally really bad at meeting real-time deadlines for 3 main reasons: a) their Linux kernel isn't engineered enough (even when applying PREEMPT_RT patches to their kernel forks, they need lots of engineering hours from knowledgeable embedded folks to assess their real-time performance issues), b) Linux kernels need to also be real-time capable and c) there're lots of daemons and processes thought for monitoring and sending telemetry back to the OS vendor that simply break completely the real-time assumptions.

Opposed to this, production-grade embedded systems often rely on strong multidisciplinary engineering teams that build custom and minimalistic Linux distributions for a particular use-case by leveraging Yocto, a project to build embedded Linux. Rationale is that instead of relying on common development-oriented Linux distros (such as Ubuntu), Yocto allows you to build a customized Linux system for your use case. This allows you an unmatched granularity wherein you can customize from the bootloader, going through the Linux kernel and all the way into userspace libraries, such as those required to enable ROS 2 support.

ROS 2 Humble in AMD ZCU104 with Yocto

Acceleration Robotics is a firm focused on designing customized brains for robots to hasten their response time. The company creates custom compute architectures for high performance robots through hardware acceleration solutions. In a nutshell, we deliver semiconductor building blocks for robots while remaining accelerator-agnostic (FPGAs or GPUs) and as such, building custom high-performing Linux distributions becomes second nature. Adding ROS to such distributions while using Yocto or derivatives (such as PetaLinux) can be done with the meta-ros layers.

As an active member of the ROS and ROS 2 communities, we're among the early contributors of meta-ros (back in the old ROS 1 days) and first ported meta-ros to ROS 2 a few years back. To get ROS 2 Humble into custom embedded Linux OSs, our team at Acceleration Robotics contributed a series of Yocto recipes by updating the meta-ros project (see Pull Request). In total our contributions modified 60K LOC across more than 1000 files.

To get ROS 2 Humble into custom embedded Linux OSs, our team at Acceleration Robotics contributed a series of Yocto recipes by updating the meta-ros project (see Pull Request). In total our contributions modified 60K LOC across more than 1000 files.

Here is how to build a custom ROS 2 Humble embedded Linux for AMD ZCU104 board:

Step 0. Install Xilinx's PetaLinux

Get the 2022.1 that ship with Honister from https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/embedded-design-tools.htm and source it:

xilinx@xilinx:~/Downloads$ source /media/xilinx/hd3/tools/Xilinx/PetaLinux2022.1/settings.sh
WARNING: /bin/sh is not bash!
bash is PetaLinux recommended shell. Please set your default shell to bash.
WARNING: This is not a supported OS
INFO: Checking free disk space
INFO: Checking installed tools
INFO: Checking installed development libraries
INFO: Checking network and other services

Step 1. Download Honister's Yocto/PetaLinux BSP

Get it from Xilinx and set up a project with it. E.g. I'll be using ZCU104 BSP:

INFO: Create project:
INFO: Projects:
INFO: 	* xilinx-zcu104-2022.1
INFO: Has been successfully installed to /home/xilinx/Downloads/
INFO: New project successfully created in /home/xilinx/Downloads/
xilinx@xilinx:~/Downloads$ cd xilinx-zcu104-2022.1
xilinx@xilinx:~/Downloads/xilinx-zcu104-2022.1$

Step 2. Configure the Yocto/PetaLinux project:

petalinux-config --silentconfig
[INFO] Sourcing buildtools
[INFO] Generating Kconfig for project
[INFO] Silentconfig project
[INFO] Extracting yocto SDK to components/yocto. This may take time!
[INFO] Sourcing build environment
[INFO] Generating kconfig for Rootfs
[INFO] Silentconfig rootfs
[INFO] Generating plnxtool conf
[INFO] Adding user layers
[INFO] Generating workspace directory
[INFO] Successfully configured project

Step 3. Add meta-layers for ROS 2 Humble and configure them in Yocto/PetaLinux:

IMPORTANT: keep in mind Xilinx's PetaLinux 2022.1 aligns with Yocto. Honister, so we need to fetch recipes that will build against that

git clone https://github.com/vmayoral/meta-ros -b honister-humble project-spec/meta-ros

After cloning the meta-layer (layer of layers) into a the project, the layer should be configured to be built by editing build/conf/bblayers.conf (in my case, /home/xilinx/Downloads/xilinx-k26-som-2022.1/build/conf/bblayers.conf) and adding the following at the end:

  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros2-humble \
  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros2 \
  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros-common \

In addition, add the following (e.g. at the beginning of the file) as well to build/conf/bblayers.conf (in my case, /home/xilinx/Downloads/xilinx-k26-som-2022.1/build/conf/bblayers.conf), which defines some variables to be used by the meta-ros recipes:

# define the ROS 2 Yocto target release
ROS_OE_RELEASE_SERIES = "honister"

# define ROS 2 distro
ROS_DISTRO = "humble"

Altogether, it ends up as follows:

Resulting "bblayers.conf" file after adding meta-ros with Humble
# WARNING: this configuration has been automatically generated and in
# most cases should not be edited. If you need more flexibility than
# this configuration provides, it is strongly suggested that you set
# up a proper instance of the full build system and use that instead.

LCONF_VERSION = "7"

# define the ROS 2 Yocto target release
ROS_OE_RELEASE_SERIES = "honister"

# define ROS 2 distro
ROS_DISTRO = "humble"

BBPATH = "${TOPDIR}"
SDKBASEMETAPATH = "/home/xilinx/Downloads/xilinx-k26-som-2022.1/components/yocto"
BBLAYERS := " \
  ${SDKBASEMETAPATH}/layers/core/meta \
  ${SDKBASEMETAPATH}/layers/core/meta-poky \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-perl \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-python \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-filesystems \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-gnome \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-multimedia \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-networking \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-webserver \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-xfce \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-initramfs \
  ${SDKBASEMETAPATH}/layers/meta-openembedded/meta-oe \
  ${SDKBASEMETAPATH}/layers/meta-clang \
  ${SDKBASEMETAPATH}/layers/meta-browser/meta-chromium \
  ${SDKBASEMETAPATH}/layers/meta-qt5 \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-microblaze \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-xilinx-core \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-xilinx-bsp \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-xilinx-pynq \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-xilinx-contrib \
  ${SDKBASEMETAPATH}/layers/meta-xilinx/meta-xilinx-standalone \
  ${SDKBASEMETAPATH}/layers/meta-xilinx-tools \
  ${SDKBASEMETAPATH}/layers/meta-petalinux \
  ${SDKBASEMETAPATH}/layers/meta-virtualization \
  ${SDKBASEMETAPATH}/layers/meta-openamp \
  ${SDKBASEMETAPATH}/layers/meta-jupyter \
  ${SDKBASEMETAPATH}/layers/meta-python2 \
  ${SDKBASEMETAPATH}/layers/meta-som \
  ${SDKBASEMETAPATH}/layers/meta-security \
  ${SDKBASEMETAPATH}/layers/meta-security/meta-tpm \
  /home/xilinx/Downloads/xilinx-k26-som-2022.1/project-spec/meta-user \
  /home/xilinx/Downloads/xilinx-k26-som-2022.1/components/yocto/workspace \
  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros2-humble \
  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros2 \
  ${SDKBASEMETAPATH}/../../project-spec/meta-ros/meta-ros-common \
  "

Step 4. Extend Yocto's minimal image with ROS 2 desired content

There're various ways to build the meta-ros recipes. One simple one (which simplifies the development and debug of recipes) is to extend default PetaLinux image recipe (petalinux-image-minimal.bb) by adding the ROS 2 Humble's content. In particular, I'm adding bare minimum packages required to execute pub/sub examples while including two open source DDS implementations as follows:

petalinux-image-minimal.bbappend
mkdir -p project-spec/meta-user/recipes-images/images
cat << 'EOF' > project-spec/meta-user/recipes-images/images/petalinux-image-minimal.bbappend
require ${COREBASE}/../meta-petalinux/recipes-core/images/petalinux-image-minimal.bb

SUMMARY = "A image including a bare-minimum installation of ROS 2 and including some basic pub/sub examples. It includes two DDS middleware implementations, FastDDS and Cyclone DDS"
DESCRIPTION = "${SUMMARY}"

inherit ros_distro_${ROS_DISTRO}
inherit ${ROS_DISTRO_TYPE}_image

ROS_SYSROOT_BUILD_DEPENDENCIES = " \
    ament-lint-auto \
    ament-cmake-auto \
    ament-cmake-core \
    ament-cmake-cppcheck \
    ament-cmake-cpplint \
    ament-cmake-export-definitions \
    ament-cmake-export-dependencies \
    ament-cmake-export-include-directories \
    ament-cmake-export-interfaces \
    ament-cmake-export-libraries \
    ament-cmake-export-link-flags \
    ament-cmake-export-targets \
    ament-cmake-gmock \
    ament-cmake-gtest \
    ament-cmake-include-directories \
    ament-cmake-libraries \
    ament-cmake \
    ament-cmake-pytest \
    ament-cmake-python \
    ament-cmake-ros \
    ament-cmake-target-dependencies \
    ament-cmake-test \
    ament-cmake-version \
    ament-cmake-uncrustify \
    ament-cmake-flake8 \
    ament-cmake-pep257 \
    ament-copyright \
    ament-cpplint \
    ament-flake8 \
    ament-index-python \
    ament-lint-cmake \
    ament-mypy \
    ament-package \
    ament-pclint \
    ament-pep257 \
    ament-pycodestyle \
    ament-pyflakes \
    ament-uncrustify \
    ament-xmllint \
    cmake \
    eigen3-cmake-module \
    fastcdr \
    fastrtps-cmake-module \
    fastrtps \
    git \
    gmock-vendor \
    gtest-vendor \
    pkgconfig \
    python-cmake-module \
    python3-catkin-pkg \
    python3-empy \
    python3 \
    python3-nose \
    python3-pytest \
    rcutils \
    rmw-implementation-cmake \
    rosidl-cmake \
    rosidl-default-generators \
    rosidl-generator-c \
    rosidl-generator-cpp \
    rosidl-generator-dds-idl \
    rosidl-generator-py \
    rosidl-parser \
    rosidl-runtime-c \
    rosidl-runtime-cpp \
    rosidl-typesupport-c \
    rosidl-typesupport-cpp \
    rosidl-typesupport-fastrtps-cpp \
    rosidl-typesupport-interface \
    rosidl-typesupport-introspection-c \
    rosidl-typesupport-introspection-cpp \
    foonathan-memory-vendor \
    libyaml-vendor \
"

IMAGE_INSTALL:append = " \
    ros-base \
    examples-rclcpp-minimal-action-client \
    examples-rclcpp-minimal-action-server \
    examples-rclcpp-minimal-client \
    examples-rclcpp-minimal-composition \
    examples-rclcpp-minimal-publisher \
    examples-rclcpp-minimal-service \
    examples-rclcpp-minimal-subscriber \
    examples-rclcpp-minimal-timer \
    examples-rclcpp-multithreaded-executor \
    examples-rclpy-executors \
    examples-rclpy-minimal-action-client \
    examples-rclpy-minimal-action-server \
    examples-rclpy-minimal-client \
    examples-rclpy-minimal-publisher \
    examples-rclpy-minimal-service \
    examples-rclpy-minimal-subscriber \
    demo-nodes-cpp \
    demo-nodes-cpp-rosnative \
    demo-nodes-py \
    cyclonedds \
    rmw-cyclonedds-cpp \
    tmux \
    byobu \
    python3-argcomplete \
    glibc-utils \
    localedef \
    rt-tests \
    stress \
    xrt-dev \
    xrt \
    zocl \
    opencl-headers-dev \
    opencl-clhpp-dev \
    ${ROS_SYSROOT_BUILD_DEPENDENCIES} \
"

IMAGE_LINGUAS = "en-us"
GLIBC_GENERATE_LOCALES = "en_US.UTF-8"

EOF

Step 5. Build the image

What's left is to build the project and generate the embedded artifacts desired (kernel, bootloader firmware, rootfs, sysroot, etc.):

petalinux-build

This will take a while and in time. Once it finalizes, you'll have your rootfs under images/linux with ROS 2 Humble 🙂.

ROBOTCORE™ to accelerate your robots

So, now that you have a custom ROS 2 Humble embedded Linux, how do you improve ROS 2 Humble beyond its CPU-centric execution? By using hardware acceleration. To improve your robot's performance and speed it up you should rely on hardware acceleration. The AMD ZCU104 is a great accelerator and our work at Acceleration Robotics focuses on getting you everything you need to create robot cores to boost your ROS 2 Humble architectures with AMD ZCU104.

ROBOTCORE™ Hardware Acceleration Framework for ROS: ROBOTCORE™ helps build custom compute architectures for robots, or robot cores, that make robots faster, more deterministic and power-efficient. Simply put, it provides a development, build and deployment experience for creating robot hardware and hardware accelerators similar to the standard, non-accelerated ROS development flow. 
ROBOTCORE | Hardware Acceleration framework for ROS
ROBOTCORE™ helps build custom compute architectures for robots, or robot cores, that make robots faster, more deterministic and power-efficient.

ROBOTCORE™ improves robotics development productivity

ROBOTCORE™ implements the ROS 2 Hardware Acceleration Architecture and Conventions REP and supports the most popular hardware acceleration solutions and development kits to build robots with hardware acceleration and ROS. While doing so, ROBOTCORE™ simplifies the ROS 2 development flow for accelerators providing pre-packaged firmware artifacts that allow you to improve your robotics development productivity. With ROBOTCORE™ building accelerators takes just the following:

# 1. Create ROS 2 workspace
mkdir -p ~/ros2_ws/src; cd ~/ros2_ws
wget https://<your-customer-id-url>/humble_repos.ros
vcs import src --recursive < humble_repos.ros

# 2. build your workspace for dev. machine arch
colcon build --merge-install  

# 3. source the overlay to enable ROBOTCORE™
source install/setup.bash  

# 4. build your workspace for embedded arch
#    and build acceleration kernels altogether
colcon build --merge-install --mixin zcu104

Get in touch to acquire a ROBOTCORE™ license including documentation, examples and reference designs.