ROS基础配置

准备工作

创建工作空间并初始化

1
2
3
mkdir -p 自定义空间名称/src
cd 自定义空间名称
catkin_make

首先会创建一个工作空间以及一个 src 子目录,然后再进入工作空间调用 catkin_make 命令编译

进入src创建ros包并添加依赖

1
2
cd src
catkin_create_pkg 自定义ROS包名 roscpp rospy std_msgs

在工作空间下生成一个功能包,该功能包依赖于 roscpp、rospy 与 std_msgs,其中roscpp是使用C++实现的库,而rospy则是使用python实现的库,std_msgs是标准消息库,创建ROS功能包时,一般都会依赖这三个库实现

HelloWorld(C++版)

进入 ros 包的 src 目录编辑源文件

1
cd 自定义的包

C++源码实现(文件名自定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "ros/ros.h"

int main(int argc, char *argv[])
{
//执行 ros 节点初始化
ros::init(argc,argv,"hello");
//创建 ros 节点句柄(非必须)
ros::NodeHandle n;
//控制台输出 hello world
ROS_INFO("Hello World!");

return 0;
}

编辑 ros 包下的 Cmakelist.txt文件

1
2
3
4
5
6
add_executable(源文件名
src/源文件名.cpp
)
target_link_libraries(源文件名
${catkin_LIBRARIES}
)

进入工作空间目录并编译

1
2
cd 自定义工作空间名称
catkin_make

生成 build devel ….

执行

先启动命令行1:
roscore

再启动命令行2:
cd 工作空间
source ./devel/setup.bash
rosrun 包名 C++节点
命令行输出: Hello World!

PS:source ~/工作空间/devel/setup.bash可以添加进.bashrc文件,使用上更方便

添加方式1: 直接使用 gedit 或 vi 编辑 .bashrc 文件,最后添加该内容

添加方式2:echo “source ~/工作空间/devel/setup.bash” >> ~/.bashrc

HelloWorld(Python版)

进入 ros 包添加 scripts 目录并编辑 python 文件

1
2
cd ros包
mkdir scripts

新建 python 文件: (文件名自定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /usr/bin/env python

#指定解释器

#1. 导包
import rospy

#2. 编写主入口
if __name__ == "__main__":
#3. 初始化 ROS 节点
rospy.init_node("hello_p")
#4. 输出日志
rospy.loginfo("HelloWorld!")

为 python 文件添加可执行权限

1
chmod +x 自定义文件名.py

编辑 ros 包下的 CamkeList.txt 文件

1
2
3
catkin_install_python(PROGRAMS scripts/自定义文件名.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

进入工作空间目录并编译

1
2
cd 自定义空间名称
catkin_make

进入工作空间目录并执行

先启动命令行1:
roscore

再启动命令行2:
cd 工作空间
source ./devel/setup.bash
rosrun 包名 自定义文件名.py
输出结果:HelloWorld!

ROS集成开发环境搭建

在 ROS 中,需要频繁的使用到终端,且可能需要同时开启多个窗口,推荐一款较为好用的终端Terminator

1
sudo apt install terminator

vscode 中编译 ros

快捷键 ctrl + shift + B 调用编译,选择:catkin_make:build

可以点击配置设置为默认,修改.vscode/tasks.json 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
// 有关 tasks.json 格式的文档,请参见
// https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"label": "catkin_make:debug", //代表提示的描述性信息
"type": "shell", //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
"command": "catkin_make",//这个是我们需要运行的命令
"args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
"group": {"kind":"build","isDefault":true},
"presentation": {
"reveal": "always"//可选always或者silence,代表是否输出信息
},
"problemMatcher": "$msCompile"
}
]
}

创建 ROS 功能包

选定 src 右击 —> create catkin package

  1. 设置包名xxx
  2. 添加依赖roscpp rospy std_msgs

PS1: 如果没有代码提示

修改 .vscode/c_cpp_properties.json

设置 “cppStandard”: “c++17”

PS2: main 函数的参数不可以被 const 修饰

PS3: 当ROS__INFO 终端输出有中文时,会出现乱码

INFO: ????????????????????????

解决办法:在函数开头加入下面代码的任意一句

1
2
3
setlocale(LC_ALL, "");
// 或者
setlocale(LC_CTYPE, "zh_CN.utf8");

Python的scripts代码需要可执行权限:*chmod +x .p

PS:

如果不编译直接执行 python 文件,会抛出异常。

  1. 第一行解释器声明,可以使用绝对路径定位到 python3 的安装路径 #! /usr/bin/python3,但是不建议

  2. 建议使用 #!/usr/bin/env python 但是会抛出异常 : /usr/bin/env: “python”: 没有那个文件或目录

解决1: #!/usr/bin/env python3 直接使用 python3 但存在问题: 不兼容之前的 ROS 相关 python 实现

解决2: 创建一个链接符号到 python 命令:sudo ln -s /usr/bin/python3 /usr/bin/python

launch文件演示

  1. 需求

一个程序中可能需要启动多个节点,比如:ROS内置的小乌龟案例,如果要控制乌龟运动,要启动多个窗口,分别启动roscore、乌龟界面节点、键盘控制节点。如果每次都调用 rosrun 逐一启动,显然效率低下,如何优化?

官方给出的优化策略是使用 launch 文件,可以一次性启动多个 ROS 节点。

  1. 实现

选定功能包右击 —> 添加 launch 文件夹

选定 launch 文件夹右击 —> 添加 launch 文件

编辑 launch 文件内容

1
2
3
4
5
6
7
<launch>
<!-- 添加被执行的节点 -->
<!-- <node pkg="" type="" name=""/> -->
<node pkg="turtlesim" type="turtlesim_node" name="t1"/>
<node pkg="turtlesim" type="turtle_teleop_key" name="key1" />
<node pkg="helloworld" type="hello_vscode_c" name="hello" output="screen" />
</launch>

node —> 包含的某个节点
pkg —–> 功能包
type —-> 被运行的节点文件
name –> 为节点命名
output-> 设置日志的输出目标

运行 launch 文件

roslaunch 包名 launch文件名

运行结果: 一次性启动了多个节点

ROS架构

立足不同的角度,对ROS架构的描述也是不同的,一般我们可以从设计者、维护者、系统结构与自身结构4个角度来描述ROS结构:

  1. 设计者

ROS设计者将ROS表述为“ROS = Plumbing + Tools + Capabilities + Ecosystem

Plumbing: 通讯机制(实现ROS不同节点之间的交互)

Tools:工具软件包(ROS中的开发和调试工具)

Capabilities:机器人高层技能(ROS中某些功能的集合,比如:导航)

Ecosystem:机器人生态系统(跨地域、跨软件与硬件的ROS联盟)

  1. 维护者

立足维护者的角度: ROS 架构可划分为两大部分

main:核心部分,主要由 Willow Garage 和一些开发者设计、提供以及维护。它提供了一些分布式计算的基本工具,以及整个ROS的核心部分的程序编写。

universe:全球范围的代码,有不同国家的ROS社区组织开发和维护。一种是库的代码,如OpenCV、PCL等;库的上一层是从功能角度提供的代码,如人脸识别,他们调用下层的库;最上层的代码是应用级的代码,让机器人完成某一确定的功能。

  1. 系统架构

立足系统架构: ROS 可以划分为三层

OS 层,也即经典意义的操作系统

ROS 只是元操作系统,需要依托真正意义的操作系统,目前兼容性最好的是 Linux 的 Ubuntu,Mac、Windows 也支持 ROS 的较新版本

中间层

是 ROS 封装的关于机器人开发的中间件,比如: 基于 TCP/UDP 继续封装的 TCPROS/UDPROS 通信系统, 用于进程间通信 Nodelet,为数据的实时性传输提供支持, 另外,还提供了大量的机器人开发实现库,如:数据类型定义、坐标变换、运动控制….

应用层

功能包,以及功能包内的节点,比如: master、turtlesim的控制与运动节点…

  1. 自身结构

就 ROS 自身实现而言: 也可以划分为三层

文件系统

ROS文件系统级指的是在硬盘上面查看的ROS源代码的组织形式

计算图

ROS 分布式系统中不同进程需要进行数据交互,计算图可以以点对点的网络形式表现数据交互过程,计算图中的重要概念: 节点(Node)、消息(message)、通信机制_主题(topic)、通信机制_服务(service)

开源社区

ROS的社区级概念是ROS网络上进行代码发布的一种表现形式

ROS文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
WorkSpace --- 自定义的工作空间

|--- build:编译空间,用于存放CMake和catkin的缓存信息、配置信息和其他中间文件。

|--- devel:开发空间,用于存放编译后生成的目标文件,包括头文件、动态&静态链接库、可执行文件等。

|--- src: 源码

|-- package:功能包(ROS基本单元)包含多个节点、库与配置文件,包名所有字母小写,只能由字母、数字与下划线组成

|-- CMakeLists.txt 配置编译规则,比如源文件、依赖项、目标文件

|-- package.xml 包信息,比如:包名、版本、作者、依赖项...(以前版本是 manifest.xml)

|-- scripts 存储python文件

|-- src 存储C++源文件

|-- include 头文件

|-- msg 消息通信格式文件

|-- srv 服务通信格式文件

|-- action 动作格式文件

|-- launch 可一次性运行多个节点

|-- config 配置信息

|-- CMakeLists.txt: 编译的基本配置
  1. package.xml

该文件定义有关软件包的属性,例如软件包名称,版本号,作者,维护者以及对其他catkin软件包的依赖性。请注意,该概念类似于旧版 rosbuild 构建系统中使用的manifest.xml文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?xml version="1.0"?>
<!-- 格式: 以前是 1,推荐使用格式 2 -->
<package format="2">
<!-- 包名 -->
<name>demo01_hello_vscode</name>
<!-- 版本 -->
<version>0.0.0</version>
<!-- 描述信息 -->
<description>The demo01_hello_vscode package</description>

<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<!-- 维护人员 -->
<maintainer email="xuzuo@todo.todo">xuzuo</maintainer>


<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<!-- 许可证信息,ROS核心组件默认 BSD -->
<license>TODO</license>


<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/demo01_hello_vscode</url> -->


<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->


<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<!-- 依赖的构建工具,这是必须的 -->
<buildtool_depend>catkin</buildtool_depend>

<!-- 指定构建此软件包所需的软件包 -->
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>

<!-- 指定根据这个包构建库所需要的包 -->
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>

<!-- 运行该程序包中的代码所需的程序包 -->
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>


<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->

</export>
</package>
  1. CMakelists.txt

文件CMakeLists.txt是CMake构建系统的输入,用于构建软件包。任何兼容CMake的软件包都包含一个或多个CMakeLists.txt文件,这些文件描述了如何构建代码以及将代码安装到何处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
​cmake_minimum_required(VERSION 3.0.2) #所需 cmake 版本
project(demo01_hello_vscode) #包名称,会被 ${PROJECT_NAME} 的方式调用

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
#设置构建所需要的软件包
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)

## System dependencies are found with CMake's conventions
#默认添加系统依赖
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# 启动 python 模块支持
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
## 声明 ROS 消息、服务、动作... ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )

## Generate added messages and services with any dependencies listed here
# 生成消息、服务时的依赖包
# generate_messages(
# DEPENDENCIES
# std_msgs
# )

################################################
## Declare ROS dynamic reconfigure parameters ##
## 声明 ROS 动态参数配置 ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
## catkin 特定配置##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
# 运行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo01_hello_vscode
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
# 添加头文件路径,当前程序包的头文件路径位于其他文件路径之前
include_directories(
# include
${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# 声明 C++ 库
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/demo01_hello_vscode.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# 添加库的 cmake 目标依赖
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# 声明 C++ 可执行文件
add_executable(Hello_VSCode src/Hello_VSCode.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
#重命名c++可执行文件
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
#添加可执行文件的 cmake 目标依赖
add_dependencies(Hello_VSCode ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
#指定库、可执行文件的链接库
target_link_libraries(Hello_VSCode
${catkin_LIBRARIES}
)

#############
## Install ##
## 安装 ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
#设置用于安装的可执行脚本
catkin_install_python(PROGRAMS
scripts/Hi.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_demo01_hello_vscode.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

ROS文件系统相关命令

catkin_create_pkg 自定义包名 依赖包 === 创建新的ROS功能包

sudo apt install xxx === 安装 ROS功能包

sudo apt purge xxx ==== 删除某个功能包

rospack list === 列出所有功能包

rospack find 包名 === 查找某个功能包是否存在,如果存在返回安装路径

roscd 包名 === 进入某个功能包

rosls 包名 === 列出某个包下的文件

apt search xxx === 搜索某个功能包

rosed 包名 文件名 === 修改功能包文件

需要安装 vim

比如:rosed turtlesim Color.msg

  1. 执行

roscore

roscore === 是 ROS 的系统先决条件节点和程序的集合, 必须运行 roscore 才能使 ROS 节点进行通信。

roscore 将启动:
ros master
ros 参数服务器
rosout 日志节点

用法:

1
roscore

或(指定端口号)

1
roscore -p xxxx

rosrun

rosrun 包名 可执行文件名 === 运行指定的ROS节点

比如:rosrun turtlesim turtlesim_node

roslaunch

roslaunch 包名 launch文件名 === 执行某个包下的 launch 文件

ROS计算图

  1. 计算图简介

ROS文件结构是磁盘上 ROS 程序的存储结构,是静态的,而 ros 程序运行之后,不同的节点之间是错综复杂的,ROS 中提供了一个实用的工具: rqt_graph。

rqt_graph能够创建一个显示当前系统运行情况的动态图形。ROS 分布式系统中不同进程需要进行数据交互,计算图可以以点对点的网络形式表现数据交互过程。rqt_graph是rqt程序包中的一部分。

  1. 计算图安装

如果前期把所有的功能包(package)都已经安装完成,则直接在终端窗口中输入

1
rosrun rqt_graph rqt_graph

如果未安装则在终端(terminal)中输入

1
2
$ sudo apt install ros-<distro>-rqt
$ sudo apt install ros-<distro>-rqt-common-plugins

请使用你的ROS版本名称(比如:kinetic、melodic、Noetic等)来替换掉<distro>

例如当前版本是 Noetic,就在终端窗口中输入

1
2
$ sudo apt install ros-noetic-rqt
$ sudo apt install ros-noetic-rqt-common-plugins

ROS通信机制

机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS…)以及运动控制实现,为了解耦合,在ROS中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。更确切的讲,ROS是进程(也称为Nodes)的分布式框架。 因为这些进程甚至还可分布于不同主机,不同主机协同工作,从而分散计算压力。

ROS 中的基本通信机制主要有如下三种实现策略:

  • 话题通信(发布订阅模式)

  • 服务通信(请求响应模式)

  • 参数服务器(参数共享模式)

话题通信

话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息另一个节点订阅该消息。话题通信的应用场景也极其广泛,比如下面一个常见场景:

机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。

概念:以发布订阅的方式实现不同节点之间数据交互的通信模式。

作用:用于不断更新的、少逻辑处理的数据传输场景。

理论模型

话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:

  • ROS Master (管理者)
  • Talker (发布者)
  • Listener (订阅者)

ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。

  1. Talker注册
    Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。

  2. Listener注册
    Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。

  3. ROS Master实现信息匹配
    ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息

  4. Listener向Talker发送请求
    Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。

  5. Talker确认请求
    Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。

  6. Listener与Talker件里连接
    Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。

  7. Talker向Listener发送消息
    连接建立后,Talker 开始向 Listener 发布消息。

注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议
注意2: Talker 与 Listener 的启动无先后顺序要求
注意3: Talker 与 Listener 都可以有多个
注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。

话题通信基本操作A(C++)

需求:编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出。

  1. 发布方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>

int main(int argc, char *argv[])
{
//设置编码
setlocale(LC_ALL,"");

//2.初始化 ROS 节点:命名(唯一)
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"talker");
//3.实例化 ROS 句柄
ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能

//4.实例化 发布者 对象
//泛型: 发布的消息类型
//参数1: 要发布到的话题
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);

//5.组织被发布的数据,并编写逻辑发布数据
//数据(动态组织)
std_msgs::String msg;
// msg.data = "你好啊!!!";
std::string msg_front = "Hello 你好!"; //消息前缀
int count = 0; //消息计数器

//逻辑(一秒10次)
ros::Rate r(1);

//节点不死
while (ros::ok())
{
//使用 stringstream 拼接字符串与编号
std::stringstream ss;
ss << msg_front << count;
msg.data = ss.str();
//发布消息
pub.publish(msg);
//加入调试,打印发送的消息
ROS_INFO("发送的消息:%s",msg.data.c_str());

//根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
r.sleep();
count++;//循环结束前,让 count 自增
//暂无应用
ros::spinOnce();
}


return 0;
}
  1. 订阅方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
ROS_INFO("我听见:%s",msg_p->data.c_str());
// ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//2.初始化 ROS 节点:命名(唯一)
ros::init(argc,argv,"listener");
//3.实例化 ROS 句柄
ros::NodeHandle nh;

//4.实例化 订阅者 对象
ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
//5.处理订阅的消息(回调函数)

// 6.设置循环调用回调函数
ros::spin();//循环读取接收的数据,并调用回调函数处理

return 0;
}
  1. 配置 CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
add_executable(Hello_pub
src/Hello_pub.cpp
)
add_executable(Hello_sub
src/Hello_sub.cpp
)

target_link_libraries(Hello_pub
${catkin_LIBRARIES}
)
target_link_libraries(Hello_sub
${catkin_LIBRARIES}
)
  1. 执行

1.启动 roscore;

2.启动发布节点;

3.启动订阅节点。

运行结果与引言部分的演示案例1类似。

  1. 注意

补充0:

  • vscode 中的 main 函数 声明 int main(int argc, char const *argv[]){},默认生成 argv 被 const 修饰,需要去除该修饰符

补充1:

ros/ros.h No such file or directory …..

检查 CMakeList.txt find_package 出现重复,删除内容少的即可

参考资料:https://answers.ros.org/question/237494/fatal-error-rosrosh-no-such-file-or-directory/

补充2:

订阅时,第一条数据丢失

原因: 发送第一条数据时, publisher 还未在 roscore 注册完毕

解决: 注册后,加入休眠 ros::Duration(3.0).sleep(); 延迟第一条数据的发送

PS:可以使用 rqt_graph 查看节点关系。

话题通信基本操作B(Python)

  1. 发布方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#! /usr/bin/env python
"""
需求: 实现基本的话题通信,一方发布数据,一方接收数据,
实现的关键点:
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)
#4.组织被发布的数据,并编写逻辑发布数据
msg = String() #创建 msg 对象
msg_front = "hello 你好"
count = 0 #计数器
# 设置循环频率
rate = rospy.Rate(1)
while not rospy.is_shutdown():

#拼接字符串
msg.data = msg_front + str(count)

pub.publish(msg)
rate.sleep()
rospy.loginfo("写出的数据:%s",msg.data)
count += 1
  1. 订阅方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #! /usr/bin/env python
    """
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
    实现的关键点:
    1.发送方
    2.接收方
    3.数据(此处为普通文本)


    消息订阅方:
    订阅话题并打印接收到的消息

    实现流程:
    1.导包
    2.初始化 ROS 节点:命名(唯一)
    3.实例化 订阅者 对象
    4.处理订阅的消息(回调函数)
    5.设置循环调用回调函数

    """
    #1.导包
    import rospy
    from std_msgs.msg import String

    def doMsg(msg):
    rospy.loginfo("I heard:%s",msg.data)

    if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("listener_p")
    #3.实例化 订阅者 对象
    sub = rospy.Subscriber("chatter",String,doMsg,queue_size=10)
    #4.处理订阅的消息(回调函数)
    #5.设置循环调用回调函数
    rospy.spin()
  2. 添加可执行权限
    终端下进入 scripts 执行:chmod +x *.py

  3. 配置 CMakeLists.txt

    1
    2
    3
    4
    5
    catkin_install_python(PROGRAMS
    scripts/talker_p.py
    scripts/listener_p.py
    DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
    )
  4. 执行

1.启动 roscore;

2.启动发布节点;

3.启动订阅节点。

运行结果与引言部分的演示案例1类似。

PS:可以使用 rqt_graph 查看节点关系。

话题通信自定义msg

在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty….

ROS中还有一种特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头

  1. 定义msg文件

功能包下新建 msg 目录,添加文件 Person.msg

1
2
3
string name
uint16 age
float64 height
  1. 编辑配置文件

package.xml中添加编译依赖与执行依赖

1
2
3
4
5
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<!--
exce_depend 以前对应的是 run_depend 现在非法
-->

CMakeLists.txt编辑 msg 相关配置

1
2
3
4
5
6
7
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
1
2
3
4
5
## 配置 msg 源文件
add_message_files(
FILES
Person.msg
)
1
2
3
4
5
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
1
2
3
4
5
6
7
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
  1. 编译

后续说说