Topic是ROS里一种异步通信的模型,,一般是节点间分工明确,有的只负责发送,有的只负责 接收处理。对于绝大多数的机器人应用场景,比如传感器数据收发,速度控制指令的收发, Topic模型是最适合的通信方式。
ROS中的通信方式中,topic是常用的一种。对于实时性、周期性的消息,使用topic来传输是 最佳的选择。topic是一种点对点的单向通信方式,这里的“点”指的是node,也就是说node之 间可以通过topic方式来传递信息。topic要经历下面几步的初始化过程:首先,publisher节点 和subscriber节点都要到节点管理器进行注册,然后publisher会发布topic,subscriber在 master的指挥下会订阅该topic,从而建立起sub-pub之间的通信。注意整个过程是单向的。其 结构示意图如下:
Subscriber接收消息会进行处理,一般这个过程叫做回调(Callback)。所谓回调就是提前定义 好了一个处理函数(写在代码中),当有消息来就会触发这个处理函数,函数会对消息进行 处理。
上图就是ROS的topic通信方式的流程示意图。topic通信属于一种异步的通信方式。
在代码中,会用到自定义类型的gps消息,因此就需要来自定义gps消息,在msg路径下 创建 gps.msg。
float32 x #x坐标
float32 y #y坐标
string state #工作状态
以上就定义了一个gps类型的消息,可以把它理解成一个C语言中的结构体,类似于
struct gps
{
string state;
float32 x;
float32 y;
}
在程序中对一个gps消息进行创建修改的方法和对结构体的操作一样。
当创建完了msg文件,记得修改 CMakeLists.txt和 package.xml,从而让系统能够编译自定义消息。在CMakeLists.txt中需要改动
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation #generate new msg must add #需要添加的地方
)
add_message_files(FILES gps.msg) #catkin在cmake之上新增的命令,指定从哪个消息文件生成
generate_messages(DEPENDENCIES std_msgs) #DEPENDENCIES后面指定生成msg需要依赖其他什么消息,由于gps.msg用到了flaot32这种ROS标准消息,因此需 要再把std_msgs作为依赖
package.xml 中需要的改动
<build_depend>message_generationbuild_depend>
<exec_depend>message_runtimeexec_depend>
当你完成了以上所有工作,就可以回到工作空间,然后编译了。编译完成之后会在 devel 路 径下生成 gps.msg 对应的头文件,头文件按照C++的语法规则定义了 topic_demo::gps 类型的 数据。
要在代码中使用自定义消息类型,只要 #include
topic_demo::gps mygpsmsg;
mygpsmsg.x = 1.6;
mygpsmsg.y = 5.5;
mygpsmsg.state = "working";
定义完了消息,开始写ROS代码。通常会把消息收发的两端分成两个节点来写,一个节点就是一个完整的C++程序。
在src下建立talker.cpp发送节点
源码如下:
#include
#include //引入生成的消息类型
int main(int argc, char **argv)
{
ros::init(argc,argv,"talker_jone");//用于解析ROS参数,第三个参数为本节点名 注意要与后面修改的Cmakelist.txt文件对应
ros::NodeHandle nh ; //实例化句柄,初始化node
topic_demo_jone::gps msg;//自定义gps消息并初始化
msg.x=1;
msg.y=2;
msg.state="working";
ros::Publisher pub = nh.advertise<topic_demo_jone::gps>("gps_info",1);//创建publisher,往 "gps_info"话题上发布消息 gps_info就是topic的名称
ros::Rate loop_rate(1);//定义发布的频率,1HZ
while (ros::ok()) //循环发布msg
{
msg.x=msg.x*1.3;//处理msg
msg.y=msg.y*1.1;//处理msg
ROS_INFO("Talker_jone:GPS: x=%f , y=%f",msg.x,msg.y);//打印 要发布的信息
pub.publish(msg); //发布消息
loop_rate.sleep();//根据前面的定义的loop_rate,设置1s的暂停
}
return 0;
}
机器人上几乎所有的传感器,几乎都是按照固定频率发布消息这种通信方式来传输数据,只 是发布频率和数据类型的区别。
源码如下
#include
#include
#include
void gpsCallback(const topic_demo_jone::gps::ConstPtr &msg )
{
std_msgs::Float32 distance; //计算离原点(0,0)的距离
distance.data=sqrt(pow(msg->x,2) + pow(msg->y,2));
ROS_INFO( "Listener :Distance to origin = %f , state = %s" ,distance.data, msg->state.c_str() ); //打印计算的距离 和msg中的state
}
int main(int argc, char ** argv)
{
ros::init(argc, argv, "listener_jone");
ros::NodeHandle nh ;
ros::Subscriber sub = nh.subscribe("gps_info", 1, gpsCallback); //设置回调函数gpsCallb ack
ros::spin();//ros::spin()用于调用所有可触发的回调函数,将进入循环,不会返回,类似于在循环里反复 调用spinOnce() //而ros::spinOnce()只会去触发一次
return 0;
}
在topic接收方,有一个比较重要的概念,就是回调(CallBack),回调就是预先 给gps_info 话题传来的消息准备一个回调函数,你事先定义好回调函数的操作,计算到原点的距离。只有当有消息来时,回调函数才会被触发执行。具体去触发的命令就是ros::spin() ,它会反复的查看有没有消息来,如果有就会让回调函数去处理。
因此千万不要认为,只要指定了回调函数,系统就回去自动触发,你必须 ros::spin()或 者ros::spinOnce()才能真正使回调函数生效。
在 CMakeLists.txt 添加以下内容,生成可执行文件
add_executable(talker_jone src/talker.cpp ) #生成可执行文件talker_jone 节点的名字
add_dependencies(talker_jone topic_demo_generate_messages_cpp) #必须添加add_dependencies,否则找不到自定义的msg产生的头文件
target_link_libraries(talker_jone ${catkin_LIBRARIES})
add_executable(listener_jone src/listener.cpp ) #生成可执行文件listener
add_dependencies(listener_jone topic_demo_generate_messages_cpp)
target_link_libraries(listener_jone ${catkin_LIBRARIES})
回到工作空间
运行指令 ,进行编译
catkin_make
之后运行ros
roscore
然后运行talker_jone 节点
rosrun topic_demo_jone talker_jone
然后运行listener_jone 节点
rosrun topic_demo_jone listener_jone
在实际应用中,应该熟悉topic的几种使用命令,列出各自的命令及其作 用。
命令 ————————作用
rostopic list 列出当前所有的topic
rostopic info topic_name 显示某个topic的属性信息
rostopic echo topic_name 显示某个topic的内容
rostopic pub topic_name … 向某个topic发布内容
rostopic bw topic_name 查看某个topic的带宽
rostopic hz topic_name 查看某个topic的频率
rostopic find topic_type 查找某个类型的topic
rostopic type topic_name 查看某个topic的类型(msg)
以刚写的例子进行测试
rostopic list
rostopic info /gps_info
rostopic echo /gps_info