
[ad_1]
Write to publishers and subscribers

In May 2022, ROS 2 Humble Hawksbill (Humble) was released, which supports Ubuntu 22.04. Since I’m still using Ubuntu 20.04, this blog will focus on foxy
,
First things first, how to install ROS2?
Because our team is migrating from ROS1 to ROS2, I need to use both right now. My current way is to install ROS2 on my OS after following official guideand install via ROS1 mamba
using the RoboStack,
Even though RoboStack also offers ros-galactic-desktop
I don’t recommend it because package support is not complete, and doesn’t work well with Python API.
One may wonder if there is a way to taste the flavor of ROS2, see if anyone likes it, before diving into the full scale installation. there are docker files For those who have Nvidia GPU, while this repo Provides Docker files for CPU and VNC. This is the VNC version used in this blog. After dragging and building the Docker file, use the browser to connect http://127.0.0.1:6080/
, which shows the desktop. Also the issue of permission needs to be taken care of sudo chmod 777 -R ~/.ros/
And you are good to go.
For the Rust interface of this section, I’m going to use r2rwhich has examples of using tokio
, Other Rust interfaces are also available, such as ros2_rust, which is under active development, but doesn’t support Tokyo yet. The code for this blog is this repo,
let’s start with the good old hello world
Example. First, create the cargo binary package
cargo new hello_world --bin --vcs none
In src/main.rs
add the following
use r2r::QosProfile;
use tokio::task;#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?;
let duration = std::time::Duration::from_millis(2500); let mut timer = node.create_wall_timer(duration)?;
let publisher =
node.create_publisher::<r2r::std_msgs::msg::String>("/hw_topic", QosProfile::default())?; task::spawn(async move {
loop {
timer.tick().await.unwrap();
let msg = r2r::std_msgs::msg::String {
data: "hello world".to_string(),
};
publisher.publish(&msg).unwrap();
std::thread::sleep(std::time::Duration::from_millis(100));
}
}); // here we spin the node in its own thread (but we could just busy wait in this thread)
let handle = std::thread::spawn(move || loop {
node.spin_once(std::time::Duration::from_millis(100));
});
handle.join().unwrap(); Ok(())
}
Cargo.toml
looks like this
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
r2r = "0.6.2"
tokio = { version = "1.15.0", features = ["full"] }
next run Cargo run
and check the subjects through ros2 topic list
output is
/hw_topic
/parameter_events
/rosout
To check the data, use ros2 topic echo /hw_topic
data: hello world
---
data: hello world
---
...
i’m a fan of Sherlock Holmes
So I’ll use this as an example, specifically one filmed by bbc, In the TV series, John Watson blogs about the cases Sherlock is dealing with. So let’s create a publisher to publish Watson’s blog, by
cargo new watson --bin
The Rust interface used in this section is RCLtrustwhoever supports tokio
, Note that we can also build the Rust client from scratch as shown Here, We need to define dependencies in Cargo.toml:
[package]
name = "watson"
version = "0.1.0"
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
rclrust = { git = "https://github.com/rclrust/rclrust.git", features = ["foxy"] }
rclrust-msg = { git = "https://github.com/rclrust/rclrust.git" }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
In the above code, we can specify the ROS2 version for rclrust
through cargo facilities. we also have to include rclrust-msg
,
publisher code is
use std::{thread::sleep, time::Duration};use anyhow::Result;
use rclrust::{qos::QoSProfile, rclrust_info};
use rclrust_msg::std_msgs::msg::String as String_;fn main() -> Result<()> {
let ctx = rclrust::init()?;
let node = ctx.create_node("watson_blog")?;
let logger = node.logger();
let publisher = node.create_publisher::<String_>("blog", &QoSProfile::default())?; let mut count = 1;
loop {
publisher.publish(&String_ {
data: format!("Watson's {}th blog", count),
})?;
rclrust_info!(logger, "Watson's {}th blog published", count);
count += 1;
sleep(Duration::from_millis(100));
} Ok(())
}
output will be
[INFO] [1653823142.928885221] [watson_blog]: Watson's 1th blog published
[INFO] [1653823143.029225096] [watson_blog]: Watson's 2th blog published
[INFO] [1653823143.129426721] [watson_blog]: Watson's 3th blog published
[INFO] [1653823143.230213513] [watson_blog]: Watson's 4th blog published
[INFO] [1653823143.330655138] [watson_blog]: Watson's 5th blog published
more examples can be found rclrust-example,
A young man named Billy, who lives in Baker Street, enjoys reading Watson’s blogs, so let’s write a subscriber for Billy. the code is below
use std::sync::Arc;use rclrust::{qos::QoSProfile, rclrust_info};
use rclrust_msg::std_msgs::msg::String as String_;#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ctx = rclrust::init()?;
let mut node = ctx.create_node("billy_reader")?;
let logger = node.logger(); let _subscription = node.create_subscription(
"blog",
move |msg: Arc<String_>| {
rclrust_info!(logger, "I read: {}", msg.data);
},
&QoSProfile::default(),
)?; node.wait();
Ok(())
}
We need to run both publisher and subscriber, the output is
[INFO] [1653826256.633754176] [billy_reader]: I read: Watson's 2th blog
[INFO] [1653826256.734067551] [billy_reader]: I read: Watson's 3th blog
[INFO] [1653826256.835288093] [billy_reader]: I read: Watson's 4th blog
Billy loves blogs so much that he rewards a little bit after reading each one. we need to publish a u8
To handle this feature type
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 main() -> anyhow::Result<()> {
let ctx = rclrust::init()?;
let mut node = ctx.create_node("billy_reader")?;
let logger = node.logger();
let publisher = node.create_publisher::<u8_>("reward", &QoSProfile::default())?;
let reward: u8 = 10; let _subscription = node.create_subscription(
"blog",
move |msg: Arc<String_>| {
rclrust_info!(logger, "I read: {}", msg.data);
publisher.publish(&u8_ {
data: reward,
}).unwrap();
rclrust_info!(logger, "I paid: {} for the blog", reward);
},
&QoSProfile::default(),
)?; node.wait();
Ok(())
}
In order to receive rewards the blog publisher node needs to be modified as follows
use std::{thread::sleep, time::Duration};
use std::sync::Arc;use anyhow::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 main() -> Result<()> {
let ctx = rclrust::init()?;
let mut node = ctx.create_node("watson_blog")?;
let logger = node.logger();
let publisher = node.create_publisher::<String_>("blog", &QoSProfile::default())?; let _subscription = node.create_subscription(
"reward",
move |msg: Arc<u8_>| {
rclrust_info!(logger, "I received ${} reward", msg.data);
},
&QoSProfile::default(),
)?; let logger = node.logger();
let mut count = 1;
loop {
publisher.publish(&String_ {
data: format!("Watson's {}th blog", count),
})?;
rclrust_info!(logger, "Watson's {}th blog published", count);
count += 1;
sleep(Duration::from_millis(100));
} Ok(())
}
and the output contains the reward message
[INFO] [1653829848.327928005] [watson_blog]: Watson's 79th blog published
[INFO] [1653829848.329881922] [watson_blog]: I received $10 reward
That’s for a quick introduction, which covers how to write to publishers and subscribers.
One thing to note is that unlike the Python or C++ examples for ROS/ROS2, there are no callback functions in the Rust ones, because the function is async, as mentioned Here,
[ad_2]
Source link
#Introduction #ROS2 #Rust #Write #Publishers #Subscribers #Timothy #Shan #June