A minimal, single-file communication middleware built on Zenoh
Project description
ZRM (Zenoh ROS-like Middleware)
A minimal, single-file communication middleware built on Zenoh, providing a clean and simple API inspired by ROS2 patterns.
https://github.com/user-attachments/assets/3e41d9a7-f553-457b-a879-cae2af45bf63
Features
- Minimalist: Single-file implementation
- Type-safe: Protobuf-based serialization with runtime type checking
- Ergonomic: Pythonic API with sensible defaults
Installation
# Install from PyPI
pip install zrm
For development:
# Clone the repository and install dependencies
git clone https://github.com/JafarAbdi/zrm.git
cd zrm
uv sync
Development
Linting
Run linting and formatting checks using pre-commit:
uv run pre-commit run -a
This runs all configured linters and formatters on all files in the repository.
Testing
Run the test suite with pytest:
uv run pytest tests/ -v
Quick Start
Publisher/Subscriber
from zrm import Node
from zrm.msgs import geometry_pb2
# Create a node
node = Node("my_node")
# Create publisher and subscriber via node factory methods
pub = node.create_publisher("robot/pose", geometry_pb2.Pose2D)
sub = node.create_subscriber("robot/pose", geometry_pb2.Pose2D)
# Publish a message
pose = geometry_pb2.Pose2D(x=1.0, y=2.0, theta=0.5)
pub.publish(pose)
# Get latest message
current_pose = sub.latest()
if current_pose:
print(f"Position: x={current_pose.x}, y={current_pose.y}")
# Clean up
pub.close()
sub.close()
node.close()
Subscriber with Callback
def handle_pose(pose):
print(f"Received: x={pose.x}, y={pose.y}")
node = Node("listener_node")
sub = node.create_subscriber(
topic="robot/pose",
msg_type=geometry_pb2.Pose2D,
callback=handle_pose,
)
Service Server/Client
Services use namespaced Request/Response messages for better organization:
from zrm import Node
from zrm.srvs import examples_pb2
# Define service handler
def add_callback(req):
return examples_pb2.AddTwoInts.Response(sum=req.a + req.b)
# Create node
node = Node("service_node")
# Create service server via node factory method
server = node.create_service(
service="add_two_ints",
service_type=examples_pb2.AddTwoInts,
callback=add_callback,
)
# Create service client via node factory method
client = node.create_client(
service="add_two_ints",
service_type=examples_pb2.AddTwoInts,
)
# Call service
request = examples_pb2.AddTwoInts.Request(a=5, b=3)
response = client.call(request)
print(f"Sum: {response.sum}") # Output: 8
# Clean up
client.close()
server.close()
node.close()
Service Definition Pattern:
// Services must have nested Request and Response messages
message AddTwoInts {
message Request {
int32 a = 1;
int32 b = 2;
}
message Response {
int32 sum = 1;
}
}
Message Organization
ZRM uses a convention-based message organization:
Directory Structure
proto/
├── msgs/ # Message definitions
│ ├── geometry.proto
│ ├── sensor.proto
│ └── header.proto
└── srvs/ # Service definitions
├── std.proto
└── examples.proto
src/zrm/
├── msgs/ # Generated message modules
│ ├── geometry_pb2.py
│ ├── sensor_pb2.py
│ └── header_pb2.py
└── srvs/ # Generated service modules
├── std_pb2.py
└── examples_pb2.py
Generating Python Code
# Generate message modules
protoc --pyi_out=src --python_out=src --proto_path=zrm/msgs=proto/msgs/ $(fd -e proto . proto/msgs/)
# Generate service modules
protoc --pyi_out=src --python_out=src --proto_path=zrm/srvs=proto/srvs/ $(fd -e proto . proto/srvs/)
Standard Messages
Messages (zrm.msgs):
- geometry: Point, Vector3, Quaternion, Pose, Pose2D, Twist, PoseStamped
- sensor: Image, LaserScan, PointCloud2
- header: Header
Services (zrm.srvs):
- std: Trigger
CLI Tools
ZRM provides command-line tools for inspecting and interacting with the network:
Topic Commands
# List all topics and their publishers/subscribers
zrm-topic list
# Echo messages from a topic (auto-discovers type)
zrm-topic echo robot/pose
# Echo with explicit type
zrm-topic echo robot/pose -t zrm/msgs/geometry/Pose2D
# Publish to a topic
zrm-topic pub robot/pose "x: 1.0 y: 2.0 theta: 0.5" -t zrm/msgs/geometry/Pose2D -r 10
# Measure topic frequency
zrm-topic hz robot/pose
Node Commands
# List all nodes in the network
zrm-node list
Service Commands
# List all services in the network
zrm-service list
# Call a service (auto-discovers type)
zrm-service call add_two_ints 'a: 1 b: 2'
# Call with explicit type
zrm-service call add_two_ints 'a: 1 b: 2' -t zrm/srvs/examples/AddTwoInts
Examples
See examples/ directory for complete working examples:
talker.py/listener.py: Basic publisher/subscriber patternservice_server.py/service_client.py: Service request/response pattern- Graph discovery and introspection
Acknowledgements
- The Graph class is inspired by ros-z
- Built on Eclipse Zenoh for efficient pub/sub and query/reply patterns
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zrm-1.3.0.tar.gz.
File metadata
- Download URL: zrm-1.3.0.tar.gz
- Upload date:
- Size: 19.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6341f3f57fa25e428d50f5fd6a729926197638f56ed08a4d021da6c311b9674e
|
|
| MD5 |
132a964d39f23537c157cadb4e4c1902
|
|
| BLAKE2b-256 |
3ef038b25b77bcbcbfd70afd0a6b324a04cb80742b5a3e33890f1a67107980f5
|
File details
Details for the file zrm-1.3.0-py3-none-any.whl.
File metadata
- Download URL: zrm-1.3.0-py3-none-any.whl
- Upload date:
- Size: 28.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
739d752e3dede5a1e41fbb760d4e915bb8df031a87f91f00d47b8bda4f1de282
|
|
| MD5 |
4fca338c36c078d261e81580eafb8ae1
|
|
| BLAKE2b-256 |
245fe7bfc1b27605a3b802a0b8282115aa5bfc42913b3c6f67e10a5474208a01
|