本文已参与「新人创作礼」活动,一起开启掘金创作之路。
问题描述
写了一个节点,要从激光雷达发布的话题中订阅点云信息,并将点云信息处理之后再发布出去。涉及到一个节点同时订阅和发布多个话题。 测试时发现节点只能订阅,不能发布话题,且订阅的话题数据也无法使用。
解决办法
ROS中的Topic机制是一种多对多的关系:可以同时有多个Node向同一个Topic发送消息,它同时也可以被多个Node订阅。但这是站在Topic角度而言的“多对多”,如果我们换一个角度,在Node角度,是否也可以实现多对多呢?一个Node是否可以同时接收、发送多个Topic?答案是肯定的。
本篇博客主要来探讨“进阶版”的Topic用法——一个节点(Node)的多Topic收发在C++及Python中的实现。其实对于这种节点同时收发Topic的需求是非常常见的,如一个Node从一个或多个Topic接收了传感器发来的数据,并对数据进行了处理(如多传感器数据融合),并将处理好的数据发送到另一个Topic上。在整个Node/Topic的关系图中,除非这个节点是起始点或终点,只需要考虑发送或者接收一个或多个Topic,其余其它所有节点都必须要面临同时接收发送的问题。例如如下Node/Topic图(rqt_graph绘图,圆形表示Node,方形表示Topic)中所示:节点
/mobild_base_nodelet_manager接收了/mobile_base_nodelet_manager/bond,同时又发送/mobile_base_nodelet_manager/bond和/cmd_vel两个Topic。对于/gazebo节点而言,同时接收/cmd_vel和发送/xbot/joint_statesTopic。
所以这在实际应用中是非常现实的需求,学好这个非常重要。但在ROS官网以及相关书籍、博客上找了一下,介绍多Topic收发的相对较少,大部分还是停留在一个节点只做一件事(收或发)的水平。所以本篇博客希望可以更加深入学习Topic。
经过查找才发现,是因为自己把发布topic的函数写在main函数中了,这就造成订阅的数据无法被发布的函数使用,因为只能在订阅的回调函数中才能使用该信息。因此我的解决办法是把发布topic的函数 写在了订阅回调中即可。
参考链接中给出了另一种解决办法:定义了一个类,把publisher和subscriber都放入类中。 这里我粘贴处第一种方法的代码 想看第二中方法的自行点击链接
# include<ros/ros.h>
# include<std_msgs/String.h>
# include<std_msgs/Float32.h>
// 定义为全局变量
static ros::Subscriber sub1;
static ros::Subscriber sub2;
static ros::Publisher pub1;
static ros::Publisher pub2;
// 回调函数1
void callback1(const std_msgs::Float32ConstPtr& flt){
std_msgs::Float32 pub_flt;
pub_flt.data = flt->data+0.4;
pub1.publish(pub_flt);
std::cout<<"receive flt:"<<flt->data<<std::endl;
std::cout<<"publish flt:"<<pub_flt.data<<std::endl;
};
// 回调函数2
void callback2(const std_msgs::StringConstPtr& str){
std_msgs::String str_msg;
str_msg.data = str->data+" hahaha";
pub2.publish(str_msg);
std::cout<<"receive str:"<<str->data<<std::endl;
std::cout<<"publish str:"<<str_msg.data<<std::endl;
}
int main(int argc, char *argv[])
{
// 初始化ROS并指定节点名称
ros::init(argc, argv, "subscribe_publish2");
// 创建节点句柄
ros::NodeHandle nh;
// 利用节点句柄对sub和pub初始化
sub1 = nh.subscribe("topic_flt",1,callback1);
sub2 = nh.subscribe("topic_str",1,callback2);
pub1 = nh.advertise<std_msgs::Float32>("processed_flt", 1);
pub2 = nh.advertise<std_msgs::String>("processed_str",1);
// 循环执行
ros::spin();
return 0;
}
另外 如何想要设置发布和订阅不同的频率 可以使用pub_rate函数
ros::Rate pub_rate(50);
//让其在主题“/cmd_vel”发布速度控制消息,启智ROS的核心节点会从这个主题获取vel_pub发布的消息,并控制机器人底盘执行消息包里的速度值。
vel_pub.publish(vel_cmd);
//循环等待回调函数
pub_rate.sleep();