作为多进程管理系统,ROS内节点node间的数据通信交互是必须的功能,本文将首先简单介绍ROS的三种节点间通信机制,再详细举例介绍话题(topic)通信方式。
1. 通信机制
(一)Topic通信
消息通过发布/订阅(Publish/Subscribe)方式传递,发布者节点(Talker)针对一个给定的话题发布消息,订阅者节点(Listener)订阅某个话题及其特定数据。话题通信是异步通信,无反馈,有缓冲,弱实时,节点关系多对多,适于数据传输。
(二)Service通信
基于服务器/客服端(Sercer/Client)模型,可以使用 ROS 提供的服务类型,也可以使用 .srv 文件自定义。服务通信是同步通信,有反馈,无缓冲,强实时,节点关系一对多,适于逻辑处理。
(三)ROS Master通信
统筹管理所有节点,进行节点间的查找、连接等,为系统提供参数服务器,管理全局参数。节点管理器也体现了ROS的弊端即ROS Master 如果 broken down,整个系统将崩溃。
2. Topic通信详解
注:案例、代码来自胡春旭《ROS机器人开发实践》
2.1 创建 Publisher
创建Publisher节点 talker 通过 talker.cpp文件。
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "talker");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为chatter的topic,消息类型为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类型的消息
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;
}
2.2 创建 Subscriber
创建Subscriber节点 listener 通过 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)
{
// 初始化ROS节点
ros::init(argc, argv, "listener");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
2.3 修改CMakeLists.txt文件
在CMakeLists.txt文件内添加:
# 参数1:可执行文件名称,参数2:源文件 cpp
add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)
# target_link_libraries 用于设置链接库(第三方库)
target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(listener ${catkin_LIBRARIES})
# add_dependencies 用于设置依赖
add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp) //necessary for ros message transmission
add_dependencies(listener ${PROJECT_NAME}_generate_messages_cpp)
2.4 通信结果验证
保存后回到workspace路径下进行编译:
catkin_make
打开三个terminal,顺次分别输入以下命令:
roscore
rosrun <package_name> talker
rosrun <package_name> listener
便可实现节点talker和listener间的通信。
3. 自定义topic通信 .msg消息文件
以上示例中通信消息仅为简单的字符串,在实际使用中我们需要自己定义发送的信息数据文件。
(1)我们需要在package包文件夹下新建msg文件夹,用来存储我们自定义的.msg文件。
例如,<package_name>/msg/Person.msg
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
(2).msg 文件编译设置
为了编译.msg文件,我们首先需要在package.xml文件中添加包依赖:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
然后修改CMakeLists.txt文件:
# 在 find_package 中添加 message_generation
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
message_generation
)
# catkin 依赖设置
catkin_package(CATKIN_DEPENDS
message_runtime
)
# 设置需要编译的 msg 文件
add_message_files(FILES
Person.msg
)
generate_messages()去掉注释
generate_messages(DEPENDENCIES
std_msgs
)
在需要用到该消息的cpp文件中包含消息头文件:
#include <package_name/Person.h>
最后回到workspace路径下进行编译即可。
参考文档:【ROS 学习笔记】通信机制 - 知乎