ROS2基础
一、初识ROS
1.1 何为ROS
Robot Operating System
1.2 特点与结构
1.2.1 组成
ROS = 通讯机制 + 开发工具 + 应用功能 + 社区生态
ROS = (安装在已有OS上的) 软件库+工具集
1.2.2 ROS2 VS ROS1
1. 优缺点:
-
ROS1 的不足:
- 成本较高;
- 相对独立,不互通;
- 偏向学术研究;
-
ROS2 的优势:
- 支持多机器人系统;
- 网络连接;
- 跨平台;
- 实时性;
2. 区别:
架构颠覆:
- ROS1 的所有 节点 都需要在 节点管理器(ROS Master) 中管理,一旦出现问题就会瘫痪;
- ROS2 采用 分布式DDS框架 的 Discovery 机制,为所有节点提供保障;
API:
- ROS2 根据最新C++与Py3重新设计了用户API,类似ROS1的老API
编译系统升级:
- ROS1 使用 rosbuild、catkin;
- ROS2 使用升级版的 ament、colcon;
1.3 核心概念
1.3.1 四种通信模型(方法):
- 话题-Topic
- 适用于node间单向的频繁的数据传输
- 服务-Service
- 适用于node间双向的数据传递
- 动作-Action
- 用于动态调整节点的设置
- 参数-Parameter (不支持自定义接口)
- 参数服务器是节点存储参数的地方、用于配置参数,全局共享参数
1.3.2 其他概念
CLI:命令行
1.4 WSL+ROS2 安装
1.5 工作空间与功能包(Workspace & Package)
1.5.0 介绍
- 工作空间:
包含若干功能包的目录,一开始理解成一个文件夹就行了,这个文件夹包含下有src
,src下包含众多功能包。 - 功能包:
可以理解为存放节点的地方。
根据编译方式的不同分为3种类型:- ament_python,适用于python程序
- cmake,适用于C++
- ament_cmake,适用于C++程序,是cmake的增强版
- 常见CLI:
#(创建create见下面) ros2 pkg executables <包名> #(列出pkg内可执行文件) ros2 pkg list #(查看系统中pkg列表)
1.5.1 预先配置ROS2工作环境(重要,别忘了)
#Linux
source /opt/ros/humble/setup.bash
#Win
call ROS2空间\setup.bat
1.5.2 创建工作空间
#Linux
mkdir -p XXX空间名/src
cd XXX空间名
#Win
md 空间名\src
cd 空间名
1.5.3 创建功能包
需要在src目录下进行
ros2 pkg create <包名> --build-type ament_python --dependencies rclpy
ros2 pkg create <包名> --build-type ament_cmake
- pkg create 为创建包,pkg还有一些其他用法;
- --build-type 指定包的编译类型,共
ament_python
,ament_cmake
,cmake
3种.不写默认是ament_cmake; - --dependencies 指定包的依赖,
rclpy
(ROS Client Library for Python)是一个常见的python客户端接口。
1.6 ROS2的编译器——Colcon
一个功能包的构建工具
1.6.1 安装
ROS2 默认没有安装Colcon
#Linux
sudo apt-get install python3-colcon-common-extensions
#Win
pip install -U colcon-common-extensions
1.6.2 编译源码、工作空间
需要在工作空间目录下编译,而不是子目录src
如果在src下编译的,需要把生成的build、install、log都删除再重新编译。
Windows
#Linux
colcon build
#Win
colcon build --merge-install
colcon支持--symlink-install
,来使得下载了的文件可以根据source的文件而变换。
#Linux
colcon build --symlink-install
#Win
colcon build --symlink-install --merge-install
编译后源码文件夹会出现如下目录:
.
├── build >>> 中间文件
├── install >>> 节点可执行文件
├── log >>> 日志
└── src
- build 目录存储的是中间文件。对于每个包,将创建一个子文件夹,在其中调用例如CMake
- install 目录是每个软件包将安装到的位置,也就是编译之后的节点可执行文件。默认情况下,每个包都将安装到单独的子目录中。
- log 目录包含有关每个colcon调用的各种日志信息。
1.6.3 colcon常用指令
- colcon build --packages-select 功能包名 (编译特定的功能包)
- colcon build --packages-select YOUR_PKG_NAME --cmake-args -DBUILD_TESTING=0 (不编译测试单元)
- colcon build --symlink-install (每次调整python脚本时都不需要重新build了)
1.6.4 source 资源 && 配置环境变量
为了让系统上下文知道可以访问各个功能包,生效环境变量
#Linux
source install/local_setup.sh
#Win
#PowerShell
install\local_setup.ps1
#CMD
call install\local_setup.bat
如果是Linux环境为了简便配置过程,可以将这段代码放入:
home/.bashrc
在末尾加上
source /opt/ros/humble/setup.bash 找到ros安装位置
source ~/XXX/install/local_setup.sh 找到工作空间位置
1.6.5 将节点告知ros2
1. Python:
在setup.py
文件中entry_points
部分的console_scripts
栏目中加入
"节点名 = 功能包名.节点文件:main"
2. C++:
在CMakeLists.txt
中:
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED) //如果需要
add_executable(节点可执行 src/节点文件.cpp)
ament_target_dependencies(节点可执行 rclcpp std_msgs)
install(TARGETS
节点可执行
DESTINATION lib/${PROJECT_NAME})
1.6.6 最后加的小配置项
在package.xml
文件中修改:
<description>Examples of minimal publisher/subscriber using rclpy</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
加入:
<exec_depend>std_msgs</exec_depend> <!--ROS2标准消息类型库-->
1.7 小Tips
1.7.1 命名规则
包名
必须 全部小写自定义的msg,srv和 action文件
必须首字母大写,且不可以有下划线节点名
首字母小写,没试过大写情况bool类型
变量字母全大写
二、概念
2.1 基础概念
2.1.0 节点 (Nodes)
ROS2中任何通信方式都依赖于Node,一般情况下每个节点都负责某一单一的功能模块
在__init__.py同级目录下创建节点文件XXX.py即可
- 创建Node流程:
- 导入ROS2
接口库
、Node类
- 接口
init
- 创建并初始化
Node对象
- 实现节点功能 (多)
- 销毁
Node对象
- 接口
shutdown
1.常见CLI
ros2 run <包名> <可执行文件名> #(运行节点文件)
ros2 node list #(列出all运行的node名)
ros2 node info <节点名> #(根据node名展示对应的详细信息)
2.1.1 接口 (Interfaces)
是一种规范
4种通信方式中,参数不支持自定义接口
-1.原始数据类型
其中每一个都可以在后面加上[]
将其变成数组形式(从一个变成多个)
bool
byte
char
float32, float64
int8, uint8
int16, uint16
int32, uint32
int64, uint64
string
0.接口格式
- 话题接口格式:
xxx.msg
(一阶)
# 用就完了
int64 num
- 服务接口格式:
xxx.srv
(二阶)
# request xxx.Request()
int64 a
int64 b
---
# response xxx.Response()
int64 sum
- 动作接口格式:
xxx.action
(三阶)
# 会有 .Feedback()\.Result()\.Goal() 三个子类,子类中分别有如下对应的属性
# request
int32 req
---
# result
int32[] result
---
# serial_feedback
int32[] s_feedback
1.常用CLI
ros2 interface list #(查看当前接口)
ros2 interface packages #(查看所有接口包)
ros2 interface package <包名> #(查看接口包内所有接口)
ros2 interface show <接口详细路径> #(查看接口详细内容)
ros2 interface proto <接口详细路径> #(查看接口属性)
2.自定义接口
目前ros2仅支持
ament_cmake
类型的接口功能包
以“topic接口 msg”为例
- 创建接口功能包
ros2 pkg create <接口功能包名> --build-type ament_cmake
- 新建
msg
文件夹并在其中新建Xxx.msg
文件,并写入自定义的接口信息 CMakeLists.txt
添加:
#添加对sensor_msgs的
find_package(rosidl_default_generators REQUIRED) #必须
find_package(sensor_msgs REQUIRED) #(对应继承的依赖包)
#添加消息文件和依赖
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Novel.msg" # 指定路径,生成对应源码
"..."
DEPENDENCIES sensor_msgs #(!!如果没有依赖则不写)
)
package.xml
添加:
<!-- 编译依赖 -->
<build_depend>rosidl_default_generators</build_depend>
<!-- 构建工具依赖 -->
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<!-- 执行依赖 -->
<exec_depend>rosidl_default_runtime</exec_depend>
<!-- 声明当前包所属的功能包组 -->
<member_of_group>rosidl_interface_packages</member_of_group>
<depend>geometry_msgs</depend> (!!前4个必备,这个根据依赖添加)
<depend>action_msgs</depend> (!!这个是action特有)
- 对应功能包引入接口:
package.xml
中引入:<depend>接口包名</depend>
节点文件
中引入:from interfaces.srv import Communication #from 接口文件的路径,到文件夹为止 import 接口文件名
2.1.2 话题 (Topics)
话题是单向的,而且不需要等待服务端上线,直接发就行,数据的实时性比较高。
频率高,实时性强的传感器数据的传递一般使用话题实现。
跨语言,只要Topic相同
1.常见CLI
ros2 topic list #(显示正在运行的话题)
ros2 topic list -t #(显示正在运行的话题并显示具体类型)
ros2 topic info <话题名> #(显示topic消息类型、suber数、puber数)
ros2 topic type <话题名> #(根据topic名查看topic消息类型)
ros2 topic find (-c) <消息类型> #(根据topic消息类型查看topic名)(-c 仅统计数量)
ros2 topic hz <话题名> #(显示话题pub频率)
ros2 topic bw <话题名> #(显示话题的带宽)
ros2 topic echo <话题名> #(CLI手动sub topic,显示pub出来的具体消息)
ros2 topic pub (-1/-t 5/-r 5) <话题名> <话题类型> "{<参数>}" #(CLI手动pub topic,加`once`或者`-1`表示不循环发布,默认循环,-t 5为循环发布5次,-r 5以5hz频率发布)
# <参数>: 格式为: 属性: 具体参数,
# “属性:” 后面要空一格
2.创建讲解
# puber发布者:
self._publisher = self.create_publisher(话题接口类名, “话题名”, 10)
第一个参数为消息类型(String),第二个参数为话题名称(“sexy_girl”),第三个参数为消息队列长度(10)。
# suber订阅者:
self._subscriber = self.create_subscription(话题接口类名,'话题名',self.listener_callback,10)
self._subscriber
基本同上,第3个参数为接到发布后的回调函数
3.使用详解
# puber:
msg = 话题接口类名() # 对象化
msg.话题接口类名中的具体对象 = ... # 赋值
self._publisher.publish(msg) # msg类型要看 `话题接口类名` 中的具体类型
# suber
def listener_callback(self,msg):
return msg.话题接口类名中的具体对象 # data属于`话题接口类名`中的具体对象
2.1.3 服务 (Services)
服务是双向的,Client发送request后,Service有response,可以得知Service的处理结果。
适用于频率较低,仅需要及时反馈的任务。
1.常见CLI
ros2 service list -t # 查看所有活跃的服务及其类型名路径
ros2 service type <服务名> # 根据service名获取其类型名路径
ros2 service find <服务类型名路径> # 根据类型名路径获取其service名
ros2 service call <服务名> <服务类型名路径> "{<参数>}" # CLI手动请求服务
2. 创建讲解
- Server:
self.srv = self.create_service(服务接口类名, '服务名', self.执行的回调函数)
- Client:
self.cli = self.create_client(服务接口类名, '服务名')
- Server_cpp:
- Client_cpp:
3.使用讲解
- Server:
# 回调函数实现逻辑处理请求
def urserverfunc_callback(self,request,response):
# request: 来自Client的请求
# response: Server的响应数
if request.成员...:
response.成员 = 赋值
return response
- Client:
- 创建发送 request 的函数
- 创建接收 response 的 callback
#1、创建发送 request 的函数
def sendRequestFunc(self):
# 确认Server是否在线
while not self.cli.wait_for_service(<float类型num>): # num时间内等待响应,响应了返回True,否则返回False
self.get_logger().warn("连接Server中...")
# 构造Request内容
request = 服务接口类名.Request()
request.成员 = ... # 赋值
# 发送异步request
self.cli.call_async(request).add_done_callback(self.recvResponse_callback)
# 发送同步request
# Do not call this method in a callback or a deadlock may occur.
self.cli.call(request).add_done_callback(self.recvResponse_callback)
#2、创建接收 response 的 callback
def recvResponse_callback(self,response):
result = response.result() # result指Response中的各个成员
# 使用 `result.成员`
node.sendRequestFunc() # main函数中node自己调用
2.1.4 动作 (Action) ✳
动作适用于需要实时反馈的场景,原理基于话题和服务。
适用于过程,提供实时反馈 ,解决service“假死”问题
创建actionserver对象
处理提交的goal值(callback)
生成连续feedback并最终响应(callback)
处理取消请求(callback)
0. 三大组成部分
- 目标(goal):即Action Client告诉Server要做什么request,Server针对该目标要有response。(如果目标成立,此时Client会针对此次response 再给一个request 作为结果的先导询问)
- 反馈(feedback):即Action Server告诉Client此时做的进度如何(Topic feedback)。
- 结果(result):即Action Server最终告诉Client其执行结果,response最后返回,用于表示任务最终执行情况。
1. 常见CLI
ros2 action list
ros2 action list -t
ros2 action info <动作名>
ros2 action send_goal <动作名> <动作类型名路径> "{<参数>}" [--feedback]
#(-f 查看连续反馈)
#(--feedback 查看连续反馈)
2. 动作创建
依赖于新的头文件,不单单是Node自带的
Python:
from rclpy.action import ActionServer
from rclpy.action import ActionClient
C++:
#include "rclcpp_action/rclcpp_action.hpp"
# python:
self._action_server = ActionServer(
self,
动作接口类型,
'动作名',
execute_callback = self.execute_callback,
goal_callback = self.goal_callback,
#handle_accepted_callback=self.handle_accepted_callback,
cancel_callback = self.cancel_callback
)
3. execute_callback(self,goal_handle):
生成连续feedback和最终result,主逻辑。
有几个常用的属性:
goal_handle.request.
(对应action文件中的request)goal_handle.publish_feedback(obj)
(生成一次feedback,如果需要连续则需要循环)goal_handle.succeed()
(生成一次最终成功响应,其他的有abort
,canceled
)
详细可以查看ActionServer
函数的实现细节
4. 修改ActionServer的默认处理实现
默认参数是无条件接收request,取消请求也是无条件拒绝
2.1.5 参数(Parameters)
是node的一个配置值(node settings),原理基于服务,是对其的二次封装;
node中的“缓存区”,其他nodes可以来访问读取其中的数据
ROS2不支持通过launch在启动Node的时候附加参数。所以,我们需要使用parameters
1. 常见CLI
ros2 param list #1.查看节点的参数列表
ros2 param describe <节点名> <参数名> #2.查看详细参数列表信息
ros2 param get <节点名> <参数名> #3.查看参数具体数值
ros2 param set <节点名> <参数名> <值> #4.设置参数值
ros2 param dump <节点名> #5.生成节点参数快照
ros2 param load <节点名> <快照名文件名> #6.加载快照
ros2 run <包名> <节点名> --ros-args --params-file <快照文件名> #7.运行node时附带参数
ros2 run <包名> <节点名> --ros-args -p <参数key名>:=<>值 #同上
2. 参数创建
参数由key、value、description组成。
key是string类型,value是基本类型,description默认为空,但是可以设置描述、类型、取值范围等约束信息。
# 先声明
self.declare_parameter('key名' [,value])
self.get_parameter('key名').get_parameter_value().string_value # bool_value、integer_value、double_value
2.1.6 Launch文件——node管理
- 项目需要的node有很多,要是启动多个节点费力,launch可以类似脚本启动
- 可以进行node间的依赖管理
ROS1的是XML格式,而ROS2可以使用Python和yaml格式编写(推荐python)。
-
在功能包目录下创建
launch
文件夹 -
文件内容:
- 导入库
- 定义launch函数名
- 创建node对象
- 创建launch_description对象,并将上面的node对象写入
- 返回launch_description
-
在setup.py下添加内容
2.1.7 话题包(Bag)
Recorder/Reader
可以记录publish的topic数据,
记录下来的内容会以rosbag2_year_month_day-hour_minute_second
形式命名,
- 常见CLI
ros2 bag record <topic_name> # 记录单个topic
ros2 bag record -o <bag_name> <topic_name1> <topic_name2> # 记录多个topic 存入特定话题包名,-o指添加特定包名
ros2 bag info <bag_name> # 显示话题包的详细信息
ros2 bag play <bag_name> # 回放话题包的内容
2.2 进阶概念
2.2.1 死锁
- When a client is blocking a thread waiting for a response, but the response can only be returned on that same thread, the client will never stop waiting, and nothing else can happen.
- 在
callback
中send_request
When deadlock occurs, you will not receive any indication that the service is blocked. There will be no warning or exception thrown, no indication in the stack trace, and the call will not fail.
It is not recommended to implement a synchronous service client. They are susceptible to deadlock, but will not provide any indication of issue when deadlock occurs.
2.2.2 多线程
ROS2中默认是单线程。
实现多线程需要用到执行器(Executor)和回调组(CallbackGroup)
2.3 高级概念
三、工具
3.1 rqt
可以进行快捷可视化的命令调用
需要先source
3.1.1 使用方法
菜单 - Plugins - 想要调用的东西 -
3.2 rqt_graph
运行过程中,可视化node间的数据关系
3.2.1 使用方法
opt/ros/XXX版本名/lib:放可执行文件
opt/ros/XXX版本名/share:放配置文件
rqt_graph 绘制节点话题关系图
ros2 run <功能包名> <可执行文件名>