【ros学习】9.ros话题通讯之publisher与subscribe的c++实现详解

632 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


1.创个话题源码包topic_demo

$ cd ~/catkin_ws/src
$ catkin_create_pkg topic_demo roscpp rospy std_msgs

2.在包下新建msg文件夹自定义消息文件gps.msg

$ cd topic_demo/
$ mkdir msg
$ cd msg
$ vi gps.msg

gps.msg

float32 x float32 y string state

3.编写talker.cpp作为publisher节点

#include <ros/ros.h>
#include <topic_demo/gps.h>//自定义msg产生的头文件

int main(int argc, char** argv){
    ros::init(argc, argv, "talker");  //解析参数,命名结点
    ros::NodeHandle nh;  //创建句柄,实例化node
    topic_demo::gps msg;  //创建gps消息
    msg.x = 1.0;
    msg.y = 1.0;
    msg.state = "working";
    ros::Publisher pub = nh.advertise<topic_demo::gps>("gps_info",1);  //创建publisher,需要发布的消息类型为topic_demo::gps,往"gps_info"话题上发布消息,发送队列长度为1
    ros::Rate loop_rate(1.0);  //定义循环发布的频率1Hz,一秒钟变化一次,一秒钟发布一次
    while(ros::ok){  //只要ros还没关闭就循环
        msg.x = 1.03 * msg.x;
        msg.y = 1.01 * msg.y;
        ROS_INFO("Talker:GPS:x = %f, y = %f", msg.x, msg.y);//输出当前的msg
        pub.publish(msg);  //发布消息
        loop_rate.sleep(); //根据前面自定义的发布评率(ros::Rate loop_rate(1.0);),sleep,休眠一秒钟,暂停一秒钟
    }
    return 0;
}

4.编写listener.cpp作为subscribe节点

#include <ros/ros.h>
#include <topic_demo/gps.h>
#include <std_msgs/Float32.h>

void gpsCallback(const topic_demo::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())
}

int main(int argc, char** argv){
    ros::init(argc, argv, "listener");  //解析参数,命名结点
    ros::NodeHandle nh;  //创建句柄,实例化node
    //创建subscribe,1.需要订阅的话题名,2.消息缓存队列长度1(来即处理),3.回调函数(指针),处理收到的消息
    ros::Subscriber sub = nh.subscribe("gps_info", 1 , gpsCallback); 
    ros::spin();  //反复调用当前可触发的回调函数,堵塞,用于触发topic、service的响应队列
    //ros::spin()用于调用所有可触发的回调函数,将进入循环,不会返回,类似于在循环里反复调用spinOnce() 
 	//而ros::spinOnce()只会去触发一次

    return 0;
}

在这里插入图片描述

扩展:回调函数与spin()方法 回调函数在编程中是一种重要的方法,在维基百科上的解释是:

In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. 回调函数作为参数被传入到了另一个函数中(在本例中传递的是函数指针),在未来某个时刻(当有新的message到达),就会立即执行。

Subscriber接收到消息,实际上是先把消息放到一个队列中去,如图所示。队列的长度在Subscriber构建的时候设置好了。当有spin函数执行,就会去处理消息队列中队首的消息。 在这里插入图片描述

spin具体处理的方法又可分为阻塞/非阻塞,单线程/多线程,在ROS函数接口层面我们有4种spin的方式: 在这里插入图片描述

阻塞与非阻塞的区别我们已经讲了,下面来看看单线程与多线程的区别: 在这里插入图片描述

我们常用的spin()、spinOnce()是单个线程逐个处理回调队列里的数据。有些场合需要用到多线程分别处理,则可以用到MultiThreadedSpin()、AsyncMultiThreadedSpin()。

5.修改Cmake编译系统的规则文件CMakeLists.txt

1.添加依赖:message_generation, 第16行左右

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  #添加这个依赖是为了帮我们生成自定义的message
  message_generation  
)

2.添加宏,第76行左右

## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

#添加自定义的msg文件,告诉cmake编译系统message头文件.h用哪个.msg文件参考生成的,catkin在cmake之上新增的命令,指定从哪个消息文件生成
add_message_files(FILES gps.msg) 

#标准消息库依赖,用来生成自定义msg对应的.h头文件
#catkin新增的命令,用于生成消息
#DEPENDENCIES后面指定生成msg需要依赖其他什么消息,由于gps.msg用到了flaot32这种ROS标准消息,因此需要再把std_msgs作为依赖
generate_messages(DEPENDENCIES std_msgs) 

3.把俩个注释放开与加上message_runtime

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES topic_demo
#用于配置ROS和package配置文件和Cmake文件
   CATKIN_DEPENDS roscpp rospy std_msgs message_runtime  
#  DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
#指定C/C++的头文件路径
include_directories(include ${catkin_INCLUDE_DIRS})
#生成可执行目标文件
add_executable(talker src/talker.cpp)
#添加依赖,必须有此局以生成msg  
#表明在编译talker前,必须先生编译完成自定义消息
#必须添加add_dependencies,否则找不到自定义的msg产生的头文件 
#add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp) 
add_dependencies(talker topic_demo_generate_messages_cpp) 
target_link_libraries(talker ${catkin_LIBRARIES})  //链接

add_executable(listener src/listener.cpp)
add_dependencies(listener topic_demo_generate_messages_cpp)
target_link_libraries(listener ${catkin_LIBRARIES})

#############
## Install ##
#############

6.修改软件包的描述文件package.xml

新版本format2

  <!--编译依赖项 -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <!-- 导出依赖项 -->
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <build_export_depend>message_generation</build_export_depend>
  <!-- 运行依赖项 -->
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>message_runtime</exec_depend>  

7.编译及运行

$ cd ~/catkin_ws
$ catkin_make

在这里插入图片描述

编译后生成的gps.h头文件放在

~/catkin_ws/devel/include/topic_demo/gps.h

自定义消息使用方法:

#include <topic_demo/gps.h>
topic_demo::gps msg;

运行发布者和订阅者:

$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ roscore
$ rosrun topic_demo listener
$ rosrun topic_demo talker

在这里插入图片描述

参考:sychaichangkun.gitbooks.io/ros-tutoria….