ROS的通信机制
ROS通信架构:
- 各种数据的处理
- 进程的运行
- 消息的传递
- 等等
ROS的核心: 分布式通信机制
ROS的通信方式主要有:
- 话题
- 服务
- 参数
- 动作
ROS编程基础
ros编程
目前最常用的只有roscpp和rospy
Client Library | 介绍 |
---|---|
roscpp | ROS的C++库, 是目前最广泛应用的ROS客户端库, 执行效率高 |
rospy | ROS的Python库, 开发效率高, 通常用在对运行时间没有太大要求的场合, 例如配置、初始化等操作 |
… | … |
整个ROS包括的packages:
roscpp
- roscpp位于
/opt/ros/kinetic
之下, 用C++实现了ROS通信- 在ROS中, C++的代码是通过catkin这个编译系统(扩展的CMake)来进行编译构建的(简单地理解, roscpp就是C++的库, 创建一个CMake工程, 在其中include了roscpp等ROS的libraries, 这样就可以在工程中使用ROS提供的函数)
调用ROS的C++接口, 首先
#include <ros/ros.h>
roscpp的主要部分:
内容 | 说明 |
---|---|
ros::init() | 解析传入的ROS参数, 创建node第一步需要用到的函数 |
ros::NodeHandle | 和topic、service、param等交互的公共接口 |
ros::master | 包含从master查询信息的函数 |
ros::this_node | 包含查询这个进程(node)的函数 |
ros::service | 包含查询服务的函数 |
ros::param | 包含查询参数服务器的函数, 而不需要用到NodeHandle |
ros::names | 包含处理ROS图资源名称的函数 |
主要功能分类:
功能 | 说明 |
---|---|
Initialization and Shutdown | 初始与关闭 |
Topics | 话题 |
Services | 服务 |
Parameter Server | 参数服务器 |
Timers | 定时器 |
NodeHandles | 节点句柄 |
Callbacks and Spinning | 回调和自旋(轮询、循环) |
Logging | 日志 |
Names and Node Information | 名称管理 |
Time | 时钟 |
Exception | 异常 |
初始化节点:
- 调用ros::init()函数, 初始化节点的名称和其他信息, 一般ROS程序以这种方式开始
- 创建ros::NodeHandle对象, 也就是节点的句柄, 它可以用来创建
Publisher
Subscriber
以及做其他事情
关闭节点:
- 通常直接在终端上按Ctrl+C关闭一个节点, 系统会自动触发SIGINT句柄来关闭这个进程
- 也可以通过调用ros::shutdown()来手动关闭节点, 但通常很少这样做
roscpp开发步骤
- 创建源文件
- mkdir -r ros_catkin_wksp/src
- cd ros_catkin_wksp
- catkin_make
- cd src
- catkin_create_pkg ros_node_test roscpp std_msgs
- cd ros_node_test/src
- rosed ros_node_test node_test.cpp 或 vim node_test.cpp
- 编辑源文件
1 |
|
- 运行程序
- 打开ros_node_test下的CMakeLists.txt文件, 添加
- add_executable(node src/node_test.cpp)
- target_link_libraries(node ${catkin_LIBRARIES})
- cd ../..
- catkin_make (编译, 如果成功)
- source devel/setup.sh
- roscore
- rosrun ros_node_test node (新终端)
- rosnode list (新终端, 查看新创建的节点)
roscpp总体编程步骤:
- 创建工作空间(创建包之前的操作只需要一次, 以后不再需要)
- 编写roscpp程序启动节点
- 修改CMakeLists.txt文件
- 编译并运行
- rosnode查看节点是否运行成功
rospy
rospy是Python版本的ROS客户端库, 提供了Python编程需要的接口, 可以认为rospy就是一个Python的模块
这个模块位于/opt/ros/kinetic/lib/python2.7/dist-packages/rospy
之中
由于Python的开发效率高, 但执行效率低
所以, 开发SLAM(即时定位与地图构建), 路径规划, 机器视觉等方面的算法时, 往往优先选择C++
对于一些简单的功能可以优先选用Python
rospy包含的功能与roscpp相似, 都有关于node, topic, service , param, time相关操作
但同时rospy和roscpp也有一些区别:
- rospy没有一个NodeHandle, 像创建publisher, subscriber等操作都被直接封装成了rospy中的函数或类, 调用起来简单直观
- rospy一些接口的命名和roscpp不一致, 有些地方需要开发者注意, 避免调用错误
rospy的组织形式
通常来说, Python代码有两种组织方式, 一种是单独的一个Python脚本, 适用于简单的程序, 另一种是Python模块, 适合体量较大的程序
对于一些小体量的ROS程序, 一般就是一个Python文件, 放在script/
路径下
ROS建议按照以下规范来建立一个Python的模块:
your_package
|- src/
|- your_package/
|- init.py
|- modulefiles.py
|- scripts/
|- your_script.py
|- setup.py
通常常用的ROS命令, 大多数其实都是一个个Python模块, 源代码存放在
ros_comm
仓库的tools
路径下:
https://github.com/ros/ros_comm/tree/lunar-devel/tools
一个命令行工具(如rosbag, rosmsg)都是用模块的形式组织核心代码, 然后在script/
下建立一个脚本来调用模块
rospy中具体的API参考
http://docs.ros.org/api/rospy/html/rospy-module.html
rospy中的各种接口
rospy开发的步骤
- 创建源文件:
cd my_ros_test/src
catkin_create_pkg node_rospy std_msgs rospy
cd node_rospy
mkdir scripts
cd scripts
vim node.py- 编辑源文件:
1 | #!/usr/bin/env python |
- 执行程序:
chmod +x node.py
source my_ros_test/devel/setup.sh
rosrun node_rospy node.py 或者直接 ./node.py
rosnode list (新终端)
ROS话题通信机制
话题在ROS中使用最为频繁, 通信模型也相对复杂
话题分为发布者和订阅者, 发布和订阅的顺序没有要求
话题通信流程
- Talker注册
- Listener注册
- ROS Master进行信息匹配
- Listener发送连接请求
- Talker确认连接请求
- Listener尝试与Talker建立网络连接
- Talker向Listener发布数据
Publisher的编程
Publisher开发流程:
- 初始化ROS节点
- 向ROS Master注册节点信息, 包括发布的话题名和话题中的消息类型
- 创建消息数据
- 按照一定的频率循环发布消息
Subscriber的编程
Subscriber的编程:
- 初始化ROS节点
- 订阅需要的话题
- 循环等待话题消息, 接收到消息后进入回调函数
- 回调函数中完成消息处理
自定义消息的发布和订阅
- 自定义话题消息, 创建msg文件, 内容如下
string name
uint8 age
uint8 sexuint8 unknow = 0
uint8 male = 1
uint8 female = 2
- 在package.xml中添加功能包依赖
1 | <build_depend>message_generation</build_depend> |
- 在CMakeLists.txt中添加编译选项
find_package(… message_generation)
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime)
编辑roscpp和rospy程序
在CMakeLsts.txt中添加
1 | add_executable(person_publisher src/person_publisher.cpp) |
ROS服务通信机制
服务是一种带有应答的通信机制, 与话题相比它减少了listener与talker之间的rpc通信
服务通信流程
- Talker注册
- Listener注册
- ROS Master 进行信息匹配
- Listener与Talker建立网络连接
- Taker向Listener发布服务应答数据
服务通信机制的特点
- 同步通信
- 双向
- 简单高效
客户端client的编程
客户端client开发流程
- 初始化一个ROS节点
- 创建一个Client实例
- 发布服务请求数据
- 等待server处理之后的应答结果
服务端server的编程
服务端server开发流程
- 初始化ROS节点
- 创建Server实例
- 循环等待服务请求, 进入回调函数
- 在回调函数中完成服务功能的处理, 并反馈应答数据
自定义服务数据的编程
- 定义srv文件
string name
uint8 age
uint8 sexuint8 unknown = 0
uint8 male = 1
uint8 female = 2
- 在package.xml中添加功能包依赖
1 | <build_depend>message_generation</build_depend> |
- 在CMakeLists.txt中添加编译选项
find_package(… message_generation)
add_service_files(FILES Person.srv)
generate_messages(DEPENDENCIES std_msgs)
编写相应程序
配置CMakeLists.txt, 向其中添加
1 | add_executable(person_server src/person_server.cpp) |
ROS参数通信机制
ROS参数管理机制
参数类似于ROS中的全局变量, 有ROS Master进行管理, 其通信机制较为简单
不涉及TCP, UDP的通信
参数通信流程:
- Talker设置变量(使用RPC向master发送参数设置数据)
- Listener查询参数值(使用RPC向master查询参数值)
- ROS Master向Listener发送参数值
注意: 如果Listener不主动查询, 则无法获得更新后的参数
ROS参数通信程序设计
参数通信程序设计流程:
- 初始化ROS节点
- get函数获取参数
- set函数设置参数
launch文件的编写
launch文件介绍
启动文件(launch file)便是ros中一种同时启动多个节点的途径, 还可以自动启动ROS Master节点管理器, 而且可以实现每个节点的各种配置, 为多个节点的操作提供了很大便利
- 在ros中以
.launch
或.xml
后缀结尾的文件就是启动文件- 使用launch文件启动时, 会自动启动ROSMaster
- launch文件的编写使用xml格式
- 运行launch文件的方法:
roslaunch 包名称 launch文件名
- 在功能包目录下创建文件夹launch, 存放所有的launch文件
launch文件编写
基本元素
一个简单的launch文件:
1 | <launch> |
这是一个简单而完整的launch文件, 包含一个根元素
元素
xml文件必须要包含一个根元素, launch文件中的根元素采用标签定义, 文件中的其他内容都必须包含在这个标签之中
1 | <launch> |
元素
启动文件的核心是启动ros节点, 采用标签定义, 语法如下
1 | <node pkg="package-name" type="executable-name" name = "node-name" /> |
的其他属性
output = “screen”: 将节点的标准输出打印到中断屏幕, 默认输出为日志文档
respawn = “true”: 复位属性, 该节点停止时, 会自动重启, 默认为false
required = “true”: 必要节点, 当该节点终止时, launch文件中的其他节点也被终止
ns = “namespace”: 命名空间, 为节点内的相对名称添加命名空间前缀
args = “arguments”: 节点需要的输入参数
实际机器人中的节点:
见robot.launch
文件中还出现了, , , 这些都是常用的标签元素
1 | <!-- robot.launch --> |
参数
- parameter是ros系统运行中的参数, 存储在参数服务器中 - 在launch文件中通过元素加载parameter - launch文件执行后, parameter就加载到ROS的参数服务器上了 - 每个活跃的节点都可以通过`ros::param::get()`接口来获取parameter的值, 用户也可以在终端中通过rosparam命令获得parameter的值 用法: - ``
- 在很多复杂的系统中, 参数的数量很多, ros也为我们提供了另外一种类似的参数加载方式 用法: - ` ` - 将一个yaml格式文件中的参数全部加载到ROS参数服务器中
- argument是另外一个概念, 类似于launch文件内部的局部变量, 仅限于launch文件使用, 便于launch文件的重构, 和ROS节点内部的实现没有关系 设置launch文件的内部变量 - ` ` launch文件中需要使用到argument时, 可以使用如下方式调用: - `` - ` `
重映射
- ros的设计目标是提高代码的复用率, 所以ros社区中的很多功能包我们都可以拿来直接使用, 而不需要关注功能包的内部实现, 那么问题就来了, 别人功能包的接口不一定和我们的系统兼容 - ROS提供一种重映射的机制, 简单来说就是取别名 例如: - ` `
嵌套复用
- 在复杂的系统当中, launch文件往往有很多, 这些launch文件之间也会存在依赖关系 - 如果需要直接复用一个已有launch文件中的内容, 可以使用` `标签包含其他launch文件, 这个C语言中的include几乎是一样的 例如: - ` `
内容小结
launch是ROS框架中非常实用, 灵活的功能, 它类似于一种高级编程语言, 可以帮助我们管理启动系统时的方方面面
在使用ROS的过程中, 很多情况下我们并不需要编写大量代码, 仅需要私用已有的功能包, 编辑一下launch文件, 就可以完成很多机器人功能
参考:
http://wiki.ros.org/roslaunch/XML
https://blog.csdn.net/weixin_41995979/article/details/81784987