Skip to content

Commit 9616fed

Browse files
committed
Convert to roslibrust
roslibrust makes the build story easier and provides both ROS1 and ROS2 support, switchable at runtime.
1 parent 90bd057 commit 9616fed

File tree

16 files changed

+340
-267
lines changed

16 files changed

+340
-267
lines changed

.gitmodules

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[submodule "assets/ros1_common_interfaces/std_msgs"]
2+
path = assets/ros1_common_interfaces/std_msgs
3+
url = https://github.com/ros/std_msgs.git
4+
[submodule "assets/ros1_common_interfaces/common_msgs"]
5+
path = assets/ros1_common_interfaces/common_msgs
6+
url = https://github.com/ros/common_msgs.git
7+
[submodule "assets/ros2_common_interfaces"]
8+
path = assets/ros2_common_interfaces
9+
url = https://github.com/ros2/common_interfaces.git
10+
[submodule "assets/ros1_common_interfaces/ros_comm_msgs"]
11+
path = assets/ros1_common_interfaces/ros_comm_msgs
12+
url = https://github.com/ros/ros_comm_msgs.git
13+
[submodule "assets/ros2_required_msgs/rcl_interfaces"]
14+
path = assets/ros2_required_msgs/rcl_interfaces
15+
url = https://github.com/ros2/rcl_interfaces.git

Cargo.toml

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ name = "modulr_agent"
33
version = "0.1.0"
44
edition = "2024"
55

6+
[build-dependencies]
7+
roslibrust = { version = "0.16.0", features = ["ros1", "codegen"] }
8+
69
[dependencies]
710
webrtc = "0.10"
811
tokio = { version = "1", features = ["full"] }
@@ -18,23 +21,7 @@ bytes = "1.10.1"
1821
thiserror = "2.0.17"
1922
env_logger = "0.11.8"
2023
log = "0.4.28"
21-
22-
# ROS 1 dependencies
23-
rosrust = { version = "0.9.12", optional = true }
24-
rosrust_msg = { version = "0.1.8", optional = true }
25-
26-
# ROS 2 dependencies
27-
rclrs = { version = "*", optional = true }
28-
geometry_msgs = { version = "*", optional = true }
29-
sensor_msgs = { version = "*", optional = true }
30-
std_msgs = { version = "*", optional = true }
31-
32-
[features]
33-
default = ["ros2"]
34-
ros1 = ["dep:rosrust", "dep:rosrust_msg"]
35-
ros2 = [
36-
"dep:rclrs",
37-
"dep:geometry_msgs",
38-
"dep:sensor_msgs",
39-
"dep:std_msgs",
40-
]
24+
roslibrust = { version = "0.16.0", features = ["ros1", "rosbridge", "codegen"] }
25+
async-trait = "0.1.89"
26+
futures = "0.3.31"
27+
base64 = "0.22.1"

README.md

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
This package contains the agent to be installed on any robot connecting to the Modulr infrastructure.
44

5+
## Cloning the Package
6+
7+
Use the following command to clone the package:
8+
9+
```bash
10+
git clone --recurse-submodules https://github.com/ModulrCloud/modulr-agent
11+
# If already cloned and you need to checkout the submodules:
12+
git submodule update --init --recursive
13+
```
14+
515
## Building the Package
616

717
This package has only been tested on Ubuntu systems. To build the package, you will need to install Rust: https://rustup.rs/
@@ -15,71 +25,64 @@ sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
1525
gstreamer1.0-libav libgstrtspserver-1.0-dev libges-1.0-dev
1626
```
1727

18-
Finally, ROS is required. ROS 1 and ROS 2 are both supported.
19-
20-
## ROS 1
21-
22-
Follow the installation instructions here: https://wiki.ros.org/noetic/Installation/Ubuntu
23-
24-
Unfortunately, dependency management is difficult with this package due to its dependency on `ros2rust`, which requires the packages to be built locally. To make this package easier to compile, open `Cargo.toml` and remove the `ros2` feature and the dependencies listed for ROS 2. Also set the default features to ros1.
28+
Finally, ROS is required. ROS 1 and ROS 2 are both supported. For ROS1, install Noetic as per [these installation instructions](https://wiki.ros.org/noetic/Installation/Ubuntu). For ROS2, we recommend using Kilted as the latest release ([installation instructions](https://docs.ros.org/en/kilted/Installation/Ubuntu-Install-Debs.html)), but earlier distros should also work.
2529

26-
Then, build the package by running:
30+
In addition to base ROS, a websocket bridge server is required:
2731

2832
```bash
29-
source /opt/ros/noetic/setup.bash
30-
cargo build
33+
sudo apt install ros-$ROS_DISTRO-rosbridge-suite
3134
```
3235

33-
You can run the package with logging enabled using:
36+
Build the package as follows:
3437

3538
```bash
36-
RUST_LOG=debug cargo run
39+
# Replace $ROS_DISTRO with the installed version of ROS
40+
source /opt/ros/$ROS_DISTRO/setup.bash
41+
# For debug mode:
42+
cargo build
43+
# For release mode:
44+
cargo build --release
3745
```
3846

39-
## ROS 2
40-
41-
Follow the installation instructions here: https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debs.html
42-
43-
Note that any distro of ROS 2 should be compatible, but only Jazzy has been tested so far.
44-
45-
Some extra installation is required for Rust-related build tooling:
47+
The agent relies on rosbridge to relay ROS traffic. Run rosbridge as follows:
4648

4749
```bash
48-
sudo apt install -y git libclang-dev python3-pip python3-vcstool
49-
pip install git+https://github.com/colcon/colcon-cargo.git --break-system-packages
50-
pip install git+https://github.com/colcon/colcon-ros-cargo.git --break-system-packages
50+
# ROS 1
51+
roslaunch rosbridge_server rosbridge_websocket.launch
52+
# ROS 2
53+
ros2 launch rosbridge_server rosbridge_websocket_launch.xml
5154
```
5255

53-
Create a ROS 2 workspace for the package:
56+
You can then run the agent using the following:
5457

5558
```bash
56-
mkdir -p ~/modulr_ws/src
57-
cd ~/modulr_ws
58-
git clone <this repo> src/modulr_agent
59-
vcs import src < src/modulr_agent/ros2rust.repos
60-
# Change the following line depending on your installed version of ROS 2
61-
vcs import src < src/ros2_rust/ros2_rust_jazzy.repos
59+
# For debug mode:
60+
cargo run
61+
# For release mode:
62+
cargo run --release
63+
# To enable logging, set the following to your desired log level:
64+
RUST_LOG=modulr_agent=debug cargo run # --release
6265
```
6366

64-
Build the package (first time command):
67+
## Running in Simulation
68+
69+
If not running on a real robot, you can test the system using a Turtlebot simulation. Install the turtlebot simulator using:
6570

6671
```bash
67-
cd ~/modulr_ws
68-
colcon build
69-
source install/setup.bash
72+
sudo apt install ros-$ROS_DISTRO-turtlebot3-simulations
7073
```
7174

72-
Build the package (subsequent calls):
75+
The simulator can then be run using:
7376

7477
```bash
75-
cd ~/modulr_ws
76-
colcon build --packages-select modulr_agent
78+
export TURTLEBOT3_MODEL=waffle_pi
79+
# ROS 1
80+
roslaunch turtlebot3_gazebo turtlebot3_world.launch
81+
# ROS 2
82+
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
7783
```
7884

79-
Run the package with debug logging:
85+
The robot should then be driveable using the agent.
8086

81-
```bash
82-
RUST_LOG=debug ./install/modulr_agent/bin/modulr_agent
83-
```
87+
*Note that at present, all topics are hard-coded. You may need to check that the simulation is producing images on `/camera/image_raw` and adjust the source code if not. Similarly, the simulation may use Twist or TwistStamped messages for velocity commands, so if movement is not working, double-check the message type.*
8488

85-
*Note that cargo commands should be enabled, including cargo build and cargo run, but these did not work on the first system. It is a TODO to fix this build issue.*
Submodule common_msgs added at 092bc23
Submodule ros_comm_msgs added at cdfe7c1
Submodule std_msgs added at 3419852

assets/ros2_common_interfaces

Submodule ros2_common_interfaces added at 5eba742
Submodule rcl_interfaces added at 1bd7d0b

build.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use std::path::PathBuf;
2+
3+
fn generate_messages(
4+
paths: Vec<PathBuf>,
5+
out_file: &str,
6+
) -> Result<(), Box<dyn std::error::Error>> {
7+
let (source, dependent_paths) =
8+
roslibrust::codegen::find_and_generate_ros_messages_without_ros_package_path(paths)?;
9+
let out_dir = std::env::var_os("OUT_DIR").unwrap();
10+
let dest_path = std::path::Path::new(&out_dir).join(out_file);
11+
std::fs::write(dest_path, source.to_string())?;
12+
13+
for path in &dependent_paths {
14+
println!("cargo:rerun-if-changed={}", path.display());
15+
}
16+
17+
Ok(())
18+
}
19+
20+
fn main() -> Result<(), Box<dyn std::error::Error>> {
21+
let ros1_paths = vec!["./assets/ros1_common_interfaces".into()];
22+
23+
let ros2_paths = vec![
24+
"./assets/ros2_common_interfaces".into(),
25+
"./assets/ros2_required_msgs/rcl_interfaces/builtin_interfaces".into(),
26+
];
27+
28+
generate_messages(ros1_paths, "ros1_messages.rs")?;
29+
generate_messages(ros2_paths, "ros2_messages.rs")?;
30+
31+
Ok(())
32+
}

src/main.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ use std::sync::Arc;
77

88
use anyhow::Result;
99
use bytes::Bytes;
10-
use log::{info, warn};
10+
use log::{debug, info, warn};
1111
use tokio::sync::Mutex;
1212

13+
use crate::ros_bridge::Ros1Bridge;
14+
use crate::ros_bridge::Ros2Bridge;
1315
use crate::ros_bridge::RosBridge;
1416
use crate::video_pipeline::VideoPipeline;
1517
use crate::video_pipeline::VideoPipelineError;
1618
use crate::webrtc_link::WebRtcLink;
1719
use crate::webrtc_link::WebRtcLinkError;
1820
use crate::webrtc_message::WebRtcMessage;
1921

22+
const ROS1: bool = false;
23+
2024
#[tokio::main]
2125
async fn main() -> Result<()> {
2226
env_logger::init();
@@ -25,7 +29,12 @@ async fn main() -> Result<()> {
2529
let signaling_url = "ws://192.168.132.19:8765";
2630
let webrtc_link = Arc::new(Mutex::new(WebRtcLink::new(robot_id, signaling_url)));
2731
let pipeline = Arc::new(Mutex::new(VideoPipeline::new()));
28-
let bridge = Arc::new(Mutex::new(RosBridge::try_new()?));
32+
33+
let bridge: Arc<Mutex<dyn RosBridge>> = if ROS1 {
34+
Arc::new(Mutex::new(Ros1Bridge::new()))
35+
} else {
36+
Arc::new(Mutex::new(Ros2Bridge::new()))
37+
};
2938

3039
info!("Creating system components and callbacks");
3140

@@ -44,6 +53,7 @@ async fn main() -> Result<()> {
4453
.lock()
4554
.await
4655
.post_movement_command(&cmd)
56+
.await
4757
.expect("Failed to post movement command!");
4858
}
4959
}
@@ -96,7 +106,8 @@ async fn main() -> Result<()> {
96106
.await
97107
.expect("Failed to write camera frame to pipeline!");
98108
})
99-
}));
109+
}))
110+
.await;
100111

101112
info!("Starting all tasks running");
102113

@@ -109,10 +120,10 @@ async fn main() -> Result<()> {
109120
let pipeline_clone = Arc::clone(&pipeline);
110121
tokio::spawn(async move {
111122
pipeline_clone.lock().await.launch().await?;
112-
info!("Spawned the launch task successfully");
123+
debug!("Finished launching video pipeline");
113124
Ok::<(), VideoPipelineError>(())
114125
});
115-
bridge.lock().await.spin();
126+
bridge.lock().await.launch().await?;
116127

117128
let _ = tokio::signal::ctrl_c().await;
118129
info!("Exit requested, cleaning up...");

0 commit comments

Comments
 (0)