• ROS与QT的那些事--0 该教程安排及后续安排


    1 作品欣赏

    作品欣赏有5个视频,最后一个是我的毕业设计。也是我们这个教程的
    最终目标。

    ros与qt的那些事-之-可视化界面作品分享-P1-ROS可视化界面之发布者作品欣赏(ros_qt_pub)


    可视化界面实现ROS话题通信的发布者实现

    ros与qt的那些事-之-可视化界面作品分享-P2-2 ROS可视化界面之订阅者作品欣赏(ros_qt_sub)


    可视化界面实现ROS话题通信的发布者及订阅者实现

    ros与qt的那些事-之-可视化界面作品分享-P3-3 ROS可视化界面之小乌龟作品欣赏(ros_qt_turtlesim)


    通过可视化界面的按键控件来控制ROS小乌龟。nice!

    ros与qt的那些事-之-可视化界面作品分享-P4-4 ROS可视化界面之六轴机械臂作品欣赏)

    利用ROS和QT制作机械臂仿真平台,完成示教模式的编写,输入示教模式,点动示教模式两种模式,并且已经测试完毕。

    ros与qt的那些事-之-可视化界面作品分享-P5-5 毕业设计作品欣赏-基于ROS的六轴机械臂抓取仿真设计


    利用ROS和QT制作机械臂仿真平台,通过示教模式来示教点位,确认机械臂的运动轨迹,在进行抓取物体,放下物体。

    2 教程时间安排

    本人是上班族,平时上班基本没有什么时间。只能在周末出该教程。

    3 教程目标

    本教程主要教大家使用QT来开发可视化界面,俗称人机交互界面,主要是跟ROS交互,目标就是上面5个作品分享视频中的大部分内容。后续也会其他的知识加入。

    4 需要掌握什么知识可以看该教程

    本教程是有视频教程和文档教程,只要会C++基础知识就可以学习该教程,(注:本教程使用的编程语言是C++,不会使用其他编程语言。);了解ROS的更好。

    5 需要什么硬件及软件

    硬件:有一个笔记本就可以,内存尽量16G以上,不然虚拟机一开可能有点不够用。当然也可以安装双系统。
    软件 : virtualbox虚拟机软件、SolidWorks软件(制作模型需要)。

    6 安装虚拟机软件及ROS

    6.1虚拟机软件下载

    安装 virtualbox 需要先访问官网,下载安装包,官网下载地址:https://www.virtualbox.org/wiki/Downloads

    在这里插入图片描述
    安装就正常安装一直下一步就好了。提示有提示安装就安装。
    在这里插入图片描述

    6.2 下载ubuntu20.04 镜像

    下载地址
    https://old-releases.ubuntu.com/releases/focal/

    在这里插入图片描述

    7 ROS部分知识介绍:

    7.0 ROS文件系统

    ROS文件系统级指的是在硬盘上ROS源代码的组织形式,其结构大致可以如下图所示:
    在这里插入图片描述
    WorkSpace — 自定义的工作空间

    |--- build:编译空间,用于存放CMake和catkin的缓存信息、配置信息和其他中间文件。
    
    |--- devel:开发空间,用于存放编译后生成的目标文件,包括头文件、动态&静态链接库、可执行文件等。
    
    |--- src: 源码
    
        |-- package:功能包(ROS基本单元)包含多个节点、库与配置文件,包名所有字母小写,只能由字母、数字与下划线组成
    
            |-- CMakeLists.txt 配置编译规则,比如源文件、依赖项、目标文件
    
            |-- package.xml 包信息,比如:包名、版本、作者、依赖项...(以前版本是 manifest.xml)
    
            |-- scripts 存储python文件
    
            |-- src 存储C++源文件
    
            |-- include 头文件
    
            |-- msg 消息通信格式文件
    
            |-- srv 服务通信格式文件
    
            |-- action 动作格式文件
    
            |-- launch 可一次性运行多个节点 
    
            |-- config 配置信息
    
        |-- CMakeLists.txt: 编译的基本配置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    ROS 文件系统中部分目录和文件前面编程中已经有所涉及,比如功能包的创建、src目录下cpp文件的编写、scripts目录下python文件的编写、launch目录下launch文件的编写,并且也配置了 package.xml 与 CMakeLists.txt 文件。其他目录下的内容后面教程将会再行介绍,当前我们主要介绍: package.xml 与 CMakeLists.txt 这两个配置文件。

    7.0.0 package.xml

    该文件定义有关软件包的属性,例如软件包名称,版本号,作者,维护者以及对其他catkin软件包的依赖性。请注意,该概念类似于旧版 rosbuild 构建系统中使用的manifest.xml文件。

    <?xml version="1.0"?>
    <!-- 格式: 以前是 1,推荐使用格式 2 -->
    <package format="2">
      <!-- 包名 -->
      <name>demo01_hello_vscode</name>
      <!-- 版本 -->
      <version>0.0.0</version>
      <!-- 描述信息 -->
      <description>The demo01_hello_vscode package</description>
    
      <!-- One maintainer tag required, multiple allowed, one person per tag -->
      <!-- Example:  -->
      <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
      <!-- 维护人员 -->
      <maintainer email="xuzuo@todo.todo">xuzuo</maintainer>
    
    
      <!-- One license tag required, multiple allowed, one license per tag -->
      <!-- Commonly used license strings: -->
      <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
      <!-- 许可证信息,ROS核心组件默认 BSD -->
      <license>TODO</license>
    
    
      <!-- Url tags are optional, but multiple are allowed, one per tag -->
      <!-- Optional attribute type can be: website, bugtracker, or repository -->
      <!-- Example: -->
      <!-- <url type="website">http://wiki.ros.org/demo01_hello_vscode</url> -->
    
    
      <!-- Author tags are optional, multiple are allowed, one per tag -->
      <!-- Authors do not have to be maintainers, but could be -->
      <!-- Example: -->
      <!-- <author email="jane.doe@example.com">Jane Doe</author> -->
    
    
      <!-- The *depend tags are used to specify dependencies -->
      <!-- Dependencies can be catkin packages or system dependencies -->
      <!-- Examples: -->
      <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
      <!--   <depend>roscpp</depend> -->
      <!--   Note that this is equivalent to the following: -->
      <!--   <build_depend>roscpp</build_depend> -->
      <!--   <exec_depend>roscpp</exec_depend> -->
      <!-- Use build_depend for packages you need at compile time: -->
      <!--   <build_depend>message_generation</build_depend> -->
      <!-- Use build_export_depend for packages you need in order to build against this package: -->
      <!--   <build_export_depend>message_generation</build_export_depend> -->
      <!-- Use buildtool_depend for build tool packages: -->
      <!--   <buildtool_depend>catkin</buildtool_depend> -->
      <!-- Use exec_depend for packages you need at runtime: -->
      <!--   <exec_depend>message_runtime</exec_depend> -->
      <!-- Use test_depend for packages you need only for testing: -->
      <!--   <test_depend>gtest</test_depend> -->
      <!-- Use doc_depend for packages you need only for building documentation: -->
      <!--   <doc_depend>doxygen</doc_depend> -->
      <!-- 依赖的构建工具,这是必须的 -->
      <buildtool_depend>catkin</buildtool_depend>
    
      <!-- 指定构建此软件包所需的软件包 -->
      <build_depend>roscpp</build_depend>
      <build_depend>rospy</build_depend>
      <build_depend>std_msgs</build_depend>
    
      <!-- 指定根据这个包构建库所需要的包 -->
      <build_export_depend>roscpp</build_export_depend>
      <build_export_depend>rospy</build_export_depend>
      <build_export_depend>std_msgs</build_export_depend>
    
      <!-- 运行该程序包中的代码所需的程序包 -->  
      <exec_depend>roscpp</exec_depend>
      <exec_depend>rospy</exec_depend>
      <exec_depend>std_msgs</exec_depend>
    
    
      <!-- The export tag contains other, unspecified, tags -->
      <export>
        <!-- Other tools can request additional information be placed here -->
    
      </export>
    </package>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    7.0.1 CMakelists.txt

    文件CMakeLists.txt是CMake构建系统的输入,用于构建软件包。任何兼容CMake的软件包都包含一个或多个CMakeLists.txt文件,这些文件描述了如何构建代码以及将代码安装到何处。

    cmake_minimum_required(VERSION 3.0.2) #所需 cmake 版本
    project(demo01_hello_vscode) #包名称,会被 ${PROJECT_NAME} 的方式调用
    
    ## Compile as C++11, supported in ROS Kinetic and newer
    # add_compile_options(-std=c++11)
    
    ## Find catkin macros and libraries
    ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
    ## is used, also find other catkin packages
    #设置构建所需要的软件包
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
    )
    
    ## System dependencies are found with CMake's conventions
    #默认添加系统依赖
    # find_package(Boost REQUIRED COMPONENTS system)
    
    
    ## Uncomment this if the package has a setup.py. This macro ensures
    ## modules and global scripts declared therein get installed
    ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
    # 启动 python 模块支持
    # catkin_python_setup()
    
    ################################################
    ## Declare ROS messages, services and actions ##
    ## 声明 ROS 消息、服务、动作... ##
    ################################################
    
    ## To declare and build messages, services or actions from within this
    ## package, follow these steps:
    ## * Let MSG_DEP_SET be the set of packages whose message types you use in
    ##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
    ## * In the file package.xml:
    ##   * add a build_depend tag for "message_generation"
    ##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
    ##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
    ##     but can be declared for certainty nonetheless:
    ##     * add a exec_depend tag for "message_runtime"
    ## * In this file (CMakeLists.txt):
    ##   * add "message_generation" and every package in MSG_DEP_SET to
    ##     find_package(catkin REQUIRED COMPONENTS ...)
    ##   * add "message_runtime" and every package in MSG_DEP_SET to
    ##     catkin_package(CATKIN_DEPENDS ...)
    ##   * uncomment the add_*_files sections below as needed
    ##     and list every .msg/.srv/.action file to be processed
    ##   * uncomment the generate_messages entry below
    ##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
    
    ## Generate messages in the 'msg' folder
    # add_message_files(
    #   FILES
    #   Message1.msg
    #   Message2.msg
    # )
    
    ## Generate services in the 'srv' folder
    # add_service_files(
    #   FILES
    #   Service1.srv
    #   Service2.srv
    # )
    
    ## Generate actions in the 'action' folder
    # add_action_files(
    #   FILES
    #   Action1.action
    #   Action2.action
    # )
    
    ## Generate added messages and services with any dependencies listed here
    # 生成消息、服务时的依赖包
    # generate_messages(
    #   DEPENDENCIES
    #   std_msgs
    # )
    
    ################################################
    ## Declare ROS dynamic reconfigure parameters ##
    ## 声明 ROS 动态参数配置 ##
    ################################################
    
    ## To declare and build dynamic reconfigure parameters within this
    ## package, follow these steps:
    ## * In the file package.xml:
    ##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
    ## * In this file (CMakeLists.txt):
    ##   * add "dynamic_reconfigure" to
    ##     find_package(catkin REQUIRED COMPONENTS ...)
    ##   * uncomment the "generate_dynamic_reconfigure_options" section below
    ##     and list every .cfg file to be processed
    
    ## Generate dynamic reconfigure parameters in the 'cfg' folder
    # generate_dynamic_reconfigure_options(
    #   cfg/DynReconf1.cfg
    #   cfg/DynReconf2.cfg
    # )
    
    ###################################
    ## catkin specific configuration ##
    ## catkin 特定配置##
    ###################################
    ## The catkin_package macro generates cmake config files for your package
    ## Declare things to be passed to dependent projects
    ## INCLUDE_DIRS: uncomment this if your package contains header files
    ## LIBRARIES: libraries you create in this project that dependent projects also need
    ## CATKIN_DEPENDS: catkin_packages dependent projects also need
    ## DEPENDS: system dependencies of this project that dependent projects also need
    # 运行时依赖
    catkin_package(
    #  INCLUDE_DIRS include
    #  LIBRARIES demo01_hello_vscode
    #  CATKIN_DEPENDS roscpp rospy std_msgs
    #  DEPENDS system_lib
    )
    
    ###########
    ## Build ##
    ###########
    
    ## Specify additional locations of header files
    ## Your package locations should be listed before other locations
    # 添加头文件路径,当前程序包的头文件路径位于其他文件路径之前
    include_directories(
    # include
      ${catkin_INCLUDE_DIRS}
    )
    
    ## Declare a C++ library
    # 声明 C++# add_library(${PROJECT_NAME}
    #   src/${PROJECT_NAME}/demo01_hello_vscode.cpp
    # )
    
    ## Add cmake target dependencies of the library
    ## as an example, code may need to be generated before libraries
    ## either from message generation or dynamic reconfigure
    # 添加库的 cmake 目标依赖
    # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    ## Declare a C++ executable
    ## With catkin_make all packages are built within a single CMake context
    ## The recommended prefix ensures that target names across packages don't collide
    # 声明 C++ 可执行文件
    add_executable(Hello_VSCode src/Hello_VSCode.cpp)
    
    ## Rename C++ executable without prefix
    ## The above recommended prefix causes long target names, the following renames the
    ## target back to the shorter version for ease of user use
    ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
    #重命名c++可执行文件
    # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
    
    ## Add cmake target dependencies of the executable
    ## same as for the library above
    #添加可执行文件的 cmake 目标依赖
    add_dependencies(Hello_VSCode ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    ## Specify libraries to link a library or executable target against
    #指定库、可执行文件的链接库
    target_link_libraries(Hello_VSCode
      ${catkin_LIBRARIES}
    )
    
    #############
    ## Install ##
    ## 安装 ##
    #############
    
    # all install targets should use catkin DESTINATION variables
    # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
    
    ## Mark executable scripts (Python etc.) for installation
    ## in contrast to setup.py, you can choose the destination
    #设置用于安装的可执行脚本
    catkin_install_python(PROGRAMS
      scripts/Hi.py
      DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
    )
    
    ## Mark executables for installation
    ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
    # install(TARGETS ${PROJECT_NAME}_node
    #   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
    # )
    
    ## Mark libraries for installation
    ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
    # install(TARGETS ${PROJECT_NAME}
    #   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
    #   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
    #   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
    # )
    
    ## Mark cpp header files for installation
    # install(DIRECTORY include/${PROJECT_NAME}/
    #   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
    #   FILES_MATCHING PATTERN "*.h"
    #   PATTERN ".svn" EXCLUDE
    # )
    
    ## Mark other files for installation (e.g. launch and bag files, etc.)
    # install(FILES
    #   # myfile1
    #   # myfile2
    #   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
    # )
    
    #############
    ## Testing ##
    #############
    
    ## Add gtest based cpp test target and link libraries
    # catkin_add_gtest(${PROJECT_NAME}-test test/test_demo01_hello_vscode.cpp)
    # if(TARGET ${PROJECT_NAME}-test)
    #   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
    # endif()
    
    ## Add folders to be run by python nosetests
    # catkin_add_nosetests(test)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223

    7.1 话题通信理论知识

    话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:

    ROS Master (管理者)
    Talker (发布者)
    Listener (订阅者)
    ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。

    在这里插入图片描述
    整个流程由以下步骤实现:

    0.Talker注册
    Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。

    1.Listener注册
    Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。

    2.ROS Master实现信息匹配
    ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。

    3.Listener向Talker发送请求
    Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。

    4.Talker确认请求
    Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。

    5.Listener与Talker件里连接
    Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。

    6.Talker向Listener发送消息

    连接建立后,Talker 开始向 Listener 发布消息。

    注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议

    注意2: Talker 与 Listener 的启动无先后顺序要求

    注意3: Talker 与 Listener 都可以有多个

    注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker
    与 Listern 照常通信。

    7.2 ROS节点运行管理launch文件

    一个程序中可能需要启动多个节点,比如:ROS 内置的小乌龟案例,如果要控制乌龟运动,要启动多个窗口,分别启动
    roscore、乌龟界面节点、键盘控制节点。如果每次都调用 rosrun 逐一启动,显然效率低下,如何优化?

    采用的优化策略便是使用roslaunch 命令集合 launch 文件启动管理节点,并且在后续教程中,也多次使用到了 launch 文件

    概念
    launch 文件是一个 XML 格式的文件,可以启动本地和远程的多个节点,还可以在参数服务器中设置参数。

    作用
    简化节点的配置与启动,提高ROS程序的启动效率。

    使用
    以 turtlesim 为例演示

    1.新建launch文件
    在功能包下添加 launch目录, 目录下新建 xxxx.launch 文件,编辑 launch 文件

    <launch>
        <node pkg="turtlesim" type="turtlesim_node"     name="myTurtle" output="screen" />
        <node pkg="turtlesim" type="turtle_teleop_key"  name="myTurtleContro" output="screen" />
    </launch>
    
    • 1
    • 2
    • 3
    • 4

    2.调用 launch 文件

    roslaunch 包名 xxx.launch
    
    • 1

    注意:roslaunch 命令执行launch文件时,首先会判断是否启动了 roscore,如果启动了,则不再启动,否则,会自动调用 roscore

  • 相关阅读:
    nginx--技术文档--架构体系--底层核心-原理
    《最新出炉》系列初窥篇-Python+Playwright自动化测试-6-元素定位大法-下篇
    SLAM从入门到精通(三边测量法详解)
    计算器到计算机的发展历史
    【C++】详细讲解C++的程序流程控制~
    第6章 数据库事务 & 第7章 DAO及相关实现类
    Android -- 每日一问:两个 Activity 之间如何传递参数?
    【Latex】模板设置及使用教程
    ORB-SLAM2 ---- computeOrbDescriptor函数
    【GIT版本控制】--项目管理与工具
  • 原文地址:https://blog.csdn.net/qq_50808730/article/details/125455508