【ROS话题通信】发布者和订阅者

前言

本文记录ROS话题通信的学习过程,便于后续复习。首先明确,ROS中的话题通信,在ROS通信中非常重要,实现了分布式发布接收消息,也是实现了不同编程语言间的解耦,下面记录下自己学习过程中的相关代码和配置。
学习环境:VSCode Ubuntu18.04
主要分为话题订阅和发布标准信息和自定义信息两个部分,每个部分会记录下C++和Python的实现

1 标准信息格式下的话题通信

1.1 C++版

发布者逻辑:

1 引入头文件
2 初始化ROS节点
3 定义句柄
4 创建发布者对象
5 定义发布的消息及消息发布的一些设置

发布者代码:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char *argv[]){
    //控制中文不乱码
    setlocale(LC_ALL,"");
    //1 初始化ros节点
    ros::init(argc,argv,"publisher");
    //2 创建句柄
    ros::NodeHandle nh;
    //3 创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //4 编写发布逻辑并发布数据
     // 创建被发布的信息
    std_msgs::String msg;
    //定义频率
    ros::Rate rate(10);
    //设置编号
    int count=0;
    //可让发送数据前 休眠3秒 当作是注册到roscore的时间
    //这样可保证订阅者 可 接收到前几个信息
    ros::Duration(3).sleep();
     //循环发布
     while(ros::ok()){
        count++;
        std::stringstream ss;
        ss << "hello ---> " << count;

        msg.data = ss.str();
        pub.publish(msg);
        //添加日志
        ROS_INFO("发布的数据为: %s",ss.str().c_str());
        rate.sleep();
        //官方建议 主要用于调用回调函数
        ros::spinOnce();
     }
    return 0;
}

订阅者逻辑:

1 引入头文件
2 初始化ROS节点
3 创建订阅者对象及消息回调函数

订阅者代码:

#include "ros/ros.h"
#include "std_msgs/String.h"
void doMsg(const std_msgs::String::ConstPtr &msg){
    ROS_INFO("subcribe msg: %s", msg->data.c_str());
}
int main(int argc, char *argv[]){
    setlocale(LC_ALL,"");
    //1 初始化ros节点
    ros::init(argc,argv,"subcribe");
    //2 创建句柄
    ros::NodeHandle nh;
    //3 创建发布者对象
    ros::Subscriber sub = nh.subscribe("fang",10,doMsg);
   //为了处理上面的回调函数
    ros::spin();
    return 0;
}

相关配置和调用测试
1)整个项目的整体配置,这里先设置 tasks.json
在这里插入图片描述

{
	"version": "2.0.0",
	"tasks": [
		{
			"label" : " catkin_make: debug", //代表提示的描述性信息 
			"type": "shell",
			"command": "catkin_make",
			"args": [], 
			//可以使用ctrl+shift+B进行编译项目
			"group": {"kind": "build", "isDefault": true},
			"presentation":{
					"reveal":"always"//可选silence和always代表输出信息
				},
				"problemMatcher": "$msCompile"
			/* 
			"type": "catkin_make",
			"args": [
				"--directory",
				"此处为项目目录/Documents/learn/ros/ros_ws_demo1",
				"-DCMAKE_BUILD_TYPE=RelWithDebInfo"
			],
			"problemMatcher": [
				"$catkin-gcc"
			],
			"group": "build",
			"label": "catkin_make: build"*/
		}
	]
}

2)首先看下文件夹的构成,及根目录为ROS_WS_DEMO,话题通信的代码在plumbing_pub_sub文件夹下(功能包):
在这里插入图片描述
上方每个文件夹的含义:
include:包含一些头文件和编译后的信息
msg:自定义的消息类型文件夹
scripts:Python 脚本文件夹
src:C++源代码文件夹
CMakeList.txt:项目执行配置文件
package.xml:一些依赖库文件
3)写好的代码需要在文件夹下的 CMakeList.txt进行配置
在这里插入图片描述
在这里插入图片描述
配置好后,其实就可以进行编译我们写好的项目了,如果您配置了项目根目录下的tasks.json,直接进行 ctrl+shift+B即可进行编译。如果没出错:
在这里插入图片描述
4)运行过程
4.1 首先打开一个终端,输入下面的命令,启动主节点:

roscore

在这里插入图片描述
4.2 然后打开另一个新终端,分别输入下方的命令,即可得到发布 订阅节点

#激活环境配置
source ./devel/setup.bash
#运行节点
rosrun plumbing_pub_sub sub_node

在这里插入图片描述
4.3 然后打开另一个新终端,分别输入下方的命令,即可得到发布 消息发布节点

#激活环境配置
source ./devel/setup.bash
#运行节点
rosrun plumbing_pub_sub pub_node

在这里插入图片描述
4.4 如果想看下话题的发布情况,也可以重新打开一个终端,输入

rqt_graph#可对当前发布的话题进行可视乎
1.2 Python版

在项目下面新建 scripts 文件夹,用来存储py的脚本
在这里插入图片描述

发布者代码:

#! /usr/bin/env python
import rospy
from std_msgs.msg import String # 发布的消息类型
"""
    使用python实现消息发布:
        1.导包;
        2.初始化ros节点
        3.创建发布者对象
        4.编写发布逻辑并发布数据
"""
if __name__ == "__main__":
    # 2.初始化ros节点
    rospy.init_node("sanDai") # 传入节点名称
    # 3.创建发布者对象
    pub = rospy.Publisher("che",String, queue_size = 10) # che是话题名称,类型是String,消息队列大小为10
    # 4.编写发布逻辑并发布数据
    # 4.1 创建数据类型
    msg = String()

    # 4.2 指定发布频率
    rate = rospy.Rate(1)

    # 4.3 设置计数器
    count = 0

    # 休眠3s,完成在master下面的注册
    rospy.sleep(3)

    # 4.4 循环发布数据
    while not rospy.is_shutdown(): # 判断当前节点是否已经关闭,没有关闭则发布数据
        count += 1 # 如果要将count追加到hello后面,则需要将其变为字符串
        msg.data = "hello" + str(count)
        # 4.5 发布数据
        pub.publish(msg)
        # 4.6 添加日志输出
        rospy.loginfo("发布的数据:%s",msg.data)
        rate.sleep()

订阅者代码:

#! /usr/bin/env python
import  rospy
from std_msgs.msg import String
"""
    订阅实现流程:
        1.导包
        2.初始化ROS节点
        3.创建订阅者对象
        4.回调函数处理数据
        5.spin()
"""
def doMsg(msg): #将订阅到的数据传进来
    rospy.loginfo("订阅的数据:%s",msg.data) #data是数据
if __name__ == "__main__":
    # 2.初始化ROS节点
    rospy.init_node("huaHua")
    # 3.创建订阅者对象
    sub = rospy.Subscriber("fang",String,doMsg,queue_size = 10)
    # sub = rospy.Subscriber("fang",String,doMsg,queue_size = 10) 跨语言通信
    # 4.回调函数处理数据
    # 5.spin()
    rospy.spin()

在Scripts下面编写的Python文件需要增加可执行权限,打开终端,切换到Scripts文件夹下,然后运行命令:

chmod +x *.py#让python文件增加可执行权限

配置过程
在CMakeList.txt中:
在这里插入图片描述
记得编译,然后运行的过程如下:
同样先启动 roscore 然后重新打开终端,执行下面的操作

启动发布者节点:

#激活环境配置
source ./devel/setup.bash
#运行节点
rosrun plumbing_pub_sub pub_node_py.py

在这里插入图片描述
启动订阅者节点:

#激活环境配置
source ./devel/setup.bash
#运行节点
rosrun plumbing_pub_sub sub_node_py.py

在这里插入图片描述

1.3 C++和Python间发布者和订阅者的解耦操作

ROS系统的强大之处在于不同的编程语言间,只要符合话题通信的机制,可以进行互相发布和订阅,只要保证两者发布和订阅的话题一样,不用考虑对应的程序语言实现,即可以按照下面的步骤实现C++和Python语言的解耦通信。
在这里插入图片描述
在这里插入图片描述
则以上两者话题只要保持一致,即可实现通信。相关过程,和上面的运行一样。

2 自定义消息类型的话题通信

以上过程使用的是ROS系统中内置的标准数据类型,但是在实际的项目实践中,往往需要自定义消息类型,这个是在本部分需要注意的内容。

2.1 自定义信息

在这里插入图片描述
内容:

string name
int32 age
float32 height

然后配置package.xml中的依赖文件
在这里插入图片描述
增加下面的语句:

<!-- 自定义消息文件依赖 -->
  <build_depend>message_generation</build_depend>
  <!-- 自定义消息文件依赖 -->
  <exec_depend>message_runtime</exec_depend>

然后在CMakeList.txt中也需要配置 Msg的配置:
在这里插入图片描述
在这里插入图片描述
生成消息时,依赖于std_msgs
在这里插入图片描述
下面的内容中增加 message_runtime 即执行过程中的依赖
在这里插入图片描述
在这里插入图片描述
以上配置完全后,可以进行编译,这时候会发现项目中出现了自定义的 Person.msg
在这里插入图片描述
以上说明后续的代码中,我们可以使用自定义的消息类型了。
另外,为保证我们在源文件编写过程中,有所提示,可以引用到生成的新消息类型,我们需要完成下面的配置:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 C++版

发布者代码:

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ROS_INFO("this is the publisher...");
    ros::init(argc,argv,"ros_pub");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<plumbing_pub_sub::Person>("chat",10);
    
    plumbing_pub_sub::Person person;
    person.name = "123";
    person.age = 10;
    person.height = 10.3;
    //定义发送频率每次发1条
    ros::Rate rate(1);
    while(ros::ok()){
        pub.publish(person);
        ROS_INFO("this is the publisher...%s,%d,%.2f",person.name.c_str(),person.age,person.height);
        rate.sleep();

        ros::spinOnce();
    }
    return 0;
    }

订阅者代码:

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"

void doMsg(const plumbing_pub_sub::Person::ConstPtr& person){
    ROS_INFO("subcribe msg:%s,%d,%.2f",person->name.c_str(),person->age,person->height);
}
int main(int argc, char *argv[]){
    setlocale(LC_ALL,"");
    ROS_INFO("this is a subcribe ... ");
    ros::init(argc,argv,"sub_person");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("chat",10,doMsg);
    ros::spin();
    return 0;
}

CMakeList.txt中:
在这里插入图片描述

add_dependencies(pub_person ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(sub_person ${PROJECT_NAME}_generate_messages_cpp)

在这里插入图片描述
在这里插入图片描述
执行过程和上面标准信息格式的 C++版本一样

2.3 Python版本

发布者代码:

#! /usr/bin/env python

import rospy
from plumbing_pub_sub.msg import Person

if __name__=="__main__":
    print("===",Person)
    rospy.init_node("pub_person_py")
    pub = rospy.Publisher("chat_py",Person,queue_size = 10)

    p = Person()
    p.name = "hh"
    p.age = 10
    p.height = 10.1
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():
            pub.publish(p)
            rospy.loginfo("the msgs:%s,%d,%.2f",p.name,p.age,p.height)
            rate.sleep()

订阅者代码:

#! /usr/bin/env python
import rospy
from plumbing_pub_sub.msg import Person
def doMsg(p):
    rospy.loginfo("the info:%s,%d,%.2f",p.name,p.age,p.height)
if __name__=="__main__":
    rospy.init_node("sub_person_py")
    sub = rospy.Subscriber("chat_py",Person,doMsg,queue_size=10)
    rospy.spin()

CMakeLists.txt配置:
在这里插入图片描述
运行过程和上面的 Python版本一样, 注意编写完py文件后,需要 chmod +x *.py 即增加可执行权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值