Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Bevy #358

Open
TanJunKiat opened this issue Dec 27, 2023 · 7 comments
Open

Support for Bevy #358

TanJunKiat opened this issue Dec 27, 2023 · 7 comments

Comments

@TanJunKiat
Copy link

  • 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:

  • Ubuntu 22.04
  • ROS2 humble built from binary
  • Bevy 0.12
@Yadunund
Copy link

Yadunund commented Dec 27, 2023

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:

  1. I have a dedicated startup plugin that initializes a 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.
  2. In plugins where I need to define, pub/sub/client/services, I define a 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!

@TanJunKiat
Copy link
Author

TanJunKiat commented Dec 28, 2023

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 ");
}

@TanJunKiat
Copy link
Author

@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.

@mxgrey
Copy link
Collaborator

mxgrey commented Dec 28, 2023

Is it stuck inside the spin_once or elsewhere? I'd recommend putting a sensible timeout in the spin_once.

@mxgrey
Copy link
Collaborator

mxgrey commented Dec 28, 2023

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.

@TanJunKiat
Copy link
Author

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 ");
}

@TanJunKiat
Copy link
Author

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants