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 talkersource ~/catkin_ws/devel/setup.bash
rosrun my_talker listener
你应该能在终端2中看到订阅者节点不断接收到发布者节点发送的消息









![[Qt] 万字详解Qt入门~ Qt Creator | 对象树 | 控件布局](https://i-blog.csdnimg.cn/img_convert/2b1617eda5a2093c213b992231ec8140.png)









