系列文章目录
第一章 ROS空间创建、helloworld的实现、开启多个节点
第二章 话题通信
第三章 服务通信
第四章 参数服务器
第五章 常用指令
第六章 通信机制实操
第七章 ROS通信机制进阶(常用API、Python模块的导入)
第八章 元功能包、节点运行管理launch文件(teleop_twist安装方法)
第九章 重名问题、分布式通信
第十章-第一节 TF坐标变换(内含PyKDL 和PyInit__tf2功能缺失等解决)
第十章-第二节 TF坐标变换实操
第十章-第三节 rosbag、rqt工具箱
第十一章-第一节 机器人系统仿真(URDF相关)
第十一章-第二节 机器人系统仿真(Gazebo相关)
第十二章 机器人导航(仿真)
前言
现在大二,之前大一有幸参加了2021的国赛,很壮烈的拿了个江苏赛区的二等奖。但发现无人机这个题,真的是往堆钱上走了。不上ROS不行,现在来记录一下一个纯小白学习ROS的过程和遇到的问题。防止学弟、学妹们再走我走过的弯路。板子用的是学长给的Jetson Nano(4GB),版本是Ubuntu18.04(已配置好基础ROS所需配置)。
一、话题通信是什么?
话题通信是以发布订阅的方式实现不同节点之间数据交互的通信模式。也即:一个节点发布消息,另一个节点订阅该消息。适用于不断更新的数据传输相关的应用场景。
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master (管理者)
- Talker (发布者)
- Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。
整个流程由以下步骤实现:
- Talker注册(RPC是远程调用)
Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。 - Listener注册
Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。 - ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。 - Listener向Talker发送请求
Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。 - Talker确认请求
Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。 - Listener与Talker件里连接
Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。 - Talker向Listener发送消息
连接建立后,Talker 开始向 Listener 发布消息。
- 注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议
- 注意2: Talker 与 Listener 的启动无先后顺序要求
- 注意3: Talker 与 Listener 都可以有多个
- 注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker与 Listern 照常通信。
二、话题通信基本操作(Python)
1.流程
- 需求
编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出。
- 分析
在模型实现中,ROS master 不需要实现(roscore已经内部包含了),而连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方
- 接收方
- 数据(此处为普通文本)
- 实现
- 编写发布方实现;
- 编写订阅方实现;
- 为python文件添加可执行权限;
- 编辑配置文件;
- 编译并执行。
2.编写发布方
- 在工作空间的src目录下新建功能包
-选定 src 右击 —> create catkin package - 设置包名 添加依赖
输入roscpp rospy std_ms
快捷键 ctrl + shift + B 调用编译 - 在 功能包 下新建 scripts 文件夹,添加 python 文件,并添加可执行权限
代码如下:
#! /usr/bin/env python3
#encoding:utf-8
"""
需求: 实现基本的话题通信一方发布数据,一方接收数据,
实现的关键点:
1.发送方
2.接收方
3.数据(此处为普通文本)
PS: 二者需要设置相同的话题
消息发布方:
循环发布信息:HelloWorld 后缀数字编号
实现流程:
1.导包
2.初始化 ROS 节点:命名(唯一)
3.实例化 发布者 对象
4.组织被发布的数据,并编写逻辑发布数据
"""
#1.导包
import rospy
from std_msgs.msg import String #发布的消息类型
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一)
rospy.init_node("talker_p")
#3.实例化 发布者 对象
pub = rospy.Publisher("chatter",String,queue_size=10) #话题名称,发布类型,容量是10
#4.组织被发布的数据,并编写逻辑发布数据
msg = String() #创建 msg 对象
msg_front = "hello 你好"
count = 0 #计数器
# 设置循环频率 1是1hz
rate = rospy.Rate(1)
rospy.sleep(3 )#防止丢数据,因为创建需要时间。
while not rospy.is_shutdown(): #判断这个节点是否关闭
#拼接字符串
msg.data = msg_front + str(count)
pub.publish(msg)
rate.sleep() #每隔1s执行一次循环
rospy.loginfo("写出的数据:%s",msg.data)
count += 1
- 在scripts下打开命令行增加可执行权限:`chmod +x *.py
- 更改cmake文件162-165:
catkin_install_python(PROGRAMS
scripts/demo01_pub_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
- 快捷键 ctrl + shift + B 调用编译
新开一个命令行,启动rosrore
刷新下环境变量source ./devel/setup.bash
- 如果遇到:
非法指令 (核心已转储)
在刷新变量签加一行:export OPENBLAS_CORETYPE=ARMV8
- 如果遇到:ModuleNotFoundError: No module named ‘rospkg‘
其实是:因为ubuntu18.04使用的ros版本是melodic,而melodic默认使用的是python2,但是我们跑代码实际用的是python3。
所以在命令行中输入:
alias python=python3
source ~/.bashrc
pip3 install rospkg
List itemrosrun 功能包名 python文件
再打开一个命令行
source ./devel/setup.bash
rostopic echo 话题名称
3.订阅方
- 在scripts 文件夹,添加 python 文件,并添加可执行权限
代码如下:
#! /usr/bin/env python3
#encoding:utf-8
"""
需求: 实现基本的话题通信,一方发布数据,一方接收数据,
实现的关键点:
1.发送方
2.接收方
3.数据(此处为普通文本)
消息订阅方:
订阅话题并打印接收到的消息
实现流程:
1.导包
2.初始化 ROS 节点:命名(唯一)
3.实例化 订阅者 对象
4.处理订阅的消息(回调函数)
5.设置循环调用回调函数
"""
#1.导包
import rospy
from std_msgs.msg import String
def doMsg(msg):#msg是订阅到的数据
rospy.loginfo("I heard:%s",msg.data) #msg.data是保存msg的数据的
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一)
rospy.init_node("listener_p")
#3.实例化 订阅者 对象
sub = rospy.Subscriber("chatter",String,doMsg,queue_size=10)#话题名称,发布类型,回调函数,最大容量是10条消息
#4.处理订阅的消息(回调函数)
#5.设置循环调用回调函数
rospy.spin()
- 更改cmake文件(162-166)
catkin_install_python(PROGRAMS
scripts/demo01_pub_p.py
scripts/demo02_sub_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
- 快捷键 ctrl + shift + B 调用编译
在scripts下打开命令行增加可执行权限:chmod +x *.py
新开一个命令行,启动rosrore
刷新下环境变量:
source ./devel/setup.bash
rosrun 功能包名 python文件(发布者)
- 如果遇到:
非法指令 (核心已转储)
在刷新变量签加一行:export OPENBLAS_CORETYPE=ARMV8
- 刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(订阅者)
- 注意哪怕发布方和订阅方一个是拿C++写的一个是python也可以进行交互(两个话题得一样)
4.话题通信自定义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标头。 - 流程:
按照固定格式创建 msg 文件
编辑配置文件
编译生成可以被 Python 或 C++ 调用的中间文件
以下为具体操作流程:
- 定义msg文件
在功能包下新建msg文件夹,并新建xxx.msg文件
定义,例如代码:
string name
uint16 age
float64 height
- 编辑配置文件
package.xml中添加编译依赖与执行依赖
在51行左右:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 msg 相关配置
在10行左右:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
在50行左右:
## 配置 msg 源文件
add_message_files(
FILES
Person.msg
)
在71行左右:(把注释放开)
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
在108行:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
快捷键 ctrl + shift + B 调用编译
- vscode配置
为了方便代码提示以及误抛异常,需要先配置 vscode,将前面生成的 python 文件路径配置进 settings.json
进入/home/lzl/myworld/devel/lib/python2.7 在命令行中打开
输入pwd
复制路径
打开/home/lzl/myworld/.vscode/settings.json
修改:
{
"python.autoComplete.extraPaths": [
"/opt/ros/noetic/lib/python3/dist-packages",
"/xxx/yyy工作空间/devel/lib/python3/dist-packages"
]
}
- 发布方
在scripts 文件夹,添加 python 文件,并添加可执行权限
代码如下:
#! /usr/bin/env python3
#encoding:utf-8
"""
发布方:
循环发送消息
"""
import rospy
from plumbing_pub_sub.msg import Person
if __name__ == "__main__":
#1.初始化 ROS 节点
rospy.init_node("talker_person_p")
#2.创建发布者对象
pub = rospy.Publisher("chatter_person",Person,queue_size=10)
#3.组织消息
p = Person()
p.name = "葫芦瓦"
p.age = 18
p.height = 0.75
#4.编写消息发布逻辑
rate = rospy.Rate(1)
while not rospy.is_shutdown():
pub.publish(p) #发布消息
rate.sleep() #休眠
rospy.loginfo("姓名:%s, 年龄:%d, 身高:%.2f",p.name, p.age, p.height)
修改CMakeLists.txt
162-167:
catkin_install_python(PROGRAMS
scripts/demo01_pub_p.py
scripts/demo02_sub_p.py
scripts/demo03_pub_person_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
新开一个命令行,启动rosrore
刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(发布者)
如果遇到:
非法指令 (核心已转储)
在刷新变量签加一行:export OPENBLAS_CORETYPE=ARMV8
刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(发布者)
再打开一个命令行
source ./devel/setup.bash
rostopic echo 话题名称
- 订阅方
在scripts 文件夹,添加 python 文件,并添加可执行权限
代码如下:
#! /usr/bin/env python3
#encoding:utf-8
"""
订阅方:
订阅消息
"""
import rospy
from plumbing_pub_sub.msg import Person
def doPerson(p):
rospy.loginfo("接收到的人的信息:%s, %d, %.2f",p.name, p.age, p.height)
if __name__ == "__main__":
#1.初始化节点
rospy.init_node("listener_person_p")
#2.创建订阅者对象
sub = rospy.Subscriber("chatter_person",Person,doPerson,queue_size=10)
rospy.spin() #4.循环
添加可执行权限
修改CMakeLists.txt
162-168:
catkin_install_python(PROGRAMS
scripts/demo01_pub_p.py
scripts/demo02_sub_p.py
scripts/demo03_pub_person_p.py
scripts/demo04_sub_person_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
快捷键 ctrl + shift + B 调用编译
刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(发布者)
如果遇到:
非法指令 (核心已转储)
在刷新变量签加一行:export OPENBLAS_CORETYPE=ARMV8
刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(发布者)
再打开一个命令行
刷新下环境变量
source ./devel/setup.bash
rosrun 功能包名 python文件(订阅者)
总结
以上就是今天要讲的内容,本文仅仅简单记录了ROS的话题通信,如果有问题请在博客下留言或者咨询邮箱:layraliu@foxmail.com