Gateway Basics¶
Important
The ROS 2 integrations are currently prototypes and have not yet been validated in real ROS 2 workflows. They are recommended only for experimentation in development deployments.
Before diving into possible gateway configurations, let’s first go over some gateway basics.
The gateway component sits at the boundary of iceoryx2 and another
communication mechanism, in this case ROS 2, and propagates payloads
across it. Inbound data gets written into shared memory, after which it is
shared zero-copy between iceoryx2 nodes. Likewise, outbound data
is provided by nodes to the gateway without copies. Currently, only
publish-subscribe services are propagated. Support for the other messaging
patterns is planned.
The gateway can be either embedded into your own process, allowing you to
have full control over its execution, or run in an isolated process via the
iox2 CLI.
Building¶
The gateway must be built from source against a ROS 2 install space, as it
links against the specific rcl library being used. See
Build the ROS 2 gateway for details on how to
build the gateway.
Important
Running the gateway also requires a sourced ROS 2 workspace to load typesupport libraries from.
Gateway Configuration¶
There are two main capabilities that should be understood in order to be able to choose the right configuration for your system.
Mapping¶
The mapping pairs iceoryx2 services with ROS 2 topics. Only paired services
and topics are propagated.
PrefixMapping¶
PrefixMapping is the default strategy. It derives pairings between ROS 2
topics and iceoryx2 services from a naming convention. All service names
matching the convention are propagated with best-guess quality of service:
ros2://topics/{NAMESPACE}/{TOPIC} <-> /{NAMESPACE}/{TOPIC}
(iceoryx2 service name) (ROS 2 topic name)
The gateway requires the typesupport of a topic’s message type to propagate it, and loads it when it first propagates a topic of that type. Topics whose typesupport cannot be loaded are not propagated, and the gateway reports an error for each of them.
With --allow, which is repeatable and accepts wildcards, the prefix mapping
is restricted to the given topics, so that the gateway only attempts to load
the typesupport of their types.
With --preload-type, which is repeatable, the typesupport of a type is
loaded at startup, and the gateway instead fails to start when it cannot be
loaded:
iox2 link gateway ros2 --allow "/cmd_vel" --allow "/sensors/*" --preload-type "geometry_msgs/msg/Twist"
This approach is only recommended as a starting point when first configuring the system.
StaticMapping¶
StaticMapping declares pairings explicitly in a TOML file. Only the specified
pairings are propagated and their types are resolved immediately at startup,
which enables fast failure on misconfiguration. The iceoryx2 service
settings and ROS 2 QoS can also be explicitly configured for each entry. See
the example configuration
for available configuration options.
A minimal entry pairs one service with one topic:
[[mapping]]
iceoryx2.service_name = "Chatter"
iceoryx2.payload_type = "std_msgs/msg/String"
ros2.topic = "/chatter"
ros2.type = "std_msgs/msg/String"
The mapping file is passed on launch:
iox2 link gateway ros2 --static-mapping mapping.toml
This approach is recommended once the shape of a system is understood.
Translation¶
ROS 2 carries messages as CDR bytes, while iceoryx2 applications may hold
payloads in shared memory in a different form. The translator converts between
the two as payloads cross the boundary.
Passthrough¶
Passthrough moves payload bytes across the boundary unmodified and is the
default. The payload must be a byte slice holding the CDR serialization of the
ROS 2 message of the paired topic. It is left to the applications to
(de)serialize the bytes.
iox2 link gateway ros2 --translator Passthrough
PlainStruct¶
PlainStruct (de)serializes payloads at the boundary using the ROS 2
typesupport libraries. The iceoryx2 applications work directly with a plain
struct in shared memory, while the gateway converts to and from the CDR bytes
that ROS 2 expects.
The payload must be the C struct that rosidl generates for the message type.
In Rust, this is the rmw variant of the generated message, e.g.
geometry_msgs::msg::rmw::Twist. Only self-contained message definitions,
free of bounded and dynamic fields, are supported.
iox2 link gateway ros2 --translator PlainStruct
Application Configuration¶
Applications require minimal changes to integrate with the ROS 2 gateway. The gateway automatically connects to services mapped by the selected mapping and propagates payloads without any involvement of the applications.
Important
A propagated service cannot use a user header of its own. It must either have
no user header or the RosHeader described in Message Info.
Services with any other user header, such as in an existing system, cannot be
propagated, and the gateway reports an error for each of them.
There is only one thing that applications interfacing with the ROS 2 gateway
must do: the payload type name of a propagated service must be the ROS 2 type
name of the paired topic, for example geometry_msgs/msg/Twist. The gateway
resolves the typesupport used for translation by this name. When translating
to plain structs, the payload’s size and alignment are additionally verified
against the layout of the ROS 2 type, and the service is not propagated when
they contradict.
The type name can be specified on the payload type when implementing
ZeroCopySend on payload types:
#[derive(Debug, ZeroCopySend)]
#[type_name("std_msgs/msg/Float64")]
#[repr(C)]
pub struct Payload {
pub data: f64,
}
For ROS 2 types generated for Rust this is typically set by wrapping them in a new type and implementing the trait:
use rosidl_runtime_rs::{Message, RmwMessage};
#[derive(Debug, Default, Clone)]
#[repr(transparent)]
pub struct Float64(pub ros_env::std_msgs::msg::rmw::Float64);
unsafe impl ZeroCopySend for Float64 {
unsafe fn type_name() -> &'static str {
<<ros_env::std_msgs::msg::Float64 as Message>::RmwMsg as RmwMessage>::TYPE_NAME
}
}
Delegating to the generated TYPE_NAME constant is preferred over hardcoding
the name, as a typo in a hardcoded name does not fail at compile time but
silently prevents the service from being propagated.
Message Info¶
ROS 2 delivers each message with its message info, such as the writer that published it, its source timestamp and its sequence number. By default, the message info is dropped at the boundary and the services the gateway creates for topics have no user header.
Applications that want to read the message info declare the RosHeader as the
user header of the service:
use iceoryx2_integrations_ros2_interop::RosHeader;
let service = node
.service_builder(&"MyService".try_into()?)
.publish_subscribe::<Payload>()
.user_header::<RosHeader>()
.open_or_create()?;
If the header is specified, the gateway must be launched with --ros-header so
that it also creates services mirroring topics with the header.
iox2 link gateway ros2 --ros-header
When ingesting messages from ROS 2, the gateway fills this header with the message info, which subscribers may use to identify the remote writer or detect message loss. Publishing applications can leave it at its default as ROS 2 populates it automatically.
Running¶
The gateway discovers matching services and topics and propagates pending samples whenever it wakes. How it wakes is configurable.
By default, the gateway polls every 100 milliseconds. A different polling
rate (in milliseconds) can be set with --poll:
iox2 link gateway ros2 --poll 10
The gateway can also wake reactively. With --reactive it wakes whenever data
arrives or endpoints change on the ROS 2 side. With --listener, which is
repeatable, it wakes whenever the named iceoryx2 event service fires. When
either is given, polling becomes opt-in and is only enabled when --poll is
set explicitly:
iox2 link gateway ros2 --reactive --listener "SensorData"
The gateway can also wake the iceoryx2 applications receiving data from
ROS 2. The gateway publishes each message received from ROS 2 on the service
its topic is mapped to. With --notify, it also notifies the event service with
the same name as that service. Subscribers can then wait on that event service
instead of polling for new samples:
iox2 link gateway ros2 --reactive --notify
When embedding the gateway, it is driven by calling discover() and
propagate() on the link, either on a timer or whenever the listener it
provides wakes, as shown in its usage examples.
Further Reading¶
Build the gateway against your own ROS 2 workspace.
Example applications propagated to ROS 2 by the gateway, one set per translator.
Understand how to extend iceoryx2 beyond its shared memory domain.