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::CPU1
: Hardware::FPGA2
: 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 subclasses3.
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
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 Nodes4 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-time5.
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.
Examples of using adaptive_component
:
- An Adaptive ROS 2 Node
- An Adaptive stateless ROS 2 Component (Node example using it)
- An Adaptive stateful ROS 2 Component (Node example using it)
The following conventions and recommendations are meant to facilitate the integration of hardware acceleration in existing ROS packages
- Component-oriented:
AdaptiveComponent
is built as a component and should be used as such to maintain composability of Nodes. - Naming: The Adaptive Node should be suffix with
_adaptive
to identify in the computational graph which Nodes have adaptive capabilities and which do not. - 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. - 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
- CPU-based computational Nodes can optionally add the
This package claims to be in the Quality Level 4 category, see the Quality Declaration for more details.
Footnotes
-
A
Node
is a process that performs computations. ROS 2 is designed to be modular at a fine-grained scale; a robot control system usually comprises many nodes. Nodes execute arbitrary logic that contribute to the overall robotics behavior. ↩ -
A ROS 2
Component
is aNode
compiled into a shared library which is then loaded at runtime by a container process. This offers roboticists additional flexibility while building their computational graphs, making the layout process a deploy-time decision. AComponent
is commonly a subclass ofrclcpp::Node
. To maintain flexibility and modularity,Components
shouldn’t perform any long running or blocking tasks in their constructors. Instead, they can use timers to get periodic notifications and use callbacks for publishers, subscribers, servers, or clients. ↩ -
See this example (ROS 2 component). ↩
-
Assumes Nodes are written as components, but it they are not, it's a great chance to do so ;). ↩
-
Though
adaptive_component
allows to disconnect nodes across packages, to facilitate source code maintenance across CPU-centric implementations and counterparts, it should be encouraged to keep the source code within the same ROS 2 package with suffixes indicating of the compute substrate (e.g._fpga.cpp
, etc.). This will facilitate maintaining implementations across different compute substrates, avoid versioning issues and fragmentation issues. ↩