ROS2学习笔记

发布于:2025-06-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

内容源自官方文档。仅作为随笔记录,不成体系,注意甄别


通常情况下每年的 world turtle day 都会发布新的发行版。但是 LTS 版本每两年发布一次(偶数年)。

ROS 只使用少数最新的操作系统构建和测试每个发行版,我们称这些操作系统为 “1 级”(Tier 1)。每个 ROS 发行版都只针对这些 1 级操作系统进行构建和测试,对于其他版本操作系统不保证能正常运行ROS2。有关 ROS 发行版及其一级操作系统的更多信息,请阅读 REP-2000

如果要使用 Gazebo 模拟器,请提前阅读 recommended version of Gazebo for your ROS installation。提前确定 Gazebo 和 ROS2 之间不同版本的兼容关系。不管安装哪个版本 Gazebo,都需要安装对应的 ROS/GZ bridge 才可以正常使用。

遇到任何问题,可以通过以下几个渠道获取帮助

  1. ROS Package Documentation:ROS 核心软件包的文档以及软件包的具体内容都存放在 docs.ros.org 上。在这个网站上,你可以找到项目的核心教程和文档,以及为各个软件包生成的 API 文档
  2. ROS Index:当您想查找某个特定软件包的信息时,index 是最好的起点。它将你连接到与软件包相关的所有重要位置。除了 ROS 软件包的官方文档外,wiki 还包含两个重要资源:故障排除指南和常见问题。这两个页面涵盖了许多常见问题的解决方案。如果你曾经使用过 PyPI,那么在 ROS Index 上你应该会有宾至如归的感觉
  3. Robotics Stack Exchange:要想了解 ROS 社区的最新动态,请加入 ROS Discourse 论坛。这些论坛是发布公告、新闻和讨论一般问题的地方。ROS Discourse 不适合用来询问故障排除问题或报告错误
  4. Open Robotics Discord Server:Open Robotics 设有一个社区 Discord 服务器,ROS、Gazebo 和 Open-RMF 用户可以在这里见面、聊天并协调开源开发工作
  5. Issue Trackers:如果您发现了一个错误(例如,在机器人堆栈交流平台上的讨论结果),或者您想申请一项新功能,请访问问题跟踪器。ROS Index 上的软件包元数据中提供了相关链接。在报告错误时,请务必提供问题的详细描述、发生问题的环境、任何有助于开发人员重现问题的细节,如有可能,请提供调试回溯

性能提升

这里是自己学习过程中的感想,是否会提升性能不知道


DDS默认使用分布式注册,这回导致网络负载较大。可以考虑设置中心节点

自己实现或某些Node单独创建executor。官方提供的executor默认使用轮询方式调度,对高优先级任务或实时性不发保证,可以考虑自己重写spin函数,按照业务需求实现自定义调度逻辑

  1. 需要自定义调度策略
    场景:默认的 SingleThreadedExecutor 或 MultiThreadedExecutor 的调度方式可能无法满足特定需求。例如,您可能希望为某些高优先级的回调(如传感器数据处理)分配更高的优先级,或者需要控制回调的执行顺序。
    原因:ROS 2 的默认执行器以轮询(round-robin)方式处理回调,可能无法保证关键任务的实时性或优先级。
    解决方法:创建自定义的 Executor,重写其 spin 方法,基于特定逻辑(如优先级队列或时间敏感性)调度回调。例如,您可以继承 rclcpp::Executor 并实现自定义调度策略。
  2. 多线程或复杂并发需求
    场景:在复杂系统中,您可能需要精细控制多线程行为,例如为不同的节点或回调组分配独立的线程池,或者避免某些回调之间的竞争条件。
    原因:默认的 MultiThreadedExecutor 使用固定数量的线程处理所有回调,可能导致资源竞争或性能瓶颈,尤其是在高负载或实时性要求高的场景中。
    解决方法:创建自定义 Executor,并结合线程池或特定的线程分配策略。例如,您可以为某些节点分配专用的 SingleThreadedExecutor,而其他节点使用共享的 MultiThreadedExecutor。
  3. 实时系统或确定性要求
    场景:在实时系统中(例如自动驾驶或机器人控制),需要确保回调的执行具有确定性(deterministic)或低延迟。
    原因:默认执行器可能无法满足硬实时或软实时系统的严格时序要求,尤其是在高频率数据处理或低抖动场景中。
    解决方法:实现一个自定义 Executor,结合实时调度策略(如优先级调度或时间片分配),并可能与实时操作系统(如RT-Preempt Linux)结合使用。
  4. 混合不同类型的节点或回调组
    场景:当系统中包含多种类型的节点(例如,周期性定时器回调和高频传感器订阅),需要为不同类型的回调分配不同的执行策略。
    原因:默认执行器对所有回调一视同仁,可能导致高频回调阻塞低频回调,或反之。
    解决方法:为不同类型的回调组创建多个 Executor 实例。例如,为传感器数据处理使用一个高优先级的 SingleThreadedExecutor,为日志记录使用另一个低优先级的 Executor。
  5. 调试或性能优化
    场景:在调试或性能分析时,您可能需要监控回调的执行时间、调度延迟或资源使用情况。
    原因:默认执行器不提供内置的监控或分析功能,可能难以识别性能瓶颈。
    解决方法:创建自定义 Executor,添加日志记录或性能分析功能,例如记录每个回调的执行时间或检测回调的阻塞情况。
  6. 特殊硬件或资源约束
    场景:在资源受限的嵌入式设备上运行 ROS 2 时,需要优化执行器的资源使用(如内存、CPU)。
    原因:默认执行器可能在内存分配或线程管理上不够高效,尤其是在低功耗设备上。
    解决方法:实现一个轻量级的自定义 Executor,减少线程数量或优化内存分配策略。
#include <rclcpp/rclcpp.hpp>

class PriorityExecutor : public rclcpp::Executor {
public:
  PriorityExecutor() : rclcpp::Executor() {}

  void spin() override {
    // 自定义调度逻辑,例如优先处理高优先级回调
    while (rclcpp::ok()) {
      rclcpp::Event::SharedPtr event = get_next_event();
      if (event) {
        // 假设某些回调有更高优先级,可以根据条件调整
        execute_any_executable(event);
      } else {
        // 等待新事件
        wait_for_work(std::chrono::milliseconds(100));
      }
    }
  }
};

int main(int argc, char **argv) {
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("priority_node");

  // 创建自定义执行器
  auto executor = std::make_shared<PriorityExecutor>();
  executor->add_node(node);

  // 启动执行器
  executor->spin();

  rclcpp::shutdown();
  return 0;
}

注意事项

  • 回调组(Callback Groups):ROS 2 的回调组(MutuallyExclusive 或 Reentrant)可以与自定义 Executor 结合使用,以进一步控制回调的并发性。
  • 线程安全:自定义 Executor 时,必须确保线程安全,避免数据竞争或死锁。
  • 性能测试:在实现自定义 Executor 后,建议进行性能测试,确保满足系统需求。

环境准备

  1. 参照官方文档安装ROS2
  2. 安装自己开发packages所需依赖sudo apt install ros-dev-toolssudo apt install python3-colcon-common-extensions
  3. 安装colcon_cd
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/humble/" >> ~/.bashrc
  1. 自己开发package或使用他人源代码时,安装依赖rosdep install -i --from-path src --rosdistro humble -y

工作空间

ros2中工作空间(workspace)不能嵌套,但是可以通过overlay机制叠加多个工作空间(如:source aa/install/setup.bash && source bb/install/setup.bash

  • Underlay:通常是 ROS2 系统安装目录(如 /opt/ros/foxy/ ),提供核心功能包
  • Overlay:用户自定义的工作空间,可以覆盖或扩展 Underlay 中的包。
  • 执行顺序:ROS2会优先使用 Overlay 中的包,再查找 Underlay

构建

构建过程类似maven,包依赖类似shell/python

  1. 安装依赖 rosdep install -i --from-path src --rosdistro <distro> -y
  2. 进行编译 colcon build --symlink-install。使用``可以减少Python脚本的重复编译

构建时 colcon 会自动遍历 src 目录下所有子文件夹。遵循以下规则:

  1. 目录必须包含符合 REP 149 规范的 package.xml 和 构建配置文件(CMakeLists.txt 或 setup.py),二者缺一不可,否则会被编译系统忽略,且不报错
  2. 符号链接:如果目录包含其他目录的符号链接,编译系统会进入对应目录查找,这可能会引起路径导致的编译错误
  3. 重复包名:如果多个子目录定义了相同的包名,colcon 会报错

实用参数

  1. --packages-select 仅编译指定的包,有多个包名时,使用空格隔开
  2. --packages-ignore 排除指定的包
  3. --base-paths 从指定的路径开始搜索。默认是从 src 开始
# demo
colcon build --packages-select package1 package2 --packages-ignore package_to_skip --base-paths src/subdir

调试

调试的目的是获取运行时数据、查找出错位置,判断出错原因。

常用的调试方案就是打印日志

  1. ros2 bag 用于记录和回放 ROS 2 话题数据的工具
  2. rqt_graph 可视化节点和话题的拓扑结构
  3. rqt_console 查看和管理ROS 2日志消息
  4. ros2doctor 检查ROS 2系统的健康状况
  5. RViz(ROS Visualization Tool)强大的3D可视化工具,支持:机器人模型显示(URDF/SDF);传感器数据(激光雷达、点云、图像等);TF坐标系可视化4。适用场景:调试SLAM、导航、机械臂运动规划等

package 包

pakcage 不能嵌套 packageworkspace 也不能嵌套 workspace

使用命令 ros2 pkg create 可以创建 package。也可以手动创建一个package,但需要手动创建必要的文件/目录,并遵循REP 149指定必要信息,具体可参照 Creating-Your-First-ROS2-Package

package 中可以有 library 用于将公共代码/逻辑等封装到一起,并复用。package可以直接导入自己内部的library,也可以引用其他 package 中的 library

# my_package/my_library.py

def add_numbers(a: int, b: int) -> int:
    """示例:库函数(加法)"""
    return a + b

class DataProcessor:
    """示例:库类(数据处理)"""
    def __init__(self):
        self._data = []

    def append_data(self, item):
        self._data.append(item)

    def get_stats(self):
        return {
            "count": len(self._data),
            "sum": sum(self._data)
        }
# my_package/my_node.py
from my_package.my_library import add_numbers, DataProcessor
import rclpy
from rclpy.node import Node

class MyNode(Node):
    def __init__(self):
        super().__init__('my_node')
        # 调用库函数
        result = add_numbers(3, 5)
        self.get_logger().info(f"3 + 5 = {result}")

        # 调用库类
        processor = DataProcessor()
        processor.append_data(10)
        processor.append_data(20)
        stats = processor.get_stats()
        self.get_logger().info(f"Stats: {stats}")

def main():
    rclpy.init()
    node = MyNode()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

如果要使用其他 package 中的 library,需要首先申明依赖,使用方法和前面 demo 一致。

<exec_depend>my_package</exec_depend>

依赖管理

这里以python开发为例。依赖分为几类:依赖其他package、依赖pypi包,依赖系统库。
每个package都有自己的依赖,使用 rosdep install -i --from-path src --rosdistro <distro> -y可以自动安装所有依赖

依赖其他package

<exec_depend>my_package</exec_depend>

依赖pypi包

这里需要修改两个地方,一个是 package.xml 另一个是 setup.py

package.xml 需要声明依赖的 python 模块名称,以python3- 开头。在 setup.py 中生命依赖的三方 python 包名称。下面以 opencv 为例说明

<exec_depend>python3-opencv</exec_depend>
# setup.py
from setuptools import setup

setup(
    name='my_package',
    # ...其他配置...
    install_requires=[
        'opencv-python<=4.5.0', # 无需加python3-前缀
        'private_package @ git+https://github.com/user/repo.git@branch',
    ],
)

依赖系统库

# 系统会自动通过apt安装(如libopencv-dev)
<depend>opencv</depend>  # 通过apt安装的库名

依赖分类

依赖类型 作用阶段 典型用途 示例(package.xml
<depend> 构建 + 运行 + 测试 通用依赖,适用于所有阶段 <depend>rclcpp</depend>
<build_depend> 仅构建阶段 编译代码所需的头文件和库 <build_depend>eigen3</build_depend>
<exec_depend> 仅运行阶段 运行节点时需要的库或工具 <exec_depend>ros2launch</exec_depend>
<test_depend> 仅测试阶段 单元测试或集成测试的框架 <test_depend>ament_cmake_gtest</test_depend>
<build_export_depend> 构建导出阶段 声明其他包在依赖当前包时需要哪些依赖(传递性构建依赖)。 <build_export_depend>eigen3</build_export_depend> <!-- 下游包也需要eigen3 -->
<buildtool_depend> 构建工具链阶段 指定构建工具(如ament_cmake、colcon),不传递给下游包。 <buildtool_depend>ament_cmake</buildtool_depend><buildtool_depend>ament_cmake</buildtool_depend>
<doc_depend> 文档生成阶段 仅用于生成文档的依赖(如Doxygen)。
<conflict> 包冲突管理 声明与当前包冲突的其他包(极少使用)。

依赖传递性规则
默认传递性:

  1. build_dependexec_depend 不会自动传递给依赖当前包的其他包。
  2. 若需传递,必须显式声明build_export_dependexec_depend

例外:
3. depend 隐含传递性(等同于同时声明 build_export_dependexec_depend)。

常用依赖

开发相关

名称 用途
rclpy 使用python开发的程序需要依赖
builtin_interface ROS2中已有的消息接口
rosidl_default_generators 将自定义的消息文件转为 C++、Python

常用工具列表

调试工具

名称 作用
rviz2 三维数据可视化
rqt 可视化图表、图像等;也可以替代命令行进行topic发送、service调用等
ros2 bag 记录/回放 Topic

rqt 插件

名称 作用
apt install -y ros2-humble-rqt-tf-free 可视化查看和操作坐标等。是 tf2 的 GUI 版本
apt install -y ros-humble-rqt-* 一次性安装所有rqt相关插件

开发工具

名称 作用
TF 工具包 常用于运动学坐标转换与坐标/坐标系管理
URDF格式文件 描述机器人的结构、关节、传感器等信息
Gazebo 仿真工具
Navigation 2应用框架 用于移动机器人导航
Moveit2 用于机械臂规划
名称 作用
apt install ros-humble-mrpt2 -y 位姿数据转换(四元数、欧拉角等)

开发小技巧

当需要使用死循环等待消息时,可以在子线程这样写

# 这样写的好处时,ROS 发生异常时,程序可以自己停止
while rclpy.ok()
  # do something or wait event
  

批量操作

  1. 可以使用 ros2 launch 机制一次性启动多个节点,同时完成节点等配置
  2. 当需要配置的参数很多时,可以使用 ros2 param dump <node|service> > <name_of_storage_file>.yaml 然后使用 ros2 run ... --params-files <name_of_storage_file>.yaml 导入参数