diff --git a/assets/contributors.csv b/assets/contributors.csv index e871b8fcb..6df4525ab 100644 --- a/assets/contributors.csv +++ b/assets/contributors.csv @@ -91,4 +91,6 @@ Gian Marco Iodice,Arm,,,, Aude Vuilliomenet,Arm,,,, Andrew Kilroy,Arm,,,, Peter Harris,Arm,,,, +Chenying Kuo,Adlink,evshary,evshary,, +William Liang,,wyliang,,, Waheed Brown,Arm,https://github.com/armwaheed,https://www.linkedin.com/in/waheedbrown/,, diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/1_intro-zenoh.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/1_intro-zenoh.md new file mode 100644 index 000000000..65d30cc1e --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/1_intro-zenoh.md @@ -0,0 +1,62 @@ +--- +title: Introduction to Zenoh +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## The Need for Scalable Communication in Robotics and Edge Computing + +Modern robotics and industrial IoT (IIoT) systems are evolving rapidly—from indoor collaborative arms on assembly lines to fleets of outdoor autonomous delivery robots. +These applications must operate in real-time, often across multiple compute nodes, over networks that may span factory LANs, 5G cellular links, or even cloud data centers. + +Such systems require fast, reliable, and scalable data communication between nodes. +This includes not just broadcasting sensor updates or actuator commands (i.e., pub/sub), but also performing state queries, storing values for later use, and even distributed computation across nodes. A modern protocol must be: +* Low-latency: Immediate delivery of time-critical messages. +* High-throughput: Efficient data flow across many devices. +* Decentralized: No reliance on central brokers or fixed infrastructure. +* Flexible: Able to run on lightweight edge devices, across WANs, or inside cloud-native environments. + +Traditional communication stacks such as DDS ([Data Distribution Service](https://en.wikipedia.org/wiki/Data_Distribution_Service)) serve as the backbone for middleware like ROS 2. However, DDS struggles in multi-network or wireless environments where multicast is unavailable, or NAT traversal is needed. +These constraints can severely impact deployment and performance at the edge. + + +## Zenoh: An Open-Source Pub/Sub Protocol for the Industrial Edge + +[Eclipse Zenoh](https://zenoh.io/) is a modern, [open-source](https://github.com/eclipse-zenoh/zenoh) data-centric communication protocol that goes beyond traditional pub/sub. Designed specifically for edge computing, IIoT, robotics, and autonomous systems, Zenoh unifies: + +* Data in motion through a powerful and efficient pub/sub model. +* Data at rest via geo-distributed storage plugins. +* Data in use with direct query support. +* On-demand computation via queryable nodes that can generate data dynamically. + +Unlike most traditional stacks, Zenoh is fully decentralized and designed to operate across cloud-to-edge-to-thing topologies, making it ideal for industrial robotics, autonomous systems, and smart environments. +It supports heterogeneous platforms, is implemented in Rust for performance and safety, and also offers bindings for Python, enabling rapid prototyping. + +Zenoh is particularly effective in wireless, 5G, or cross-network deployments where multicast and DDS fall short. +Its routing engine avoids excessive discovery traffic, conserves bandwidth, and supports seamless bridging between legacy ROS 2/DDS apps and modern, optimized Zenoh networks using zenoh-bridge-dds. + +In this learning path, you’ll use Zenoh to build and validate a multi-node distributed communication system across multiple Arm-based platforms, gaining hands-on experience with data exchange and coordination between edge devices. + +To make the upcoming demo more intuitive and easy to follow, we’ll demonstrate the setup using two physical Cortex-A Linux devices. + +I’ll be using Raspberry Pi boards in this learning path, but you’re free to substitute them with any Cortex-A devices that support network connectivity with Linux-based OS installed, depending on your development setup. + +In real-world ROS 2 deployment scenarios, developers typically conduct validation and performance testing across systems with more than two nodes. +To simulate such environments, using [Arm virtual hardware](https://www.arm.com/products/development-tools/simulation/virtual-hardware) is also a common and efficient approach. + +This will help you quickly validate your architecture choices and communication patterns when designing distributed applications. + +* Raspberry Pi, +* Linux-based Cortex-A, or +* Arm Virtual Hardware (AVH). + +After this learning path, you will: +* Understand the core architecture and data flow principles behind Eclipse Zenoh, including its support for pub/sub, querying, and queryable edge functions. +* Build and run distributed Zenoh examples across multiple Arm-based nodes—using Raspberry Pi or AVH to simulate scalable deployment environments. +* Rebuild and extend the Zenoh queryable example to simulate edge-side logic. + +By the end of this learning path, you’ll have deployed a fully functional, scalable, and latency-aware Zenoh system. + +You can also check [here](https://zenoh.io/docs/getting-started/first-app) to find some simple examples. diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/2_zenoh-install.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/2_zenoh-install.md new file mode 100644 index 000000000..5a750202e --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/2_zenoh-install.md @@ -0,0 +1,107 @@ +--- +title: Setting Up Zenoh on Arm Devices +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Setting Up Zenoh on Arm Devices + +The following instructions are verified on both Raspberry Pi 4/5 and Arm Virtual Hardware, but you can implement them on any Cortex-A Linux device. + +Before building Zenoh, make sure your system has the necessary development tools and runtime libraries. + +### Install the Rust build environment + +First, we need to install the [Rust](https://www.rust-lang.org/) build environment, since the core of Zenoh is totally developed using Rust to keep it safe and efficient. + +Follow this [installation guide](https://learn.arm.com/install-guides/rust/) to install Rust and Cargo on Arm Linux, a build system for Rust. Or, simply use the following commands. + +```bash +curl https://sh.rustup.rs -sSf | sh +``` + +Follow the prompts to complete the installation. If successful, you’ll see: + +```output +Rust is installed now. Great! +``` + +For more details, refer to [Rust’s official install guide.](https://doc.rust-lang.org/cargo/getting-started/installation.html#install-rust-and-cargo) + +### Install ROS 2 + +[Robot Operating System](https://www.ros.org/) is a set of software libraries and tools that help you build robot applications. From drivers to state-of-the-art algorithms, and with powerful developer tools, ROS has what you need for your next robotics project. And it's all open source. + +Since ROS was started in 2007, a lot has changed in the robotics and ROS community. The goal of the [ROS 2](https://docs.ros.org/en/rolling/index.html) project is to adapt to these changes, leveraging what is great about ROS 1 and improving what isn’t. + +Here is the quick [installation guide](https://learn.arm.com/install-guides/ros2/) about how to install ROS 2 in Arm platform. + +### Download and build the Zenoh source + +Now, we can clone the Zenoh. + +```bash +cd ~ +git clone https://github.com/eclipse-zenoh/zenoh.git +``` + +After that, simply use cargo to build the source. + +```bash +cd zenoh +cargo build --release --all-targets -j $(nproc) +``` + +This will take several minutes depending on your device. Once the installation is complete, you should see: + +```output +cargo build --release --all-targets -j $(nproc) + Updating crates.io index + Downloaded humantime v2.2.0 + Downloaded spin v0.10.0 + Downloaded crossbeam-channel v0.5.14 + Downloaded uhlc v0.8.1 + Downloaded 4 crates (182.5 KB) in 2.19s +warning: output filename collision. +The lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)` has the same output filename as the lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)`. +Colliding filename is: /home/ubuntu/zenoh/target/release/deps/libzenoh_plugin_storage_manager.so +The targets should have unique names. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . +warning: output filename collision. +The lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)` has the same output filename as the lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)`. +Colliding filename is: /home/ubuntu/zenoh/target/release/deps/libzenoh_plugin_storage_manager.so.dwp +The targets should have unique names. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . +warning: output filename collision. +The lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)` has the same output filename as the lib target `zenoh_plugin_storage_manager` in package `zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager)`. +Colliding filename is: /home/ubuntu/zenoh/target/release/deps/libzenoh_plugin_storage_manager.rlib +The targets should have unique names. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . + Compiling proc-macro2 v1.0.86 + Compiling unicode-ident v1.0.13 + Compiling libc v0.2.158 + Compiling version_check v0.9.5 + Compiling autocfg v1.3.0 +... + Compiling zenoh-link-quic v1.4.0 (/home/ubuntu/zenoh/io/zenoh-links/zenoh-link-quic) + Compiling zenoh_backend_traits v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-backend-traits) + Compiling zenoh-plugin-storage-manager v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-storage-manager) + Compiling zenoh-ext v1.4.0 (/home/ubuntu/zenoh/zenoh-ext) + Compiling zenoh-ext-examples v1.4.0 (/home/ubuntu/zenoh/zenoh-ext/examples) + Compiling zenoh-plugin-example v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-plugin-example) + Compiling zenoh-backend-example v1.4.0 (/home/ubuntu/zenoh/plugins/zenoh-backend-example) + Finished `release` profile [optimized] target(s) in 6m 28s +``` + +After the build process, the binary executables will be stored under the directory of `~/zenoh/target/release/examples/`. + +{{% notice Note %}} +Installation time may vary depending on your device’s processing power. +{{% /notice %}} + +With Zenoh successfully compiled, you’re ready to explore how nodes communicate using the Zenoh runtime. diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/3_zenoh-multinode.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/3_zenoh-multinode.md new file mode 100644 index 000000000..840dcc38d --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/3_zenoh-multinode.md @@ -0,0 +1,114 @@ +--- +title: Setting Up a Multi-Node Environment +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Deploying Zenoh on Multiple Raspberry Pi Devices Using Docker + +After building Zenoh and its core examples, your next step is to deploy them across multiple Arm-based devices. + +In this session, you’ll use Raspberry Pi boards to simulate a scalable, distributed environment—but the same workflow applies to any Arm Linux system, including Arm cloud instances and Arm Virtual Hardware. + +You’ll learn how to use Docker to deploy the environment on physical devices, and how to duplicate virtual instances using snapshot cloning on Arm Virtual Hardware. + +This setup lets you simulate `real-world`, `cross-node communication`, making it ideal for validating Zenoh’s performance in robotics and industrial IoT use cases. + +### Install Docker on Raspberry Pi + +To simplify this process and ensure consistency, you’ll use Docker to containerize your Zenoh and ROS 2 environment. +This lets you quickly replicate the same runtime on any device without needing to rebuild from source. + +This enables multi-node testing and real-world distributed communication scenarios. + +First, install the docker environment on each of Raspberry Pi if you don't have that. + +```bash +curl -sSL https://get.docker.com | sh +sudo usermod -aG docker pi +``` + +Log out and back in, or run newgrp docker to activate Docker group permissions. + +### Create a ROS 2 + DDS Docker Image + +In a working directory, create a `Dockerfile` with the following content to create the ROS 2 / DDS docker image. + +```bash +FROM ros:galactic +RUN apt-get update +RUN apt-get install -y ros-galactic-demo-nodes-cpp ros-galactic-rmw-cyclonedds-cpp ros-galactic-turtlesim +ENV RMW_IMPLEMENTATION=rmw_cyclonedds_cpp +CMD bash +``` + +Under the directory where the above Dockerfile exists, run the following command to generate the docker image. + +```bash +$ docker build -t zenoh-node . +``` + +After this has been done, the created ROS 2 docker image can be seen by the following command. + +```bash +$ docker images | grep zenoh-node +``` + +```output +zenoh-node latest b7a9c27cf8a8 About a minute ago 962MB +``` + +### Transfer the Docker Image on Another RPi + +You now need to transfer the Docker image to your second device. Choose one of the following methods: + +You have two options: + +Option 1: Save and copy via file + +```bash +docker save zenoh-node > zenoh-node.tar +scp zenoh-node.tar pi@:/home/pi/ +``` + +On the target device: +```bash +docker load < zenoh-node.tar +``` + +Option 2: Push to a container registry (e.g., DockerHub or GHCR). + +You can also push the image to Docker Hub or GitHub Container Registry and pull it on the second device. + +### Run the Docker Image + +Once the image is successfully loaded into second device, you can run the container by + +```bash +docker run -it --network=host zenoh-node +``` + +Now, all the Zenoh example binaries are now available within this container, allowing you to test pub/sub and query flows across devices. + +### Another Duplicate Setting Option on Arm Virtual Hardware + +If you have [Corellium](https://www.corellium.com/) account, you can + +1. Set up and install Zenoh on a single AVH instance. +2. Use the [Clone](https://support.corellium.com/features/snapshots) function to duplicate the environment. +3. Optionally, you may optionally rename the device to avh* for easy device recognition by changing the setting in the `/etc/hostname` file. + +## Run Zenoh in Multi-Node Environment + +You’re now ready to run and test Zenoh communication flows across distributed edge devices. + +The source of the examples written in Rust will be provided, and both are interoperable. The +Rust binaries are already available under: `$ZENOH_LOC/target/release/examples/` directory. + +The following sections illustrate the procedures to run the Zenoh examples so as to demonstrate the primary capabilities of Zenoh +1. Basic Pub/Sub – for real-time message distribution +2. Query and Storage – to persist and retrieving historical data +3. Queryable – to enable on-demand remote computation +4. Dynamic Queryable with Computation diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/4_zenoh-ex1-pubsub.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/4_zenoh-ex1-pubsub.md new file mode 100644 index 000000000..e1a223c56 --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/4_zenoh-ex1-pubsub.md @@ -0,0 +1,39 @@ +--- +title: Zenoh Example-1 Simple Pub/Sub +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Example 1: Simple Pub/Sub + +This first test demonstrates Zenoh’s real-time publish/subscribe model using two Raspberry Pi devices. + +The following command is to initiate a subscriber for a key expression `demo/example/**`, i.e. a set of topics starting with the path `demo/example`. + +### Step 1: Run Subscriber + +Log in to Pi using any of the methods: + +```bash +cd ~/zenoh/target/release/examples +./z_sub +``` + +### Step 2: Run Publisher + +Then, log in to another machine Pi. + +```bash +cd ~/zenoh/target/release/examples +./z_pub +``` + +The result will look like: +![img1 alt-text#center](zenoh_ex1.gif "Figure 1: Simple Pub/Sub") + +In the left-side window, I have logged into the device Pi4 and run the z_sub program. +It receives values with the key `demo/example/zenoh-rs-pub` continuously published by z_pub running on Pi in the right-side window. + +This basic example shows Zenoh’s zero-config discovery and low-latency pub/sub across physical nodes. diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/5_zenoh-ex2-storagequery.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/5_zenoh-ex2-storagequery.md new file mode 100644 index 000000000..d99dbcceb --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/5_zenoh-ex2-storagequery.md @@ -0,0 +1,74 @@ +--- +title: Zenoh Example-2 Storage and Query +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Example 2: Storage and Query + +The second example adds Zenoh’s data storage and querying capabilities—enabling nodes to retrieve historical values on demand. + +Building on the previous Pub/Sub example, you’ll now explore how Zenoh supports `persistent data storage` and `on-demand querying` -- a powerful feature for robotics and IIoT applications. + +In a typical warehouse or factory scenario, autonomous robots may periodically publish sensor data (e.g., position, temperature, battery level), and a central system—or another robot—may later need to query the latest state of each unit. + +Unlike Pub/Sub, which requires live, real-time message exchange, Zenoh’s storage and query model enables asynchronous access to data that was published earlier, even if the original publisher is no longer online. + +In this example, you’ll run the zenohd daemon with in-memory storage and use z_put to publish data and z_get to retrieve it. + +This is especially useful for distributed systems where nodes may intermittently connect or request snapshots of state from peers. + + +### Step 1: Start the Zenoh Daemon with In-Memory Storage + +On one Raspberry Pi, launch the Zenoh daemon with a configuration that enables in-memory storage for keys under demo/example/**: + +```bash +cd ~/zenoh/target/release/ +./zenohd --cfg='plugins/storage_manager/storages/demo:{key_expr:"demo/example/**",volume:"memory"}' & +``` + +This starts the Zenoh daemon with in-memory storage support. + +You should see log messages indicating that the storage_manager plugin is loaded. +If port 7447 is already in use, either stop any previous Zenoh processes or configure a custom port using the listen.endpoints.router setting. + +### Step 2: Publish Data + +On 2nd device, use z_put to send a key-value pair that will be handled by the zenohd storage: + +```bash +cd ~/zenoh/target/release/examples +./z_put -k demo/example/test1 -p "Hello from storage!" +``` + +This command stores the string `Hello from storage!` under the key demo/example/test1. + + +### Step 3: Query the Data + +Back on first Raspberry Pi, you can now query the stored data from any Zenoh-connected node: + +```bash +cd ~/zenoh/target/release/examples +./z_get -s demo/example/test1 +``` + +You should see an output similar to: + +```bash +Sending Query 'demo/example/test1'... +>> Received ('demo/example/test1': 'Hello from storage!') +``` + +The result will look like: +![img2 alt-text#center](zenoh_ex2.gif "Figure 2: Storage and Query") + +{{% notice tip %}} +If you have more than two Raspberry Pi devices, you can run the z_get command on a third RPi to validate that storage queries work seamlessly across a multi-node setup. +{{% /notice %}} + +This example shows how Zenoh’s Storage + Query model supports asynchronous data access and resilient state-sharing—critical capabilities in robotics and industrial IoT systems where network connectivity may be intermittent or system components loosely coupled. + diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/6_zenoh-ex3-queryable.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/6_zenoh-ex3-queryable.md new file mode 100644 index 000000000..16e0b3557 --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/6_zenoh-ex3-queryable.md @@ -0,0 +1,73 @@ +--- +title: Zenoh Example-3 Computation on Query using Queryable +weight: 7 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Example 3: Computation on Query using Queryable + +Next, you’ll explore Zenoh’s queryable capability, which lets a node dynamically respond to data queries by executing a custom computation or data generation function in this example. + +Unlike zenohd which simply returns stored data, a queryable node can register to handle a specific key expression and generate responses at runtime. This is ideal for distributed computing at the edge, where lightweight devices—such as Raspberry Pi nodes—can respond to requests with calculated values (e.g., sensor fusion, AI inference results, or diagnostics). + +### Use Case: On-Demand Battery Health Estimation + +Imagine a robot fleet management system where the central planner queries each robot for its latest battery health score, which is not published continuously but calculated only when queried. + +This saves bandwidth and enables edge compute optimization using Zenoh’s Queryable. + +### Step 1: Launch a Queryable Node + +On one Raspberry Pi device, run the built-in Zenoh example to register a queryable handler. + +```bash +cd ~/zenoh/target/release/examples +./z_queryable +``` + +You'll see the output like: + +``` +pi@raspberrypi:~/zenoh/target/release/examples$ ./z_queryable +Opening session... +Declaring Queryable on 'demo/example/zenoh-rs-queryable'... +Press CTRL-C to quit... +``` + +The node is now ready to accept queries on the key demo/example/zenoh-rs-queryable and respond with a predefined message. + +### Step 2: Trigger a Query from Another Node + +On another Raspberry Pi device, run: + +```bash +cd ~/zenoh/target/release/examples +./z_get -s demo/example/zenoh-rs-queryable +``` + +You should see: + +``` +./z_get -s demo/example/zenoh-rs-queryable +Opening session... +Sending Query 'demo/example/zenoh-rs-queryable'... +>> Received ('demo/example/zenoh-rs-queryable': 'Queryable from Rust!') +``` + +The result will look like: +![img3 alt-text#center](zenoh_ex3.gif "Figure 3: Computation on Query using Queryable") + +The value you receive comes not from storage, but from the computation inside the queryable handler. + +### Real-World Application: Distributed Inference & Computation + +This model enables edge-based intelligence, such as: +- Executing custom logic in response to a query (e.g., “calculate load average”) +- Triggering ML inference (e.g., “classify image X on demand”) +- Decentralized diagnostics (e.g., “report actuator status”) + +Queryable is a key feature for data-in-use scenarios, allowing fine-grained, on-demand compute inside your Zenoh-powered architecture. + +Next, you’ll extend this Queryable pattern to perform parameterized computation — simulating edge diagnostics and adaptive inference. \ No newline at end of file diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/7_zenoh-querycomp.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/7_zenoh-querycomp.md new file mode 100644 index 000000000..ecc463263 --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/7_zenoh-querycomp.md @@ -0,0 +1,166 @@ +--- +title: Zenoh Example-4 Dynamic Queryable with Computation +weight: 8 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Parameterized Battery Health Estimation with Zenoh Queryable + +Finally, you’ll combine pub/sub, storage, and queryable components to simulate a distributed computation flow—demonstrating how Zenoh enables intelligent, coordinated edge systems. + +You’ll learn how to use Zenoh’s Queryable API in Rust to build a parameterized query system for estimating battery health at the edge. + +This extends a previous example by supporting runtime query parameters like battery level and temperature. + +## Use Case: Real-time Battery Health via Computation + +In robotic fleet management, a central controller may need to assess each robot’s battery health on demand. + +Instead of streaming data continuously, robots expose a queryable endpoint that returns a real-time health score based on current battery level and temperature. + +This saves bandwidth and enables lightweight edge-side decision-making.” + +### Step 1: Create a New Zenoh Rust Project + +On any Raspberry Pi: + +```bash +cd ~/zenoh +cargo new zenoh_battery_estimator +``` + +Update following `dependencies` setting at ~/zenoh/zenoh_battery_estimator/Cargo.toml. + +``` +[dependencies] +zenoh = { path = "../zenoh" } +tokio = { version = "1", features = ["full"] } +url = "2" +``` + +### Step 2: Implement the Queryable Node + +Then, log in to another machine Pi. + +Replace the contents of ~/zenoh/zenoh_battery_estimator/src/main.rs with: + +```rust +use zenoh::{open, Config}; +use std::collections::HashMap; +use url::form_urlencoded; + +#[tokio::main] +async fn main() -> zenoh::Result<()> { + let session = open(Config::default()).await?; + + let _queryable = session + .declare_queryable("robot/battery/estimate") + .callback(|query| { + tokio::spawn(async move { + let selector = query.selector(); + let key = selector.key_expr(); + + let params = selector.parameters().as_str(); + + let decoded: HashMap<_, _> = + form_urlencoded::parse(params.as_bytes()).into_owned().collect(); + + let battery = decoded + .get("level") + .unwrap_or(&"50".to_string()) + .parse::() + .unwrap_or(50); + + let temp = decoded + .get("temp") + .unwrap_or(&"25".to_string()) + .parse::() + .unwrap_or(25); + + let health_score = 100 - (100 - battery) - ((temp.saturating_sub(25)) / 2); + let response = format!("Estimated battery health: {}%", health_score); + + let _ = query.reply(key, response).await; + }); + }) + .await?; + + println!("Queryable running on 'robot/battery/estimate'"); + tokio::signal::ctrl_c().await.unwrap(); + Ok(()) +} +``` + +This edge node responds to real-time queries using Zenoh’s Queryable API. It listens for requests on the robot/battery/estimate key and returns a calculated battery health score based on provided input parameters. + +The program starts by establishing a Zenoh session using open(Config::default()). It then registers a queryable resource on the robot/battery/estimate key. Whenever this key is queried, a callback function is invoked asynchronously using tokio::spawn. + +Inside the callback: +- Query parameters are extracted from the URL-style selector string. +- Two main parameters are used: level (battery percentage) and temp (temperature in Celsius). +- A health score is computed from these inputs. +- The result is sent back to the querying client using query.reply(). + +This design pattern enables efficient, on-demand data exchange with minimal bandwidth usage—ideal for edge computing scenarios where resources and connectivity are constrained. + +The health score is calculated using the following logic: + +```rust +let health_score = 100 - (100 - battery) - ((temp.saturating_sub(25)) / 2); +``` +This formula estimates battery health as a percentage, considering both battery level and temperature: +- battery: Current battery level (default 50%) +- temp: Current temperature (default 25°C) + +The health estimation logic begins with the battery level as the baseline score. +If the temperature rises above 25°C, the score is adjusted downward—specifically, for every 2°C above this threshold, the health is reduced by 1%. +To ensure the calculation remains safe even when the temperature is below 25°C, the code uses saturating_sub(25), which prevents the result from becoming negative and avoids potential underflow errors. + +For example, if battery = 88 and temp = 32, then: +- Temperature offset = (32 - 25) / 2 = 3 +- Health = 88 - 3 = 85% + +### Step 3: Build and Run + +```bash +cd ~/zenoh/zenoh_battery_estimator +cargo build --release +``` + +After the build process, you will see: + +``` +cargo build --release + Compiling zenoh_battery_estimator v1.4.0 (/home/ubuntu/zenoh_v1.4/zenoh_battery_estimator) + Finished `release` profile [optimized] target(s) in 1m 22s +``` + +### Step 4: Query It with Parameters + +Run it on the Raspberry Pi you just built. +```bash +cd ~/zenoh/target/release/ +./zenoh_battery_estimator +``` + +You can reuse the built-in Zenoh z_get CLI in another Raspberry Pi. + +```bash +cd ~/zenoh/target/release/examples +./z_get -s "robot/battery/estimate?level=88&temp=32" +``` + +The result will look like: +![img4 alt-text#center](zenoh_ex4.gif "Figure 4: Dynamic Queryable with Computation") + +The excepted output will be +``` +>> Received ('robot/battery/estimate': 'Estimated battery health: 85%') +``` + +You’ve just built a responsive, parameterized edge compute service using Zenoh’s Queryable API in Rust — a lightweight but powerful pattern for real-time intelligence at the edge. + +This approach not only minimizes network overhead but also enables each device to process and respond to context-aware queries on demand. +It’s a strong foundation for building scalable, event-driven IoT systems that can adapt dynamically to operational needs. diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/_index.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/_index.md new file mode 100644 index 000000000..c0d19b65b --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/_index.md @@ -0,0 +1,58 @@ +--- +title: Scalable Networking for Industrial and Robotics with Zenoh on Raspberry Pi + +minutes_to_complete: 45 + +who_is_this_for: This learning path is designed for robotics developers, industrial automation engineers, and IoT system architects building distributed, scalable, and low-latency applications. Whether you are using Robot Operating System (ROS), developing autonomous systems, or designing multi-node communication frameworks, this guide will show you how to leverage the Eclipse Zenoh protocol on Arm-based platforms — both in the cloud (AVH or EC2) and on physical devices like Raspberry Pi. + +learning_objectives: + - Understand Zenoh’s architecture and its integration of pub/sub, storage, querying, and computation models. + - Build and run Zenoh examples on both Arm servers and Raspberry Pi. + - Set up and deploy a multi-node Zenoh system using Arm-based hardware or virtual environments. + +prerequisites: + - At least two [Raspberry Pi5 or Pi4](https://www.raspberrypi.com/products/raspberry-pi-5/) or other Cortex-A instances with a Linux-based OS installed. + - Basic understanding with the Linux command line. + - Experience with ROS 2 applications. + - Corellium account for virtual hardware testing. (Option) + +author: + - Odin Shen + - William Liang + - ChenYing Kuo + +skilllevels: Introductory +subjects: Robotics +armips: + - Cortex-A + - Neoverse +tools_software_languages: + - ROS2 + - C + - Raspberry Pi + +operatingsystems: + - Linux +### Cross-platform metadata only +shared_path: true +shared_between: + - iot + - automotive + +further_reading: + - resource: + title: Eclipse Zenoh Website + link: https://zenoh.io/ + type: documentation + - resource: + title: Eclipse Zenoh Github + link: https://github.com/eclipse-zenoh/zenoh + type: documentation + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/_next-steps.md b/content/learning-paths/cross-platform/zenoh-multinode-ros2/_next-steps.md new file mode 100644 index 000000000..c3db0de5a --- /dev/null +++ b/content/learning-paths/cross-platform/zenoh-multinode-ros2/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # Set to always be larger than the content in this path to be at the end of the navigation. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +--- diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex1.gif b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex1.gif new file mode 100644 index 000000000..097250654 Binary files /dev/null and b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex1.gif differ diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex2.gif b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex2.gif new file mode 100644 index 000000000..eaa36bf6e Binary files /dev/null and b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex2.gif differ diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex3.gif b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex3.gif new file mode 100644 index 000000000..119a79c2d Binary files /dev/null and b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex3.gif differ diff --git a/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex4.gif b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex4.gif new file mode 100644 index 000000000..480278f1f Binary files /dev/null and b/content/learning-paths/cross-platform/zenoh-multinode-ros2/zenoh_ex4.gif differ