ROS学习笔记2:话题通信

前言

本人ROS小白,利用寒假时间学习ROS,在此以笔记的方式记录自己每天的学习过程。争取写满20篇(2/20)。
环境:Ubuntu20.04、ROS1:noetic
环境配置:严格按照下方学习链接的教程配置,基本一次成功。
学习链接:【Autolabor初级教程】ROS机器人入门
对应链接文档:ROS机器人入门课程《ROS理论与实践》
笔记绝大部分代码使用Python语言编写。
本期关键词:理论模型,发布,订阅,自定义msg

话题通信理论模型

  1. 理论模型主要分为三个部分:
    • ROS Master(管理者)
    • Talker(发布者,对应publish)
    • Listener(订阅者,对应subscribe)
  2. 形象了解话题通信:ROS的主要通讯方式:Topic话题与Message消息
  3. 涉及到7个步骤:
    • 第0步:Talker向Master注册
    • 第1步:Listener向Master注册
    • 第2步:Master向Listener发送匹配信息
    • 第3步:Listener向Talker发送请求
    • 第4步:Talker确认请求(以上均是RPC协议)
    • 第5步:Listener与Talker件里连接(以下均是TCP协议)
    • 第6步:Talker向Listener发送消息
  4. 以上步骤了解即可,编程时基本不会用到,我们需要关注的主要是以下三个内容:
    • 发布者
    • 订阅者
    • 消息载体

自定义消息类型

这里先简要概述话题通信自定义msg的内容,接下来的“发布者实现“和“订阅者实现”会结合代码来演示如何使用自定义消息类型msg。

  1. ROS中有原装的数据类型,在std_msgs依赖包里,例如StringInt32Char等(该依赖包里的数据类型首字母就是大写的)。
  2. 在某些情况下,std_msgs包里的数据类型是不能满足我们的要求的,ROS允许用户自定义数据类型,来满足用户特定场景下的需求。
  3. 自定义消息类型的创建方式:
    1. 在功能包下新建msg目录,添加Person.msg文件,名字是自定义的,你也可以叫Man.msg。(msg文件名称的首字母最好大写。)
    2. 编辑.msg文件内容,很简单,根据需求来添加就行,数据类型就是stringint16float64这些。注意:不要直接intfloatchar这几个C语言的数据类型,msg文件和这些还是有些区别的,下面列举一些msg文件允许的字段类型:
      • int8, int16, int32, int64 (或者无符号类型: uint16、uint32等)
      • float32, float64
      • string
      • time, duration
      • Header
    3. 编辑配置文件,这一步包含的步骤较多,比较繁琐:
package.xml文件修改:
	在原来文件的基础上添加两行,即
	message_generationmessage_runtime
	整体如下:roscpp等包是原来添加依赖库时自动创建的
	catkin
	roscpp
	rospy
	std_msgs
	message_generation
	roscpp
	rospy
	std_msgs
	roscpp
	rospy
	std_msgs
	message_runtime

CMakeLists.txt文件修改
	第一部分:
	find_package(catkin REQUIRED COMPONENTS
	roscpp
	rospy
	std_msgs
	message_generation
	)
	第二部分:
	add_message_files(
	FILES
	Person.msg
	)
	第三部分:这个不用添加东西,直接取消注释就行
	enerate_messages(
	DEPENDENCIES
	std_msgs
	)
	第四部分:只用取消一行的注释
	catkin_package(
	#  INCLUDE_DIRS include
	#  LIBRARIES helloworld
	CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
	#  DEPENDS system_lib
	)
  1. 配置好以上,用catkin_make指令编译一下,没有报错就问题不大。
  2. devel/lib/python3/msg/目录下能找到_Person.py文件,则表示msg文件配置成功。(记住这个路径的文件,待会会用到。)
  3. 接下来,在_Person.py所在目录下打开终端,执行pwd指令查看当前目录路径,选中/home/用户名/工作空间/devel/lib/python3/dist-packages部分,添加到settings.json文件中,如下所示。这一步的目的是方便接下来的代码编写(会自动补齐内容),不添加也不会影响编译运行。
{
    "python.autoComplete.extraPaths": [
        "/opt/ros/noetic/lib/python3/dist-packages",
        "/home/用户名/工作空间/devel/lib/python3/dist-packages"
    ],
    "python.analysis.extraPaths": [
        "/opt/ros/noetic/lib/python3/dist-packages",
        "/home/用户名/工作空间/devel/lib/python3/dist-packages"
    ]
}

小tips

我们在执行代码的过程中,会经常用到source ./devel/setup.bash指令,虽然指令并不复杂,但是每新开一次终端都得输入一次,效率还是蛮低的。除此之外,使用Python编写代码,我们需要对新建的Python文件进行权限修改操作:chmod +x *.py,这条指令也不复杂,但是输入多了效率也挺低的。

为了解决以上问题,我修改主目录下的.bashrc文件,在其末尾添加以下内容:

alias g-path='source ./devel/setup.bash'
alias get-x='chmod +x *.py'

(添加完之后要执行source .bashrc指令让其生效)
添加之后,以后要输入source ./devel/setup.bash指令就可以用g-path代替,同理,chmod +x *.py可以用get-x代替。

发布者实现

基本消息类型

  1. 在ROS功能包下的scripts目录下新建Python文件:demo_pub.py
  2. 代码内容如下:
#! /usr/bin/env python

import rospy
from std_msgs.msg import String

if __name__ == "__main__":
    rospy.init_node("talker_pub")
    pub = rospy.Publisher("chat",String,queue_size=10)
    msg=String()
    cont="hello! How are you? "
    count=0
    rate=rospy.Rate(1)
    rospy.sleep(3.0)
     
    while not rospy.is_shutdown():
        msg.data=cont+str(count)
        pub.publish(msg)
        rate.sleep()
        rospy.loginfo("output data is: %s",msg.data)
        count+=1
  1. 修改CMakeLists.txt文件,将新创建的Python文件路径添加到catkin_install_python内容下:(由于CMakeLists.txt文件的修改方法比较简单,之后的文章可能不会提及。)
catkin_install_python(PROGRAMS
  scripts/hello_p.py # 这是之前添加的
  scripts/demo_pub.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
  1. 编译后执行即可。

自定义消息类型

  1. 创建并编写Person.msg文件,配置方法参考上文。
string name
int16 age
float64 height
  1. 创建Python文件,代码如下:
#! /usr/bin/env python

import rospy
from helloworld.msg import Person

if __name__ == "__main__":
    rospy.init_node("pub_msg")
    pub = rospy.Publisher("msg_test",Person,queue_size=10)
    p=Person()
    p.name="Zhang San"
    p.age=18
    p.height=1.78
    
    rate=rospy.Rate(1)
    while not rospy.is_shutdown():
        pub.publish(p)
        rate.sleep()
        rospy.loginfo("name is: %s, age is: %d, height is: %f", p.name,p.age,p.height)
  1. 修改CMakeLists.txt文件后,编译执行即可

订阅者实现

基本消息类型

代码如下:

#! /usr/bin/env python

import rospy
from std_msgs.msg import String

def doMsg(msg):
    rospy.loginfo("listener get: %s",msg.data)

if __name__=="__main__":
    rospy.init_node("listener_sub")
    sub = rospy.Subscriber("chat",String,doMsg,queue_size=10)
    rospy.spin()

自定义消息类型

代码如下:(msg文件已经创建编译,直接用就行)

#! /usr/bin/env python

import rospy
from helloworld.msg import Person

def doMsg(p):
    rospy.loginfo("I get: name is: %s, age is: %d, height is: %2f",p.name,p.age,p.height)
    
if __name__=="__main__":
    rospy.init_node("sub_msg")
    sub = rospy.Subscriber("msg_test",Person,doMsg,queue_size=10)
    rospy.spin()

注意事项

  1. 订阅方订阅的话题必须与发布方发布的话题一致,否则无法接收消息。rospy.Subscriberrospy.Publisher的第一个参数就是话题名。
  2. 发布方名称和订阅方名称不能一致,否则无法同时执行。rospy.init_node()指定的就是发布方或订阅方。
  3. rospy.spin()的作用:让ROS节点保持运行状态,以便能够处理消息和回调。如果不调用 rospy.spin(),你的程序可能会在执行完所有初始化代码后立即退出,这样回调函数就无法被触发。

往期内容

  1. ROS学习笔记1:基础知识

你可能感兴趣的:(学习,笔记,python,c++)