之前看depthimage_to_laserscan
包,发现它的订阅发布采用的是所谓 lazy模式。今天研究了一下,发现并不复杂。先看advertise
函数的一个原型:1
2
3
4
5
6
7Publisher ros::NodeHandle::advertise(const std::string& topic,
uint32_t queue_size,
const SubscriberStatusCallback & connect_cb,
const SubscriberStatusCallback & disconnect_cb = SubscriberStatusCallback(),
const VoidConstPtr & tracked_object = VoidConstPtr(),
bool latch = false
)
原来有三个原型,我们常用的是第一个,这个是第二个,第三个就极其罕见了,形参是个AdvertiseOptions类型。
话题有订阅者时,发布者会触发connect_cb
函数,停止订阅时又触发disconnect_cb
函数。如果让它们做类成员函数,就使用Boost::bind
形式,tracked_object
暂时不用。所以程序可以这样写:1
2
3
4
5
6
7
8
9
10void connectCb(const ros::SingleSubscriberPublisher& singlePub)
{
ROS_INFO("Subscriber Name: %s", singlePub.getSubscriberName().c_str());
}
void disconnectCb(const ros::SingleSubscriberPublisher& singlePub)
{
ROS_INFO("disconnectCb");
}
......
ros::Publisher pub = nh.advertise<std_msgs::Int8>("topic", 50, connectCb,disconnectCb);
可以用rostopic echo topic
测试
再来看depthimage_to_laserscan
的程序,构造函数里是这样的:1
2
3
4pub_ = n.advertise<sensor_msgs::LaserScan>("scan", 10,
boost::bind(&DepthImageToLaserScanROS::connectCb, this, _1),
boost::bind(&DepthImageToLaserScanROS::disconnectCb, this, _1)
);
它是把两个函数用成员函数的形式,connectCb
是这样的:1
2
3
4
5
6if (!sub_ && pub_.getNumSubscribers() > 0)
{
ROS_DEBUG("Connecting to depth topic.");
image_transport::TransportHints hints("raw", ros::TransportHints(), pnh_);
sub_ = it_.subscribeCamera("/camera/depth/image_raw", 10, &DepthImageToLaserScanROS::depthCb, this, hints);
}
而depthCb
是:1
2sensor_msgs::LaserScanPtr scan_msg = dtl_.convert_msg(depth_msg, info_msg);
pub_.publish(scan_msg);
显然逻辑是这样的:有其他节点订阅scan
话题时,发布者里才订阅image_raw
话题,然后才会发布scan
话题的实际消息,所谓的lazy就是scan
话题的消息发布
disconnectCb
里,如果另一个节点不再订阅scan
话题,本节点就会sub_.shutdown();
;如果再有订阅scan
,又可以通过connectCb
使用sub_
,设计确实很巧妙