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); //节点不死,就一直循环 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(); 延迟第一条数据的发送。