ROS--基于机器人操作系统设计与实现

摘 要:近年来,机器人领域取得了举世瞩目的进展。性价比较高的机器人平台,包括地面移动机器人、旋翼无人机和类人机器人等,得到了广泛应用。更令人感到振奋的是,越来越多的高级智能算法让机器人的自主等级逐步提高。尽管如此,对于机器人软件开发人员来说,仍然存在着诸多挑战[1]。主要介绍一个软件平台,即机器人操作系统(Robot Operating System, 或简称 ROS)[2],它可以帮助提高机器人软件的开发效率。
关键词:ROS;机器人;工具;

1 选题背景

随着技术的发展及人们需求的提高,机器人集成了越来越多的功能、传感器,对用户来说这越来越方便,但对开发者来说恰恰相反,功能的增加带来开发与集成难度迅速上升,机器人操作系统的出现有效缓解了这种问题。从计算机和智能手机的发展过程来看,合适与成熟的操作系统是智能机器人行业大规模发展和在人们的生活中普及的必要条件。可以预见,未来几年将会出现众多机器人操作系统,在经过充分的发展竞争后将会有为数不多的几个操作系统会发展壮大并占据绝大部分市场,就像曾经的计算机操作系统和现在的手机操作系统。

2 选题价值

2.1 理论价值

ROS(Robot Operating System,下文简称“ROS”)是一个适用于机器人的开源的元操作系统。它提供了操作系统应有的服务,包括硬件抽象,底层设备控制,常用函数的实现,进程间消息传递,以及包管理。它也提供用于获取、编译、编写、和跨计算机运行代码所需的工具和库函数[2]。
ROS是用于编写机器人软件程序的一种具有高度灵活性的软件架构。它包含了大量工具软件、库代码和约定协议,旨在简化跨机器人平台创建复杂、鲁棒的机器人行为这一过程的难度与复杂度。
ROS设计者将ROS表述为“ROS = Plumbing + Tools + Capabilities + Ecosystem”,即ROS是通讯机制、工具软件包、机器人高层技能以及机器人生态系统的集合体 [3]。
ROS 的主要目标是为机器人研究和开发提供代码复用的支持。ROS是一个分布式的进程(也就是“节点”)框架,这些进程被封装在易于被分享和发布的程序包和功能包中。ROS也支持一种类似于代码储存库的联合系统,这个系统也可以实现工程的协作及发布。这个设计可以使一个工程的开发和实现从文件系统到用户接口完全独立决策(不受ROS限制)。同时,所有的工程都可以被ROS的基础工具整合在一起。

2.2 实践价值

ROS提供一些标准操作系统服务,例如硬件抽象,底层设备控制,常用功能实现,进程间消息以及数据包管理。ROS是基于一种图状架构,从而不同节点的进程能接受,发布,聚合各种信息(例如传感,控制,状态,规划等等)[4]。
ROS可以分成两层,低层是上面描述的操作系统层,高层则是广大用户群贡献的实现不同功能的各种软件包,例如定位绘图,行动规划,感知,模拟等等[4]。

3 文献综述

3.1 ROS的历史

ROS是一个由来已久、贡献者众多的大型软件项目。在ROS诞生之前,很多学者认为,机器人研究需要一个开放式的协作框架,并且已经有不少类似的项目致力于实现这样的框架。在这些工作中,斯坦福大学在2000年年中开展了一系列相关研究项目,如斯坦福人工智能机器人(STandford AI Robot, STAIR)项目、个人机器人(Personal Robots, PR)项目等,在上述项目中,在研究具有代表性、集成式人工智能系统的过程中,创立了用于室内场景的高灵活性、动态软件系统,其可以用于机器人学研究。
2007年,柳树车库(Willow Garage)提供了大量资源,用于将斯坦福大学项目中的软件系统进行扩展与完善,同时,在无数研究人员的共同努力下,ROS的核心思想和基本软件包逐渐得到完善[4]。

3.2 ROS的概述

通常这样解释ROS[4]:

  1. 通道:ROS提供了一种发布-订阅式的通信框架用以简单、快速地构建分布式计算系。
  2. 工具:ROS提供了大量的工具组合用以配置、启动、自检、调试、可视化、登录、测试、终止分布式计算系统。
  3. 强大的库:ROS提供了广泛的库文件实现以机动性、操作控制、感知为主的机器人功能。
  4. 生态系统:ROS的支持与发展依托着一个强大的社区。ros.org尤其关注兼容性和支持文档,提供了一套“一站式”的方案使得用户得以搜索并学习来自全球开发者数以千计的ROS程序包。
    ROS的首要目标是提供一套统一的开源程序框架,用以在多样化的现实世界与仿真环境中实现对机器人的控制[4]。
    ROS的主要目标是为机器人研究和开发提供代码复用的支持。ROS是一个分布式的进程(即“节点”)框架,这些进程被封装在易于被分享和发布的程序包和功能包中。ROS也支持一种类似于代码储存库的联合系统,这个系统也可以实现工程的协作及发布。可以使一个工程的开发和实现从文件系统到用户接口完全独立决策(不受ROS限制)。同时,所有的工程都可以被ROS的基础工具整合在一起[5]。
    ROS在某些程度上和其他常见的机器人架构有些相似之处,如:Player、Orocos、CARMEN、Orca和Microsoft Robotics Studio。对于简单的无机械手的移动平台来说,Player是非常不错的选择。ROS则不同,它被设计为适用于有机械臂和运动传感器的移动平台(倾角激光、云台、机械臂传感器)。与Player相比,ROS更有利于分布式计算环境。当然,Player 提供了较多的硬件驱动程序,ROS则在高层架构上提供了更多的算法应用(如集成OpenCV的视觉算法)[5]。

3.3 ROS的主要特点

3.3.1 点对点设计

ROS通过点对点设计以及服务和节点管理器等机制可以分散由于计算机视觉和语音识别等功能带来的实时计算压力,这种设计能适应服务机器人遇到的调战。

3.3.2 不依赖编程语言

ROS支持多种现代高级编程语言,C++、Python和Lisp语言已经在ROS中实现编译,并得到应用,Octave和Java的测试库也已经实现。为了支持多语言编程,ROS采用了一种语言中立的接口定义语言(language-neutral interface definition language,IDL )来实现各模块之间的消息传送。

3.3.3 精简与集成

ROS建立的系统具有模块化的特点,各模块中的代码可以单独编译,而且编译使用的CMake工具使它很容易的就实现精简的理念。ROS基本将复杂的代码封装在库里,只是创建了一些小的应用程序为ROS显示库的功能,这就允许了对简单的代码超越原型进行移植和重新使用。作为一种新加入的有优势,单元测试当代码在库中分散后也变得非常的容易,一个单独的测试程序可以测试库中很多的特点。
ROS不修改用户的主函数,所以代码可以被其他的机器人软件使用。其优点是ROS很容易和其他的机器人软件平台集成。例如,在计算机视觉方面,ROS已经与OpenCV实现集成。在驱动、导航和模拟器方面,ROS已经与Player系统实现集成。在规划算法方面,ROS也已与OpenAVE系统实现集成。

3.3.4 便于测试

为机器人开发软件比其他软件开发更具挑战性,主要是因为调试准备时间长,且调试过程复杂。况且,因为硬件维修、经费有限等因素,不一定随时有机器人可供使用。ROS提供两种策略来解决上述问题。
1.精心设计的ROS系统框架将底层硬件控制模块和顶层数据处理与决策模块分离,从而可以使用模拟器替代底层硬件模块,独立测试顶层部分,提高测试效率。
2.ROS另外提供了一种简单的方法可以在调试过程中记录传感器数据及其他类型的消息数据,并在试验后按时间戳回放。通过这种方式,每次运行机器人可以获得更多的测试机会。例如,可以记录传感器的数据,并通过多次回放测试不同的数据处理算法。

3.3.5 开源

ROS遵从BSD协议,这给了使用者很大的自由,使开发者可以清楚的查看、自由的使用源代码,如果有需要,可以根据不同的系统及硬件环境对源代码进行修改,或者进行二次开发。

3.3.6 强大的库及社区

ROS提供了广泛的库文件实现以机动性、操作控制、感知为主的机器人功能。同时由于其开源特性,ROS的支持与发展依托着一个强大的社区。其官方网站尤其关注兼容性和支持文档,提供了一套“一站式”的方案使得用户得以搜索并学习来自全球开发者数以千计的ROS程序包。

4 拟研究内容

4.1 ROS的基本架构

ROS作为一个分布式框架,从微观的角度讲,分布式体现在节点的布局和配置上,而从宏观的角度来讲,这种分布式可以体现在多机器人、多主机集成的系统当中[9]。ROS社区中针对多机器人系统并没有很多的涉及,相关应用也比较少。在HRMRP机器人的基础上,我们试图去提出一种多机器人实现的框架,如图 1所示:
在这里插入图片描述

图 1 多机器人框架

整个ROS系统分为以下三个层次,如图 2所示:
在这里插入图片描述

图 2 ROS层次

4.1.1 计算图层

主要是指进程之间(节点之间)的通信。ROS创建了一个连接所有进程的网络,通过这个网络节点之间完成交互,获取其他节点发布的信息。围绕计算图级和节点,一些重要的概念也随即产生:节点,节点管理器,参数服务器,消息,服务,主题(或称话题)和消息记录包等
节点:作为ROS系统的核心,节点是用C++或Python(ROS客户端库roscpp、rospy)编写的程序,用来执行任务或进程。
节点管理器:ROS 的一个基本目标是使机器人专家设计的很多称为节点(node)的几乎相对独立的小程序能够同时运行。为此,这些节点必须能够彼此通信。ROS 中实现通信的关键部分就是 ROS 节点管理器。要启动节点管理器,使用如下命令: roscore 。节点管理器应该在使用 ROS 的全部时间内持续运行。一个合理的工作流程是在一个终端启动 roscore,然后打开其他终端运行其他程序。除非你已经完成 ROS 的相关工作,否则一般没有理由终止 roscore 命令。当结束时,可以通过在 roscore 终端键入 Ctrl-C停止节点管理器。
消息:节点之间通过消息进行通信,这些消息包含一个节点发送给其他节点的信息数据,消息类型有ROS标准类型和基于标准消息开发的自定义类型两种。
话题:ROS的通讯机制是点对点通讯,也就是一个个节点之间的通讯,ROS节点之间的主要通讯方式是两种,这个话题通讯就是一种,另外一种是接下来会介绍到的服务通讯,话题通讯和服务通讯是主要的通讯方式,这两种通讯方式的区别如下图所示,大家可以先看下面对这两种通信方式的具体介绍之后再来看表 1进行对比[6]。

表 1 话题与服务的区别

话题服务
同步性异步同步
通信模型发布/订阅服务端/客户端
底层协议ROSTCP/ROSUDPROSTCP/ROSUDP
反馈机制
缓冲区
实时性
结点关系多对多一对多(一个server)
适用场景数据传输逻辑处理

服务:服务通信是单向的,大家可以很容易理解,信息流只能是由发布者流向接收者,而且节点之间的通信是有延迟的,并不同步,针对这两个缺陷,这个小点介绍的服务通信则满足了双向同步的通信,此外,服务通信两端的节点一个叫服务器(talker),一个叫客户端(listener),服务器还是发布数据信息的,客户端则是接收数据信息的,但是这种通信只允许有一个服务器,客户端可以有多个,服务器和客户端之间双向同步通信,采取请求和应答的模式进行通信,如图 3所示。
在这里插入图片描述

图 3 服务通讯

4.1.2 文件系统层

在这里插入图片描述

图 4 文件系统

(1)功能包清单(Package manifest):记录功能包的基本信息,包含作者信息、许可信息、依赖选项、编译标志等[7]。
(2)元功能包(Meta Packages):组织多个用于同一目的功能的功能包[7]。
(3)元功能包清单(Meta Packages):类似于功能包清单不同之处在于元功能包清单中可能会包含运行时需要依赖的功能包或者声明一些引用的标签[7]。
(4)消息类型(Message):消息是ROS节点之间发布/订阅的通信信息,可以使用ROS系统提供的消息类型,也可以使用.msg文件功能包的msg文件夹下自定义需要的消息类型[7]。
(5)服务类型(Service):服务类型定义了ROS服务器/客户端通信模型下的请求与应答数据类型,可以使用ROS系统提供的服务类型,也可以使用.srv文件在功能包的srv文件夹中进行定义[7]。
(6)代码(code):放置功能包节点源代码的文件夹[7]。

4.1.3 开源社区层

主要是指ROS资源的获取和分享。通过独立的网络社区,我们可以共享和获取知识、算法和代码,开源社区的大力支持使得ROS系统得以快速成长[8]。
在这里插入图片描述

图 5 开源社区

ROS社区内的功能包数量、关注度、相关文章均呈指数级上涨,如图 6所示:
在这里插入图片描述

图 6 ROS关注度

4.2 ROS中开发工具

4.2.1 Gazebo

1.构建机器人运动仿真模型

在Gazebo里,提供了 最基础的三个物体,球体,圆柱体,立方体,利用这三个物体以及它们的伸缩变换或者旋转变换,可以设计一个最简单的机器人三维仿真模型[10]。
Gazebo提供了CAD,Blender等各种2D, 3D设计软件的接口,可以导入这些图纸让Gazebo的机器人模型更加真实,这些之后会详细介绍。
Gazebo提供了 机器人的运动仿真,通过Model Editor下的plugin,来添加我们需要验证的算法文件,就可以在Gazebo里对机器人的运动进行仿真[10]。

2.构建现实世界各种场景的仿真模型

Gazebo可以建立一个用来测试机器人的仿真场景,通过添加物体库,放入垃圾箱,雪糕桶,甚至是人偶等物体来模仿现实世界,还可以通过Building Editor,添加2D的房屋设计图,在设计图基础上构建出3D的房屋[10]。

3.构建传感器仿真模型

Gazebo拥有一个很强大的传感器模型库,包括camera,depth camera,laser,imu等机器人常用的传感器,并且已经有模拟库,已经可以直接使用,也可以自己从0创建一个新的传感器,添加它的具体参数,甚至还可以添加传感器噪声模型,让传感器更加真实[10]。

4.为机器人模型添加现实世界的物理性质

Gazebo里有force,physics的选项,可以为机器人添加例如重力,阻力等,Gazebo有一个很接近真实的物理仿真引擎,要记得一般的地面是没有阻力的,和现实世界有区别[10]。

4.2.2 Rviz

Rviz是ROS的三维可视化工具。它的主要目的是以三维方式显示ROS消息,可以将 数据进行可视化表达。例如,可以无需编程就能表达激光测距仪(LRF)传感器中的传感 器到障碍物的距离,RealSense、Kinect或Xtion等三维距离传感器的点云数据(PCD, Point Cloud Data),从相机获取的图像值等[11]。
另外,利用用户指定的多边形(polygon)支持各种表现形式,交互标记 (Interactive Markers)可以表达接收来自用户节点的命令和数据并互交的过程。在 ROS中,机器人以URDF(Unified Robot Description Format,统一机器人描述格 式)描述,它可以表示为三维模型,并且每个模型可以根据自由度进行移动或驱动,因 此可以用于仿真或控制。例如,如图所示,可以显示移动机器人模型,同时可以通过 接收来自LDS(激光距离传感器)的距离值,并用于导航。而且,安装在机器人上的照相 机的图像可以如左下方那样显示。另外,可以从Kinect、LRF 和RealSense等各种传感器获取数据,并以三维图像显示[11]。

4.2.3 rqt

rqt是ROS的软件框架,它以插件的形式实现各种GUI工具。 可以将所有现有的GUI工具作为rqt中的可停靠窗口运行,这些工具仍然可以以传统的独立方法运行,但是rqt使得同时管理屏幕上的所有各种窗口变得更加容易[12]。
rqt是为ROS准备的基于qt框架的GUI开发环境,包含3各子package,rqt、rqt_common_plugins, rqt_robot_plugins[12].

4.3 ROS的应用功能

ROS中有很多功能强大的功能包,例如:gmapping、amcl、move_base等等。

4.3.1 gmapping算法

gmapping功能包集成了Rao-Blackwellized粒子滤波算法,为开发者隐去了复杂的内部实现。下图 7所示的是gmapping功能包的总体框架。

在这里插入图片描述

图 7 gmapping总体框架

gmapping功能包订阅机器人的深度信息、IMU信息和里程计信息,同时完成一些必要参数的配置,即可创建并输出基于概率的二维栅格地图[13]。
gmapping功能包基于openslam社区的开源SLAM算法。

4.3.2 amcl

amcl是移动机器人在2D环境中的概率定位系统。它实现了自适应(或KLD采样)蒙特卡罗定位方法,其使用粒子滤波器来针对已知的地图跟踪机器人的位姿。目前情况下,该节点仅能使用激光扫描数据和激光雷达地图来工作。它可以通过修改代码以扩展到其他传感器数据[14]。
amcl接收基于激光的地图,激光扫描和tf变换消息,并输出位姿估计。在启动时,amcl根据提供的参数初始化其粒子滤波器。注意,如果没有设置初始化参数,则根据默认值初始滤波器状态将是以(0,0,0)为中心的中等大小的粒子云。在rviz中可以通过2D Pose Estimate按钮来初始化位姿[14]。
在ROS自带的文件中,rospack find amcl/examples/amcl_diff.launch支持差分驱动机器人平台,而amcl_omni.launch 支持全向移动机器人平台。
在启动launch文件前,可以配置一系列的参数来满足系统的要求。其中比较重要的几个参数如下:

  1. 参数min_particles和max_particles设定了算法运行所允许的粒子最小和最大数量。粒子数越多,结果越精确,CPU消耗也越高。
  2. 参数laser_model_type用于配置激光雷达的类型。
  3. 参数laser_likehood_max_dist用于设置地图中障碍物膨胀的最大距离,使用此参数前提是laser_type_model选择了likehood_field模式

4.3.3 move_base

move_base包提供了一个动作的实现(参见actionlib包),在地图上给定一个目标,move_base将会规划出路径并使机器人避开障碍物从而到达目标。详见:http://wiki.ros.org/move_base
move_base节点将全局导航和局部导航链接在一起以完成其导航任务。全局导航用于建立到地图上最终目标或一个远距离目标的路径,局部导航用于建立到近距离目标和为了临时躲避障碍物的路径,例如机器人四周一个4x4 m的方形窗口[14]。
move_base节点支持任何遵循nav_core包中指定的nav_core :: BaseGlobalPlanner接口的全局规划以及任何遵循nav_core包中指定的nav_core :: BaseLocalPlanner接口的局部规划。move_base节点还维护两个代价地图,分别为全局代价地图和局部代价地图[14]。

在这里插入图片描述

图 8 move_base框架

在运行move_base节点之前需要四个配置文件,这些文建定义了一系列相关参数,包括越过障碍物的代价、机器人的半径、路劲规划时的需要考虑未来多长的路,机器人的的最大速度等等,四个配置文件分别如下[14]:

  • base_local_palnner_params.yaml
  • costmap_common_params.yaml
  • global_costmap_params.yaml
  • local_costmap_params.yaml
    具体的参数配置信息参见http://wiki.ros.org/base_local_planner、http://wiki.ros.org/costmap_2d
    在仿真过程中发现对导航效果有较大影响的参数如下:
    1.基本局部规划器配置(base_local_palnner_params.yaml)
  • max_vel_x:机器人的最大线速度,单位是 m/s,对于室内机器人来说0.5就已经很快了。
  • max_rotation_vel:机器人的最大旋转速度,单位是 rad/s,不要把这个值设的过高,不然机器人会错过目标方向。
  • yaw_goal_tolerance:至多距离目标方向的偏差,把这个值设的太小可能会导致机器人在目标附近徘徊。
  • xy_goal_tolerance:至多距离目标位置的偏差,把这个值设的太小可能会导致机器人在目标附近徘徊。注意:不要把最大误差设置的比地图分辨率还小,否则机器人会一直在目标附近徘徊而永远到不了目标。
  • pdist_scale:全局路径规划和到达目的之间的权重。
  • gdist_scale:到达目的位置和全局路径规划之间的权重。
    2.代价地图共同的参数配置(costmap_common_params.yaml)
  • inflation_radius:地图上障碍物的膨胀半径,单位为 m,如果机器人不能很好的通过狭窄的地方,则稍微减小这个值,相反,如果机器人不断的撞到东西,则稍微增大这个值[14]。
    5 拟解决关键问题
    (1)ROS底层架构;
    (2)ROS通讯机制;
    (3)ROS应用功能;

5 ROS效果图

5.1 Gazebo

Gazebo仿真界面,可以在仿真环境下进行测试,减少成本的开销。如图 9所示。

在这里插入图片描述

图 9 Gazebo效果图

5.2 Rviz

Rviz界面,Rviz可以进行可视化的一些操作。如图 10所示,进行建图时可视化的效果图,控制小车进行建图。
在这里插入图片描述

图 10 建图效果图

如图 11所示,小车进行自主导航的效果图,通过rviz可以清楚的看到小车路径规划和避障的效果。

在这里插入图片描述

图 11 自主导航效果图

5.3 rqt_graph

如图 12所示,通过看节点图,可以清楚看到各节点之间的通讯。
在这里插入图片描述

图 12 节点图

5.4 tf树

如图 13所示,通过看tf树,可以清楚看到model中link之间的tf转换。

在这里插入图片描述

图 13 TF树

5.5 节点管理器(ROS Master)

如图 14所示,启动节点管理器成功,ROS的版本是Kinetic。

在这里插入图片描述

图 14 启动结点管理器
ROS学习

6 参考文献

[1][美] Jason M. O’Kane 著,肖军浩 译.机器人操作系统(ROS)浅析[M].
[2]http://wiki.ros.org/ROS/Introduction.2021.4.18
[3]https://baike.baidu.com/reference/4710560/661bN7NXk6pYn9Kb3ldm6x80flHxK3zOjJ0HlsUYn6okFJehRgsvrVsTPClnJKyVXLWFCKxhiTsX.2021.4.18
[4]https://baike.baidu.com/item/ros/4710560?

[4]https://baike.baidu.com/item/ros/4710560?fr=aladdin.2021.4.22
[5]https://www.sohu.com/a/235215889_797635.2021.4.23
[6]https://blog.csdn.net/qq_25267657/article/details/84316111.2021.4.23
[7]https://blog.csdn.net/wangguchao/article/details/83661655.2021.4.23
[8]https://blog.csdn.net/jimson_zhu/article/details/81227495.2021.4.23
[9]https://blog.csdn.net/hcx25909/article/details/48596087.2021.4.23
[10]http://blog.csdn.net/kevin_chan04/article/details/78467218.2021.4.23
[11]https://blog.csdn.net/kh815/article/details/88138401.2021.4.23
[12]https://blog.csdn.net/wendox/article/details/52351608.2021.4.23
[13]https://blog.csdn.net/weixin_44827364/article/details/104202921.2021.4.23
[14]https://blog.csdn.net/wangchao7281/article/details/53691351.2021.4.23

  • 4
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ROS系统是一种常用于机器人操作系统的开源平台,它提供了丰富的工具和功能,用于设计、构建和控制各类机器人。水滴机器人是一种球形移动机器人,可以在平面上自由移动,因此需要进行路径规划和轨迹规划来实现自动化控制。 首先,路径规划是指确定机器人从起点到终点的最佳路径。在ROS中,可以使用导航堆栈(navigation stack)来实现路径规划。导航堆栈包括了一系列的节点,如地图构建、定位、路径规划等。其中,路径规划模块可以使用ROS导航包中的全局规划器(global planner)和局部规划器(local planner)。全局规划器主要负责在整个地图上搜索最佳路径,通常使用A*算法或Dijkstra算法等;而局部规划器则负责实时避障和执行轨迹跟踪。 其次,轨迹规划是指根据路径规划结果生成机器人运动的实际轨迹。在ROS中,可以使用MoveIt软件包来实现轨迹规划。MoveIt是一个用于机器人运动规划的高级软件框架,提供了一组功能强大的工具和算法。通过使用MoveIt的运动规划器(motion planner),可以将路径规划结果转化为机器人的运动轨迹,考虑到机器人的运动学约束和物体遮挡等因素。 在设计基于ROS系统的水滴机器人的路径规划与轨迹规划时,首先需要构建环境地图,并利用地图构建节点将环境信息传输到导航堆栈中。然后,利用全局规划器进行路径规划,得到机器人的最佳路径。接着,通过局部规划器生成实际运动轨迹,并考虑机器人动力学和障碍物避障。最后,利用运动规划器将轨迹规划结果转化为机器人的运动控制指令,实现水滴机器人的自动化控制。 综上所述,基于ROS系统的水滴机器人设计路径规划与轨迹规划需要利用导航堆栈进行路径规划,使用MoveIt进行轨迹规划,同时考虑机器人的动力学约束和障碍物避障,以实现机器人的自动化控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BoBo玩ROS

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

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

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

打赏作者

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

抵扣说明:

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

余额充值