创建简单的发布者和订阅者

创建发布者和订阅者

发布者

  • 切换到软件包src目录,创建一个发布节点
roscd <package_name>
cd src
touch talker.cpp
  • 发布节点talker.cpp的内容如下
#include <ros/ros.h> // 导入ROS系统包含核心公共文件
#include <std_msgs/String.h> // 导入std_msgs/String消息头文件,这个由std_msgs包的string.msg自动生成
#include <sstream>
/**
* 发布方实现:
* 1.包含头文件
* 2.初始化ROS节点
* 3.创建节点句柄
* 4.创建发布者对象
* 5.编写发布逻辑并发布数据
*/
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker"); // ROS节点初始化,指定节点名称为"talker",节点名称要保持唯一性
ros::NodeHandle n; // 创建节点句柄
// 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String,队列长度1000
// 第二个参数为消息队列长度,队列中最大保存的消息数量, 超过这个数量, 旧的消息会被丢弃(先进先销毁)
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// 创建一个循环频率对象, 指定发布消息的频率
ros::Rate loop_rate(10);
int count = 0;
ros::Duration(3).sleep(); // 等待3s, 以便发布节点注册到ROS Master
while (ros::ok()) // 检查节点是否应继续运行,当接收到SIGINT(Ctrl+C)或者 ros::shutdown() 被调用时返回false
{
++count;
std_msgs::String msg; // 初始化std_msgs::String msg类型的消息
std::stringstream ss; // 初始化std::stringstream类型的消息
ss << "hello world --- " << count; // 定义字符串流"hello world"并赋给ss
msg.data = ss.str(); // 最后转换为字符串赋值给 msg.data
ROS_INFO_STREAM(" " << msg->data.c_str()); // 输出调试信息
chatter_pub.publish(msg); // 发布消息
ros::spinOnce(); // 处理传入消息的回调函数, 不是必需要的,但是保持增加这个调用,是好习惯
loop_rate.sleep(); // 按照循环频率延时
}
return 0;
}

总结

  • 初始化ROS系统

  • 向ROSMaster注册节点信息,包括发布的话题名和话题中的消息类型

  • 创建消息数据

  • 按照一定的频率循环发布消息

订阅者

  • 同样
roscd <package_name>
cd src
touch listener.cpp
  • 订阅节点listener.cpp的内容如下
#include <ros/ros.h> // 导入ROS系统包含核心公共头文件
#include <std_msgs/String.h> // 导入std_msgs/String消息头文件,这个是由std_msgs包的string.msg文件自动生成
/**
* 订阅方实现:
* 1.包含头文件
* 2.初始化ROS节点
* 3.创建节点句柄
* 4.创建订阅者对象
* 5.编写回调函数处理接收到的数据
* 6.设置循环调用回调函数
*/
// 接收到chatter话题的消息后,回调函数就会被调用并且收到的消息会作为参数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO_STREAM("I heard: " << msg->data.c_str()); // 将接收到的消息打印出来
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener"); // 初始化ROS节点
ros::NodeHandle n; // 创建节点句柄
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
// 创建一个Subscriber,订阅名为chatter的topic,指定回调函数chatterCallback
ros::spin(); // 启动自循环,等待回调函数
return 0;
}

总结

  • 初始化ROS节点
  • 订阅需要的话题
  • 循环等待话题消息,接收到信息后进入回调函数
  • 在回调函数中完成消息处理

修改CMakeLists.txt

  • find_package依赖项
# 将message_generation加在括号闭合前
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
  • 调用生成的消息
generate_messages(
DEPENDENCIES
std_msgs
)
  • 设置生成可执行文件和链接库(添加到CMakeLists.txt文件的底部)
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})

句柄

  • 句柄(Handle)这个概念可以理解为一个“把手”,你握住了门把手,就可以很容易把整扇门拉开,而不必关心门是什么样子。NodeHandle就是对节点资源的描述,有了它你就可以操作这个节点了,比如为程序提供服务、监听某个topic上的消息、访问和修改param等等

  • NodeHandle是Node的句柄,用来对当前节点进行各种操作。在ROS中,NodeHandle是一个定义好的类,通过include<ros/ros.h>,我们可以创建这个类,以及使用它的成员函数

NodeHandle

publisher

  • 用句柄创建publisher
ros::Publisher publisher = NodeHandle.advertise<message_type>(topic, queue_size);
  • 编写消息内容
  • 发布消息
publisher.publish(message);

subscriber

  • 通过调用节点句柄的 subscriber() 方法来订阅话题
ros::Subscriber subscriber = NodeHandle.subscriber(topic, queue_size, callblack_function);
  • 持续订阅话题,直到你取消订阅

参数

  • 使用C++来获取参数
nodeHandle.getParam(param_name, variable)
  • 当函数找到对应的参数就会返回true,否则返回false

四种节点句柄命名空间的种类

  • ros::NodeHandle:默认命名空间
  • ros::NodeHandle("~"):私有命名空间
  • ros::NodeHandle("ksals"):指定命名空间
  • ros::NodeHandle("/"):全局命名空间(不推荐)

这些节点的命名空间将解析为

  • /namespace/topic
  • /namespace/node/topic
  • /namespace/ksals/topic
  • /topic


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   TNTksals  阅读(136)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示