From 22f3d81ebf7cc716ae38bb7cd868cb5f0aa3b8ef Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 9 Apr 2024 20:59:01 +0200 Subject: [PATCH 001/113] Added another tutorial to ros2 rust. --- ...iting_a_simple_publisher_and_subscriber.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/writing_a_simple_publisher_and_subscriber.md diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md new file mode 100644 index 000000000..b0a2908cc --- /dev/null +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -0,0 +1,70 @@ +# Writing a simple publisher and subscriber (RUST) +* Goal: Create and run a publisher and subscriber node using Python. +* Tutorial level: Beginner +* Time: 20 minutes +
Background + +In this tutorial you will create +[nodes](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Nodes/Understanding-ROS2-Nodes.html) that pass information to each other via a +[topic](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html) in the form of string messages. The example used here is a simple "talker" and "listener" system; one node publishes data and the other subscribes to the topic to receive that data. + +The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node) +
+
Sidenode to dependencies + +You may be wondering why you can't just add all your ros2-specific dependencies to `cargo.toml` with `cargo add ${dependencie}` and have to edit this file manually. Here is why: +Almost none of the ROS2 dependencies you'll need for your ros2 rust node development currently exist on [crates.io](https://crates.io/), the main source for rust depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ros2 rust dependencies and your ros2 rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ros2 rust project this way. + +
+ +
+ +
Prerequisites + +In previous tutorials, you learned how to create a [workspace](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html) and [create a package](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html). + +A basic understanding of [RUST](https://www.rust-lang.org/) is recommended, but not entirely necessary. +Before developing ROS2 RUST nodes, you must follow the +[installation instructions](https://github.com/ros2-rust/ros2_rust/blob/main/README.md) for them. + + +
+ +
Tasks +
+
Create a Package + +Currently, building a package for ROS2 RUST is different +from building packages for Python or C/C++. + +First, you'll need to create and go into a standard [cargo](https://doc.rust-lang.org/cargo/) +project as follows: +``` +cargo new your_project_name && your_project_name +``` +In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html) + + +Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: +```xml + + your_project_name + 0.0.0 + TODO: Package description. Seriously. Please make a Package description. We all will thank for it. + user + TODO: License declaration. Licenses are Great. Please add a Licence + + rclrs + std_msgs + + + ament_cargo + + +``` +
Write the publisher node
+ + +
+
+
From 5f2896f654cdfcf3668636f38714579dcc2e0c4e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 10 Apr 2024 11:24:54 +0200 Subject: [PATCH 002/113] added more documentation to build your own ros2 rust nodes! yay --- ...iting_a_simple_publisher_and_subscriber.md | 277 +++++++++++++++++- 1 file changed, 271 insertions(+), 6 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b0a2908cc..2c1d09fa0 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -4,10 +4,12 @@ * Time: 20 minutes
Background -In this tutorial you will create +In this tutorial you will create a [nodes](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Nodes/Understanding-ROS2-Nodes.html) that pass information to each other via a [topic](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html) in the form of string messages. The example used here is a simple "talker" and "listener" system; one node publishes data and the other subscribes to the topic to receive that data. +Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). + The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node)
Sidenode to dependencies @@ -31,8 +33,7 @@ Before developing ROS2 RUST nodes, you must follow the
Tasks -
-
Create a Package +
Create a Package Currently, building a package for ROS2 RUST is different from building packages for Python or C/C++. @@ -42,7 +43,19 @@ project as follows: ``` cargo new your_project_name && your_project_name ``` -In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html) +In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: +``` +[package] +name = "your_package_name" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rclrs = "*" +std_msgs = "*" +``` Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: @@ -62,9 +75,261 @@ Additionally, create a new `package.xml` if you want your node to be buildable w ``` -
Write the publisher node
+By taking a look at your package, for example by typing [`tree .`](https://www.geeksforgeeks.org/tree-command-unixlinux/) inside your package, and you'll see a structure similar to the following: +``` +├── Cargo.toml +├── package.xml +└── src + └── main.rs +2 directories, 3 files +``` +Of course, you can use any capable editor or even your file explorer to do this.
-
+ +
Write the publisher node + + + +To construct a node, replace the code in your main.rs with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): +``` +/// Creates a SimplePublisherNode, initializes a node and publisher, and provides +/// methods to publish a simple "Hello World" message on a loop in separate threads. + +/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, +/// and the StringMsg message type from std_msgs for publishing string messages. +use std::{sync::Arc,time::Duration,iter,thread}; +use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use std_msgs::msg::String as StringMsg; +// / SimplePublisherNode struct contains node and publisher members. +// / Used to initialize a ROS 2 node and publisher, and publish messages. +struct SimplePublisherNode { + node: Arc, + _publisher: Arc>, +} +/// Creates a new SimplePublisherNode by initializing a node and publisher. +/// +/// The `new` function takes a context and returns a Result containing the +/// initialized SimplePublisherNode or an error. It creates a node with the +/// given name and creates a publisher on the "publish_hello" topic. +/// +/// The SimplePublisherNode contains the node and publisher members. +impl SimplePublisherNode { + /// Creates a new SimplePublisherNode by initializing a node and publisher. + /// + /// This function takes a context and returns a Result containing the + /// initialized SimplePublisherNode or an error. It creates a node with the + /// given name and creates a publisher on the "publish_hello" topic. + /// + /// The SimplePublisherNode contains the node and publisher members. + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_publisher").unwrap(); + let _publisher = node + .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .unwrap(); + Ok(Self { node, _publisher, }) + } + + /// Publishes a "Hello World" message on the publisher. + /// + /// Creates a StringMsg with "Hello World" as the data, publishes it on + /// the `_publisher`, and returns a Result. This allows regularly publishing + /// a simple message on a loop. + fn publish_data(&self,inkrement:i32) -> Result { + + let msg: StringMsg = StringMsg { + data: format!("Hello World {}",inkrement), + }; + self._publisher.publish(msg).unwrap(); + Ok(inkrement+1_i32) + } +} + +/// The main function initializes a ROS 2 context, node and publisher, +/// spawns a thread to publish messages repeatedly, and spins the node +/// to receive callbacks. +/// +/// It creates a context, initializes a SimplePublisherNode which creates +/// a node and publisher, clones the publisher to pass to the thread, +/// spawns a thread to publish "Hello World" messages repeatedly, and +/// calls spin() on the node to receive callbacks. This allows publishing +/// messages asynchronously while spinning the node. +fn main() -> Result<(),RclrsError> { + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut iterator: i32=0; + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + iterator=publisher_other_thread.publish_data(iterator).unwrap(); + }); + }); + rclrs::spin(publisher.node.clone()) +} +``` + +
Examine the Code: + +#### This first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. It's likely setting up a ROS 2 node that publishes string messages. +``` +use std::{sync::Arc,time::Duration,iter,thread}; +use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use std_msgs::msg::String as StringMsg; +``` +* use std::{sync::Arc, time::Duration, iter, thread};: - Imports specific features from the standard library: - Arc is for thread-safe shared ownership of data. - Duration represents a time span. - iter provides tools for working with iterators. - thread enables creating and managing threads. +* use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};: - Imports elements for ROS 2 communication: - RclrsError for handling errors. - QOS_PROFILE_DEFAULT likely for default Quality of Service settings. - Context, create_node, Node, Publisher are for ROS 2 node creation and publishing. +* use std_msgs::msg::String as StringMsg;: - Imports the StringMsg type for publishing string messages. + +#### Next this struct defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. +``` +struct SimplePublisherNode { + node: Arc, + _publisher: Arc>, +} +``` +1. Structure: +struct SimplePublisherNode: This line defines a new struct named SimplePublisherNode. It serves as a blueprint for creating objects that hold information related to a simple publisher node in ROS 2. + +2. Members: +* node: Arc: This member stores a reference to a ROS 2 node, wrapped in an Arc (Atomic Reference Counted) smart pointer. This allows for safe sharing of the node reference across multiple threads. +* _publisher: Arc>: This member stores a reference to a publisher specifically for string messages (StringMsg), also wrapped in an Arc for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. + +3. This code defines methods for the SimplePublisherNode struct. The new method creates a ROS 2 node and publisher, storing them in the struct. The publish_data method publishes a string message with a counter and returns the incremented counter. +``` +impl SimplePublisherNode { + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_publisher").unwrap(); + let _publisher = node + .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .unwrap(); + Ok(Self { node, _publisher, }) + } + fn publish_data(&self,inkrement:i32) -> Result { + + let msg: StringMsg = StringMsg { + data: format!("Hello World {}",inkrement), + }; + self._publisher.publish(msg).unwrap(); + Ok(inkrement+1_i32) + } +} +``` + +1. Implementation Block: +`impl SimplePublisherNode { ... }`: This line indicates that methods are being defined for the `SimplePublisherNode` struct. + +2. Constructor Method: +* `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of SimplePublisherNode. + * It takes a Context object as input, which is necessary for interacting with the ROS 2 system. + * It returns a Result type, indicating either a successful Self (the created SimplePublisherNode object) or an RclrsError if something goes wrong. + * Inside the new method: + * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named "simple_publisher" within the given context. The unwrap() unwraps the result, handling any errors immediately. + * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic "publish_hello" with default quality of service settings. + * `Ok(Self { node, _publisher, })`: Returns a Result with the newly created SimplePublisherNode object, containing the node and publisher references. + +3. Publishing Method: +* `fn publish_data(&self, inkrement: i32) -> Result { ... }`: This method publishes a string message and increments a counter. + * It takes an inkrement value (an integer) as input, which is likely used for counting purposes within the message content. + * It also returns a Result type, indicating either the incremented inkrement value or an RclrsError if publishing fails. + * Inside the publish_data method: + * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", inkrement), };`:tCreates a string message with the content "Hello World" followed by the inkrement value. + * self._publisher.publish(msg).unwrap();: Publishes the created message onto the topic associated with the publisher. + * Ok(inkrement + 1_i32): Returns a Result with the incremented inkrement value. + +#### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. + +``` +fn main() -> Result<(),RclrsError> { + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut iterator: i32=0; + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + iterator=publisher_other_thread.publish_data(iterator).unwrap(); + }); + }); + rclrs::spin(publisher.node.clone()) +} +``` + +1. Main Function: +* `fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a Result type, indicating either successful execution or an RclrsError. + +2. Context and Node Setup: + +* `let context = Context::new(std::env::args()).unwrap();`: Creates a ROS 2 context using command-line arguments. +* `let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());`: + * Creates an [Arc (atomic reference counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) pointer to a `SimplePublisherNode` object. + * Calls the new method on SimplePublisherNode to construct the node and publisher within the context. + +3. Thread and Iterator: +* `let publisher_other_thread = Arc::clone(&publisher);`: Clones the shared publisher pointer for use in a separate thread. +* `let mut iterator: i32 = 0;`: Initializes a counter variable for message content. +* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a closure: `iter::repeat(()).for_each(|()| { ... });`: Creates an infinite loop using `iter::repeat`. + +4. Publishing Loop within Thread: + +* `thread::sleep(Duration::from_millis(1000));`: Pauses the thread for 1 second (1 Hz publishing rate). +* `iterator = publisher_other_thread.publish_data(iterator).unwrap();`: Calls the publish_data method on the publisher_other_thread to publish a message with the current counter value. Increments the iterator for the next message. + +5. Main Thread Spin: +* `rclrs::spin(publisher.node.clone());`: Keeps the main thread running, processing ROS 2 events and messages. Uses a cloned reference to the node to ensure it remains active even with other threads. + +
+ +Once you have implemented the code, you are ready to run it: +``` +cd ${MainFolderOfWorkspace} +colcon build +source install/setub.bash +``` +And finally run with: +``` +ros2 run your_project_name your_project_name +``` +(Please give your package a better name than me ;) )
+
Having several Ros2 rust nodes in one Package + +Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. this could look like this: +``` +[package] +name = "your_package_name" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name="simple_publisher" +path="src/main.rs" +[dependencies] +rclrs = "*" +std_msgs = "*" +``` +You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The rust compiler will be a bit grumpy if you don't. +Now, by recompiling the package from the previous chapter and making it usable: +``` +cd ${MainFolderOfWorkspace} +colcon build +source install/setub.bash +``` +node will look like this: +``` +ros2 run your_package_name simple_publisher +``` +As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. +
+
Write the subscriber node + +Of course, you can implement a new ros2 rust package for this node. You can find out how to do this in the section called 'Create a package'. +Or you can add a new binary target to your package. Then just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: +``` + +``` + +
+
From 8e8300feadd67883d4866e95e46373bf7fb4f296 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 10 Apr 2024 22:02:28 +0200 Subject: [PATCH 003/113] Added descriptions for the subscriber --- ...iting_a_simple_publisher_and_subscriber.md | 91 ++++++++++++++++++- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 2c1d09fa0..0ba52302b 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -90,8 +90,6 @@ Of course, you can use any capable editor or even your file explorer to do this.
Write the publisher node - - To construct a node, replace the code in your main.rs with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): ``` /// Creates a SimplePublisherNode, initializes a node and publisher, and provides @@ -328,8 +326,93 @@ As you can see, you are now calling your node by the name declared in `[[bin]]` Of course, you can implement a new ros2 rust package for this node. You can find out how to do this in the section called 'Create a package'. Or you can add a new binary target to your package. Then just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ``` - +[[bin]] +name="simple_subscriber" +path="src/simple_subscriber.rs" ``` - +To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a file.rs - in my case its the `src/simple_subscriber.rs`: +``` +use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; +use std::{ + env, + iter,thread, + sync::{Arc, Mutex}, + time::Duration, +}; +use std_msgs::msg::String as StringMsg; +/// A simple ROS2 subscriber node that receives and prints "hello" messages. +/// +/// This node creates a subscription to the "publish_hello" topic and prints the +/// received messages to the console. It runs the subscription in a separate +/// thread, while the main thread calls `rclrs::spin()` to keep the node running. +pub struct SimpleSubscriptionNode { + node: Arc, + _subscriber: Arc>, + data: Arc>>, +} +/// Implements a simple ROS2 subscriber node that receives and prints "hello" messages. +/// +/// The `SimpleSubscriptionNode` creates a subscription to the "publish_hello" topic and +/// prints the received messages to the console. It runs the subscription in a separate +/// thread, while the main thread calls `rclrs::spin()` to keep the node running. +/// +/// The `new` function creates the node and the subscription, and returns a `SimpleSubscriptionNode` +/// instance. The `data_callback` function can be used to access the latest received message. +impl SimpleSubscriptionNode { + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_subscription").unwrap(); + let data: Arc>> = Arc::new(Mutex::new(None)); + let data_mut: Arc>> = Arc::clone(&data); + let _subscriber = node + .create_subscription::( + "publish_hello", + QOS_PROFILE_DEFAULT, + move |msg: StringMsg| { + *data_mut.lock().unwrap() = Some(msg); + }, + ) + .unwrap(); + Ok(Self { + node, + _subscriber, + data, + }) + } + fn data_callback(&self) -> Result<(), RclrsError> { + if let Some(data) = self.data.lock().unwrap().as_ref() { + println!("{}", data.data); + } else { + println!("No message available yet."); + } + Ok(()) + } +} +/// The `main` function creates a new ROS2 context, a `SimpleSubscriptionNode` instance, and starts a separate thread to periodically call the `data_callback` method on the subscription. The main thread then calls `rclrs::spin()` to keep the node running and receive messages. +/// +/// The separate thread is used to ensure that the `data_callback` method is called regularly, even if the main thread is blocked in `rclrs::spin()`. This allows the subscriber to continuously process and print the received "hello" messages. +fn main() -> Result<(), RclrsError> { + let context = Context::new(env::args()).unwrap(); + let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); + let subscription_other_thread = Arc::clone(&subscription); + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + subscription_other_thread.data_callback().unwrap() + }); + }); + rclrs::spin(subscription.node.clone()) +} +``` +Once you've implemented the code, you're ready to make it runnable: +``` +cd ${MainFolderOfWorkspace} +colcon build +source install/setub.bash +``` +And finally run with: +``` +ros2 run your_project_name your_node_name +``` +(Please give your package a better name than me ;) )
From 741c197fff6db037897e53cee14461eb9e081104 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 00:23:34 +0200 Subject: [PATCH 004/113] Incorporation of recommendations. Unfortunately, I found the tutorial itself unfinished so far and wanted to incorporate the justified criticism properly. One big change is that there is now a separate `Build and Run` section, which generally explains the correct building and starting of nodes. There are also a few sections that deal with explanations and a few that most people skip over. I'm largely satisfied so far and of course open to suggestions for improvement. --- ...iting_a_simple_publisher_and_subscriber.md | 254 +++++++++++++----- 1 file changed, 180 insertions(+), 74 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 0ba52302b..4742dfdc5 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -4,7 +4,7 @@ * Time: 20 minutes
Background -In this tutorial you will create a +In this tutorial you will create a pair of [nodes](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Nodes/Understanding-ROS2-Nodes.html) that pass information to each other via a [topic](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html) in the form of string messages. The example used here is a simple "talker" and "listener" system; one node publishes data and the other subscribes to the topic to receive that data. @@ -12,7 +12,7 @@ Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node)
-
Sidenode to dependencies +
Side-note on dependencies You may be wondering why you can't just add all your ros2-specific dependencies to `cargo.toml` with `cargo add ${dependencie}` and have to edit this file manually. Here is why: Almost none of the ROS2 dependencies you'll need for your ros2 rust node development currently exist on [crates.io](https://crates.io/), the main source for rust depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ros2 rust dependencies and your ros2 rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ros2 rust project this way. @@ -27,7 +27,7 @@ In previous tutorials, you learned how to create a [workspace](https://docs.ros. A basic understanding of [RUST](https://www.rust-lang.org/) is recommended, but not entirely necessary. Before developing ROS2 RUST nodes, you must follow the -[installation instructions](https://github.com/ros2-rust/ros2_rust/blob/main/README.md) for them. +[installation instructions](https://github.com/ros2-rust/ros2_rust/blob/main/README.md) for [`rclrs`](https://docs.rs/rclrs/latest/rclrs/).
@@ -41,7 +41,7 @@ from building packages for Python or C/C++. First, you'll need to create and go into a standard [cargo](https://doc.rust-lang.org/cargo/) project as follows: ``` -cargo new your_project_name && your_project_name +cargo new your_project_name && cd your_project_name ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: ``` @@ -63,9 +63,9 @@ Additionally, create a new `package.xml` if you want your node to be buildable w your_project_name 0.0.0 - TODO: Package description. Seriously. Please make a Package description. We all will thank for it. + TODO: Package description. user - TODO: License declaration. Licenses are Great. Please add a Licence + TODO: License declaration. rclrs std_msgs @@ -90,7 +90,7 @@ Of course, you can use any capable editor or even your file explorer to do this.
Write the publisher node -To construct a node, replace the code in your main.rs with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): +To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): ``` /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. @@ -106,7 +106,7 @@ struct SimplePublisherNode { node: Arc, _publisher: Arc>, } -/// Creates a new SimplePublisherNode by initializing a node and publisher. +/// An impl block in Rust defines methods or associated functions for a specific type. /// /// The `new` function takes a context and returns a Result containing the /// initialized SimplePublisherNode or an error. It creates a node with the @@ -134,13 +134,13 @@ impl SimplePublisherNode { /// Creates a StringMsg with "Hello World" as the data, publishes it on /// the `_publisher`, and returns a Result. This allows regularly publishing /// a simple message on a loop. - fn publish_data(&self,inkrement:i32) -> Result { + fn publish_data(&self,increment:i32) -> Result { let msg: StringMsg = StringMsg { - data: format!("Hello World {}",inkrement), + data: format!("Hello World {}",increment), }; self._publisher.publish(msg).unwrap(); - Ok(inkrement+1_i32) + Ok(increment+1_i32) } } @@ -157,43 +157,49 @@ fn main() -> Result<(),RclrsError> { let context = Context::new(std::env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); let publisher_other_thread = Arc::clone(&publisher); - let mut iterator: i32=0; + let mut count: i32=0; thread::spawn(move || -> () { iter::repeat(()).for_each(|()| { thread::sleep(Duration::from_millis(1000)); - iterator=publisher_other_thread.publish_data(iterator).unwrap(); + count=publisher_other_thread.publish_data(count).unwrap(); }); }); rclrs::spin(publisher.node.clone()) } ``` -
Examine the Code: +
Examining the code in detail: -#### This first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. It's likely setting up a ROS 2 node that publishes string messages. +#### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. ``` use std::{sync::Arc,time::Duration,iter,thread}; use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; use std_msgs::msg::String as StringMsg; ``` -* use std::{sync::Arc, time::Duration, iter, thread};: - Imports specific features from the standard library: - Arc is for thread-safe shared ownership of data. - Duration represents a time span. - iter provides tools for working with iterators. - thread enables creating and managing threads. -* use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};: - Imports elements for ROS 2 communication: - RclrsError for handling errors. - QOS_PROFILE_DEFAULT likely for default Quality of Service settings. - Context, create_node, Node, Publisher are for ROS 2 node creation and publishing. -* use std_msgs::msg::String as StringMsg;: - Imports the StringMsg type for publishing string messages. +* use std::{sync::Arc, time::Duration, iter, thread};: Imports specific features from the standard library: + - Arc is for thread-safe shared ownership of data. + - Duration represents a time span. + - iter provides tools for working with iterators. - thread enables creating and managing threads. +* use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};: + - Imports elements for ROS 2 communication: + - RclrsError for handling errors. + - QOS_PROFILE_DEFAULT for default Quality of Service settings. + - Context, create_node, Node, Publisher are for ROS 2 node creation and publishing. publishing. +* use std_msgs::msg::String as StringMsg;: Imports the StringMsg type for publishing string messages. -#### Next this struct defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. +#### Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. ``` struct SimplePublisherNode { node: Arc, _publisher: Arc>, } ``` -1. Structure: -struct SimplePublisherNode: This line defines a new struct named SimplePublisherNode. It serves as a blueprint for creating objects that hold information related to a simple publisher node in ROS 2. +1. Structure: +struct SimplePublisherNode: This line defines a new struct named SimplePublisherNode. It serves as a blueprint for creating objects that hold information related to a simple publisher node in ROS 2. 2. Members: * node: Arc: This member stores a reference to a ROS 2 node, wrapped in an Arc (Atomic Reference Counted) smart pointer. This allows for safe sharing of the node reference across multiple threads. * _publisher: Arc>: This member stores a reference to a publisher specifically for string messages (StringMsg), also wrapped in an Arc for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. - 3. This code defines methods for the SimplePublisherNode struct. The new method creates a ROS 2 node and publisher, storing them in the struct. The publish_data method publishes a string message with a counter and returns the incremented counter. ``` impl SimplePublisherNode { @@ -204,53 +210,51 @@ impl SimplePublisherNode { .unwrap(); Ok(Self { node, _publisher, }) } - fn publish_data(&self,inkrement:i32) -> Result { + fn publish_data(&self,increment:i32) -> Result { let msg: StringMsg = StringMsg { - data: format!("Hello World {}",inkrement), + data: format!("Hello World {}",increment), }; self._publisher.publish(msg).unwrap(); - Ok(inkrement+1_i32) + Ok(increment+1_i32) } } ``` -1. Implementation Block: -`impl SimplePublisherNode { ... }`: This line indicates that methods are being defined for the `SimplePublisherNode` struct. - -2. Constructor Method: -* `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of SimplePublisherNode. - * It takes a Context object as input, which is necessary for interacting with the ROS 2 system. - * It returns a Result type, indicating either a successful Self (the created SimplePublisherNode object) or an RclrsError if something goes wrong. - * Inside the new method: - * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named "simple_publisher" within the given context. The unwrap() unwraps the result, handling any errors immediately. - * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic "publish_hello" with default quality of service settings. - * `Ok(Self { node, _publisher, })`: Returns a Result with the newly created SimplePublisherNode object, containing the node and publisher references. - +1. Implementation Block: +`impl SimplePublisherNode { ... }`: This line indicates that methods are being defined for the `SimplePublisherNode` struct. +2. Constructor Method: +* `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of SimplePublisherNode. + * It takes a Context object as input, which is necessary for interacting with the ROS 2 syste. + * It returns a Result type, indicating either a successful Self (the created SimplePublisherNode object) or an RclrsError if something goes wrong. + * Inside the new method: + * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named "simple_publisher" within the given context. The unwrap() unwraps the result, handling any errors immediately by forcing the program to abort (`panic`) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. + * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic "publish_hello" with default quality of service settings. + * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created SimplePublisherNode object, containing the node and publisher references. 3. Publishing Method: -* `fn publish_data(&self, inkrement: i32) -> Result { ... }`: This method publishes a string message and increments a counter. - * It takes an inkrement value (an integer) as input, which is likely used for counting purposes within the message content. - * It also returns a Result type, indicating either the incremented inkrement value or an RclrsError if publishing fails. +* `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a counter. + * It takes an inkrement value (an integer) as input, which is used for counting purposes within the message content. + * It also returns a Result type, indicating either the incremented value or an RclrsError if publishing fails. * Inside the publish_data method: - * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", inkrement), };`:tCreates a string message with the content "Hello World" followed by the inkrement value. + * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), };`: Creates a string message with the content "Hello World" followed by the inkrement value. * self._publisher.publish(msg).unwrap();: Publishes the created message onto the topic associated with the publisher. - * Ok(inkrement + 1_i32): Returns a Result with the incremented inkrement value. + * Ok(increment + 1_i32): Returns a Result with the incremented increment value. #### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. ``` fn main() -> Result<(),RclrsError> { - let context = Context::new(std::env::args()).unwrap(); - let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); - let publisher_other_thread = Arc::clone(&publisher); - let mut iterator: i32=0; - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - iterator=publisher_other_thread.publish_data(iterator).unwrap(); - }); - }); - rclrs::spin(publisher.node.clone()) + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut count: i32=0; + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + count=publisher_other_thread.publish_data(count).unwrap(); + }); + }); + rclrs::spin(publisher.node.clone()) } ``` @@ -278,22 +282,10 @@ fn main() -> Result<(),RclrsError> { * `rclrs::spin(publisher.node.clone());`: Keeps the main thread running, processing ROS 2 events and messages. Uses a cloned reference to the node to ensure it remains active even with other threads.
- -Once you have implemented the code, you are ready to run it: -``` -cd ${MainFolderOfWorkspace} -colcon build -source install/setub.bash -``` -And finally run with: -``` -ros2 run your_project_name your_project_name -``` -(Please give your package a better name than me ;) )
Having several Ros2 rust nodes in one Package -Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. this could look like this: +Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ``` [package] name = "your_package_name" @@ -315,7 +307,7 @@ cd ${MainFolderOfWorkspace} colcon build source install/setub.bash ``` -node will look like this: +Running the node will look like this: ``` ros2 run your_package_name simple_publisher ``` @@ -324,13 +316,13 @@ As you can see, you are now calling your node by the name declared in `[[bin]]`
Write the subscriber node Of course, you can implement a new ros2 rust package for this node. You can find out how to do this in the section called 'Create a package'. -Or you can add a new binary target to your package. Then just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: +Or you can add a new binary target to your package. To do so, just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ``` [[bin]] name="simple_subscriber" path="src/simple_subscriber.rs" ``` -To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a file.rs - in my case its the `src/simple_subscriber.rs`: +To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a file.rs - in my case its the `src/simple_subscriber.rs`: ``` use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ @@ -403,16 +395,130 @@ fn main() -> Result<(), RclrsError> { rclrs::spin(subscription.node.clone()) } ``` -Once you've implemented the code, you're ready to make it runnable: +
Examining the code in detail: + +#### The main Construct: +``` +pub struct SimpleSubscriptionNode { + node: Arc, + _subscriber: Arc>, + data: Arc>>, +} +``` +Instead of a Publisher, there is a Subscription object in the Subscriber node. The data needs to be an `Arc>>` because there can be errors in the data transfer process and this can be caught by including the value of the incoming subscription in an optional. +#### This code defines a function named new that likely creates an instance of some SimpleSubscriptionNode. +``` + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_subscription").unwrap(); + let data: Arc>> = Arc::new(Mutex::new(None)); + let data_mut: Arc>> = Arc::clone(&data); + let _subscriber = node + .create_subscription::( + "publish_hello", + QOS_PROFILE_DEFAULT, + move |msg: StringMsg| { + *data_mut.lock().unwrap() = Some(msg); + }, + ) + .unwrap(); + Ok(Self { + node, + _subscriber, + data, + }) + } + +``` +A few special features: +1. Initializing Shared Data: + * `let data: Arc>> = Arc::new(Mutex::new(None));` + This line creates a shared data structure that will hold the received message. + * `Arc>>`: This is a complex type combining several functionalities: + * `Arc`: An atomically reference-counted pointer (Arc) allows multiple parts of the code to safely access the same data (T). + * `Mutex`: A mutual exclusion lock (`Mutex`) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. + * `Option`: This represents an optional value that can either hold a message of type `StringMsg` or be `None` if no message has been received yet. + * `Arc::new(Mutex::new(None))`: This creates a new instance of `Arc>>` and initializes the inner `Mutex` with `None`. +2. Creating a Subscription: + * `let _subscriber = node.create_subscription::(...` + This line attempts to create a subscription using the created ROS node (`node`). + * `create_subscription`: This is creates a subscription to a specific topic. + * ``: This specifies the type of message the subscription is interested in (`StringMsg`) and a placeholder (`_`) for the callback closure type. + `"publish_hello"`: This is the name of the ROS topic this node wants to subscribe to. Messages of type StringMsg are expected on this topic. + * `move |msg: StringMsg| { ... }`: This is a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html) (anonymous function) that will be called whenever a new message arrives on the subscribed topic. + * `msg: StringMsg`: This parameter receives the received message of type `StringMsg`. The closure body (`{...}`) uses the `Mutex` to access and update the shared data (`data_mut`) with the received message. +3. Cloning the Shared Data: + * `let data_mut: Arc>> = Arc::clone(&data)`; This line creates another `Arc` reference (`data_mut`) pointing to the same underlying data structure as data. This allows the closure to access and modify the shared data. +#### this function provides a way to access and potentially use the received message data stored within the `Arc>>` member variable of the struct. It checks if a message exists, prints it if available, or informs the user there's no message yet. +``` +fn data_callback(&self) -> Result<(), RclrsError> { + if let Some(data) = self.data.lock().unwrap().as_ref() { + println!("{}", data.data); + } else { + println!("No message available yet."); + } + Ok(()) +} + +``` +A few special features: + + +1. Checking for Received Message: + * `if let Some(data) = self.data.lock().unwrap().as_ref() { ... }`: This is an if-let statement used for pattern matching on optional values. + * `self.data`: This accesses the member variable data of the struct (likely the `Arc>>` created earlier). + * `.lock().unwrap()`: This calls the lock method on the `Mutex` to gain exclusive access to the shared data. If another thread already holds the lock, lock might block until the lock is released. + `.as_ref()`: This converts the borrowed `MutexGuard` (returned by `.lock()`) into a reference to the inner value (`Option`). + * `Some(data)`: This pattern attempts to match the value inside the Option with `Some(data)`. If there's a message (`Some(data)`), the code block after the if is executed, and data is bound to the actual message content of type `StringMsg`. + +
+
+
Build and Run + +Once you have implemented the code, you are ready to make it runnable: ``` cd ${MainFolderOfWorkspace} colcon build -source install/setub.bash ``` -And finally run with: +Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ros2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: ``` +cd ${MainFolderOfWorkspace} +source install/setup.bash ros2 run your_project_name your_node_name ``` -(Please give your package a better name than me ;) ) -
+In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. +If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: +``` +Hello World 230 +Hello World 231 +Hello World 232 +Hello World 233 +Hello World 234 +Hello World 235 +Hello World 236 +Hello World 237 +Hello World 238 +Hello World 239 +Hello World 240 +Hello World 241 +Hello World 242 +Hello World 243 +Hello World 244 +Hello World 245 +Hello World 246 +``` +My nodes have been running for some time. +Enter `Ctrl+C` in each terminal to stop the nodes from spinning.
+
+ +
Summary + +You created two nodes to publish and subscribe to data over a topic. Before running them, you added their dependencies and entry points to the package configuration files. + +
+ +
Last thoughts + +At the end of the day, tools must not only work more safely and efficiently from a purely rational point of view, but they must also give the end user, as well as the developer, a good time. Hopefully you had fun developing the two nodes. Without fun, software development can be boring and will often prevent you from using this tool again. + +
From 1365bee1fbccc05c5c0e309b2090808d6a61ea2d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 11:52:02 +0200 Subject: [PATCH 005/113] Fixup of the main rust_formatter.sh --- rust_formater.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 rust_formater.sh diff --git a/rust_formater.sh b/rust_formater.sh new file mode 100755 index 000000000..a84714329 --- /dev/null +++ b/rust_formater.sh @@ -0,0 +1,24 @@ +project_dirs=( + "./rclrs" + "./rosidl_runtime_rs" + "./examples/message_demo" + "./examples/minimal_client_service" + "./examples/minimal_pub_sub" + "./examples/your_package_name/" +) + +# Loop through each subdirectory +for project_dir in "${project_dirs[@]}"; do + # Check if the directory exists + cd "$project_dir" + + # Run cargo fmt + cargo-fmt + cargo-clippy + + # Change back to the root directory + cd "./" + +done + +echo "Formatting completed!" From 9eba93f447b20b5d600dd5c2f786c67e2869167d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 11:52:19 +0200 Subject: [PATCH 006/113] Spellchecking --- ...iting_a_simple_publisher_and_subscriber.md | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 4742dfdc5..eebcf34ed 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -1,5 +1,5 @@ # Writing a simple publisher and subscriber (RUST) -* Goal: Create and run a publisher and subscriber node using Python. +* Goal: Create and run a publisher and subscriber node using RUST. * Tutorial level: Beginner * Time: 20 minutes
Background @@ -8,14 +8,14 @@ In this tutorial you will create a pair of [nodes](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Nodes/Understanding-ROS2-Nodes.html) that pass information to each other via a [topic](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html) in the form of string messages. The example used here is a simple "talker" and "listener" system; one node publishes data and the other subscribes to the topic to receive that data. -Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). +Since RUST doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). -The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node) +The code used in these examples can be found [here](https://gitlab.com/ROS21923912/simple_ROS2_node)
Side-note on dependencies -You may be wondering why you can't just add all your ros2-specific dependencies to `cargo.toml` with `cargo add ${dependencie}` and have to edit this file manually. Here is why: -Almost none of the ROS2 dependencies you'll need for your ros2 rust node development currently exist on [crates.io](https://crates.io/), the main source for rust depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ros2 rust dependencies and your ros2 rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ros2 rust project this way. +You may be wondering why you can't just add all your ROS2-specific dependencies to `Cargo.toml` with `cargo add YOUR_DEPENDENCIES` and have to edit this file manually. Here is why: +Almost none of the ROS2 dependencies you'll need for your ROS2 RUST node development currently exist on [crates.io](https://crates.io/), the main source for RUST depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 RUST dependencies and your ROS2 RUST project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated RUST projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 RUST project this way.
@@ -23,11 +23,13 @@ Almost none of the ROS2 dependencies you'll need for your ros2 rust node develop
Prerequisites -In previous tutorials, you learned how to create a [workspace](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html) and [create a package](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html). +Basic concepts of development with ROS2 should be known: +* [workspaces](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html) +* [packages](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html). -A basic understanding of [RUST](https://www.rust-lang.org/) is recommended, but not entirely necessary. -Before developing ROS2 RUST nodes, you must follow the -[installation instructions](https://github.com/ros2-rust/ros2_rust/blob/main/README.md) for [`rclrs`](https://docs.rs/rclrs/latest/rclrs/). +A basic understanding of [RUST](https://doc.rust-lang.org/book/) is recommended, but not entirely necessary. +Before developing [ros2-rust](https://github.com/ros2-rust/ros2_rust) nodes, you must follow the +[installation instructions](https://github.com/ros2-rust/ros2-rust/blob/main/README.md) for [`rclrs`](https://docs.rs/rclrs/latest/rclrs/).
@@ -35,13 +37,13 @@ Before developing ROS2 RUST nodes, you must follow the
Tasks
Create a Package -Currently, building a package for ROS2 RUST is different -from building packages for Python or C/C++. +Currently, building a package for ros2-rust is different +from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html) or [C/C++](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html). First, you'll need to create and go into a standard [cargo](https://doc.rust-lang.org/cargo/) project as follows: ``` -cargo new your_project_name && cd your_project_name +cargo new your_package_name && cd your_package_name ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: ``` @@ -61,7 +63,7 @@ std_msgs = "*" Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: ```xml - your_project_name + your_package_name 0.0.0 TODO: Package description. user @@ -106,7 +108,7 @@ struct SimplePublisherNode { node: Arc, _publisher: Arc>, } -/// An impl block in Rust defines methods or associated functions for a specific type. +/// An impl block in RUST defines methods or associated functions for a specific type. /// /// The `new` function takes a context and returns a Result containing the /// initialized SimplePublisherNode or an error. It creates a node with the @@ -170,7 +172,7 @@ fn main() -> Result<(),RclrsError> {
Examining the code in detail: -#### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. +#### The first 3 lines of the RUST code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. ``` use std::{sync::Arc,time::Duration,iter,thread}; use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; @@ -283,7 +285,7 @@ fn main() -> Result<(),RclrsError> {
-
Having several Ros2 rust nodes in one Package +
Having several ROS2 RUST nodes in one Package Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ``` @@ -300,7 +302,7 @@ path="src/main.rs" rclrs = "*" std_msgs = "*" ``` -You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The rust compiler will be a bit grumpy if you don't. +You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The RUST compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: ``` cd ${MainFolderOfWorkspace} @@ -315,7 +317,7 @@ As you can see, you are now calling your node by the name declared in `[[bin]]`
Write the subscriber node -Of course, you can implement a new ros2 rust package for this node. You can find out how to do this in the section called 'Create a package'. +Of course, you can implement a new ROS2 RUST package for this node. You can find out how to do this in the section called 'Create a package'. Or you can add a new binary target to your package. To do so, just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ``` [[bin]] @@ -479,11 +481,11 @@ Once you have implemented the code, you are ready to make it runnable: cd ${MainFolderOfWorkspace} colcon build ``` -Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ros2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: +Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ROS2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: ``` cd ${MainFolderOfWorkspace} source install/setup.bash -ros2 run your_project_name your_node_name +ros2 run your_package_name your_node_name ``` In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: From 8d0232232055fef941fec63aa00aee0df11209d1 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 11:52:23 +0200 Subject: [PATCH 007/113] Spellchecking --- examples/your_package_name | 1 + 1 file changed, 1 insertion(+) create mode 160000 examples/your_package_name diff --git a/examples/your_package_name b/examples/your_package_name new file mode 160000 index 000000000..4b440f9b3 --- /dev/null +++ b/examples/your_package_name @@ -0,0 +1 @@ +Subproject commit 4b440f9b36f0b6a56dfc98c7c98e31b48ae77dac From e376a5a7882e9d450bbadbe4420b9c8ad03cd638 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 11:54:59 +0200 Subject: [PATCH 008/113] removed rust_formatter to keep convenience --- rust_formater.sh | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100755 rust_formater.sh diff --git a/rust_formater.sh b/rust_formater.sh deleted file mode 100755 index a84714329..000000000 --- a/rust_formater.sh +++ /dev/null @@ -1,24 +0,0 @@ -project_dirs=( - "./rclrs" - "./rosidl_runtime_rs" - "./examples/message_demo" - "./examples/minimal_client_service" - "./examples/minimal_pub_sub" - "./examples/your_package_name/" -) - -# Loop through each subdirectory -for project_dir in "${project_dirs[@]}"; do - # Check if the directory exists - cd "$project_dir" - - # Run cargo fmt - cargo-fmt - cargo-clippy - - # Change back to the root directory - cd "./" - -done - -echo "Formatting completed!" From 78fb09dd866dbf563d5fb85421154827ca862e49 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 12:07:06 +0200 Subject: [PATCH 009/113] submodule removed --- examples/your_package_name | 1 - 1 file changed, 1 deletion(-) delete mode 160000 examples/your_package_name diff --git a/examples/your_package_name b/examples/your_package_name deleted file mode 160000 index 4b440f9b3..000000000 --- a/examples/your_package_name +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b440f9b36f0b6a56dfc98c7c98e31b48ae77dac From 7d4c7138e729d36a9ff8ad0703449da1a0f86114 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 12:07:45 +0200 Subject: [PATCH 010/113] Added your_package_name.xml --- examples/your_package_name/package.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 examples/your_package_name/package.xml diff --git a/examples/your_package_name/package.xml b/examples/your_package_name/package.xml new file mode 100644 index 000000000..b7aa0df2f --- /dev/null +++ b/examples/your_package_name/package.xml @@ -0,0 +1,14 @@ + + your_project_name + 0.0.0 + TODO: Package description. Seriously. Please make a Package description. We all will thank for it. + user + TODO: License declaration. Licenses are Great. Please add a Licence + + rclrs + std_msgs + + + ament_cargo + + From 625357f0f3523b1ca0e7602530ae7faa035816f8 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 12:07:56 +0200 Subject: [PATCH 011/113] added cargo manifest --- examples/your_package_name/Cargo.toml | 15 ++++ examples/your_package_name/src/main.rs | 72 +++++++++++++++++++ .../src/simple_subscriber.rs | 65 +++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 examples/your_package_name/Cargo.toml create mode 100644 examples/your_package_name/src/main.rs create mode 100644 examples/your_package_name/src/simple_subscriber.rs diff --git a/examples/your_package_name/Cargo.toml b/examples/your_package_name/Cargo.toml new file mode 100644 index 000000000..38f02a2b7 --- /dev/null +++ b/examples/your_package_name/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "your_package_name" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name="simple_publisher" +path="src/main.rs" +[[bin]] +name="simple_subscriber" +path="src/simple_subscriber.rs" +[dependencies] +rclrs = "*" +std_msgs = "*" diff --git a/examples/your_package_name/src/main.rs b/examples/your_package_name/src/main.rs new file mode 100644 index 000000000..bbe12c615 --- /dev/null +++ b/examples/your_package_name/src/main.rs @@ -0,0 +1,72 @@ +/// Creates a SimplePublisherNode, initializes a node and publisher, and provides +/// methods to publish a simple "Hello World" message on a loop in separate threads. + +/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, +/// and the StringMsg message type from std_msgs for publishing string messages. +use std::{sync::Arc,time::Duration,iter,thread}; +use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use std_msgs::msg::String as StringMsg; +// / SimplePublisherNode struct contains node and publisher members. +// / Used to initialize a ROS 2 node and publisher, and publish messages. +struct SimplePublisherNode { + node: Arc, + _publisher: Arc>, +} +/// The `new` function takes a context and returns a Result containing the +/// initialized SimplePublisherNode or an error. It creates a node with the +/// given name and creates a publisher on the "publish_hello" topic. +/// +/// The SimplePublisherNode contains the node and publisher members. +impl SimplePublisherNode { + /// Creates a new SimplePublisherNode by initializing a node and publisher. + /// + /// This function takes a context and returns a Result containing the + /// initialized SimplePublisherNode or an error. It creates a node with the + /// given name and creates a publisher on the "publish_hello" topic. + /// + /// The SimplePublisherNode contains the node and publisher members. + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_publisher").unwrap(); + let _publisher = node + .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .unwrap(); + Ok(Self { node, _publisher, }) + } + + /// Publishes a "Hello World" message on the publisher. + /// + /// Creates a StringMsg with "Hello World" as the data, publishes it on + /// the `_publisher`, and returns a Result. This allows regularly publishing + /// a simple message on a loop. + fn publish_data(&self,inkrement:i32) -> Result { + + let msg: StringMsg = StringMsg { + data: format!("Hello World {}",inkrement), + }; + self._publisher.publish(msg).unwrap(); + Ok(inkrement+1_i32) + } +} + +/// The main function initializes a ROS 2 context, node and publisher, +/// spawns a thread to publish messages repeatedly, and spins the node +/// to receive callbacks. +/// +/// It creates a context, initializes a SimplePublisherNode which creates +/// a node and publisher, clones the publisher to pass to the thread, +/// spawns a thread to publish "Hello World" messages repeatedly, and +/// calls spin() on the node to receive callbacks. This allows publishing +/// messages asynchronously while spinning the node. +fn main() -> Result<(),RclrsError> { + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut iterator: i32=0; + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + iterator=publisher_other_thread.publish_data(iterator).unwrap(); + }); + }); + rclrs::spin(publisher.node.clone()) +} diff --git a/examples/your_package_name/src/simple_subscriber.rs b/examples/your_package_name/src/simple_subscriber.rs new file mode 100644 index 000000000..60abcf98f --- /dev/null +++ b/examples/your_package_name/src/simple_subscriber.rs @@ -0,0 +1,65 @@ +use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; +use std::{ + env, + iter,thread, + sync::{Arc, Mutex}, + time::Duration, +}; +use std_msgs::msg::String as StringMsg; +/// A simple ROS2 subscriber node that receives and prints "hello" messages. +/// +/// This node creates a subscription to the "publish_hello" topic and prints the +/// received messages to the console. It runs the subscription in a separate +/// thread, while the main thread calls `rclrs::spin()` to keep the node running. +pub struct SimpleSubscriptionNode { + node: Arc, + _subscriber: Arc>, + data: Arc>>, +} +/// Implements a simple ROS2 subscriber node that receives and prints "hello" messages. +/// The `new` function creates the node and the subscription, and returns a `SimpleSubscriptionNode` +/// instance. The `data_callback` function can be used to access the latest received message. +impl SimpleSubscriptionNode { + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_subscription").unwrap(); + let data: Arc>> = Arc::new(Mutex::new(None)); + let data_mut: Arc>> = Arc::clone(&data); + let _subscriber = node + .create_subscription::( + "publish_hello", + QOS_PROFILE_DEFAULT, + move |msg: StringMsg| { + *data_mut.lock().unwrap() = Some(msg); + }, + ) + .unwrap(); + Ok(Self { + node, + _subscriber, + data, + }) + } + fn data_callback(&self) -> Result<(), RclrsError> { + if let Some(data) = self.data.lock().unwrap().as_ref() { + println!("{}", data.data); + } else { + println!("No message available yet."); + } + Ok(()) + } +} +/// The `main` function creates a new ROS2 context, a `SimpleSubscriptionNode` instance, and starts a separate thread to periodically call the `data_callback` method on the subscription. The main thread then calls `rclrs::spin()` to keep the node running and receive messages. +/// +/// The separate thread is used to ensure that the `data_callback` method is called regularly, even if the main thread is blocked in `rclrs::spin()`. This allows the subscriber to continuously process and print the received "hello" messages. +fn main() -> Result<(), RclrsError> { + let context = Context::new(env::args()).unwrap(); + let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); + let subscription_other_thread = Arc::clone(&subscription); + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + subscription_other_thread.data_callback().unwrap() + }); + }); + rclrs::spin(subscription.node.clone()) +} From 503896693f63c01a3b14f120edd548237eb1438c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 12:14:55 +0200 Subject: [PATCH 012/113] fixed some links and added empty lines --- docs/writing_a_simple_publisher_and_subscriber.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index eebcf34ed..0bf67e2fe 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -10,7 +10,7 @@ In this tutorial you will create a pair of Since RUST doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). -The code used in these examples can be found [here](https://gitlab.com/ROS21923912/simple_ROS2_node) +The code used in these examples can be found [here](https://github.com/Guelakais/ros2_rust/tree/better_tutorial/examples/your_package_name)
Side-note on dependencies @@ -92,7 +92,7 @@ Of course, you can use any capable editor or even your file explorer to do this.
Write the publisher node -To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/raw/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): +To construct a node, replace the code in your `main.rs` file with the [following](https://github.com/Guelakais/ros2_rust/blob/better_tutorial/examples/your_package_name/src/main.rs): ``` /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. @@ -314,6 +314,7 @@ Running the node will look like this: ros2 run your_package_name simple_publisher ``` As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. +
Write the subscriber node @@ -324,7 +325,7 @@ Or you can add a new binary target to your package. To do so, just add a new ` Date: Fri, 12 Apr 2024 12:46:21 +0200 Subject: [PATCH 013/113] cargo-fmt usage --- examples/your_package_name/src/main.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/your_package_name/src/main.rs b/examples/your_package_name/src/main.rs index bbe12c615..ede53ad01 100644 --- a/examples/your_package_name/src/main.rs +++ b/examples/your_package_name/src/main.rs @@ -1,10 +1,10 @@ +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. /// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, /// and the StringMsg message type from std_msgs for publishing string messages. -use std::{sync::Arc,time::Duration,iter,thread}; -use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use std::{iter, sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; // / SimplePublisherNode struct contains node and publisher members. // / Used to initialize a ROS 2 node and publisher, and publish messages. @@ -25,12 +25,12 @@ impl SimplePublisherNode { /// given name and creates a publisher on the "publish_hello" topic. /// /// The SimplePublisherNode contains the node and publisher members. - fn new(context: &Context) -> Result { + fn new(context: &Context) -> Result { let node = create_node(context, "simple_publisher").unwrap(); let _publisher = node .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) .unwrap(); - Ok(Self { node, _publisher, }) + Ok(Self { node, _publisher }) } /// Publishes a "Hello World" message on the publisher. @@ -38,34 +38,33 @@ impl SimplePublisherNode { /// Creates a StringMsg with "Hello World" as the data, publishes it on /// the `_publisher`, and returns a Result. This allows regularly publishing /// a simple message on a loop. - fn publish_data(&self,inkrement:i32) -> Result { - + fn publish_data(&self, increment: i32) -> Result { let msg: StringMsg = StringMsg { - data: format!("Hello World {}",inkrement), + data: format!("Hello World {}", increment), }; self._publisher.publish(msg).unwrap(); - Ok(inkrement+1_i32) + Ok(increment + 1_i32) } } /// The main function initializes a ROS 2 context, node and publisher, /// spawns a thread to publish messages repeatedly, and spins the node /// to receive callbacks. -/// +/// /// It creates a context, initializes a SimplePublisherNode which creates /// a node and publisher, clones the publisher to pass to the thread, /// spawns a thread to publish "Hello World" messages repeatedly, and /// calls spin() on the node to receive callbacks. This allows publishing /// messages asynchronously while spinning the node. -fn main() -> Result<(),RclrsError> { +fn main() -> Result<(), RclrsError> { let context = Context::new(std::env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); let publisher_other_thread = Arc::clone(&publisher); - let mut iterator: i32=0; + let mut count: i32 = 0; thread::spawn(move || -> () { iter::repeat(()).for_each(|()| { thread::sleep(Duration::from_millis(1000)); - iterator=publisher_other_thread.publish_data(iterator).unwrap(); + count = publisher_other_thread.publish_data(count).unwrap(); }); }); rclrs::spin(publisher.node.clone()) From 4e9a7c9e09345d4f364656d3500dac8d9b88b663 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 12:46:30 +0200 Subject: [PATCH 014/113] cargo-fmt usage --- examples/your_package_name/src/simple_subscriber.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/your_package_name/src/simple_subscriber.rs b/examples/your_package_name/src/simple_subscriber.rs index 60abcf98f..f70563e1c 100644 --- a/examples/your_package_name/src/simple_subscriber.rs +++ b/examples/your_package_name/src/simple_subscriber.rs @@ -1,8 +1,8 @@ use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ - env, - iter,thread, + env, iter, sync::{Arc, Mutex}, + thread, time::Duration, }; use std_msgs::msg::String as StringMsg; From 826436e30b0090ee56fefa0ca75b96b56a18ace3 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 15:43:23 +0200 Subject: [PATCH 015/113] removed main.rs --- examples/your_package_name/src/main.rs | 71 -------------------------- 1 file changed, 71 deletions(-) delete mode 100644 examples/your_package_name/src/main.rs diff --git a/examples/your_package_name/src/main.rs b/examples/your_package_name/src/main.rs deleted file mode 100644 index ede53ad01..000000000 --- a/examples/your_package_name/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; -/// Creates a SimplePublisherNode, initializes a node and publisher, and provides -/// methods to publish a simple "Hello World" message on a loop in separate threads. - -/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, -/// and the StringMsg message type from std_msgs for publishing string messages. -use std::{iter, sync::Arc, thread, time::Duration}; -use std_msgs::msg::String as StringMsg; -// / SimplePublisherNode struct contains node and publisher members. -// / Used to initialize a ROS 2 node and publisher, and publish messages. -struct SimplePublisherNode { - node: Arc, - _publisher: Arc>, -} -/// The `new` function takes a context and returns a Result containing the -/// initialized SimplePublisherNode or an error. It creates a node with the -/// given name and creates a publisher on the "publish_hello" topic. -/// -/// The SimplePublisherNode contains the node and publisher members. -impl SimplePublisherNode { - /// Creates a new SimplePublisherNode by initializing a node and publisher. - /// - /// This function takes a context and returns a Result containing the - /// initialized SimplePublisherNode or an error. It creates a node with the - /// given name and creates a publisher on the "publish_hello" topic. - /// - /// The SimplePublisherNode contains the node and publisher members. - fn new(context: &Context) -> Result { - let node = create_node(context, "simple_publisher").unwrap(); - let _publisher = node - .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) - .unwrap(); - Ok(Self { node, _publisher }) - } - - /// Publishes a "Hello World" message on the publisher. - /// - /// Creates a StringMsg with "Hello World" as the data, publishes it on - /// the `_publisher`, and returns a Result. This allows regularly publishing - /// a simple message on a loop. - fn publish_data(&self, increment: i32) -> Result { - let msg: StringMsg = StringMsg { - data: format!("Hello World {}", increment), - }; - self._publisher.publish(msg).unwrap(); - Ok(increment + 1_i32) - } -} - -/// The main function initializes a ROS 2 context, node and publisher, -/// spawns a thread to publish messages repeatedly, and spins the node -/// to receive callbacks. -/// -/// It creates a context, initializes a SimplePublisherNode which creates -/// a node and publisher, clones the publisher to pass to the thread, -/// spawns a thread to publish "Hello World" messages repeatedly, and -/// calls spin() on the node to receive callbacks. This allows publishing -/// messages asynchronously while spinning the node. -fn main() -> Result<(), RclrsError> { - let context = Context::new(std::env::args()).unwrap(); - let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); - let publisher_other_thread = Arc::clone(&publisher); - let mut count: i32 = 0; - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - count = publisher_other_thread.publish_data(count).unwrap(); - }); - }); - rclrs::spin(publisher.node.clone()) -} From d6e2a8f36433cb69a64c06ea6e77674cf0238c0c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 15:43:57 +0200 Subject: [PATCH 016/113] moved main.rs to simple_publisher.rs for better understanding --- .../your_package_name/src/simple_publisher.rs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 examples/your_package_name/src/simple_publisher.rs diff --git a/examples/your_package_name/src/simple_publisher.rs b/examples/your_package_name/src/simple_publisher.rs new file mode 100644 index 000000000..ede53ad01 --- /dev/null +++ b/examples/your_package_name/src/simple_publisher.rs @@ -0,0 +1,71 @@ +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; +/// Creates a SimplePublisherNode, initializes a node and publisher, and provides +/// methods to publish a simple "Hello World" message on a loop in separate threads. + +/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, +/// and the StringMsg message type from std_msgs for publishing string messages. +use std::{iter, sync::Arc, thread, time::Duration}; +use std_msgs::msg::String as StringMsg; +// / SimplePublisherNode struct contains node and publisher members. +// / Used to initialize a ROS 2 node and publisher, and publish messages. +struct SimplePublisherNode { + node: Arc, + _publisher: Arc>, +} +/// The `new` function takes a context and returns a Result containing the +/// initialized SimplePublisherNode or an error. It creates a node with the +/// given name and creates a publisher on the "publish_hello" topic. +/// +/// The SimplePublisherNode contains the node and publisher members. +impl SimplePublisherNode { + /// Creates a new SimplePublisherNode by initializing a node and publisher. + /// + /// This function takes a context and returns a Result containing the + /// initialized SimplePublisherNode or an error. It creates a node with the + /// given name and creates a publisher on the "publish_hello" topic. + /// + /// The SimplePublisherNode contains the node and publisher members. + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_publisher").unwrap(); + let _publisher = node + .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .unwrap(); + Ok(Self { node, _publisher }) + } + + /// Publishes a "Hello World" message on the publisher. + /// + /// Creates a StringMsg with "Hello World" as the data, publishes it on + /// the `_publisher`, and returns a Result. This allows regularly publishing + /// a simple message on a loop. + fn publish_data(&self, increment: i32) -> Result { + let msg: StringMsg = StringMsg { + data: format!("Hello World {}", increment), + }; + self._publisher.publish(msg).unwrap(); + Ok(increment + 1_i32) + } +} + +/// The main function initializes a ROS 2 context, node and publisher, +/// spawns a thread to publish messages repeatedly, and spins the node +/// to receive callbacks. +/// +/// It creates a context, initializes a SimplePublisherNode which creates +/// a node and publisher, clones the publisher to pass to the thread, +/// spawns a thread to publish "Hello World" messages repeatedly, and +/// calls spin() on the node to receive callbacks. This allows publishing +/// messages asynchronously while spinning the node. +fn main() -> Result<(), RclrsError> { + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut count: i32 = 0; + thread::spawn(move || -> () { + iter::repeat(()).for_each(|()| { + thread::sleep(Duration::from_millis(1000)); + count = publisher_other_thread.publish_data(count).unwrap(); + }); + }); + rclrs::spin(publisher.node.clone()) +} From a653269c921ad2916ec137eed3cbc04c13bd40f2 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 15:44:27 +0200 Subject: [PATCH 017/113] changed path declaration for simple_publisher, so it will call the right file --- examples/your_package_name/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/your_package_name/Cargo.toml b/examples/your_package_name/Cargo.toml index 38f02a2b7..68e49d1b0 100644 --- a/examples/your_package_name/Cargo.toml +++ b/examples/your_package_name/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] name="simple_publisher" -path="src/main.rs" +path="src/simple_publisher.rs" [[bin]] name="simple_subscriber" path="src/simple_subscriber.rs" From 6bd5c16d31ae4bca1736f039e906a19e8e8c9305 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 15:46:14 +0200 Subject: [PATCH 018/113] Changed RUST to Rust --- ...iting_a_simple_publisher_and_subscriber.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 0bf67e2fe..d00119639 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -1,5 +1,5 @@ -# Writing a simple publisher and subscriber (RUST) -* Goal: Create and run a publisher and subscriber node using RUST. +# Writing a simple publisher and subscriber (Rust) +* Goal: Create and run a publisher and subscriber node using Rust. * Tutorial level: Beginner * Time: 20 minutes
Background @@ -8,14 +8,14 @@ In this tutorial you will create a pair of [nodes](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Nodes/Understanding-ROS2-Nodes.html) that pass information to each other via a [topic](https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Topics/Understanding-ROS2-Topics.html) in the form of string messages. The example used here is a simple "talker" and "listener" system; one node publishes data and the other subscribes to the topic to receive that data. -Since RUST doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). +Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). The code used in these examples can be found [here](https://github.com/Guelakais/ros2_rust/tree/better_tutorial/examples/your_package_name)
Side-note on dependencies You may be wondering why you can't just add all your ROS2-specific dependencies to `Cargo.toml` with `cargo add YOUR_DEPENDENCIES` and have to edit this file manually. Here is why: -Almost none of the ROS2 dependencies you'll need for your ROS2 RUST node development currently exist on [crates.io](https://crates.io/), the main source for RUST depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 RUST dependencies and your ROS2 RUST project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated RUST projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 RUST project this way. +Almost none of the ROS2 dependencies you'll need for your ROS2 Rust node development currently exist on [crates.io](https://crates.io/), the main source for Rust depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 Rust dependencies and your ROS2 Rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated Rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 Rust project this way.
@@ -27,7 +27,7 @@ Basic concepts of development with ROS2 should be known: * [workspaces](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html) * [packages](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html). -A basic understanding of [RUST](https://doc.rust-lang.org/book/) is recommended, but not entirely necessary. +A basic understanding of [Rust](https://doc.rust-lang.org/book/) is recommended, but not entirely necessary. Before developing [ros2-rust](https://github.com/ros2-rust/ros2_rust) nodes, you must follow the [installation instructions](https://github.com/ros2-rust/ros2-rust/blob/main/README.md) for [`rclrs`](https://docs.rs/rclrs/latest/rclrs/). @@ -45,7 +45,7 @@ project as follows: ``` cargo new your_package_name && cd your_package_name ``` -In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into RUST, please read the very good [RUST book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: +In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into Rust, please read the very good [Rust book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: ``` [package] name = "your_package_name" @@ -108,7 +108,7 @@ struct SimplePublisherNode { node: Arc, _publisher: Arc>, } -/// An impl block in RUST defines methods or associated functions for a specific type. +/// An impl block in Rust defines methods or associated functions for a specific type. /// /// The `new` function takes a context and returns a Result containing the /// initialized SimplePublisherNode or an error. It creates a node with the @@ -172,7 +172,7 @@ fn main() -> Result<(),RclrsError> {
Examining the code in detail: -#### The first 3 lines of the RUST code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. +#### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. ``` use std::{sync::Arc,time::Duration,iter,thread}; use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; @@ -285,7 +285,7 @@ fn main() -> Result<(),RclrsError> {
-
Having several ROS2 RUST nodes in one Package +
Having several ROS2 Rust nodes in one Package Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ``` @@ -302,7 +302,7 @@ path="src/main.rs" rclrs = "*" std_msgs = "*" ``` -You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The RUST compiler will be a bit grumpy if you don't. +You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The Rust compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: ``` cd ${MainFolderOfWorkspace} @@ -318,7 +318,7 @@ As you can see, you are now calling your node by the name declared in `[[bin]]`
Write the subscriber node -Of course, you can implement a new ROS2 RUST package for this node. You can find out how to do this in the section called 'Create a package'. +Of course, you can implement a new ROS2 Rust package for this node. You can find out how to do this in the section called 'Create a package'. Or you can add a new binary target to your package. To do so, just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ``` [[bin]] From 26a88f0a82b1be37fb992d607fe9c45eaf87f03d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 16:04:27 +0200 Subject: [PATCH 019/113] Sample package has been removed again. As long as I don't understand how to run colcon with cargo-args correctly, it just doesn't do any good when I start adding code. I need proper instructions on how to run your buildchain locally. Otherwise it just doesn't work. --- examples/your_package_name/Cargo.toml | 15 ---- examples/your_package_name/package.xml | 14 ---- .../your_package_name/src/simple_publisher.rs | 71 ------------------- .../src/simple_subscriber.rs | 65 ----------------- 4 files changed, 165 deletions(-) delete mode 100644 examples/your_package_name/Cargo.toml delete mode 100644 examples/your_package_name/package.xml delete mode 100644 examples/your_package_name/src/simple_publisher.rs delete mode 100644 examples/your_package_name/src/simple_subscriber.rs diff --git a/examples/your_package_name/Cargo.toml b/examples/your_package_name/Cargo.toml deleted file mode 100644 index 68e49d1b0..000000000 --- a/examples/your_package_name/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "your_package_name" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[[bin]] -name="simple_publisher" -path="src/simple_publisher.rs" -[[bin]] -name="simple_subscriber" -path="src/simple_subscriber.rs" -[dependencies] -rclrs = "*" -std_msgs = "*" diff --git a/examples/your_package_name/package.xml b/examples/your_package_name/package.xml deleted file mode 100644 index b7aa0df2f..000000000 --- a/examples/your_package_name/package.xml +++ /dev/null @@ -1,14 +0,0 @@ - - your_project_name - 0.0.0 - TODO: Package description. Seriously. Please make a Package description. We all will thank for it. - user - TODO: License declaration. Licenses are Great. Please add a Licence - - rclrs - std_msgs - - - ament_cargo - - diff --git a/examples/your_package_name/src/simple_publisher.rs b/examples/your_package_name/src/simple_publisher.rs deleted file mode 100644 index ede53ad01..000000000 --- a/examples/your_package_name/src/simple_publisher.rs +++ /dev/null @@ -1,71 +0,0 @@ -use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; -/// Creates a SimplePublisherNode, initializes a node and publisher, and provides -/// methods to publish a simple "Hello World" message on a loop in separate threads. - -/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, -/// and the StringMsg message type from std_msgs for publishing string messages. -use std::{iter, sync::Arc, thread, time::Duration}; -use std_msgs::msg::String as StringMsg; -// / SimplePublisherNode struct contains node and publisher members. -// / Used to initialize a ROS 2 node and publisher, and publish messages. -struct SimplePublisherNode { - node: Arc, - _publisher: Arc>, -} -/// The `new` function takes a context and returns a Result containing the -/// initialized SimplePublisherNode or an error. It creates a node with the -/// given name and creates a publisher on the "publish_hello" topic. -/// -/// The SimplePublisherNode contains the node and publisher members. -impl SimplePublisherNode { - /// Creates a new SimplePublisherNode by initializing a node and publisher. - /// - /// This function takes a context and returns a Result containing the - /// initialized SimplePublisherNode or an error. It creates a node with the - /// given name and creates a publisher on the "publish_hello" topic. - /// - /// The SimplePublisherNode contains the node and publisher members. - fn new(context: &Context) -> Result { - let node = create_node(context, "simple_publisher").unwrap(); - let _publisher = node - .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) - .unwrap(); - Ok(Self { node, _publisher }) - } - - /// Publishes a "Hello World" message on the publisher. - /// - /// Creates a StringMsg with "Hello World" as the data, publishes it on - /// the `_publisher`, and returns a Result. This allows regularly publishing - /// a simple message on a loop. - fn publish_data(&self, increment: i32) -> Result { - let msg: StringMsg = StringMsg { - data: format!("Hello World {}", increment), - }; - self._publisher.publish(msg).unwrap(); - Ok(increment + 1_i32) - } -} - -/// The main function initializes a ROS 2 context, node and publisher, -/// spawns a thread to publish messages repeatedly, and spins the node -/// to receive callbacks. -/// -/// It creates a context, initializes a SimplePublisherNode which creates -/// a node and publisher, clones the publisher to pass to the thread, -/// spawns a thread to publish "Hello World" messages repeatedly, and -/// calls spin() on the node to receive callbacks. This allows publishing -/// messages asynchronously while spinning the node. -fn main() -> Result<(), RclrsError> { - let context = Context::new(std::env::args()).unwrap(); - let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); - let publisher_other_thread = Arc::clone(&publisher); - let mut count: i32 = 0; - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - count = publisher_other_thread.publish_data(count).unwrap(); - }); - }); - rclrs::spin(publisher.node.clone()) -} diff --git a/examples/your_package_name/src/simple_subscriber.rs b/examples/your_package_name/src/simple_subscriber.rs deleted file mode 100644 index f70563e1c..000000000 --- a/examples/your_package_name/src/simple_subscriber.rs +++ /dev/null @@ -1,65 +0,0 @@ -use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; -use std::{ - env, iter, - sync::{Arc, Mutex}, - thread, - time::Duration, -}; -use std_msgs::msg::String as StringMsg; -/// A simple ROS2 subscriber node that receives and prints "hello" messages. -/// -/// This node creates a subscription to the "publish_hello" topic and prints the -/// received messages to the console. It runs the subscription in a separate -/// thread, while the main thread calls `rclrs::spin()` to keep the node running. -pub struct SimpleSubscriptionNode { - node: Arc, - _subscriber: Arc>, - data: Arc>>, -} -/// Implements a simple ROS2 subscriber node that receives and prints "hello" messages. -/// The `new` function creates the node and the subscription, and returns a `SimpleSubscriptionNode` -/// instance. The `data_callback` function can be used to access the latest received message. -impl SimpleSubscriptionNode { - fn new(context: &Context) -> Result { - let node = create_node(context, "simple_subscription").unwrap(); - let data: Arc>> = Arc::new(Mutex::new(None)); - let data_mut: Arc>> = Arc::clone(&data); - let _subscriber = node - .create_subscription::( - "publish_hello", - QOS_PROFILE_DEFAULT, - move |msg: StringMsg| { - *data_mut.lock().unwrap() = Some(msg); - }, - ) - .unwrap(); - Ok(Self { - node, - _subscriber, - data, - }) - } - fn data_callback(&self) -> Result<(), RclrsError> { - if let Some(data) = self.data.lock().unwrap().as_ref() { - println!("{}", data.data); - } else { - println!("No message available yet."); - } - Ok(()) - } -} -/// The `main` function creates a new ROS2 context, a `SimpleSubscriptionNode` instance, and starts a separate thread to periodically call the `data_callback` method on the subscription. The main thread then calls `rclrs::spin()` to keep the node running and receive messages. -/// -/// The separate thread is used to ensure that the `data_callback` method is called regularly, even if the main thread is blocked in `rclrs::spin()`. This allows the subscriber to continuously process and print the received "hello" messages. -fn main() -> Result<(), RclrsError> { - let context = Context::new(env::args()).unwrap(); - let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); - let subscription_other_thread = Arc::clone(&subscription); - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - subscription_other_thread.data_callback().unwrap() - }); - }); - rclrs::spin(subscription.node.clone()) -} From c8f12b71e4505a49fd504fc12b3119581c16135b Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Fri, 12 Apr 2024 16:09:33 +0200 Subject: [PATCH 020/113] Changed the links to a valid path As long as I don't understand how your building process works properly and can reproduce it, I won't add any code for the time being. --- docs/writing_a_simple_publisher_and_subscriber.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d00119639..c54e64e00 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -10,7 +10,7 @@ In this tutorial you will create a pair of Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). -The code used in these examples can be found [here](https://github.com/Guelakais/ros2_rust/tree/better_tutorial/examples/your_package_name) +The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node/-/tree/more_simple_nodes?ref_type=heads)
Side-note on dependencies @@ -92,7 +92,7 @@ Of course, you can use any capable editor or even your file explorer to do this.
Write the publisher node -To construct a node, replace the code in your `main.rs` file with the [following](https://github.com/Guelakais/ros2_rust/blob/better_tutorial/examples/your_package_name/src/main.rs): +To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): ``` /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. @@ -325,7 +325,7 @@ Or you can add a new binary target to your package. To do so, just add a new ` Date: Sat, 13 Apr 2024 10:51:37 +0200 Subject: [PATCH 021/113] added language declaration to the first `Cargo.toml` Codesnippet Added language declaration to the first upcoming `Cargo.toml` code snippet in lines 49 - 60 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index c54e64e00..ec06414fa 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -46,7 +46,7 @@ project as follows: cargo new your_package_name && cd your_package_name ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into Rust, please read the very good [Rust book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: -``` +```toml [package] name = "your_package_name" version = "0.1.0" From f8b8efd93d3d6f6334f47ae8fa4caa8dac6ff791 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 10:54:30 +0200 Subject: [PATCH 022/113] added language declaration to the first `tree .` Codesnippet Added language declaration to the possible output of the `tree .` command in lines 81 - 88 to allow markdown colouring. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index ec06414fa..16aa26bf7 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -78,7 +78,7 @@ Additionally, create a new `package.xml` if you want your node to be buildable w ``` By taking a look at your package, for example by typing [`tree .`](https://www.geeksforgeeks.org/tree-command-unixlinux/) inside your package, and you'll see a structure similar to the following: -``` +```shell ├── Cargo.toml ├── package.xml └── src From af561320b621e41ec503426cd320a60843c8f345 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 10:56:12 +0200 Subject: [PATCH 023/113] added language declaration to the first `rust` Codesnippet Added language declaration to the first upcoming `rust` code snippet in lines 96 - 171 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 16aa26bf7..c0b860e49 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -93,7 +93,7 @@ Of course, you can use any capable editor or even your file explorer to do this.
Write the publisher node To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): -``` +```rust /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. From ad88b9b1c81ed0f7d79fec14d6f454a7bf4b8434 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 10:57:09 +0200 Subject: [PATCH 024/113] added language declaration to the second `rust` Codesnippet Added language declaration to the second upcoming `rust` code snippet in lines 176 - 180 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index c0b860e49..328b30747 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -173,7 +173,7 @@ fn main() -> Result<(),RclrsError> {
Examining the code in detail: #### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. -``` +```rust use std::{sync::Arc,time::Duration,iter,thread}; use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; use std_msgs::msg::String as StringMsg; From 6a916015b2c4a98cd231a7dd1ec85be680441741 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:00:43 +0200 Subject: [PATCH 025/113] Fixed declarations I have inserted a few characters so that code parts are also recognisable as such. --- ...riting_a_simple_publisher_and_subscriber.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 328b30747..ea593cc98 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -178,16 +178,16 @@ use std::{sync::Arc,time::Duration,iter,thread}; use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; use std_msgs::msg::String as StringMsg; ``` -* use std::{sync::Arc, time::Duration, iter, thread};: Imports specific features from the standard library: - - Arc is for thread-safe shared ownership of data. - - Duration represents a time span. - - iter provides tools for working with iterators. - thread enables creating and managing threads. -* use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};: +* `use std::{sync::Arc, time::Duration, iter, thread};`: Imports specific features from the standard library: + - `Arc` is for thread-safe shared ownership of data. + - `Duration` represents a time span. + - `iter` provides tools for working with iterators. - thread enables creating and managing threads. +* `use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};`: - Imports elements for ROS 2 communication: - - RclrsError for handling errors. - - QOS_PROFILE_DEFAULT for default Quality of Service settings. - - Context, create_node, Node, Publisher are for ROS 2 node creation and publishing. publishing. -* use std_msgs::msg::String as StringMsg;: Imports the StringMsg type for publishing string messages. + - `RclrsError` for handling errors. + - `QOS_PROFILE_DEFAULT` for default Quality of Service settings. + - `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing. publishing. +* `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages. #### Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. ``` From 93e6d1a3ded211a7e907f526ca29777dba993ba5 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:01:43 +0200 Subject: [PATCH 026/113] added language declaration to the third `rust` Codesnippet Added language declaration to the third upcoming `rust` code snippet in lines 193 - 198 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index ea593cc98..d372330b8 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -190,7 +190,7 @@ use std_msgs::msg::String as StringMsg; * `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages. #### Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. -``` +```rust struct SimplePublisherNode { node: Arc, _publisher: Arc>, From e43e0f7ab58afe7d146cf13db1cd517111cbeb67 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:06:26 +0200 Subject: [PATCH 027/113] Fixed declarations I have inserted a few characters so that code parts are also recognisable as such. Lines 199 - 205 --- docs/writing_a_simple_publisher_and_subscriber.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d372330b8..5ff3f9583 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -197,12 +197,12 @@ struct SimplePublisherNode { } ``` 1. Structure: -struct SimplePublisherNode: This line defines a new struct named SimplePublisherNode. It serves as a blueprint for creating objects that hold information related to a simple publisher node in ROS 2. +`struct SimplePublisherNode`: This line defines a new [`struct`](https://doc.rust-lang.org/rust-by-example/custom_types/structs.html) named `SimplePublisherNode`. It serves as a blueprint for creating objects that hold information related to a simple publisher node in ROS 2. 2. Members: -* node: Arc: This member stores a reference to a ROS 2 node, wrapped in an Arc (Atomic Reference Counted) smart pointer. This allows for safe sharing of the node reference across multiple threads. -* _publisher: Arc>: This member stores a reference to a publisher specifically for string messages (StringMsg), also wrapped in an Arc for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. -3. This code defines methods for the SimplePublisherNode struct. The new method creates a ROS 2 node and publisher, storing them in the struct. The publish_data method publishes a string message with a counter and returns the incremented counter. +* `node: Arc`: This member stores a reference to a ROS 2 node, wrapped in an [`Arc` (Atomic Reference Counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) smart pointer. This allows for safe sharing of the node reference across multiple threads. +* `_publisher: Arc>`: This member stores a reference to a publisher specifically for string messages (`StringMsg`), also wrapped in an `Arc` for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. +#### This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. ``` impl SimplePublisherNode { fn new(context: &Context) -> Result { From 83c5167281894096d3c9a5e1c675f39e666abc83 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:07:28 +0200 Subject: [PATCH 028/113] added language declaration to the thourth `rust` Codesnippet Added language declaration to the thourth upcoming `rust` code snippet in lines 206 - 224 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 5ff3f9583..305e04eb4 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -203,7 +203,7 @@ struct SimplePublisherNode { * `node: Arc`: This member stores a reference to a ROS 2 node, wrapped in an [`Arc` (Atomic Reference Counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) smart pointer. This allows for safe sharing of the node reference across multiple threads. * `_publisher: Arc>`: This member stores a reference to a publisher specifically for string messages (`StringMsg`), also wrapped in an `Arc` for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. #### This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. -``` +```rust impl SimplePublisherNode { fn new(context: &Context) -> Result { let node = create_node(context, "simple_publisher").unwrap(); From 7e99b5dafc5e83a85bfea95e11b273519dd4c21a Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:13:25 +0200 Subject: [PATCH 029/113] Fixed declarations I have inserted a few characters so that code parts are also recognisable as such. Lines 226 - 243 --- .../writing_a_simple_publisher_and_subscriber.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 305e04eb4..417aeadc1 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -228,19 +228,19 @@ impl SimplePublisherNode { 2. Constructor Method: * `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of SimplePublisherNode. * It takes a Context object as input, which is necessary for interacting with the ROS 2 syste. - * It returns a Result type, indicating either a successful Self (the created SimplePublisherNode object) or an RclrsError if something goes wrong. + * It returns a Result type, indicating either a successful Self (the created `SimplePublisherNode` object) or an `RclrsError` if something goes wrong. * Inside the new method: - * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named "simple_publisher" within the given context. The unwrap() unwraps the result, handling any errors immediately by forcing the program to abort (`panic`) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. - * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic "publish_hello" with default quality of service settings. - * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created SimplePublisherNode object, containing the node and publisher references. + * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort (`panic`) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. + * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic `"publish_hello"` with default quality of service settings. + * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created `SimplePublisherNode` object, containing the node and publisher references. 3. Publishing Method: -* `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a counter. +* `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a `counter`. * It takes an inkrement value (an integer) as input, which is used for counting purposes within the message content. * It also returns a Result type, indicating either the incremented value or an RclrsError if publishing fails. * Inside the publish_data method: - * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), };`: Creates a string message with the content "Hello World" followed by the inkrement value. - * self._publisher.publish(msg).unwrap();: Publishes the created message onto the topic associated with the publisher. - * Ok(increment + 1_i32): Returns a Result with the incremented increment value. + * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), };`: Creates a string message with the content `"Hello World"` followed by the increment value. + * `self._publisher.publish(msg).unwrap();`: Publishes the created message onto the topic associated with the publisher. + * `Ok(increment + 1_i32)`: Returns a Result with the incremented increment value. #### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. From 9bb8d685afd1b277c97840c8dbdabc5cf394e707 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:15:22 +0200 Subject: [PATCH 030/113] added language declaration to the fifth `rust` Codesnippet Added language declaration to the fifth upcoming `rust` code snippet in lines 247 - 261 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 417aeadc1..f26a3612f 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -244,7 +244,7 @@ impl SimplePublisherNode { #### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. -``` +```rust fn main() -> Result<(),RclrsError> { let context = Context::new(std::env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); From 4b1823ae332047db256b06c18ae69ea5e1491e7a Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:22:42 +0200 Subject: [PATCH 031/113] Fixed declarations I have inserted a few characters so that code parts are also recognisable as such. Lines 245 - 277 --- ...iting_a_simple_publisher_and_subscriber.md | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f26a3612f..49209e1d3 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -242,8 +242,7 @@ impl SimplePublisherNode { * `self._publisher.publish(msg).unwrap();`: Publishes the created message onto the topic associated with the publisher. * `Ok(increment + 1_i32)`: Returns a Result with the incremented increment value. -#### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. - +#### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. ```rust fn main() -> Result<(),RclrsError> { let context = Context::new(std::env::args()).unwrap(); @@ -261,27 +260,21 @@ fn main() -> Result<(),RclrsError> { ``` 1. Main Function: -* `fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a Result type, indicating either successful execution or an RclrsError. - -2. Context and Node Setup: - -* `let context = Context::new(std::env::args()).unwrap();`: Creates a ROS 2 context using command-line arguments. -* `let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());`: - * Creates an [Arc (atomic reference counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) pointer to a `SimplePublisherNode` object. - * Calls the new method on SimplePublisherNode to construct the node and publisher within the context. - -3. Thread and Iterator: -* `let publisher_other_thread = Arc::clone(&publisher);`: Clones the shared publisher pointer for use in a separate thread. -* `let mut iterator: i32 = 0;`: Initializes a counter variable for message content. -* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a closure: `iter::repeat(()).for_each(|()| { ... });`: Creates an infinite loop using `iter::repeat`. - -4. Publishing Loop within Thread: - -* `thread::sleep(Duration::from_millis(1000));`: Pauses the thread for 1 second (1 Hz publishing rate). -* `iterator = publisher_other_thread.publish_data(iterator).unwrap();`: Calls the publish_data method on the publisher_other_thread to publish a message with the current counter value. Increments the iterator for the next message. - -5. Main Thread Spin: -* `rclrs::spin(publisher.node.clone());`: Keeps the main thread running, processing ROS 2 events and messages. Uses a cloned reference to the node to ensure it remains active even with other threads. +* `fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a `Result` type, indicating either successful execution or an `RclrsError`. +2. Context and Node Setup: +* `let context = Context::new(std::env::args()).unwrap();`: Creates a ROS 2 context using command-line arguments. +* `let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());`: + * Creates an [Arc (atomic reference counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) pointer to a `SimplePublisherNode` object. + * Calls the new method on `SimplePublisherNode` to construct the node and publisher within the context. +3. Thread and Iterator: +* `let publisher_other_thread = Arc::clone(&publisher);`: Clones the shared publisher pointer for use in a separate thread. +* `let mut iterator: i32 = 0;`: Initializes a counter variable for message content. +* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html): `iter::repeat(()).for_each(|()| { ... });`: Creates an infinite loop using `iter::repeat`. +4. Publishing Loop within Thread: +* `thread::sleep(Duration::from_millis(1000));`: Pauses the thread for 1 second (1 Hz publishing rate). +* `iterator = publisher_other_thread.publish_data(count).unwrap();`: Calls the `publish_data` method on the `publisher_other_thread` to publish a message with the current counter value. Increments the iterator for the next message. +5. Main Thread Spin: +* `rclrs::spin(publisher.node.clone());`: Keeps the main thread running, processing ROS 2 events and messages. Uses a cloned reference to the node to ensure it remains active even with other threads.
From 16b810d25ae948ddc10074ca1c0e4ba26113bfb5 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:29:53 +0200 Subject: [PATCH 032/113] added language declaration to the second `toml` Codesnippet Added language declaration to the second upcoming `toml` code snippet in lines 284 - 289 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 49209e1d3..e0dc6d5f5 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -281,7 +281,7 @@ fn main() -> Result<(),RclrsError> {
Having several ROS2 Rust nodes in one Package Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: -``` +```toml [package] name = "your_package_name" version = "0.1.0" From f754af0e52e275386a7b6a6f201c7d36fa720dd9 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:30:19 +0200 Subject: [PATCH 033/113] Added missing code snippet end at line 289 --- docs/writing_a_simple_publisher_and_subscriber.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index e0dc6d5f5..721147041 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -286,6 +286,7 @@ Of course, you can write for each node you want to implement its own package, an name = "your_package_name" version = "0.1.0" edition = "2021" +``` # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] From 92939e0d0fe7b6a4b4a155b42c9566e7d0d790ae Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:31:36 +0200 Subject: [PATCH 034/113] Removed wrong set code snippet end at line 289 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 721147041..3ae2955c2 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -286,9 +286,9 @@ Of course, you can write for each node you want to implement its own package, an name = "your_package_name" version = "0.1.0" edition = "2021" -``` # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + [[bin]] name="simple_publisher" path="src/main.rs" From afac56aab1e6af575079ace6bc60ce94305a3f3c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:32:34 +0200 Subject: [PATCH 035/113] changed file declaration: Line 294 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 3ae2955c2..adc31b167 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -291,7 +291,7 @@ edition = "2021" [[bin]] name="simple_publisher" -path="src/main.rs" +path="src/simple_publisher.rs" [dependencies] rclrs = "*" std_msgs = "*" From 011e9114441143787fe34714591e8970ee9e1834 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:32:53 +0200 Subject: [PATCH 036/113] Added line at 299 --- docs/writing_a_simple_publisher_and_subscriber.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index adc31b167..cc2e05ffe 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -296,6 +296,7 @@ path="src/simple_publisher.rs" rclrs = "*" std_msgs = "*" ``` + You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The Rust compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: ``` From 1305b67f1abf3e073d949293b615911db158cc92 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:34:18 +0200 Subject: [PATCH 037/113] Added language declaration for `shell` script for lines 302 - 306 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index cc2e05ffe..f0e98568e 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -299,7 +299,7 @@ std_msgs = "*" You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The Rust compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: -``` +```shell cd ${MainFolderOfWorkspace} colcon build source install/setub.bash From 38fe16a473e5a269b882a998defc95e6668fdcd7 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:35:00 +0200 Subject: [PATCH 038/113] Added language declaration for `shell` script for lines 308 - 310 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f0e98568e..dfc9f4b38 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -305,7 +305,7 @@ colcon build source install/setub.bash ``` Running the node will look like this: -``` +```shell ros2 run your_package_name simple_publisher ``` As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. From cedba149391b3d28ccf31fefc410176ea86b7e77 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:36:19 +0200 Subject: [PATCH 039/113] Changed shell specific variable declaration with Variable name --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index dfc9f4b38..487305258 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -300,7 +300,7 @@ std_msgs = "*" You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The Rust compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: ```shell -cd ${MainFolderOfWorkspace} +cd WORKSPACE colcon build source install/setub.bash ``` From e5a65a26f70f97b38040db9eaf2ae575cc785388 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:37:29 +0200 Subject: [PATCH 040/113] Changed filename in Diamond clamps through Variable name --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 487305258..242c3ffdc 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -314,7 +314,7 @@ As you can see, you are now calling your node by the name declared in `[[bin]]`
Write the subscriber node Of course, you can implement a new ROS2 Rust package for this node. You can find out how to do this in the section called 'Create a package'. -Or you can add a new binary target to your package. To do so, just add a new `.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: +Or you can add a new binary target to your package. To do so, just add a new `FILE.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ``` [[bin]] name="simple_subscriber" From 88b94211e75534e670171c71e98410aac90a643e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:38:25 +0200 Subject: [PATCH 041/113] added language declaration to the third `toml` Codesnippet Added language declaration to the third upcoming `toml` code snippet in lines 3128 - 322 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 242c3ffdc..a10166d13 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -315,7 +315,7 @@ As you can see, you are now calling your node by the name declared in `[[bin]]` Of course, you can implement a new ROS2 Rust package for this node. You can find out how to do this in the section called 'Create a package'. Or you can add a new binary target to your package. To do so, just add a new `FILE.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: -``` +```toml [[bin]] name="simple_subscriber" path="src/simple_subscriber.rs" From 18a2cac3633c7879de51dbf926f3987f03a71277 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:39:24 +0200 Subject: [PATCH 042/113] Set file.rs up --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index a10166d13..098b0f3f4 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -320,7 +320,7 @@ Or you can add a new binary target to your package. To do so, just add a new `FI name="simple_subscriber" path="src/simple_subscriber.rs" ``` -To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a file.rs - in my case its the `src/simple_subscriber.rs`: +To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a `FILE.rs` - in my case its the `src/simple_subscriber.rs`: ``` use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ From 12f29392c643ab9712e88c4d7ac8276b022a2d90 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:41:11 +0200 Subject: [PATCH 043/113] added language declaration to the sixth `rust` Codesnippet Added language declaration to the sixth upcoming `rust` code snippet in lines 324 - 395 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 098b0f3f4..b9ab7225c 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -321,7 +321,7 @@ name="simple_subscriber" path="src/simple_subscriber.rs" ``` To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a `FILE.rs` - in my case its the `src/simple_subscriber.rs`: -``` +```rust use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ env, From bc2ee4d68dee0320cf146c02c674062b814f922e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:42:11 +0200 Subject: [PATCH 044/113] added language declaration to the seventh `rust` Codesnippet Added language declaration to the seventh upcoming `rust` code snippet in lines 399 - 405 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b9ab7225c..4c83c0873 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -396,7 +396,7 @@ fn main() -> Result<(), RclrsError> {
Examining the code in detail: #### The main Construct: -``` +```rust pub struct SimpleSubscriptionNode { node: Arc, _subscriber: Arc>, From f39263c310578eadf2c71d5885ce6b1c8d09d5cd Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:42:52 +0200 Subject: [PATCH 045/113] Added markdown nextline operator at end of line --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 4c83c0873..b5111af17 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -403,7 +403,7 @@ pub struct SimpleSubscriptionNode { data: Arc>>, } ``` -Instead of a Publisher, there is a Subscription object in the Subscriber node. The data needs to be an `Arc>>` because there can be errors in the data transfer process and this can be caught by including the value of the incoming subscription in an optional. +Instead of a Publisher, there is a Subscription object in the Subscriber node. The data needs to be an `Arc>>` because there can be errors in the data transfer process and this can be caught by including the value of the incoming subscription in an optional. #### This code defines a function named new that likely creates an instance of some SimpleSubscriptionNode. ``` fn new(context: &Context) -> Result { From 50dcc4eb086cf0fd75917a481b67c26ef8cccb63 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:43:48 +0200 Subject: [PATCH 046/113] added language declaration to the eigth `rust` Codesnippet Added language declaration to the eigth upcoming `rust` code snippet in lines 408 - 429 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b5111af17..4967169f7 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -405,7 +405,7 @@ pub struct SimpleSubscriptionNode { ``` Instead of a Publisher, there is a Subscription object in the Subscriber node. The data needs to be an `Arc>>` because there can be errors in the data transfer process and this can be caught by including the value of the incoming subscription in an optional. #### This code defines a function named new that likely creates an instance of some SimpleSubscriptionNode. -``` +```rust fn new(context: &Context) -> Result { let node = create_node(context, "simple_subscription").unwrap(); let data: Arc>> = Arc::new(Mutex::new(None)); From c4560840da2cf222e4841768244ba0ef140f1fdb Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:44:15 +0200 Subject: [PATCH 047/113] Added markdown nextline operator at end of line --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 4967169f7..d87620a2a 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -430,7 +430,7 @@ Instead of a Publisher, there is a Subscription object in the Subscriber node. T A few special features: 1. Initializing Shared Data: * `let data: Arc>> = Arc::new(Mutex::new(None));` - This line creates a shared data structure that will hold the received message. + This line creates a shared data structure that will hold the received message. * `Arc>>`: This is a complex type combining several functionalities: * `Arc`: An atomically reference-counted pointer (Arc) allows multiple parts of the code to safely access the same data (T). * `Mutex`: A mutual exclusion lock (`Mutex`) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. From 44702fd7c34bab3b291bacd9ed885c005ba69cd2 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:44:25 +0200 Subject: [PATCH 048/113] Added markdown nextline operator at end of line --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d87620a2a..6546d33fb 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -429,7 +429,7 @@ Instead of a Publisher, there is a Subscription object in the Subscriber node. T ``` A few special features: 1. Initializing Shared Data: - * `let data: Arc>> = Arc::new(Mutex::new(None));` + * `let data: Arc>> = Arc::new(Mutex::new(None));` This line creates a shared data structure that will hold the received message. * `Arc>>`: This is a complex type combining several functionalities: * `Arc`: An atomically reference-counted pointer (Arc) allows multiple parts of the code to safely access the same data (T). From bbecd82f1c7192c74dfa30a6e34b04b73e2a5ae9 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:44:35 +0200 Subject: [PATCH 049/113] Added markdown nextline operator at end of line --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 6546d33fb..215a99d52 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -427,8 +427,8 @@ Instead of a Publisher, there is a Subscription object in the Subscriber node. T } ``` -A few special features: -1. Initializing Shared Data: +A few special features: +1. Initializing Shared Data: * `let data: Arc>> = Arc::new(Mutex::new(None));` This line creates a shared data structure that will hold the received message. * `Arc>>`: This is a complex type combining several functionalities: From 987bbe43d68e52495ddea8e02e92aebddcabe182 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:47:03 +0200 Subject: [PATCH 050/113] Added some links for more clarity --- docs/writing_a_simple_publisher_and_subscriber.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 215a99d52..94c5a574c 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -432,11 +432,11 @@ A few special features: * `let data: Arc>> = Arc::new(Mutex::new(None));` This line creates a shared data structure that will hold the received message. * `Arc>>`: This is a complex type combining several functionalities: - * `Arc`: An atomically reference-counted pointer (Arc) allows multiple parts of the code to safely access the same data (T). - * `Mutex`: A mutual exclusion lock (`Mutex`) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. + * `Arc`: An atomically reference-counted pointer ([`Arc`](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://doc.rust-lang.org/std/sync/struct.Arc.html&ved=2ahUKEwiJz_n3876FAxXZg_0HHc-yDZ8QFnoECAYQAQ&usg=AOvVaw2ZAPxD2olFejU3a_Ngb4f5)) allows multiple parts of the code to safely access the same data (`T`). + * `Mutex`: A mutual exclusion lock ([`Mutex`](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://doc.rust-lang.org/std/sync/struct.Mutex.html&ved=2ahUKEwjNx8uP9L6FAxVrhP0HHY4DB3YQFnoECAcQAQ&usg=AOvVaw3gOprM5PxBUUZd_3W6wFaG)) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. * `Option`: This represents an optional value that can either hold a message of type `StringMsg` or be `None` if no message has been received yet. * `Arc::new(Mutex::new(None))`: This creates a new instance of `Arc>>` and initializes the inner `Mutex` with `None`. -2. Creating a Subscription: +2. Creating a Subscription: * `let _subscriber = node.create_subscription::(...` This line attempts to create a subscription using the created ROS node (`node`). * `create_subscription`: This is creates a subscription to a specific topic. From 7cbdce0a0c9c19bdc70b3daa4d8f02f485c444e4 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:52:33 +0200 Subject: [PATCH 051/113] Fixed some wrong declarations in the lines 430 - 448 --- .../writing_a_simple_publisher_and_subscriber.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 94c5a574c..c5b9f8217 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -437,16 +437,16 @@ A few special features: * `Option`: This represents an optional value that can either hold a message of type `StringMsg` or be `None` if no message has been received yet. * `Arc::new(Mutex::new(None))`: This creates a new instance of `Arc>>` and initializes the inner `Mutex` with `None`. 2. Creating a Subscription: - * `let _subscriber = node.create_subscription::(...` - This line attempts to create a subscription using the created ROS node (`node`). - * `create_subscription`: This is creates a subscription to a specific topic. - * ``: This specifies the type of message the subscription is interested in (`StringMsg`) and a placeholder (`_`) for the callback closure type. - `"publish_hello"`: This is the name of the ROS topic this node wants to subscribe to. Messages of type StringMsg are expected on this topic. - * `move |msg: StringMsg| { ... }`: This is a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html) (anonymous function) that will be called whenever a new message arrives on the subscribed topic. + * `let _subscriber = node.create_subscription::(...` + This line attempts to create a subscription using the created ROS node (`node`). + * `create_subscription`: This is creates a subscription to a specific topic. + * ``: This specifies the type of message the subscription is interested in (`StringMsg`) and a placeholder (`_`) for the callback [`closure`](https://doc.rust-lang.org/book/ch13-01-closures.html) type. + `"publish_hello"`: This is the name of the ROS topic this node wants to subscribe to. Messages of type `StringMsg` are expected on this topic. + * `move |msg: StringMsg| { ... }`: This is a closure ([anonymous function](https://en.wikipedia.org/wiki/Anonymous_function)) that will be called whenever a new message arrives on the subscribed topic. * `msg: StringMsg`: This parameter receives the received message of type `StringMsg`. The closure body (`{...}`) uses the `Mutex` to access and update the shared data (`data_mut`) with the received message. 3. Cloning the Shared Data: - * `let data_mut: Arc>> = Arc::clone(&data)`; This line creates another `Arc` reference (`data_mut`) pointing to the same underlying data structure as data. This allows the closure to access and modify the shared data. -#### this function provides a way to access and potentially use the received message data stored within the `Arc>>` member variable of the struct. It checks if a message exists, prints it if available, or informs the user there's no message yet. + * `let data_mut: Arc>> = Arc::clone(&data)`; This line creates another `Arc` reference (`data_mut`) pointing to the same underlying data structure as data. This allows the closure to access and modify the shared data. +#### this function provides a way to access and potentially use the received message data stored within the `Arc>>` member variable of the `struct`. It checks if a message exists, prints it if available, or informs the user there's no message yet. ``` fn data_callback(&self) -> Result<(), RclrsError> { if let Some(data) = self.data.lock().unwrap().as_ref() { From 0106568cbca9a8f0005b4e1092b025fccb68d401 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:53:37 +0200 Subject: [PATCH 052/113] added language declaration to the ninth `rust` Codesnippet Added language declaration to the ninth upcoming `rust` code snippet in lines 450 - 460 to allow markdown colouring of this snippet. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index c5b9f8217..9a779dafe 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -447,7 +447,7 @@ A few special features: 3. Cloning the Shared Data: * `let data_mut: Arc>> = Arc::clone(&data)`; This line creates another `Arc` reference (`data_mut`) pointing to the same underlying data structure as data. This allows the closure to access and modify the shared data. #### this function provides a way to access and potentially use the received message data stored within the `Arc>>` member variable of the `struct`. It checks if a message exists, prints it if available, or informs the user there's no message yet. -``` +```rust fn data_callback(&self) -> Result<(), RclrsError> { if let Some(data) = self.data.lock().unwrap().as_ref() { println!("{}", data.data); From 23330561e00c9bfa31fe96923b9553ab2930c04d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:56:57 +0200 Subject: [PATCH 053/113] Fixed some wrong declarations in the lines 461 - 467 --- .../writing_a_simple_publisher_and_subscriber.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 9a779dafe..f08cd6783 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -458,15 +458,13 @@ fn data_callback(&self) -> Result<(), RclrsError> { } ``` -A few special features: - - -1. Checking for Received Message: - * `if let Some(data) = self.data.lock().unwrap().as_ref() { ... }`: This is an if-let statement used for pattern matching on optional values. - * `self.data`: This accesses the member variable data of the struct (likely the `Arc>>` created earlier). - * `.lock().unwrap()`: This calls the lock method on the `Mutex` to gain exclusive access to the shared data. If another thread already holds the lock, lock might block until the lock is released. - `.as_ref()`: This converts the borrowed `MutexGuard` (returned by `.lock()`) into a reference to the inner value (`Option`). - * `Some(data)`: This pattern attempts to match the value inside the Option with `Some(data)`. If there's a message (`Some(data)`), the code block after the if is executed, and data is bound to the actual message content of type `StringMsg`. +A few special features: +1. Checking for Received Message: + * `if let Some(data) = self.data.lock().unwrap().as_ref() { ... }`: This is an [`if-let`](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html) statement used for pattern matching on optional values. + * `self.data`: This accesses the member variable data of the `struct` (likely the `Arc>>` created earlier). + * `.lock().unwrap()`: This calls the lock method on the `Mutex` to gain exclusive access to the shared data. If another thread already holds the lock, lock might block until the lock is released. + `.as_ref()`: This converts the borrowed `MutexGuard` (returned by `.lock()`) into a reference to the inner value (`Option`). + * `Some(data)`: This pattern attempts to match the value inside the Option with `Some(data)`. If there's a message (`Some(data)`), the code block after the if is executed, and data is bound to the actual message content of type `StringMsg`.
From 8d8c9911832b9e8789b8efd0fd42cb10d1831774 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:58:20 +0200 Subject: [PATCH 054/113] Changed shell specific variable call through Variable name - Line 475 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f08cd6783..d84f49c38 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -472,7 +472,7 @@ A few special features: Once you have implemented the code, you are ready to make it runnable: ``` -cd ${MainFolderOfWorkspace} +cd WORKSPACE colcon build ``` Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ROS2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: From 99ab0c3e7e9456fa374c56ddb129b8a3a6835a9f Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 11:59:15 +0200 Subject: [PATCH 055/113] Added language declaration to line 474 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d84f49c38..50a40dec7 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -471,7 +471,7 @@ A few special features:
Build and Run Once you have implemented the code, you are ready to make it runnable: -``` +```sh cd WORKSPACE colcon build ``` From 30917246858e32873fc78d0b46510abffa2cf497 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 12:00:02 +0200 Subject: [PATCH 056/113] Added language declaration to line 479 --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 50a40dec7..07a6fc294 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -476,8 +476,8 @@ cd WORKSPACE colcon build ``` Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ROS2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: -``` -cd ${MainFolderOfWorkspace} +```sh +cd WORKSPACE source install/setup.bash ros2 run your_package_name your_node_name ``` From b2532f71ffc4e0f2fe429de0d30109bb27bc1edf Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 12:00:49 +0200 Subject: [PATCH 057/113] Added Markdown newline operator. Line 484 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 07a6fc294..8966e5cb9 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -481,7 +481,7 @@ cd WORKSPACE source install/setup.bash ros2 run your_package_name your_node_name ``` -In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. +In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: ``` Hello World 230 From 71cb002ecc22541d8119c99f74f4bb86cebdd53e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 12:01:24 +0200 Subject: [PATCH 058/113] Added language declaration to line 486 --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 8966e5cb9..45339c048 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -483,7 +483,7 @@ ros2 run your_package_name your_node_name ``` In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: -``` +```sh Hello World 230 Hello World 231 Hello World 232 From a06c3f184cd72d882cac0bc447fa303460f2b9ab Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 13 Apr 2024 12:02:32 +0200 Subject: [PATCH 059/113] Fixed some wrong declarations in the lines 505 - 506 --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 45339c048..a44220563 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -502,8 +502,8 @@ Hello World 244 Hello World 245 Hello World 246 ``` -My nodes have been running for some time. -Enter `Ctrl+C` in each terminal to stop the nodes from spinning. +(My nodes have been running for some time.) +Enter `Ctrl+c` in each terminal to stop the nodes from spinning.
From 479ae7ddc69d9f2031337b3b4deae4cf6d6b6bb1 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:38:34 +0200 Subject: [PATCH 060/113] Added new Changes to the documentation --- docs/writing_a_simple_publisher_and_subscriber.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index a44220563..f78545c9a 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -30,7 +30,7 @@ Basic concepts of development with ROS2 should be known: A basic understanding of [Rust](https://doc.rust-lang.org/book/) is recommended, but not entirely necessary. Before developing [ros2-rust](https://github.com/ros2-rust/ros2_rust) nodes, you must follow the [installation instructions](https://github.com/ros2-rust/ros2-rust/blob/main/README.md) for [`rclrs`](https://docs.rs/rclrs/latest/rclrs/). - +For a full Introduction into Rust, please read the very good [Rust book](https://doc.rust-lang.org/book/title-page.html).
@@ -45,7 +45,7 @@ project as follows: ``` cargo new your_package_name && cd your_package_name ``` -In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. For a full Introduction into Rust, please read the very good [Rust book](https://doc.rust-lang.org/book/title-page.html). Your `Cargo.toml` could now look like this: +In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: ```toml [package] name = "your_package_name" @@ -181,7 +181,8 @@ use std_msgs::msg::String as StringMsg; * `use std::{sync::Arc, time::Duration, iter, thread};`: Imports specific features from the standard library: - `Arc` is for thread-safe shared ownership of data. - `Duration` represents a time span. - - `iter` provides tools for working with iterators. - thread enables creating and managing threads. + - `iter` provides tools for working with iterators. + - `thread` enables creating and managing threads. * `use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};`: - Imports elements for ROS 2 communication: - `RclrsError` for handling errors. From 178d977d095e392c8ff2fd739ec90b8cc8fd89ed Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:40:04 +0200 Subject: [PATCH 061/113] Removed unnecessary Justifications --- docs/writing_a_simple_publisher_and_subscriber.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f78545c9a..789924ef8 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -83,10 +83,7 @@ By taking a look at your package, for example by typing [`tree .`](https://www.g ├── package.xml └── src └── main.rs - -2 directories, 3 files ``` -Of course, you can use any capable editor or even your file explorer to do this.
From 80799cd04f50041d93a497bb7293f9bfc0edd1d3 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:44:27 +0200 Subject: [PATCH 062/113] added some tweaks to the code --- ...iting_a_simple_publisher_and_subscriber.md | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 789924ef8..d30f4d98f 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -91,21 +91,21 @@ By taking a look at your package, for example by typing [`tree .`](https://www.g To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): ```rust +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. /// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, /// and the StringMsg message type from std_msgs for publishing string messages. -use std::{sync::Arc,time::Duration,iter,thread}; -use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use std::{env, sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; // / SimplePublisherNode struct contains node and publisher members. // / Used to initialize a ROS 2 node and publisher, and publish messages. struct SimplePublisherNode { node: Arc, - _publisher: Arc>, + publisher: Arc>, } -/// An impl block in Rust defines methods or associated functions for a specific type. +/// Creates a new SimplePublisherNode by initializing a node and publisher. /// /// The `new` function takes a context and returns a Result containing the /// initialized SimplePublisherNode or an error. It creates a node with the @@ -120,12 +120,12 @@ impl SimplePublisherNode { /// given name and creates a publisher on the "publish_hello" topic. /// /// The SimplePublisherNode contains the node and publisher members. - fn new(context: &Context) -> Result { + fn new(context: &Context) -> Result { let node = create_node(context, "simple_publisher").unwrap(); - let _publisher = node + let publisher = node .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) .unwrap(); - Ok(Self { node, _publisher, }) + Ok(Self { node, publisher }) } /// Publishes a "Hello World" message on the publisher. @@ -133,39 +133,35 @@ impl SimplePublisherNode { /// Creates a StringMsg with "Hello World" as the data, publishes it on /// the `_publisher`, and returns a Result. This allows regularly publishing /// a simple message on a loop. - fn publish_data(&self,increment:i32) -> Result { - + fn publish_data(&self, increment: i32) -> Result { let msg: StringMsg = StringMsg { - data: format!("Hello World {}",increment), + data: format!("Hello World {}", increment), }; - self._publisher.publish(msg).unwrap(); - Ok(increment+1_i32) + self.publisher.publish(msg).unwrap(); + Ok(increment + 1_i32) } } /// The main function initializes a ROS 2 context, node and publisher, /// spawns a thread to publish messages repeatedly, and spins the node /// to receive callbacks. -/// +/// /// It creates a context, initializes a SimplePublisherNode which creates /// a node and publisher, clones the publisher to pass to the thread, /// spawns a thread to publish "Hello World" messages repeatedly, and /// calls spin() on the node to receive callbacks. This allows publishing /// messages asynchronously while spinning the node. -fn main() -> Result<(),RclrsError> { - let context = Context::new(std::env::args()).unwrap(); +fn main() -> Result<(), RclrsError> { + let context = Context::new(env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); let publisher_other_thread = Arc::clone(&publisher); - let mut count: i32=0; - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - count=publisher_other_thread.publish_data(count).unwrap(); - }); + let mut count: i32 = 0; + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(1000)); + count = publisher_other_thread.publish_data(count).unwrap(); }); rclrs::spin(publisher.node.clone()) -} -``` +}```
Examining the code in detail: From 02e48edaa43cb74ca737f55fa1948ae0431ed97c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:46:39 +0200 Subject: [PATCH 063/113] Fixed misspellings --- docs/writing_a_simple_publisher_and_subscriber.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d30f4d98f..b0c65c439 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -91,12 +91,12 @@ By taking a look at your package, for example by typing [`tree .`](https://www.g To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): ```rust -use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. /// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, /// and the StringMsg message type from std_msgs for publishing string messages. +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; use std::{env, sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; // / SimplePublisherNode struct contains node and publisher members. @@ -161,14 +161,15 @@ fn main() -> Result<(), RclrsError> { count = publisher_other_thread.publish_data(count).unwrap(); }); rclrs::spin(publisher.node.clone()) -}``` +} +```
Examining the code in detail: #### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. ```rust -use std::{sync::Arc,time::Duration,iter,thread}; -use rclrs::{RclrsError,QOS_PROFILE_DEFAULT,Context,create_node,Node,Publisher}; +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; +use std::{env, sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; ``` * `use std::{sync::Arc, time::Duration, iter, thread};`: Imports specific features from the standard library: From bb0d860de90eba9d715243a2215d02c32aca9fa3 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:47:43 +0200 Subject: [PATCH 064/113] removed underlines --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b0c65c439..868fa0563 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -188,7 +188,7 @@ use std_msgs::msg::String as StringMsg; ```rust struct SimplePublisherNode { node: Arc, - _publisher: Arc>, + publisher: Arc>, } ``` 1. Structure: From f688632e4235a879ec9f525665798d30b132d324 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:50:24 +0200 Subject: [PATCH 065/113] Fixed misspellings --- docs/writing_a_simple_publisher_and_subscriber.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 868fa0563..b2efb888f 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -120,12 +120,12 @@ impl SimplePublisherNode { /// given name and creates a publisher on the "publish_hello" topic. /// /// The SimplePublisherNode contains the node and publisher members. - fn new(context: &Context) -> Result { + fn new(context: &context) -> result { let node = create_node(context, "simple_publisher").unwrap(); let publisher = node - .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .create_publisher("publish_hello", qos_profile_default) .unwrap(); - Ok(Self { node, publisher }) + ok(self { node, publisher }) } /// Publishes a "Hello World" message on the publisher. @@ -200,12 +200,12 @@ struct SimplePublisherNode { #### This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. ```rust impl SimplePublisherNode { - fn new(context: &Context) -> Result { + fn new(context: &context) -> result { let node = create_node(context, "simple_publisher").unwrap(); - let _publisher = node - .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + let publisher = node + .create_publisher("publish_hello", qos_profile_default) .unwrap(); - Ok(Self { node, _publisher, }) + ok(self { node, publisher }) } fn publish_data(&self,increment:i32) -> Result { From a89ee9fa5c1eff28f91930919b4925d68f9ddb18 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:51:03 +0200 Subject: [PATCH 066/113] Fixed misspellings --- docs/writing_a_simple_publisher_and_subscriber.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b2efb888f..451915987 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -207,13 +207,12 @@ impl SimplePublisherNode { .unwrap(); ok(self { node, publisher }) } - fn publish_data(&self,increment:i32) -> Result { - + fn publish_data(&self, increment: i32) -> Result { let msg: StringMsg = StringMsg { - data: format!("Hello World {}",increment), + data: format!("Hello World {}", increment), }; - self._publisher.publish(msg).unwrap(); - Ok(increment+1_i32) + self.publisher.publish(msg).unwrap(); + Ok(increment + 1_i32) } } ``` From 9a5a96f40899e7c45ec885ac76ce7041d736b423 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 19:54:04 +0200 Subject: [PATCH 067/113] Fixed misspellings --- docs/writing_a_simple_publisher_and_subscriber.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 451915987..33494c136 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -238,16 +238,14 @@ impl SimplePublisherNode { #### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. ```rust -fn main() -> Result<(),RclrsError> { - let context = Context::new(std::env::args()).unwrap(); +fn main() -> Result<(), RclrsError> { + let context = Context::new(env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); let publisher_other_thread = Arc::clone(&publisher); - let mut count: i32=0; - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - count=publisher_other_thread.publish_data(count).unwrap(); - }); + let mut count: i32 = 0; + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(1000)); + count = publisher_other_thread.publish_data(count).unwrap(); }); rclrs::spin(publisher.node.clone()) } From 163a966c59b0c2c81d2dcfabb85336c51426668e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 20:04:08 +0200 Subject: [PATCH 068/113] Fixed misspellings --- docs/writing_a_simple_publisher_and_subscriber.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 33494c136..5b5724b53 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -252,7 +252,7 @@ fn main() -> Result<(), RclrsError> { ``` 1. Main Function: -* `fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a `Result` type, indicating either successful execution or an `RclrsError`. +`fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a `Result` type, indicating either successful execution or an `RclrsError`. 2. Context and Node Setup: * `let context = Context::new(std::env::args()).unwrap();`: Creates a ROS 2 context using command-line arguments. * `let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());`: @@ -261,7 +261,7 @@ fn main() -> Result<(), RclrsError> { 3. Thread and Iterator: * `let publisher_other_thread = Arc::clone(&publisher);`: Clones the shared publisher pointer for use in a separate thread. * `let mut iterator: i32 = 0;`: Initializes a counter variable for message content. -* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html): `iter::repeat(()).for_each(|()| { ... });`: Creates an infinite loop using `iter::repeat`. +* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html): `loop { ... }`: Creates an infinite loop using `loop`. 4. Publishing Loop within Thread: * `thread::sleep(Duration::from_millis(1000));`: Pauses the thread for 1 second (1 Hz publishing rate). * `iterator = publisher_other_thread.publish_data(count).unwrap();`: Calls the `publish_data` method on the `publisher_other_thread` to publish a message with the current counter value. Increments the iterator for the next message. @@ -387,7 +387,7 @@ fn main() -> Result<(), RclrsError> { ```
Examining the code in detail: -#### The main Construct: +#### SimpleSubscriptionNode: ```rust pub struct SimpleSubscriptionNode { node: Arc, @@ -396,7 +396,11 @@ pub struct SimpleSubscriptionNode { } ``` Instead of a Publisher, there is a Subscription object in the Subscriber node. The data needs to be an `Arc>>` because there can be errors in the data transfer process and this can be caught by including the value of the incoming subscription in an optional. -#### This code defines a function named new that likely creates an instance of some SimpleSubscriptionNode. +#### impl SimpleSubscriptionNode +This code defines methods for the `SimpleSubscriptionNode` `struct`. + +##### new +The `new` method creates a ROS 2 node, subscriber, and a storage location for received messages, storing them in the `struct`. ```rust fn new(context: &Context) -> Result { let node = create_node(context, "simple_subscription").unwrap(); From 7970c304a6783c08d1e247f932687f39f70ace48 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Sat, 27 Apr 2024 20:06:00 +0200 Subject: [PATCH 069/113] I have now changed it so that the name of the method is in the heading. Below this is a short introduction to the method. --- docs/writing_a_simple_publisher_and_subscriber.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 5b5724b53..f0f2b9879 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -442,7 +442,8 @@ A few special features: * `msg: StringMsg`: This parameter receives the received message of type `StringMsg`. The closure body (`{...}`) uses the `Mutex` to access and update the shared data (`data_mut`) with the received message. 3. Cloning the Shared Data: * `let data_mut: Arc>> = Arc::clone(&data)`; This line creates another `Arc` reference (`data_mut`) pointing to the same underlying data structure as data. This allows the closure to access and modify the shared data. -#### this function provides a way to access and potentially use the received message data stored within the `Arc>>` member variable of the `struct`. It checks if a message exists, prints it if available, or informs the user there's no message yet. +##### data_callback +This function provides a way to access and potentially use the received message data stored within the `data` member variable of the `SimpleSubscriptionNode`. It checks if a message exists, prints it if available, or informs the user there's no message yet. ```rust fn data_callback(&self) -> Result<(), RclrsError> { if let Some(data) = self.data.lock().unwrap().as_ref() { From 2ce65e1b76c7446fc9ce0947a5549295adcd0792 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 29 May 2024 17:04:46 +0200 Subject: [PATCH 070/113] changed packagename This change is intended to better adapt the given case study to the existing ros2 documentation. --- docs/writing_a_simple_publisher_and_subscriber.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f0f2b9879..a8e808441 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -43,12 +43,12 @@ from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beg First, you'll need to create and go into a standard [cargo](https://doc.rust-lang.org/cargo/) project as follows: ``` -cargo new your_package_name && cd your_package_name +cargo new rust_pub_sub && cd rust_pub_sub ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: ```toml [package] -name = "your_package_name" +name = "rust_pub_sub" version = "0.1.0" edition = "2021" @@ -63,7 +63,7 @@ std_msgs = "*" Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: ```xml - your_package_name + rust_pub_sub 0.0.0 TODO: Package description. user @@ -275,7 +275,7 @@ fn main() -> Result<(), RclrsError> { Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ```toml [package] -name = "your_package_name" +name = "rust_pub_sub" version = "0.1.0" edition = "2021" @@ -298,7 +298,7 @@ source install/setub.bash ``` Running the node will look like this: ```shell -ros2 run your_package_name simple_publisher +ros2 run rust_pub_sub simple_publisher ``` As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. @@ -476,7 +476,7 @@ Please note that you'll need to run your nodes in separate terminals. In each te ```sh cd WORKSPACE source install/setup.bash -ros2 run your_package_name your_node_name +ros2 run rust_pub_sub your_node_name ``` In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: From 2d2909a4b8c8a722bc8def6c154c27feb43d7a6e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 29 May 2024 17:07:25 +0200 Subject: [PATCH 071/113] removed in depth comments --- docs/writing_a_simple_publisher_and_subscriber.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index a8e808441..b6859f6ea 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -332,14 +332,6 @@ pub struct SimpleSubscriptionNode { _subscriber: Arc>, data: Arc>>, } -/// Implements a simple ROS2 subscriber node that receives and prints "hello" messages. -/// -/// The `SimpleSubscriptionNode` creates a subscription to the "publish_hello" topic and -/// prints the received messages to the console. It runs the subscription in a separate -/// thread, while the main thread calls `rclrs::spin()` to keep the node running. -/// -/// The `new` function creates the node and the subscription, and returns a `SimpleSubscriptionNode` -/// instance. The `data_callback` function can be used to access the latest received message. impl SimpleSubscriptionNode { fn new(context: &Context) -> Result { let node = create_node(context, "simple_subscription").unwrap(); @@ -369,9 +361,6 @@ impl SimpleSubscriptionNode { Ok(()) } } -/// The `main` function creates a new ROS2 context, a `SimpleSubscriptionNode` instance, and starts a separate thread to periodically call the `data_callback` method on the subscription. The main thread then calls `rclrs::spin()` to keep the node running and receive messages. -/// -/// The separate thread is used to ensure that the `data_callback` method is called regularly, even if the main thread is blocked in `rclrs::spin()`. This allows the subscriber to continuously process and print the received "hello" messages. fn main() -> Result<(), RclrsError> { let context = Context::new(env::args()).unwrap(); let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); From b9e94af4bb35adca7781f96a77e0dddb060e5a9d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 29 May 2024 17:09:43 +0200 Subject: [PATCH 072/113] removed in depth comments --- ...iting_a_simple_publisher_and_subscriber.md | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b6859f6ea..ec395a015 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -94,32 +94,16 @@ To construct a node, replace the code in your `main.rs` file with the [following /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. -/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, -/// and the StringMsg message type from std_msgs for publishing string messages. use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; use std::{env, sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; -// / SimplePublisherNode struct contains node and publisher members. -// / Used to initialize a ROS 2 node and publisher, and publish messages. +/// SimplePublisherNode struct contains node and publisher members. +/// Used to initialize a ROS 2 node and publisher, and publish messages. struct SimplePublisherNode { node: Arc, publisher: Arc>, } -/// Creates a new SimplePublisherNode by initializing a node and publisher. -/// -/// The `new` function takes a context and returns a Result containing the -/// initialized SimplePublisherNode or an error. It creates a node with the -/// given name and creates a publisher on the "publish_hello" topic. -/// -/// The SimplePublisherNode contains the node and publisher members. impl SimplePublisherNode { - /// Creates a new SimplePublisherNode by initializing a node and publisher. - /// - /// This function takes a context and returns a Result containing the - /// initialized SimplePublisherNode or an error. It creates a node with the - /// given name and creates a publisher on the "publish_hello" topic. - /// - /// The SimplePublisherNode contains the node and publisher members. fn new(context: &context) -> result { let node = create_node(context, "simple_publisher").unwrap(); let publisher = node @@ -127,12 +111,6 @@ impl SimplePublisherNode { .unwrap(); ok(self { node, publisher }) } - - /// Publishes a "Hello World" message on the publisher. - /// - /// Creates a StringMsg with "Hello World" as the data, publishes it on - /// the `_publisher`, and returns a Result. This allows regularly publishing - /// a simple message on a loop. fn publish_data(&self, increment: i32) -> Result { let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), @@ -141,16 +119,6 @@ impl SimplePublisherNode { Ok(increment + 1_i32) } } - -/// The main function initializes a ROS 2 context, node and publisher, -/// spawns a thread to publish messages repeatedly, and spins the node -/// to receive callbacks. -/// -/// It creates a context, initializes a SimplePublisherNode which creates -/// a node and publisher, clones the publisher to pass to the thread, -/// spawns a thread to publish "Hello World" messages repeatedly, and -/// calls spin() on the node to receive callbacks. This allows publishing -/// messages asynchronously while spinning the node. fn main() -> Result<(), RclrsError> { let context = Context::new(env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); From d06a6b700f796024517e3ccc49ae0cd68cb2c43c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 29 May 2024 17:38:18 +0200 Subject: [PATCH 073/113] streamlining --- docs/writing_a_simple_publisher_and_subscriber.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index ec395a015..29a4e07b1 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -15,7 +15,7 @@ The code used in these examples can be found [here](https://gitlab.com/ros219239
Side-note on dependencies You may be wondering why you can't just add all your ROS2-specific dependencies to `Cargo.toml` with `cargo add YOUR_DEPENDENCIES` and have to edit this file manually. Here is why: -Almost none of the ROS2 dependencies you'll need for your ROS2 Rust node development currently exist on [crates.io](https://crates.io/), the main source for Rust depencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 Rust dependencies and your ROS2 Rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated Rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 Rust project this way. +Almost none of the ROS2 dependencies you'll need for your ROS2 Rust node development currently exist on [crates.io](https://crates.io/), the main source for Rust dependencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 Rust dependencies and your ROS2 Rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated Rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 Rust project this way.
@@ -89,7 +89,7 @@ By taking a look at your package, for example by typing [`tree .`](https://www.g
Write the publisher node -To construct a node, replace the code in your `main.rs` file with the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_publisher.rs?ref_type=heads): +To construct a node, replace the code in your `main.rs` file with the following: ```rust /// Creates a SimplePublisherNode, initializes a node and publisher, and provides /// methods to publish a simple "Hello World" message on a loop in separate threads. @@ -280,7 +280,7 @@ Or you can add a new binary target to your package. To do so, just add a new `FI name="simple_subscriber" path="src/simple_subscriber.rs" ``` -To construct the subscriber node, put the [following](https://gitlab.com/ros21923912/simple_ros2_node/-/blob/more_simple_nodes/src/simple_subscriber.rs?ref_type=heads) code into a `FILE.rs` - in my case its the `src/simple_subscriber.rs`: +To construct the subscriber node, put the following code into a `FILE.rs` - in my case its the `src/simple_subscriber.rs`: ```rust use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ From 8b1a3e8af2ab0931ca7b4af797e0e083a9823da6 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Tue, 25 Jun 2024 14:38:23 +0200 Subject: [PATCH 074/113] Update writing_a_simple_publisher_and_subscriber.md done --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 29a4e07b1..d9f5cbaa1 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -40,7 +40,7 @@ For a full Introduction into Rust, please read the very good [Rust book](https:/ Currently, building a package for ros2-rust is different from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html) or [C/C++](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html). -First, you'll need to create and go into a standard [cargo](https://doc.rust-lang.org/cargo/) +First, you'll need to go and create into a standard [cargo](https://doc.rust-lang.org/cargo/) project as follows: ``` cargo new rust_pub_sub && cd rust_pub_sub From dfa6b4c3d6add172893bf397a0c0a085c4fec8d6 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Tue, 25 Jun 2024 14:49:27 +0200 Subject: [PATCH 075/113] Update writing_a_simple_publisher_and_subscriber.md --- docs/writing_a_simple_publisher_and_subscriber.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d9f5cbaa1..06a75bdf0 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -43,12 +43,12 @@ from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beg First, you'll need to go and create into a standard [cargo](https://doc.rust-lang.org/cargo/) project as follows: ``` -cargo new rust_pub_sub && cd rust_pub_sub +cargo new rust_pubsub && cd rust_pubsub ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: ```toml [package] -name = "rust_pub_sub" +name = "rust_pubsub" version = "0.1.0" edition = "2021" @@ -63,7 +63,7 @@ std_msgs = "*" Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: ```xml - rust_pub_sub + rust_pubsub 0.0.0 TODO: Package description. user From 4d22fc88bfbad8e4e426b3e5553a96b6c4e3bf05 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Tue, 25 Jun 2024 14:58:08 +0200 Subject: [PATCH 076/113] Update writing_a_simple_publisher_and_subscriber.md done --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 06a75bdf0..a682fdb11 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -385,8 +385,8 @@ A few special features: * `let data: Arc>> = Arc::new(Mutex::new(None));` This line creates a shared data structure that will hold the received message. * `Arc>>`: This is a complex type combining several functionalities: - * `Arc`: An atomically reference-counted pointer ([`Arc`](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://doc.rust-lang.org/std/sync/struct.Arc.html&ved=2ahUKEwiJz_n3876FAxXZg_0HHc-yDZ8QFnoECAYQAQ&usg=AOvVaw2ZAPxD2olFejU3a_Ngb4f5)) allows multiple parts of the code to safely access the same data (`T`). - * `Mutex`: A mutual exclusion lock ([`Mutex`](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://doc.rust-lang.org/std/sync/struct.Mutex.html&ved=2ahUKEwjNx8uP9L6FAxVrhP0HHY4DB3YQFnoECAcQAQ&usg=AOvVaw3gOprM5PxBUUZd_3W6wFaG)) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. + * `Arc`: An atomically reference-counted pointer ([`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)) allows multiple parts of the code to safely access the same data (`T`). + * `Mutex`: A mutual exclusion lock ([`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)) ensures only one thread can modify the data (`T`) at a time, preventing race conditions. * `Option`: This represents an optional value that can either hold a message of type `StringMsg` or be `None` if no message has been received yet. * `Arc::new(Mutex::new(None))`: This creates a new instance of `Arc>>` and initializes the inner `Mutex` with `None`. 2. Creating a Subscription: From 54901aa94be0a83a695aa6fc9dd9ceb864531ad8 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Tue, 25 Jun 2024 15:03:48 +0200 Subject: [PATCH 077/113] Update writing_a_simple_publisher_and_subscriber.md I have changed the sentence so that the call of a command line tool is no longer included. --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index a682fdb11..49c88f3d2 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -77,7 +77,7 @@ Additionally, create a new `package.xml` if you want your node to be buildable w ``` -By taking a look at your package, for example by typing [`tree .`](https://www.geeksforgeeks.org/tree-command-unixlinux/) inside your package, and you'll see a structure similar to the following: +Your package should now have a similar structure: ```shell ├── Cargo.toml ├── package.xml From 6a4daf78a21fc0cda49c409b5ec2d78a59ea46ec Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:05:32 +0200 Subject: [PATCH 078/113] Addind simple subscriber code --- examples/rust_pubsub/src/simple_subscriber.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 examples/rust_pubsub/src/simple_subscriber.rs diff --git a/examples/rust_pubsub/src/simple_subscriber.rs b/examples/rust_pubsub/src/simple_subscriber.rs new file mode 100644 index 000000000..f810c250e --- /dev/null +++ b/examples/rust_pubsub/src/simple_subscriber.rs @@ -0,0 +1,57 @@ +use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; +use std::{ + env, iter, + sync::{Arc, Mutex}, + thread, + time::Duration, +}; +use std_msgs::msg::String as StringMsg; +/// A simple ROS2 subscriber node that receives and prints "hello" messages. +/// +/// This node creates a subscription to the "publish_hello" topic and prints the +/// received messages to the console. It runs the subscription in a separate +/// thread, while the main thread calls `rclrs::spin()` to keep the node running. +pub struct SimpleSubscriptionNode { + node: Arc, + _subscriber: Arc>, + data: Arc>>, +} +impl SimpleSubscriptionNode { + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_subscription").unwrap(); + let data: Arc>> = Arc::new(Mutex::new(None)); + let data_mut: Arc>> = Arc::clone(&data); + let _subscriber = node + .create_subscription::( + "publish_hello", + QOS_PROFILE_DEFAULT, + move |msg: StringMsg| { + *data_mut.lock().unwrap() = Some(msg); + }, + ) + .unwrap(); + Ok(Self { + node, + _subscriber, + data, + }) + } + fn data_callback(&self) -> Result<(), RclrsError> { + if let Some(data) = self.data.lock().unwrap().as_ref() { + println!("{}", data.data); + } else { + println!("No message available yet."); + } + Ok(()) + } +} +fn main() -> Result<(), RclrsError> { + let context = Context::new(env::args()).unwrap(); + let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); + let subscription_other_thread = Arc::clone(&subscription); + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(1000)); + subscription_other_thread.data_callback().unwrap() + }); + rclrs::spin(subscription.node.clone()) +} From fcc41fe914f6414e72d20cf67d0fab97de9dda58 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:05:43 +0200 Subject: [PATCH 079/113] added content to carto.toml --- examples/rust_pubsub/Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/rust_pubsub/Cargo.toml diff --git a/examples/rust_pubsub/Cargo.toml b/examples/rust_pubsub/Cargo.toml new file mode 100644 index 000000000..db5ad9a92 --- /dev/null +++ b/examples/rust_pubsub/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rust_pub_sub" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name="simple_publisher" +path="src/simple_publisher.rs" +[[bin]] +name="simple_subscriber" +path="src/simple_subscriber.rs" +[dependencies] +rclrs = "*" +std_msgs = "*" From 75208b9ff81ebb3002d0f155804e466da42ad7dc Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:05:51 +0200 Subject: [PATCH 080/113] added ros2 manifest file --- examples/rust_pubsub/package.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 examples/rust_pubsub/package.xml diff --git a/examples/rust_pubsub/package.xml b/examples/rust_pubsub/package.xml new file mode 100644 index 000000000..f988ccfa4 --- /dev/null +++ b/examples/rust_pubsub/package.xml @@ -0,0 +1,14 @@ + + rust_pubsub + 0.0.0 + TODO: Package description. + user + TODO: License declaration. + + rclrs + std_msgs + + + ament_cargo + + From 9033f10895c588561f3d19683b7cac1d22a567ea Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:17:53 +0200 Subject: [PATCH 081/113] added simple publisher example --- examples/rust_pubsub/src/simple_publisher.rs | 69 ++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/rust_pubsub/src/simple_publisher.rs diff --git a/examples/rust_pubsub/src/simple_publisher.rs b/examples/rust_pubsub/src/simple_publisher.rs new file mode 100644 index 000000000..8316d4fc9 --- /dev/null +++ b/examples/rust_pubsub/src/simple_publisher.rs @@ -0,0 +1,69 @@ +use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; +/// Creates a SimplePublisherNode, initializes a node and publisher, and provides +/// methods to publish a simple "Hello World" message on a loop in separate threads. + +/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, +/// and the StringMsg message type from std_msgs for publishing string messages. +use std::{sync::Arc, thread, time::Duration}; +use std_msgs::msg::String as StringMsg; +// / SimplePublisherNode struct contains node and publisher members. +// / Used to initialize a ROS 2 node and publisher, and publish messages. +struct SimplePublisherNode { + node: Arc, + _publisher: Arc>, +} +/// The `new` function takes a context and returns a Result containing the +/// initialized SimplePublisherNode or an error. It creates a node with the +/// given name and creates a publisher on the "publish_hello" topic. +/// +/// The SimplePublisherNode contains the node and publisher members. +impl SimplePublisherNode { + /// Creates a new SimplePublisherNode by initializing a node and publisher. + /// + /// This function takes a context and returns a Result containing the + /// initialized SimplePublisherNode or an error. It creates a node with the + /// given name and creates a publisher on the "publish_hello" topic. + /// + /// The SimplePublisherNode contains the node and publisher members. + fn new(context: &Context) -> Result { + let node = create_node(context, "simple_publisher").unwrap(); + let _publisher = node + .create_publisher("publish_hello", QOS_PROFILE_DEFAULT) + .unwrap(); + Ok(Self { node, _publisher }) + } + + /// Publishes a "Hello World" message on the publisher. + /// + /// Creates a StringMsg with "Hello World" as the data, publishes it on + /// the `_publisher`, and returns a Result. This allows regularly publishing + /// a simple message on a loop. + fn publish_data(&self, increment: i32) -> Result { + let msg: StringMsg = StringMsg { + data: format!("Hello World {}", increment), + }; + self._publisher.publish(msg).unwrap(); + Ok(increment + 1_i32) + } +} + +/// The main function initializes a ROS 2 context, node and publisher, +/// spawns a thread to publish messages repeatedly, and spins the node +/// to receive callbacks. +/// +/// It creates a context, initializes a SimplePublisherNode which creates +/// a node and publisher, clones the publisher to pass to the thread, +/// spawns a thread to publish "Hello World" messages repeatedly, and +/// calls spin() on the node to receive callbacks. This allows publishing +/// messages asynchronously while spinning the node. +fn main() -> Result<(), RclrsError> { + let context = Context::new(std::env::args()).unwrap(); + let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); + let publisher_other_thread = Arc::clone(&publisher); + let mut count: i32 = 0; + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(1000)); + count = publisher_other_thread.publish_data(count).unwrap(); + }); + rclrs::spin(publisher.node.clone()) +} From 87e789b659dd199dfd5f71a716a047170278c8da Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:28:04 +0200 Subject: [PATCH 082/113] fixed unused impport --- examples/rust_pubsub/src/simple_subscriber.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rust_pubsub/src/simple_subscriber.rs b/examples/rust_pubsub/src/simple_subscriber.rs index f810c250e..930f0cacc 100644 --- a/examples/rust_pubsub/src/simple_subscriber.rs +++ b/examples/rust_pubsub/src/simple_subscriber.rs @@ -1,6 +1,6 @@ use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ - env, iter, + env, sync::{Arc, Mutex}, thread, time::Duration, From b7158dd7eb53d22e65822b97815de4bdaf0aa127 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:45:59 +0200 Subject: [PATCH 083/113] removed comments because of rustdoc error --- examples/rust_pubsub/src/simple_publisher.rs | 31 -------------------- 1 file changed, 31 deletions(-) diff --git a/examples/rust_pubsub/src/simple_publisher.rs b/examples/rust_pubsub/src/simple_publisher.rs index 8316d4fc9..89d87af58 100644 --- a/examples/rust_pubsub/src/simple_publisher.rs +++ b/examples/rust_pubsub/src/simple_publisher.rs @@ -1,9 +1,4 @@ use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; -/// Creates a SimplePublisherNode, initializes a node and publisher, and provides -/// methods to publish a simple "Hello World" message on a loop in separate threads. - -/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers, -/// and the StringMsg message type from std_msgs for publishing string messages. use std::{sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; // / SimplePublisherNode struct contains node and publisher members. @@ -12,19 +7,7 @@ struct SimplePublisherNode { node: Arc, _publisher: Arc>, } -/// The `new` function takes a context and returns a Result containing the -/// initialized SimplePublisherNode or an error. It creates a node with the -/// given name and creates a publisher on the "publish_hello" topic. -/// -/// The SimplePublisherNode contains the node and publisher members. impl SimplePublisherNode { - /// Creates a new SimplePublisherNode by initializing a node and publisher. - /// - /// This function takes a context and returns a Result containing the - /// initialized SimplePublisherNode or an error. It creates a node with the - /// given name and creates a publisher on the "publish_hello" topic. - /// - /// The SimplePublisherNode contains the node and publisher members. fn new(context: &Context) -> Result { let node = create_node(context, "simple_publisher").unwrap(); let _publisher = node @@ -33,11 +16,6 @@ impl SimplePublisherNode { Ok(Self { node, _publisher }) } - /// Publishes a "Hello World" message on the publisher. - /// - /// Creates a StringMsg with "Hello World" as the data, publishes it on - /// the `_publisher`, and returns a Result. This allows regularly publishing - /// a simple message on a loop. fn publish_data(&self, increment: i32) -> Result { let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), @@ -47,15 +25,6 @@ impl SimplePublisherNode { } } -/// The main function initializes a ROS 2 context, node and publisher, -/// spawns a thread to publish messages repeatedly, and spins the node -/// to receive callbacks. -/// -/// It creates a context, initializes a SimplePublisherNode which creates -/// a node and publisher, clones the publisher to pass to the thread, -/// spawns a thread to publish "Hello World" messages repeatedly, and -/// calls spin() on the node to receive callbacks. This allows publishing -/// messages asynchronously while spinning the node. fn main() -> Result<(), RclrsError> { let context = Context::new(std::env::args()).unwrap(); let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap()); From 85fb2192123f4b583ed5a3aef242e0799dc3f49c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Tue, 25 Jun 2024 20:46:03 +0200 Subject: [PATCH 084/113] removed comments because of rustdoc error --- examples/rust_pubsub/src/simple_subscriber.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/rust_pubsub/src/simple_subscriber.rs b/examples/rust_pubsub/src/simple_subscriber.rs index 930f0cacc..a0d02bb4c 100644 --- a/examples/rust_pubsub/src/simple_subscriber.rs +++ b/examples/rust_pubsub/src/simple_subscriber.rs @@ -6,11 +6,6 @@ use std::{ time::Duration, }; use std_msgs::msg::String as StringMsg; -/// A simple ROS2 subscriber node that receives and prints "hello" messages. -/// -/// This node creates a subscription to the "publish_hello" topic and prints the -/// received messages to the console. It runs the subscription in a separate -/// thread, while the main thread calls `rclrs::spin()` to keep the node running. pub struct SimpleSubscriptionNode { node: Arc, _subscriber: Arc>, From 75870bb188813aa5a3026c2a7db0baf7d97e00a6 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Wed, 26 Jun 2024 10:32:24 +0200 Subject: [PATCH 085/113] Ignore rustdoc and cargo test for rust_pubsub --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e6f015e6d..3fd48dba0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -101,7 +101,7 @@ jobs: run: | cd ${{ steps.build.outputs.ros-workspace-directory-name }} . install/setup.sh - for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" { print $2 }'); do + for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" && $1 != "rust_pubsub" { print $2 }'); do cd $path echo "Running cargo test in $path" # Run cargo test for all features except generate_docs (needed for docs.rs) @@ -119,7 +119,7 @@ jobs: run: | cd ${{ steps.build.outputs.ros-workspace-directory-name }} . /opt/ros/${{ matrix.ros_distribution }}/setup.sh - for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" { print $2 }'); do + for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" && $1 != "rust_pubsub" { print $2 }'); do cd $path echo "Running rustdoc check in $path" cargo rustdoc -- -D warnings From 67a3fecfffc1cf94cf8644e11bf13b11b97faf0c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 12:05:42 +0200 Subject: [PATCH 086/113] Have overssen this suggestion My bad. This way, it's way more acurate --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 49c88f3d2..526ad4209 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -40,8 +40,8 @@ For a full Introduction into Rust, please read the very good [Rust book](https:/ Currently, building a package for ros2-rust is different from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html) or [C/C++](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html). -First, you'll need to go and create into a standard [cargo](https://doc.rust-lang.org/cargo/) -project as follows: +First, you'll need to create a standard [cargo](https://doc.rust-lang.org/cargo/) +package: ``` cargo new rust_pubsub && cd rust_pubsub ``` From 4210c4f8fb658ab8be9f55fd55b6079f62b64b8e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 13:38:43 +0200 Subject: [PATCH 087/113] changed something in docs --- docs/writing_a_simple_publisher_and_subscriber.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 526ad4209..9c46ee11a 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -134,7 +134,9 @@ fn main() -> Result<(), RclrsError> {
Examining the code in detail: -#### The first 3 lines of the Rust code imports tools for thread synchronization, time handling, iteration, threading, ROS 2 communication, and string message publishing. +#### Importing +The first 3 lines of the Rust code imports tools for thread synchronization, time +handling, iteration, threading, ROS 2 communication, and string message publishing. ```rust use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; use std::{env, sync::Arc, thread, time::Duration}; From b0d4efb393b25417940d4faa82a6f3faedc20bb5 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 13:38:52 +0200 Subject: [PATCH 088/113] fixing manifest file --- examples/rust_pubsub/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rust_pubsub/Cargo.toml b/examples/rust_pubsub/Cargo.toml index db5ad9a92..992c86267 100644 --- a/examples/rust_pubsub/Cargo.toml +++ b/examples/rust_pubsub/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rust_pub_sub" +name = "rust_pubsub" version = "0.1.0" edition = "2021" From e8b6dcfe7345dfa6efae42bffb9d1f43a7eaa0f5 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 13:42:09 +0200 Subject: [PATCH 089/113] changed ROS2 to ROS 2 --- docs/writing_a_simple_publisher_and_subscriber.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 9c46ee11a..59427243c 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -14,8 +14,12 @@ The code used in these examples can be found [here](https://gitlab.com/ros219239
Side-note on dependencies -You may be wondering why you can't just add all your ROS2-specific dependencies to `Cargo.toml` with `cargo add YOUR_DEPENDENCIES` and have to edit this file manually. Here is why: -Almost none of the ROS2 dependencies you'll need for your ROS2 Rust node development currently exist on [crates.io](https://crates.io/), the main source for Rust dependencies. So the add command simply can't find the dependency targets. What colcon does by compiling the ROS2 Rust dependencies and your ROS2 Rust project is redirect the cargo search for dependencies directly into your `workspace/install` folder, where it'll find locally generated Rust projects to use as dependencies. In particular, almost all message types will be called as dependencies for your ROS2 Rust project this way. +You may be wondering why you can't just add all your ROS 2-specific dependencies to `Cargo.toml` with `cargo add YOUR_DEPENDENCIES` and have to edit this file manually. Here is why: +Almost none of the ROS 2 dependencies you'll need for your ROS 2 Rust node development currently exist on [crates.io](https://crates.io/), the +main source for Rust dependencies. So the add command simply can't find the dependency targets. What colcon does by compiling +the ROS 2 Rust dependencies and your ROS 2 Rust project is redirect the cargo search for dependencies directly into your +`workspace/install` folder, where it'll find locally generated Rust projects to use as dependencies. In particular, almost +all message types will be called as dependencies for your ROS 2 Rust project this way.
From a409071126cc0dac151d3987444c57e8c6fb0048 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 13:45:52 +0200 Subject: [PATCH 090/113] changed ROS2 to ROS 2 --- docs/writing_a_simple_publisher_and_subscriber.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 59427243c..d88824b3c 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -27,7 +27,7 @@ all message types will be called as dependencies for your ROS 2 Rust project thi
Prerequisites -Basic concepts of development with ROS2 should be known: +Basic concepts of development with ROS 2 should be known: * [workspaces](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html) * [packages](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html). @@ -249,7 +249,7 @@ fn main() -> Result<(), RclrsError> { Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ```toml [package] -name = "rust_pub_sub" +name = "rust_pubsub" version = "0.1.0" edition = "2021" @@ -272,7 +272,7 @@ source install/setub.bash ``` Running the node will look like this: ```shell -ros2 run rust_pub_sub simple_publisher +ros2 run rust_pubsub simple_publisher ``` As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. @@ -439,7 +439,7 @@ Please note that you'll need to run your nodes in separate terminals. In each te ```sh cd WORKSPACE source install/setup.bash -ros2 run rust_pub_sub your_node_name +ros2 run rust_pubsub your_node_name ``` In my case, the nodes are called `simple_publisher` and `simple_subscriber`. You can name your nodes whatever you like. It is important that the publisher and subscriber use the same topic type and name. If you haven't had any errors so far and have successfully started the Publisher and Subscriber, you should see something similar in the Subscriber's Terminal window: From 489d40e6659e24c211f01ebdcac6a8934e0719b6 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:29:28 +0200 Subject: [PATCH 091/113] Apply suggestions from code review Co-authored-by: Sam Privett --- docs/writing_a_simple_publisher_and_subscriber.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d88824b3c..6865e4e7b 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -155,10 +155,11 @@ use std_msgs::msg::String as StringMsg; - Imports elements for ROS 2 communication: - `RclrsError` for handling errors. - `QOS_PROFILE_DEFAULT` for default Quality of Service settings. - - `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing. publishing. +- `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing. * `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages. -#### Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. +#### SimplePublisherNode +Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. ```rust struct SimplePublisherNode { node: Arc, @@ -171,7 +172,8 @@ struct SimplePublisherNode { 2. Members: * `node: Arc`: This member stores a reference to a ROS 2 node, wrapped in an [`Arc` (Atomic Reference Counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) smart pointer. This allows for safe sharing of the node reference across multiple threads. * `_publisher: Arc>`: This member stores a reference to a publisher specifically for string messages (`StringMsg`), also wrapped in an `Arc` for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. -#### This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. +#### impl SimplePublisher +This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. ```rust impl SimplePublisherNode { fn new(context: &context) -> result { @@ -198,7 +200,7 @@ impl SimplePublisherNode { * It takes a Context object as input, which is necessary for interacting with the ROS 2 syste. * It returns a Result type, indicating either a successful Self (the created `SimplePublisherNode` object) or an `RclrsError` if something goes wrong. * Inside the new method: - * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort (`panic`) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. + * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort ([`panic`](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html)) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic `"publish_hello"` with default quality of service settings. * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created `SimplePublisherNode` object, containing the node and publisher references. 3. Publishing Method: @@ -210,7 +212,8 @@ impl SimplePublisherNode { * `self._publisher.publish(msg).unwrap();`: Publishes the created message onto the topic associated with the publisher. * `Ok(increment + 1_i32)`: Returns a Result with the incremented increment value. -#### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. +#### main +The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. ```rust fn main() -> Result<(), RclrsError> { let context = Context::new(env::args()).unwrap(); From 71c616ffd9996600fcd262425b04dbe01c009c79 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 15:35:40 +0200 Subject: [PATCH 092/113] fixes --- docs/writing_a_simple_publisher_and_subscriber.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index d88824b3c..5581a4b3e 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -10,7 +10,6 @@ In this tutorial you will create a pair of Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). -The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node/-/tree/more_simple_nodes?ref_type=heads)
Side-note on dependencies @@ -158,7 +157,8 @@ use std_msgs::msg::String as StringMsg; - `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing. publishing. * `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages. -#### Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. +#### SimplePublisherNode +Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. ```rust struct SimplePublisherNode { node: Arc, @@ -171,7 +171,8 @@ struct SimplePublisherNode { 2. Members: * `node: Arc`: This member stores a reference to a ROS 2 node, wrapped in an [`Arc` (Atomic Reference Counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) smart pointer. This allows for safe sharing of the node reference across multiple threads. * `_publisher: Arc>`: This member stores a reference to a publisher specifically for string messages (`StringMsg`), also wrapped in an `Arc` for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. -#### This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. +#### Implemenation +This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. ```rust impl SimplePublisherNode { fn new(context: &context) -> result { @@ -210,7 +211,8 @@ impl SimplePublisherNode { * `self._publisher.publish(msg).unwrap();`: Publishes the created message onto the topic associated with the publisher. * `Ok(increment + 1_i32)`: Returns a Result with the incremented increment value. -#### The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. +#### Main +The main Method creates a ROS 2 node that publishes string messages at a rate of 1 Hz. ```rust fn main() -> Result<(), RclrsError> { let context = Context::new(env::args()).unwrap(); From 928122c6dd820da5d7ac19c2534091e13d1a35b6 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Wed, 26 Jun 2024 15:36:43 +0200 Subject: [PATCH 093/113] Update docs/writing_a_simple_publisher_and_subscriber.md Co-authored-by: Esteve Fernandez <33620+esteve@users.noreply.github.com> --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 6865e4e7b..c72702dc6 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -10,7 +10,7 @@ In this tutorial you will create a pair of Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). -The code used in these examples can be found [here](https://gitlab.com/ros21923912/simple_ros2_node/-/tree/more_simple_nodes?ref_type=heads) +The code used in these examples can be found [here](https://github.com/ros2-rust/ros2_rust/tree/main/examples/rust_pubsub)
Side-note on dependencies From 50359646097562cd4daa30e6d2854afc61698337 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 15:52:48 +0200 Subject: [PATCH 094/113] done --- docs/writing_a_simple_publisher_and_subscriber.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index a1f3c6240..de4990821 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -10,6 +10,8 @@ In this tutorial you will create a pair of Since Rust doesn't have inheritance, it's not possible to inherit from `Node` as is common practice in [`rclcpp`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html) or [`rclpy`](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html). +The code used in these examples can be found [here](https://github.com/ros2-rust/ros2_rust/tree/main/examples/rust_pubsub) +
Side-note on dependencies @@ -299,10 +301,6 @@ use std::{ }; use std_msgs::msg::String as StringMsg; /// A simple ROS2 subscriber node that receives and prints "hello" messages. -/// -/// This node creates a subscription to the "publish_hello" topic and prints the -/// received messages to the console. It runs the subscription in a separate -/// thread, while the main thread calls `rclrs::spin()` to keep the node running. pub struct SimpleSubscriptionNode { node: Arc, _subscriber: Arc>, From a5ebc6445dede4868464f6db1bdc6b3b96faaf7a Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:57:58 +0200 Subject: [PATCH 095/113] Update docs/writing_a_simple_publisher_and_subscriber.md --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 7f8c956d1..3559be3c5 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -46,7 +46,7 @@ from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beg First, you'll need to create a standard [cargo](https://doc.rust-lang.org/cargo/) package: -``` +```console cargo new rust_pubsub && cd rust_pubsub ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: From 6558f3b5ec988a8c6515b540d8d24bebfe40780e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 16:51:58 +0200 Subject: [PATCH 096/113] resolved hints --- docs/writing_a_simple_publisher_and_subscriber.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 7f8c956d1..cc0be1650 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -46,7 +46,7 @@ from building packages for [Python](https://docs.ros.org/en/humble/Tutorials/Beg First, you'll need to create a standard [cargo](https://doc.rust-lang.org/cargo/) package: -``` +```sh cargo new rust_pubsub && cd rust_pubsub ``` In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: @@ -82,7 +82,7 @@ Additionally, create a new `package.xml` if you want your node to be buildable w ``` Your package should now have a similar structure: -```shell +```sh ├── Cargo.toml ├── package.xml └── src @@ -268,13 +268,13 @@ std_msgs = "*" You'll find the name of your executable and the corresponding file name under the `[[bin]]` tag. As you can see, the filename and the name you want to call your node don't have to match. Please remember to include your executable name with snake_cases. The Rust compiler will be a bit grumpy if you don't. Now, by recompiling the package from the previous chapter and making it usable: -```shell +```sh cd WORKSPACE colcon build source install/setub.bash ``` Running the node will look like this: -```shell +```sh ros2 run rust_pubsub simple_publisher ``` As you can see, you are now calling your node by the name declared in `[[bin]]` using the `name` variable. From 79eb4258ac3d8cd4b39bacd502520f0ecd1a71de Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:45:52 +0200 Subject: [PATCH 097/113] resolved ROS2 to ROS 2 --- docs/writing_a_simple_publisher_and_subscriber.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index cc0be1650..b3d73205a 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -247,7 +247,7 @@ fn main() -> Result<(), RclrsError> {
-
Having several ROS2 Rust nodes in one Package +
Having several ROS 2 Rust nodes in one Package Of course, you can write for each node you want to implement its own package, and that can have it's advantages. I implore you to use some cargo tricks and add some binary targets to your `cargo.toml`. That could look like this: ```toml @@ -282,7 +282,7 @@ As you can see, you are now calling your node by the name declared in `[[bin]]`
Write the subscriber node -Of course, you can implement a new ROS2 Rust package for this node. You can find out how to do this in the section called 'Create a package'. +Of course, you can implement a new ROS 2 Rust package for this node. You can find out how to do this in the section called 'Create a package'. Or you can add a new binary target to your package. To do so, just add a new `FILE.rs` to your source directory - for simplicity I'll call this file `simple_subscriber.rs` - and add a corresponding binary target to your `Cargo.toml`: ```toml [[bin]] @@ -299,7 +299,7 @@ use std::{ time::Duration, }; use std_msgs::msg::String as StringMsg; -/// A simple ROS2 subscriber node that receives and prints "hello" messages. +/// A simple ROS 2 subscriber node that receives and prints "hello" messages. pub struct SimpleSubscriptionNode { node: Arc, _subscriber: Arc>, @@ -434,7 +434,7 @@ Once you have implemented the code, you are ready to make it runnable: cd WORKSPACE colcon build ``` -Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ROS2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: +Please note that you'll need to run your nodes in separate terminals. In each terminal, you'll need to source your ROS 2 installation separately. So for each of the two nodes you've built so far, open a terminal and type the following: ```sh cd WORKSPACE source install/setup.bash From c57cec89ef0d3f7681c056dd25d0c09fa495c512 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:47:32 +0200 Subject: [PATCH 098/113] changed could through should --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index b3d73205a..3f32aed46 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -49,7 +49,7 @@ package: ```sh cargo new rust_pubsub && cd rust_pubsub ``` -In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` could now look like this: +In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) file, add a dependency on `rclrs = "*"` and `std_msgs = "*"` by editing this file. Your `Cargo.toml` should now look like this: ```toml [package] name = "rust_pubsub" From 5238dfe2c641a4cc7338ea83e414d1bd3846852f Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:52:06 +0200 Subject: [PATCH 099/113] fixes --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 3f32aed46..f5288be79 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -158,8 +158,8 @@ use std_msgs::msg::String as StringMsg; - `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing. * `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages. -#### SimplePublisherNode -Next, this structure defines a SimplePublisherNode which holds references to a ROS 2 node and a publisher for string messages. +#### `SimplePublisherNode` +Next, this structure defines a `SimplePublisherNode` which holds references to a ROS 2 node and a publisher for string messages. ```rust struct SimplePublisherNode { node: Arc, From bbeca68c4b6fec34ec7cd05c649f129129c9128d Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:52:10 +0200 Subject: [PATCH 100/113] fixes --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index f5288be79..6517e7fff 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -172,7 +172,7 @@ struct SimplePublisherNode { 2. Members: * `node: Arc`: This member stores a reference to a ROS 2 node, wrapped in an [`Arc` (Atomic Reference Counted)](https://doc.rust-lang.org/std/sync/struct.Arc.html) smart pointer. This allows for safe sharing of the node reference across multiple threads. * `_publisher: Arc>`: This member stores a reference to a publisher specifically for string messages (`StringMsg`), also wrapped in an `Arc` for thread safety. The publisher is responsible for sending string messages to other nodes in the ROS 2 system. -#### impl SimplePublisher +#### `impl SimplePublisher` This code defines methods for the `SimplePublisherNode` `struct`. The `new` method creates a ROS 2 node and publisher, storing them in the `struct`. The `publish_data` method publishes a string message with a `counter` and returns the incremented `counter`. ```rust impl SimplePublisherNode { From 43d54f1aeb89550e01278324b6156a0257445634 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:52:20 +0200 Subject: [PATCH 101/113] fixes --- docs/writing_a_simple_publisher_and_subscriber.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 6517e7fff..c2d56c506 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -196,11 +196,11 @@ impl SimplePublisherNode { 1. Implementation Block: `impl SimplePublisherNode { ... }`: This line indicates that methods are being defined for the `SimplePublisherNode` struct. 2. Constructor Method: -* `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of SimplePublisherNode. - * It takes a Context object as input, which is necessary for interacting with the ROS 2 syste. +* `fn new(context: &Context) -> Result { ... }`: This method serves as a constructor for creating instances of `SimplePublisherNode`. + * It takes a Context object as input, which is necessary for interacting with the ROS 2 system. * It returns a Result type, indicating either a successful Self (the created `SimplePublisherNode` object) or an `RclrsError` if something goes wrong. * Inside the new method: - * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort ([`panic`](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html)) if something goes wrong. Since our code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. + * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort ([`panic`](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html)) if something goes wrong. Since your code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic `"publish_hello"` with default quality of service settings. * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created `SimplePublisherNode` object, containing the node and publisher references. 3. Publishing Method: From 8663ade9002abdcaa67d61844c62ccbb9a8263b6 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:56:38 +0200 Subject: [PATCH 102/113] added references --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index c2d56c506..3e32cc77e 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -202,7 +202,7 @@ impl SimplePublisherNode { * Inside the new method: * `let node = create_node(context, "simple_publisher").unwrap();`: Creates a new ROS 2 node named `"simple_publisher"` within the given context. The [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html) unwraps the [`Result`](https://doc.rust-lang.org/std/result/), handling any errors immediately by forcing the program to abort ([`panic`](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html)) if something goes wrong. Since your code can't function properly if the node is not able to be created, this is a valid error-handling response for our use-case. * `let _publisher = node.create_publisher("publish_hello", QOS_PROFILE_DEFAULT).unwrap();`: Creates a publisher for string messages on the topic `"publish_hello"` with default quality of service settings. - * `Ok(Self { node, _publisher, })`: Returns an `Ok` Result with the newly created `SimplePublisherNode` object, containing the node and publisher references. + * `Ok(Self { node, _publisher, })`: Returns an [`Ok(T)`](https://doc.rust-lang.org/std/result/) Result with the newly created `SimplePublisherNode` as `T` object, containing the node and publisher references. 3. Publishing Method: * `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a `counter`. * It takes an inkrement value (an integer) as input, which is used for counting purposes within the message content. From 34bfd6881e74fb33afab21c8ca54e902f08e0fd8 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 17:56:46 +0200 Subject: [PATCH 103/113] corrections --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 3e32cc77e..85334cc68 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -205,7 +205,7 @@ impl SimplePublisherNode { * `Ok(Self { node, _publisher, })`: Returns an [`Ok(T)`](https://doc.rust-lang.org/std/result/) Result with the newly created `SimplePublisherNode` as `T` object, containing the node and publisher references. 3. Publishing Method: * `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a `counter`. - * It takes an inkrement value (an integer) as input, which is used for counting purposes within the message content. + * It takes an increment value (an integer) as input, which is used for counting purposes within the message content. * It also returns a Result type, indicating either the incremented value or an RclrsError if publishing fails. * Inside the publish_data method: * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), };`: Creates a string message with the content `"Hello World"` followed by the increment value. From 4ecd4edd0307d6f0c22c5bd914ca58120ffcc392 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 18:07:20 +0200 Subject: [PATCH 104/113] added references --- docs/writing_a_simple_publisher_and_subscriber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 85334cc68..39ee31b63 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -206,7 +206,7 @@ impl SimplePublisherNode { 3. Publishing Method: * `fn publish_data(&self, increment: i32) -> Result { ... }`: This method publishes a string message and increments a `counter`. * It takes an increment value (an integer) as input, which is used for counting purposes within the message content. - * It also returns a Result type, indicating either the incremented value or an RclrsError if publishing fails. + * It also returns a Result type, indicating either the incremented value or an [`RclrsError`](https://docs.rs/rclrs/latest/rclrs/enum.RclrsError.html) if publishing fails. * Inside the publish_data method: * `let msg: StringMsg = StringMsg { data: format!("Hello World {}", increment), };`: Creates a string message with the content `"Hello World"` followed by the increment value. * `self._publisher.publish(msg).unwrap();`: Publishes the created message onto the topic associated with the publisher. From d6631b5612fc1eff766b908539fbbdaac15d0454 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Wed, 26 Jun 2024 18:07:43 +0200 Subject: [PATCH 105/113] added references --- docs/writing_a_simple_publisher_and_subscriber.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 39ee31b63..42b68360d 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -229,7 +229,7 @@ fn main() -> Result<(), RclrsError> { ``` 1. Main Function: -`fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a `Result` type, indicating either successful execution or an `RclrsError`. +`fn main() -> Result<(), RclrsError> { ... }`: This defines the main entry point of the program. It returns a [`Result`](https://doc.rust-lang.org/std/result/) type, indicating either successful execution or an [`RclrsError`](https://docs.rs/rclrs/latest/rclrs/enum.RclrsError.html). 2. Context and Node Setup: * `let context = Context::new(std::env::args()).unwrap();`: Creates a ROS 2 context using command-line arguments. * `let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());`: @@ -238,7 +238,7 @@ fn main() -> Result<(), RclrsError> { 3. Thread and Iterator: * `let publisher_other_thread = Arc::clone(&publisher);`: Clones the shared publisher pointer for use in a separate thread. * `let mut iterator: i32 = 0;`: Initializes a counter variable for message content. -* `thread::spawn(move || -> () { ... });`: Spawns a new thread with a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html): `loop { ... }`: Creates an infinite loop using `loop`. +* `thread::spawn(move || -> () { ... });`: Spawns a new [thread](https://doc.rust-lang.org/std/thread/index.html) with a [closure](https://doc.rust-lang.org/book/ch13-01-closures.html): `loop { ... }`: Creates an infinite loop using [`loop`](https://doc.rust-lang.org/std/keyword.loop.html). 4. Publishing Loop within Thread: * `thread::sleep(Duration::from_millis(1000));`: Pauses the thread for 1 second (1 Hz publishing rate). * `iterator = publisher_other_thread.publish_data(count).unwrap();`: Calls the `publish_data` method on the `publisher_other_thread` to publish a message with the current counter value. Increments the iterator for the next message. From bc054864b667559718d9c7ada067d0d0b87ee609 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Thu, 27 Jun 2024 10:00:20 +0200 Subject: [PATCH 106/113] Update examples/rust_pubsub/Cargo.toml Co-authored-by: Sam Privett --- examples/rust_pubsub/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/rust_pubsub/Cargo.toml b/examples/rust_pubsub/Cargo.toml index 992c86267..0e4ae24c8 100644 --- a/examples/rust_pubsub/Cargo.toml +++ b/examples/rust_pubsub/Cargo.toml @@ -8,9 +8,11 @@ edition = "2021" [[bin]] name="simple_publisher" path="src/simple_publisher.rs" + [[bin]] name="simple_subscriber" path="src/simple_subscriber.rs" + [dependencies] rclrs = "*" std_msgs = "*" From 5ff450dd30923376fee262032d18dcc3c05b465b Mon Sep 17 00:00:00 2001 From: Guelakais Date: Thu, 27 Jun 2024 10:00:33 +0200 Subject: [PATCH 107/113] Update examples/rust_pubsub/src/simple_publisher.rs Co-authored-by: Sam Privett --- examples/rust_pubsub/src/simple_publisher.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/rust_pubsub/src/simple_publisher.rs b/examples/rust_pubsub/src/simple_publisher.rs index 89d87af58..98d0e0f74 100644 --- a/examples/rust_pubsub/src/simple_publisher.rs +++ b/examples/rust_pubsub/src/simple_publisher.rs @@ -1,8 +1,6 @@ use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; use std::{sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; -// / SimplePublisherNode struct contains node and publisher members. -// / Used to initialize a ROS 2 node and publisher, and publish messages. struct SimplePublisherNode { node: Arc, _publisher: Arc>, From 1d8ce8a6af42c224d75dcd3f66d11a341a10cb20 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Thu, 27 Jun 2024 10:02:12 +0200 Subject: [PATCH 108/113] Update examples/rust_pubsub/Cargo.toml Co-authored-by: Sam Privett --- examples/rust_pubsub/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/rust_pubsub/Cargo.toml b/examples/rust_pubsub/Cargo.toml index 0e4ae24c8..487667556 100644 --- a/examples/rust_pubsub/Cargo.toml +++ b/examples/rust_pubsub/Cargo.toml @@ -3,8 +3,6 @@ name = "rust_pubsub" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [[bin]] name="simple_publisher" path="src/simple_publisher.rs" From 33943c2d01ba4d63c9992e3082107b7e9c49befd Mon Sep 17 00:00:00 2001 From: Guelakais Date: Thu, 27 Jun 2024 10:05:16 +0200 Subject: [PATCH 109/113] Update docs/writing_a_simple_publisher_and_subscriber.md Co-authored-by: Sam Privett --- docs/writing_a_simple_publisher_and_subscriber.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 42b68360d..be1a2e9b1 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -62,8 +62,6 @@ edition = "2021" rclrs = "*" std_msgs = "*" ``` - - Additionally, create a new `package.xml` if you want your node to be buildable with [`colcon`](https://colcon.readthedocs.io/en/released/user/installation.html). Make sure to change the build type to `ament_cargo` and to include the two packages mentioned above in the dependencies, as such: ```xml From 725428f943fef42e98e43e4dfdd8f51c49d1ce8c Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Thu, 27 Jun 2024 10:21:27 +0200 Subject: [PATCH 110/113] fixes --- docs/writing_a_simple_publisher_and_subscriber.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index 42b68360d..bfe85b4a1 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -294,12 +294,11 @@ To construct the subscriber node, put the following code into a `FILE.rs` - in m use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT}; use std::{ env, - iter,thread, sync::{Arc, Mutex}, + thread, time::Duration, }; use std_msgs::msg::String as StringMsg; -/// A simple ROS 2 subscriber node that receives and prints "hello" messages. pub struct SimpleSubscriptionNode { node: Arc, _subscriber: Arc>, @@ -338,11 +337,9 @@ fn main() -> Result<(), RclrsError> { let context = Context::new(env::args()).unwrap(); let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap()); let subscription_other_thread = Arc::clone(&subscription); - thread::spawn(move || -> () { - iter::repeat(()).for_each(|()| { - thread::sleep(Duration::from_millis(1000)); - subscription_other_thread.data_callback().unwrap() - }); + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(1000)); + subscription_other_thread.data_callback().unwrap() }); rclrs::spin(subscription.node.clone()) } @@ -383,7 +380,6 @@ The `new` method creates a ROS 2 node, subscriber, and a storage location for re data, }) } - ``` A few special features: 1. Initializing Shared Data: From ad4b4c8f0ca333df5e1c741b7a35c04ff0a489b5 Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Thu, 27 Jun 2024 10:21:31 +0200 Subject: [PATCH 111/113] fixes --- examples/rust_pubsub/src/simple_publisher.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rust_pubsub/src/simple_publisher.rs b/examples/rust_pubsub/src/simple_publisher.rs index 89d87af58..8056a482c 100644 --- a/examples/rust_pubsub/src/simple_publisher.rs +++ b/examples/rust_pubsub/src/simple_publisher.rs @@ -1,8 +1,8 @@ use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT}; use std::{sync::Arc, thread, time::Duration}; use std_msgs::msg::String as StringMsg; -// / SimplePublisherNode struct contains node and publisher members. -// / Used to initialize a ROS 2 node and publisher, and publish messages. +/// SimplePublisherNode struct contains node and publisher members. +/// Used to initialize a ROS 2 node and publisher, and publish messages. struct SimplePublisherNode { node: Arc, _publisher: Arc>, From b33c8b7a0afddbce437fe7f81876fff95967679e Mon Sep 17 00:00:00 2001 From: GueLaKais Date: Thu, 27 Jun 2024 10:23:12 +0200 Subject: [PATCH 112/113] dings --- docs/writing_a_simple_publisher_and_subscriber.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index bfe85b4a1..838324c60 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -149,7 +149,6 @@ use std_msgs::msg::String as StringMsg; * `use std::{sync::Arc, time::Duration, iter, thread};`: Imports specific features from the standard library: - `Arc` is for thread-safe shared ownership of data. - `Duration` represents a time span. - - `iter` provides tools for working with iterators. - `thread` enables creating and managing threads. * `use rclrs::{RclrsError, QOS_PROFILE_DEFAULT, Context, create_node, Node, Publisher};`: - Imports elements for ROS 2 communication: From 50ca289a46bb90adae5cae1288fd68d025274bd5 Mon Sep 17 00:00:00 2001 From: Guelakais Date: Thu, 27 Jun 2024 14:48:25 +0200 Subject: [PATCH 113/113] Update docs/writing_a_simple_publisher_and_subscriber.md yeah, ok Co-authored-by: Sam Privett --- docs/writing_a_simple_publisher_and_subscriber.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/writing_a_simple_publisher_and_subscriber.md b/docs/writing_a_simple_publisher_and_subscriber.md index e3af70ec1..a666ba80f 100644 --- a/docs/writing_a_simple_publisher_and_subscriber.md +++ b/docs/writing_a_simple_publisher_and_subscriber.md @@ -55,9 +55,6 @@ In the [`Cargo.toml`](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) f name = "rust_pubsub" version = "0.1.0" edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] rclrs = "*" std_msgs = "*"