diff --git a/doc/Tutorials/Approximate-Synchronizer-Cpp.rst b/doc/Tutorials/Approximate-Synchronizer-Cpp.rst index 8662894..4159e6a 100644 --- a/doc/Tutorials/Approximate-Synchronizer-Cpp.rst +++ b/doc/Tutorials/Approximate-Synchronizer-Cpp.rst @@ -22,8 +22,8 @@ If you have not done so already `create a workspace + #include using namespace std::chrono_literals; @@ -48,7 +48,8 @@ If you have not done so already `create a workspace `_. -To simulate a working ``Synchronizer`` using the ``ApproximateTime`` Policy. We will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. +To simulate a working ``Synchronizer`` using the ``ApproximateTime`` Policy. +We will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. .. code-block:: C++ @@ -57,7 +58,8 @@ To simulate a working ``Synchronizer`` using the ``ApproximateTime`` Policy. We message_filters::Subscriber temp_sub; message_filters::Subscriber fluid_sub; -Notice that the ``Subscribers`` are in the ``message_filters`` namespace, while we can utilize ``rclcpp::Publishers``. To simulate them we will also need two ``TimerBases``. Then, we will be utilizing a ``Synchronizer`` to get these messages from the sensor topics aligned. +Notice that the ``Subscribers`` are in the ``message_filters`` namespace, while we can utilize ``rclcpp::Publishers``. +To simulate them we will also need two ``TimerBases``. Then, we will be utilizing a ``Synchronizer`` to get these messages from the sensor topics aligned. Next, we can initialize these private elements within a basic ``Node`` constructor @@ -87,7 +89,11 @@ Next, we can initialize these private elements within a basic ``Node`` construct } -It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. So, create one ``rclcpp::QoS`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. For each private class member, do basic construction of the object relating to the ``Node`` and callback methods that may be used in the future. Both of the two timers we utilize will have different timer values of ``500ms`` and ``550ms`` which causes the timers to off at different points, which is an advantage of using ``ApproximateTime``. This will then work since we called ``setAgePenalty`` to ``0.50`` (50ms) Notice that we must call ``sync->registerCallback`` to sync up the two (or more) chosen topics. +It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. +So, create one ``rclcpp::QoS`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. +For each private class member, do basic construction of the object relating to the ``Node`` and callback methods that may be used in the future. +Both of the two timers we utilize will have different timer values of ``500ms`` and ``550ms`` which causes the timers to off at different points, which is an advantage of using ``ApproximateTime``. +This will then work since we called ``setAgePenalty`` to ``0.50`` (50ms) Notice that we must call ``sync->registerCallback`` to sync up the two (or more) chosen topics. So, we must create three (or more) private callbacks, one for the ``Synchronizer``, then two for our ``TimerBases`` which are each for a certain ``sensor_msgs``. @@ -131,7 +137,8 @@ So, we must create three (or more) private callbacks, one for the ``Synchronizer } -``SyncCallback`` takes ``const shared_ptr references`` relating to both topics becasue they will be taken at the exact time, from here you can compare these topics, set values, etc. This callback is the final goal of synching multiple topics and the reason why the qos and header stamps must be the same. This will be seen with the logging statement as both of the times will be the same. Though, the headers have to have the same ``stamp`` value, they don't have to be triggered at the same time with ``ApproximateTime`` which will be seen in a delay between logging calls. For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary. . +``SyncCallback`` takes ``const shared_ptr references`` relating to both topics because they will be taken at the exact time, from here you can compare these topics, set values, etc. This callback is the final goal of syncing multiple topics and the reason why the qos and header stamps must be the same. This will be seen with the logging statement as both of the times will be the same. Though, the headers have to have the same ``stamp`` value, they don't have to be triggered at the same time with ``ApproximateTime`` which will be seen in a delay between logging calls. +For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary. . Finally, create a main function and spin the node @@ -169,14 +176,30 @@ Finally, add the ``install(TARGETS…)`` section so ``ros2 run`` can find your e approximate_time_sync DESTINATION lib/${PROJECT_NAME}) - 3. Build ~~~~~~~~ From the root of your package, build and source. -.. code-block:: bash +.. tabs:: + + .. group-tab:: Linux + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: macOS + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: Windows + + .. code-block:: console - colcon build && . install/setup.zsh + $ colcon build + $ call C:\dev\ros2\local_setup.bat 4. Run ~~~~~~ diff --git a/doc/Tutorials/Approximate-Synchronizer-Python.rst b/doc/Tutorials/Approximate-Synchronizer-Python.rst index 0dc8812..4fcb1af 100644 --- a/doc/Tutorials/Approximate-Synchronizer-Python.rst +++ b/doc/Tutorials/Approximate-Synchronizer-Python.rst @@ -43,8 +43,13 @@ If you have not done so already `create a workspace `_. -To simulate a working ``ApproximateTimeSynchronizer``. We will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. To simulate them we will also need two ``Timers`` on different intervals. Then, we will be utilizing an ``ApproximateTimeSynchronizer`` to get these messages from the sensor topics aligned with a slight delay between messages.. -It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. So, create one ``QoSProfile`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. Do basic construction of each object relating to the ``Node`` and callback methods that may be used in the future. Both of the two timers we utilize will have different timer values of ``1`` and ``1.05`` which causes the timers to off at different points, which is an advantage of using ``ApproximateTime``. Notice that we must call ``sync->registerCallback`` to sync up the two (or more) chosen topics. +To simulate a working ``ApproximateTimeSynchronizer``. +We will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. +To simulate them we will also need two ``Timers`` on different intervals. +Then, we will be utilizing an ``ApproximateTimeSynchronizer`` to get these messages from the sensor topics aligned with a slight delay between messages.. +It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. So, create one ``QoSProfile`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. +Do basic construction of each object relating to the ``Node`` and callback methods that may be used in the future. +Both of the two timers we utilize will have different timer values of ``1`` and ``1.05`` which causes the timers to off at different points, which is an advantage of using ``ApproximateTime``. Notice that we must call ``sync->registerCallback`` to sync up the two (or more) chosen topics. So, we must create three (or more) private callbacks, one for the ``ApproximateTimeSynchronizer``, then two for our ``Timers`` which are each for a certain ``sensor_msg``. @@ -80,7 +85,10 @@ So, we must create three (or more) private callbacks, one for the ``ApproximateT self.fluid_pub.publish(fluid) -``SyncCallback`` takes both messages relating to both topics, then, from here you can compare these topics, set values, etc. This callback is the final goal of synching multiple topics and the reason why the qos must be the same. This will be seen with the logging statement as both of the times will be the same. Though, the headers have to have the same ``stamp`` value, they don't have to be triggered at the same time using an ``ApproximateTimeSynchronizer`` which will be seen in a delay between logging calls. For both ``TimerCallbacks`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary. . +``SyncCallback`` takes both messages relating to both topics, then, from here you can compare these topics, set values, etc. +This callback is the final goal of synching multiple topics and the reason why the qos must be the same. +This will be seen with the logging statement as both of the times will be the same. Though, the headers have to have the same ``stamp`` value, they don't have to be triggered at the same time using an ``ApproximateTimeSynchronizer`` which will be seen in a delay between logging calls. +For both ``TimerCallbacks`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary. . Finally, create a main function and spin the node @@ -107,7 +115,7 @@ Finally, create a main function and spin the node ^^^^^^^^^^^^^^^^^^^^^^ Navigate to the root of your package's directory, where ``package.xml`` is located, open, and add the following dependencies: -.. code-block:: Python +.. code-block:: xml rclpy message_filters diff --git a/doc/Tutorials/Cache-Cpp.rst b/doc/Tutorials/Cache-Cpp.rst index d7ed2bd..7240ea4 100644 --- a/doc/Tutorials/Cache-Cpp.rst +++ b/doc/Tutorials/Cache-Cpp.rst @@ -29,17 +29,17 @@ The next step is to create a new C++ file inside your package, e.g., ``cache_tut #include #include - #include "rclcpp/rclcpp.hpp" + #include #include "message_filters/subscriber.hpp" #include "message_filters/cache.hpp" - #include "std_msgs/msg/string.hpp" - + #include + using namespace std::chrono_literals; - + const std::string TUTORIAL_TOPIC_NAME = "tutorial_topic"; - + class CacheNode : public rclcpp::Node { public: CacheNode() @@ -48,36 +48,36 @@ The next step is to create a new C++ file inside your package, e.g., ``cache_tut auto qos = rclcpp::QoS(10); publisher_ = this->create_publisher(TUTORIAL_TOPIC_NAME, qos); subscriber_filter_.subscribe(this, TUTORIAL_TOPIC_NAME, qos); - + publisher_timer_ = this->create_wall_timer( 1s, std::bind(&CacheNode::publisher_timer_callback, this) ); - + query_timer_ = this->create_wall_timer( 1s, std::bind(&CacheNode::query_timer_callback, this) ); } - + void publisher_timer_callback() { auto message = std_msgs::msg::String(); message.data = "example string"; publisher_->publish(message); } - + void query_timer_callback() { rclcpp::Time latest_time = cache_filter_.getLatestTime(); - + if (latest_time == rclcpp::Time()) { RCLCPP_INFO( this->get_logger(), "Cache is empty" ); return; } - + rclcpp::Time oldest_time = cache_filter_.getOldestTime(); - + RCLCPP_INFO( this->get_logger(), "oldest_time: %f, latest_time: %f", @@ -85,28 +85,27 @@ The next step is to create a new C++ file inside your package, e.g., ``cache_tut oldest_time.seconds() ); } - + private: rclcpp::Publisher::SharedPtr publisher_; - + rclcpp::TimerBase::SharedPtr publisher_timer_; rclcpp::TimerBase::SharedPtr query_timer_; - + message_filters::Subscriber subscriber_filter_; message_filters::Cache cache_filter_{subscriber_filter_, 10, true}; }; - - + + int main(int argc, char ** argv) { rclcpp::init(argc, argv); auto cache_node = std::make_shared(); rclcpp::spin(cache_node); rclcpp::shutdown(); - + return 0; } - 1.1 Examine the code ~~~~~~~~~~~~~~~~~~~~ @@ -119,15 +118,15 @@ Now, let's break down this code and examine the details. #include #include - #include "rclcpp/rclcpp.hpp" + #include #include "message_filters/subscriber.hpp" #include "message_filters/cache.hpp" - #include "std_msgs/msg/string.hpp" + #include using namespace std::chrono_literals; - + We start by including ``chrono`` and ``functional`` headers. The ``chrono`` header is required for the ``chrono_literals`` namespace, necessary for creating timers. The ``functional`` header is also required to use ``std::bind`` function to bind timer callbacks to timers. @@ -143,10 +142,10 @@ For starters, let's take a look at the ``private`` section of this class: .. code-block:: C++ rclcpp::Publisher::SharedPtr publisher_; - + rclcpp::TimerBase::SharedPtr publisher_timer_; rclcpp::TimerBase::SharedPtr query_timer_; - + message_filters::Subscriber subscriber_filter_; message_filters::Cache cache_filter_{subscriber_filter_, 10, true}; @@ -205,7 +204,7 @@ Now it is worthy to draw some attention to the following line of code. .. code-block:: C++ if (latest_time == rclcpp::Time()) - + Since we use the headerless ``String`` message in this tutorial, the time source for this message is the default ``RCL_SYSTEM_TIME``. If we would use messages with headers, the expected time source for them would be the ``RCL_ROS_TIME``. @@ -219,18 +218,18 @@ Finally, let's take a look at the class constructor. auto qos = rclcpp::QoS(10); publisher_ = this->create_publisher(TUTORIAL_TOPIC_NAME, qos); subscriber_filter_.subscribe(this, TUTORIAL_TOPIC_NAME, qos); - + publisher_timer_ = this->create_wall_timer( 1s, std::bind(&CacheNode::publisher_timer_callback, this) ); - + query_timer_ = this->create_wall_timer( 1s, std::bind(&CacheNode::query_timer_callback, this) ); } - + Here we create a ``publisher_``, that is going to publish messages to some topic. .. code-block:: C++ @@ -251,7 +250,7 @@ After that all what's left to be done is to create timers and we are good to go. 1s, std::bind(&CacheNode::publisher_timer_callback, this) ); - + query_timer_ = this->create_wall_timer( 1s, std::bind(&CacheNode::query_timer_callback, this) @@ -267,7 +266,7 @@ The ``main`` function in this case is pretty straightforward. auto cache_node = std::make_shared(); rclcpp::spin(cache_node); rclcpp::shutdown(); - + return 0; } @@ -300,7 +299,7 @@ Finally, add the install(TARGETS…) section so ros2 run can find your executabl install(TARGETS cache_tutorial DESTINATION lib/${PROJECT_NAME}) - + 4. Build Your Package ~~~~~~~~~~~~~~~~~~~~~ @@ -341,7 +340,7 @@ The first message in the output is going to be .. code-block:: console [INFO] [1752701571.845039452] [cache_node]: Cache is empty - + As there were no messages published yet, and the cache is empty. After that, the publisher will start populate the cache with messages: @@ -365,7 +364,7 @@ Note as the oldest time is starting to update after the 5'th message is added to The cache size for the ``Cache`` in this example is 10. So as the 10'th message is added to the cache, the oldest messages are being removed from it, thus updating oldest time. -6. Other methods of the Cache filter interface +6. Other methods of the Cache filter interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``Cache`` filter stores the last N messages (in this case, 5), and allows querying: diff --git a/doc/Tutorials/Cache-Python.rst b/doc/Tutorials/Cache-Python.rst index 0753c1f..e68305b 100644 --- a/doc/Tutorials/Cache-Python.rst +++ b/doc/Tutorials/Cache-Python.rst @@ -162,7 +162,7 @@ It is the way to chain these two filters together. Message is going to pass thro down the chain. It may be useful to point out that the ``Subscriber`` filter is not the only -way to start a chain of filters. One may consider using ``SimpleFilter``. +way to start a chain of filters. One may consider using ``SimpleFilter``. It does not create a new subscription on it's own and may be used directly in a subscription callback instead. @@ -254,7 +254,7 @@ From the root of your workspace: .. code-block:: console - $ colcon build && . install/setup.bash + $ colcon build && . install/setup.bash .. group-tab:: macOS @@ -283,7 +283,7 @@ The first message in the output is going to be .. code-block:: bash [INFO] [1750884527.235426721] [cache_node]: Cache filters cache is empty - + As there were no messages published yet, and the cache is empty. After that, the publisher will start populate the cache with messages: @@ -302,7 +302,7 @@ Note as the oldest time is starting to update after the 5'th message is added to The cache size for the ``cache`` in this example is 5. So as the 5'th message is added to the cache, the oldest messages are being removed from it, thus updating oldest time. -6. Other methods of the Cache filter interface +6. Other methods of the Cache filter interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``Cache`` filter stores the last N messages (in this case, 5), and allows querying: diff --git a/doc/Tutorials/Writing-A-Time-Synchronizer-Cpp.rst b/doc/Tutorials/Writing-A-Time-Synchronizer-Cpp.rst index a576594..00a8090 100644 --- a/doc/Tutorials/Writing-A-Time-Synchronizer-Cpp.rst +++ b/doc/Tutorials/Writing-A-Time-Synchronizer-Cpp.rst @@ -7,13 +7,12 @@ This tutorial assumes you have a working knowledge of ROS 2 If you have not done so already `create a workspace `_ and `create a package `_ - 1. Create a Basic Node with Includes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: C++ - #include "rclcpp/rclcpp.hpp" + #include #include #include @@ -22,8 +21,8 @@ If you have not done so already `create a workspace + #include using namespace std::chrono_literals; @@ -35,14 +34,13 @@ If you have not done so already `create a workspace ::SharedPtr temp_pub; - rclcpp::Publisher::SharedPtr fluid_pub; - message_filters::Subscriber temp_sub; - message_filters::Subscriber fluid_sub; - std::shared_ptr> sync; - rclcpp::TimerBase::SharedPtr timer; + rclcpp::Publisher::SharedPtr temp_pub; + rclcpp::Publisher::SharedPtr fluid_pub; + message_filters::Subscriber temp_sub; + message_filters::Subscriber fluid_sub; + std::shared_ptr> sync; + rclcpp::TimerBase::SharedPtr timer; }; @@ -124,7 +122,10 @@ So, we must create some private callbacks. fluid_pub->publish(fluid); } -``SyncCallback`` takes ``const shared_ptr references`` relating to both topics becasue they will be taken at the exact time, from here you can compare these topics, set values, etc. This callback is the final goal of synching multiple topics and the reason why the qos and header stamps must be the same. This will be seen with the logging statement as both of the times will be the same. For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary, but make sure the header stamp of both have the same exact time, otherwise the ``TimeSynchronizer`` will be misaligned and won't do anything. This is becasue the ``TimeSynchronizer`` has an ``ExactTime`` sync policy. +``SyncCallback`` takes ``const shared_ptr references`` relating to both topics because they will be taken at the exact time, from here you can compare these topics, set values, etc. +This callback is the final goal of synching multiple topics and the reason why the qos and header stamps must be the same. This will be seen with the logging statement as both of the times will be the same. +For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary, but make sure the header stamp of both have the same exact time, otherwise the ``TimeSynchronizer`` will be misaligned and won't do anything. +This is because the ``TimeSynchronizer`` has an ``ExactTime`` sync policy. Finally, create a main function and spin the node @@ -162,14 +163,30 @@ Finally, add the ``install(TARGETS…)`` section so ``ros2 run`` can find your e time_sync DESTINATION lib/${PROJECT_NAME}) - 3. Build ~~~~~~~~ From the root of your package, build and source. -.. code-block:: bash +.. tabs:: + + .. group-tab:: Linux + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: macOS + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: Windows + + .. code-block:: console - colcon build && . install/setup.zsh + $ colcon build + $ call C:\dev\ros2\local_setup.bat 4. Run ~~~~~~ diff --git a/doc/Tutorials/Writing-A-Time-Synchronizer-Python.rst b/doc/Tutorials/Writing-A-Time-Synchronizer-Python.rst index 0e8416c..c10e4ef 100644 --- a/doc/Tutorials/Writing-A-Time-Synchronizer-Python.rst +++ b/doc/Tutorials/Writing-A-Time-Synchronizer-Python.rst @@ -41,9 +41,14 @@ If you have not done so already `create a workspace `_. -To simulate a working ``TimeSynchronizer`` we will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. To simulate them we will also need some sort of ``Timer``. Then, we will be utilizing said ``TimeSynchronizer`` to get these messages from the sensor topics aligned, seen with the two ``Subscribers`` conjoined in the ``TimeSynchronizer`` initialization. +To simulate a working ``TimeSynchronizer`` we will be publishing and subscribing to topics of those respective types, to showcase how real sensors would be working. +To simulate them we will also need some sort of ``Timer``. +Then, we will be utilizing said ``TimeSynchronizer`` to get these messages from the sensor topics aligned, seen with the two ``Subscribers`` conjoined in the ``TimeSynchronizer`` initialization. -It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. So, create one ``QoSProfile`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. For each class member, do basic construction of the object relating to the ``Node`` and callback methods that may be used in the future. Notice that we must call ``sync.registerCallback`` to sync up the two (or more) chosen topics. +It is essential that the QoS is the same for all of the publishers and subscribers, otherwise the Message Filter cannot align the topics together. +So, create one ``QoSProfile`` and stick with it, or find out what ``qos`` is being used in the native sensor code, and replicate it. +For each class member, do basic construction of the object relating to the ``Node`` and callback methods that may be used in the future. +Notice that we must call ``sync.registerCallback`` to sync up the two (or more) chosen topics. So, we must create some callbacks. @@ -75,7 +80,10 @@ So, we must create some callbacks. fluid.fluid_pressure = 2.0 self.fluid_pub.publish(fluid) -``SyncCallback`` takes a fluid_pressure and a temperature relating to both topics becasue they will be taken at the exact time, from here you can compare these topics, set values, etc. This callback is the final goal of synching multiple topics and the reason why the qos and header stamps must be the same. This will be seen with the logging statement as both of the times will be the same. For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary, but make sure the header stamp of both have the same exact time, otherwise the ``TimeSynchronizer`` will be misaligned and won't do anything. +``SyncCallback`` takes a fluid_pressure and a temperature relating to both topics because they will be taken at the exact time, from here you can compare these topics, set values, etc. +This callback is the final goal of synching multiple topics and the reason why the qos and header stamps must be the same. +This will be seen with the logging statement as both of the times will be the same. +For the ``TimerCallback`` just initialize both the ``Temperature`` and ``FluidPressure`` in whatever way necessary, but make sure the header stamp of both have the same exact time, otherwise the ``TimeSynchronizer`` will be misaligned and won't do anything. Finally, create a main function and spin the node @@ -102,7 +110,7 @@ Finally, create a main function and spin the node ^^^^^^^^^^^^^^^^^^^^^^ Navigate to the root of your package's directory, where ``package.xml`` is located, open, and add the following dependencies: -.. code-block:: Python +.. code-block:: xml rclpy message_filters @@ -118,15 +126,31 @@ Add the following line between the 'console_scripts': brackets, with the name of 'time_sync = pkg_name.time_sync:main', - 3. Build ~~~~~~~~ From the root of your package, build and source. -.. code-block:: bash +.. tabs:: + + .. group-tab:: Linux + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: macOS + + .. code-block:: console + + $ colcon build && . install/setup.bash + + .. group-tab:: Windows + + .. code-block:: console - colcon build && . install/setup.zsh + $ colcon build + $ call C:\dev\ros2\local_setup.bat 4. Run ~~~~~~