ROS 1 和 ROS 2 的前世、今生、安装使用说明与资料汇总

----ROS 2 Ardent Apalone发布----

现在,最常用的ROS indigo或ROS Kinetic等都是1.0时代的ROS,这个时代的ROS有一个master(roscore)。


历经10年发展,不断壮大。那么问题来了为什么会有ROS 2.0?

要想搞清楚这个问题,需要参考ROS 2.0设计网址:http://design.ros2.org/


这里摘录一段话,选自Why ROS 2.0:

Why not just enhance ROS 1

In principle, the changes described above could be integrated into the existing core ROS code. E.g., new transport technologies could be added to roscpp and rospy. We considered this option and concluded that, given the intrusive nature of the changes that would be required to achieve the benefits that we are seeking, there is too much risk associated with changing the current ROS system that is relied upon by so many people. We want ROS 1 as it exists today to keep working and be unaffected by the development of ROS 2. So ROS 2 will be built as a parallel set of packages that can be installed alongside and interoperate with ROS 1 (e.g., through message bridges).

----

为什么不改进完善ROS 1.0?

从本质上来说,上面描述的更改都可以集成到现有的ROS核心代码中。如,新的传输机制可以添加到roscpp和rospy中。但是当我们考虑到下面问题时,就能理解这个结论,即这些改进和优化对原有系统会造成入侵式的变化,改变当前ROS系统有非常大的风险,当前有很多功能是开发人员依赖原ROS系统开发的。于是,我们既继续维护ROS 1.0,不受ROS 2.0的影响,同时进行ROS 2.0的设计。ROS 2.0与ROS 1.0并行不悖,同时通过消息桥接进行通信。

----

下面放几张图,大家感受一下两者的差别:

图1,架构图


Master没了,节点平等了,无主节点了。DDS是什么?数据分发服务。我想到了微软的DSS(分布式软件服务)。

图2,数据发布与订阅

    

需要了解更多内容,推荐古月居ROS 2.0探索总结+ROS 1.0和ROS 2.0到底用哪个呢?

ROS1与ROS2的区别?http://design.ros2.org/articles/changes.html

ROS 1和ROS 2之间的区别

本文概述了ROS 2与ROS 1相比所做的更改。

原作者:德克托马斯

前言

尽可能简短地描述每一个变化,但是给熟悉ROS 1的读者提供足够的上下文和理由。如果有更多的外部信息可用(例如其他文章),应该将其链接到。

某些描述的功能尚不可用,并标有⏳。

平台和依赖关系

平台

ROS 1仅在Ubuntu上进行CI测试。社区在其他Linux版本以及OS X上积极支持。

ROS 2目前在Ubuntu Xenial,OS X El Capitan以及Windows 10上进行了CI测试和支持(请参阅ci.ros2.org)。

语言

C ++标准

ROS 1的核心是针对C ++ 03,并没有在其API中使用C ++ 11功能。ROS 2广泛使用C ++ 11,并使用C ++ 14的某些部分。在未来,ROS 2可能会开始使用C ++ 17,只要它在所有主要平台上受支持。

蟒蛇

ROS 1是针对Python 2. ROS 2至少需要Python版本3.5。

重用现有的中间件

ROS 1使用自定义序列化格式,自定义传输协议以及自定义中央发现机制。ROS 2有一个抽象的中间件接口,通过它提供序列化,传输和发现。目前这个接口的所有实现都是基于DDS标准的。这使得ROS 2能够提供各种服务质量的策略,从而改善通过不同网络的通信。

建立系统

有关构建系统的更多信息,请参见柔夷花序文章。

支持CMake旁边的其他构建系统

每个ROS包都是一个CMake项目。在ROS 2中,可以轻松地支持其他构建系统。现在构建工具支持CMake旁边的纯Python包。

Python包

在ROS 1中,使用Python代码的软件包只能使用setup.py文件中可用功能的一小部分,因为setup.py文件是由CMake内的自定义逻辑处理的。在ROS 2中,一个Python包可以使用setup.py文件中的任何东西,例如入口点,因为它们被调用python3 setup.py install

环境设置

在ROS 1中,构建工具会生成脚本,这些脚本必须来源以便在能够使用构建的ROS软件包之前设置环境。这种方法只有在使用ROS特定的构建工具构建ROS包时才有效。

在ROS 2中,环境设置分为特定于包的脚本和特定于工作空间的脚本。每个软件包都提供了必要的脚本,使其在构建后可用。构建工具只调用特定于工作空间的脚本,然后调用特定于包的脚本。

没有非隔离的构建

在ROS 1中,可以在单个CMake上下文中构建多个包。虽然这加快了构建步骤,但是每个包都需要确保正确定义了交叉包目标依赖关系。另外所有的软件包共享相同的命名空间,导致目标名称冲突等。

在ROS 2中,只支持隔离的构建,即每个包都是独立构建的。安装空间可以是隔离的或合并的。

没有开发空间

在ROS 1中,可以在不安装包的情况下构建包。从开发空间与源空间结合起来,系统已经可以使用。但是每个软件包都必须积极支持开发空间,例如环境钩子和CMake代码。

在ROS 2中,必须先安装一个软件包,然后才能使用它。

ROS 1中的开发空间的一个原因是使开发人员能够更改文件,例如Python代码或启动文件,并直接使用修改后的代码,而无需重新编译软件包。在ROS 2中保留了这个好处,可以选择性地用符号链接替换安装步骤中的复制操作。

支持catkin_simple用例

在ROS 1中,包catkin_simple旨在使编写ROS包的CMake代码变得更容易。在许多情况下,它不能实现这个目标,这通常是由于像开发空间这样的支持功能所必需的设计的限制。

在ROS 2中,CMake API进行了重组,以支持这个用例。

对没有清单的软件包的最小支持

在ROS 1中,构建系统只考虑具有清单文件的包。在ROS 2中,可以在没有清单文件的文件夹中检测具有支持的构建系统的包。如果软件包遵循惯例,甚至有可能检测到一些缺失的元信息(如依赖关系)。

消息,服务

有关更多信息,请参阅ROS接口定义文章。

C ++中的分隔名称空间

在ROS 1中,.msg和.srv文件可以具有相同的名称,但生成的代码会发生冲突。请求和响应部分的服务也是如此。

在ROS 2中,生成的代码使用单独的名称空间来保证它是无冲突的。

Python中的同名

为消息和服务生成的Python代码当前在ROS 1和ROS 2中使用相同的模块和类名称。因此,它们不能在单个应用程序中导入。如果需要,这个决定可能会被重新审视。

消息定义中的可选默认值

在ROS 2中,消息中的原始值现在可以具有默认值,在消息构建时设置。非原始字段(即字符串数组,嵌套消息)的默认值不可能(⏳)。

数组和字符串的可选上限

为了计算消息在内存中的最大大小,这是必要的,这允许用动态大小预分配消息。这对性能和实时用例很有用。

统一持续时间和时间类型

在ROS 1中,持续时间和时间类型是在客户端库中定义的。数据结构的成员名称在C ++(sec,nsec)和Python(secs,nsecs)中是不同的。

在ROS 2中,这些类型被定义为消息,因此在不同的语言中是一致的。

从标题消息中删除序列字段

该领域已被弃用了很长一段时间,并没有在ROS 1中一贯设置。

客户端库

跨语言

主题命名空间(⏳)

目前ROS 2不支持主题名称中的命名空间。这主要是由于DDS主题名称中有效字符的限制。一个设计文档描述了如何在未来应该加入。

通知

在ROS 1中,关于ROS图的所有信息都必须从主站查询。在ROS 2中,更改将发布,例如,如果参数已更改,则会发出通知。

具有生命周期的组件

在ROS 1中,每个节点通常都有自己的主要功能。在ROS 2中,建议从具有生命周期的组件中继承子类。

生命周期可以被像roslaunch这样的工具用来以确定性的方式启动一个由许多组件组成的系统(⏳)。

有关更多信息,请参阅节点生命周期文章。

参数和动态重新配置

在ROS 1中,全局参数和节点特定的动态重新配置参数是两个单独的概念。在ROS 2中,正在使用统一的方法。它类似于动态重新配置,名为“全局参数服务器”(⏳)的节点将接受无条件设置值的请求。在ROS 1中,所有这些信息都需要被调查,因为ROS 2的变化将被公布以通知其他实体。

欲了解更多信息,请参阅参数设计文章。

行动(⏳)

ROS 2目前没有行动的概念。它将在未来作为可抢占服务器和反馈发布者的组合加入。

线程模型

在ROS 1中,开发人员只能选择单线程执行或多线程执行。在ROS 2中,更多的粒度执行模型可以在C ++中使用(例如跨越多个节点),并且自定义执行器可以轻松实现。对于Python来说,执行模型还没有实现。

ROS图表

在ROS 1中,节点和主题只能在启动时重新映射。在ROS 2中,对重映射的支持尚未实现(⏳)。目标是启用重新映射以及别名,不仅在启动时间,而且在运行时。

在ROS 1中,节点名称是唯一的,当启动具有相同名称的新节点时,通过关闭现有节点来强制实施。在ROS 2中,节点名称的唯一性还没有被强制执行。

C和C ++

支持实时

ROS 1不支持编写实时代码,而是依赖于Orocos等外部框架。在ROS 2中,当使用合适的RTOS和仔细编写的用户代码时,可以编写实时节点。

C ++

节点与Nodelet

在ROS 1中,节点和节点的API是不同的,并且需要开发者在编程时决定节点到进程的映射。在ROS 2中,建议将每个组件编译成一个共享库,然后可以在单独的进程中加载​​它,或与其他组件(如ROS 1 nodelets)共享相同的进程。这使得可以在部署时选择流程布局。

每个进程允许多个节点

在ROS 1中,不可能在一个进程中创建多个节点。这是由于API本身,也是由于内部的实施决定。在ROS 2中,可以在一个进程中创建多个节点。

工具

roslaunch(⏳)

在ROS 1中,roslaunch文件是用XML定义的,功能非常有限。在ROS 2中,启动文件是用Python编写的,可以使用更复杂的逻辑,如条件等。当前状态只提供最小的功能来运行使用多个进程的测试。

资源查找

在ROS 1中,通过基于ROS_PACKAGE_PATH搜索文件系统来查找各种资源(包,消息,插件等)。当ROS_PACKAGE_PATH中的树很大时,这可能导致性能较差,并且缓存产生不一致的状态。

在ROS 2中,资源可以在编译时在索引处注册,然后在运行时被有效查询。有关更多信息,请参阅资源索引的文档

打包

ABI版本控制(⏳)

由于它假定ABI不兼容,ROS 1重建所有下游软件包。为了避免这种大的开销,ROS 2包应该能够声明它的ABI,以尽可能避免重建下游包。

Windows二进制包(⏳)

ROS 1只能从Windows上的源代码(也只适用于几个ROS软件包,不受支持)构建。ROS 2将提供基于Chocolatey的二进制包。


~~~~

~~~~

小乌龟动起来


relaybot@relaybot-desktop:~$ rosnode list 
/ros_bridge
/rosout
/turtlesim
relaybot@relaybot-desktop:~$ ros2 node list 
teleop_turtle
ros_bridge
ros_bridge
teleop_turtle
relaybot@relaybot-desktop:~$ ros2 topic list
/color_sensor
/parameter_events
/pose
/turtle1/cmd_vel
relaybot@relaybot-desktop:~$ rostopic list 
/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose


$ ros2 run ros1_bridge dynamic_bridge
Created 2 to 1 bridge for service /clear
Created 2 to 1 bridge for service /reset
created 2to1 bridge for topic '/turtle1/cmd_vel' with ROS 2 type 'geometry_msgs/Twist' and ROS 1 type ''
[INFO] [ros1_bridge]: Passing message from ROS 2 ROS2_T to ROS 1 ROS1_T (showing msg only once per type) (ros2_callback() at /tmp/binarydeb/ros-r2b3-ros1-bridge-0.0.3/include/ros1_bridge/factory.hpp:144)


下面介绍一下ROS 2.0的安装和使用

官方wiki: https://github.com/ros2/ros2/wiki

这里有一点需要特别说明,ROS 2.0是支持Linux,Mac OS X和Windows,但仍然是使用Ubuntu最方便。

安装过程比较简单,直接使用Debian包即可,简单说一下,参考链接:https://github.com/ros2/ros2/wiki/Installation

设置安装源:

sudo apt update && sudo apt install curl
curl http://repo.ros2.org/repos.key | sudo apt-key add -
sudo sh -c 'echo "deb http://repo.ros2.org/ubuntu/main xenial main" > /etc/apt/sources.list.d/ros2-latest.list'

安装ROS 2.0软件包:

sudo apt update
sudo apt install `apt list ros-r2b3-* 2> /dev/null | grep "/" | awk -F/ '{print $1}' | grep -v -e ros-r2b3-ros1-bridge -e ros-r2b3-turtlebot2- | tr "\n" " "`

环境配置:

source /opt/ros/r2b3/setup.bash

如果需要与ROS 1.0通信

sudo apt update
sudo apt install ros-r2b3-ros1-bridge ros-r2b3-turtlebot2-*

具体安装请参考英文原版网址。

----

下面我们来测试一下ROS 2.0。

老方法在一个终端发送消息,另一个终端接受消息:

ros2 run demo_nodes_cpp talker

ros2 run demo_nodes_py listener



发布TF数据:

ros2 run tf2_ros static_transform_publisher -- 1 2 3 0.5 0.1 -1.0 foo bar

ros2 run tf2_ros tf2_echo -- foo bar



测试USB摄像头:

ros2 run image_tools cam2image

ros2 run image_tools showimage



----

ROS 1.0和ROS 2.0的示例:

1. 简单的发布和订阅

需要四个终端,需要注意环境配置:



2.  图像传输




3. 服务


ros2.0的命令与ros1.0保持了比较好的兼容性。示例如下:


relaybot@relaybot-desktop:~$ ros2 pkg executables
amcl amcl
astra_camera astra_camera_node
astra_camera create_udev_rules
cartographer_ros cartographer_node
composition api_composition
composition api_composition_cli
composition dlopen_composition
composition linktime_composition
composition manual_composition
demo_nodes_cpp add_two_ints_client
demo_nodes_cpp add_two_ints_client_async
demo_nodes_cpp add_two_ints_server
demo_nodes_cpp allocator_tutorial
demo_nodes_cpp imu_listener
demo_nodes_cpp list_parameters
demo_nodes_cpp list_parameters_async
demo_nodes_cpp listener
demo_nodes_cpp listener_best_effort
demo_nodes_cpp one_off_timer
demo_nodes_cpp parameter_events
demo_nodes_cpp parameter_events_async
demo_nodes_cpp parameter_node
demo_nodes_cpp reuse_timer
demo_nodes_cpp ros2param
demo_nodes_cpp set_and_get_parameters
demo_nodes_cpp set_and_get_parameters_async
demo_nodes_cpp talker
demo_nodes_cpp_native talker
demo_nodes_py add_two_ints_client
demo_nodes_py add_two_ints_client_async
demo_nodes_py add_two_ints_server
demo_nodes_py listener
demo_nodes_py listener_qos
demo_nodes_py talker
demo_nodes_py talker_qos
depthimage_to_laserscan depthimage_to_laserscan_node
depthimage_to_pointcloud2 depthimage_to_pointcloud2_node
dummy_map_server dummy_map_server
dummy_sensors dummy_joint_states
dummy_sensors dummy_laser
examples_rclcpp_minimal_client client_main
examples_rclcpp_minimal_composition composition_composed
examples_rclcpp_minimal_composition composition_publisher
examples_rclcpp_minimal_composition composition_subscriber
examples_rclcpp_minimal_publisher publisher_lambda
examples_rclcpp_minimal_publisher publisher_member_function
examples_rclcpp_minimal_publisher publisher_not_composable
examples_rclcpp_minimal_service service_main
examples_rclcpp_minimal_subscriber subscriber_lambda
examples_rclcpp_minimal_subscriber subscriber_member_function
examples_rclcpp_minimal_subscriber subscriber_not_composable
examples_rclcpp_minimal_timer timer_lambda
examples_rclcpp_minimal_timer timer_member_function
examples_rclpy_executors callback_group
examples_rclpy_executors composed
examples_rclpy_executors custom_callback_group
examples_rclpy_executors custom_executor
examples_rclpy_executors listener
examples_rclpy_executors talker
examples_rclpy_minimal_client client
examples_rclpy_minimal_client client_async
examples_rclpy_minimal_client client_async_member_function
examples_rclpy_minimal_publisher publisher_local_function
examples_rclpy_minimal_publisher publisher_member_function
examples_rclpy_minimal_publisher publisher_old_school
examples_rclpy_minimal_service service
examples_rclpy_minimal_service service_member_function
examples_rclpy_minimal_subscriber subscriber_lambda
examples_rclpy_minimal_subscriber subscriber_member_function
examples_rclpy_minimal_subscriber subscriber_old_school
image_tools cam2image
image_tools showimage
intra_process_demo camera_node
intra_process_demo cyclic_pipeline
intra_process_demo image_pipeline_all_in_one
intra_process_demo image_pipeline_with_two_image_view
intra_process_demo image_view_node
intra_process_demo two_node_pipeline
intra_process_demo watermark_node
joy joy_node
lifecycle lifecycle_listener
lifecycle lifecycle_service_client
lifecycle lifecycle_service_client_py.py
lifecycle lifecycle_talker
map_server map_saver
map_server map_server
robot_state_publisher robot_state_publisher
ros1_bridge dynamic_bridge
ros1_bridge ros1_bridge_generate_factories
ros1_bridge parameter_bridge
ros1_bridge simple_bridge
ros1_bridge simple_bridge_1_to_2
ros1_bridge simple_bridge_2_to_1
ros1_bridge static_bridge
ros1_bridge test_ros2_client_cpp
ros1_bridge test_ros2_server_cpp
rosidl_generator_c rosidl_generator_c
rosidl_generator_cpp rosidl_generator_cpp
rosidl_generator_dds_idl rosidl_generator_dds_idl
rosidl_generator_py rosidl_generator_py
rosidl_typesupport_c rosidl_typesupport_c
rosidl_typesupport_cpp rosidl_typesupport_cpp
rosidl_typesupport_introspection_c rosidl_typesupport_introspection_c
rosidl_typesupport_introspection_cpp rosidl_typesupport_introspection_cpp
rosidl_typesupport_opensplice_c rosidl_typesupport_opensplice_c
rosidl_typesupport_opensplice_cpp rosidl_typesupport_opensplice_cpp
teleop_twist_joy teleop_node
teleop_twist_keyboard teleop_twist_keyboard
tf2_ros static_transform_publisher
tf2_ros tf2_echo
tf2_ros tf2_monitor
topic_monitor data_publisher
topic_monitor launch_depth_demo
topic_monitor launch_fragmentation_demo
topic_monitor launch_reliability_demo
topic_monitor topic_monitor
turtlebot2_drivers dumb_teleop
turtlebot2_drivers kobuki_node
turtlebot2_follower follower
relaybot@relaybot-desktop:~$ 


relaybot@relaybot-desktop:~$ ros2 topic -h
usage: ros2 topic [-h] [--include-hidden-topics]
                  Call `ros2 topic <command> -h` for more detailed usage. ...

Various topic related sub-commands

optional arguments:
  -h, --help            show this help message and exit
  --include-hidden-topics
                        Consider hidden topics as well

Commands:
  echo  Output messages from a topic
  list  Output a list of available topics
  pub   Publish a message to a topic

  Call `ros2 topic <command> -h` for more detailed usage.
relaybot@relaybot-desktop:~$ ros2 topic list
/parameter_events
relaybot@relaybot-desktop:~$ ros2 node list
ros_bridge
relaybot@relaybot-desktop:~$ 

relaybot@relaybot-desktop:~$ ros2 --help
usage: ros2 [-h] Call `ros2 <command> -h` for more detailed usage. ...

ros2 is an extensible command-line tool for ROS 2.

optional arguments:
  -h, --help            show this help message and exit

Commands:
  daemon    Various daemon related sub-commands
  msg       Various msg related sub-commands
  node      Various node related sub-commands
  pkg       Various package related sub-commands
  run       Run a package specific executable
  security  Various security related sub-commands
  service   Various service related sub-commands
  srv       Various srv related sub-commands
  topic     Various topic related sub-commands

  Call `ros2 <command> -h` for more detailed usage.


资料汇总:

------

机器人操作系统(ROS)的2.0时代(ROS 2.0) 

一个精心设计的机器人操作系统2.0版本(ROS 2.0)资源和库的列表

内容

软件包(或库)

文档

图书

没有书出版

培训班

还没有课程

演讲


------

发布Ardent Apalone

Chris Lalancette  

ROS 2 Ardent Apalone(代号“ardent”; 2017年12月)

欢迎使用Ardent Apalone的ROS 2软件的第一个非beta版本

支持的平台

这个版本的ROS 2支持三种平台:

  • Ubuntu 16.04(Xenial)
  • Mac OS X 10.12(Sierra)
  • Windows 10

所有3个平台都提供了二进制包以及如何从源代码编译的说明(请参阅安装说明以及文档)。

特征

  • 分布式发现,发布/订阅,请求/响应通信
    • 由C API提供
    • 使用不同的供应商实施:
      • eProsima的FastRTPS以及凌华的OpenSplice(来自二进制和源代码)
      • RTI的Connext(仅来自源)
    • 许多服务质量的设置处理非理想的网络
    • DDS安全支持(使用Connext和FastRTPS)
  • C ++和Python 3客户端库
    • 在C中共享通用代码来统一实现
    • 执行模型与节点分离,可组合的节点
    • 节点特定的参数(仅在C ++ atm中)
    • 生命周期(仅在C ++ atm中)
    • 可选的使用相同API的进程内通信(仅在C ++中)
  • 消息定义(包含数组和字符串以及默认值)
  • 命令行工具(例如ros2 run
  • rviz 与几个显示类型(Windows版本可能会在几个星期后)
  • 基于文件系统的资源索引(查询信息无递归爬行)
  • pub / sub的实时安全代码路径(仅兼容DDS实现)
  • ROS 1和ROS 2之间的桥梁
  • HSR演示参见Beta 3
  • Turtlebot演示请参阅Beta 2

有关更详细的说明,请参阅功能页面。

自Beta 3发布以来的更改

自Beta 3版本以来的改进:

  • rviz
  • C ++中消息数据结构的不同初始化选项
  • 记录API改进,现在也用于演示
  • 用C ++支持不同的时钟
  • Python客户端库中的等待服务支持
  • REP 149的实现草案指定了包清单文件的格式3

已知的问题

  • FastRTPS性能与更大的数据,如图像演示
  • 使用Connext,目前不允许具有相同基本名称但名称空间不同的两个主题具有不同的类型(请参见问题)。
  • 节点名称(例如使用ros2 node list)的列表在某些rmw实现中不起作用。
  • 在Windows上,尝试中止使用时,Python启动文件可能会挂起Ctrl-C(请参阅问题)。为了继续使用被挂起命令阻塞的shell,可能需要使用进程监视器来结束挂起的Python进程。

------

我们很高兴宣布ROS 2发布Ardent Apalone

安装说明和教程可以在ROS 2 wiki 86上找到

要了解此版本中的内容,请务必阅读Ardent页面36

我们希望在此版本中强调一些功能和改进:

  • 第一个非beta版本。
  • rviz 包括几个插件(Windows版本将在几周后)
  • C ++中消息数据结构的不同初始化选项
  • 记录API改进,现在也用于演示
  • 用C ++支持不同的时钟
  • Python客户端库中的等待服务支持
  • 实施草案REP 149 6包清单文件的指定格式3

虽然总是有更多的工作要做,但我们还是要考虑几个核心组件,以供更广泛的受众使用。因此,我们放弃了“beta”标签,并将支持Ardent一年,修复错误并保持发布工作,尤其是在具有滚动依赖版本(OS X / Windows)的平台上。我们将为ROS 2的新版本定期发布六个月的发布周期。虽然我们的目标是尽可能保持API的稳定性,但我们不能保证发布之间的API兼容性为100%。也就是说,ROS 2是否可以用于您的应用程序,或者现在是否可以从ROS 1切换到ROS 2,取决于用例的确切功能集和要求。请检查功能页面21以及路线图16 了解更多信息。

与往常一样,我们邀请您尝试新软件,提供反馈,报告错误,并建议功能(并贡献代码!):https//github.com/ros2/ros2/wiki/Contact 1

------

~End~


  • 15
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zhangrelay

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值