├── .github └── workflows │ ├── build-and-test.sh │ └── ros2-ci.yml ├── CHANGELOG.rst ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package.xml ├── readme_images ├── rosgraph1.png └── rosgraph2.png ├── resources └── rosbag2_2023_08_05-16_08_51.tar.xz ├── scripts ├── publisher.py └── subscriber_old_school.py └── src ├── my_encoder.cpp ├── my_publisher.cpp └── my_subscriber.cpp /.github/workflows/build-and-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | # Configuration. 5 | export COLCON_WS=~/ws 6 | export COLCON_WS_SRC=${COLCON_WS}/src 7 | export DEBIAN_FRONTEND=noninteractive 8 | export ROS_PYTHON_VERSION=3 9 | 10 | apt update -qq 11 | apt install -qq -y lsb-release wget curl build-essential 12 | 13 | # Dependencies. 14 | echo "deb http://packages.ros.org/ros2-testing/ubuntu `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-testing.list 15 | curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add - 16 | apt-get update -qq 17 | apt-get install -y python3-colcon-common-extensions \ 18 | python3-rosdep python3-vcstool python3-vcstools 19 | 20 | rosdep init 21 | rosdep update 22 | 23 | # Build. 24 | mkdir -p $COLCON_WS_SRC 25 | cp -r $GITHUB_WORKSPACE $COLCON_WS_SRC 26 | cd $COLCON_WS 27 | rosdep install --from-paths ./ -i -y -r --rosdistro $ROS_DISTRO $ROSDEP_ARGS 28 | source /opt/ros/$ROS_DISTRO/setup.bash 29 | colcon build --event-handlers console_direct+ 30 | 31 | # Tests. 32 | colcon test --event-handlers console_direct+ 33 | colcon test-result 34 | -------------------------------------------------------------------------------- /.github/workflows/ros2-ci.yml: -------------------------------------------------------------------------------- 1 | name: ROS2 CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | point_cloud_transport_tutorial_ci: 7 | name: point_cloud_transport_tutorial CI 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | include: 13 | - docker-image: "ubuntu:24.04" 14 | ros-distro: "rolling" 15 | container: 16 | image: ${{ matrix.docker-image }} 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Build and Test 21 | run: .github/workflows/build-and-test.sh 22 | env: 23 | DOCKER_IMAGE: ${{ matrix.docker-image }} 24 | ROS_DISTRO: ${{ matrix.ros-distro }} 25 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package point_cloud_transport_tutorial 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.0.3 (2025-05-21) 6 | ------------------- 7 | * Fixed CI (`#12 `_) 8 | * Use target_link_libraries instead of ament_target_dependencies (`#11 `_) 9 | * Replaced deprecated rcpputils::path (`#10 `_) 10 | * Contributors: Alejandro Hernández Cordero 11 | 12 | 0.0.2 (2023-03-19) 13 | ------------------- 14 | * point_cloud_Transport_plugins is not a build dependency of this package (`#9 `_) 15 | * Added humble CI (`#8 `_) 16 | * Contributors: Alejandro Hernández Cordero, john-maidbot 17 | 18 | 0.0.1 (2023-10-09) 19 | ------------------- 20 | 21 | * Added CI (`#7 `_) 22 | * Installed scripts, some minor fixes and warnings (`#6 `_) 23 | * ROS2 Port (`#2 `_) 24 | Co-authored-by: Alejandro Hernández Cordero 25 | * Report log messages from transport C API. 26 | * Added encoder/decoder tutorial. 27 | * Initial cleanup before releasing. No substantial changes made. 28 | * Contributors: Alejandro Hernández Cordero, john-maidbot 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | 3 | project(point_cloud_transport_tutorial) 4 | 5 | # Default to C++17 6 | if(NOT CMAKE_CXX_STANDARD) 7 | set(CMAKE_CXX_STANDARD 17) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | endif() 10 | 11 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 12 | add_compile_options(-Wall -Wextra -Wpedantic) 13 | endif() 14 | 15 | find_package(ament_cmake_ros REQUIRED) 16 | 17 | find_package(point_cloud_transport REQUIRED) 18 | find_package(rclcpp REQUIRED) 19 | find_package(rosbag2_cpp REQUIRED) 20 | find_package(sensor_msgs REQUIRED) 21 | 22 | include_directories( 23 | include 24 | ) 25 | 26 | # encoder 27 | add_executable(encoder_test src/my_encoder.cpp) 28 | 29 | target_link_libraries(encoder_test PUBLIC 30 | ${sensor_msgs_TARGETS} 31 | point_cloud_transport::point_cloud_transport 32 | rosbag2_cpp::rosbag2_cpp 33 | sensor_msgs::sensor_msgs_library 34 | ) 35 | 36 | 37 | # publisher 38 | add_executable(publisher_test src/my_publisher.cpp) 39 | target_link_libraries(publisher_test PUBLIC 40 | ${sensor_msgs_TARGETS} 41 | point_cloud_transport::point_cloud_transport 42 | rclcpp::rclcpp 43 | rosbag2_cpp::rosbag2_cpp 44 | sensor_msgs::sensor_msgs_library 45 | ) 46 | 47 | # subscriber 48 | add_executable(subscriber_test src/my_subscriber.cpp) 49 | target_link_libraries(subscriber_test PUBLIC 50 | ${sensor_msgs_TARGETS} 51 | point_cloud_transport::point_cloud_transport 52 | rclcpp::rclcpp 53 | sensor_msgs::sensor_msgs_library 54 | ) 55 | 56 | # Install executables 57 | install( 58 | TARGETS 59 | encoder_test 60 | publisher_test 61 | subscriber_test 62 | RUNTIME DESTINATION lib/${PROJECT_NAME} 63 | ) 64 | 65 | install( 66 | DIRECTORY 67 | resources 68 | DESTINATION share/${PROJECT_NAME} 69 | ) 70 | 71 | install(PROGRAMS 72 | scripts/publisher.py 73 | scripts/subscriber_old_school.py 74 | DESTINATION lib/${PROJECT_NAME} 75 | ) 76 | 77 | # linting tests 78 | if(BUILD_TESTING) 79 | find_package(ament_cmake_copyright REQUIRED) 80 | find_package(ament_cmake_cppcheck REQUIRED) 81 | find_package(ament_cmake_cpplint REQUIRED) 82 | find_package(ament_cmake_lint_cmake REQUIRED) 83 | find_package(ament_cmake_uncrustify REQUIRED) 84 | find_package(ament_cmake_xmllint REQUIRED) 85 | 86 | ament_copyright(EXCLUDE ${_linter_excludes}) 87 | ament_cppcheck( 88 | EXCLUDE ${_linter_excludes} 89 | LANGUAGE c++ 90 | ) 91 | ament_cpplint(EXCLUDE ${_linter_excludes}) 92 | ament_lint_cmake() 93 | ament_uncrustify( 94 | LANGUAGE c++ 95 | ) 96 | ament_xmllint() 97 | endif() 98 | 99 | 100 | ament_package() 101 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Any contribution that you make to this repository will 2 | be under the 3-Clause BSD License, as dictated by that 3 | [license](https://opensource.org/licenses/BSD-3-Clause). 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | All rights reserved. 2 | 3 | Software License Agreement (BSD License 2.0) 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # \ 2 | **ROS2 v0.1.** 3 | 4 | _**Contents**_ 5 | 6 | * [Writing a Simple Publisher](#writing-a-simple-publisher) 7 | * [Code of the Publisher](#code-of-the-publisher) 8 | * [Code Explained](#code-of-publisher-explained) 9 | * [Example of Running the Publisher](#example-of-running-the-publisher) 10 | * [Writing a Simple Subscriber](#writing-a-simple-subscriber) 11 | * [Code of the Subscriber](#code-of-the-subscriber) 12 | * [Code Explained](#code-of-subscriber-explained) 13 | * [Example of Running the Subscriber](#example-of-running-the-subscriber) 14 | * [Using Publishers And Subscribers With Plugins](#using-publishers-and-subscribers-with-plugins) 15 | * [Running the Publisher and Subsriber](#running-the-publisher-and-subsriber) 16 | * [Changing the Transport Used](#changing-the-transport-used) 17 | * [Changing Transport Behavior](#changing-transport-behavior) 18 | * [Implementing Custom Plugins](#implementing-custom-plugins) 19 | 20 | # Writing a Simple Publisher 21 | In this section, we'll see how to create a publisher node, which opens a ROS 2 bag and publishes `PointCloud2` messages from it. 22 | 23 | This tutorial assumes that you have created your workspace containing [](https://github.com/ros-perception/point_cloud_transport) and [](https://github.com/ros-perception/point_cloud_transport_plugins) 24 | 25 | Before we start, change to the directory, clone this repository, and unzip the example rosbag in the resources folder: 26 | ```bash 27 | $ cd ~//src 28 | $ git clone https://github.com/ros-perception/point_cloud_transport_tutorial 29 | $ cd point_cloud_transport_tutorial 30 | $ tar -C resources/ -xvf resources/rosbag2_2023_08_05-16_08_51.tar.xz 31 | $ cd ~/ 32 | $ colcon build --merge-install --event-handlers console_direct+ 33 | ``` 34 | 35 | ## Code of the Publisher 36 | Take a look at my_publisher.cpp 37 | ```cpp 38 | #include 39 | 40 | // for reading rosbag 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | int main(int argc, char ** argv) 50 | { 51 | rclcpp::init(argc, argv); 52 | 53 | auto node = std::make_shared("point_cloud_publisher"); 54 | 55 | point_cloud_transport::PointCloudTransport pct(node); 56 | point_cloud_transport::Publisher pub = pct.advertise("pct/point_cloud", 100); 57 | 58 | const std::string bagged_cloud_topic = "/point_cloud"; 59 | const std::string shared_directory = ament_index_cpp::get_package_share_directory( 60 | "point_cloud_transport_tutorial"); 61 | const std::string bag_file = shared_directory + "/resources/rosbag2_2023_08_05-16_08_51"; 62 | 63 | // boiler-plate to tell rosbag2 how to read our bag 64 | rosbag2_storage::StorageOptions storage_options; 65 | storage_options.uri = bag_file; 66 | storage_options.storage_id = "mcap"; 67 | rosbag2_cpp::ConverterOptions converter_options; 68 | converter_options.input_serialization_format = "cdr"; 69 | converter_options.output_serialization_format = "cdr"; 70 | 71 | // open the rosbag 72 | rosbag2_cpp::readers::SequentialReader reader; 73 | reader.open(storage_options, converter_options); 74 | 75 | sensor_msgs::msg::PointCloud2 cloud_msg; 76 | rclcpp::Serialization cloud_serialization; 77 | while (reader.has_next() && rclcpp::ok()) { 78 | // get serialized data 79 | auto serialized_message = reader.read_next(); 80 | rclcpp::SerializedMessage extracted_serialized_msg(*serialized_message->serialized_data); 81 | if (serialized_message->topic_name == bagged_cloud_topic) { 82 | // deserialize and convert to message 83 | cloud_serialization.deserialize_message(&extracted_serialized_msg, &cloud_msg); 84 | // publish the message 85 | pub.publish(cloud_msg); 86 | rclcpp::spin_some(node); 87 | rclcpp::sleep_for(std::chrono::milliseconds(100)); 88 | } 89 | } 90 | reader.close(); 91 | 92 | node.reset(); 93 | rclcpp::shutdown(); 94 | } 95 | ``` 96 | 97 | ## Code of Publisher Explained 98 | Now we'll break down the code piece by piece. 99 | 100 | Header for including [](https://github.com/ros-perception/point_cloud_transport): 101 | ```cpp 102 | #include 103 | ``` 104 | 105 | Creates *PointCloudTransport* instance and initializes it with our *Node* shared pointer. Methods of *PointCloudTransport* can later be used to create point cloud publishers and subscribers similar to how methods of *Node* are used to create generic publishers and subscribers. 106 | 107 | ```cpp 108 | point_cloud_transport::PointCloudTransport pct(node); 109 | ``` 110 | 111 | Uses *PointCloudTransport* method to create a publisher on base topic *"pct/point_cloud"*. Depending on whether more plugins are built, additional (per-plugin) topics derived from the base topic may also be advertised. The second argument is the size of our publishing queue. 112 | 113 | ```cpp 114 | point_cloud_transport::Publisher pub = pct.advertise("pct/point_cloud", 10); 115 | ``` 116 | 117 | Publishes sensor_msgs::PointCloud2 message from the specified rosbag: 118 | ```cpp 119 | sensor_msgs::msg::PointCloud2 cloud_msg; 120 | //... rosbag boiler plate to populate cloud_msg ... 121 | // publish the message 122 | pub.publish(cloud_msg); 123 | // spin the node... 124 | rclcpp::spin_some(node); 125 | // repeat... 126 | ``` 127 | 128 | ## Example of Running the Publisher 129 | To run [my_publisher.cpp](src/my_publisher.cpp) open terminal in the root of workspace and run the following: 130 | 131 | ```bash 132 | $ source install/setup.bash 133 | $ ros2 run point_cloud_transport_tutorial publisher_test 134 | ``` 135 | 136 | # Writing a Simple Subscriber 137 | In this section, we'll see how to create a subscriber node, which receives `PointCloud2` messages and prints the number of points in them. 138 | 139 | ## Code of the Subscriber 140 | Take a look at [my_subscriber.cpp](src/my_subscriber.cpp): 141 | 142 | ```cpp 143 | #include 144 | #include 145 | #include 146 | 147 | int main(int argc, char ** argv) 148 | { 149 | rclcpp::init(argc, argv); 150 | auto node = std::make_shared("point_cloud_subscriber"); 151 | 152 | point_cloud_transport::PointCloudTransport pct(node); 153 | point_cloud_transport::Subscriber pct_sub = pct.subscribe( 154 | "pct/point_cloud", 100, 155 | [node](const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg) 156 | { 157 | RCLCPP_INFO_STREAM( 158 | node->get_logger(), 159 | "Message received, number of points is: " << msg->width * msg->height); 160 | }, {}); 161 | 162 | RCLCPP_INFO_STREAM(node->get_logger(), "Waiting for point_cloud message..."); 163 | 164 | rclcpp::spin(node); 165 | 166 | rclcpp::shutdown(); 167 | 168 | return 0; 169 | } 170 | ``` 171 | 172 | ## Code of Subscriber Explained 173 | Now we'll break down the code piece by piece. 174 | 175 | Header for including [](https://github.com/ros-perception/point_cloud_transport): 176 | 177 | ```cpp 178 | #include 179 | ``` 180 | 181 | Initializes the ROS node: 182 | 183 | ```cpp 184 | rclcpp::init(argc, argv); 185 | auto node = rclcpp::Node::make_shared("point_cloud_subscriber"); 186 | ``` 187 | 188 | Creates *PointCloudTransport* instance and initializes it with our *Node*. Methods of *PointCloudTransport* can later be used to create point cloud publishers and subscribers similar to how methods of *NodeHandle* are used to create generic publishers and subscribers. 189 | 190 | ```cpp 191 | point_cloud_transport::PointCloudTransport pct(node); 192 | ``` 193 | 194 | Uses *PointCloudTransport* method to create a subscriber on base topic *"pct/point_cloud"*. The second argument is the size of our subscribing queue. The third argument tells the subscriber to execute lambda function whenever a message is received. 195 | 196 | ```cpp 197 | point_cloud_transport::Subscriber pct_sub = pct.subscribe( 198 | "pct/point_cloud", 100, 199 | [node](const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg) 200 | { 201 | RCLCPP_INFO_STREAM( 202 | node->get_logger(), 203 | "draco message received, number of points is: " << msg->width * msg->height); 204 | }, {}); 205 | ``` 206 | 207 | ### Select a specific transport 208 | 209 | Or you can select a specific transport using the *TransportHint* class. Creates a *TransportHint* shared pointer. 210 | This is how to tell the subscriber that we want to subscribe to a particular transport 211 | (in this case "pct/point_cloud/draco"), rather than the raw "pct/point_cloud" topic. 212 | 213 | ```cpp 214 | auto transport_hint = std::make_shared("draco"); 215 | ``` 216 | 217 | Uses *PointCloudTransport* method to create a subscriber on base topic *"pct/point_cloud"* and add the `transport_hint` variable as the last argument. 218 | 219 | ```cpp 220 | auto transport_hint = std::make_shared("draco"); 221 | point_cloud_transport::Subscriber pct_sub = pct.subscribe( 222 | "pct/point_cloud", 100, 223 | [node](const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg) 224 | { 225 | RCLCPP_INFO_STREAM( 226 | node->get_logger(), 227 | "draco message received, number of points is: " << msg->width * msg->height); 228 | }, {}, transport_hint.get()); 229 | ``` 230 | 231 | ## Example of Running the Subscriber 232 | To run my_subscriber.cpp, open terminal in the root of workspace and run the following: 233 | 234 | ```bash 235 | $ source install/setup.bash 236 | $ ros2 run point_cloud_transport_tutorial subscriber_test --ros-args -p point_cloud_transport:=draco 237 | ``` 238 | 239 | The `point_cloud_transport` parameter is read by the point_cloud_transport library. The complexity of the parameters is hidden in the library. 240 | 241 | # Using Publishers And Subscribers With Plugins 242 | 243 | ## Running the Publisher and Subsriber 244 | 245 | Now we can run the Publisher/Subsriber nodes. To run both start two terminal tabs and enter commands: 246 | 247 | ```bash 248 | $ source install/setup.bash 249 | $ ros2 run point_cloud_transport_tutorial subscriber_test 250 | ``` 251 | 252 | And in the second tab: 253 | 254 | ```bash 255 | $ source install/setup.bash 256 | $ ros2 run point_cloud_transport_tutorial publisher_test 257 | $ # or choose which plugin you want load (a.k.a. whitelist them). 258 | $ ros2 run point_cloud_transport_tutorial publisher_test --ros-args -p pct.point_cloud.enable_pub_plugins:=["point_cloud_transport/draco"] 259 | ``` 260 | 261 | If both nodes are running properly, you should see the subscriber node start printing out messages similar to: 262 | 263 | ```bash 264 | Message received, number of points is: XXX 265 | ``` 266 | 267 | To list the topics, which are being published and subscribed to, enter command: 268 | ```bash 269 | $ ros2 topic list 270 | ``` 271 | 272 | The output should look similar to this: 273 | ```bash 274 | Published topics: 275 | * /parameter_events [rcl_interfaces/msg/ParameterEvent] 3 publishers 276 | * /pct/point_cloud [sensor_msgs/msg/PointCloud2] 1 publisher 277 | * /pct/point_cloud/draco [point_cloud_interfaces/msg/CompressedPointCloud2] 1 publisher 278 | * /pct/point_cloud/zlib [point_cloud_interfaces/msg/CompressedPointCloud2] 1 publisher 279 | * /rosout [rcl_interfaces/msg/Log] 3 publishers 280 | 281 | Subscribed topics: 282 | * /parameter_events [rcl_interfaces/msg/ParameterEvent] 2 subscribers 283 | * /pct/point_cloud/draco [point_cloud_interfaces/msg/CompressedPointCloud2] 1 subscriber 284 | ``` 285 | 286 | To display the ROS computation graph, enter command: 287 | 288 | ```bash 289 | $ ros2 run rqt_graph rqt_graph 290 | ``` 291 | You should see a graph similar to this: 292 | 293 | ![Graph1](https://github.com/ros-perception/point_cloud_transport_tutorial/blob/rolling/readme_images/rosgraph1.png) 294 | 295 | ## Changing the Transport Used 296 | 297 | To check which plugins are built on your machine, enter command: 298 | 299 | ```bash 300 | $ ros2 run point_cloud_transport list_transports 301 | ``` 302 | 303 | You should see output similar to: 304 | 305 | ```bash 306 | Declared transports: 307 | point_cloud_transport/draco 308 | point_cloud_transport/raw 309 | 310 | Details: 311 | ---------- 312 | "point_cloud_transport/draco" 313 | - Provided by package: draco_point_cloud_transport 314 | - Publisher: 315 | This plugin publishes a CompressedPointCloud2 using KD tree compression. 316 | 317 | - Subscriber: 318 | This plugin decompresses a CompressedPointCloud2 topic. 319 | 320 | ---------- 321 | "point_cloud_transport/raw" 322 | - Provided by package: point_cloud_transport 323 | - Publisher: 324 | This is the default publisher. It publishes the PointCloud2 as-is on the base topic. 325 | 326 | - Subscriber: 327 | This is the default pass-through subscriber for topics of type sensor_msgs/PointCloud2. 328 | ``` 329 | 330 | Shut down your publisher node and restart it. If you list the published topics again and have [](https://github.com/ros-perception/point_cloud_transport_plugins) installed, you should see: 331 | 332 | ```bash 333 | * /pct/point_cloud/draco [draco_point_cloud_transport/CompressedPointCloud2] 1 publisher 334 | ``` 335 | 336 | ```bash 337 | ros2 run point_cloud_transport_tutorial my_subscriber --ros-args -r __node:=draco_listener -p point_cloud_transport:= 338 | ros2 run point_cloud_transport_tutorial my_subscriber --ros-args -r __node:=draco_listener -p point_cloud_transport:=draco 339 | ``` 340 | 341 | If we check the node graph again: 342 | 343 | ```bash 344 | rqt_graph 345 | ``` 346 | 347 | ![Graph2](https://github.com/ros-perception/point_cloud_transport_tutorial/blob/rolling/readme_images/rosgraph2.png) 348 | 349 | ## Changing Transport Behavior 350 | For a particular transport, we may want to tweak settings such as compression level and speed, quantization of particular attributes of point cloud, etc. Transport plugins can expose such settings through `rqt_reconfigure`. For example, `/point_cloud_transport/draco/` allows you to change multiple parameters of the compression on the fly. 351 | 352 | For now let's adjust the position quantization. By default, "draco" transport uses quantization of 14 bits, allowing 16384 distinquishable positions in each axis; let's change it to 8 bits (256 positions): 353 | 354 | ```bash 355 | $ ros2 run rqt_reconfigure rqt_reconfigure 356 | ``` 357 | 358 | Now pick `/pct/point_cloud/draco` in the drop-down menu and move the quantization_POSITION slider down to 8. If you visualize the messages, such as in RVIZ, you should be able to see the level of detail of the point cloud drop. 359 | 360 | Dynamic Reconfigure has updated the dynamically reconfigurable parameter `/pct/point_cloud/draco/quantization_POSITION`. You can verify this by running: 361 | 362 | ``` bash 363 | ros2 param get /point_cloud_subscriber /pct/point_cloud/draco/quantization_POSITION 364 | ``` 365 | 366 | This should display 8. 367 | 368 | Full explanation of the reconfigure parameters and an example of how to use them can be found at [](https://github.com/ros-perception/point_cloud_transport_plugins) repository. 369 | 370 | 371 | ### Whitelist point cloud transport 372 | 373 | This allows you to specify plugins you do want to load (a.k.a. whitelist them). 374 | 375 | ```bash 376 | ros2 run point_cloud_transport_tutorial publisher_test --ros-args -p pct.point_cloud.enable_pub_plugins:=["point_cloud_transport/zlib"] 377 | ``` 378 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | point_cloud_transport_tutorial 3 | 4 | 0.0.3 5 | 6 | Tutorial for point_cloud_transport. 7 | 8 | Jakub Paplham 9 | 10 | John D'Angelo 11 | 12 | BSD 13 | 14 | ament_cmake_ros 15 | 16 | point_cloud_transport 17 | rclcpp 18 | rosbag2_cpp 19 | sensor_msgs 20 | 21 | point_cloud_transport_plugins 22 | point_cloud_transport 23 | rclcpp 24 | rosbag2_cpp 25 | sensor_msgs 26 | 27 | ament_cmake_copyright 28 | ament_cmake_cppcheck 29 | ament_cmake_cpplint 30 | ament_cmake_lint_cmake 31 | ament_cmake_uncrustify 32 | ament_cmake_xmllint 33 | 34 | 35 | ament_cmake 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /readme_images/rosgraph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-perception/point_cloud_transport_tutorial/89521c4d7e22af4307098784d60f551fc7ed0783/readme_images/rosgraph1.png -------------------------------------------------------------------------------- /readme_images/rosgraph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-perception/point_cloud_transport_tutorial/89521c4d7e22af4307098784d60f551fc7ed0783/readme_images/rosgraph2.png -------------------------------------------------------------------------------- /resources/rosbag2_2023_08_05-16_08_51.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-perception/point_cloud_transport_tutorial/89521c4d7e22af4307098784d60f551fc7ed0783/resources/rosbag2_2023_08_05-16_08_51.tar.xz -------------------------------------------------------------------------------- /scripts/publisher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) 2023 Open Source Robotics Foundation, Inc. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions 7 | # are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # * Neither the name of the TU Darmstadt nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | # POSSIBILITY OF SUCH DAMAGE. 31 | 32 | """Publisher that automatically publishes to all declared transports.""" 33 | 34 | import point_cloud_transport_py 35 | 36 | import time 37 | import sys 38 | 39 | import rclpy 40 | from rclpy.node import Node 41 | from rclpy.serialization import deserialize_message, serialize_message 42 | from sensor_msgs.msg import PointCloud2 43 | import rosbag2_py 44 | from rosidl_runtime_py.utilities import get_message 45 | from point_cloud_transport_py.common import pointCloud2ToString 46 | 47 | def pointCloud2ToString(msg: PointCloud2): 48 | buffer = serialize_message(msg) 49 | return buffer 50 | 51 | if __name__ == '__main__': 52 | 53 | rclpy.init(args=sys.argv) 54 | 55 | bag_path = sys.argv[1] 56 | serialization_format='cdr' 57 | 58 | storage_options = rosbag2_py.StorageOptions( 59 | uri=bag_path) 60 | 61 | converter_options = rosbag2_py.ConverterOptions( 62 | input_serialization_format=serialization_format, 63 | output_serialization_format=serialization_format) 64 | 65 | reader = rosbag2_py.SequentialReader() 66 | reader.open(storage_options, converter_options) 67 | 68 | topic_types = reader.get_all_topics_and_types() 69 | 70 | # Create a map for quicker lookup 71 | type_map = {topic_types[i].name: topic_types[i].type for i in range(len(topic_types))} 72 | 73 | pct = point_cloud_transport_py.PointCloudTransport("point_cloud_transport", ""); 74 | pub = pct.advertise("pct/point_cloud", 100); 75 | 76 | try: 77 | while reader.has_next(): 78 | (topic, data, t) = reader.read_next() 79 | msg_type = get_message(type_map[topic]) 80 | msg = deserialize_message(data, msg_type) 81 | pub.publish(pointCloud2ToString(msg)) 82 | time.sleep(0.1) 83 | except Exception as e: 84 | print('Error in publisher node!') 85 | print(e) 86 | finally: 87 | # if publisher_node is not None: 88 | # publisher_node.destroy_node() 89 | rclpy.shutdown() 90 | -------------------------------------------------------------------------------- /scripts/subscriber_old_school.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) 2023 Open Source Robotics Foundation, Inc. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions 7 | # are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # * Neither the name of the TU Darmstadt nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | # POSSIBILITY OF SUCH DAMAGE. 31 | 32 | """Subscriber automatically converting from any transport to raw.""" 33 | 34 | from point_cloud_transport_py import PointCloudCodec, VectorString 35 | from point_cloud_transport_py.common import stringToMsgType, stringToPointCloud2, TransportInfo 36 | 37 | from rclpy.node import Node 38 | from rclpy.qos import qos_profile_sensor_data 39 | from sensor_msgs.msg import PointCloud2 40 | 41 | 42 | def _get_loadable_transports(codec: PointCloudCodec): 43 | transports = VectorString() 44 | names = VectorString() 45 | codec.getLoadableTransports(transports, names) 46 | return dict(zip(transports, names)) 47 | 48 | 49 | def _get_topic_to_subscribe(codec, base_topic, transport_name): 50 | (topic, name, data_type) = codec.getTopicToSubscribe( 51 | base_topic, transport_name) 52 | 53 | if len(data_type) == 0: 54 | return None 55 | 56 | return TransportInfo(name, topic, data_type) 57 | 58 | 59 | class Subscriber(Node): 60 | 61 | def __init__(self): 62 | node_name = 'point_cloud_transport_subscriber' 63 | super().__init__(node_name) 64 | 65 | self.base_topic = '/pct/point_cloud' 66 | self.transport = self.get_parameter_or('transport', 'draco') 67 | self.codec = PointCloudCodec() 68 | 69 | transports = _get_loadable_transports(self.codec) 70 | if self.transport not in transports and self.transport not in transports.values(): 71 | raise RuntimeError( 72 | 'Point cloud transport "%s" not found.' % (self.transport,)) 73 | 74 | self.transport_info = _get_topic_to_subscribe( 75 | self.codec, self.base_topic, self.transport) 76 | if self.transport_info is None: 77 | raise RuntimeError( 78 | 'Point cloud transport "%s" not found.' % (self.transport,)) 79 | 80 | # subscribe to compressed, serialized msg 81 | self.subscriber = self.create_subscription(stringToMsgType( 82 | self.transport_info.data_type), self.transport_info.topic, self.cb, qos_profile_sensor_data) 83 | 84 | def cb(self, cloud): 85 | print(cloud.height * cloud.width) 86 | 87 | 88 | if __name__ == '__main__': 89 | import rclpy 90 | import sys 91 | 92 | rclpy.init(args=sys.argv) 93 | 94 | subscriber_node = None 95 | try: 96 | subscriber_node = Subscriber() 97 | rclpy.spin(subscriber_node) 98 | except Exception as e: 99 | print(e) 100 | finally: 101 | if subscriber_node is not None: 102 | subscriber_node.destroy_node() 103 | rclpy.shutdown() 104 | -------------------------------------------------------------------------------- /src/my_encoder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Czech Technical University in Prague 2 | // Copyright (c) 2023, Open Source Robotics Foundation, Inc. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name of the Willow Garage nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | int main(int /*argc*/, char ** /*argv*/) 41 | { 42 | auto logger_ = rclcpp::get_logger("my_encoder"); 43 | 44 | point_cloud_transport::PointCloudCodec codec; 45 | 46 | // set the transport to use 47 | const std::string transport = "draco"; 48 | RCLCPP_INFO(logger_, "Using transport: %s", transport.c_str()); 49 | 50 | const std::string bagged_cloud_topic = "/point_cloud"; 51 | const std::string shared_directory = ament_index_cpp::get_package_share_directory( 52 | "point_cloud_transport_tutorial"); 53 | const std::string bag_file = shared_directory + "/resources/rosbag2_2023_08_05-16_08_51"; 54 | 55 | // boiler-plate to tell rosbag2 how to read our bag 56 | rosbag2_storage::StorageOptions storage_options; 57 | storage_options.uri = bag_file; 58 | storage_options.storage_id = "mcap"; 59 | rosbag2_cpp::ConverterOptions converter_options; 60 | converter_options.input_serialization_format = "cdr"; 61 | converter_options.output_serialization_format = "cdr"; 62 | 63 | // open the rosbag 64 | rosbag2_cpp::readers::SequentialReader reader; 65 | reader.open(storage_options, converter_options); 66 | 67 | sensor_msgs::msg::PointCloud2 cloud_msg; 68 | rclcpp::Serialization cloud_serialization; 69 | while (reader.has_next()) { 70 | // get serialized data 71 | auto serialized_message = reader.read_next(); 72 | rclcpp::SerializedMessage extracted_serialized_msg(*serialized_message->serialized_data); 73 | if (serialized_message->topic_name == bagged_cloud_topic) { 74 | // deserialize and convert to ros2 message 75 | const size_t original_serialized_size = extracted_serialized_msg.size(); 76 | cloud_serialization.deserialize_message(&extracted_serialized_msg, &cloud_msg); 77 | const size_t original_deserialized_size = cloud_msg.data.size(); 78 | 79 | // 80 | // Encode using C++ API 81 | // 82 | 83 | // encode/decode communicate via a serialized message. This was done to support arbitrary 84 | // encoding formats and to make it easy to bind to other languages (since the serialized 85 | // message is simply a uchar buffer and its size) 86 | rclcpp::SerializedMessage compressed_msg; 87 | const bool encode_success = codec.encode(transport, cloud_msg, compressed_msg); 88 | 89 | // BUT encodeTyped/decodeTyped are also available if you would rather work with the actual 90 | // encoded message type (which may vary depending on the transport being used). 91 | 92 | if (encode_success) { 93 | RCLCPP_INFO( 94 | logger_, "ENCODE Raw size: %zu, compressed size: %zu, ratio: %.2f %%, transport type: %s", 95 | original_serialized_size, compressed_msg.size(), 96 | 100.0 * compressed_msg.size() / original_serialized_size, 97 | transport.c_str()); 98 | } else { 99 | RCLCPP_ERROR(logger_, "Failed to encode message"); 100 | } 101 | 102 | // 103 | // Decode using C++ API 104 | // 105 | sensor_msgs::msg::PointCloud2 decoded_msg; 106 | const bool decode_success = codec.decode(transport, compressed_msg, decoded_msg); 107 | if (decode_success) { 108 | RCLCPP_INFO( 109 | logger_, "DECODE Raw size: %zu, compressed size: %zu, ratio: %.2f %%, transport type: %s", 110 | original_deserialized_size, decoded_msg.data.size(), 111 | 100.0 * decoded_msg.data.size() / original_deserialized_size, 112 | transport.c_str()); 113 | } else { 114 | RCLCPP_ERROR(logger_, "Failed to decode message"); 115 | } 116 | } 117 | } 118 | reader.close(); 119 | } 120 | -------------------------------------------------------------------------------- /src/my_publisher.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Czech Technical University in Prague 2 | // Copyright (c) 2023, Open Source Robotics Foundation, Inc. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name of the Willow Garage nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // for reading rosbag 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | int main(int argc, char ** argv) 49 | { 50 | rclcpp::init(argc, argv); 51 | 52 | auto node = std::make_shared("point_cloud_publisher"); 53 | 54 | point_cloud_transport::PointCloudTransport pct(node); 55 | point_cloud_transport::Publisher pub = pct.advertise("pct/point_cloud", 100); 56 | 57 | const std::string bagged_cloud_topic = "/point_cloud"; 58 | const std::string shared_directory = ament_index_cpp::get_package_share_directory( 59 | "point_cloud_transport_tutorial"); 60 | std::string bag_file = shared_directory + "/resources/rosbag2_2023_08_05-16_08_51"; 61 | 62 | if (argc > 1) { 63 | bag_file = argv[1]; 64 | } 65 | 66 | if (!std::filesystem::exists(bag_file)) { 67 | std::cout << "Not able to open file [" << bag_file << "]" << '\n'; 68 | return -1; 69 | } 70 | 71 | std::cout << "Reading [" << bag_file << "] bagfile" << '\n'; 72 | 73 | // boiler-plate to tell rosbag2 how to read our bag 74 | rosbag2_storage::StorageOptions storage_options; 75 | storage_options.uri = bag_file; 76 | storage_options.storage_id = "mcap"; 77 | rosbag2_cpp::ConverterOptions converter_options; 78 | converter_options.input_serialization_format = "cdr"; 79 | converter_options.output_serialization_format = "cdr"; 80 | 81 | // open the rosbag 82 | rosbag2_cpp::readers::SequentialReader reader; 83 | reader.open(storage_options, converter_options); 84 | 85 | sensor_msgs::msg::PointCloud2 cloud_msg; 86 | rclcpp::Serialization cloud_serialization; 87 | while (reader.has_next() && rclcpp::ok()) { 88 | // get serialized data 89 | auto serialized_message = reader.read_next(); 90 | rclcpp::SerializedMessage extracted_serialized_msg(*serialized_message->serialized_data); 91 | if (serialized_message->topic_name == bagged_cloud_topic) { 92 | // deserialize and convert to ros2 message 93 | cloud_serialization.deserialize_message(&extracted_serialized_msg, &cloud_msg); 94 | // publish the message 95 | pub.publish(cloud_msg); 96 | rclcpp::spin_some(node); 97 | rclcpp::sleep_for(std::chrono::milliseconds(100)); 98 | } 99 | } 100 | reader.close(); 101 | 102 | node.reset(); 103 | rclcpp::shutdown(); 104 | } 105 | -------------------------------------------------------------------------------- /src/my_subscriber.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Czech Technical University in Prague 2 | // Copyright (c) 2023, Open Source Robotics Foundation, Inc. All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 14 | // * Neither the name of the Willow Garage nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | int main(int argc, char ** argv) 36 | { 37 | rclcpp::init(argc, argv); 38 | auto node = std::make_shared("point_cloud_subscriber"); 39 | 40 | point_cloud_transport::PointCloudTransport pct(node); 41 | point_cloud_transport::Subscriber pct_sub = pct.subscribe( 42 | "pct/point_cloud", 100, 43 | [node](const sensor_msgs::msg::PointCloud2::ConstSharedPtr & msg) 44 | { 45 | RCLCPP_INFO_STREAM( 46 | node->get_logger(), 47 | "Message received, number of points is: " << msg->width * msg->height); 48 | }, {}); 49 | 50 | RCLCPP_INFO_STREAM(node->get_logger(), "Waiting for point_cloud message..."); 51 | 52 | rclcpp::spin(node); 53 | 54 | rclcpp::shutdown(); 55 | 56 | return 0; 57 | } 58 | --------------------------------------------------------------------------------