Skip to content
 
 

Repository files navigation

ROS2 Client

Static Checks Tests on Ubuntu Tests on macOS Security audit

This is a Rust native client library for ROS2. It does not link to rcl, rclcpp, or any non-Rust DDS library. RustDDS is used for communication.

This is a fork — published as ros2-client-multi-rmw

semio-ai/ros2-client is a fork of Atostek/ros2-client that adds a second, rmw_zenoh-compatible ROS 2-over-Zenoh backend alongside the original DDS one, selected at compile time by the mutually-exclusive dds (default) / zenoh features (see docs/decisions/0002-dual-backend-compile-time-feature-selection.md).

Upstream owns the ros2-client name on crates.io, so this fork is published as ros2-client-multi-rmw. The library is still imported as ros2_client (the [lib] name is pinned), so downstream code is unchanged — only the dependency line differs:

ros2-client = { package = "ros2-client-multi-rmw", version = "0.13", default-features = false, features = ["zenoh"] }

Code stays use ros2_client::…. A release is cut by merging a version bump to master: release.yml publishes through crates.io trusted publishing when that version is not on crates.io yet — no tag, no token. CHANGELOG.md records each version. With the upstream remote configured, gh pr create targets Atostek/ros2-client unless told otherwise: pass --repo semio-ai/ros2-client.

Re-aligning with upstream

This fork is meant to keep tracking Atostek/ros2-client, and ideally to be retired once the Zenoh backend is upstreamed. To pull upstream changes in:

git remote add upstream https://github.com/Atostek/ros2-client.git   # once
git fetch upstream
git switch master && git merge upstream/master        # or: git rebase upstream/master

The fork's changes are additive and feature-gated, so merges stay small — the divergence is concentrated in a few seams:

  • the new src/zenoh_backend/ module (upstream has nothing there);
  • the dds / zenoh feature gates and the compile_error! enforcing exactly one backend (src/lib.rs);
  • thin backend-dispatch shims where Context / Node / topics are constructed.

Conflicts almost always land in those seams. Resolve by keeping the backend split, then re-check both backends and the nightly formatter before re-publishing:

cargo test                                              # DDS (default)
cargo test --no-default-features --features zenoh,jazzy --lib
cargo +nightly-2026-07-22 fmt -- --check                # the nightly CI formats with

The API is not identical to rclcpp or rclpy, because some parts would be very awkward in Rust. For example, there are no callbacks. Rust async mechanism is used instead. Alternatively, some of the functionality can be polled using the Metal I/O library.

There is a .spin() call, but it is required only to have ros2-client execute some background tasks. You can spawn an async task to run it, and retain the flow of control in your code.

Please see the included examples on how to use the various features.

Features Status

  • Topics, Publish and Subscribe ✅
  • QoS ✅
  • Serialization ✅ - via Serde
  • Services: Clients and Servers ✅ (async recommended)
  • Actions ✅ (async required)
  • Discovery / ROS Graph update events ✅ (async)
  • rosout logging ✅
  • Parameters ✅
    • Parameter Services (remote Parameter manipulation) ✅
  • Time support
    • ROS Time ✅
    • Simulated time support ✅
    • Steady time ✅
  • Message generation: from .msg to .rs- experimental
  • ROS 2 Security - experimental

Middleware backends: DDS and Zenoh

ros2-client can talk to ROS 2 over either of two middleware backends, selected at compile time by mutually exclusive Cargo features:

  • dds (default) — communicates via RustDDS, interoperating with ROS 2's default DDS RMWs (rmw_fastrtps, rmw_cyclonedds, …).
  • zenoh — communicates via Zenoh, mirroring the wire protocol of the official rmw_zenoh middleware, so it interoperates with ROS 2 nodes running rmw_zenoh.

Exactly one backend must be enabled; the build emits a compile_error! if both or neither are active. The default build uses dds. Build the Zenoh backend with:

cargo build --no-default-features --features zenoh

Run the bundled Zenoh example (a self-contained talker + listener over loopback):

cargo run --no-default-features --features zenoh --example zenoh_demo

Zenoh router requirement

Like rmw_zenoh, the Zenoh backend discovers peers and exchanges the ROS graph through Zenoh's infrastructure. For anything beyond a single process you normally run a Zenoh router (zenohd), exactly as rmw_zenoh does, or configure explicit peer connect/listen endpoints. The in-process examples and tests connect two peers directly over loopback, so they need no router. See docs/decisions/0009-zenoh-router-and-config.md for configuration details.

Feature support on the Zenoh backend

Capability Zenoh backend Notes
Topics (publish/subscribe) ✅ CDR payload + (seq, timestamp, gid) attachment, per rmw_zenoh
Services (client/server) ✅ Zenoh queryable / get
Actions ✅ Composed of services + a feedback topic, as in ROS 2
Parameters + parameter_events ✅ Six rcl_interfaces services + events topic
rosout logging ✅ /rosout publisher + optional reader
Discovery / ROS graph ✅ Zenoh liveliness tokens + a graph cache
QoS ⚠️ Backend-neutral profile carried in liveliness keys; not all policies enforced
ROS 2 Security ❌ DDS-only (RustDDS security)
Message generation (msggen) ✅ Backend-neutral

The design, the rmw_zenoh mapping, and the wire-format details are documented under docs/zenoh_study/; the design decisions are recorded under docs/decisions/. To validate interoperability against a real ROS 2 + rmw_zenoh stack, follow docs/zenoh_study/interop_runbook.md.

Type hashes (send direction): REP-2016 RIHS01_… type hashes are emitted from a table of known interop types; types outside that table use a wildcard on receive and a placeholder on send. Computing hashes from parsed IDL is a tracked post-MVP follow-up (ADR-0007).

ROS 2 Releases Compatibility

This is what is expected to work. There are no routine tests against older releases.

Select the target distribution with a Cargo feature.

Note: The distribution features form a chain (galactic < humble < iron < jazzy < kilted < lyrical), and enabling any of these also enables all the older features, but the build aims to be compatible only with the latest enabled.

The default feature is currently jazzy, because it has LTS status. Build against a specific distribution with, e.g., cargo build --no-default-features --features humble (--no-default-features avoids also pulling in the default).

ROS 2 Release ros2-client should interoperate?
A - E Maybe. Not tested.
Foxy, Galactic, Humble Yes. Build with feature galactic or humble (older Gid format).
Iron Yes. Not well tested. Build with feature iron.
Jazzy Yes (default). Build with feature jazzy.
Kilted Yes. Build with feature kilted.
Lyrical Yes. Build with feature lyrical.

Please see test results for details.

Version 0.10

  • Experimental Zenoh middleware backend (Cargo feature zenoh), mirroring rmw_zenoh. See "Middleware backends: DDS and Zenoh" above.
  • Add interoperability tests and results.
  • ROS 2 distribution selection via a feature (galactic .. lyrical; default jazzy).
  • Context now checks the ROS_DISTRO environment variable against the compiled distribution.
  • Upgrade to RustDDS 0.13 to improve interoperability.

Version 0.9

  • Upgrade to RustDDS 0.12, which had an API change.

Version 0.8:

  • API change: ParameterFunc must now implement Sync, so that Node is also Sync. This helps in using multithreaded async executors.

Version 0.8.1:

  • AsyncActionServer methods changes from taking &mut self into &self for better serving concurrent goals.
  • Bump RustDDS and other depencency versions
  • Add example concurrent_action_server
  • msggen logging can be redirected.
  • Additional unit tests, including the use of tokio async executor.

New in Version 0.7:

  • NodeName namespace is no longer allowed to be the empty string, because it confuses ROS 2 tools. Minimum namespace is "/".
  • Parameter support, incl. Paramater services
  • Time support

0.7.1

  • Subscribers can take() samples with deserialization "seed" value. This allows more run-time control of deserialization. Upgrade to RustDDS 0.10.0.

0.7.2

  • Adapt to separation of CDR encoding from RustDDS.

0.7.4

  • Implement std Error trait for NameError and NodeCreateError
  • Async wait_for_writer and wait_for_reader results now implement Send.

0.7.5

  • New feature pre-iron-gid. The Gid .msg definition has changed between ROS2 Humble and Iron. ros2-client now uses the newer version by default. Use this feature to revert to the old definition.

New in Version 0.6:

  • Reworked ROS 2 Discovery implementation. Now Node has .status_receiver()
  • Async .spin() call to run the Discovery mechanism.
  • Client has .wait_for_service()
  • New API for naming Nodes, Topics, Services, Actions, and data types for Topics, Actions, and Services. The new API is more structured to avoid possible confusion and errors from parsing strings.

New in version 0.5:

  • Actions are supported
  • async programming interface. This should make a built-in event loop unnecessary, as Rust async executors sort of do that already. This means that ros2-client is not going to implement a call similar to rclcpp::spin(..).

Example: minimal_action_server and minimal_action_client

These are re-implementations of similarly named ROS examples. They should be interoperable with ROS 2 example programs in C++ or Python.

To test this, start a server and then, in a separate terminal, a client, e.g.

ros2 run examples_rclcpp_minimal_action_server action_server_member_functions and cargo run --example=minimal_action_client

or

cargo run --example=minimal_action_server and ros2 run examples_rclpy_minimal_action_client client

You should see the client requesting for a sequence of Fibonacci numbers, and the server providing them until the requested sequence length is reached.

Example: turtle_teleop

The included example program should be able to communicate with out-of-the-box ROS2 turtlesim example.

Install ROS2 and start the simulator by ros2 run turtlesim turtlesim_node. Then run the turtle_teleop example to control the simulator.

Turtlesim screenshot

Teleop example program currently has the following keyboard commands:

  • Cursor keys: Move turtle
  • q or Ctrl-C: quit
  • r: reset simulator
  • p: change pen color (for turtle1 only)
  • a/b : spawn turtle1 / turtle2
  • A/B : kill turtle1 / turtle2
  • 1/2 : switch control between turtle1 / turtle2
  • d/f/g: Trigger or cancel absolute rotation action.

Example: ros2_service_server

Install ROS2. This has been tested to work against "Galactic" release, using either eProsima FastDDS or RTI Connext DDS (rmw_connextdds, not rmw_connext_cpp).

Start server: cargo run --example=ros2_service_server

In another terminal or computer, run a client: ros2 run examples_rclpy_minimal_client client

Example: ros2_service_client

Similar to above.

Start server: ros2 run examples_rclpy_minimal_service service

Run client: cargo run --example=ros2_service_client

Used by

arora-sdk builds on this client to give an Arora device a ROS 2 surface over either backend: arora-bridge-ros2 (ROS as the device's remote — keys as topics, methods as services, task runs as actions) and arora-hal-ros2 (ROS as the device's own hardware). The ROS 2 message definitions they carry — including the hri_msgs / ROS4HRI human-robot-interaction vocabulary — live in arora-msgs-ros2; this crate is the message-type-agnostic transport underneath.

Related Work

  • ros2_rust is closest(?) to an official ROS2 client library. It links to ROS2 rcl library written in C.
  • rclrust is another ROS2 client library for Rust. It supports also ROS2 Services in addition to Topics. It links to ROS2 libraries, e.g. rcl and rmw.
  • rus2 exists, but appears to be inactive since September 2020.

License

Copyright 2022 Atostek Oy

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Acknowledgements

This crate is developed and open-source licensed by Atostek Oy.

About

ROS 2 client reimplementation

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages