一、话题通信理论模型
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:
ROS Master (管理者)
Talker (发布者)
Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。
整个流程由以下步骤实现:
0.Talker注册
Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。
1.Listener注册
Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。
2.ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。
3.Listener向Talker发送请求
Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。
4.Talker确认请求
Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。
5.Listener与Talker件里连接
Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。
6.Talker向Listener发送消息
连接建立后,Talker 开始向 Listener 发布消息。
注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议
注意2: Talker 与 Listener 的启动无先后顺序要求
注意3: Talker 与 Listener 都可以有多个
注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。
二、简单的发布方框架
#include "ros/ros.h"
#include "std_msgs/String.h"
/*
发布方实现
1.包含头文件
ros中的文本类型对应---->"std_msgs/string.h"
2.初始化ros节点(创建二狗子对象)
3.创建节点句柄
4.创建发布者对象
5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
//2.初始化ros节点
ros::init(argc,argv,"ergouzi");
//3.创建节点句柄
ros::NodeHandle nh;
//4.创建发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
//5.编写发布逻辑并发布数据
//5.1先创建被发布的消息
std_msgs::String msg;
//5.2编写循环,循环中发布数据
while(ros::ok())
{
msg.data = "hello";
pub.publish(msg);
}
return 0;
}
注意:通过ros topic echo + 话题
可以查看发布的消息
三、要求以每秒10次发布文本并跟上编号
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
发布方实现
1.包含头文件
ros中的文本类型对应---->"std_msgs/string.h"
2.初始化ros节点(创建二狗子对象)
3.创建节点句柄
4.创建发布者对象
5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
//设置编码,解决中文乱码问题
setlocale(LC_ALL,"");
//2.初始化ros节点
ros::init(argc,argv,"ergouzi");
//3.创建节点句柄
ros::NodeHandle nh;
//4.创建发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
//5.编写发布逻辑并发布数据
//5.1先创建被发布的消息
std_msgs::String msg;
//发布频率
ros::Rate rate(10);//创建Rate对象
//设置编号
int count = 0;
//为了防止先执行订阅方却还是不能接收前几条消息,因为这时发布方已经开始发送但是还未注册成功
ros::Duration(3).sleep();
//5.2编写循环,循环中发布数据
while(ros::ok())
{
count++;
//msg.data = "hello";
//实现字符串拼接
std::stringstream ss;//创建stringstream对象
ss << "hello -->" << count;//实现拼接
msg.data = ss.str(); //将拼接好的数据赋值给data并转换成字符串格式
pub.publish(msg);
//添加日志
ROS_INFO("发布的数据是:%s",ss.str().c_str());//转换为c分格的字符串输出
rate.sleep();//前面调用了Rate10HZ这里相当于睡0.1秒
}
return 0;
}
四、订阅方实现
#include "ros/ros.h"
#include "std_msgs/String.h"
/*
订阅方实现
1.包含头文件
ros中的文本类型对应---->"std_msgs/string.h"
2.初始化ros节点(创建翠花对象)
3.创建节点句柄
4.创建订阅者对象
5.处理订阅到的数据
6.spin()函数
*/
//回调函数处理订阅到的消息
void domsg(const std_msgs::String::ConstPtr &message)
{
//通过message获取并操作订阅到的数据
ROS_INFO("翠花订阅的数据:%s",message->data.c_str());
}
int main(int argc, char *argv[])
{
//设置编码
setlocale(LC_ALL,"");
//2.初始化ros节点(创建翠花对象)
ros::init(argc,argv,"cuihua");
//3.创建节点句柄
ros::NodeHandle nh;
//4.创建订阅者对象
ros::Subscriber sub = nh.subscribe("fang",10,domsg);
//5.处理订阅到的数据
ros::spin();//循环读取接收的数据,并调用回调函数处理
return 0;
}
五、ros中的解耦合
(1)用Python和C++写的话题通信程序两者之间可以正常数据通信。
六、话题通信自定义msg
在 ROS 通信协议中,数据载体是一个较为重要组成部分,
ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:
String、Int32、Int64、Char、Bool、Empty.... 但是,
这些数据一般只包含一个 data 字段,结构的单一意味着功能上
的局限性,当传输一些复杂的数据,比如: 激光雷达的信息...
std_msgs 由于描述性较差而显得力不从心,这种场景下可以
使用自定义的消息类型.
msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:
int8, int16, int32, int64 (或者无符号类型: uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]
ROS中还有一种特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头。
(1)在当前功能包下创建msg文件夹,里面创建Person.msg文件包含内容为自己需要的变量类型,譬如:string name、int32 age、float32 height
(2)配置当前功能包的package.xml文件,添加<build_depend>message_generation</build_depend> , <exec_depend>message_runtime</exec_depend>
(3)配置当前功能包的CMakeLists.txt有关message的相关信息
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
## 配置 msg 源文件
add_message_files(
FILES
Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
(4)编译后在/home/zzh/demo03_ws/devel/include/plumbing_pub_sub
下面生成一个Person.h文件,如果是Python相关的会在/home/zzh/demo03_ws/devel/lib/python3/dist-packages/plumbing_pub_sub/msg
生成_Person.py文件
七、自定义msg的发布方实现
(1)配置.vscode下的c_cpp_properties.json
加入自定义msg的路径
(2)消息发布方的实现