Skip to content

Commit c6bec8e

Browse files
xiyuoharjo129
andauthored
[Reopen] Introduce nav2 traffic for next gen (#29)
* Introduce nav2 traffic for next gen Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Bring in spatio temporal stuff Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Remove sp from repos file Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Remove .repos file Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Persistent cancelllation Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * More robust inner nav request handling Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Add clearer description Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Review comments Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Add visualizer launch Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Move bevy ros2 stuff to its own crate + cleanup Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Compare destination session Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Adjust log level Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Add more deps instructions to readme Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Some readme stuff, some ci stuff, some logging stuff Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * See if CI will fetch the right things Signed-off-by: Arjo Chakravarty <arjoc@intrinsic.ai> * Add deps Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> * Forgot the 2 Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> --------- Signed-off-by: Xiyu Oh <xiyu@openrobotics.org> Signed-off-by: Arjo Chakravarty <arjoc@intrinsic.ai> Co-authored-by: Arjo Chakravarty <arjoc@intrinsic.ai>
1 parent 34b2d6a commit c6bec8e

56 files changed

Lines changed: 7310 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bevy_ros2/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
Cargo.lock
3+

bevy_ros2/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "bevy_ros2"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lib]
7+
name = "bevy_ros2"
8+
9+
[dependencies]
10+
bevy = "0.16"
11+
futures = "0.3"
12+
rclrs = "0.7"

bevy_ros2/src/lib.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright (C) 2026 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
use bevy::prelude::*;
19+
use futures::Future;
20+
use rclrs::{
21+
ActionClientState, ActionIDL, ActionServerState, ClientState, Context, CreateBasicExecutor,
22+
ExecutorCommands, IntoPrimitiveOptions, MessageIDL, NodeState, PublisherState, RclrsError,
23+
RequestedGoal, RequestedGoalClient, ServiceIDL, SpinOptions, Subscription, TerminatedGoal,
24+
};
25+
use std::{
26+
fmt::Debug,
27+
sync::{Arc, Mutex},
28+
thread,
29+
};
30+
31+
#[derive(Resource, Deref)]
32+
pub struct RclrsNode(Arc<NodeState>);
33+
34+
impl FromWorld for RclrsNode {
35+
fn from_world(world: &mut World) -> Self {
36+
let executor_commands = world.resource::<RclrsExecutorCommands>();
37+
let node_name = "nav_traffic_node".to_string();
38+
let node = executor_commands.create_node(&node_name).unwrap();
39+
RclrsNode(node.clone())
40+
}
41+
}
42+
43+
#[derive(Resource, Deref)]
44+
pub struct RclrsExecutorCommands(Arc<ExecutorCommands>);
45+
46+
#[derive(Default)]
47+
pub struct RclrsPlugin {}
48+
49+
impl Plugin for RclrsPlugin {
50+
fn build(&self, app: &mut App) {
51+
let mut executor = Context::default_from_env().unwrap().create_basic_executor();
52+
app.insert_resource(RclrsExecutorCommands(Arc::clone(executor.commands())));
53+
54+
thread::spawn(move || {
55+
let r = executor.spin(SpinOptions::default());
56+
for err in r {
57+
error!("An error occurred in rclrs: {err}");
58+
}
59+
});
60+
61+
app.init_resource::<RclrsNode>();
62+
}
63+
}
64+
65+
// Template for creating ROS 2 subscribers
66+
pub struct RosSubscription<T: MessageIDL + Debug> {
67+
_subscriber: Arc<Subscription<T>>,
68+
data: Arc<Mutex<Option<T>>>,
69+
}
70+
71+
impl<T: MessageIDL + Debug> RosSubscription<T> {
72+
pub fn new(node: &Arc<NodeState>, topic: String) -> Self {
73+
let data = Arc::new(Mutex::new(None));
74+
let data_clone: Arc<Mutex<Option<T>>> = Arc::clone(&data);
75+
76+
let subscriber = node
77+
.create_subscription(
78+
topic.clone().reliable().transient_local().keep_all(),
79+
move |msg: T| {
80+
debug!("Received a message on [{}]: {:?}", topic, msg);
81+
*data_clone.lock().unwrap() = Some(msg.clone());
82+
83+
// TODO(@xiyuoh) allow customized qos
84+
},
85+
)
86+
.unwrap();
87+
88+
Self {
89+
_subscriber: Arc::new(subscriber),
90+
data,
91+
}
92+
}
93+
94+
pub fn data_callback(&self) -> Option<T> {
95+
self.data.lock().unwrap().as_ref().cloned()
96+
}
97+
}
98+
99+
// Template for creating ROS 2 publishers
100+
pub struct RosPublisher<T: MessageIDL + Debug> {
101+
publisher: Arc<PublisherState<T>>,
102+
}
103+
104+
impl<T: MessageIDL + Debug> RosPublisher<T> {
105+
pub fn new(node: &Arc<NodeState>, topic: String) -> Self {
106+
let publisher = node.create_publisher(&topic).unwrap();
107+
108+
Self { publisher }
109+
}
110+
111+
pub fn new_transient_local(node: &Arc<NodeState>, topic: String) -> Self {
112+
let publisher = node
113+
.create_publisher(topic.reliable().transient_local().keep_all())
114+
.unwrap();
115+
116+
Self { publisher }
117+
}
118+
119+
pub fn publish(&self, msg: T) -> Result<(), RclrsError> {
120+
self.publisher.publish(msg)
121+
}
122+
}
123+
124+
// Template for creating ROS 2 service clients
125+
pub struct RosServiceClient<T: ServiceIDL> {
126+
client: Arc<ClientState<T>>,
127+
}
128+
129+
impl<T: ServiceIDL> RosServiceClient<T> {
130+
pub fn new(node: &Arc<NodeState>, service_name: String) -> Self {
131+
let client = node.create_client::<T>(&service_name).unwrap();
132+
133+
Self { client }
134+
}
135+
}
136+
137+
// Template for creating ROS 2 action clients
138+
pub struct RosActionClient<T: ActionIDL> {
139+
action_client: Arc<ActionClientState<T>>,
140+
action_name: String,
141+
}
142+
143+
impl<T: ActionIDL> RosActionClient<T> {
144+
pub fn new(node: &Arc<NodeState>, action_name: String) -> Self {
145+
let action_client = node.create_action_client::<T>(&action_name).unwrap();
146+
147+
Self {
148+
action_client,
149+
action_name,
150+
}
151+
}
152+
153+
pub fn request_goal(&self, goal: T::Goal) -> RequestedGoalClient<T> {
154+
self.action_client.request_goal(goal)
155+
}
156+
}
157+
158+
// Template for creating ROS 2 action servers
159+
pub struct RosActionServer<A: ActionIDL> {
160+
action_server: Arc<ActionServerState<A>>,
161+
action_name: String,
162+
}
163+
164+
impl<A: ActionIDL> RosActionServer<A> {
165+
pub fn new<Task>(
166+
node: &Arc<NodeState>,
167+
action_name: String,
168+
callback: impl FnMut(RequestedGoal<A>) -> Task + Send + Sync + 'static,
169+
) -> Self
170+
where
171+
Task: Future<Output = TerminatedGoal> + Send + Sync + 'static,
172+
{
173+
let action_server = node.create_action_server(&action_name, callback).unwrap();
174+
175+
Self {
176+
action_server,
177+
action_name,
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)