|
| 1 | +.. _filtered_topic_keys_tutorial: |
| 2 | + |
| 3 | +Topic Keys Subscription Filtering Tutorial |
| 4 | +========================================== |
| 5 | + |
| 6 | +This tutorial aims to demonstrate how to receive data only from certain topic instances by combining the use of topic keys and topic content filtering. |
| 7 | + |
| 8 | +.. contents:: |
| 9 | + :depth: 2 |
| 10 | + :local: |
| 11 | + :backlinks: none |
| 12 | + |
| 13 | +Background |
| 14 | +---------- |
| 15 | + |
| 16 | +In ROS 2, topics are a mean for representing the state of an object. |
| 17 | +Keyed topics are special topics where each data sample represent an update of the state of a specific object (known as *instance*) among all those objects represented in the topic. |
| 18 | +This allows the user to reduce the number of required resources (topics, along with its associated publisher and subscriber) by multiplexing updates of several objects of the same kind into a single resource. |
| 19 | + |
| 20 | +The :doc:`Content Filter Topic <../../Demos/Content-Filtering-Subscription>` facilitates efficient data distribution by allowing the subscription (reader-side) to specify criteria for the types of data they wish to receive. |
| 21 | +By defining this criteria, irrelevant data can be filtered out and applications can focus only on the information that is relevant to their needs. |
| 22 | +This functionality not only reduces the amount of data transmitted over the network but also minimizes processing overhead on the receiving end, leading to improved system performance and scalability. |
| 23 | + |
| 24 | +When combined with topic instances, the benefits of the Content Filter are further enhanced. |
| 25 | +By associating specific filter criteria with each topic instance, it is possible fine-tune the data selection process and tailor it to their precise requirements. |
| 26 | +This granular level of filtering enables applications to optimally manage data while ensuring that they exchange only the information that is pertinent to their individual use cases. |
| 27 | + |
| 28 | +.. image:: figures/keyed-topics-cft.gif |
| 29 | + :align: center |
| 30 | + :width: 70% |
| 31 | + |
| 32 | +RMW Support |
| 33 | +----------- |
| 34 | + |
| 35 | +Keyed topics require RMW implementation support. |
| 36 | + |
| 37 | +.. list-table:: Keyed Topics Support Status |
| 38 | + :widths: 25 25 |
| 39 | + |
| 40 | + * - rmw_fastrtps |
| 41 | + - supported |
| 42 | + * - rmw_connextdds |
| 43 | + - supported |
| 44 | + * - rmw_cyclonedds |
| 45 | + - not supported |
| 46 | + |
| 47 | +Implementations not supporting the feature will treat keyed topics as standard topics. |
| 48 | +The implications are explained throughout the tutorial. |
| 49 | +In addition, endpoints using `Cyclone DDS <https://index.ros.org/p/rmw_cyclonedds_cpp/>`_ will not even match with Fast DDS or Connext DDS endpoints for this kind of topics. |
| 50 | + |
| 51 | +Prerequisites |
| 52 | +------------- |
| 53 | + |
| 54 | +* An up-to-date ROS 2 installation and setup. |
| 55 | + Either installed in local host, or using Docker images. |
| 56 | + |
| 57 | +Preparing the demo package |
| 58 | +-------------------------- |
| 59 | + |
| 60 | +Lets start by setting up the ROS 2 environment. |
| 61 | +For this, there are two possible options: |
| 62 | + |
| 63 | +#. Running a ROS 2 Docker image. |
| 64 | + |
| 65 | + .. code-block:: bash |
| 66 | +
|
| 67 | + docker run -it --rm osrt/ros:{DISTRO}-desktop |
| 68 | +
|
| 69 | + Then, within the container, source the ROS 2 installation with: |
| 70 | + |
| 71 | + .. code-block:: bash |
| 72 | +
|
| 73 | + source /opt/ros/{DISTRO}/setup.bash |
| 74 | +
|
| 75 | +#. Running the tutorial on the local host. |
| 76 | + Please, follow the :doc:`installation instructions <../../../Installation>` for details on installing ROS 2. |
| 77 | + |
| 78 | + Source the following file to setup the ROS 2 environment: |
| 79 | + |
| 80 | + .. code-block:: bash |
| 81 | +
|
| 82 | + source /opt/ros/{DISTRO}/setup.bash |
| 83 | +
|
| 84 | +
|
| 85 | +Retrieving the sources |
| 86 | +^^^^^^^^^^^^^^^^^^^^^^ |
| 87 | + |
| 88 | +Create a new workspace and download the demo package sources as indicated below: |
| 89 | + |
| 90 | +.. code-block:: bash |
| 91 | +
|
| 92 | + # Create directory structure |
| 93 | + mkdir -p ~/tutorial_ws/src/demo_keys_filtering_cpp |
| 94 | + mkdir ~/tutorial_ws/src/demo_keys_filtering_cpp/msg |
| 95 | + mkdir ~/tutorial_ws/src/demo_keys_filtering_cpp/src |
| 96 | + mkdir ~/tutorial_ws/src/demo_keys_filtering_cpp/launch |
| 97 | + cd ~/tutorial_ws/src/demo_keys_filtering_cpp |
| 98 | +
|
| 99 | + # Download demo package source code |
| 100 | + wget -O CMakeLists.txt https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/CMakeLists.txt |
| 101 | + wget -O package.xml https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/package.xml |
| 102 | + wget -O README.md https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/README.md |
| 103 | + wget -O msg/KeyedSensorDataMsg.msg https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/msg/KeyedSensorDataMsg.msg |
| 104 | + wget -O src/filtered_keyed_sensor.cpp https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/src/filtered_keyed_sensor.cpp |
| 105 | + wget -O src/filtered_keyed_controller.cpp https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/src/filtered_keyed_controller.cpp |
| 106 | + wget -O launch/keyed_sensors_launch.py https://raw.githubusercontent.com/ros2/ros2_documentation/{DISTRO}/source/Tutorials/Advanced/Topic-Keys/resources/Filtered/launch/keyed_sensors_launch.py |
| 107 | +
|
| 108 | +The resulting directory structure should be: |
| 109 | + |
| 110 | +.. code-block:: |
| 111 | +
|
| 112 | + ~/tutorial_ws |
| 113 | + ├──src |
| 114 | + ├── demo_keys_filtering_cpp |
| 115 | + ├── CMakeLists.txt |
| 116 | + ├── README.md |
| 117 | + ├── launch |
| 118 | + │ └── keyed_sensors_launch.py |
| 119 | + ├── msg |
| 120 | + │ └── KeyedSensorDataMsg.msg |
| 121 | + ├── package.xml |
| 122 | + └── src |
| 123 | + ├── filtered_keyed_controller.cpp |
| 124 | + └── filtered_keyed_sensor.cpp |
| 125 | +
|
| 126 | +A brief analysis on the provided files is explained below: |
| 127 | + |
| 128 | +* *demo_keys_filtering_cpp* : This directory contains the main source code and configuration files for the demonstration. |
| 129 | +* ``CMakeLists.txt``: This file is used with CMake to specify build configurations and dependencies. |
| 130 | +* ``README.md``: This is a markdown file providing instructions or information about the demonstration. |
| 131 | +* *launch*: This directory contains launch configuration files for launching ROS nodes. |
| 132 | + |
| 133 | + * ``keyed_sensors_launch.py``: This Python script is used to launch the demonstration nodes. |
| 134 | + |
| 135 | +* *msg*: This directory contains message definition files. |
| 136 | + |
| 137 | + * KeyedSensorDataMsg.msg: This is the file defining the message structure for keyed sensor data used in the tutorial. |
| 138 | + |
| 139 | +* ``package.xml``: This is an XML file containing metadata about the ROS package. |
| 140 | +* *src*: This directory contains the source code files for the demonstration. |
| 141 | + |
| 142 | + * ``filtered_keyed_controller.cpp``: This is the source code for a controller node that filters keyed sensor data in reception, being the most relevant lines the ones that define the filter expression and Quality of Service settings: |
| 143 | + |
| 144 | + .. code-block:: bash |
| 145 | +
|
| 146 | + // Initialize a subscription with a content filter to receive data from sensors 2 to 4 |
| 147 | + rclcpp::SubscriptionOptions sub_options; |
| 148 | + sub_options.content_filter_options.filter_expression = |
| 149 | + "sensor_id >= 2 AND sensor_id <= 4 AND measurement > %0"; |
| 150 | + sub_options.content_filter_options.expression_parameters = { |
| 151 | + std::to_string(SENSOR_TRIGGER) |
| 152 | + }; |
| 153 | +
|
| 154 | + // Create the subscription with the content filter options |
| 155 | + sub_ = create_subscription<demo_keys_filtering_cpp::msg::KeyedSensorDataMsg>("/robot/sensors", |
| 156 | + rclcpp::QoS(rclcpp::KeepLast(1)).reliable().transient_local(), |
| 157 | + callback, |
| 158 | + sub_options); |
| 159 | +
|
| 160 | + * ``filtered_keyed_sensor.cpp``: This is the source code for a sensor node that publishes keyed sensor data. |
| 161 | + The most relevant lines are the ones that create the publication with a particular Quality of Service settings that enables the controller to late join the application but still receiving the latest update for every instance with the use of topic keys. |
| 162 | + |
| 163 | + .. code-block:: bash |
| 164 | +
|
| 165 | + pub_ = this->create_publisher<demo_keys_filtering_cpp::msg::KeyedSensorDataMsg>( |
| 166 | + "/robot/sensors", |
| 167 | + rclcpp::QoS(rclcpp::KeepLast(1)).reliable().transient_local()); |
| 168 | +
|
| 169 | +Generating the IDL files |
| 170 | +^^^^^^^^^^^^^^^^^^^^^^^^ |
| 171 | +
|
| 172 | +Generate the corresponding IDL definition from the provided ``KeyedSensorDataMsg.msg`` file, using the ``msg2idl.py`` script from the ``rosidl_adapter`` package. |
| 173 | +
|
| 174 | +.. code-block:: bash |
| 175 | +
|
| 176 | + source /opt/ros/{DISTRO}/setup.bash |
| 177 | + cd ~/tutorial_ws/src/demo_keys_filtering_cpp/msg |
| 178 | + ros2 run rosidl_adapter msg2idl.py KeyedSensorDataMsg.msg |
| 179 | + rm KeyedSensorDataMsg.msg |
| 180 | +
|
| 181 | +Next, annotate the ``sensor_id`` field as ``@key`` in the generated ``KeyedSensorDataMsg.idl``. |
| 182 | +Its content should look like the following: |
| 183 | +
|
| 184 | +.. code-block:: bash |
| 185 | +
|
| 186 | + # KeyedSensorDataMsg.idl |
| 187 | + module demo_keys_filtering_cpp { |
| 188 | + module msg { |
| 189 | + struct KeyedSensorDataMsg { |
| 190 | + @key int16 sensor_id; |
| 191 | + double measurement; |
| 192 | + string data; |
| 193 | + }; |
| 194 | + }; |
| 195 | + }; |
| 196 | +
|
| 197 | +Building the demo package |
| 198 | +^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 199 | +
|
| 200 | +Once the environment has been setup and the demo package sources are available, the demo package can be built. |
| 201 | +Get into the root of the workspace and build it with the following commands: |
| 202 | +
|
| 203 | +.. code-block:: bash |
| 204 | +
|
| 205 | + source /opt/ros/{DISTRO}/setup.bash |
| 206 | + cd ~/tutorial_ws |
| 207 | + colcon build |
| 208 | +
|
| 209 | +
|
| 210 | +Running the demo |
| 211 | +---------------- |
| 212 | +
|
| 213 | +In the demo, different sensors are publishing data to a controller node using a keyed topic as exemplified below: |
| 214 | +
|
| 215 | +.. image:: figures/cft_tutorial_diagram.svg |
| 216 | + :align: center |
| 217 | + :width: 80% |
| 218 | +
|
| 219 | +Run the demo by executing the following commands in separate terminals: |
| 220 | +
|
| 221 | +.. note:: |
| 222 | +
|
| 223 | + If a docker deployment was preferred, it would be necessary to attach the other two terminals to the running docker container before executing the above commands. |
| 224 | + This can be done by running ``docker exec -it <container_name> /bin/bash``. |
| 225 | +
|
| 226 | +.. tabs:: |
| 227 | +
|
| 228 | + .. tab:: Shell 1 (Sensors) |
| 229 | +
|
| 230 | + .. code-block:: bash |
| 231 | +
|
| 232 | + source ~/tutorial_ws/install/setup.bash |
| 233 | + ros2 launch demo_keys_filtering_cpp keyed_sensors_launch.py |
| 234 | +
|
| 235 | + .. tab:: Shell 2 (Controller) |
| 236 | +
|
| 237 | + .. code-block:: bash |
| 238 | +
|
| 239 | + source ~/tutorial_ws/install/setup.bash |
| 240 | + ros2 run demo_keys_filtering_cpp filtered_keyed_controller |
| 241 | +
|
| 242 | +
|
| 243 | +The resulting output should be similar to the following, in which the controller node is only receiving data from the specified sensors, i.e. sensors which sensor_id is in the range [2, 4]. |
| 244 | +In addition, only when the measurement is greater than 60, the controller node will receive data. |
| 245 | +That is specified in the filter expression ``sensor_id >= 2 AND sensor_id <= 4 AND measurement > %0``: |
| 246 | +
|
| 247 | +.. image:: figures/filtered_keyed_topic.gif |
| 248 | + :align: center |
| 249 | + :width: 100% |
| 250 | +
|
| 251 | +Even in a late-joining scenario, the controller node will receive the latest data of the sensors that meet the filtering criteria at the moment it joins the application, which could be crucial depending on the type of the real application. |
| 252 | +
|
| 253 | +Overall, the combination of topic instances with a content filter topic offers significant benefits in terms of data efficiency, scalability, adaptability and resource optimization. |
| 254 | +By leveraging these capabilities, ROS 2 applications can efficiently manage and distribute data in complex distributed environments. |
0 commit comments