【ROS2】高级:使用 Fast DDS 发现服务器作为发现协议 [社区贡献]

目标:本教程将展示如何使用 Fast DDS Discovery Server 发现协议启动 ROS 2 节点。

 教程级别:高级

 时间:20 分钟

 目录

  •  背景

  •  快速 DDS 发现服务器 v2

  •  先决条件

  •  运行此教程

    •  设置发现服务器

    •  启动监听节点

    •  启动对话节点

    • 展示发现服务器执行

    •  可视化工具 rqt_graph

  •  高级用例

    •  服务器冗余

    •  备份服务器

    •  发现分区

  •  ROS 2 内省

    •  守护进程的相关工具

    •  没有虚拟光驱工具

  • 比较 Fast DDS 发现服务器与简单发现协议

 背景

从 ROS 2 Eloquent Elusor 开始,Fast DDS Discovery Server 协议是一项提供集中动态发现机制的功能而不是 DDS 默认使用的分布式机制。本教程解释了如何使用 Fast DDS Discovery Server 功能作为发现通信来运行一些 ROS 2 示例。

为了获取有关可用发现配置的更多信息,请查看以下文档 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery.html 或阅读 Fast DDS 发现服务器特定文档。https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server

简单发现协议 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/simple.html 是 DDS 标准 https://www.omg.org/omg-dds-portal/ 中定义的标准协议。然而,它在某些场景中存在已知的缺点。

  • 它不能有效扩展,因为随着新节点的添加,交换的数据包数量显著增加。

  • 它需要多播功能,但在某些情况下可能无法可靠地工作,例如 WiFi

Fast DDS 发现服务器提供了一种客户端-服务器架构,允许节点使用中间服务器相互连接。每个节点都充当发现客户端,与一个或多个发现服务器共享其信息,并从中接收发现信息。这减少了与发现相关的网络流量,并且不需要多播功能。

1573d92953cd912f4a47ba63069d22d1.png

这些发现服务器可以是独立的、重复的或相互连接的,以便在网络上创建冗余并避免单点故障。

快速 DDS 发现服务器 v2

最新的 ROS 2 Foxy Fitzroy 版本(2020 年 12 月)包含了一个新版本,即 Fast DDS 发现服务器的第 2 版。该版本包括一个新的过滤功能,可以进一步减少发送的发现消息数量。该版本使用不同节点的主题来决定两个节点是否希望通信,或者它们是否可以不匹配(即不相互发现)。下图显示了发现消息的减少情况:

2350a2289f9e367dff9021621241e42e.png

这种架构大大减少了服务器和客户端之间发送的消息数量。在下图中,显示了在 RMF 诊所演示 https://github.com/open-rmf/rmf_demos#Clinic-World 的发现阶段网络流量的减少情况:

46339b0b10585cdc371c189b7524f3a9.png

要使用此功能,可以使用参与者的 XML 配置 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server 来配置发现服务器。也可以使用 fastdds 工具 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastddscli/cli/cli.html#discovery 和环境变量 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/env_vars/env_vars.html 来配置发现服务器,这是本教程中使用的方法。有关发现服务器配置的更详细说明,请访问 Fast DDS 发现服务器文档。

 先决条件

本教程假设您安装 https://docs.ros.org/en/jazzy/Installation.html 了 ROS 2 Foxy(或更新版本)。如果您的安装使用的是低于 Foxy 的 ROS 2 版本,则无法使用 fastdds 工具。因此,为了使用发现服务器,您可以更新您的存储库以使用不同的 Fast DDS 版本,或者使用 Fast DDS XML QoS 配置 https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server 来配置发现服务器。

运行本教程

talker-listener ROS 2 演示创建一个 talker 节点,该节点每秒发布一次“hello world”消息,并创建一个 listener 节点,该节点监听这些消息。

通过获取 ROS 2 https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html ,您将可以访问 CLI 工具 fastdds 。该工具可以访问发现工具 https://fast-dds.docs.eprosima.com/en/v2.14.3/fastddscli/cli/cli.html ,可用于启动发现服务器。此服务器将管理连接到它的节点的发现过程

 重要

请勿忘记在每个新打开的终端中源 ROS 2。

设置发现服务器

首先启动一个 ID 为 0 的发现服务器,端口为 11811(默认端口),并监听所有可用接口。

打开一个新的终端并运行:

cxy@ubuntu2404-cxy:~$ fastdds discovery --server-id 0
### Server is running ###
  Participant Type:   SERVER
  Security:           NO
  Server ID:          0
  Server GUID prefix: 44.53.00.5f.45.50.52.4f.53.49.4d.41
  Server Addresses:   UDPv4:[0.0.0.0]:11811

启动监听器节点

执行监听器演示,以监听 /chatter 主题。

在一个新的终端中,将环境变量 ROS_DISCOVERY_SERVER 设置为发现服务器的位置。(不要忘记在每个新终端中 source ROS 2)

export ROS_DISCOVERY_SERVER=127.0.0.1:11811

启动监听节点。使用参数 --remap __node:=listener_discovery_server 更改此教程的节点名称。

ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_discovery_server

这将创建一个 ROS 2 节点,该节点将自动为发现服务器创建一个客户端,并连接到先前创建的服务器以执行发现,而不是使用多播。

启动 talker 节点

打开一个新的终端,并像之前一样设置 ROS_DISCOVERY_SERVER 环境变量,以便节点启动发现客户端。

export ROS_DISCOVERY_SERVER=127.0.0.1:11811
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_discovery_server

您现在应该看到讲话者发布“hello world”消息,并且听众正在接收这些消息。

演示发现服务器执行

到目前为止,没有证据表明这个例子和标准的说话者-听者例子运行得不同。为了清楚地证明这一点,请运行另一个未连接到发现服务器的节点。在一个新的终端中运行一个新的监听器(默认情况下监听 /chatter 主题),并检查它是否未连接到已经运行的说话者。

ros2 run demo_nodes_cpp listener --ros-args --remap __node:=simple_listener

新的监听节点不应该接收“hello world”消息。

要最终验证一切是否正常运行,可以使用简单发现协议(默认的 DDS 分布式发现机制)创建一个新的 talker 进行发现。

ros2 run demo_nodes_cpp talker --ros-args --remap __node:=simple_talker

现在你应该看到 simple_listener 节点接收到来自 simple_talker 的“hello world”消息,但没有接收到来自 talker_discovery_server 的其他消息

可视化工具 rqt_graph 

rqt_graph 工具可用于验证此示例的节点和结构。请记住,为了使用 rqt_graph 与发现服务器协议(即查看 listener_discovery_server 和 talker_discovery_server 节点),必须在启动前设置 ROS_DISCOVERY_SERVER 环境变量。

53cdce59ab79c9811b1a60c8cb4373d4.png

cxy@ubuntu2404-cxy:~$ export ROS_DISCOVERY_SERVER=127.0.0.1:11811
cxy@ubuntu2404-cxy:~$ rqt_graph  # 未隐藏

d8cccf09b30a32b8a5cd5a9dab1c21e6.png

cxy@ubuntu2404-cxy:~$ rqt_graph  # 隐藏debug

高级用例

以下部分显示了发现服务器的不同功能,这些功能使您能够通过网络构建一个强大的发现服务器。

 服务器冗余

通过使用 fastdds 工具,可以创建多个发现服务器发现客户端(ROS 节点)可以根据需要连接到任意数量的服务器。这允许拥有一个冗余网络,即使某些服务器或节点意外关闭也能正常工作。下图显示了提供服务器冗余的简单架构。

0e6d21252b33b746d84e87299f4ee277.png

在多个终端中,运行以下代码以建立与冗余服务器的通信。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811
fastdds discovery --server-id 1 --ip-address 127.0.0.1 --port 11888

--server-id N 表示 ID 为 N 的服务器。在引用带有 ROS_DISCOVERY_SERVER 的服务器时,服务器 0 必须放在第一位,服务器 1 必须放在第二位。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker
export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener

现在,如果其中一台服务器发生故障,仍然会有发现功能可用,节点仍然会相互发现。

备份服务器

Fast DDS 发现服务器允许创建具有备份功能的服务器。这允许服务器在关闭的情况下恢复其保存的最后状态。

66e55978e875ccc659205fc2bce210d9.png

在不同的终端上运行以下代码,以与备份服务器建立通信。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811 --backup
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener

在发现服务器的工作目录(启动时的目录)中创建了几个备份文件。两个 SQLite 文件和两个 json 文件包含启动新服务器和恢复失败服务器状态所需的信息,以防止再次发生发现过程,并且不会丢失信息。

发现分区

与发现服务器的通信可以分割,以在发现信息中创建虚拟分区。这意味着只有在两个端点之间存在共享的发现服务器或发现服务器网络时,它们才会彼此了解。我们将执行一个具有两个独立服务器的示例。下图显示了架构。

d003f7d36066283cd15aaf49b6d272bc.png

通过此架构, Listener 1 将连接到 Talker 1 和 Talker 2 ,因为它们共享 Server 1 。 Listener 2 将与 Talker 1 连接,因为它们共享 Server 2 。但是 Listener 2 将不会听到来自 Talker 2 的消息,因为它们不共享任何发现服务器或发现服务器,包括通过冗余发现服务器之间的连接间接共享。

在本地主机上运行第一个监听默认端口 11811 的服务器。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811

在另一个终端上运行第二个服务器,监听本地主机上的另一个端口,在本例中为端口 11888。

fastdds discovery --server-id 1 --ip-address 127.0.0.1 --port 11888

现在,在不同的终端中运行每个节点。使用 ROS_DISCOVERY_SERVER 环境变量来决定它们连接到哪个服务器。请注意,ID 必须匹配。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_1
export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_1
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_2
export ROS_DISCOVERY_SERVER=";127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_2

我们应该看看 Listener 1 如何从两个对话节点接收消息,而 Listener 2 与 Talker 2 在不同的分区中,因此不会接收来自它的消息。

 便条

一旦两个端点(ROS 节点)彼此发现,它们之间就不需要发现服务器网络来监听彼此的消息。

ROS 2 内省

ROS 2 命令行界面支持多种自省工具来分析 ROS 2 网络的行为。这些工具(即 ros2 bag record 、 ros2 topic list 等)对于理解 ROS 2 工作网络非常有帮助。

这些工具大多使用 DDS 简单发现来与每个现有参与者交换主题信息(使用简单发现,网络中的每个参与者都相互连接)。然而,新的 Discovery Server v2 实现了一种网络流量减少方案,限制了不共享主题的参与者之间的发现数据。这意味着节点只有在拥有该主题的写入者或读取者时才会接收该主题的发现数据。由于大多数 ROS 2 CLI 需要网络中的节点(其中一些依赖于正在运行的 ROS 2 守护进程,一些创建自己的节点),使用 Discovery Server v2 这些节点将不会拥有所有的网络信息,因此它们的功能将受到限制

Discovery Server v2 功能允许每个参与者作为超级客户端运行,这是一种连接到服务器的客户端,从服务器接收所有可用的发现信息(而不仅仅是它需要的信息)。从这个意义上说,ROS 2 内省工具可以配置为超级客户端,从而能够发现网络中使用 Discovery Server 协议的每个实体。

 便条

在本节中,我们使用术语参与者 Participant 作为 DDS 实体。每个 DDS 参与者对应一个 ROS 2 上下文,这是 DDS 上的 ROS 2 抽象。节点是依赖 DDS 通信接口的 ROS 2 实体: DataWriter 和 DataReader 。每个参与者可以容纳多个 ROS 2 节点。有关这些概念的更多详细信息,请访问节点到参与者映射设计文档https://design.ros2.org/articles/Node_to_Participant_mapping.html 。

守护进程的相关工具 Daemon’s related tools

ROS 2 守护进程用于多个 ROS 2 CLI 自省工具。它创建自己的参与者以将 ROS 2 节点添加到网络图中,以接收发送的所有数据。为了使 ROS 2 CLI 在使用发现服务器机制时工作,需要将 ROS 2 守护进程配置为超级客户端。因此,本节专门解释如何使用 ROS 2 CLI 和作为超级客户端运行的 ROS 2 守护进程。这将允许守护进程发现整个节点图,并接收所有主题和端点信息。为此,使用 Fast DDS XML 配置文件来配置 ROS 2 守护进程和 CLI 工具。

下面你可以找到一个 XML 配置文件,在本教程中应将其保存为工作目录中的 `super_client_configuration_file.xml` 文件。此文件将配置每个使用它的新参与者,作为超级客户端。

<?xml version="1.0" encoding="UTF-8" ?>  <!-- XML声明,版本1.0,编码格式UTF-8 -->
 <dds>  <!-- 开始定义dds -->
     <profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">  <!-- 定义profiles,xmlns属性定义了元素和属性的命名空间 -->
         <participant profile_name="super_client_profile" is_default_profile="true">  <!-- 定义参与者,profile_name属性定义了参与者的名称,is_default_profile属性定义了该参与者是否为默认参与者 -->
             <rtps>  <!-- 开始定义rtps -->
                 <builtin>  <!-- 开始定义builtin -->
                     <discovery_config>  <!-- 开始定义discovery_config -->
                         <discoveryProtocol>SUPER_CLIENT</discoveryProtocol>  <!-- 定义发现协议,此处为SUPER_CLIENT -->
                         <discoveryServersList>  <!-- 开始定义discoveryServersList -->
                             <RemoteServer prefix="44.53.00.5f.45.50.52.4f.53.49.4d.41">  <!-- 定义远程服务器,prefix属性定义了服务器的前缀 -->
                                 <metatrafficUnicastLocatorList>  <!-- 开始定义metatrafficUnicastLocatorList -->
                                     <locator>  <!-- 开始定义locator -->
                                         <udpv4>  <!-- 开始定义udpv4 -->
                                             <address>127.0.0.1</address>  <!-- 定义地址,此处为127.0.0.1 -->
                                             <port>11811</port>  <!-- 定义端口,此处为11811 -->
                                         </udpv4>  <!-- 结束定义udpv4 -->
                                     </locator>  <!-- 结束定义locator -->
                                 </metatrafficUnicastLocatorList>  <!-- 结束定义metatrafficUnicastLocatorList -->
                             </RemoteServer>  <!-- 结束定义RemoteServer -->
                         </discoveryServersList>  <!-- 结束定义discoveryServersList -->
                     </discovery_config>  <!-- 结束定义discovery_config -->
                 </builtin>  <!-- 结束定义builtin -->
             </rtps>  <!-- 结束定义rtps -->
         </participant>  <!-- 结束定义participant -->
     </profiles>  <!-- 结束定义profiles -->
 </dds>  <!-- 结束定义dds -->

 便条

在 RemoteServer 标签下,前缀属性值应根据 CLI 上传递的服务器 ID 进行更新(参见 Fast DDS CLI)。所示 XML 片段中指定的值对应于值为 0 的 ID。

首先,使用 Fast DDS CLI 实例化一个 ID 值为 0 的发现服务器。

fastdds discovery -i 0 -l 127.0.0.1 -p 11811

运行一个对话器和一个监听器,它们将通过服务器相互发现(注意 ROS_DISCOVERY_SERVER 配置与 super_client_configuration_file.xml 中的配置相同)。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker

然后,使用超级客户端配置实例化一个 ROS 2 守护进程(记住在每个新终端中都要 source ROS 2 安装)。

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
ros2 daemon stop
ros2 daemon start
ros2 topic list
ros2 node info /talker
ros2 topic info /chatter
ros2 topic echo /chatter
cxy@ubuntu2404-cxy:~$ export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
cxy@ubuntu2404-cxy:~$ ros2 daemon stop
The daemon is not running
cxy@ubuntu2404-cxy:~$ ros2 daemon start
The daemon has been started
cxy@ubuntu2404-cxy:~$ ros2 topic list
/chatter
/parameter_events
/rosout
cxy@ubuntu2404-cxy:~$ ros2 node info /talker
/talker
  Subscribers:
    /parameter_events: rcl_interfaces/msg/ParameterEvent
  Publishers:
    /chatter: std_msgs/msg/String
    /parameter_events: rcl_interfaces/msg/ParameterEvent
    /rosout: rcl_interfaces/msg/Log
  Service Servers:
    /talker/describe_parameters: rcl_interfaces/srv/DescribeParameters
    /talker/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
    /talker/get_parameters: rcl_interfaces/srv/GetParameters
    /talker/get_type_description: type_description_interfaces/srv/GetTypeDescription
    /talker/list_parameters: rcl_interfaces/srv/ListParameters
    /talker/set_parameters: rcl_interfaces/srv/SetParameters
    /talker/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
  Service Clients:


  Action Servers:


  Action Clients:


cxy@ubuntu2404-cxy:~$ ros2 topic info /chatter
Type: std_msgs/msg/String
Publisher count: 1
Subscription count: 1
cxy@ubuntu2404-cxy:~$ ros2 topic echo /chatter
data: 'Hello World: 59'
---
data: 'Hello World: 60'
---
data: 'Hello World: 61'
---
data: 'Hello World: 62'
---
data: 'Hello World: 63'

我们还可以使用 ROS 2 工具 rqt_graph 查看节点图,如下所示(您可能需要按刷新按钮):

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
rqt_graph

b6ec2aac2a4818730e1dd82baa61549f.png

11d0c7243f29b5ff95fc7a2dd3cf1e5e.png

没有守护进程工具 No Daemon tools

有些 ROS 2 CLI 工具不使用 ROS 2 Daemon为了使这些工具能够连接到发现服务器并接收所有主题信息,它们需要被实例化为连接到服务器的超级客户端。

根据之前的配置,构建一个包含说话者和听者的简单系统。首先,运行一个服务器:

fastdds discovery -i 0 -l 127.0.0.1 -p 11811

然后,在不同的终端中运行 talker 和 listener:

export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker

继续使用带有 --no-daemon 选项的新配置的 ROS 2 CLI。新节点将连接到现有服务器,并将知道每个主题。不需要导出 ROS_DISCOVERY_SERVER ,因为 ROS 2 工具将通过 FASTRTPS_DEFAULT_PROFILES_FILE 进行配置。

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
ros2 topic list --no-daemon
ros2 node info /talker --no-daemon --spin-time 2

比较 Fast DDS 发现服务器与简单发现协议

为了比较使用简单发现协议(默认的 DDS 机制用于分布式发现)或发现服务器执行节点,提供了两个脚本来执行一个讲述者和多个监听器,并在此期间分析网络流量。对于这个实验,您的系统上需要安装 tshark 。配置文件是必需的,以避免使用进程内模式。

 便条

这些脚本仅支持 Linux,并且需要一个发现服务器关闭功能,该功能仅在比 ROS 2 Foxy 版本更新的版本中可用。为了使用此功能,请使用 Fast DDS v2.1.0 或更高版本编译 ROS 2。

这些脚本的功能是供高级用途参考的,学习由用户自行完成。

  • bash network traffic generator

  • python3 graph generator

  • XML configuration

https://docs.ros.org/en/jazzy/_downloads/4091f7105a6c8d10b26927288ede29fc/generate_discovery_packages.bash

https://docs.ros.org/en/jazzy/_downloads/617a6849c029c43a931e24db314cb224/discovery_packets.py

https://docs.ros.org/en/jazzy/_downloads/358434c24ca02455545e1739688b839b/no_intraprocess_configuration.xml

运行 bash 脚本,并将 setup.bash 文件的路径作为参数传递给 ROS 2。这将生成简单发现的流量跟踪。使用第二个参数 SERVER 执行相同的脚本。它将生成使用发现服务器的跟踪。

 便条

根据您对 tcpdump 的配置,此脚本可能需要 sudo 权限才能读取网络设备上的流量。

在两个执行都完成后,运行 Python 脚本生成一个类似于下面的图表。

$ export FASTRTPS_DEFAULT_PROFILES_FILE="no_intraprocess_configuration.xml"
$ sudo bash generate_discovery_packages.bash ~/ros2/install/local_setup.bash
$ sudo bash generate_discovery_packages.bash ~/ros2/install/local_setup.bash SERVER
$ python3 discovery_packets.py

999e41d05a648719db9dc8a8caa5b6c3.png

此图是实验特定运行的结果。读者可以执行脚本并生成自己的结果进行比较。可以很容易地看出,使用发现服务时网络流量减少

流量的减少是由于避免每个节点宣布自己并等待网络上每个其他节点的响应。这在大型架构中会产生大量流量。随着节点数量的增加,这种方法的减少量增加,使得这种架构比简单发现协议方法更具可扩展性

新的 Fast DDS Discovery Server v2 自 Fast DDS v2.0.2 起可用,取代了旧的发现服务器。在这个新版本中,那些不共享主题的节点将自动不发现彼此,从而节省了连接它们及其端点所需的整个发现数据。上面的实验没有显示这种情况,但即便如此,由于 ROS 2 节点的隐藏基础设施主题,流量的大幅减少仍然可以被欣赏。

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值