欢迎光临
我们一直在努力

一文搞懂 ROS2 工程结构:工作空间、功能包、msg/srv/action 自定义接口

目录

前言

一、本章核心知识点

1.1 ROS2 工作空间是什么

1.2 ROS2 功能包是什么

1.3 msg、srv、action 分别用来做什么

二、ROS2 工作空间结构

2.1 src、build、install、log 四个目录

2.2 src 源码空间

(1)工作空间中的 src

(2)功能包内部的 src

2.3 build 编译空间

(1)build 的作用

(2)什么时候需要清理 build

2.4 install 安装空间

(1)install 是运行结果所在目录

(2)src、build、install 的关系

2.5 log 日志空间

2.6 为什么修改代码后需要重新编译

(1)C++ 代码不是解释执行

(2)正确编译运行流程

三、ROS2 功能包概念

3.1 什么是功能包

功能包是 ROS2 中组织代码的基本单位。

3.2 功能包和普通文件夹有什么区别

(1)普通文件夹没有 ROS2 工程属性

(2)C++ 功能包基本结构

3.3 C++ 功能包和 Python 功能包的区别

(1)两种常见构建类型

(2)创建 C++ 功能包

(3)创建 Python 功能包

3.4 功能包内部常见目录结构及作用

(1)完整功能包结构

(2)普通 C++ 节点功能包

(3)自定义接口功能包

四、ROS2 自定义接口 msg、srv、action

4.1 为什么需要自定义接口

(1)ROS2 已经提供了很多标准消息

(2)标准消息不够用时就需要自定义接口

4.2 msg 消息文件是什么

(1)msg 用于 Topic 话题通信

(2)msg 文件放在哪里

(3)LimoStatus.msg 示例

(4)std_msgs/Header header 是什么意思

(5)发布消息时如何赋值

(6)msg 文件注意事项

4.3 srv 服务文件是什么

(1)srv 用于 Service 服务通信

(2)srv 文件放在哪里

(3)LimoSrv.srv 示例

(4)Service 适合什么场景

4.4 action 动作文件是什么

(1)action 适合长时间任务

(2)action 文件放在哪里

(3)LimoAction.action 示例

(4)Action 的特点

4.5 msg、srv、action 对比总结

五、本章总结

5.1 ROS2 工作空间通常包含四个核心目录:

5.2 功能包是 ROS2 组织代码的基本单位。

5.3 ROS2 中常见的自定义接口有三种:


前言

前面几篇文章已经分别讲解了 ROS2 常见消息类型、话题通信基础以及常用调试命令。到这里,已经可以初步理解 ROS2 中节点、话题、消息之间的关系。

但是,真正开始写 ROS2 工程代码时,只理解节点和话题还不够。因为 ROS2 并不是简单写一个 .cpp 文件就能直接运行,而是需要放在特定的工程结构中,通过工作空间、功能包、编译目录、安装目录以及接口文件共同组织起来

很多初学者刚接触 ROS2 工程时,最容易疑惑的问题就是:

  • 为什么 ROS2 工程里有 src、build、install、log 这几个目录?
  • 为什么工作空间里面有一个 src,功能包里面也可能有一个 src?
  • 为什么修改了代码后,运行结果却没有变化?
  • 功能包和普通文件夹到底有什么区别?
  • msg、srv、action 这三种自定义接口分别适合什么场景?
  • 本文就围绕这些问题,系统讲解 ROS2 工程结构基础,重点包括:

  • ROS2 工作空间是什么
  • src、build、install、log 四个目录的作用
  • ROS2 功能包是什么
  • C++ 功能包和 Python 功能包的区别
  • msg、srv、action 自定义接口的作用和区别
  • 本文的重点是先把 ROS2 工程结构讲清楚,让大家知道代码应该放在哪里、功能包如何组织、接口文件如何理解。

    至于 ROS2 C++ 功能包中非常重要的两个配置文件:package.xml 和 CMakeLists.txt,我已经在下一篇文章中单独详细讲解:

    ROS2 功能包配置文件详解:package.xml 与 CMakeLists.txt 一文搞懂-CSDN博客https://blog.csdn.net/m0_58954356/article/details/161804636?spm=1001.2014.3001.5501除此之外ROS2其他相关学习博客链接如下:

    ROS2 常见消息类型保姆级教程-CSDN博客https://blog.csdn.net/m0_58954356/article/details/161693378?spm=1001.2014.3001.5501ROS2 话题通信实战:消息对象、Publisher 发布器与 Subscriber 订阅器保姆级教程-CSDN博客https://blog.csdn.net/m0_58954356/article/details/161702288?spm=1001.2014.3001.5501

    史上最全 ROS2 常用命令保姆级教程:节点、话题、消息结构、参数、服务和动作一篇讲透-CSDN博客https://blog.csdn.net/m0_58954356/article/details/161726502?spm=1001.2014.3001.5501


    一、本章核心知识点

    1.1 ROS2 工作空间是什么

    工作空间可以理解成 ROS2 项目的总文件夹。它用来统一存放源代码、编译中间文件、安装后的可执行文件以及日志文件

    一个典型 ROS2 工作空间一般长这样:

    agilex_open_class_ws/
    ├── src
    ├── build
    ├── install
    └── log


    1.2 ROS2 功能包是什么

    功能包是 ROS2 中组织代码的基本单位。简单理解,功能包就是用来存放某一类功能源代码的文件夹

    比如:

    • 一个功能包可以专门放小车运动控制代码
    • 一个功能包可以专门放自定义消息
    • 一个功能包可以专门放 launch 启动文件

    ROS2 不是直接管理零散的源码文件,而是通过功能包来管理代码和依赖。


    1.3 msg、srv、action 分别用来做什么

    ROS2 中常见的自定义通信接口有三类:msg、srv、action。

    接口类型文件后缀用途适合场景
    消息 .msg 定义 Topic 发布和订阅的数据结构 状态上报、传感器数据、速度指令
    服务 .srv 定义 Service 请求和响应的数据结构 一次性查询、参数设置、触发某个动作
    动作 .action 定义 Action 的目标、结果和反馈 导航、长时间运动任务、需要过程反馈的任务

    二、ROS2 工作空间结构

    2.1 src、build、install、log 四个目录

    ROS2 工作空间中最常见的四个目录如下:

    目录作用学习时的理解
    src 源码空间 存放我们自己写的代码、脚本、launch 文件、功能包源码
    build 编译空间 编译过程中产生的中间文件,一般不需要手动修改
    install 安装空间 编译安装后的可执行文件、脚本、接口文件会放在这里
    log 日志空间 记录编译和运行过程中的日志,出错时可以查看

    工作空间目录里能看到 build、install、log、src 四个文件夹。这也是 ROS2 使用 colcon build 后最典型的结构。

    注意:

    工作空间下的 src 不是直接随便放代码的地方,而是用来存放 ROS2 功能包的地方。

    例如:

    agilex_open_class_ws/
    └── src
    ├── limo_learning
    ├── limo_msgs
    └── other_package

    这里的 limo_learning、limo_msgs 才是真正的 ROS2 功能包。


    2.2 src 源码空间

    src 是源码空间,主要用来存放我们自己创建或下载的 ROS2 功能包。

    (1)工作空间中的 src

    工作空间下的 src 不是随便放代码的地方,而是用来存放 ROS2 功能包的地方。

    例如:

    agilex_open_class_ws/
    └── src
    ├── limo_learning
    ├── limo_msgs
    └── other_package

    这里的:

    limo_learning
    limo_msgs
    other_package

    才是真正的 ROS2 功能包。


    (2)功能包内部的 src

    功能包内部,也可能还有一个 src 文件夹。

    例如:

    limo_learning/
    ├── src
    ├── include
    ├── launch
    ├── config
    ├── CMakeLists.txt
    └── package.xml

    这里要特别区分两个 src:

  • 工作空间的 src:用来存放功能包
  • 功能包内部的 src:用来存放 C++ 源代码
  • 例如:

    agilex_open_class_ws/src/limo_learning/src/limo_topic_cmd.cpp

    这里前面的 src 是工作空间源码目录,后面的 src 是功能包内部的 C++ 源码目录。

    完整结构如下:

    agilex_open_class_ws/
    └── src
    └── limo_learning
    ├── src
    │ └── limo_topic_cmd.cpp
    ├── CMakeLists.txt
    └── package.xml


    2.3 build 编译空间

    (1)build 的作用

    build 是编译空间。

    当我们在工作空间根目录执行:

    colcon build

    ROS2 会根据每个功能包中的 CMakeLists.txt 和 package.xml 进行编译。

    编译过程中产生的中间文件会放到 build 目录中,例如:

    build/limo_learning
    build/limo_msgs

    一般情况下,build 目录不需要我们手动修改。


    (2)什么时候需要清理 build

    如果遇到编译缓存异常、依赖更新后不生效等问题,可以清理后重新编译:

    rm -rf build install log
    colcon build

    这相当于重新生成整个工作空间的编译结果。


    2.4 install 安装空间

    (1)install 是运行结果所在目录

    install 是 ROS2 中非常重要的目录。

    很多初学者以为 ros2 run 运行的是 src 里面的源码,其实不是。

    对于 C++ 功能包来说,我们运行的通常是 编译后安装到 install 空间中的可执行文件。

    例如运行:

    ros2 run limo_learning limo_topic_cmd

    系统会在已经 source 的 install 空间中查找 limo_topic_cmd 这个可执行文件。


    (2)src、build、install 的关系

    可以这样理解 ROS2 的编译运行流程:

    src 里面写代码

    colcon build 编译

    build 里面生成中间文件

    install 里面生成运行结果

    ros2 run 从 install 空间查找程序

    这也是为什么修改 C++ 源代码之后,一定要重新编译:

    colcon build
    source install/setup.bash

    如果只修改了 src 里的源码,但没有重新编译,那么 install 里面还是旧的可执行文件,运行结果自然不会变化。


    2.5 log 日志空间

    log 是日志空间。

    每次执行:

    colcon build

    • 编译过程中的日志都会保存在 log 目录中。
    • 如果某个功能包编译失败,可以去 log 目录查看更详细的错误信息。

    不过对于初学阶段来说,大多数时候直接看终端中的报错信息就已经够用了。

    但是当项目变大后,log 目录会很有用,尤其是排查复杂编译错误时。


    2.6 为什么修改代码后需要重新编译

    (1)C++ 代码不是解释执行

    这是初学 ROS2 时非常重要的问题。

    因为 ROS2 的 C++ 代码不是解释执行的,而是需要先编译成可执行文件。

    修改 C++ 文件后:

    src/limo_learning/src/limo_topic_cmd.cpp

    只是源码发生了变化。

    但是运行时用到的可执行文件在:

    install/limo_learning/lib/limo_learning/limo_topic_cmd

    如果没有重新执行:

    colcon build

    那么 install 里面的可执行文件不会更新。


    (2)正确编译运行流程

    正确流程应该是:

  • cd ~/agilex_open_class_ws
  • colcon build
  • source install/setup.bash
  • ros2 run limo_learning limo_topic_cmd
  • 如果只修改了某一个功能包,也可以单独编译:

    colcon build –packages-select limo_learning
    source install/setup.bash


    三、ROS2 功能包概念

    3.1 什么是功能包

    功能包是 ROS2 中组织代码的基本单位。

    一个 ROS2 工程不是直接管理零散的源码文件,而是通过一个个功能包来管理代码、接口、配置和依赖。

    一个功能包可以包含:

    • C++ 节点代码
    • Python 节点代码
    • 自定义 msg 消息
    • 自定义 srv 服务
    • 自定义 action 动作
    • launch 启动文件
    • config 配置文件
    • URDF / xacro 机器人模型文件
    • RViz 配置文件

    例如:

    limo_learning

    可以用来存放小车的控制节点代码。

    limo_msgs

    可以专门用来存放小车相关的自定义消息、服务和动作接口。

    所以功能包不只是一个普通文件夹,它是 ROS2 用来组织工程的基本单位。


    3.2 功能包和普通文件夹有什么区别

    (1)普通文件夹没有 ROS2 工程属性

    普通文件夹只是一个目录,本身没有 ROS2 工程属性。

    而 ROS2 功能包通常至少包含两个关键文件:

    CMakeLists.txt
    package.xml

    其中:

    文件作用
    CMakeLists.txt 描述如何编译、生成可执行文件、安装文件
    package.xml 描述功能包名称、版本、依赖、构建类型等信息

    也就是说,一个文件夹想被 ROS2 当作功能包识别,不能只是随便建一个目录,而是需要符合 ROS2 功能包结构。


    (2)C++ 功能包基本结构

    例如一个 C++ 功能包可能长这样:

    limo_learning/
    ├── src
    │ └── limo_topic_cmd.cpp
    ├── CMakeLists.txt
    └── package.xml

    其中:

    src

    用来存放 C++ 源代码。

    CMakeLists.txt

    用来描述 C++ 编译规则。

    package.xml

    用来描述功能包信息和依赖关系。


    3.3 C++ 功能包和 Python 功能包的区别

    (1)两种常见构建类型

    ROS2 中常见的功能包构建类型有两种:

  • ament_cmake
  • ament_python
  • 它们分别对应不同语言的开发方式。

    构建类型适合语言特点
    ament_cmake C++ 需要 CMakeLists.txt 编译生成可执行文件
    ament_python Python 主要通过 setup.py 安装 Python 节点入口

    (2)创建 C++ 功能包

    如果要创建 C++ 功能包,可以执行:

    cd ~/agilex_open_class_ws/src
    ros2 pkg create –build-type ament_cmake limo_learning

    如果是写 C++ 节点,一般选择:

    ament_cmake


    (3)创建 Python 功能包

    如果要创建 Python 功能包,可以执行:

    cd ~/agilex_open_class_ws/src
    ros2 pkg create –build-type ament_python limo_learning_python

    如果是写 Python 节点,一般选择:

    ament_python

    在 ROS2 实际开发中:

    底层控制、驱动、实时性要求较高的模块,常用 C++。 快速验证、工具脚本、简单节点,也经常使用 Python。


    3.4 功能包内部常见目录结构及作用

    (1)完整功能包结构

    一个比较完整的 ROS2 功能包内部结构可能如下:

    limo_learning/
    ├── src
    ├── include
    ├── launch
    ├── config
    ├── msg
    ├── srv
    ├── action
    ├── CMakeLists.txt
    └── package.xml

    各目录作用如下:

    目录或文件作用
    src 存放 C++ 源代码
    include 存放 C++ 头文件
    launch 存放 launch 启动文件
    config 存放参数配置文件
    msg 存放自定义消息文件
    srv 存放自定义服务文件
    action 存放自定义动作文件
    CMakeLists.txt C++ 编译和安装规则
    package.xml 功能包信息和依赖声明

    (2)普通 C++ 节点功能包

    不过不是每个功能包都必须包含所有目录。

    例如普通 C++ 节点功能包可能只需要:

    limo_learning/
    ├── src
    ├── CMakeLists.txt
    └── package.xml


    (3)自定义接口功能包

    自定义接口功能包可能是:

    limo_msgs/
    ├── msg
    ├── srv
    ├── action
    ├── CMakeLists.txt
    └── package.xml


    四、ROS2 自定义接口 msg、srv、action

    4.1 为什么需要自定义接口

    (1)ROS2 已经提供了很多标准消息

    ROS2 已经提供了很多常用消息类型,例如:

    geometry_msgs/msg/Twist
    sensor_msgs/msg/LaserScan
    nav_msgs/msg/Odometry
    std_msgs/msg/String

    这些消息类型可以满足很多基础需求。

    例如:

    Twist 用来表示速度指令
    LaserScan 用来表示激光雷达数据
    Odometry 用来表示里程计数据
    String 用来表示字符串消息


    (2)标准消息不够用时就需要自定义接口

    但是实际机器人开发中,经常会遇到系统自带消息不够用的情况。

    比如我们想描述小车状态:

  • 车辆状态
  • 控制模式
  • 电池电压
  • 错误码
  • 运动模式
  • 这时候就可以自定义一个消息

    LimoStatus.msg

    如果想定义一个服务,让客户端发送速度请求,服务端返回是否执行成功,可以定义:

    LimoSrv.srv

    如果想定义一个长时间执行的任务,例如发送目标、执行过程反馈、最后返回结果,可以定义:

    LimoAction.action

    这就是自定义接口的意义。


    4.2 msg 消息文件是什么

    (1)msg 用于 Topic 话题通信

    msg 表示 message,也就是消息。

    它主要用于 Topic 话题通信。

    Topic 通信可以理解为:

  • 发布者不断发布消息
  • 订阅者接收消息
  • 结构类似:

    Publisher —- topic —-> Subscriber


    (2)msg 文件放在哪里

    自定义 msg 文件一般放在功能包的 msg 文件夹中。

    例如:

    limo_msgs/
    ├── msg
    │ └── LimoStatus.msg


    (3)LimoStatus.msg 示例

    LimoStatus.msg 示例:

    std_msgs/Header header

    uint8 vehicle_state
    uint8 control_mode
    float64 battery_voltage
    uint16 error_code
    uint8 motion_mode


    (4)std_msgs/Header header 是什么意思

    其中:

    std_msgs/Header header

    这个消息里包含了一个 标准消息头 header,它不是普通变量,而是 ROS2 里 std_msgs 包提供的一个标准结构。

    header = 时间戳 + 坐标系名称
    时间戳 stamp
    坐标系 frame_id

    其余每一行格式一般是:

    字段类型 字段名

    例如:

    float64 battery_voltage

    表示定义一个 float64 类型的字段,字段名叫 battery_voltage,可以用来表示电池电压


    (5)发布消息时如何赋值

    比如发布时可以这样填:

    msg.header.stamp = this->now();
    msg.header.frame_id = "base_link";

    msg.vehicle_state = 1;
    msg.control_mode = 2;
    msg.battery_voltage = 24.5;
    msg.error_code = 0;
    msg.motion_mode = 1;

  • 表示这条消息产生的时间。
  • 比如机器人系统中经常需要知道:
  • 这条状态消息是什么时候发布的?
  • 这条传感器数据是什么时候采集的?
  • 这条里程计数据是什么时候计算出来的?
  • 所以 stamp 用来记录时间。

  • (6)msg 文件注意事项

    需要注意:

    msg 文件里面不需要写 C++ 的分号

    错误写法:

    float64 battery_voltage;

    正确写法:

    float64 battery_voltage

    如果 msg 文件中使用了其他功能包里的类型,例如:

    std_msgs/Header header

    那么在 package.xml 和 CMakeLists.txt 中也要声明对应依赖。


    4.3 srv 服务文件是什么

    (1)srv 用于 Service 服务通信

    srv 表示 service,也就是服务。

    Service 是请求响应模型。

    它的特点是:

  • 客户端发送请求
  • 服务端处理请求
  • 服务端返回响应
  • 结构类似:

    Client —- request —-> Server
    Client <— response — Server


    (2)srv 文件放在哪里

    自定义 srv 文件一般放在功能包的 srv 文件夹中。

    例如:

    limo_msgs/
    ├── srv
    │ └── LimoSrv.srv


    (3)LimoSrv.srv 示例

    LimoSrv.srv 示例:

    float32 x
    float32 y
    float32 z

    bool success

    其中 — 是分隔符。

    — 上面是请求部分:

    float32 x
    float32 y
    float32 z

    — 下面是响应部分:

    bool success

    可以理解为:

  • 客户端发送 x、y、z 三个数据
  • 服务端处理后返回 success,表示本次请求是否执行成功。

  • (4)Service 适合什么场景

    Service 适合一次性请求场景,例如:

    查询一次状态
    设置一次参数
    触发一次动作
    请求一次计算结果


    4.4 action 动作文件是什么

    (1)action 适合长时间任务

    action 表示动作。

    Action 可以理解为增强版 Service。

    Service 更适合短时间完成的“一问一答”任务,而 Action 更适合执行时间较长、需要过程反馈的任务。

    例如:

    导航到某个目标点
    机械臂移动到某个位置
    机器人执行一段巡检任务
    小车执行一段运动任务

  • 这些任务往往不是瞬间完成的。
  • 客户端发送目标后,服务端需要执行一段时间,并且在执行过程中持续反馈状态,最后再返回执行结果。

  • (2)action 文件放在哪里

    自定义 action 文件一般放在功能包的 action 文件夹中。

    例如:

    limo_msgs/
    ├── action
    │ └── LimoAction.action


    (3)LimoAction.action 示例

    LimoAction.action 示例:

    float32 x
    float32 y
    float32 z

    bool success

    uint32 status

    Action 文件由三部分组成,中间用两个 — 分隔:

    目标 Goal

    结果 Result

    反馈 Feedback

    对应上面的例子:

    第一部分:表示客户端发送的目标数据。

    float32 x
    float32 y
    float32 z

    第二部分:表示任务最终执行结果。

    bool success

    第三部分:表示任务执行过程中的状态反馈。

    uint32 status


    (4)Action 的特点

    所以 Action 的特点是:

    发送目标
    执行过程中持续反馈
    执行结束返回最终结果


    4.5 msg、srv、action 对比总结

    三种接口的区别可以总结为:

    类型通信方式文件后缀适合场景
    msg 发布 / 订阅 .msg 状态上报、传感器数据、速度指令
    srv 请求 / 响应 .srv 参数设置、状态查询、触发一次动作
    action 目标 / 反馈 / 结果 .action 导航、巡检、机械臂运动、长时间任务

    更简单的记忆方式:

  • msg:一直发
  • srv:问一下,回一下
  • action:发目标,中途反馈,最后返回结果
  • 例如:

    /cmd_vel 速度控制:适合 Topic
    查询机器人是否准备好:适合 Service
    导航到目标点:适合 Action

    理解这三种通信方式之后,后面写 ROS2 代码时,就能根据任务特点选择合适的通信模型。


    五、本章总结

    本章主要讲解了 ROS2 工程基础概念。

    5.1 ROS2 工作空间通常包含四个核心目录:

    src 源码空间,用来存放功能包
    build 编译空间,用来存放编译中间文件
    install 安装空间,用来存放编译安装后的结果
    log 日志空间,用来存放编译和运行日志

    其中最关键的是理解:

    src 里面写代码
    install 里面放运行结果
    ros2 run 通常从 install 空间查找程序

    所以修改 C++ 源码、接口文件、CMakeLists.txt 或 package.xml 后,通常都需要重新执行:

    colcon build
    source install/setup.bash


    5.2 功能包是 ROS2 组织代码的基本单位。

    普通文件夹只是目录,而 ROS2 功能包通常需要包含:

    CMakeLists.txt
    package.xml

    C++ 功能包一般使用:

    ament_cmake

    Python 功能包一般使用:

    ament_python


    5.3 ROS2 中常见的自定义接口有三种:

    msg 用于 Topic 话题通信
    srv 用于 Service 服务通信
    action 用于 Action 动作通信

    三者可以简单记忆为:

    msg:一直发
    srv:问一下,回一下
    action:发目标,中途反馈,最后返回结果

    赞(0)
    未经允许不得转载:171主机测评 » 一文搞懂 ROS2 工程结构:工作空间、功能包、msg/srv/action 自定义接口
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址