ROS编写第一个订阅器Subscriber

23次阅读

共计 2413 个字符,预计需要花费 7 分钟才能阅读完成。

在上一篇文章中,我们实现了第一个 ROS 程序—发布器(publisher),然而在上一篇文章的最后我们也注意到,尽管我们的程序非常小,但占据的 CPU 资源却非常多。

这是因为在发布器的 while 循环里没有执行必要的 sleep 操作,使得发布器一直以最高速率运行,长时间占用 CPU。

本篇文章分为以下两部分:

  1. 在发布器中加入 sleep 调用使发布器的频率稳定在1Hz
  2. 实现一个订阅器(Subscriber)

1. 发布器加入sleep

事实上,我们所需要做的只有两行工作,首先创建一个 ros::Rate 对象,然后在 while 循环里调用该对象的 .sleep() 函数即可。

修改后完整的代码如下:

#include <ros/ros.h>
#include <std_msgs/Float64.h>

int main(int argc, char **argv) {ros::init(argc, argv, "minimal_publisher"); // 初始化节点名
    ros::NodeHandle n; //
    
    // ++++
    ros::Rate s_timer(1.0); // 参数 1.0 代表发布频率即 1.0Hz
    // ++++
    
    ros::Publisher my_publisher_object = n.advertise<std_msgs::Float64>("topic1", 1); // 创建一个发布器,调用 advertise 通知 ROS Master 话题名称以及话题类型
    //"topic1" 是话题名
    // 参数 "1" 是 queue_size,表示缓冲区大小
    
    std_msgs::Float64 input_float; // 创建一个发布器将要使用的消息变量
    // 该消息定义在:/opt/ros/indigo/share/std_msgs
    // 在 ROS 中发布的消息都应该提前定义,以便订阅者接收到消息后该如何解读
    // Float64 消息的定义如下,其中包含一个数据字段 data:// float64 data
    
    input_float.data = 0.0; // 设置数据字段
    
    
    // 程序所要做的工作将在下面的循环里完成
    while (ros::ok()) 
    {
        // 该循环没有 sleep,因此将一直处于运行状态,不断消耗 CPU 资源
        input_float.data = input_float.data + 0.001; // 每循环一次 +0.01
        my_publisher_object.publish(input_float); // 发布消息到对应的话题
        // ++++
        s_timer.sleep(); // 在这里调用 sleep 函数可以让程序在这里
        // 停止一段时间以便达到要求的发布频率
        // ++++
    }
}

将修改后的发布器重新进行编译,然后按照和上篇文章一样依次运行:

roscore

再打开一个终端,运行

rosrun my_minimal_node my_minimal_publisher # 启动发布器

检查发布频率,运行

rostopic hz /topic1

可以看到此时发布器的发布频率已经基本稳定在 1Hz 了。然后检查系统监视器的状态:

也同样可以看到此时 CPU 的占用率已经降下来了。

2. 实现一个订阅器

首先将我们提前修改好的订阅器代码复制到 src 目录下,代码如下:

#include<ros/ros.h> 
#include<std_msgs/Float64.h> 
void myCallback(const std_msgs::Float64& message_holder) 
{ 
  // 打印出我们接收到的值
  ROS_INFO("received value is: %f",message_holder.data); 
} 

int main(int argc, char **argv) 
{ros::init(argc,argv,"minimal_subscriber"); // 初始化节点
  // 节点名定义为 minimal_subscriber
  ros::NodeHandle n; // 节点句柄,用来创建订阅器
  // 订阅话题 'topic1'
  // subscribe 中的 mycallback 是回调函数,每当有新数据到来时,该函数
  // 便会被调用
  // 实际的工作是在回调函数中完成的
  
  ros::Subscriber my_subscriber_object=
      n.subscribe("topic1",1,myCallback); 

  ros::spin(); // 类似于 `while(1)` 语句,但是当有新消息到来时,会调用回调函数

  return 0; 
} 

然后和上篇文章一样,为了编译我们刚写的订阅器,我们还需要修改 CMakeLists.txt 文件,以便让编译器知道应该编译我们新增的文件。类比上篇文章的发布器,我们在 CMakeLists.txt 文件中加入如下两行:

add_executable(my_minimal_subscriber src/minimal_subscriber .cpp) # 第一个参数是生成后的可执行文件名 第二个参数
# 是源文件路径名
target_link_libraries(my_minimal_subscriber  ${catkin_LIBRARIES}) # 链接库

打开终端,导航到工作区目录下~/catkin_ws,然后执行命令

catkin_make

等待编译完成后,依次执行命令(这些命令都是在不同的终端下输入)

roscore
rosrun my_minimal_node my_minimal_publisher
rosrun my_minimal_node my_minimal_subscriber

然后在订阅器的终端下就可以看出输出

运行命令 rosnode list 检查节点

最后,可以运行命令

rqt_graph

来显示一个图形化的节点 - 话题连接图:

由上面的直观展示可以看出,消息由发布器流出到话题 topic1 然后再流向订阅器。

视频

ROS 编写第一个订阅器程序

以上所有过程我录制了一个视频,在浏览文章过程中如果遇到问题,您可以查看视频来看看我是怎么做的。

正文完
 0