ROS话题通信机制实操C++
- 创建ROS工程
- 发布方(二狗子)
- 订阅方(翠花)
- 编辑配置文件
- 编译并执行
- 注意
- 订阅的第一条数据丢失
ROS话题通信的理论查阅ROS话题通信流程理论
在ROS话题通信机制实现中,ROS master 不需要实现,且连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方(二狗子)
- 订阅方(翠花)
- 数据(此处为普通文本)
创建ROS工程
创建一个ROS工程包括以下几个步骤:
- 创建一个
topic_ws
的ROS工作空间 - 启动VScode
- VScode 编译ROS
- 创建ROS功能包
详细操作可以查阅VScode创建ROS项目 ROS集成开发环境
发布方(二狗子)
-
基本流程
- 1.包含头文件
- 2.初始化 ROS 节点:命名(唯一)
- 3.实例化 ROS 句柄
- 4.实例化 发布者 对象
- 5.组织被发布的数据,并编写逻辑发布数据
-
代码实现
#include "ros/ros.h" #include "std_msgs/String.h" #include "sstream"/*1.包含头文件 2.初始化 ROS 节点:命名(唯一)3.实例化 ROS 句柄4.实例化 发布者 对象5.组织被发布的数据,并编写逻辑发布数据*/int main(int argc, char *argv[]) { //设置编码setlocale(LC_ALL,"");//2.初始化 ROS 节点:命名(唯一)// 参数1和参数2 后期为节点传值会使用// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一ros::init(argc,argv,"erGouzi");//3.实例化 ROS 句柄ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能//4.实例化 发布者 对象//泛型: 发布的消息类型//参数1: 要发布到的话题//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);//5.组织被发布的数据,并编写逻辑发布数据//数据(动态组织)std_msgs::String msg;// msg.data = "你好啊!!!";int count = 0; //消息计数器//逻辑(一秒1次)ros::Rate rate(1);ros::Duration(3.0).sleep(); //休眠3s等待 publisher 注册完成,不然订阅者读不到第一条消息//节点不死,就一直循环while (ros::ok()){//使用 stringstream 拼接字符串与编号std::stringstream ss;ss << "Hello 你好! --> " << count;msg.data = ss.str();//发布消息pub.publish(msg);//加入调试,打印发送的消息ROS_INFO("发送的消息:%s",msg.data.c_str());//根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;rate.sleep();count++;//循环结束前,让 count 自增//暂无应用ros::spinOnce();}return 0; }
-
验证是否有问题
- 编辑配置文件
修改plumbing_pub_sub
功能包下的CMakeLists.txt,找到add_executable和target_link_libraries,修改成如图所示
- 按快捷键
ctrl + shift + B
编译 - 开启一个Terminal,运行 roscore 命令;再开启一个新的Terminal,运行
source ./devel/setup.bash rosrun plumbing_pub_sub demo_pub_c
;再开启一个Terminal,运行rostopic echo fang,查看订阅数据
- 编辑配置文件
订阅方(翠花)
-
基本流程
- 1.包含头文件
- 2.初始化 ROS 节点:命名(唯一)
- 3.实例化 ROS 句柄
- 4.实例化 订阅者 对象
- 5.处理订阅的消息(回调函数)
- 6.设置循环调用回调函数
-
代码实现
// 1.包含头文件 #include "ros/ros.h" #include "std_msgs/String.h"/*1.包含头文件 2.初始化 ROS 节点:命名(唯一)3.实例化 ROS 句柄4.实例化 订阅者 对象5.处理订阅的消息(回调函数)6.设置循环调用回调函数 */void doMsg(const std_msgs::String::ConstPtr& msg_p) {//通过msg获取并操作订阅到的数据ROS_INFO("我听见:%s",msg_p->data.c_str());// ROS_INFO("我听见:%s",(*msg_p).data.c_str()); }int main(int argc, char *argv[]) {setlocale(LC_ALL,"");//2.初始化 ROS 节点:命名(唯一)ros::init(argc,argv,"cuiHua");//3.实例化 ROS 句柄ros::NodeHandle nh;//4.实例化 订阅者 对象//参数1: 要发布到的话题//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)ros::Subscriber sub = nh.subscribe<std_msgs::String>("fang",10,doMsg);//5.处理订阅的消息(回调函数)//6.设置循环调用回调函数ros::spin();//循环读取接收的数据,并调用回调函数处理return 0; }
发布方的订阅方声明的话题必须一直,案例中的话题是 fang
编辑配置文件
修改 plumbing_pub_sub
功能包下的CMakeLists.txt,找到add_executable和target_link_libraries,修改成如图所示,
编译并执行
-
编译代码
按快捷键ctrl + shift + B
编译
-
执行代码
-
1.启动 roscore;
开启一个Terminal,启动 roscore
-
2.启动发布节点;
开启一个新的Terminalcd topic_ws/ source ./devel/setup.bash rosrun plumbing_pub_sub demo_pub_c
-
3.启动订阅节点。
cd topic_ws/ source ./devel/setup.bash rosrun plumbing_pub_sub demo_sub_c
-
注意
订阅的第一条数据丢失
- 原因:
发送第一条数据时, publisher 还未在 roscore 注册完毕,但是数据已经再发送 - 解决方法:
注册后,加入休眠 ros::Duration(3.0).sleep(); 延迟第一条数据的发送。