Introduction to ROS2 With Rust. write publishers and subscribers | by Timothy Shan | Jun, 2022

Write publishers and subscribers

In Might 2022, ROS 2 Humble Hawksbill (humble) was launched, which helps Ubuntu 22.04. Since I’m nonetheless utilizing Ubuntu 20.04, this weblog will deal with cunning.

Very first thing first, how one can set up ROS2?

As a result of our crew is migrating from ROS1 to ROS2, I would like to make use of each for now. My present manner is to put in ROS2 on my OS following the official guide, and set up ROS1 by way of mamba utilizing RoboStack.

Although RoboStack additionally supplies ros-galactic-desktop, I don’t suggest it because the bundle help just isn’t full, and doesn’t work properly with Python APIs.

One could ponder whether there’s a solution to simply style the flavour of ROS2, see whether or not one likes it, earlier than diving into the full-scale set up. There are docker files for many who have nvidia GPU, whereas this repo supplies docker information for CPU and VNC. The one used on this weblog is the VNC model. After pulling and constructing the docker file, use a browser to hook up with http://127.0.0.1:6080/, which reveals the desktop. Additionally have to deal with the the permission subject by way of sudo chmod 777 -R ~/.ros/ and you’re good to go.

For the Rust interface of this part, I’m going to make use of r2r, which has examples on how one can use tokio. Different Rust interfaces are additionally accessible, reminiscent of ros2_rust, which is in energetic growth, however doesn’t help tokio but. The code for this weblog is in this repo.

Let’s start with the great previous good day world instance. First, create a cargo binary bundle

cargo new hello_world --bin --vcs none

Within the src/fundamental.rs, add the next

use r2r::QosProfile;
use tokio::activity;
#[tokio::main]
async fn fundamental() -> End result<(), Field<dyn std::error::Error>> loop
node.spin_once(std::time::Period::from_millis(100));
);
deal with.be part of().unwrap();
Okay(())

The Cargo.toml appears like this

[package]
title = "hello_world"
model = "0.1.0"
version = "2021"
# See extra keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
r2r = "0.6.2"
tokio = model = "1.15.0", options = ["full"]

Subsequent run Cargo run and checkout the subjects by way of ros2 matter checklist, output is

/hw_topic
/parameter_events
/rosout

To test the information, use ros2 matter echo /hw_topic

information: good day world
---
information: good day world
---
...

I’m a fan of Sherlock Holmes so I’ll use that for example, particularly the one filmed by BBC. Within the TV collection, John Watson writes blogs concerning the instances Sherlock is coping with. So let’s create a writer to publish Watson’s weblog, by

cargo new watson --bin

The Rust interface used on this part is rclrust, wich additionally helps tokio. Word that we are able to additionally create a Rust Consumer from scratch as proven here. We have to outline the dependencies within the Cargo.toml as

[package]
title = "watson"
model = "0.1.0"
version = "2021"
# See extra keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
rclrust = git = "https://github.com/rclrust/rclrust.git", options = ["foxy"]
rclrust-msg = git = "https://github.com/rclrust/rclrust.git"
tokio = model = "1", options = ["full"]
anyhow = "1.0"

In code above, we are able to specify the ROS2 model for rclrust by way of cargo options. We additionally want to incorporate rclrust-msg.

The writer code is

use std::thread::sleep, time::Period;use anyhow::End result;
use rclrust::qos::QoSProfile, rclrust_info;
use rclrust_msg::std_msgs::msg::String as String_;
fn fundamental() -> End result<()>
let ctx = rclrust::init()?;
let node = ctx.create_node("watson_blog")?;
let logger = node.logger();
let writer = node.create_publisher::<String_>("weblog", &QoSProfile::default())?;
let mut rely = 1;
loop
writer.publish(&String_
information: format!("Watson's th weblog", rely),
)?;
rclrust_info!(logger, "Watson's th weblog revealed", rely);
rely += 1;
sleep(Period::from_millis(100));
Okay(())

Output will likely be

[INFO] [1653823142.928885221] [watson_blog]: Watson's 1th weblog revealed
[INFO] [1653823143.029225096] [watson_blog]: Watson's 2th weblog revealed
[INFO] [1653823143.129426721] [watson_blog]: Watson's 3th weblog revealed
[INFO] [1653823143.230213513] [watson_blog]: Watson's 4th weblog revealed
[INFO] [1653823143.330655138] [watson_blog]: Watson's fifth weblog revealed

Extra examples might be present in rclrust-examples.

A younger man named Billy who lives at Baker Avenue enjoys studying Watson’s blogs very a lot, so let’s write a subscriber for Billy. The code is beneath

use std::sync::Arc;use rclrust::qos::QoSProfile, rclrust_info;
use rclrust_msg::std_msgs::msg::String as String_;
#[tokio::main]
async fn fundamental() -> anyhow::End result<()>
let ctx = rclrust::init()?;
let mut node = ctx.create_node("billy_reader")?;
let logger = node.logger();
let _subscription = node.create_subscription(
"weblog",
transfer

We have to run each writer and subscriber, the output is

[INFO] [1653826256.633754176] [billy_reader]: I learn: Watson's 2th weblog
[INFO] [1653826256.734067551] [billy_reader]: I learn: Watson's 3th weblog
[INFO] [1653826256.835288093] [billy_reader]: I learn: Watson's 4th weblog

Billy likes the blogs a lot that he pays a little bit reward after studying every one. We have to publish an u8 kind to deal with this characteristic as follows

use std::sync::Arc;use rclrust::qos::QoSProfile, rclrust_info;
use rclrust_msg::std_msgs::msg::String as String_;
use rclrust_msg::std_msgs::msg::UInt8 as u8_;
#[tokio::main]
async fn fundamental() -> anyhow::End result<()>
let ctx = rclrust::init()?;
let mut node = ctx.create_node("billy_reader")?;
let logger = node.logger();
let writer = node.create_publisher::<u8_>("reward", &QoSProfile::default())?;
let reward: u8 = 10;
let _subscription = node.create_subscription(
"weblog",
transfer

The weblog writer node must be modified to obtain the reward as follows

use std::thread::sleep, time::Period;
use std::sync::Arc;
use anyhow::End result;
use rclrust::qos::QoSProfile, rclrust_info;
use rclrust_msg::std_msgs::msg::String as String_;
use rclrust_msg::std_msgs::msg::UInt8 as u8_;
#[tokio::main]
async fn fundamental() -> End result<()> msg: Arc<u8_>

And the output consists of the reward message

[INFO] [1653829848.327928005] [watson_blog]: Watson's 79th weblog revealed
[INFO] [1653829848.329881922] [watson_blog]: I obtained $10 reward

That’s it for a fast introduction, which covers how one can write publishers and subscribers.

One factor to notice is that in contrast to the Python or C++ examples for ROS/ROS2, there aren’t any callback features within the Rust ones, because the perform is async, as talked about here.

More Posts