v0.1.
Contents
- Writing a Simple Publisher
- Writing a Simple Subscriber
- Using Publishers And Subscribers With Plugins
- Implementing Custom Plugins
In this section, we'll see how to create a publisher node, which opens a .bag file and publishes PointCloud2 messages from it.
This tutorial assumes, that you have created your workspace during <point_cloud_transport> installation.
Before we start, change to the directory and clone this repository:
$ cd ~/point_cloud_transport_ws/src
$ git clone https://github.com/paplhjak/point_cloud_transport_tutorial.gitTake a look at my_publisher.cpp:
#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <point_cloud_transport/point_cloud_transport.h>
#include <rosbag/bag.h>
#include <rosbag/view.h>
int main(int argc, char** argv)
{
ros::init(argc, argv, "point_cloud_publisher");
ros::NodeHandle nh;
point_cloud_transport::PointCloudTransport pct(nh);
point_cloud_transport::Publisher pub = pct.advertise("pct/point_cloud", 10);
rosbag::Bag bag;
bag.open(argv[1], rosbag::bagmode::Read);
ros::Rate loop_rate(5);
for(rosbag::MessageInstance const m: rosbag::View(bag))
{
sensor_msgs::PointCloud2::ConstPtr i = m.instantiate<sensor_msgs::PointCloud2>();
if (i != nullptr)
{
pub.publish(i);
ros::spinOnce();
loop_rate.sleep();
}
if (!nh.ok())
{
break;
}
}
}Now we'll break down the code piece by piece.
Header for including <point_cloud_transport>:
#include <point_cloud_transport/point_cloud_transport.h>Headers for opening .bag file with sensor_msgs::PointCloud2 messages:
#include <rosbag/bag.h>
#include <rosbag/view.h>Initializing the ROS node:
ros::init(argc, argv, "point_cloud_publisher");
ros::NodeHandle nh;Creates PointCloudTransport instance and initializes it with our NodeHandle. 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.
point_cloud_transport::PointCloudTransport pct(nh);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.
point_cloud_transport::Publisher pub = pct.advertise("pct/point_cloud", 10);Opens .bag file given as an argument to the program:
rosbag::Bag bag;
bag.open(argv[1], rosbag::bagmode::Read);Publishes sensor_msgs::PointCloud2 message from the specified .bag with frequency of 5Hz:
ros::Rate loop_rate(5);
for(rosbag::MessageInstance const m: rosbag::View(bag))
{
sensor_msgs::PointCloud2::ConstPtr i = m.instantiate<sensor_msgs::PointCloud2>();
if (i != nullptr)
{
pub.publish(i);
ros::spinOnce();
loop_rate.sleep();
}
if (!nh.ok())
{
break;
}
}To run my_publisher.cpp, open terminal in the root of workspace and run the following:
$ catkin_make_isolated
$ source devel_isolated/setup.bash
$ rosrun point_cloud_transport_tutorial publisher_test /path/to/my_bag.bagOf course, roscore/master must also be running.
In this section, we'll see how to create a subscriber node, which receives PointCloud2 messages and prints the number of points in them.
Take a look at my_subscriber.cpp:
#include <ros/ros.h>
#include <point_cloud_transport/point_cloud_transport.h>
#include <sensor_msgs/PointCloud2.h>
void Callback(const sensor_msgs::PointCloud2ConstPtr& msg)
{
std::cout << "Message received, number of points is: " << msg->width*msg->height << std::endl;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "point_cloud_subscriber", ros::init_options::AnonymousName);
ros::NodeHandle nh;
point_cloud_transport::PointCloudTransport pct(nh);
point_cloud_transport::Subscriber sub = pct.subscribe("pct/point_cloud", 100, Callback);
ros::spin();
return 0;
}Now we'll break down the code piece by piece.
Header for including <point_cloud_transport>:
#include <point_cloud_transport/point_cloud_transport.h>A callback function, which we will bind to the subscriber. Whenever our subscriber receives a message, the Callback function gets executed and number of points in the message (msg->width * msg->height) is printed.
void Callback(const sensor_msgs::PointCloud2ConstPtr& msg)
{
std::cout << "Message received, number of points is: " << msg->width*msg->height << std::endl;
}Initializes the ROS node:
ros::init(argc, argv, "point_cloud_subscriber", ros::init_options::AnonymousName);
ros::NodeHandle nh;Creates PointCloudTransport instance and initializes it with our NodeHandle. 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.
point_cloud_transport::PointCloudTransport pct(nh);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 Callback function, whenever a message is received.
point_cloud_transport::Subscriber sub = pct.subscribe("pct/point_cloud", 100, Callback);To run my_subscriber.cpp, open terminal in the root of workspace and run the following:
$ catkin_make_isolated
$ source devel_isolated/setup.bash
$ rosrun point_cloud_transport_tutorial subscriber_testOf course, roscore/master must also be running.
In this section, we'll first make sure that the nodes are running properly. Later on, we'll change the transport to use Draco compressed format.
Make sure that roscore/master is up and running:
$ roscoreNow we can run the Publisher/Subsriber nodes. To run both start two terminal tabs and enter commands:
$ source devel_isolated/setup.bash
$ rosrun point_cloud_transport_tutorial subscriber_testAnd in the second tab:
$ source devel_isolated/setup.bash
$ rosrun point_cloud_transport_tutorial publisher_test /path/to/my_bag.bagIf both nodes are running properly, you should see the subscriber node start printing out messages similar to:
Message received, number of points is: 63744To list the topics, which are being published and subscribed to, enter command:
$ rostopic list -vThe output should look similar to this:
Published topics:
* /rosout [rosgraph_msgs/Log] 1 publisher
* /pct/point_cloud [sensor_msgs/PointCloud2] 1 publisher
* /rosout_agg [rosgraph_msgs/Log] 1 publisher
Subscribed topics:
* /rosout [rosgraph_msgs/Log] 1 subscriber
* /pct/point_cloud [sensor_msgs/PointCloud2] 1 subscriberTo display the ROS computation graph, enter command:
$ rqt_graphYou should see a graph similar to this:
Currently our nodes are communicating raw sensor_msgs/PointCloud2 messages, so we are not gaining anything over using basic ros::Publisher and ros::Subscriber. We can change that by introducing a new transport.
The <draco_point_cloud_transport> package provides plugin for <point_cloud_transport>, which sends point clouds over the wire encoded through kd-tree or sequantial compression. Notice that draco_point_cloud_transport is not a dependency of your package; point_cloud_transport automatically discovers all transport plugins built in your ROS system.
Assuming you have followed <point_cloud_transport> installation, you should already have <draco_point_cloud_transport> built.
To check which plugins are built on your machine, enter command:
$ rosrun point_cloud_transport list_transportsYou should see output similar to:
Declared transports:
point_cloud_transport/draco
point_cloud_transport/raw
Details:
----------
"point_cloud_transport/draco"
- Provided by package: draco_point_cloud_transport
- Publisher:
This plugin publishes a CompressedPointCloud2 using KD tree compression.
- Subscriber:
This plugin decompresses a CompressedPointCloud2 topic.
----------
"point_cloud_transport/raw"
- Provided by package: point_cloud_transport
- Publisher:
This is the default publisher. It publishes the PointCloud2 as-is on the base topic.
- Subscriber:
This is the default pass-through subscriber for topics of type sensor_msgs/PointCloud2.Shut down your publisher node and restart it. If you list the published topics again and have <draco_point_cloud_transport> installed, you should see a new one:
* /pct/point_cloud/draco [draco_point_cloud_transport/CompressedPointCloud2] 1 publisherNow let's start up a new subscriber, this one using draco transport. The key is that <point_cloud_transport> subscribers check the parameter ~point_cloud_transport for the name of a transport to use in place of "raw". Let's set this parameter and start a subscriber node with name "draco_listener":
$ rosparam set /draco_listener/point_cloud_transport draco
$ rosrun point_cloud_transport_tutorial subscriber_test __name:=draco_listenerIf we check the node graph again:
$ rqt_graphWe can see, that draco_listener is listening to a separate topic carrying compressed messages.
Note that if you just want the draco messages, you can change the parameter globally in-line:
$ rosrun point_cloud_transport_tutorial subscriber_test _point_cloud_transport:=dracoFor 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.
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):
$ rosrun rqt_reconfigure rqt_reconfigureNow 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.
Dynamic Reconfigure has updated the dynamically reconfigurable parameter /pct/point_cloud/draco/quantization_POSITION. You can verify this by running:
rosparam get /pct/point_cloud/draco/quantization_POSITIONThis should display 8.
Full explanation of the reconfigure parameters and an example of how to use them can be found at <draco_point_cloud_transport> repository.
The process of implementing your own plugins is described in a separate repository.

