Open TanJunKiat opened 9 months ago
Hi there!
I've been using ros2_rust
along with bevy
(although v0.11) without much problems.
Here's how I integrate ROS 2 interfaces within my bevy application:
bevy Resource
which is a struct that bundles an rclrs::Context
and an Arc<rclrs::Node>
. A Default
trait for this resource is defined which instantiates the context and node. The same plugin also adds a system which retrieves the above resource, checks if the context is okay and if so, calls rclrs::spin_once()
with a clone of the node.bevy Component
struct with the corresponding rclrs
entity. Then, there are systems that query for the Resource above, initialize the component using the node from the resource, and finally insert the component into the entities.Hope that helps!
use bevy::prelude::*;
use std::sync::{Arc, Mutex};
use std_msgs::msg::String as StringMsg;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(NodePlugin)
.add_systems(Startup, setup)
.add_systems(Update,print_string)
.run();
}
fn setup() {
eprintln!("Bevy initialized.");
}
fn print_string(
string_node: ResMut<NodeSetting>,
) {
let s = string_node.data.clone().lock().unwrap().clone().unwrap();
eprintln!("{}",&s.data);
eprintln!("Received string.");
}
pub struct NodePlugin;
#[derive(Resource)]
struct NodeSetting {
node: Arc<rclrs::Node>,
_subscription: Arc<rclrs::Subscription<StringMsg>>,
data: Arc<Mutex<Option<StringMsg>>>,
}
impl Plugin for NodePlugin {
fn build (&self, app: &mut App){
let context = rclrs::Context::new(std::env::args()).unwrap();
let node = rclrs::Node::new(&context, "republisher").unwrap();
let data = Arc::new(Mutex::new(None));
let data_cb = Arc::clone(&data);
let string_node = NodeSetting {
node: node.clone(),
data: data.clone(),
_subscription: node.create_subscription(
"in_topic",
rclrs::QOS_PROFILE_DEFAULT,
move |msg: StringMsg| {
*data_cb.lock().unwrap() = Some(msg); // (4)
},
).unwrap(),
};
app.insert_resource(string_node)
.add_systems(Update, spin_node)
.insert_resource(Time::<Fixed>::from_hz(1.0));
}
}
fn spin_node(string_node: ResMut<NodeSetting>){
eprintln!("spinning node ");
rclrs::spin_once(string_node.node.clone(),None).unwrap();
eprintln!("exit node ");
}
@Yadunund thanks for the support.
I tried to create a simple test code to try out what you suggested.
Not sure did I understood the recommendation correctly but my Bevy still gets stuck after the node was spun once.
Is it stuck inside the spin_once
or elsewhere? I'd recommend putting a sensible timeout in the spin_once
.
Instead of transferring the subscription data using an Arc<Mutex<Option<T>>>
I would recommend creating a mpsc channel. Give the sender to your subscription callback and store the receiver in your resource.
Your subscription callback would simply pass the messages into the sender. Your print_string
system would drain the receiver using try_recv
.
use bevy::prelude::*;
use std::{sync::{Arc, Mutex}, time::Duration};
use std_msgs::msg::String as StringMsg;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(NodePlugin)
.add_systems(Startup, setup)
.add_systems(Update,print_string)
.run();
}
fn setup() {
eprintln!("Bevy initialized.");
}
fn print_string(
string_node: ResMut<NodeSetting>,
) {
let guard = string_node.data.lock().unwrap();
if guard.as_ref() != None{
let info = guard.as_ref().unwrap();
eprintln!("Bevy got message: {}",&info.data);
}
}
pub struct NodePlugin;
#[derive(Resource)]
struct NodeSetting {
node: Arc<rclrs::Node>,
_subscription: Arc<rclrs::Subscription<StringMsg>>,
data: Arc<Mutex<Option<StringMsg>>>,
}
impl Plugin for NodePlugin {
fn build (&self, app: &mut App){
let context = rclrs::Context::new(std::env::args()).unwrap();
let node = rclrs::Node::new(&context, "republisher").unwrap();
let data = Arc::new(Mutex::new(None));
let data_cb = Arc::clone(&data);
let string_node = NodeSetting {
node: node.clone(),
data: data.clone(),
_subscription: node.create_subscription(
"in_topic",
rclrs::QOS_PROFILE_DEFAULT,
move |msg: StringMsg| {
eprintln!("Node received message: {}",msg.data);
*data_cb.lock().unwrap() = Some(msg); // (4)
},
).unwrap(),
};
app.insert_resource(string_node)
.add_systems(Update, spin_node)
.insert_resource(Time::<Fixed>::from_hz(1.0));
}
}
fn spin_node(string_node: ResMut<NodeSetting>){
eprintln!("Entering node ");
let _action = rclrs::spin_once(string_node.node.clone(),Some(Duration::new(1, 0)));
eprintln!("Exiting node ");
}
@mxgrey @Yadunund Awesome guys.
Thank you so much for the help.
I managed to get the message on the bevy resource struct and bevy can print the string as it receives it.
Tried to add a subscriber node as a system in Bevy but Bevy stopped running (black screen) after the node started spinning
Tried multi-threading. It works (bevy and ros node both running), but ros node is unable to update the resource database in Bevy with the new messages.
Any help or suggestion will be appreciated and we can try to get it working and improve the Docs or an example for the community.
Setup: