【ROS2简介】体系框架

 

一、简介

        2007年,一家名为柳树车库(Willow Garage)的机器人公司发布了ROS,ROS集开源、免费、高复用、低耦合、工具丰富等等诸多优势于一身,一经推出便迅速吸引了大量的开发者、科研人员、硬件供应商的加入,形成了稳定且多样的机器人生态,水到渠成的ROS也成为了机器人领域的主流软件框架并流行至今。

        但是自ROS诞生的十几年来,不管是机器人相关软件、硬件还是ROS社区都发生了天翻地覆的变化,加之ROS1存在一些设计上的先天性缺陷,各种内外因素叠加下,导致ROS1在许多应用场景下都已经显得力不从心了。此背景下,官方于2017正式推出了新一代机器人操作系统——ROS2,ROS2基于全新的设计框架,保留了ROS1的优点并改进其缺陷,ROS2的目标是适应新时代的新需求。

整个ROS生态由通信(Plumbing)、工具(Tools)、功能(Capabilities)与社区(Community)四大部分组成。

 二、ROS2文件系统

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

  • 操作系统层(OS Layer)

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

  • 中间层(Middleware Layer)

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

  • 应用层(Application Layer)

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

对于一般开发者而言,工作内容主要集中在应用层,开发者一般通过实现具有某一特定功能的功能包来构建机器人应用程序。对应的我们所介绍的ROS2文件系统主要是指在硬盘上以功能包为核心的目录与文件的组织形式。

2.1 功能包

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

WorkSpace --- 自定义的工作空间。
    |--- build:存储中间文件的目录,该目录下会为每一个功能包创建一个单独子目录。
    |--- install:安装目录,该目录下会为每一个功能包创建一个单独子目录。
    |--- log:日志目录,用于存储日志文件。
    |--- src:用于存储功能包源码的目录。
        |-- 1.C++功能包
            |-- package.xml:包信息,比如:包名、版本、作者、依赖项。
            |-- CMakeLists.txt:配置编译规则,比如源文件、依赖项、目标文件。
            |-- src:C++源文件目录。
            |-- include:头文件目录。
            |-- msg:消息接口文件目录。
            |-- srv:服务接口文件目录。
            |-- action:动作接口文件目录。
        |-- 2.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:存储导航所需地图文件。
    |-- ......

2.2 源文件说明

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

C++实例化Node示例如下:
#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
  rclcpp::init(argc,argv);
  auto node = rclcpp::Node::make_shared("helloworld_node");
  RCLCPP_INFO(node->get_logger(),"hello world!");
  rclcpp::shutdown();
  return 0;
}
Python实例化Node示例如下:
import rclpy

def main():
    rclpy.init()
    node = rclpy.create_node("helloworld_py_node")
    node.get_logger().info("hello world!")
    rclpy.shutdown()

if __name__ == '__main__':
    main()

但是在ROS2中,上述编码风格是不被推荐的,更推荐以继承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;
}
Python继承Node实现示例如下:
import rclpy
from rclpy.node import Node

class MyNode(Node):
    def __init__(self):
        super().__init__("node_name_py")
        self.get_logger().info("hello world!")
def main():

    rclpy.init()
    node = MyNode() 
    rclpy.shutdown()

之所以继承比直接实例化Node更被推荐,是因为继承方式可以在一个进程内组织多个节点,这对于提高节点间的通信效率是很有帮助的,但是直接实例化则与该功能不兼容。

2.3 配置文件说明

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

2.3.1 package.xml

不管是何种类型的功能包,package.xml的格式都是类似的,在该文件中包含了包名、版本、作者、依赖项的信息,package.xml可以为colcon构建工具确定功能包的编译顺序。一个简单的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">    <!-- 根标签<package>:该标签为整个xml文件的根标签,format属性用来声明文件的格式版本。 -->
  <name>pkg01_helloworld_cpp</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="ros2@todo.todo">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.3.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()
2.3.3 setup.py

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

from setuptools import setup

package_name = 'pkg02_helloworld_py'

setup(
    name=package_name, # 包名
    version='0.0.0',   # 版本
    packages=[package_name], # 功能包列表
    data_files=[ #需要被安装的文件以及安装路径
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'], # 安装依赖
    zip_safe=True,
    maintainer='ros2', # 维护者
    maintainer_email='ros2@todo.todo', # 维护者 email
    description='TODO: Package description', # 包描述
    license='TODO: License declaration', # 软件协议
    tests_require=['pytest'], # 测试依赖
    entry_points={
        'console_scripts': [
            # 映射源文件与可执行文件
            'helloworld = pkg02_helloworld_py.helloworld:main'
        ],
    },
)

2.4 操作命令

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

1.创建

新建功能包语法如下:

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

格式解释:

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

编译功能包语法如下:

colcon build

colcon build --packages-select 功能包列表

前者会构建工作空间下的所有功能包,后者可以构建指定功能包。

3.查找

ros2 pkg命令下包含了多个查询功能包相关信息的参数。

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

执行命令语法如下:

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

小提示:可以通过命令 -h命令 --help来获取命令的帮助文档。

三、ROS2核心模块

通信与工具是ROS2的核心模块,也是我们以后学习和工作的重点所在。

3.1 通信模块

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

3.2 功能包应用

功能包的应用主要有三种方式:

1.二进制安装

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

sudo apt install ros-ROS2版本代号-功能包名称

小提示:可以调用apt search ros-ROS2版本代号-* | grep -i 关键字格式的命令,根据关键字查找所需的功能包。

2.源码安装

也可以直接下载官方、社区或其他第三方提供的源代码,一般我们会从github获取源码,下载命令如下:

git clone 仓库地址

源码下载后,需要自行编译。

3.自实现

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

3.3 分布式

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

3.4 终端命令与rqt

在ROS2中提供了丰富的命令行工具,可以方便的调试程序、提高开发效率。rqt是一个图形化工具,它的功能与命令行工具类似,但是图形化的交互方式更为友好。

3.5 launch文件

通过launch文件,可以批量的启动ROS2节点,这是在构建大型项目时启动多节点的常用方式。

示例:一次性启动多个turtlesim_node节点。

3.6 TF坐标变换

TF坐标变换可以实现机器人不同部件或不同机器人之间的相对位置关系的转换。

示例1:发布机器人不同部件之间的坐标系关系

示例2:使用turtlesim_node模拟多机器人编队。

3.7 可视化

ROS2内置了三维可视化工具rviz2,它可以图形化的方式显示机器人模型或显示机器人系统中的一些抽象数据。

四、ROS2应用方向

许多ROS团队伴随ROS成长到今日,其规模已经发展到足以被认为是独立组织的程度了。在导航、机械臂、无人驾驶、无人机等诸多领域大放异彩,下面列出了其中的一些团队项目,这些项目对我们以后的进阶发展,也提供了指导。


Nav2项目继承自ROS Navigation Stack。该项目旨在可以让移动机器人从A点安全的移动到B点。它也可以应用于涉及机器人导航的其他应用,例如跟随动态点。Nav2将用于实现路径规划、运动控制、动态避障和恢复行为等一系列功能。


OpenCV

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。OpenCV旨在为计算机视觉应用程序提供通用基础架构,并加速机器感知在商业产品中的使用。OpenCV允许企业轻松地使用和修改代码。


MoveIt

MoveIt是一组ROS软件包, 主要包含运动规划、碰撞检测、运动学、3D感知、操作控制等功能。它可以用于构建机械臂的高级行为。MoveIt现在可以用于市面上的大多数机械臂,并被许多大公司使用。


The Autoware Foundation

Autoware Foundation是ROS下属的非营利组织,支持实现自动驾驶的开源项目。Autoware基金会在企业发展和学术研究之间创造协同效应,为每个人提供自动驾驶技术。


F1 Tenth

F1 Tenth是将模型车改为无人车的竞速赛事,是一个由研究人员、工程师和自主系统爱好者组成的国际社区。它最初于 2016 年在宾夕法尼亚大学成立,但后来扩展到全球许多其他机构。


microROS

在基于ROS的机器人应用中,micro-ROS正在弥合性能有限的微控制器和一般处理器之间的差距。micro-ROS在各种嵌入式硬件上运行,使ROS能直接应用于机器人硬件。


Open Robotics

Open Robotics与全球ROS社区合作,为机器人创建开放的软件和硬件平台,包括 ROS1、ROS2、Gazebo模拟器和Ignition模拟器。Open Robotics使用这些平台解决一些重要问题,并通过为各种客户组织提供软件和硬件开发服务来帮助其他人做同样的事情。


PX4

PX4是一款用于无人机和其他无人驾驶车辆的开源飞行控制软件。该项目为无人机开发人员提供了一套灵活的工具,用于共享技术并为无人机应用程序创建量身定制解决方案。


ROS-Industrial

ROS-Industrial是一个开源项目,将 ROS 软件的高级功能扩展到工业相关硬件和应用程序。

### ROSROS2的兼容性差异 ROSROS 2 是两个不同的机器人操作系统版本,在架构设计上有显著区别。这些变化影响了两者间的直接兼容性和互操作方式。 #### 架构上的根本转变 早期版本的 ROS 基于主节点(Master Node)概念构建,采用集中式的体系结构[^1]。相比之下,ROS 2 设计之初就考虑到了分布式系统的特性,采用了去中心化的网络拓扑,并引入了数据分发服务(DDS),这是一种高性能的消息传递中间件标准,用于实时系统中的通信[^2]。 #### 中间件的变化 由于 ROS 使用自定义传输层而 ROS 2 则基于 DDS 实现消息交换机制,这使得二者在底层协议层面存在不一致之处。这种改变不仅提高了安全性、可靠性以及性能表现,同时也增加了跨版本交互的技术难度[^3]。 #### 安全策略的不同实现 为了增强网络安全防护能力,ROS 2 提供了一套完整的安全框架——SROS 2,它通过环境变量 `ROS_SECURITY_STRATEGY` 来控制启用与否及具体的安全级别配置选项。然而,这样的功能并未存在于原版 ROS 当中,因此当涉及到敏感信息处理时会形成一定的障碍。 --- 针对上述提到的兼容性挑战,以下是几种可能帮助实现 ROSROS 2 之间互操作性的方法: - **桥接工具**:开发专门的应用程序作为中介桥梁连接两种平台下的节点实例,负责转换并转发来自任一方的数据流至另一方。 - **混合部署**:在同一物理硬件平台上同时运行多个独立的操作环境分别支持各自对应的软件包集合;借助虚拟化技术隔离不同进程空间从而减少冲突风险的同时保持相对松散耦合关系。 - **API适配器**:创建一层抽象接口封装住目标端口特有的调用约定细节,使应用程序能够透明地访问对方资源而不必关心内部工作原理的具体形式。 ```python import rclpy from std_msgs.msg import String def ros_to_ros2_bridge(): node = rclpy.create_node('bridge') pub = node.create_publisher(String, 'chatter', 10) def callback(msg): bridge_msg = String() bridge_msg.data = msg.data pub.publish(bridge_msg) sub = node.create_subscription( String, '/ros/chatter', callback, 10) while True: rclpy.spin_once(node) if __name__ == '__main__': ros_to_ros2_bridge() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gui林

你的热爱是我更新的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值