Skip to main content

Add your description here

Project description

ZRM (Zenoh ROS-like Middleware)

CI

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-33.0-linux-x86_64/bin/protoc --pyi_out=src --python_out=src --proto_path=zrm/msgs=proto/msgs/ $(fd -e proto . proto/msgs/)

# Generate service modules
../protoc-33.0-linux-x86_64/bin/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 pattern
  • service_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zrm-1.1.1.tar.gz (17.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zrm-1.1.1-py3-none-any.whl (25.3 kB view details)

Uploaded Python 3

File details

Details for the file zrm-1.1.1.tar.gz.

File metadata

  • Download URL: zrm-1.1.1.tar.gz
  • Upload date:
  • Size: 17.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for zrm-1.1.1.tar.gz
Algorithm Hash digest
SHA256 da1e2d724464c95f7609744cd176e21dabaac6824c5e0392557193f56211f8b5
MD5 1d3115f2c3034333b03b627d17172ec9
BLAKE2b-256 40ebf090a287daeb287dc5acccec784c2e93cb916b81c4eb30be83d525137615

See more details on using hashes here.

File details

Details for the file zrm-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: zrm-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 25.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for zrm-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 eb472d9acdfa9abe5a4d0ece6f1383dbeecdbdd8b5a121854afd21e7f69305de
MD5 dbe9cb2af38c669d7267285e498b5ac1
BLAKE2b-256 8d268e09600468f3d91d37ebb040fa45098e52160def1edafe286c7497ebeeae

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page