ROS2理论入门

写在前面:本文章是基于赵老师的视频后,所做笔记


1. ROS快速体验

ROS2中涉及的编程语言以C++和Python为主,ROS2中的大多数功能两者都可以实现,在本系列教程中,如无特殊情况,每一个案例也会用C++方案演示。

我们将介绍一个最基本的案例——ROS2版本的HelloWorld,通过学习本节内容,你可以了解ROS2程序的编写、编译与执行流程。

案例:

(1)准备:无论是使用C++还是Python编写ROS2程序,都需要依赖于工作空间,在此,我们先实现工作空间的创建与编译,打开终端,输入如下指令:

mkdir -p ws00_helloworld/src #创建工作空间以及子级目录 src,工作空间名称可以自定义
cd ws00_helloworld #进入工作空间
colcon build #编译

工作空间创建完毕后,我么可以在工作空间下的src目录中编写C++或Python程序,且两种语言的实现流程大致一致,主要包含如下步骤:

  1. 创建功能包;2. 编辑源文件;3. 编辑配置文件;4. 编译;5. 执行。

1. 创建功能包

终端下,进入ws00_helloworld/src目录,使用如下指令创建一个C++功能包:

ros2 pkg create pkg01_helloworld_cpp --build-type ament_cmake --dependencies rclcpp --node-name helloworld

2. 编辑源文件

进入pkg01_helloworld_cpp/src目录,该目录下有一helloworld.cpp文件,修改文件内容如下:

#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
  // 初始化 ROS2
  rclcpp::init(argc,argv);
  // 创建节点
  auto node = rclcpp::Node::make_shared("helloworld_node");
  // 输出文本
  RCLCPP_INFO(node->get_logger(),"hello world!");
  // 释放资源
  rclcpp::shutdown();
  return 0;
}

3. 编译配置文件

在步骤1创建功能包时所使用的指令已经默认生成且配置了配置文件,不过实际应用中经常需要自己编辑配置文件,所以在此对相关内容做简单介绍,所使用的配置文件主要有两个,分别是功能包下的package.xml与CMakeLists.txt。

1. package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>pkg01_helloworld_cpp</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="[email protected]">ros2</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <!-- 所需要依赖 -->
  <depend>rclcpp</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

2. CMakeLists.txt

文件内容如下:

cmake_minimum_required(VERSION 3.8)
project(pkg01_helloworld_cpp)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# 引入外部依赖包
find_package(rclcpp REQUIRED)

# 映射源文件与可执行文件
add_executable(helloworld src/helloworld.cpp)
# 设置目标依赖库
ament_target_dependencies(
  helloworld
  "rclcpp"
)
# 定义安装规则
install(TARGETS helloworld
  DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

4. 编译

终端下进入到工作空间,执行如下指令:

colcon build

5. 执行

终端下进入到工作空间,执行如下指令:

. install/setup.bash
ros2 run pkg01_helloworld_cpp helloworld

效果:

可以看到在最后一行成功输出了


6. 运行优化

每次终端中执行工作空间下的节点时,都需要调用. install/setup.bash指令,使用不便,优化策略是,可以将该指令的调用添加进~/setup.bash,操作格式如下

7. 在VScode下配置

然后打开修复

修改的实质是.vscode配置文件

使用快捷键:ctrl+~ 快捷打开终端  ctrl+s保存,然后进行编译,运行

 注意节点不能有空格

二、 ROS2文件系统

立足系统架构,如下图所示,ROS2可以划分为三层:

  • 操作系统层(OS Layer)

    如前所述,ROS虽然称之为机器人操作系统,但实质只是构建机器人应用程序的软件开发工具包,ROS必须依赖于传统意义的操作系统,目前ROS2可以运行在Linux、Windows、Mac或RTOS上。 实质上是个SDK

  • 中间层(Middleware Layer)

    主要由数据分发服务DDS与ROS2封装的关于机器人开发的中间件组成。DDS是一种去中心化的数据通讯方式,ROS2还引入了服务质量管理 (Quality of Service)机制,借助该机制可以保证在某些较差网络环境下也可以具备良好的通讯效果。ROS2中间件则主要由客户端库、DDS抽象层与进程内通讯API构成。

  • 应用层(Application Layer)

    是指开发者构建的应用程序,在应用程序中是以功能包为核心的,在功能包中可以包含源码、数据定义、接口等内容。

1.概览

功能包是ROS2应用程序的核心,但是功能包不能直接构建,必须依赖于工作空间,一个ROS2工作空间的目录结构如下:

WorkSpace --- 自定义的工作空间。
    |--- build:存储中间文件的目录,该目录下会为每一个功能包创建一个单独子目录。
    |--- install:安装目录,该目录下会为每一个功能包创建一个单独子目录。
    |--- log:日志目录,用于存储日志文件。
    |--- src:用于存储功能包源码的目录。
        |-- C++功能包
            |-- package.xml:包信息,比如:包名、版本、作者、依赖项。
            |-- CMakeLists.txt:配置编译规则,比如源文件、依赖项、目标文件。
            |-- src:C++源文件目录。
            |-- include:头文件目录。
            |-- msg:消息接口文件目录。
            |-- srv:服务接口文件目录。
            |-- action:动作接口文件目录。
        |-- Python功能包
            |-- package.xml:包信息,比如:包名、版本、作者、依赖项。
            |-- setup.py:与C++功能包的CMakeLists.txt类似。
            |-- setup.cfg:功能包基本配置文件。
            |-- resource:资源目录。
            |-- test:存储测试相关文件。
            |-- 功能包同名目录:Python源文件目录。

另外,无论是Python功能包还是C++功能包,都可以自定义一些配置文件相关的目录。

|-- C++或Python功能包
    |-- launch:存储launch文件。
    |-- rviz:存储rviz2配置相关文件。
    |-- urdf:存储机器人建模文件。
    |-- params:存储参数文件。
    |-- world:存储仿真环境相关文件。
    |-- map:存储导航所需地图文件。
    |-- ......

源文件说明:

在实现第一个ROS2程序时,都需要创建节点,无论是C++实现还是Python实现,都是直接实例化的Node对象。

C++实例化Node示例如下:(推荐)

#include "rclcpp/rclcpp.hpp"

#创建节点
class MyNode: public rclcpp::Node{
public:
    MyNode():Node("node_name"){
        RCLCPP_INFO(this->get_logger(),"hello world!");
    }

};

int main(int argc, char *argv[])
{
    rclcpp::init(argc,argv);
    auto node = std::make_shared<MyNode>();
    rclcpp::shutdown();
    return 0;
}

举例:

问题:初始化与资源释放在程序中启一个什么样的作用

答: 1.前提:构建的程序可能由若干步骤或阶段组成

      初始化——> 节点对象——>日志输出——>数据发布——>数据订阅——>....——>资源释放

        2. 不同步骤或阶段之间涉及到数据的传递

        3. 怎么实现不同数据的传递呢?

            使用Context(上下文)对象。这是一个容器,可以存储数据,也可以从中读取数据

        4. 初始化其实就是要创建Context对象,资源释放就是要销毁Context对象

    

配置文件说明:

经常需要开发者编辑一些配置文件以设置功能包的构建信息,功能包类型不同,所需修改的配置文件也有所不同。C++功能包的构建信息主要包含在package.xml与CMakeLists.txt中,Python功能包的构建信息则主要包含在package.xml和setup.py中,接下来我们就简单了解一下这些配置文件。

1. package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>pkg01_helloworld_cpp</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="[email protected]">ros2</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <depend>rclcpp</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

1.根标签

  • <package>:该标签为整个xml文件的根标签,format属性用来声明文件的格式版本。

2.元信息标签

  • <name>:包名;
  • <version>:包的版本号;
  • <description>:包的描述信息;
  • <maintainer>:维护者信息;
  • <license>:软件协议;
  • <url>:包的介绍网址;
  • <author>:包的作者信息。

3.依赖项

  • <buildtool_depend>:声明编译工具依赖;
  • <build_depend>:声明编译依赖;
  • <build_export_depend>:声明根据此包构建库所需依赖;
  • <exec_depend>:声明执行时依赖;
  • <depend>:相当于<build_depend>、<build_export_depend>、<exec_depend>三者的集成;
  • <test_depend>:声明测试依赖;
  • <doc_depend>:声明构建文档依赖。
2.CMakeLists.txt

C++功能包中需要配置CMakeLists.txt文件,该文件描述了如何构建C++功能包,一个简单的CMakeLists.txt示例如下:

# 声明cmake的最低版本
cmake_minimum_required(VERSION 3.8)
# 包名,需要与package.xml中的包名一致
project(pkg01_helloworld_cpp)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# 引入外部依赖包
find_package(rclcpp REQUIRED)

# 映射源文件与可执行文件
add_executable(helloworld src/helloworld.cpp)
# 设置目标依赖库
ament_target_dependencies(
  helloworld
  "rclcpp"
)
# 定义安装规则
install(TARGETS helloworld
  DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

3.操作命令

ROS2的文件系统核心是功能包,我们可以通过编译指令colcon和ROS2内置的工具指令ros2来实现功能包的创建、编译、查找与执行等相关操作。

1.创建

新建功能包语法如下:

ros2 pkg create 包名 --build-type 构建类型 --dependencies 依赖列表 --node-name 可执行程序名称

格式解释:

  • --build-type:是指功能包的构建类型,有cmake、ament_cmake、ament_python三种类型可选;
  • --dependencies:所依赖的功能包列表;
  • --node-name:可执行程序的名称,会自动生成对应的源文件并生成配置文件。

例:

ros2 pkg create pkg01_helloworld_cpp --build-type ament_cmake --dependencies rclcpp --node-name helloworld

2.编译

colcon build

3.查找

ros2 pkg executables [包名] # 输出所有功能包或指定功能包下的可执行程序。
ros2 pkg list # 列出所有功能包
ros2 pkg prefix 包名 # 列出功能包路径
ros2 pkg xml # 输出功能包的package.xml内容

4.执行

ros2 run 功能包 可执行程序 参数

例:ros2 run pkg01_helloworld_cpp helloworld

三、核心模块

1. 通信模块

通信模块是整个ROS2架构中的重中之重,比如你可能想要了解在ROS2中是如何控制机器人底盘运动的?雷达、摄像头、imu、GPS等这些传感器数据是如何传输到ROS2系统的?人机交互时调用者如何下发指令,机器人又是如何反馈数据的?导航、机械臂等系统性实现不同模块之间是如何交互数据的......等等,其实这些都离不开通信模块。另外,开发者构建应用程序时,通信部分在工作内容中占有相当大的比重

2.功能包应用

1.二进制安装

ROS官方或社区提供的功能包可以很方便的通过二进制方式安装,安装命令如下:

sudo apt install ros-ROS2版本代号-功能包名称
2.源码安装
git clone 仓库地址
3.自实现

开发者按照业务需求自己编写功能包实现。

3.分布式

ROS2是一个分布式架构,不同的ROS2设备之间可以方便的实现通信,这在多机器人设备协同中是极其重要的。

4.终端命令与rqt

在ROS2中提供了丰富的命令行工具,可以方便的调试程序、提高开发效率。

rqt是一个图形化工具,它的功能与命令行工具类似,但是图形化的交互方式更为友好。

2.1 通信机制简介

1.节点

在通信时,不论采用何种方式,通信对象的构建都依赖于节点(Node),在ROS2中,一般情况下每个节点都对应某一单一的功能模块(例如:雷达驱动节点可能负责发布雷达消息,摄像头驱动节点可能负责发布图像消息)。一个完整的机器人系统可能由许多协同工作的节点组成,ROS2中的单个可执行文件(C++程序或Python程序)可以包含一个或多个节点。

2.话题

话题(Topic)是一个纽带,具有相同话题的节点可以关联在一起,而这正是通信的前提。并且ROS2是跨语言的,有的节点可能是使用C++实现,有的节点可能是使用Python实现的,但是只要二者使用了相同的话题,就可以实现数据的交互。

3.通信模型

不同的通信对象通过话题关联到一起之后,以何种方式实现通信呢?在ROS2中,常用的通信模型有四种:

1.话题通信:是一种单向通信模型,在通信双方中,发布方发布数据,订阅方订阅数据,数据流单向的由发布方传输到订阅方。

2.服务通信:是一种基于请求响应的通信模型,在通信双方中,客户端发送请求数据到服务端,服务端响应结果给客户端。

3.动作通信:是一种带有连续反馈的通信模型,在通信双方中,客户端发送请求数据到服务端,服务端响应结果给客户端,但是在服务端接收到请求到产生最终响应的过程中,会发送连续的反馈信息到客户端。

4.参数服务:是一种基于共享的通信模型,在通信双方中,服务端可以设置数据,而客户端可以连接服务端并操作服务端数据。

4.接口

在通信过程中,需要传输数据,就必然涉及到数据载体,也即要以特定格式传输数据。在ROS2中,数据载体称之为接口(interfaces)。通信时使用的数据载体一般需要使用接口文件定义。常用的接口文件有三种:msg文件、srv文件与action文件。每种文件都可以按照一定格式定义特定数据类型的“变量”。

1.msg文件

msg文件是用于定义话题通信中数据载体的接口文件,一个典型的.msg文件示例如下。

int64 num1
int64 num2
2.srv文件

srv文件是用于定义服务通信中数据载体的接口文件,一个典型的.srv文件示例如下。

int64 num1
int64 num2
---
int64 sum

文件中声明的数据被---分割为两部分,上半部分用于声明请求数据,下半部分用于声明响应数据。

3.action文件

action文件使用用于定义动作通信中数据载体的接口文件,一个典型的.action文件示例如下。

int64 num
---
int64 sum
---
float64 progress

文件中声明的数据被---分割为三部分,上半部分用于声明请求数据,中间部分用于声明响应数据,下半部分用于声明连续反馈数据。

4.变量类型

不管是何种接口文件,在文件中每行声明的数据都由字段类型和字段名称组成,可以使用的字段类型有:

  • int8, int16, int32, int64 (或者无符号类型: uint*)

  • float32, float64

  • string

  • time, duration

  • 其他msg文件

  • 变长数组和定长数组

ROS中还有一种特殊类型:Header,标头包含时间戳和ROS2中常用的坐标帧信息。许多接口文件的第一行包含Header标头。

2.2 话题通信

话题通信是一种以发布订阅的方式实现不同节点之间数据传输的通信模型。数据发布对象称为发布方,数据订阅对象称之为订阅方,发布方和订阅方通过话题相关联,发布方将消息发布在话题上,订阅方则从该话题订阅消息,消息的流向是单向的。

话题通信的发布方与订阅方是一种多对多的关系,也即,同一话题下可以存在多个发布方,也可以存在多个订阅方,这意味着数据会出现交叉传输的情况,当然如果没有订阅方,数据传输也会出现丢失的情况。

话题通信一般应用于不断更新的、少逻辑处理的数据传输场景。

2.使用分析

在写程序时,需要关注的要素有三个:

  1. 发布方;
  2. 订阅方;
  3. 消息载体。

3.流程简介

需要先自定义接口消息,除此之外的实现流程与案例1一致,主要步骤如下:

  1. 编写发布方实现;
  2. 编写订阅方实现;
  3. 编辑配置文件;
  4. 编译;
  5. 执行。

案例我们会采用C++和Python分别实现,二者都遵循上述实现流程。

4.准备工作

终端下进入工作空间的src目录,调用如下两条命令分别创建C++功能包和Python功能包

 创建功能包:

ros2 pkg create cpp01_topic --build-type ament_cmake --dependencies rclcpp std_msgs base_interfaces_demo
ros2 pkg create py01_topic --build-type ament_python --dependencies rclpy std_msgs base_interfaces_demo

案例尝试:

2.2.2 话题通信之原生消息(C++)

源码解析:

/*  
  需求:以某个固定频率发送文本“hello world!”,文本后缀编号,每发送一条消息,编号递增1。
  步骤:
    1.包含头文件;
    2.初始化 ROS2 客户端;
    3.定义节点类;
      3-1.创建发布方;
      3-2.创建定时器;
      3-3.组织消息并发布。
    4.调用spin函数,并传入节点对象指针;
    5.释放资源。
*/

// 1.包含头文件;
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

// 3.定义节点类;
class MinimalPublisher : public rclcpp::Node // MinimalPublisher是自定义节点名
{
  public:
   //构造函数
    MinimalPublisher(): Node("minimal_publisher"), //节点名称
    count_(0)
    {
      // 3-1.创建发布方;
      publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
      // 3-2.创建定时器;
      timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));
    }

  private:
    void timer_callback()
    {
      // 3-3.组织消息并发布。
      auto message = std_msgs::msg::String();
      message.data = "Hello, world! " + std::to_string(count_++);
      RCLCPP_INFO(this->get_logger(), "发布的消息:'%s'", message.data.c_str()); //发送命令
      publisher_->publish(message);
    }
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    size_t count_;
};

int main(int argc, char * argv[])
{
  // 2.初始化 ROS2 客户端;
  rclcpp::init(argc, argv);
  // 4.调用spin函数,并传入节点对象指针。
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  // 5.释放资源;
  rclcpp::shutdown();
  return 0;
}

显示效果:

1. colcon build 2.. install/setup.bash 3. ros2 run 包+文件

猜你喜欢

转载自blog.csdn.net/ArtoriaLili/article/details/131827322
今日推荐