[ROS 系列学习教程] ROS服务(Service)通信:通信模型、Hello World与拓展

ROS中rosservice系列命令详解 Rosservice call service_name arg1 arg2…… 我们如何可以实现像rostopic pub topic_name topic_type: “arg1_name:arg1……”那样在命令行发布消息,rosservice call可以帮我们做到: 输入完topic_type后,后面紧跟着arg1 arg2……就行,不用像话题通信那样繁琐。输入之后,客户端反应如下: Rosservice find service_type 如果说“rostopic f.. 阅读详情

在这里插入图片描述

ROS 系列学习教程(总目录)
ROS2 系列学习教程(总目录)

服务通讯是基于请求响应模式的,是一种应答机制。

用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。

一、服务通讯模型

服务是一种双向通讯方式,它通过请求和应答的方式传递消息,该模型涉及到三个角色:

  • Master (管理者)
  • Server(服务端)
  • Client(客户端)

Master 负责保管 Server 和 Client 的注册信息,并匹配服务名称相同的 Server 和 Client ,帮助 他们建立连接,连接建立后,Client 可以发送请求信息, Server 收到请求后返回响应信息。

在这里插入图片描述

服务模型通讯流程:

  • 0)advertise:服务端注册

    服务端(Server)向管理者(Master)注册信息,包括RPC地址和Service名字。Master会将服务端的注册信息加入到注册表中。

  • 1)客户端注册

    客户端(Client)向管理者(Master)注册信息,包括Service名字。Master会将客户端(Client)的注册信息加入到注册表中。

  • 2)Master匹配信息:牵线搭桥

    管理者(Master)通过查询注册表发现有匹配的服务端(Server)和客户端(Client),则通过RPC向客户端(Client)发送服务端(Server)的 TCP/UDP 地址信息。

  • 3)客户端发送请求信息

    客户端根据服务端的 TCP/UDP 地址与服务端建立网络连接,并发送请求信息。

  • 4)服务端响应请求

    服务端收到请求数据后,通过处理产生响应数据,通过 TCP/UDP 返回给客户端。

Note:

  1. 上述实现流程中,前三步使用 RPC 协议,最后两步使用 TCP/UDP 协议,默认TCP。
  2. 客户端请求时,必须保证服务端已经启动。
  3. 服务名相同的客户端可以有多个,服务端只能有1个。
  4. 与话题通信不同,服务通信过程中,ROS Master必须处于启动状态。

二、Service Hello World

万物始于Hello World,同样,使用Hello World介绍Service的简单使用。

使用Service传输数据时,需要注意以下几点:

  • Service名称
  • 消息格式(srv)
  • 服务端实现
  • 客户端实现

接下来实现一个简单的 Service 服务通信,客户端请求启动机器人,服务端启动机器人的各个模块,然后返回执行结果。

2.1 创建并初始化功能包

首先创建 service_hello_world 包,命令如下:

catkin_create_pkg service_hello_world std_srvs roscpp rospy

创建后,文件结构如下:

在这里插入图片描述

2.2 确定Service名称及消息格式

Service名称:/hello_world_service

消息格式:std_srvs::SetBool

消息文件路径:/opt/ros/noetic/share/std_srvs/srv/SetBool.srv

消息文件内容:

bool data # e.g. for hardware enabling / disabling
---
bool success   # indicate successful run of triggered service
string message # informational, e.g. for error messages

2.3 实现服务端与客户端(C++版)

在创建的 service_hello_world 包路径下有一个 src 目录,在这里存储C++源码,我们创建 service_hello_world_server.cpp 以实现服务端,编辑内容如下:

#include <ros/ros.h>
#include <std_srvs/SetBool.h>

bool dealRobotSwitch(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &resp)
{
    bool flag = req.data;
    ROS_INFO("服务器收到 [%s] 机器人的指令.", flag ? "启动" : "关闭");

    // 逻辑处理
    if (flag)
    {
        ROS_INFO("正在启动机器人各模块...");
        ros::Duration(2).sleep();
        // 使用时间模拟随机成功与失败
        if (ros::Time::now().toNSec() % 2 == 0)
        {
            resp.success = true;
            resp.message = "Hello World.";
            ROS_INFO("机器人各模块启动成功.\n");
        }
        else
        {
            resp.success = false;
            resp.message = "再睡一会";
            ROS_INFO("机器人各模块启动失败.\n");
        }
    }
    else
    {
        ROS_INFO("正在关闭机器人各模块...");
        ros::Duration(2).sleep();
        // 模拟成功与失败
        if (ros::Time::now().toNSec() % 2 == 0)
        {
            resp.success = true;
            resp.message = "Good Night.";
            ROS_INFO("机器人各模块关闭成功.\n");
        }
        else
        {
            resp.success = false;
            resp.message = "我还能卷";
            ROS_INFO("机器人各模块关闭失败.\n");
        }
    }

    return true;
}

int main(int argc, char **argv)
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "service_hello_world_server");
    ros::NodeHandle nh;
    ros::ServiceServer server = nh.advertiseService("/robotSwitch", dealRobotSwitch);
    ROS_INFO("robotSwitch 服务已启动...");
    ros::spin();
    return 0;
}

创建 service_hello_world_client.cpp 以实现客户端,编辑内容如下:

#include <ros/ros.h>
#include <std_srvs/SetBool.h>

int main(int argc, char **argv)
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "service_hello_world_client");
    ros::NodeHandle nh;
    ros::ServiceClient client = nh.serviceClient<std_srvs::SetBool>("/robotSwitch");

    std_srvs::SetBool srv;
    if (strcmp(argv[1], "on") == 0)
    {
        srv.request.data = true;
    }
    else if (strcmp(argv[1], "off") == 0)
    {
        srv.request.data = false;
    }
    else
    {
        ROS_WARN("仅支持on和off");

        return 1;
    }

    // 等待服务启动
    // ros::service::waitForService("/robotSwitch");
    // client.waitForExistence();
    if (client.call(srv))
    {
        if (srv.response.success)
        {
            ROS_INFO("操作成功, %s", srv.response.message.c_str());
        }
        else
        {
            ROS_ERROR("操作失败, %s", srv.response.message.c_str());
        }
    }
    else
    {
        ROS_ERROR("操作失败, 未知错误!");
    }

    return 0;
}

修改 CMakeLists.txt ,只需添加如下内容:

add_executable(${PROJECT_NAME}_client src/service_hello_world_client.cpp)
add_executable(${PROJECT_NAME}_server src/service_hello_world_server.cpp)

target_link_libraries(${PROJECT_NAME}_client
  ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}_server
  ${catkin_LIBRARIES}
)

编译运行

进入工作空间执行 catkin_make 命令编译工程,编译成功后,使用如下命令依次启动服务端和客户端。

1. 启动ros master
roscore
2. 启动服务端
rosrun service_hello_world service_hello_world_server
3. 启动客户端
rosrun service_hello_world service_hello_world_client

结果如下:

在这里插入图片描述

目前为止,Service Hello World 已经成功了。

2.4 实现服务端与客户端(Python版)

在创建的 service_hello_world 包路径下 src 目录的同级,创建一个 scripts 目录,在这里存储脚本(如python脚本),我们创建 service_hello_world_server.py 以实现服务端,编辑内容如下:

import rospy
from std_srvs.srv import SetBool, SetBoolResponse


def dealRobotSwitch(req):
    flag = req.data
    rospy.loginfo("服务器收到 [%s] 机器人的指令.", "启动" if flag else "关闭")
    if flag:
        rospy.loginfo("正在启动机器人各模块...")
        if rospy.Time.now().to_nsec() % 2 == 0:
            rospy.loginfo("机器人各模块启动成功.\n")
            return SetBoolResponse(True, "Hello World.")
        else:
            rospy.logerr("机器人各模块启动失败.\n")
            return SetBoolResponse(False, "再睡一会")
    else:
        rospy.loginfo("正在关闭机器人各模块...")
        if rospy.Time.now().to_nsec() % 2 == 0:
            rospy.loginfo("机器人各模块关闭成功.\n")
            return SetBoolResponse(True, "Good Night.")
        else:
            rospy.logerr("机器人各模块关闭失败.\n")
            return SetBoolResponse(False, "我还能卷")


if __name__ == "__main__":
    rospy.init_node("service_hello_world_server")
    server = rospy.Service("/robotSwitch", SetBool, dealRobotSwitch)
    rospy.loginfo("robotSwitch 服务已启动...")
    rospy.spin()

创建 service_hello_world_client.py 以实现客户端,编辑内容如下:

import sys
import rospy
from std_srvs.srv import SetBool, SetBoolRequest


if __name__ == "__main__":
    rospy.init_node("service_hello_world_client")

    if len(sys.argv) != 2:
        rospy.logerr("参数个数有误")
        sys.exit(1)

    flag = False
    if sys.argv[1] == "on":
        flag = True
    elif sys.argv[1] == "off":
        pass
    else:
        rospy.logwarn("仅支持on和off")
        sys.exit(1)

    rospy.loginfo("客户端请求 [%s] 机器人.", "启动" if flag else "关闭")
    client = rospy.ServiceProxy("/robotSwitch", SetBool)
    client.wait_for_service()
    req = SetBoolRequest()
    req.data = flag
    res = client.call(req)

    if res.success:
        rospy.loginfo("操作成功,%s", res.message)
    else:
        rospy.logerr("操作失败,%s", res.message)

修改 CMakeLists.txt ,只需添加如下内容:

catkin_install_python(PROGRAMS
  scripts/service_hello_world_server.py
  scripts/service_hello_world_client.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编译运行

进入工作空间执行 catkin_make 命令编译工程,编译成功后,使用如下命令依次启动服务端和客户端。

1. 启动ros master(如果已启动,无需再启动)
roscore
2. 启动服务端
rosrun service_hello_world service_hello_world_server.py
3. 启动客户端
rosrun service_hello_world service_hello_world_client.py

结果如下:

在这里插入图片描述

【ROS 笔记4】service 通信(通俗理解) 首先,我们需要定义一个服务,包括请求和响应的消息类型。这通常在一个.srv文件中完成,该文件位于你的ROS包的srv目录下。例如,如果我们要创建一个服务来执行两个整数的加法,我们可能会定义一个名为# 定义一个AddTwoInts.srv 文件# 内容如下int64 aint64 b---int64 sum---之前的部分是请求消息(客户端发送给服务端的),---之后的部分是响应消息(服务端返回给客户端的)。 阅读详情

相关推荐

ROS2教程 05 服务Service

一、ROS2 服务Service 运行示意图 服务Service是一种通信方式,与话题Topic不同,服务是call-and-response模型,不同于话题的publisher-subscriber模型。因此,话题Topic用于订阅数据流并获得持续更新,而服务Service仅在被客户端Client请求调用Request时,从服务端Server取得数据,并将客户端Client的反馈Response传回给服务端Server。 话题Topic用于单向通信,而服务Service用于双向通信,在需要持续发布内容时

m0_56661101的博客 5438

ROS学习笔记(四)—— 服务 Service 详解

1.服务 service

zxxRobot的博客 1万+

[ROS 系列学习教程] ROS服务(Service)通信:从模型解析到实战进阶

本文深入解析ROS服务(Service)通信模型,通过请求-响应机制实现机器人任务调度。从基础Hello World示例到实战进阶,详细讲解C++和Python实现方式,并对比服务通信与话题通信的关键差异,帮助开发者掌握ROS核心通信技术。

cuhongjiao7003的博客 371

ROS 服务Server

ROS 服务

TTTTTTTTTSS的博客 2065

ROS C++入门:从Hello World到话题通信的完整实践指南

机器人操作系统(ROS)作为机器人软件开发的事实标准框架,其核心在于分布式节点间的通信机制。理解ROS的节点、话题、消息等基础概念,是掌握机器人软件架构的关键。从原理上看,ROS采用基于发布/订阅的异步通信模型,节点通过话题进行数据交换,这种松耦合设计提升了系统的模块化和可扩展性。在技术价值层面,ROS为机器人感知、决策与控制提供了统一的中间件平台,大幅降低了开发复杂度。对于应用场景,无论是移动机器人导航、机械臂控制还是自动驾驶系统,ROS都扮演着核心角色。本文以C++实现ROS的Hello World节点

weixin_30906185的博客 484

ROS通信核心:Topic与Service的保姆级实战指南

在机器人开发中,分布式系统通信是连接感知、决策与执行模块的基石。其核心原理在于节点间高效、可靠的数据交换,这直接决定了机器人系统的实时性与稳定性。从技术价值看,理解异步与同步通信模型是构建健壮机器人软件架构的关键,它使得传感器数据流、控制指令与任务请求能够有序协作。在应用场景上,无论是自动驾驶中的激光雷达数据流,还是工业机械臂的抓取指令,都依赖于对底层通信机制的精准运用。本文聚焦ROS(机器人操作系统)中最基础的Topic(话题)和Service(服务)机制,通过从环境搭建、代码编写到问题排查的完整实操,并

weixin_33937913的博客 384

【ROS2】服务 Service Hello World 代码示例讲解(Python版)

ROS2建议创建一个 License 文件以说明该功能包的发布许可。hello_world_service_py:自定义功能包名称。编译成功后,会在ros2_learning目录下生成。欢迎大家加QQ群,一起讨论学习:894013891。命令,可以根据包名和节点名,在任何目录执行。ROS2的python版本使用。配置包的构建、分发和安装。

淋曦的进击手记 715

ROS Talker/Listener原理深度解析:从Hello World到工业级通信

ROS话题(Topic)是机器人分布式系统中最基础的异步通信机制,其发布-订阅模型基于节点(Node)进程隔离、主节点(Master)动态注册与P2P数据传输三大原理,兼顾松耦合性与实时性。该机制避免了RPC调用的阻塞风险,支撑激光雷达、IMU、视觉等多源高频数据流并行处理,在AGV调度、无人机编队、手术机器人等场景中成为低延迟通信的事实标准。本文以经典talker/listener为例,深入剖析节点生命周期、回调队列调度、消息缓冲策略及底层TCP直连机制,揭示ROS如何在Linux进程模型之上构建确定性通

weixin_30876945的博客 400

【ROS2】服务 Service Hello World 代码示例讲解(C++版)

其中,使用指定编译系统为hello_world_service_cpp:自定义功能包名称结果如下图:其中,有ROS2建议创建一个 License 文件以说明该功能包的发布许可。可以使用└── src。

淋曦的进击手记 1480

ROS发布器与订阅器原理深度解析:从Hello World到分布式通信

ROS发布器(Publisher)和订阅器(Subscriber)是机器人操作系统最基础的通信机制,其本质是基于话题(Topic)的异步、解耦、类型安全的消息传递模型。它依托ROS Master实现节点发现与连接协商,通过消息序列化、缓冲区管理(queue_size)和时间同步(rospy.get_time)保障实时性与可靠性。该机制支撑着传感器数据流、控制指令分发、多机协同等核心场景,是构建tf坐标变换、sensor_msgs图像处理、多节点调试等高阶能力的前提。掌握ros python客户端库(rosp

aome1470的博客 477

ROS入门实战:从零搭建环境到掌握Topic与Service通信

在机器人软件开发中,进程间通信是构建复杂系统的基石。其核心原理在于实现不同功能模块间的数据交换与协同,从而将传感器、控制器、规划器等独立组件整合为有机整体。ROS作为机器人操作系统,其技术价值正是通过提供标准化的通信中间件,解决了分布式系统中节点发现、消息序列化与网络传输等底层难题,大幅提升了开发效率。在自动驾驶、无人机、机械臂等具身智能应用场景中,ROS的Topic和Service两种核心通信机制分别对应了持续数据流和请求-响应式交互,是实现传感器数据处理、运动控制等关键功能的基础。本文聚焦ROS环境搭建

weixin_34245749的博客 339

【ROS 基础教学系列】ROS服务(Service)通信

本文介绍了ROS中的服务通讯模型,包括服务端与客户端交互过程,并通过C++和Python实现了一个简单的ServiceHelloWorld示例,展示了如何创建、初始化功能包,定义服务名称、消息格式,以及服务端与客户端的实现。服务通讯是基于请求响应模式的,是一种应答机制。用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。

yechen1的博客 2032

ROS 2 C++发布-订阅系统深度解析:从Hello World到生产级通信

发布-订阅(Pub/Sub)是机器人中间件最基础的异步通信范式,其核心在于解耦节点、支持松散耦合与分布式扩展。在ROS 2中,该机制依托rclcpp客户端库实现,通过QoS策略(如Reliability、Durability)控制消息传递语义,并由底层RMW(如CycloneDDS)提供跨进程通信能力。技术价值体现在工程可控性——队列长度(queue_size)、回调模型(Lambda+Node继承)、生命周期管理共同决定了系统的实时性、可靠性和可调试性。典型应用场景包括传感器数据广播、状态同步、模块间事件

weixin_30279315的博客 326

ROS机器人开发入门:从零掌握Topic与Service核心通信机制

在机器人软件开发领域,分布式系统通信是构建复杂功能的基础。其核心原理在于实现不同进程(节点)间的数据交换与协同,这直接决定了系统的实时性、可靠性和扩展性。从技术价值看,高效的通信机制能够支撑传感器数据处理、运动控制、任务调度等关键机器人功能。在应用场景上,无论是自动驾驶的感知融合、工业机械臂的精准操控,还是服务机器人的自主导航,都依赖于稳定可靠的底层通信框架。ROS(Robot Operating System)作为机器人领域的“元操作系统”,其提供的Topic(发布/订阅)和Service(请求/响应)两

weixin_33826609的博客 447

Simulink Test Sequence自动化测试实战:从Excel到模型验证

本文详细介绍了如何利用Excel和MATLAB脚本实现Simulink Test Sequence的自动化测试。通过设计结构化Excel模板管理测试数据,并调用Simulink Test API批量创建与执行测试用例,该方法能显著提升复杂模型单元测试的效率,便于集成到CI/CD流程,实现从数据到验证的全自动化。

weixin_29261711的博客 137

机器视觉中的特征提取【0】:什么是特征提取,特征提取有什么作用?

1.什么是特征提取? 特征提取的英文叫做feature extractor,它是将一些原始的输入的数据维度减少或者将原始的特征进行重新组合以便于后续的使用。简单来说有两个作用:减少数据维度,整理已有的数据特征。 这里我给一个例子来解释特征提取的第二个作用。那就是我们很熟悉的奖学金评定,我们知道学生的文化课成绩,道德分数,以及各种其他竞赛的成绩。摆在奖学金评定委员会面前的任务就是如何知道一个学生是否...

月下花弄影 5万+

ROS机器人开发入门:Topic与Service通信机制详解与实战

在机器人系统开发中,节点间通信是构建复杂、模块化软件架构的核心基础。其原理在于通过标准化的消息传递机制,实现传感器、控制器与算法模块之间的解耦与数据交换,从而提升系统的可维护性和扩展性。这一技术价值在机器人操作系统(ROS)中得到了充分体现,它通过发布/订阅(Topic)和请求/响应(Service)两种核心模式,为流式数据(如激光雷达点云)和离散命令(如路径规划请求)提供了高效解决方案。具体到应用场景,无论是移动机器人导航还是机械臂控制,理解并掌握ROS的通信模型都是实现具身智能的第一步。本文将以Ubun

cuiji1279的博客 407

【云原生技术】云计算与网络:容器服务(Tencent Kubernetes Engine,简称TKE)简介

基本概念主要特点应用场景技术实现容器服务(Tencent Kubernetes Engine,简称TKE)是腾讯云提供的一种托管的容器管理服务,基于开源的Kubernetes系统。TKE旨在简化容器的部署、管理和扩展,并且提供高性能、可靠性和安全性,适用于各种规模的企业和应用。

weixin_46453070的博客 2027

Ros 通信方式教程 hello word 基本编程

1.Ros通信编程之话题编程 Talker:发布者 C++创建 Listener:订阅者 C++创建 Ros master:管理节点的仓库 --- 添加编译预选 ------ 运行可执行程序 2.实现一个talker 发布者 创建talker 进入ros工作空间下的 src 文件夹 创建 talker.cpp文件 这里就不详细描述创建过程 (*)创建ros工作空间 mkdir-p ~/catkin_ws/src cd~/catkin_ws/src carkin_int

weixin_44025389的博客 1871

温升试验详解

温升试验是电子设备可靠性验证的重要手段,用于评估器件在正常工作或极限条件下的热稳定性。试验通过测量器件表面或关键点温度与环境温度的差值(ΔT),判断是否存在过热风险。主要依据行业标准,采用热电偶、热像仪等设备测量稳态温度。热阻分析可建立温升与功耗的关系,指导器件选型、PCB热设计和散热结构优化。典型应用包括电源模块和LED电路测试。温升试验通过系统性热分析,能有效提升产品在高负载、高温环境下的可靠性和安全性,对现代小型化、高密度电子设备尤为重要。

硬核科技 1315

【ROS2】机械臂MoveIt2

navigation2 专注于移动机器人的导航与定位,处理大范围空间的底盘运动;MoveIt2 专注于机械臂的操作规划,处理多关节协同的末端执行器运动。在复合机器人(如,移动机械臂)navigation2 负责移动到目标区域,MoveIt2 负责机械臂抓取。官网:https://moveit.picknik.ai/main/index.html。

郭老二 572

从‘Hello World’到节点通信:一个C++文件带你打通ROS1核心概念(附VSCode配置)

本文通过一个C++文件从基础到进阶,完整演示了ROS1核心概念的应用,包括节点创建、话题通信、服务实现和参数服务器配置。特别提供了VSCode开发环境的高效配置指南,帮助开发者快速掌握ROS1节点通信的关键技术,提升机器人软件开发效率。

weixin_30887919的博客 422

【论文复现】——基于LM优化的NDT点云配准算法

传统的正态分布变换配准算法处理初始位姿变换相差较大的两帧点云时,存在无法收敛或者陷入局部最小值的问题。为了提高算法的收敛性能,提出了一种基于LM方法改进的三维正态分布变换配准算法。

点云侠的博客 1161

ROS 1 Service 与 Topic 深度对比:5 个维度解析同步/异步通信的适用场景

本文深入对比ROS 1中Service与Topic的通信机制,从原理、性能、可靠性、编程模型和应用场景5个维度解析同步/异步通信的差异。通过典型代码示例和性能数据,帮助开发者在机器人系统中合理选择通信方式,提升系统效率。特别针对Hello World等基础场景,提供实用的选型建议和优化技巧。

B1334628598的博客 560

【YOLOv11】070、YOLOv11异常检测:正常数据训练下的异常目标识别

本文探讨了如何利用YOLOv11模型进行工业异常检测,重点介绍了仅使用正常样本训练模型的创新方法。文章首先通过产线误报案例引出异常检测的核心问题,阐述了YOLOv11作为特征提取器的改造思路,包括正常数据训练、特征空间构建和异常度量等关键步骤。实战部分提供了精简代码示例,强调数据增强控制、特征层选择和自编码器应用等技巧。作者还总结了容易出错的细节,如特征选择、阈值设定和计算效率优化等,并给出实用建议:从简单方法入手、重视可视化、确保数据质量、部署时设置复核机制等。最后指出该方法虽能减少对异常样本的依赖,但需

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 221

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: [ROS 系列学习教程] ROS话题(Topic)通信:自定义msg - 例程与讲解
下一篇: [LeetCode] 7.整数反转
万俟淋曦
万俟淋曦 优质创作者: 人工智能技术领域 优质创作者: 人工智能技术领域 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄12年 10万+粉丝 357原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

万俟淋曦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值