Skip to main content

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. The tide.namespaces module 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:

  1. Load the configuration from config/config.yaml
  2. Start all the defined nodes
  3. 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:

  1. 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))
    
  2. 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))
    
  3. 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


Download files

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

Source Distribution

tide_sdk-0.1.7.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

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

tide_sdk-0.1.7-py3-none-any.whl (60.2 kB view details)

Uploaded Python 3

File details

Details for the file tide_sdk-0.1.7.tar.gz.

File metadata

  • Download URL: tide_sdk-0.1.7.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for tide_sdk-0.1.7.tar.gz
Algorithm Hash digest
SHA256 d12b454ca2d48bbfd05cdccc6b5076ab64fb585690f2114d99a9083fcf5c62b7
MD5 3a598e6c3c2a483a256d4f733a03228a
BLAKE2b-256 77b2a4552ca67705bd5b4d77ac0241ac874d0e160c9fbb6028b0939308619552

See more details on using hashes here.

Provenance

The following attestation bundles were made for tide_sdk-0.1.7.tar.gz:

Publisher: pypi.yaml on NorthCarolinaRivalRobotics/tide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tide_sdk-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: tide_sdk-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 60.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for tide_sdk-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 039542511995eea89bde755aae62865c4a14c25719a93322550f8c08945e8175
MD5 75010d0a870893e9fe4f0fcbff32d3cd
BLAKE2b-256 d3f9280e66f2dc7686c1a188c57cc1e703abcc760861dd428b58c1d24f7a5f53

See more details on using hashes here.

Provenance

The following attestation bundles were made for tide_sdk-0.1.7-py3-none-any.whl:

Publisher: pypi.yaml on NorthCarolinaRivalRobotics/tide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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