Key Expression Formats¶
ros-z uses key expression formats to map ROS 2 entities (topics, services, actions) to Eclipse Zenoh key expressions. The independent ros-z-protocol crate provides the format and determines how ros-z translates ROS 2 names for Zenoh routing and discovery.
Note
Key expression format is a runtime choice that affects how ros-z maps ROS 2 entities to Zenoh key expressions. Choose the format that matches your infrastructure for proper message routing.
Available Formats¶
ros-z supports multiple key expression formats for interoperability with different Zenoh-ROS bridges:
| Format | Compatibility | Use Case |
|---|---|---|
| RmwZenoh (default) | rmw_zenoh_cpp |
Standard ROS 2 Zenoh middleware |
| Ros2Dds | zenoh-bridge-ros2dds |
DDS bridge compatibility |
RmwZenoh Format (Default)¶
The RmwZenoh format targets compatibility with ROS 2's official Zenoh middleware implementation (rmw_zenoh_cpp).
Key Expression Patterns:
Topic keys: <domain_id>/<topic>/<type>/<hash>
Liveliness: @ros2_lv/<domain_id>/<entity_kind>/<namespace>/<name>/...
Example Topic Keys:
0/chatter/std_msgs::msg::dds_::String_/RIHS01_...
5/robot/sensors/camera/sensor_msgs::msg::dds_::Image_/RIHS01_...
Use this format when:
- Using
rmw_zenoh_cppas your ROS 2 middleware - Running pure ros-z deployments
- Requiring domain isolation via Zenoh
Ros2Dds Format¶
The Ros2Dds format targets compatibility with zenoh-bridge-ros2dds, which bridges standard DDS-based ROS 2 nodes to Zenoh.
Key Expression Format:
Example:
chatter/** # Topic /chatter (no domain prefix)
robot/sensors/camera/** # Topic /robot/sensors/camera
Use this format when:
- Bridging existing DDS-based ROS 2 systems to Zenoh
- Using
zenoh-bridge-ros2dds - Integrating with CycloneDDS or FastDDS nodes via Zenoh
Key Expression Behavior (IMPORTANT)¶
Understanding how ros-z converts topic names to key expressions is critical for debugging:
Topic Key Expressions (For Data Routing)¶
ALL entity types (publishers, subscriptions, services, clients, actions) use strip_slashes() behavior:
- Removes leading and trailing slashes only
- Preserves internal slashes for hierarchical routing
- Enables multi-segment topic names
Examples:
| ROS 2 Topic Name | Topic Key Expression | ✓/✗ |
|---|---|---|
/chatter |
0/chatter/... |
✅ Correct |
/robot/sensors |
0/robot/sensors/... |
✅ Correct |
/a/b/c |
0/a/b/c/... |
✅ Correct |
/talker/service |
0/talker/service/... |
✅ Correct |
Why preserve slashes?
- Zenoh uses
/for hierarchical routing - Enables wildcard subscriptions:
0/robot/** - Human-readable key expressions
Liveliness Tokens (For Discovery)¶
ALL fields in liveliness tokens use mangle_name() behavior:
- Replaces all
/with% - Ensures unambiguous parsing of entity metadata
- Machine-parsable format for discovery protocol
Examples:
| ROS 2 Name | Liveliness Field | ✓/✗ |
|---|---|---|
/chatter |
%chatter |
✅ Correct |
/robot/sensors |
%robot%sensors |
✅ Correct |
/my_node |
%my_node |
✅ Correct |
Why mangle slashes?
- Liveliness tokens have fixed structure:
@ros2_lv/<domain>/<kind>/<ns>/<name>/... - Prevents ambiguity when parsing fields
- Ensures reliable entity discovery
Why Two Different Behaviors?¶
This is intentional design in rmw_zenoh_cpp, not an inconsistency:
- Topic keys: Human-readable, hierarchical (optimized for Zenoh routing)
- Liveliness: Machine-parsable, unambiguous (optimized for discovery protocol)
Tip
If multi-segment topics like /robot/sensors/camera don't receive messages, check your ros-z version. Versions before 0.1.0 had a bug where publishers incorrectly mangled topic key expressions.
API Usage¶
Specifying Format at Context Creation¶
use ros_z::context::ZContextBuilder;
use ros_z_protocol::KeyExprFormat;
// Default (RmwZenoh)
let ctx = ZContextBuilder::default().build()?;
// Explicit format selection
let ctx = ZContextBuilder::default()
.keyexpr_format(KeyExprFormat::RmwZenoh)
.build()?;
// Ros2Dds format for DDS bridge compatibility
let ctx = ZContextBuilder::default()
.keyexpr_format(KeyExprFormat::Ros2Dds)
.build()?;
Key points:
- Format is set at context creation time
- All nodes and entities created from the context use the same format
- Default format is
KeyExprFormat::RmwZenoh - Format choice is type-safe and explicit
Creating Entities¶
Once you create the context with a format, all entities inherit it:
use ros_z_msgs::std_msgs::String as RosString;
use ros_z::Builder;
// Create context with RmwZenoh format (default)
let ctx = ZContextBuilder::default().build()?;
let node = ctx.create_node("my_node").build()?;
// Publisher uses context's format
let pub_rmw = node
.create_pub::<RosString>("chatter")
.build()?;
// Subscriber uses same format
let sub_rmw = node
.create_sub::<RosString>("chatter")
.build()?;
Mixing Formats (Advanced)¶
To communicate with both RmwZenoh and Ros2Dds systems, create separate contexts:
// Context for rmw_zenoh_cpp nodes
let ctx_rmw = ZContextBuilder::default()
.keyexpr_format(KeyExprFormat::RmwZenoh)
.build()?;
// Context for zenoh-bridge-ros2dds nodes
let ctx_dds = ZContextBuilder::default()
.keyexpr_format(KeyExprFormat::Ros2Dds)
.build()?;
// Create nodes from each context
let node_rmw = ctx_rmw.create_node("rmw_node").build()?;
let node_dds = ctx_dds.create_node("dds_node").build()?;
Architecture Diagrams¶
RmwZenoh Format Architecture¶
graph LR
A[ros-z Node<br/>RmwZenoh Format] -->|"0/chatter/**"| B[Zenoh Router<br/>rmw_zenoh]
B -->|"0/chatter/**"| C[ROS 2 Node<br/>rmw_zenoh_cpp]
Use case: Native Zenoh-based ROS 2 deployment
- All nodes use rmw_zenoh or ros-z
- Direct Zenoh communication
- Domain isolation via key expression prefix
Ros2Dds Format Architecture¶
graph LR
A[ros-z Node<br/>Ros2Dds Format] -->|"chatter/**"| B[zenoh-bridge-ros2dds<br/>Router + Bridge]
B -->|DDS| C[ROS 2 Node<br/>CycloneDDS/FastDDS]
Use case: Bridge existing DDS systems to Zenoh
- ROS 2 nodes use standard DDS middleware
zenoh-bridge-ros2ddstranslates DDS ↔ Zenoh- ros-z communicates via Zenoh side of bridge
Key Expression Generation Details¶
Understanding how ros-z-protocol generates key expressions helps with debugging and monitoring.
Topic Key Expression Structure¶
Components:
- Domain ID: ROS 2 domain (e.g.,
0,5) - Topic (stripped): Topic name with leading/trailing slashes removed, internal slashes preserved
- Type: Mangled message type (e.g.,
std_msgs::msg::dds_::String_) - Hash: Type hash for compatibility (e.g.,
RIHS01_...)
Example:
Topic: /robot/sensors/camera
Type: sensor_msgs/msg/Image
Hash: RIHS01_abc123...
Key Expression:
0/robot/sensors/camera/sensor_msgs::msg::dds_::Image_/RIHS01_abc123...
Liveliness Token Structure¶
ros-z mangles all name fields (/ → %):
Example:
ros-z-protocol Crate¶
The independent ros-z-protocol crate provides the key expression logic:
Features:
no_stdcompatible (withalloc)- Language-agnostic protocol layer (FFI-ready)
- Feature-gated format implementations
- Comprehensive unit tests
- Type-safe API
Cargo features:
Using ros-z-protocol directly:
use ros_z_protocol::{KeyExprFormat, entity::*};
let format = KeyExprFormat::default(); // RmwZenoh
// Generate topic key expression
let topic_ke = format.topic_key_expr(&entity)?;
// Generate liveliness token
let lv_ke = format.liveliness_key_expr(&entity, &zid)?;
// Parse liveliness token back to entity
let parsed_entity = format.parse_liveliness(&lv_ke)?;
See the ros-z-protocol documentation for details.
Troubleshooting¶
Multi-Segment Topics Not Working?¶
Symptom: Publisher publishes to /robot/sensors/camera but subscriber never receives messages.
Cause: Old versions of ros-z (before 0.1.0) incorrectly mangled slashes in topic key expressions.
Fix: Update to ros-z 0.1.0+ which correctly uses strip_slashes() for all topic key expressions.
Verify: Enable debug logging to check key expressions:
Look for key expressions like:
✅ Correct: 0/robot/sensors/camera/sensor_msgs::msg::Image_/...
❌ Wrong: 0/robot%sensors%camera/sensor_msgs::msg::Image_/...
No Messages Between ros-z and rmw_zenoh_cpp?¶
Check format: Ensure ros-z uses KeyExprFormat::RmwZenoh (the default).
Check type hash: Enable debug logging and compare type hashes:
Type hashes must match between ros-z and rmw_zenoh_cpp. If they don't, you may have:
- Different message definitions
- Different ROS 2 distros
- Outdated generated messages
No Messages Through zenoh-bridge-ros2dds?¶
Check format: Ensure ros-z uses KeyExprFormat::Ros2Dds:
Check bridge configuration: Verify zenoh-bridge-ros2dds is running and connected to the same Zenoh router as ros-z.
Format Comparison¶
When to Use RmwZenoh Format¶
✅ Use RmwZenoh when:
- Building pure Zenoh-based ROS 2 systems
- Using
rmw_zenoh_cppmiddleware - Requiring domain isolation
- Deploying new systems with native Zenoh support
- Maximizing Zenoh performance benefits
When to Use Ros2Dds Format¶
✅ Use Ros2Dds when:
- Bridging existing DDS-based ROS 2 systems
- Using
zenoh-bridge-ros2dds - Integrating with legacy ROS 2 infrastructure
- Gradual migration from DDS to Zenoh
- Heterogeneous deployments (DDS + Zenoh)