A Zenoh-based robotics framework with opinionated namespacing
Project description
Tide
A lightweight, strongly-typed framework for robotics based on Zenoh, with opinionated namespacing.
Overview
Tide wraps Zenoh's key/value-based pub-sub-query model in a set of strongly-typed "robot nodes," each running in its own thread that talks over a shared Zenoh session.
The framework enforces an opinionated namespacing pattern:
/{robot_id}/{group}/{topic}
For example:
/frogbot/cmd/twist- Command velocity for the "frogbot" robot/frogbot/state/pose2d- 2D pose state for the robot/frogbot/sensor/lidar- Lidar data from the robot See docs/namespacing.md for a full list of reserved namespaces. Thetide.namespacesmodule exposes enums for these topics so you don't have to hardcode strings.
Features
- Opinionated namespacing: Clear, consistent naming pattern for all messages
- Zero-config networking: Lean on Zenoh's peer discovery for automatic device connection
- Strongly-typed messages: Uses Pydantic models for validation and serialization
- Pythonic, thread-based architecture: Each node runs in its own thread to keep latency low
- Callback-based: Register callbacks for specific topics
- Command-line interface: Easily create and manage Tide projects
Installation
pip install tide-sdk
Or with uv (recommended):
uv add tide-sdk
Command-Line Interface
Tide comes with a powerful CLI to help you create and manage projects.
Creating a New Project
tide init my_robot_project --robot-id robot1
This creates a new project directory with the following structure:
my_robot_project/
├── config/
│ └── config.yaml # Configuration for nodes
├── nodes/
│ ├── robot_node.py # Main robot control node
│ ├── teleop_node.py # Node for commanding the robot
│ └── monitor_node.py # Node for monitoring state
├── main.py # Project entry point
├── README.md # Project documentation
└── requirements.txt # Dependencies
Running Your Project
To start your Tide project:
cd my_robot_project
tide up
This will:
- Load the configuration from
config/config.yaml - Start all the defined nodes
- Display a table of running nodes
You can specify a custom configuration file:
tide up --config path/to/custom_config.yaml
Checking Node Status
To discover running Tide nodes on the network:
tide status
This shows a list of all discovered Tide nodes. The discovery uses the
*/*/* wildcard so custom groups like the ping-pong example are also found.
The output includes:
- Robot ID
- Group
- Topic
For longer discovery times:
tide status --timeout 5.0
Programming API
Defining a Node
from tide.core.node import BaseNode
from tide.models import Twist2D, Pose2D, to_zenoh_value
from tide import CmdTopic, StateTopic
class MyRobotNode(BaseNode):
ROBOT_ID = "myrobot" # Your robot's unique ID
GROUP = "controller" # Group for this node
def __init__(self, *, config=None):
super().__init__(config=config)
# Subscribe to command velocity using the reserved enum
self.subscribe(CmdTopic.TWIST.value, self._on_cmd_vel)
def _on_cmd_vel(self, data):
# Process command velocity message
# ...
def step(self):
# Called at the node's update rate
# Publish robot state
pose = Pose2D(x=1.0, y=2.0, theta=0.5)
self.put(StateTopic.POSE2D.value, to_zenoh_value(pose))
Launching Nodes
import time
from tide.core.utils import launch_from_config
from tide.config import load_config
def main():
# Load configuration
config = load_config('config.yaml')
# Launch nodes
nodes = launch_from_config(config)
try:
print(f"Started {len(nodes)} nodes. Press Ctrl+C to exit.")
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Interrupted by user")
for node in nodes:
node.stop()
if __name__ == "__main__":
main()
Configuration File
session:
mode: peer # Mesh network
nodes:
- type: my_package.MyRobotNode
params:
robot_id: "robot1"
- type: my_package.TeleopNode
params:
robot_id: "robot1"
See docs/config_spec.md for the formal configuration specification.
Complete Example
The generated project template includes a complete working example. Here's how the nodes work together:
-
TeleopNode - Generates simple oscillating motion commands (simulating joystick)
def step(self): # Create sinusoidal motion pattern t = time.time() self.linear_vel = 0.5 * math.sin(0.2 * t) self.angular_vel = 0.2 * math.cos(0.1 * t) # Create and publish the command cmd = Twist2D( linear=Vector2(x=self.linear_vel), angular=self.angular_vel ) self.put(CmdTopic.TWIST.value, to_zenoh_value(cmd))
-
RobotNode - Receives commands and simulates robot movement
def _on_cmd_vel(self, data): cmd = from_zenoh_value(data, Twist2D) self.linear_vel = cmd.linear.x self.angular_vel = cmd.angular def step(self): # Simple motion model - integrate velocity dt = time.time() - self.last_update self.theta += self.angular_vel * dt self.x += self.linear_vel * math.cos(self.theta) * dt self.y += self.linear_vel * math.sin(self.theta) * dt # Publish the current pose pose = Pose2D(x=self.x, y=self.y, theta=self.theta) self.put(StateTopic.POSE2D.value, to_zenoh_value(pose))
-
MonitorNode - Displays the robot's state
def _on_pose(self, data): pose = from_zenoh_value(data, Pose2D) print(f"Robot pose: x={pose.x:.2f}, y={pose.y:.2f}, theta={pose.theta:.2f}")
This demonstrates:
- Opinionated topic naming (
cmd/twist,state/pose2d) - Callback-based architecture for handling messages
- Strong typing with Pydantic models
- Serialization/deserialization with Zenoh
Common Message Types
- Twist2D: 2D velocity command (linear x, y and angular z)
- Pose2D: 2D pose (x, y, theta)
- Pose3D: 3D pose (position and orientation)
- Acceleration3D: 3D acceleration (linear and angular)
- LaserScan: 2D laser scan data
Built-in Nodes
Tide ships with a small library of reusable nodes. PIDNode implements a
basic PID controller that reads a state and reference value and publishes a
command. WebcamNode captures frames from a V4L2 webcam and publishes raw
images. See docs/pid_node.md and
docs/webcam_node.md for configuration details.
License
MIT
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 tide_sdk-0.1.11.tar.gz.
File metadata
- Download URL: tide_sdk-0.1.11.tar.gz
- Upload date:
- Size: 30.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2daafb06b0d55beca29b1a8f4cf781f9ddc8ef38c853abcaf3bf979850d13764
|
|
| MD5 |
34b227b6b64487176b52a69d91d84d60
|
|
| BLAKE2b-256 |
576a5827ddb6ed214d8d3a506362281e817d9423a1d6f63751e4f271fad21265
|
Provenance
The following attestation bundles were made for tide_sdk-0.1.11.tar.gz:
Publisher:
pypi.yaml on NorthCarolinaRivalRobotics/tide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tide_sdk-0.1.11.tar.gz -
Subject digest:
2daafb06b0d55beca29b1a8f4cf781f9ddc8ef38c853abcaf3bf979850d13764 - Sigstore transparency entry: 436724633
- Sigstore integration time:
-
Permalink:
NorthCarolinaRivalRobotics/tide@17e32b00e17279dd3ee4ceb35a86b679e1fc042d -
Branch / Tag:
refs/tags/v0.1.11 - Owner: https://github.com/NorthCarolinaRivalRobotics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@17e32b00e17279dd3ee4ceb35a86b679e1fc042d -
Trigger Event:
push
-
Statement type:
File details
Details for the file tide_sdk-0.1.11-py3-none-any.whl.
File metadata
- Download URL: tide_sdk-0.1.11-py3-none-any.whl
- Upload date:
- Size: 60.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfa7ccd0b67464d7776d1b6d9954e3679390ed88011af91a2fae324b84a115c3
|
|
| MD5 |
8af9e41e8bb5a82d7364b8027bafdf9f
|
|
| BLAKE2b-256 |
aa27fc5fa362af2c4c642d388e14642fd70894ad4209dad1ef1d8a4e624e309b
|
Provenance
The following attestation bundles were made for tide_sdk-0.1.11-py3-none-any.whl:
Publisher:
pypi.yaml on NorthCarolinaRivalRobotics/tide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tide_sdk-0.1.11-py3-none-any.whl -
Subject digest:
cfa7ccd0b67464d7776d1b6d9954e3679390ed88011af91a2fae324b84a115c3 - Sigstore transparency entry: 436724653
- Sigstore integration time:
-
Permalink:
NorthCarolinaRivalRobotics/tide@17e32b00e17279dd3ee4ceb35a86b679e1fc042d -
Branch / Tag:
refs/tags/v0.1.11 - Owner: https://github.com/NorthCarolinaRivalRobotics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@17e32b00e17279dd3ee4ceb35a86b679e1fc042d -
Trigger Event:
push
-
Statement type: