ROS笔记本

创建工作空间

下面我们开始创建一个catkin 工作空间:

$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src

即使这个工作空间是空的(在’src’目录中没有任何软件包,只有一个CMakeLists.txt链接文件),你依然可以编译它:

$ cd ~/catkin_ws/ (在工作空间目录进行编译)
$ catkin_make

catkin_make命令在catkin 工作空间中是一个非常方便的工具。如果你查看一下当前目录应该能看到’build’和’devel’这两个文件夹。在’devel’文件夹里面你可以看到几个setup.*sh文件。source这些文件中的任何一个都可以将当前工作空间设置在ROS工作环境的最顶层

接下来首先source一下新生成的setup.*sh文件:
$ source devel/setup.bash (打开新的终端后使用roscd命令发现找不到你自己创建的package,这是因为你的source devel/setup.bash并没有写进~/.bashrc中,新的终端下需要重新source)

要想保证工作空间已配置正确需确保ROS_PACKAGE_PATH环境变量包含你的工作空间目录,采用以下命令查看
echo $ROS_PACKAGE_PATH
/home//catkin_ws/src:/opt/ros/indigo/share:/opt/ros/indigo/stacks

ROS文件系统介绍

快速了解文件系统概念

Packages: 软件包,是ROS应用程序代码的组织单元,每个软件包都可以包含程序库、可执行文件、脚本或者其它手动创建的东西。

Manifest (package.xml): 清单,是对于'软件包'相关信息的描述,用于定义软件包相关元信息之间的依赖关系,这些信息包括版本、维护者和许可协议等。 

rospack允许你获取软件包的有关信息
rospack find [包名称] 可以返回软件包的路径信息。

roscd是rosbash命令集中的一部分,它允许你直接切换(cd)工作目录到某个软件包或者软件包集当中。
roscd [本地包名称[/子目录]]
像ROS中的其它工具一样,roscd只能切换到那些路径已经包含在ROS_PACKAGE_PATH环境变量中的软件包,要查看ROS_PACKAGE_PATH中包含的路径可以输入:
$ echo $ROS_PACKAGE_PATH

rosls是rosbash命令集中的一部分,它允许你直接按软件包的名称而不是绝对路径执行ls命令(罗列目录)。
用法:
rosls [本地包名称[/子目录]]

创建ROS程序包

一个catkin程序包由什么组成?

一个程序包要想称为catkin程序包必须符合以下要求:

1 该程序包必须包含catkin compliant package.xml文件
这个package.xml文件提供有关程序包的元信息。

2 程序包必须包含一个catkin 版本的CMakeLists.txt文件,而Catkin metapackages中必须包含一个对CMakeList.txt文件的引用。

3*每个目录下只能有一个程序包。
这意味着在同一个目录下不能有嵌套的或者多个程序包存在。*

最简单的程序包也许看起来就像这样:

   my_package/
    	CMakeLists.txt
        package.xml
    		 
开发catkin程序包的一个推荐方法是使用catkin工作空间,但是你也可以单独开发(standalone)catkin 软件包。一个简单的工作空间也许看起来像这样:

    workspace_folder/        -- WORKSPACE
      src/                   -- SOURCE SPACE
        CMakeLists.txt       -- 'Toplevel' CMake file, provided by catkin
        package_1/
          CMakeLists.txt     -- CMakeLists.txt file for package_1
          package.xml        -- Package manifest for package_1
        ...
        package_n/
          CMakeLists.txt     -- CMakeLists.txt file for package_n
          package.xml        -- Package manifest for package_n

创建一个catkin程序包

首先切换到之前通过创建catkin工作空间教程创建的catkin工作空间中的src目录下:

$ cd ~/catkin_ws/src

现在使用catkin_create_pkg命令来创建一个名为’beginner_tutorials’的新程序包,这个程序包依赖于std_msgs、roscpp和rospy:

$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp

这将会创建一个名为beginner_tutorials的文件夹,这个文件夹里面包含一个package.xml文件和一个CMakeLists.txt文件,这两个文件都已经自动包含了部分你在执行catkin_create_pkg命令时提供的信息。

catkin_create_pkg命令会要求你输入package_name,如果有需要你还可以在后面添加一些需要依赖的其它程序包:
#catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

程序包依赖关系

一级依赖

之前在使用catkin_create_pkg命令时提供了几个程序包作为依赖包,现在我们可以使用rospack命令工具来查看一级依赖包。
$ rospack depends1 beginner_tutorials

std_msgs
rospy
roscpp

一个程序包还可以有好几个间接的依赖包,幸运的是使用rospack可以递归检测出所有的依赖包。

$ rospack depends beginner_tutorials

自定义你的程序包

自定义 package.xml

主要修改依赖关系

自定义 CMakeLists.txt

编译ROS程序包

使用 catkin_make
catkin_make 是一个命令行工具,它简化了catkin的标准工作流程。你可以认为catkin_make是在CMake标准工作流程中依次调用了cmake 和 make。

#在一个CMake项目里
$ mkdir build
$ cd build
$ cmake …
$ make
$ make install # (可选)
每个CMake工程在编译时都会执行这个操作过程。相反,多个catkin项目可以放在工作空间中一起编译,工作流程如下:
#In a catkin workspace
$ catkin_make
$ catkin_make install # (可选)

如果你的源代码不在默认工作空间中(~/catkin_ws/src),比如说存放在了my_src中,那么你可以这样来使用catkin_make:
In a catkin workspace
$ catkin_make --source my_src

build 目录是build space的默认所在位置,同时cmake 和 make也是在这里被调用来配置并编译你的程序包。devel (development)目录是devel space的默认所在位置, 同时也是在你安装程序包之前存放可执行文件和库文件的地方。

理解 ROS节点

节点

一个节点其实只不过是ROS程序包中的一个可执行文件。ROS节点可以使用ROS客户库与其他节点通信。节点可以发布或接收一个话题。节点也可以提供或使用某种服务。
(以遥控器控制小乌龟为例:我们编写一个节点,这个节点有订阅器和发布器,订阅器订阅遥控器的话题,接受遥控器的数据,然后经过处理发布到另一个小乌龟也订阅的话题上,这样小乌龟就可以遥控啦。暂时的理解,之后更新)

(节点是ros中非常重要的一个概念,为了帮助初学者理解这个概念,这里举一个通俗的例子:
例如,咱们有一个机器人,和一个遥控器,那么这个机器人和遥控器开始工作后,就是两个节点。遥控器起到了下达指 令的作用;机器人负责监听遥控器下达的指令,完成相应动作。从这里我们可以看出,节点是一个能执行特定工作任 务的工作单元,并且能够相互通信,从而实现一个机器人系统整体的功能。在这里我们把遥控器和机器人简单定义为两个节点,实际上在机器人中根据控制器、传感器、执行机构等不同组成模块,还可以将其进一步细分为更多的节点,这个是根据用户编写的程序来定义的。)

客户端库

ROS客户端库允许使用不同编程语言编写的节点之间互相通信:

rospy = python 客户端库
roscpp = c++ 客户端库 

roscore

roscore 是你在运行所有ROS程序前首先要运行的命令。

如果 roscore 运行后无法正常初始化,很有可能是存在网络配置问题。参见[网络设置——单机设置](http://www.ros.org/wiki/ROS/NetworkSetup#Single_machine_configuration)
如果 roscore 不能初始化并提示缺少权限,这可能是因为~/.ros文件夹归属于root用户(只有root用户才能访问),修改该文件夹的用户归属关系:
$ sudo chown -R <your_username> ~/.ros

使用rosnode

当打开一个新的终端时,你的运行环境会复位,同时你的~/.bashrc文件会复原。如果你在运行类似于rosnode的指令时出现一些问题,也许你需要添加
一些环境设置文件到你的~/.bashrc或者手动重新配置他们。 (source 一下)

rosnode list 指令列出活跃的节点:
$ rosnode list
你会看到:
/rosout
这表示当前只有一个节点在运行: rosout。因为这个节点用于收集和记录节点调试输出信息,所以它总是在运行的。

rosnode info 命令返回的是关于一个特定节点的信息。

使用 rosrun

rosrun 允许你使用包名直接运行一个包内的节点(而不需要知道这个包的路径)。

用法:
$ rosrun [package_name] [node_name]
rosrun turtlesim turtlesim_node(怎么知道一个包有哪些节点,使用TAB键)
ROS的一个强大特性就是你可以通过命令行重新配置名称。
使用Remapping Argument改变节点名称:
$ rosrun turtlesim turtlesim_node __name:=my_turtle

清除rosnode列表,通过: $ rosnode cleanup (应该是清除已经不再运行的节点)

另外一个 rosnode 指令, ping, 来测试:
$ rosnode ping my_turtle

理解ROS话题

ROS Topics

turtlesim_node节点和turtle_teleop_key节点之间是通过一个ROS话题来互相通信的。turtle_teleop_key在一个话题上发布按键输入消息,而turtlesim则订阅该话题以接收该消息。

使用 rqt_graph

rqt_graph能够创建一个显示当前系统运行情况的动态图形。rqt_graph是rqt程序包中的一部分。如果你没有安装,请通过以下命令来安装:

$ sudo apt-get install ros-<distro>-rqt
$ sudo apt-get install ros-<distro>-rqt-common-plugins

请使用你的ROS版本名称(比如fuerte、groovy、hydro等)来替换掉。

在一个新终端中运行:
$ rosrun rqt_graph rqt_graph

在这里插入图片描述

rostopic

rostopic is a command-line tool for printing information about ROS Topics.

Commands:
rostopic bw display bandwidth used by topic
rostopic delay display delay of topic from timestamp in header
rostopic echo print messages to screen
rostopic find find topics by type
rostopic hz display publishing rate of topic
rostopic info print information about active topic
rostopic list list active topics
rostopic pub publish data to topic
rostopic type print topic or field type

ROS Messages

话题之间的通信是通过在节点之间发送ROS消息实现的。对于发布器(turtle_teleop_key)和订阅器(turtulesim_node)之间的通信,发布器和订阅器之间必须发送和接收相同类型的消息。
这意味着话题的类型是由发布在它上面的消息类型决定的。使用rostopic type命令可以查看发布在某个话题上的消息类型。

使用 rostopic type

rostopic type 命令用来查看所发布话题的消息类型。

rostopic type /turtle1/cmd_vel
你应该会看到:
geometry_msgs/Twist

我们可以使用rosmsg命令来查看消息的详细情况
rosmsg show geometry_msgs/Twist

geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z
使用 rostopic pub

rostopic pub可以把数据发布到当前某个正在广播的话题上。
用法:

rostopic pub [topic] [msg_type] [args]
$ rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'

这是一个非常复杂的例子,因此让我们来详细分析一下其中的每一个参数。

rostopic pub

    这条命令将会发布消息到某个给定的话题。 

 -1

    (单个破折号)这个参数选项使rostopic发布一条消息后马上退出。 

/turtle1/command_velocity

    这是消息所发布到的话题名称。 

turtlesim/Velocity

    这是所发布消息的类型。 

--

(双破折号)这会告诉命令选项解析器接下来的参数部分都不是命令选项。这在参数里面包含有破折号-(比如负号)时是必须要添加的。

2.0 1.8

正如之前提到的,在一个turtlesim/Velocity消息里面包含有两个浮点型元素:linear和angular。在本例中,2.0是linear的值,1.8是angular的值。这些参数其实是按照YAML语法格式编写的,这在YAML文档中有更多的描述。 

在这里插入图片描述

使用 rqt_plot

rqt_plot命令可以实时显示一个发布到某个话题上的数据变化图形。这里我们将使用rqt_plot命令来绘制正在发布到/turtle1/pose话题上的数据变化图形。首先,在一个新终端中运行rqt_plot命令:

$ rosrun rqt_plot rqt_plot

这会弹出一个新窗口,在窗口左上角的一个文本框里面你可以添加需要绘制的话题。在里面输入/turtle1/pose/x后之前处于禁用状态的加号按钮将会被使能变亮。按一下该按钮,并对/turtle1/pose/y重复相同的过程。现在你会在图形中看到turtle的x-y位置坐标图。
在这里插入图片描述

理解ROS服务和参数

ROS Services

服务(services)是节点之间通讯的另一种方式。服务允许节点发送请求(request) 并获得一个响应(response)

使用rosservice

rosservice可以很轻松的使用 ROS 客户端/服务器框架提供的服务。rosservice提供了很多可以在topic上使用的命令,如下所示:

使用方法:

rosservice list         输出可用服务的信息
rosservice call         调用带参数的服务
rosservice type         输出服务类型
rosservice find         依据类型寻找服务find services by service type
rosservice uri          输出服务的ROSRPC uri

rosservice list

$ rosservice list

list 命令显示turtlesim节点提供了9个服务:重置(reset), 清除(clear), 再生(spawn), 终止(kill), turtle1/set_pen, /turtle1/teleport_absolute, /turtle1/teleport_relative, turtlesim/get_loggers, and turtlesim/set_logger_level. 同时还有另外两个rosout节点提供的服务: /rosout/get_loggers and /rosout/set_logger_level.

/clear
/kill
/reset
/rosout/get_loggers
/rosout/set_logger_level
/spawn
/teleop_turtle/get_loggers
/teleop_turtle/set_logger_level
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/get_loggers
/turtlesim/set_logger_level

我们使用rosservice type命令更进一步查看clear服务:

rosservice type

使用方法:

rosservice type [service]

我们来看看clear服务的类型:

$ rosservice type clear

std_srvs/Empty

服务的类型为空(empty),这表明在调用这个服务是不需要参数(比如,请求不需要发送数据,响应也没有数据)。下面我们使用rosservice call命令调用服务:

rosservice call

使用方法:

rosservice call [service] [args]

因为服务类型是空,所以进行无参数调用:

$ rosservice call clear

正如我们所期待的,服务清除了turtlesim_node的背景上的轨迹。
通过查看再生(spawn)服务的信息,我们来了解带参数的服务:

$ rosservice type spawn| rossrv show

float32 x
float32 y
float32 theta
string name
---
string name

这个服务使得我们可以在给定的位置和角度生成一只新的乌龟。名字参数是可选的,这里我们不设具体的名字,让turtlesim自动创建一个。

$ rosservice call spawn 2 2 0.2 ""

服务返回了新产生的乌龟的名字:

name: turtle2

Using rosparam

rosparam使得我们能够存储并操作ROS 参数服务器(Parameter Server)上的数据。参数服务器能够存储整型、浮点、布尔、字符串、字典和列表等数据类型。rosparam使用YAML标记语言的语法。一般而言,YAML的表述很自然:1 是整型, 1.0 是浮点型, one是字符串, true是布尔, [1, 2, 3]是整型列表, {a: b, c: d}是字典. rosparam有很多指令可以用来操作参数,如下所示:

使用方法:

rosparam set 设置参数
rosparam get 获取参数
rosparam load 从文件读取参数
rosparam dump 向文件中写入参数
rosparam delete 删除参数
rosparam list 列出参数名

我们来看看现在参数服务器上都有哪些参数:

rosparam list

$ rosparam list

我们可以看到turtlesim节点在参数服务器上有3个参数用于设定背景颜色:

/background_b
/background_g
/background_r
/roslaunch/uris/aqy:51932
/run_id

rosparam set and rosparam get

rosparam set [param_name]
rosparam get [param_name]

现在我们修改背景颜色的红色通道:

$ rosparam set background_r 150

上述指令修改了参数的值,现在我们调用清除服务使得修改后的参数生效
$ rosservice call clear
现在我们来查看参数服务器上的参数值——获取背景的绿色通道的值:

$ rosparam get background_g

86

我们可以使用rosparam get /来显示参数服务器上的所有内容:

$ rosparam get /

background_b: 255
background_g: 86
background_r: 150
roslaunch:
  uris: {'aqy:51932': 'http://aqy:51932/'}
run_id: e07ea71e-98df-11de-8875-001b21201aa8

你可能希望存储这些信息以备今后重新读取。这通过rosparam很容易就可以实现:

rosparam dump and rosparam load

使用方法:

rosparam dump [file_name]
rosparam load [file_name] [namespace]

现在我们将所有的参数写入params.yaml文件:

$ rosparam dump params.yaml

你甚至可以将yaml文件重载入新的命名空间,比如说copy空间:

$ rosparam load params.yaml copy
$ rosparam get copy/background_b

255

使用 rqt_console 和 roslaunch

rqt_console属于ROS日志框架(logging framework)的一部分,用来显示节点的输出信息。rqt_logger_level允许我们修改节点运行时输出信息的日志等级(logger levels)(包括 DEBUG、WARN、INFO和ERROR)。

现在让我们来看一下turtlesim在rqt_console中的输出信息,同时在rqt_logger_level中修改日志等级。在启动turtlesim之前先在另外两个新终端中运行rqt_console和rqt_logger_level:

$ rosrun rqt_console rqt_console
在这里插入图片描述
$ rosrun rqt_logger_level rqt_logger_level在这里插入图片描述

日志等级说明

日志等级按以下优先顺序排列:
Fatal
Error
Warn
Info
Debug
Fatal是最高优先级,Debug是最低优先级。通过设置日志等级你可以获取该等级及其以上优先等级的所有日志消息。比如,将日志等级设为Warn时,你会得到Warn、Error和Fatal这三个等级的所有日志消息。

使用roslaunch

roslaunch可以用来启动定义在launch文件中的多个节点。

用法:
$ roslaunch [package] [filename.launch]
先切换到beginner_tutorials程序包目录下:
$ roscd beginner_tutorials

然后创建一个launch文件夹:
$ mkdir launch
$ cd launch

现在我们来创建一个名为turtlemimic.launch的launch文件并复制粘贴以下内容到该文件里面:

<launch>

  <group ns="turtlesim1">
    <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
  </group>

  <group ns="turtlesim2">
    <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
  </group>

  <node pkg="turtlesim" name="mimic" type="mimic">
    <remap from="input" to="turtlesim1/turtle1"/>
    <remap from="output" to="turtlesim2/turtle1"/>
  </node>

</launch>

使用rosed编辑ROS中的文件

rosed 是 rosbash 的一部分。利用它可以直接通过package名来获取到待编辑的文件而无需指定该文件的存储路径了。

使用方法:

$ rosed [package_name] [filename]

创建ROS消息和ROS服务

消息(msg)和服务(srv)介绍

消息(msg): msg文件就是一个描述ROS中所使用消息类型的简单文本。它们会被用来生成不同语言的源代码。

服务(srv): 一个srv文件描述一项服务。它包含两个部分:请求和响应。 

msg文件存放在package的msg目录下,srv文件则存放在srv目录下。

msg文件实际上就是每行声明一个数据类型和变量名。可以使用的数据类型如下:

int8, int16, int32, int64 (plus uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C] 

在ROS中有一个特殊的数据类型:Header,它含有时间戳和坐标系信息。在msg文件的第一行经常可以看到Header header的声明.

下面是一个msg文件的样例,它使用了Header,string,和其他另外两个消息类型。

Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist

srv文件分为请求和响应两部分,由’—'分隔。下面是srv的一个样例:

int64 A
int64 B


int64 Sum

其中 A 和 B 是请求, 而Sum 是响应。

创建一个 msg

1、定义msg
例:

$ cd ~/catkin_ws/src/beginner_tutorials
$ mkdir msg
$ echo "int64 num" > msg/Num.msg`

2、查看package.xml, 确保它包含一下两条语句:(确保msg文件被转换成为C++,Python和其他语言的源代码

 <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

如果没有,添加进去。 注意,在构建的时候,我们只需要"message_generation"。然而,在运行的时候,我们只需要"message_runtime"。

3、在 CMakeLists.txt文件中,
3-1 利用find_packag函数,增加对message_generation的依赖,这样就可以生成消息了。

find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)

3-2 设置运行依赖:

catkin_package(
  ...
  CATKIN_DEPENDS message_runtime ...
  ...)

3-3 找到如下代码块:

#add_message_files(
#FILES
#Message1.msg
#Message2.msg
#)

去掉注释符号#,用你的.msg文件替代Message*.msg,就像下边这样:

add_message_files(
  FILES
  Num.msg
)

3-4 确保CMake知道在什么时候重新配置我们的project:

generate_messages()

现在,可以生成自己的消息源代码了。

使用 rosmsg

通过rosmsg show命令,检查ROS是否能够识消息。

使用方法:

$ rosmsg show [message type]

样例:

$ rosmsg show beginner_tutorials/Num

你将会看到:

int64 num

在上边的样例中,消息类型包含两部分:

beginner_tutorials -- 消息所在的package

Num -- 消息名Num. 

如果你忘记了消息所在的package,你也可以省略掉package名。输入:

$ rosmsg show Num

你将会看到:

[beginner_tutorials/Num]:
int64 num

创建一个srv

1、定义srv
在刚刚那个package中创建一个服务:

$ roscd beginner_tutorials
$ mkdir srv

这次我们不再手动创建服务,而是从其他的package中复制一个服务。 roscp是一个很实用的命令行工具,它实现了将文件从一个package复制到另外一个package的功能。

使用方法:

$ roscp [package_name] [file_to_copy_path] [copy_path]

现在我们可以从rospy_tutorials package中复制一个服务文件了:

$ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
2、确保srv文件被转换成C++,Python和其他语言的源代码。
在CMakeLists.txt文件中增加了对message_generation的依赖

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation)

(对的, message_generation 对msg和srv都起作用)

3、删掉#,去除对下边语句的注释:

# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

用你自己的srv文件名替换掉那些Service*.srv文件:

add_service_files(
  FILES
  AddTwoInts.srv
)

现在,可以生成自己的服务源代码了。

使用 rossrv

通过rosmsg show命令,检查ROS是否能够识该服务。

使用方法:

$ rossrv show <service type>

例子:

$ rossrv show beginner_tutorials/AddTwoInts

你将会看到:

int64 a
int64 b
---
int64 sum

msg和srv都需要的步骤

1、在CMakeLists.txt中找到如下部分:

# generate_messages(
#   DEPENDENCIES
# #  std_msgs  # Or other packages containing msgs
# )

去掉注释并附加上所有你消息文件所依赖的那些含有.msg文件的package(这个例子是依赖std_msgs,不要添加roscpp,rospy),结果如下:

generate_messages(
  DEPENDENCIES
  std_msgs
)

2、重新编译我们的package:

# In your catkin workspace
$ cd ../..
$ catkin_make
$ cd -

所有在msg路径下的.msg文件都将转换为ROS所支持语言的源代码。
生成的C++头文件将会放置在~/catkin_ws/devel/include/beginner_tutorials/。
Python脚本语言会在~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg目录下创建。
lisp文件会出现在~/catkin_ws/devel/share/common-lisp/ros/beginner_tutorials/msg/ 路径下.

获得帮助

我们已经接触到不少的ROS工具了。有时候很难记住他们所需要的参数。还好大多数ROS工具都提供了帮助。

输入:

$ rosmsg -h

你可以看到一系列的rosmsg子命令.

Commands:
  rosmsg show Show message description
  rosmsg users  Find files that use message
  rosmsg md5  Display message md5sum
  rosmsg package  List messages in a package
  rosmsg packages List packages that contain messages

同样你也可以获得子命令的帮助:

$ rosmsg show -h

这会显示rosmsg show 所需的参数:

Usage: rosmsg show [options] <message type>

Options:
  -h, --help  show this help message and exit
  -r, --raw   show raw message text, including comments
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值