[Documentation] [TitleIndex] [WordIndex

Only released in EOL distros:  

Documentation

This stack exists because I found that being only able to make transport_plugin and shared memory for sensor_msgs::Image was a bit limiting.

As a result, the image_transport framework has been adapted to use templates, and to make it generic. This has then been extended to a generic shared memory transport and a udp multicasting transport. The compressed and theora transport, have also been ported to this framework while still keeping them specialised to Image.

The resulting framework allows to create a starting set of plugins (raw sharedmem and udp multicasting) for (e.g) PointCloud using the following code:

manifest.cpp:

   1 #include <pluginlib/class_list_macros.h>
   2 #include <message_transport/raw_publisher.h>
   3 #include <message_transport/raw_subscriber.h>
   4 #include <sharedmem_transport/sharedmem_publisher.h>
   5 #include <sharedmem_transport/sharedmem_subscriber.h>
   6 #include <udpmulti_transport/udpmulti_publisher.h>
   7 #include <udpmulti_transport/udpmulti_subscriber.h>
   8 #include <pointcloud_transport_base/decimated_publisher.h>
   9 #include <pointcloud_transport_base/decimated_subscriber.h>
  10 #include <sensor_msgs/PointCloud.h>
  11 
  12 // Raw class
  13 PLUGINLIB_REGISTER_CLASS(raw_pub, 
  14     message_transport::RawPublisher<sensor_msgs::PointCloud>,  
  15     message_transport::PublisherPlugin<sensor_msgs::PointCloud>)
  16 PLUGINLIB_REGISTER_CLASS(raw_sub, 
  17     message_transport::RawSubscriber<sensor_msgs::PointCloud>, 
  18     message_transport::SubscriberPlugin<sensor_msgs::PointCloud>)
  19 
  20 // Sharedmem class
  21 PLUGINLIB_REGISTER_CLASS(sharedmem_pub, 
  22     sharedmem_transport::SharedmemPublisherWithHeader<sensor_msgs::PointCloud>, 
  23     message_transport::PublisherPlugin<sensor_msgs::PointCloud>)
  24 PLUGINLIB_REGISTER_CLASS(sharedmem_sub, 
  25     sharedmem_transport::SharedmemSubscriber<sensor_msgs::PointCloud>, 
  26     message_transport::SubscriberPlugin<sensor_msgs::PointCloud>)
  27 
  28 // Decimated class
  29 PLUGINLIB_REGISTER_CLASS(decimated_pub, 
  30     decimated_transport::DecimatedPublisher, 
  31     message_transport::PublisherPlugin<sensor_msgs::PointCloud>)
  32 PLUGINLIB_REGISTER_CLASS(decimated_sub, 
  33     decimated_transport::DecimatedSubscriber, 
  34     message_transport::SubscriberPlugin<sensor_msgs::PointCloud>)
  35 
  36 // UDP multi-casting class
  37 PLUGINLIB_REGISTER_CLASS(udpmulti_pub, 
  38     udpmulti_transport::UDPMultiPublisher<sensor_msgs::PointCloud>, 
  39     message_transport::PublisherPlugin<sensor_msgs::PointCloud>)
  40 PLUGINLIB_REGISTER_CLASS(udpmulti_sub, 
  41     udpmulti_transport::UDPMultiSubscriber<sensor_msgs::PointCloud>, 
  42     message_transport::SubscriberPlugin<sensor_msgs::PointCloud>)

Note that a specialised list_transport must be compiled for each message classes where plugins are developed: list_transport.cpp:

   1 #include <message_transport/list_transport.h>
   2 #include <sensor_msgs/PointCloud.h>
   3 
   4 using namespace message_transport;
   5 
   6 int main(int argc, char** argv)
   7 {
   8     LIST_TRANSPORT("pointcloud_transport",sensor_msgs::PointCloud);
   9 
  10     return 0;
  11 }

As an instance, the plugin declaration for the compressed images starts with:

   1 class CompressedPublisher : public    
   2     message_transport::SimplePublisherPlugin<sensor_msgs::Image,
   3         sensor_msgs::CompressedImage>
   4 { ...
   5 
   6 protected:
   7   virtual void publish(const sensor_msgs::Image& message,
   8           const message_transport::SimplePublisherPlugin<sensor_msgs::Image,
   9                 sensor_msgs::CompressedImage>::PublishFn& publish_fn)
  10 const ;
  11 };

For the moment, the message_transport stack contains: message_transport_common

Other plugins are obviously possible and easy to write.

Writing a publisher

The example code below should be pretty trivial to adapt to any need:

   1 #include <ros/ros.h>
   2 #include <sensor_msgs/PointCloud.h>
   3 #include <message_transport/message_transport.h>
   4 
   5 class PointCloudPublisher {
   6   protected:
   7     ros::NodeHandle n_;
   8     message_transport::MessageTransport<sensor_msgs::PointCloud> it_;
   9     message_transport::Publisher pcmsg_pub_;
  10     sensor_msgs::PointCloud pointcloud;
  11   public:
  12     PointCloudPublisher(ros::NodeHandle &n) : n_(n), 
  13       // Register the transport to the pointcloud_transport plugin
  14       // set, and define the type of object transported
  15       it_(n_,"pointcloud_transport","sensor_msgs::PointCloud") 
  16     {
  17       // Advertise like a normal topic
  18       pcmsg_pub_ = it_.advertise("pc_source",1);
  19     } 
  20 
  21     ~PointCloudPublisher() {}
  22 
  23     void mainloop() {
  24       ros::Rate loop_rate(30);
  25       while (ros::ok()) {
  26         pointcloud.header.stamp = ros::Time::now();
  27         // Fill-in other point cloud data, and publish normally
  28         pcmsg_pub_.publish(pointcloud);
  29         ros::spinOnce();
  30         loop_rate.sleep();
  31       }
  32     }
  33 };
  34 
  35 int main(int argc, char** argv) {
  36   ros::init(argc, argv, "test_publisher");
  37   ros::NodeHandle n;
  38   PointCloudPublisher ic(n);
  39   ic.mainloop();
  40   return 0;
  41 }

Writing a subscriber

The example code below should be pretty trivial to adapt to any need:

   1 #include <ros/ros.h>
   2 #include <message_transport/message_transport.h>
   3 #include <sensor_msgs/PointCloud.h>
   4 
   5 void callback(const sensor_msgs::PointCloudConstPtr& pointcloud)
   6 {
   7   // process the pointcloud
   8 }
   9 
  10 int main(int argc, char** argv) {
  11   std::string transport = std::string((argc > 1) ? argv[1] : "raw");
  12   ros::init(argc, argv, "test_receiver", ros::init_options::AnonymousName);
  13   ros::NodeHandle nh;
  14   // Register the transport to the pointcloud_transport plugin set
  15   message_transport::MessageTransport<sensor_msgs::PointCloud> 
  16           it(nh,"pointcloud_transport","sensor_msgs::PointCloud");
  17   // Subscribe to the topic using the plugin specified as an argument,
  18   // defaults to raw
  19   message_transport::Subscriber sub = 
  20     it.subscribe("pc_source", 1, callback, transport);
  21   ros::spin();
  22 }

BZ2 Compression (bz2_transport)

This package implement a template message compression using libbzip2. When listening on the respective topic, message are compressed into a specific message type (BZ2Packet), sent over the normal ROS transport and decompressed by the subscriber.

Performance will depend on the data being sent. Note however, that for small messages (<100bytes), the BZ2 compression is likely to increase the bandwidth, as the compression is done per message. Best performance is expected for large messages with a lot of repetitive structure.

Shared memory and udp multicasting

These two packages implement generic transport that can then be instantiated for specific types.

Shared memory

Shared memory uses a boost shared memory segment to share the data. Each time an item is published, it is copied to the shared memory segment, and shared condition is triggered. When the subscriber wakes up from the condition it copies the shared memory segment. Obviously, this works only for two processes on the same machine. In our experience, shared memory transfer is useful for very big object 1 MB or more. Before that, normal loopback transfer is equivalent.

The shared memory block is managed by a the sharedmem_manager binary in the sharedmem_transport package. This must be started before subscribers can start to access the shared memory. This node provides several services to inspect the state of the shared memory block.

The graph below shows how the performance of the shared memory transport improves with the message size. Delivery delay as a function of message size

UDP Multicasting

UDP Multicasting is useful when a topic needs to be sent to many subscribers distributed over several machines. The publisher publishes a single message on a latched topic, describing the udp multicast channels used to send the data. Each time a message is received it is serialised and sent on the UDP channel.

The subscriber reads the latched message on the topic, and then uses boost to connect to the multicast channel.

UDP packets are limited to 8092 bytes. So message that cannot be serialised in this space will be rejected with an error message.

Each topic published via the udpmulti transport will be associated to a port number. The port numbers are managed by the udpmulti_manager in the udpmulti_transport package. This binary must be running before a topic can be published on this transport. It also provides a set of services to inspect and control the topic-port association table.

Limitation

Right now, one plugin must be defined for each data-type. This requires creating a specific package and copy pasting the manifest.cpp file from another package (pointcloud transport is a good example).

Report a Bug

Please report bugs and request features using the github page.

Change log

See the change list page.


2023-10-28 12:35