ros之message_filter消息同步


前言

A message filter is defined as something which a message arrives into and may or may not be spit back out of at a later point in time. An example is the time synchronizer, which takes in messages of different types from multiple sources, and outputs them only if it has received a message on each of those sources with the same timestamp.

Time Synchronizer

The TimeSynchronizer filter synchronizes incoming channels by the timestamps contained in their headers, and outputs them in the form of a single callback that takes the same number of channels. The C++ implementation can synchronize up to 9 channels.

ExactTime Policy

必須是相同的時間戳才能同步上.

The message_filters::sync_policies::ExactTime policy requires messages to have exactly the same timestamp in order to match. Your callback is only called if a message has been received on all specified channels with the same exact timestamp. The timestamp is read from the header field of all messages (which is required for this policy).

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/exact_time.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/CameraInfo.h>

using namespace sensor_msgs;
using namespace message_filters;

void callback(const ImageConstPtr& image, const CameraInfoConstPtr& cam_info)
{
  // Solve all of perception here...
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "vision_node");

  ros::NodeHandle nh;
  message_filters::Subscriber<Image> image_sub(nh, "image", 1);
  message_filters::Subscriber<CameraInfo> info_sub(nh, "camera_info", 1);

  typedef sync_policies::ExactTime<Image, CameraInfo> MySyncPolicy;
  // ExactTime takes a queue size as its constructor argument, hence MySyncPolicy(10)
  Synchronizer<MySyncPolicy> sync(MySyncPolicy(10), image_sub, info_sub);
  sync.registerCallback(boost::bind(&callback, _1, _2));

  ros::spin();
  return 0;
}

ApproximateTime Policy

The message_filters::sync_policies::ApproximateTime policy uses an adaptive algorithm to match messages based on their timestamp.

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

#include <sensor_msgs/Image.h>

void callback(const sensor_msgs::ImageConstPtr& image1, const sensor_msgs::ImageConstPtr& image2)
{
  // Solve all of perception here...
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "vision_node");

  ros::NodeHandle nh;
  message_filters::Subscriber<sensor_msgs::Image> image1_sub(nh, "image1", 1);
  message_filters::Subscriber<sensor_msgs::Image> image2_sub(nh, "image2", 1);

  typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::Image> MySyncPolicy;
  // ApproximateTime takes a queue size as its constructor argument, hence MySyncPolicy(10)
  message_filters::Synchronizer<MySyncPolicy> sync(MySyncPolicy(10), image1_sub, image2_sub);
  sync.registerCallback(boost::bind(&callback, _1, _2));

  ros::spin();
  return 0;
}

注: void callback(const boost::shared_ptr &, ..., const boost::shared_ptr &) , 回調函數的輸入類型必須是ConstPtr類型的共享指針,當年找了這個錯誤找了一下午,謹記。

ApproximateTime Algorithm

Let S be the last published set, and T the next one being created. It works as follows:

  • When set S is published, for each topic, all messages older than the one in the set are discarded.
  • Messages are inserted in a topic-specific queue as they arrive.
  • Once each topic-specific queue contains at least one message, we find the latest message among the heads of the queues, that's the pivot. The pivot belongs to every set contiguous to S, so it must belong to T (property d), hence its special status. For each message m older than the pivot, call Tm the smallest set that starts at m. It is obtained by taking the earliest message arrived after m on each queue. Since T starts at some message, it must be one of the Tm's.
  • Go through messages m in order of time. If the next message cannot yet be determined, wait until the queues fill up enough.
  • When m reaches the pivot so that all Tm's have been enumerated, or whenever it can be proved using various bounds that the smallest Tm has been found, publish the smallest Tm.

It works in three nested phases:

  • Find the pivot and a first valid candidate set.
  • Advance along the queues until one of the queues is empty, in search of a better candidate.
  • Advance past this point assuming that future messages will arrive at the most optimistic time, to try to prove that the current candidate is optimal.

Here are some example results. Time goes from left to right, each line is a topic, and each dot is a message. The red message is the pivot, and the broken line links the messages in a set.

我的理解:

  • 對於每個需要同步的topic都有一個消息隊列
  • 如果每個隊列中至少有一個msg, 選最后到的那個為pivot
  • 計算每個隊列中距離pivot最近的消息與pivot的消息差和, 為T
  • 等待, 直到能夠確定最小的T, 那么這個T set為需要publish的set
  • 那么在這個publish set之前的消息都需要刪除

實踐

CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS
             message_filters)

頭文件

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

boost::bind()

  • 如果callback()函數是全局函數,如上例所示:
boost::bind(&callback, _1, _2)  

注: _1, _2為占位符, 代表接收的參數, 在函數真正調用的時候傳入真正的參數。占位符的名字知識表示它在調用式中的順序, 最多可以綁定9個占位符。

  • 如果callback()函數是類成員函數:
boost::bind(&X::back, this, _1, _2)

注:第二個參數this指針指向當前對象, 從而可以調用當前對象的成員函數,因為this指針占用了一個占位符,所以最多綁定8個參數

message_filter作為成員變量

在構造函數中.

xx.h

class A
{
private:
typedef message_filters::sync_policies::ApproximateTime<type_a, type_b> MySyncPolicy;
typedef message_filters::Synchronizer<MySyncPolicy> Sync;
boost::shared_ptr<Sync> sync_;
message_filters::Subscriber<type_a> a_sub_;
message_filters::Subscriber<type_b> b_sub_;
};

xx.cpp

A::A(ros::NodeHandle& nh, ros::NodeHandle& priv_nh)
{
  a_sub_.subscribe(nh, a_topic, 100);
  b_sub_.subscribe(nh, b_topic, 100);
  sync_.reset(new Sync(MySyncPolicy(1000), a_sub_, b_sub_));
  sync_->registerCallback(boost::bind(&A::callback, this, _1, _2));
}

示例

圖像和點雲同步

#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/CompressedImage.h>

#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

#include <pcl_conversions/pcl_conversions.h>
#include <opencv2/opencv.hpp>


void callback(const sensor_msgs::CompressedImageConstPtr input_image_msg,
              const sensor_msgs::PointCloud2ConstPtr input_cloud_msg)
{
  std_msgs::Header image_header = input_image_msg->header;
  std_msgs::Header cloud_header = input_cloud_msg->header;

  // sensor_msgs to cv image
  cv::Mat input_image;
  try
  {
    input_image = cv::imdecode(cv::Mat(input_image_msg->data), 1);
  }
  catch (cv_bridge::Exception& e)
  {
    std::cout << "could not convert sensor_msg image into opencv image!" << std::endl;
    return;
  }

  // sensor_msgs to point cloud
  pcl::PointCloud<pcl::PointXYZI>::Ptr input_cloud_ptr(new pcl::PointCloud<pcl::PointXYZI>);
  pcl::fromROSMsg(*input_cloud_msg, *input_cloud_ptr);
  if (input_cloud_ptr->size() == 0)
  {
    std::cout << "input cloud is empty, please check it out!" << std::endl;
    return;
  }

}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "vision_node");
  ros::NodeHandle nh;
  ros::NodeHandle priv_nh("~");
  
  std::string camera_topic = "/camera_front/image_color/compressed";
  std::string lidar_topic = "/rslidar_points";
  message_filters::Subscriber<sensor_msgs::CompressedImage> camera_sub(nh, camera_topic, 10);
  message_filters::Subscriber<sensor_msgs::PointCloud2> lidar_sub(nh, lidar_topic, 10);
  typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::CompressedImage, sensor_msgs::PointCloud2>
      MySyncPolicy;
  message_filters::Synchronizer<MySyncPolicy> sync(MySyncPolicy(10), camera_sub, lidar_sub);
  sync.registerCallback(boost::bind(&callback, _1, _2));
  ros::spin();

  return 0;
}

多圖像同步

#include <ros/ros.h>
#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

#include <sensor_msgs/Image.h>

void callback(const sensor_msgs::ImageConstPtr& image1, const sensor_msgs::ImageConstPtr& image2)
{
  // Solve all of perception here...
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "vision_node");
  ros::NodeHandle nh;
  ros::NodeHandle priv_nh("~");

  message_filters::Subscriber<sensor_msgs::Image> image1_sub(nh, "image1", 1);
  message_filters::Subscriber<sensor_msgs::Image> image2_sub(nh, "image2", 1);

  typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::Image> MySyncPolicy;
  message_filters::Synchronizer<MySyncPolicy> sync(MySyncPolicy(10), image1_sub, image2_sub);
  sync.registerCallback(boost::bind(&callback, _1, _2));

  ros::spin();
  return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM