ROS话题通信

1 .理论模型

话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:

  • ROS Master (管理者)
  • Talker (发布者)
  • Listener (订阅者)

ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。

2 话题通信基本操作A(C++)

1. 创建工作空间

接下来,先创建一个新的ROS工作空间:

mkdir -p ~/catkin_ws/src

再进入工作空间

cd ~/catkin_ws/

编译工作空间

catkin_make

刷新环境变量

source devel/setup.bash

2. 创建发布者节点

先进入src目录

cd ~/catkin_ws/src

 创建依赖

catkin_create_pkg my_talker std_msgs rospy roscpp

~/catkin_ws/src/mytalker/src目录下创建一个新的C++文件,比如talker.cpp

gedit talker.cpp
// my_talker/src/talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"

#include <sstream>

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");//防止出现乱码现象
  // 初始化ROS节点
  ros::init(argc, argv, "talker");

  // 创建节点句柄
  ros::NodeHandle n;

  // 创建一个Publisher,话题名为"chatter",消息类型为std_msgs/String
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

  // 设置发布频率
  ros::Rate loop_rate(10);

  int count = 0;
  while (ros::ok())
  {
    // 创建一个消息实例
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();

    // 发布消息
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);

    // 循环等待
    ros::spinOnce();
    loop_rate.sleep();
    ++count;
  }

  return 0;
}
发布者(talker)代码解释
包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
  • ros/ros.h:包含ROS的核心功能,如节点初始化、日志记录等。
  • std_msgs/String.h:包含标准消息类型std_msgs/String的定义,这是一个简单的字符串消息。
  • <sstream>:包含字符串流的功能,用于构建字符串。
main函数
int main(int argc, char *argv)
{
  // 初始化ROS节点,节点名为"talker"
  ros::init(argc, argv, "talker");

  // 创建节点句柄,用于与ROS系统交互
  ros::NodeHandle n;

  // 创建一个Publisher,话题名为"chatter",消息类型为std_msgs/String,消息处理队列大小为1000
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

  // 设置发布频率,这里设置为10Hz
  ros::Rate loop_rate(10);

  int count = 0;
  while (ros::ok()) // 只要ROS系统还在运行
  {
    // 创建一个消息实例
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count; // 使用字符串流构建消息内容
    msg.data = ss.str(); // 将字符串流的内容赋值给消息的数据字段

    // 发布消息
    ROS_INFO("%s", msg.data.c_str()); // 在日志中打印消息内容(可选)
    chatter_pub.publish(msg); // 发布消息到话题"chatter"

    // 循环等待,保持发布频率
    ros::spinOnce(); // 处理任何挂起的回调函数
    loop_rate.sleep(); // 休眠1/10s
    ++count; // 计数器递增
  }

  return 0;
}
  • ros::init(argc, argv, "talker"):初始化ROS节点,节点名为"talker"。
  • ros::NodeHandle n:创建节点句柄,用于与ROS系统交互。
  • n.advertise<std_msgs::String>("chatter", 1000):创建一个发布者,话题名为"chatter",消息类型为std_msgs/String,队列大小为1000。队列大小决定了在订阅者来不及处理消息时,系统可以缓存多少条未处理的消息。
  • ros::Rate loop_rate(10):设置发布频率为10Hz。
  • ros::ok():检查ROS系统是否还在运行。
  • std_msgs::String msg:创建一个消息实例。
  • std::stringstream ss 和 ss << ...:使用字符串流构建消息内容。
  • msg.data = ss.str():将字符串流的内容转换为字符串并赋值给消息的数据字段。
  • ROS_INFO("%s", msg.data.c_str()):在日志中打印消息内容(这一步是可选的,仅用于调试)。
  • chatter_pub.publish(msg):发布消息到话题"chatter"。
  • ros::spinOnce():处理任何挂起的回调函数。在这个例子中,可能没有挂起的回调函数,但这是一个好习惯,可以确保系统的响应性。
  • loop_rate.sleep():休眠直到下一个发布周期。


3. 创建订阅者节点

同样地,在my_talker包的src目录下创建一个新的C++文件,比如listener.cpp

gedit listener.cpp
// my_talker/src/listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"

// 回调函数,当收到消息时被调用
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
  setlocale(LC_ALL,"");//防止出现乱码现象
  // 初始化ROS节点
  ros::init(argc, argv, "listener");

  // 创建节点句柄
  ros::NodeHandle n;

  // 创建一个Subscriber,话题名为"chatter",消息类型为std_msgs/String,回调函数为chatterCallback
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  // 循环等待回调函数
  ros::spin();

  return 0;
}
接收者(listener)代码解释
回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}
  • chatterCallback:这是一个回调函数,当订阅者收到话题"chatter"上的消息时,ROS系统会自动调用这个函数。
  • const std_msgs::String::ConstPtr& msg:这是传递给回调函数的消息参数,它是一个指向std_msgs/String消息类型的常量指针的智能指针。
  • ROS_INFO("I heard: [%s]", msg->data.c_str()):在日志中打印接收到的消息内容。
main函数
int main(int argc, char **argv)
{
  // 初始化ROS节点,节点名为"listener"
  ros::init(argc, argv, "listener");

  // 创建节点句柄
  ros::NodeHandle n;

  // 创建一个Subscriber,话题名为"chatter",消息类型为std_msgs/String,回调函数为chatterCallback
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  // 循环等待回调函数
  ros::spin();

  return 0;
}

  • ros::init(argc, argv, "listener"):初始化ROS节点,节点名为"listener"。
  • ros::NodeHandle n:创建节点句柄。
  • n.subscribe("chatter", 1000, chatterCallback):创建一个订阅者,话题名为"chatter",消息类型为std_msgs/String,队列大小为1000,回调函数为chatterCallback。当收到消息时,ROS系统会自动调用chatterCallback函数。
  • ros::spin():进入一个循环,等待并处理回调函数。在这个例子中,它会等待直到收到话题"chatter"上的消息,并调用chatterCallback函数。由于chatterCallback函数中没有显式的退出条件,因此ros::spin()会无限循环下去,直到ROS节点被手动停止。

5. 编译代码

回到~/catkin_ws目录,编译你的包:

cd ~/catkin_ws
catkin_make

4. 运行节点

首先,确保ROS核心服务正在运行:

roscore
然后,打开两个新的终端窗口,分别运行发布者和订阅者节点:
source ~/catkin_ws/devel/setup.bash
rosrun my_talker talker
source ~/catkin_ws/devel/setup.bash
rosrun my_talker listener

你应该能在终端2中看到订阅者节点不断接收到发布者节点发送的消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌你一颗小芯芯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值