adaptive_component

A composable container for Adaptive ROS 2 Node computations.

Allows building Nodes that can select between FPGA, CPU or GPU, at run-time. Stateless by default, can be made stateful to meet use-case specific needs. Refer to examples in README.

Technically, provides A ROS 2 Node subclass programmed as a "Component" and including its own single threaded executor to build adaptive computations. Adaptive ROS 2 Nodes are able to perform computations in the CPU, the FPGA or the GPU, adaptively. Adaptive behavior is controlled through the "adaptive" ROS 2 parameter.

README

adaptive_component

A composable stateless container for Adaptive ROS 2 Node computations. Select between FPGA, CPU or GPU at run-time.

Nodes using hardware acceleration are able to perform computations faster relying on FPGAs or GPUs, improving performance. Adaptive ROS 2 Nodes leverage hardware acceleration at run-time, allowing robotics engineers to select which computational resource the Node uses on-the-go, giving roboticists a finer-grained control over the resources their computional graphs use in the underlying hardware.

This ROS 2 package provides a composable stateless container for Adaptive ROS 2 Node computations: adaptive_component. It allows building Nodes that can select between FPGA, CPU or GPU, at run-time.

Technically, it’s a ROS 2 Node[^1] subclass programmed as a Component[^2] and including its own single threaded executor to build adaptive computations. Adaptive ROS 2 Nodes can then be built easily and are able to perform computations in the CPU, the FPGA or the GPU. Adaptive behavior is controlled through the adaptive ROS 2 parameter, with the following values allowed:

  • 0: Hardware::CPU

  • 1: Hardware::FPGA

  • 2: Hardware::GPU

adaptive_component is stateless by default, if you need your Adaptive Nodes to be stateful, feel free to inherit from composition::AdaptiveComponent and create your own stateful subclasses[^5].

How does it work?

asciicast

using NodeCPU = composition::DoubleVaddComponent;
using NodeFPGA = composition::DoubleVaddComponentFPGA;

rclcpp::NodeOptions options;

// Create an executor
rclcpp::executors::MultiThreadedExecutor exec;

// Create an adaptive ROS 2 Node using "components", the resulting
// Node is also programed as a "component", retaining composability
auto adaptive_node = std::make_shared<composition::AdaptiveComponent>(
      "doublevadd_publisher_adaptive",
      options,
                                              // CPU
      std::make_shared<NodeCPU>("_doublevadd_publisher_adaptive_cpu", options),
                                              // FPGA
      std::make_shared<NodeFPGA>("_doublevadd_publisher_adaptive_fpga", options),
                                              // GPU
      nullptr);

exec.add_node(adaptive_node);  // fill up the executor
exec.spin();  // spin the executor

Then, dynamically, one could switch from CPU to FPGA by setting the adaptive parameter in the /doublevadd_publisher_adaptive Node:

  • To run in the CPU: ros2 param set /doublevadd_publisher_adaptive adaptive 0

  • To run in the FPGA: ros2 param set /doublevadd_publisher_adaptive adaptive 1

Why should I care as a ROS package maintainer?

The integration of hardware acceleration into ROS often requires rewriting parts of the Node computations to further exploit parallelism. These changes often conflict with CPU-centric architectures and as a maintainer, you’re likely to care for “not breaking” CPU-centric implementations.

To consistently integrate hardware acceleration, avoid unnecessary forks and discourage package fragmentation, composition::AdaptiveComponent allows to extend ROS 2 CPU-centric Nodes[^3] with their computational counterparts separating concerns at build-time. From a package-maintenance perspective, each Node (across computation options) is written in a separated file and as a separated Component. These can live either within the same package, or in totally different (disconnected) ones. adaptive_component takes care of putting them together at launch time and no dependency with the package is required at build-time[^4].

From an execution perspective, developers can easily create Adaptive ROS 2 Nodes and compose them together as desired at launch-time, with capabilities to adaptively switch between compute alternatives at run-time.

Some examples

Examples of using adaptive_component:

Conventions and recommendations

The following conventions and recommendations are meant to facilitate the integration of hardware acceleration in existing ROS packages

  1. Component-oriented: AdaptiveComponent is built as a component and should be used as such to maintain composability of Nodes.

  2. Naming: The Adaptive Node should be suffix with _adaptive to identify in the computational graph which Nodes have adaptive capabilities and which do not.

  3. Hidden sub-Nodes: Adaptive Node components (compute-specific ones e.g. CPU’s or FPGA’s) should be named with a hyphen (_) as a prefix, which will make them hidden Nodes by default.

  4. File names: When possible, source code file names should adhere to the following guidelines:

    • CPU-based computational Nodes can optionally add the _cpu suffix

    • FPGA-based computational Nodes shall add the _fpga suffix

    • GPU-based computational Nodes shall add the _gpu suffix

Quality Declaration

This package claims to be in the Quality Level 4 category, see the Quality Declaration for more details.