古月居《ROS入门21讲》零基础学习笔记

这篇具有很好参考价值的文章主要介绍了古月居《ROS入门21讲》零基础学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

”怕什么真理无穷,进一寸有一寸的欢喜。”

——古月 适

本人大一小白一枚,参加了一个本科生科研项目,目前正在学习一些ROS1相关的一些前置基础知识。
在这里以博客的形式记录一下学习的过程、操作的细节及操作的结果、爬坑方法、听课笔记。
希望能给同样在学习相关知识的小伙伴一些问题解决的参考以及学习之路的陪伴。
我是基本按照导师推荐的B站上古月居的《ROS入门21讲》(ROS1)走的
本博客也是按照学习顺序的笔记

有交大航模队2024寒假集训的同学看到可以给我留个言哈哈

1.课程简介

老师的课程使用的是Ubuntu18.04,ROS melodic
我用的是Ubuntu20.04,ROS noetic
由于都是ROS1,所以原理和操作相同

2.Linux系统介绍及安装

先下载VMware
然后建议上某宝安装想要的ubuntu和ros
(推荐 大熊一加一)
此处含泪删除上千字搞机记录

3.Linux基础操作(操作集)

命令结构

命令 选项 参数
command option parameter
之间都要加空格

常用命令

  • pwd查看终端所在路径(Print Working Directory)
    古月居《ROS入门21讲》零基础学习笔记
  • cd切换路径(注意斜杠前面有空格)(change directory)
    古月居《ROS入门21讲》零基础学习笔记
  • cd . .跳回到上级目录(cd和dot之间也要有空格)古月居《ROS入门21讲》零基础学习笔记
  • mkdir 在当前路径创建目录(make directory)
    古月居《ROS入门21讲》零基础学习笔记
  • ls 列出当前路径下面的所有文件夹
    古月居《ROS入门21讲》零基础学习笔记
  • touch 创建新的文件(该文件相当于text记事本)
    古月居《ROS入门21讲》零基础学习笔记
  • mv 移动文件(move)
    (在该文件所在目录的路径下执行此操作)
    古月居《ROS入门21讲》零基础学习笔记
  • cp 复制粘贴文件 (copy)
    (在该文件所在目录的路径下执行此操作)
    (cp 文件名 目标目录/重命名的新名称)
    古月居《ROS入门21讲》零基础学习笔记
  • rm 删除某个文件(remove)
    古月居《ROS入门21讲》零基础学习笔记
  • rm -r 删除一个文件夹(recursive递归)
    (要删除的文件夹后面加不加斜杠效果好像一样,不知道斜杠的使用逻辑)
    古月居《ROS入门21讲》零基础学习笔记
  • sudo 提升用户权限(super user do超级用户!)
    (这里以获取软件更新权限为例)
    古月居《ROS入门21讲》零基础学习笔记
  • “- - help “查看帮助
    古月居《ROS入门21讲》零基础学习笔记

快捷操作

  • Tab快速补全。
    长长的文件名可以用Tab补全
    若文件名首字母有歧义,按两次,出选项
  • 用方向上下键调出之前的命令
    不想找的时候按Ctrl+C,自己输命令
  • 文件名前面加dot是隐藏文件

4.cpp&python极简基础(操作集)

简单对比

  • cpp适合底层硬件开发、框架开发
  • python适合应用层、算法的开发
  • 不同场景选择不同语言

安装编译器

  • 安装g++编译器
    古月居《ROS入门21讲》零基础学习笔记
  • 安装python编译器(点击方向上键重复前一步操作,g++改成python就行了)
    出了点问题
    古月居《ROS入门21讲》零基础学习笔记
    在CSDN上找到解决方法,解决方式如下
    古月居《ROS入门21讲》零基础学习笔记
    编译器安装完成
    老师简单讲了一下语言逻辑。并将使用一些源码做演示教学。我在他的微信公众号上进入了github文件地址并下载了源码。

古月居《ROS入门21讲》零基础学习笔记
但是没能够像老师一样简单地复制粘贴到虚拟机。
最后呢,关掉虚拟机改了一个虚拟机设置,把硬件CD/DCD里面,使用ISO文件改成了使用物理驱动器自动检测。(不知道这一步有没有作用)
后面还是拖不动下载的文件夹。但是大文件夹里面的linux文件夹可以拖进虚拟机。我试了一下另一个下面有子目录的文件夹,也是可以拖进去的。或许是因为大文件夹太大了?我不知道。拖到桌面,出现奇怪东东,不明白。(后来重启虚拟机之后消失了)
古月居《ROS入门21讲》零基础学习笔记
啊这,我成功吧教学文件夹拖进虚拟机了。方法是什么呢?。。。把文件夹名字改短。。。
古月居《ROS入门21讲》零基础学习笔记
我又把里面的文件夹拖到桌面,再把原空白目录删除。这一波下来和老师的直接拖动效果一样了,不失为一种办法。
古月居《ROS入门21讲》零基础学习笔记

编译和运行

下面开始编译和运行cpp for 循环的文件。

  • g++ 文件名 -o 输出的可执行的文件的名字
 g++ c++_for.cpp -o c++_for

古月居《ROS入门21讲》零基础学习笔记

  • ./ 运行文件
./c++_for

古月居《ROS入门21讲》零基础学习笔记
python不用编译,直接用
结果寄,SyntaxError: Missing parentheses in call to ‘print‘
记得我之前安装python编译器的时候有过改动,大概偏差出在那里。
于是我进python文件修改了一下语法(print改为print()),与胡老师课件上有一点点不同,运行成功。
古月居《ROS入门21讲》零基础学习笔记
再运行cpp while程序,连按多次方向上键,调出前前条命令。
古月居《ROS入门21讲》零基础学习笔记
做了一件小事,在虚拟机上安装了IDE。关于我决定在虚拟机上学编程这件事,原因一是新鲜感多一些,二是有一位计算机大佬朋友就是这么做的。据说有一些指令会很方便。在虚拟机上安装IDE的时候,我在clion和VScode两者之间还纠结了一阵,最终选择了后者。一方面是之前使用了一段时间clion体验不佳,让我满意的只是图表和页面比较好看;另一方面是听说VS的功能比较强大,甚至可以和人工智能ChatGPT交流,爱了爱了。安装的过程和结果也很干净,期待接下来的学习。

5.安装ROS

省流:上某宝

6.ROS是什么

  • 通信机制+开发工具+应用功能+生态系统

提高机器人研发中的软件复用率
学习过程中建议用谷歌搜索,搜到wiki的网站
(ROSwiki是记录ROS信息文档的主要论坛)

7.ROS的核心概念

节点与节点管理器

节点(Node)— 执行单元

  • 节点是具体的执行单元,执行具体任务的进程,独立运行的可执行文件
  • 不同的节点可以使用不同的编程语言,可分布式运行在不同的主机
  • 节点在系统中的名称必须是唯一的,重名ros系统会找不到

节点管理器(ROS Master) — 控制中心

  • 节点的管理。命名、注册、建立通讯
  • 提供参数服务器,节点使用此服务器存储和检索运行时的参数

话题通信(异步)

话题 topic

  • 分为发布者和订阅者
  • 单向数据传输,从驱动端传到订阅段,发布者到订阅者
  • 通道被定义为话题,时数据传输的总线

消息 message

  • 具有一定的类型和数据结构(有ros提供的标准类型和用户自定义的类型
  • 使用与编程语言无关的 .msg 文件定义类型和数据结构

服务通信(同步)

服务 service

  • 使用客户端/服务器(service/client) 模型,客户端发送请求数据,服务器完成处理后返回应答数据
  • 使用与编程语言无关的, .srv 文件定义请求和应答的数据结构
  • 一般是一次,发出一个配置指令,完成配置内容,返回一个反馈
    古月居《ROS入门21讲》零基础学习笔记
    缓冲区是防止发布与订阅的速度不匹配
    服务是会有阻塞

参数 parameter

  • 适合存储静态、非二进制的配置参数,不适合动态配置的数据

文件系统

  • 功能包 package:ros软件的基本单元,包含源码、配置文件、数据定义
  • 功能包清单 package manifest:记录功能包的基本信息,包含作者信息、许可信息等等信息
  • 元功能包 meta packages:组织多个用于同一目的的功能包

8.ROS命令行工具的使用(操作集)

(开始实操)
键盘突然在虚拟机用不了了。有效解决方案

rosrun

  • roscore启动ROS master(要首先运行的一个指令)
    古月居《ROS入门21讲》零基础学习笔记
  • rosrun来运行一个节点
  • (要呼出一个新终端)
  • (加两个参数,功能包名 节点名)
  • (输入功能包名后按tab可以显示功能包下有的节点)
    古月居《ROS入门21讲》零基础学习笔记
    光标要再键盘控制节点的那个窗口才能控制
    古月居《ROS入门21讲》零基础学习笔记

rqt_graph

  • rqt_graph用来显示系统计算图的工具
  • (直观,可视化,了解系统全貌)
  • (显示了节点和话题的信息)
    古月居《ROS入门21讲》零基础学习笔记

rosnode

  • rosnode list
  • (列出系统当中使用的节点)
  • (rosout是ros默认的一个话题)
    古月居《ROS入门21讲》零基础学习笔记
  • rosnode info
  • (查看某个节点的信息)
  • (发布和订阅的信息)
rosnode info /turtlesim

古月居《ROS入门21讲》零基础学习笔记

rostopic

古月居《ROS入门21讲》零基础学习笔记
看到下面的cmd_vel就是刚才就是键盘控制节点和海龟仿真器节点通讯的话题
古月居《ROS入门21讲》零基础学习笔记
学会使用tab, 这里输入消息类型的时候还要连按两次tab

  • 用pub发布数据给某一个topic
  • 后面跟话题名
  • 再后面跟消息结构(类型)
  • 再跟消息结构里的具体数据
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist "linear:
  x: 1.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.0" 

注意这里线速度的单位是米每秒,角速度单位弧度每秒)
在调具体数据的时候,移动光标只可以用左右方向键(用上下是会调出历史命令)
古月居《ROS入门21讲》零基础学习笔记
古月居《ROS入门21讲》零基础学习笔记
看到小海龟向左前方45度移动了一小段
想让小海龟连续移动

  • -r 表示rate, 10表示10Hz
rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist "linear:
>   x: 1.0
>   y: 1.0
>   z: 0.0
> angular:
>   x: 0.0
>   y: 0.0
>   z: 0.0" 

古月居《ROS入门21讲》零基础学习笔记
调angular的z调旋转
看到方向和角速度都是可以调的
圆圈的半径也符合速度和角速度的计算
古月居《ROS入门21讲》零基础学习笔记
在前面加负号也有意义
古月居《ROS入门21讲》零基础学习笔记

rosmsg

  • rosmsg show显示消息的数据结构
    古月居《ROS入门21讲》零基础学习笔记
    这里吐槽一下,好像不方便改设备的名称
    改了之后节点之间的通讯会出现问题
    只好继续用ros-virtual-machine了。。

rosservice

  • rosservice list
  • (提供的所有服务)
  • (服务端都是仿真器)(客户端是终端)
    古月居《ROS入门21讲》零基础学习笔记
  • rosservice call 召唤新海龟
rosservice call /spawn "x: 2.0
y: 2.0
theta: 0.0
name: 'turtle2'" 

古月居《ROS入门21讲》零基础学习笔记
可以看到是有一个反馈的(反馈信息name: “turtle2”)
古月居《ROS入门21讲》零基础学习笔记

  • 话题记录
  • rosbag record -a -O cmd_record
  • (rosbag 记录 all -大写O保存成压缩包 压缩包的名字)

看到了订阅的话题
随便让海龟动几下
结束之后在终端ctrl+c
默认保存到了主文件夹
从回车之后就开始记录了,包括没有操作的等待时间
古月居《ROS入门21讲》零基础学习笔记

  • 话题复现
  • rosbag play cmd_record.bag
    古月居《ROS入门21讲》零基础学习笔记

9.创建工作空间与功能包

概念

  • 工作空间(workspace)是一个存放工程开发相关文件的文件夹。所有的源码、配置文件、可执行文件都是放置在里面的。主要分为四个文件夹:
  • 1.src: 代码空间 (source space)
    放置功能包(里面的代码、配置文件、launch文件
  • 2.build:编译空间 (build space)
    编译过程中的中间文件(不太用关心
  • 3.devel:开发空间 (development space)
    编译生成的一些可执行文件、库、脚本
  • 4.install:安装空间 (install space)
    install命令的结果就放在里面

放一张老师课件截图:
古月居《ROS入门21讲》零基础学习笔记

创建工作空间

overview:
古月居《ROS入门21讲》零基础学习笔记
operation:
古月居《ROS入门21讲》零基础学习笔记

catkin_init_workspace //初始化工作空间(属性变化),看到冒出了CMakeLists.txt
  • 下面对工程编译(catkin_make
  • 要来到工作空间的根目录进行编译
    古月居《ROS入门21讲》零基础学习笔记
    看到出现了另两个空间:
    古月居《ROS入门21讲》零基础学习笔记
    使install空间出现:
    古月居《ROS入门21讲》零基础学习笔记

创建功能包

  • 功能包是放置ROS源码的最小单元
  • 同一个工作空间下不允许存在同名功能包

创建功能包:
(要放在src里面):
古月居《ROS入门21讲》零基础学习笔记

命令 功能包名 依赖1 依赖2 依赖3:

catkin_create_pkg  test_pkg roscpp rospy std_msgs

古月居《ROS入门21讲》零基础学习笔记
打开功能包:
include放置头文件
src放代码
后面两个文件是功能包一定有的,使其区别于普通文件夹的属性
古月居《ROS入门21讲》零基础学习笔记
编译功能包
还是先回到catkin_ws
编译工作空间就行了
因为没有代码所以也没有实际效果
古月居《ROS入门21讲》零基础学习笔记
要运行功能包里某一个程序的话,要先设置一下工作空间的环境变量
设置之后系统才能找到工作空间

设置环境变量

ros@ros-virtual-machine:~/catkin_ws$ source devel/setup.bash 

古月居《ROS入门21讲》零基础学习笔记
古月居《ROS入门21讲》零基础学习笔记
检查环境变量

  • 用echo命令把环境变量打开
  • (ROS_PACKAGE_PATH 是ROS本身的一个环境变量,通过这个环境变量查找所有功能包的路径)
  • 也是只有在前一步设置了工作空间的setup.bash之后,path里面才会包含工作空间的路径
ros@ros-virtual-machine:~/catkin_ws$ echo $ROS_PACKAGE_PATH 
/home/ros/catkin_ws/src:/opt/ros/noetic/share

有点不懂了,贴一下chatGPT的解释
古月居《ROS入门21讲》零基础学习笔记
小结:

创建工作空间

  • 创建:新建工作空间文件夹,该文件夹下新建代码空间
  • cd到代码空间里去
  • 用命令初始化工作空间
  • cd到工作空间里去
  • 用命令编译工作空间
  • source设置环境变量
  • 检查环境变量

创建功能包

  • cd进入工作空间下的代码空间
  • 创建功能包,并配置依赖
  • cd到工作空间
  • 编译工作空间
  • source设置环境变量

10. Publisher的编程实现

  • cd进入工作空间下的代码空间, 创建功能包,并配置依赖
  • 在功能包里面的代码空间里编写C++代码文件
  • 在cmakelist文件里面配置编译规则
  • cd到工作空间,编译工作空间,source设置环境变量
  • 打开roscore, 运行海龟仿真节点,运行功能包里发布者的节点

11. Subscriber的编程实现

  • 编写C++代码文件,配置编译规则
  • cd到工作空间,编译,设置环境变量
  • 打开roscore, 运行海龟节点,运行功能包里面订阅者的节点

12. 话题消息的定义与使用

  • 定义msg文件
  • 在package.xml里面添加功能包依赖
  • 在cmakelist.txt里面添加编译选项
  • 编译生成语言相关文件
  • 编写发布者
  • 编写订阅者
  • 在cmake文件里面配置编译规则:

1. 设置要编译的代码和生成的可执行文件

2. 设置链接库

3. 添加依赖项

  • cd到工作空间,编译,配置环境变量
  • 打开roscore, 运行发布者节点和订阅者节点

13. client的编程实现

  • cd到代码空间,创建功能包

  • 编写C++代码, 配置编译规则

  • cd到工作空间,编译,配置环境变量

  • 打开roscore, 运行海龟仿真节点,运行客户端节点

14. server的编程实现

  • 编写C++代码, 配置编译规则

  • cd到工作空间,编译,配置环境变量

  • 打开roscore, 运行海龟仿真节点,运行服务端节点

  • $ rosservice call /turtle_command"{}" 发布消息

15. 服务数据的定义与使用

  • 定义msg文件,添加功能包依赖,添加编译选项,编译生成相关文件
  • 编写发布者, 编写订阅者
  • 在cmake文件里面配置编译规则:

1. 设置要编译的代码和生成的可执行文件 2. 设置链接库 3. 添加依赖项

  • cd到工作空间,编译,配置环境变量
  • 打开roscore, 运行服务端,运行客户端节点

16. 参数的使用与编程方法

  • cd到代码空间,创建功能包
  • 编写C++代码, 配置编译规则
  • cd到工作空间,编译,配置环境变量
  • 打开roscore, 运行海龟仿真节点,运行参数设置节点

17. ROS中的坐标系管理系统

  • $ sudo apt-getinstall ros-melodic-turtle-tf
  • $ roslaunch turtle_tf turtletf demo.launch
  • $ rosrun turtlesim turtle_teleop_key
  • $ rosrun tf view_frames

18. tf坐标系广播与监听的编程实现

  • cd到代码空间,创建功能包
  • 编写广播器和监听器的C++代码,配置编译规则
  • cd到工作空间,编译,配置环境变量
  • 打开roscore
  • 运行海龟仿真节点
  • 运行turtle1和turtle2的广播器节点
  • 运行监听器节点
  • 运行键盘控制节点

19. launch 启动文件的使用方法

  • launch文件:通过xml文件实现多节点的配置和启动(可自动启动ros master)
  • 在launch里面编写启动节点的语法和参数设置的语法

20. 常用可视化工具的使用

Qt工具箱

  • 提供了一系列可视化工具

Rviz

  • 三维可视化工具

Gazebo

一些琐碎信息的整理

这周主要是复现的古月居老师课程里的代码实现环节

不求背住每一个细节和命令

需要达到的目标是当需要某个功能的时候能够想到在哪里,对着老师的课件实现自己想要的功能

文字识别软件

注意:

命令行和cmakelist里面的语句不要少了空格

注意第11节第6页有一个错误,rosrun里面应该是pose_publisher

  • 三维物理仿真平台
  • 使用老师教学包里python程序,要把python文件的第一行改成python3
    古月居《ROS入门21讲》零基础学习笔记

运行python的时候记得改成python3的语法
运行phthon文件的命令在11讲里有

有什么好说的呢,出错的地方跟着弹幕大佬改就好了
古月居《ROS入门21讲》零基础学习笔记
这里可以调整文本格式,达到高亮效果文章来源地址https://www.toymoban.com/news/detail-419400.html

到了这里,关于古月居《ROS入门21讲》零基础学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Dynamo学习笔记】基础入门

    参考资料 罗嘉祥,宋姗,田宏钧. 《Autodesk Revit炼金术——Dynamo基础实战教程》,同济大学出版社 最近在备课、带本科生的毕业设计,要用到Dynamo。自己花点时间复习一下,顺便记个笔记。 从Revit2018之后就自带Dynamo了,从“管理”选项卡中可以进入。 启动Dynamo之后,可以进

    2024年01月19日
    浏览(41)
  • Linux 基础入门(学习笔记通俗易懂版)

    本文章是学习了Linux的学习记录,着重记录了我对于Linux各命令的用法与感悟, 欢迎各位大佬批评指正 记录者:CYH-BI 记录日期:2023年7月7日 本篇文章将使用 虚拟机并安装centos 进行实操。关于虚拟机的安装请看其他教程,篇幅过长,不一展示。 Linux简介部分 Linux起初由Linus

    2024年02月08日
    浏览(53)
  • 斯坦福 Stats60:21 世纪的统计学:前言到第四章

    原文: statsthinking21.github.io/statsthinking21-core-site/index.html 译者:飞龙 协议:CC BY-NC-SA 4.0 这本书的目标是讲述统计学的故事,以及它如何被全球的研究人员所使用。这是一个与大多数统计学入门书籍中讲述的故事不同的故事,后者侧重于教授如何使用一套工具来实现非常具体的

    2024年01月18日
    浏览(50)
  • 如何学习网络安全?(零基础入门网络安全学习笔记)

    概括来说,网络安全课程的主要内容包括: 安全基本知识 应用加密学 协议层安全 Windows安全(攻击与防御) Unix/Linux安全(攻击与防御) 防火墙技术 入侵监测系统 审计和日志分析 下面分别对每部分知识介绍相应的具体内容和一些参考书。 一、安全基本知识 这部分的学习过

    2024年02月11日
    浏览(47)
  • STM32基础入门学习笔记:内部高级功能应用

    文章目录: 一:低功耗模式 1.睡眠模式测试程序 NVIC.h NVIC.c key.h key.c main.c 2.停机模式测试程序 main.c 3.待机模式测试程序 main.c 二:看门狗 1.独立看门狗测试程序 iwdg.h iwdg.c main.c 2.窗口看门狗测试程序 wwdg.h wwdg.c main.c 三:TIM定时器 tim.h tim.c main.c 四:CRC循环冗余校验计算单元与

    2024年02月13日
    浏览(45)
  • 【ros2】ros2环境安装与基础入门

    😏 ★,° :.☆( ̄▽ ̄)/$: .°★ 😏 这篇文章主要介绍ros2环境安装与基础入门。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路🥞 ROS 2 (Robot Operating System 2)是一个开源的机器人操作系统,它是ROS(Robot

    2024年02月09日
    浏览(45)
  • STM32基础入门学习笔记:核心板 电路原理与驱动编程

    文章目录: 一:LED灯操作  1.LED灯的点亮和熄灭 延迟闪烁 main.c  led.c led.h BitAction枚举 2.LED呼吸灯(灯的强弱交替变化) main.c  delay.c 3.按键控制LED灯 key.h key.c main.c  二:FLASH读写程序(有记忆可以保存断电之前的状态) flash.h flash.c main.c flash操作注意事项 三:蜂鸣器驱动程序(

    2024年02月13日
    浏览(39)
  • web渗透安全学习笔记:1、入门基础知识/ XXS漏洞

        自编写python渗透工具编写学习笔记专栏以来,笔者便发现了一个较为严重的问题:我们大多数文章都是学习如何用python编写扫描与利用漏洞的渗透工具,却没有真正解析漏洞的形成原因,长此以往我们的学习就只会浮于表面,广而不深。为了改变这一现状,笔者决定以深

    2024年02月03日
    浏览(55)
  • 【UnityShader入门精要学习笔记】第六章(1)Unity中的基础光照

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 一个物体为什么看起来是红色的?从物理上解释是因为这个物体

    2024年03月22日
    浏览(52)
  • 【unity基础】关于学习通用渲染管线(UniversalRenderPipeline)入门级的分享笔记

            文章是学习入门级别的文章,定位偏向于个人学习笔记,文章内有错误的点希望大家理性指出,感谢各位。因为URP本身是一个比较杂的东西,涉及到的东西也非常的多,下面主要是对于Unity URP的创建、简单使用(资深级别使用起来可能会设计到渲染方方面面的知识

    2024年02月05日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包