赞
踩
ROS程序由多个功能组成;一个功能由多个节点完成;各节点的功能独立,节点与节点间通过不同的通信方式,实现数据的传输,完成功能。即总功能由分功能组成,各分功能由节点的子功能及节点间的通信完成。
话题是一种节点与节点间进行一对多、单向传输的数据通信方式,数据通过话题的发布与订阅实现传送。
参与节点及节点功能:
1. 发布者节点Talker: 发布话题。
2. 订阅者节点Listener: 订阅话题。
3. 节点管理器ROS Master:保管节点注册信息,匹配话题的订阅者和发布者,并帮助订阅者和发布者的建立连接。
通信过程如下:
七步骤如下:
步骤 | 步骤内容 | 类比 |
---|---|---|
1.发布者注册 | 向节点管理器ROS Master注册相关信息,包括:节点信息、发布的话题 | 在婚姻介绍所登记信息 |
2.订阅者注册 | 向节点管理器ROS Master注册相关信息,包括:节点信息、订阅的话题 | 在婚姻介绍所登记信息 |
3.节点管理器进行话题匹配 | 保管注册信息,匹配话题相同的Talker和Listener,通过RPC向Listener发送Talker的RPC地址 | 婚姻介绍所举办相亲会 |
4.订阅者向发送连接请求 | Listener通过RPC向Talker发送连接请求,传输话题名、消息类型、通信协议 | 相亲会上选择中意对象 |
5.发布者确认连接请求 | Talker接收连接请求,通过RPC向Listener确认连接 | 也看上了,坐下 |
6.发布者尝试与订阅者建立网络连接 | Listener接收到确认信息后,通过TCP与Talker建立网络连接。 | 聊得不错,加微信 |
7.发布者向订阅者发布消息 | 成功建立连接后,Talker开始向Listener发送话题消息数据 | 同意好友请求,火热聊天 |
打开一终端
$ roscore
打开另一终端
$ rosrun turtlesim turtlesim_node
[ INFO] [1679577658.273288946]: Starting turtlesim with node name /turtlesim
[ INFO] [1679577658.283734154]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
再启另一终端
$ rosrun turtlesim turtle_teleop_key
Reading from keyboard
---------------------------
Use arrow keys to move the turtle. 'q' to quit.
此时,通过输入四个方向键可以使乌龟动起来。
再启另一终端:
$ rosrun rqt_graph rqt_graph
rqt_graph:图形化的工具,画出正在发布主题和订阅的关系图
再启另一终端:
$ rostopic info /turtle1/cmd_vel
Type: geometry_msgs/Twist
Publishers:
* /teleop_turtle (http://ubuntu:45347/)
Subscribers:
* /turtlesim (http://ubuntu:39403/)
上述终端中的信息可以看出, /teleop_turtle节点发布话题 /turtle1/cmd_vel ,/turtlesim节点订阅话题 /turtle1/cmd_vel,但话题名上看不出传递了什么信息。
话题要实现信息传递,实际上是通过话题下的消息message,例如:话题/turtle1/cmd_vel的消息是[geometry_msgs/Twist] ,话题和消息的关系可以类比成报纸和报纸上的内容。
话题的消息数据类型是根据需要决定的。查看消息[geometry_msgs/Twist] 传递数据:
$ rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z
上述过程验证了节点Node通过发布和订阅Topic实现信息的传递。
前提:ROS 创建工作空间和软件包
当前所处软件包为beginner_tutorials,该软件包的文件系统结构如下图:
话题消息的数据结构:为单个数据结构。即由一组字段定义,字段间的顺序无关紧要。
一个字段由类型和名称组成,一个字段的结构:<type>空格<name> = <value>
<type>字段的类型:数据类型 + 数组说明符[](可选)
→数据类型:内置类型 或者 非内置类型
→数组说明符[]:有界[N] 或者 无界[]
<name> 字段的名称: 由小写字母、数字、下划线组成
= <value>字段值:可选, ROS1中表明该字段是常量
#注释
自定义话题消息文件的后缀为.msg,消息名称与软件包名的组合实现唯一标识消息。
$ roscd beginner_tutorials
$ mkdir msg
$ cd msg
$ touch Example.msg
编辑Example.msg 文件
int64 num #内置类型
std_msgs/String str2 #ROS其他消息包
char[] ch #数组
string str1 = "hello" #常量,此行的注释在实例时不要添加,会被认为是字符串内容
编辑 ‘软件包’ 下的package.xml 文件(修改后)
#下面两行取消注释,不存在,则添加
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
# <build_depend> 编译时的依赖
# <exec_depend> 运行时的依赖
编辑‘软件包’ 下CMakeLists.txt 文件(修改后)
# 需添加部分1:添加消息生成依赖 find_package(catkin REQUIRED COMPONENTS ....... message_generation ) # 需添加部分2 :添加运行时依赖关系 catkin_package( ... CATKIN_DEPENDS message_runtime ... ...) # 需修改部分3: # 1)删除前面的# 符号 # 2)替换Message1.msg,Message2.msg 为 Example.msg add_message_files( FILES Example.msg ) # 需修改部分4: 删除前面的# 符号 ,int64是规定的ros标准消息类型,属于消息包std_msgs generate_messages( DEPENDENCIES std_msgs )
查看消息是否创建成功:
$ rosmsg show beginner_tutorials/Example
string str1="hello"
int64 num
std_msgs/String str2
string data
char[] ch
std_msgs/String是ROS的标准数据包。
要使用自定义消息,需将.msg 文件编译为 .h 文件,再引入到程序中 。
$ cd ~/catkin_ws
$ catkin_make
编译后,生成相应的Example.h。
Example.h头文件位置在:
./devel/include/beginner_tutorials/Example.h
在软件包的src文件中,创建talker.cpp文件。
$ touch talker.cpp
编辑talker.cpp文件:
#include "ros/ros.h" #include "beginner_tutorials/Example.h" //自定义消息包 int main(int argc, char **argv) { ros::init(argc, argv, "talker");//初始化节点,标识"listener"的节点名, 需唯一 ros::NodeHandle n;//初始化句柄 ros::Publisher chatter_pub = n.advertise<beginner_tutorials::Example>("chatter", 1000); //定义发布者,发布话题"chatter" ros::Rate loop_rate(10); beginner_tutorials::Example example; //定义消息对象,以组织发布的数据 int count = 0; while (ros::ok()) { example.str2.data = "talker"; example.num = count; example.ch.push_back('A'); ROS_INFO(" %s say :%s, the %c part is %ld ", example.str2.data.c_str(), example.str1.c_str(), example.ch.at(0), example.num); ++count; chatter_pub.publish(example); //发布话题 ros::spinOnce(); loop_rate.sleep(); } return 0; }
编辑CMakeLists.txt,增添内容:
#生成可执行文件
add_executable(listener src/listener_msg.cpp)
#需要连接的库
target_link_libraries(listener ${catkin_LIBRARIES})
#需要的依赖
add_dependencies(listener ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
在工作空间下编译,生成可执行文件。
$ cd catkin_ws
$ catkin_make
$ source ./devel/setup.bash
可执行文件放置于:devel/lib/beginner_tutorials
在软件包的src文件中,创建listener.cpp
$ touch listener.cpp
编辑 listener.cpp
# include "ros/ros.h" #include "beginner_tutorials/Example.h" //自定义消息 //回调函数:处理订阅消息 void chatterCallback(const beginner_tutorials::Example::ConstPtr& msg) { ROS_INFO("listener get the value is : [%ld]", msg->num); } int main(int argc, char **argv) { ros::init(argc, argv, "listener");//初始化节点,标识"listener"的节点名, 需唯一 ros::NodeHandle n; //初始化句柄 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); //定义订阅者,订阅话题"chatter",绑定回调函数 ros::spin();//设置循环,调用回调函数 return 0; }
回调函数的要求:
- 参数只能有一个且必须以const修饰
- 参数类型为xxxConstPtr
- 参数为引用传递
- 函数没有返回值
编辑CMakeLists.txt,增添内容:
add_executable(talker src/talker_msg.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
在工作空间下编译, 生成可执行文件。
$ cd catkin_ws
$ catkin_make
$ source ./devel/setup.bash
可执行文件放置于:devel/lib/beginner_tutorials
打开终端,运行roscore。
$ roscore
另起终端,先运行订阅者listener节点。
$ rosrun beginner_tutorials listener
再另起终端,运行发布者talker节点
$ rosrun beginner_tutorials talker
结果:
当前软件包的文件系统结构:(红框为当前教程新增)
6.1 ros指令总结:
roscore 运行节点管理器
rosrun <package_name> <node_Name> : 运行软件包的节点
rospack info <topic_name> :打印有关活动主题的详细信息
rosmsg info <package_name>/<\message_name> : 打印与话题相关的发布者订阅者信息
rosmsg show <package_name>/<\message_name> : 查看话题的消息
6.2 核心函数:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。