内容源自官方文档。仅作为随笔记录,不成体系,注意甄别
文章目录
通常情况下每年的 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 才可以正常使用。
遇到任何问题,可以通过以下几个渠道获取帮助
- ROS Package Documentation:ROS 核心软件包的文档以及软件包的具体内容都存放在 docs.ros.org 上。在这个网站上,你可以找到项目的核心教程和文档,以及为各个软件包生成的 API 文档
- ROS Index:当您想查找某个特定软件包的信息时,index 是最好的起点。它将你连接到与软件包相关的所有重要位置。除了 ROS 软件包的官方文档外,wiki 还包含两个重要资源:故障排除指南和常见问题。这两个页面涵盖了许多常见问题的解决方案。如果你曾经使用过 PyPI,那么在 ROS Index 上你应该会有宾至如归的感觉
- Robotics Stack Exchange:要想了解 ROS 社区的最新动态,请加入 ROS Discourse 论坛。这些论坛是发布公告、新闻和讨论一般问题的地方。ROS Discourse 不适合用来询问故障排除问题或报告错误
- Open Robotics Discord Server:Open Robotics 设有一个社区 Discord 服务器,ROS、Gazebo 和 Open-RMF 用户可以在这里见面、聊天并协调开源开发工作
- Issue Trackers:如果您发现了一个错误(例如,在机器人堆栈交流平台上的讨论结果),或者您想申请一项新功能,请访问问题跟踪器。ROS Index 上的软件包元数据中提供了相关链接。在报告错误时,请务必提供问题的详细描述、发生问题的环境、任何有助于开发人员重现问题的细节,如有可能,请提供调试回溯
性能提升
这里是自己学习过程中的感想,是否会提升性能不知道
DDS默认使用分布式注册,这回导致网络负载较大。可以考虑设置中心节点
自己实现或某些Node单独创建executor。官方提供的executor默认使用轮询方式调度,对高优先级任务或实时性不发保证,可以考虑自己重写spin
函数,按照业务需求实现自定义调度逻辑
- 需要自定义调度策略
场景:默认的 SingleThreadedExecutor 或 MultiThreadedExecutor 的调度方式可能无法满足特定需求。例如,您可能希望为某些高优先级的回调(如传感器数据处理)分配更高的优先级,或者需要控制回调的执行顺序。
原因:ROS 2 的默认执行器以轮询(round-robin)方式处理回调,可能无法保证关键任务的实时性或优先级。
解决方法:创建自定义的 Executor,重写其 spin 方法,基于特定逻辑(如优先级队列或时间敏感性)调度回调。例如,您可以继承 rclcpp::Executor 并实现自定义调度策略。 - 多线程或复杂并发需求
场景:在复杂系统中,您可能需要精细控制多线程行为,例如为不同的节点或回调组分配独立的线程池,或者避免某些回调之间的竞争条件。
原因:默认的 MultiThreadedExecutor 使用固定数量的线程处理所有回调,可能导致资源竞争或性能瓶颈,尤其是在高负载或实时性要求高的场景中。
解决方法:创建自定义 Executor,并结合线程池或特定的线程分配策略。例如,您可以为某些节点分配专用的 SingleThreadedExecutor,而其他节点使用共享的 MultiThreadedExecutor。 - 实时系统或确定性要求
场景:在实时系统中(例如自动驾驶或机器人控制),需要确保回调的执行具有确定性(deterministic)或低延迟。
原因:默认执行器可能无法满足硬实时或软实时系统的严格时序要求,尤其是在高频率数据处理或低抖动场景中。
解决方法:实现一个自定义 Executor,结合实时调度策略(如优先级调度或时间片分配),并可能与实时操作系统(如RT-Preempt Linux)结合使用。 - 混合不同类型的节点或回调组
场景:当系统中包含多种类型的节点(例如,周期性定时器回调和高频传感器订阅),需要为不同类型的回调分配不同的执行策略。
原因:默认执行器对所有回调一视同仁,可能导致高频回调阻塞低频回调,或反之。
解决方法:为不同类型的回调组创建多个 Executor 实例。例如,为传感器数据处理使用一个高优先级的 SingleThreadedExecutor,为日志记录使用另一个低优先级的 Executor。 - 调试或性能优化
场景:在调试或性能分析时,您可能需要监控回调的执行时间、调度延迟或资源使用情况。
原因:默认执行器不提供内置的监控或分析功能,可能难以识别性能瓶颈。
解决方法:创建自定义 Executor,添加日志记录或性能分析功能,例如记录每个回调的执行时间或检测回调的阻塞情况。 - 特殊硬件或资源约束
场景:在资源受限的嵌入式设备上运行 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 后,建议进行性能测试,确保满足系统需求。
环境准备
- 参照官方文档安装ROS2
- 安装自己开发packages所需依赖
sudo apt install ros-dev-tools
、sudo apt install python3-colcon-common-extensions
- 安装colcon_cd
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/humble/" >> ~/.bashrc
- 自己开发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
- 安装依赖
rosdep install -i --from-path src --rosdistro <distro> -y
- 进行编译
colcon build --symlink-install
。使用``可以减少Python脚本的重复编译
构建时 colcon
会自动遍历 src
目录下所有子文件夹。遵循以下规则:
- 目录必须包含符合 REP 149 规范的
package.xml
和 构建配置文件(CMakeLists.txt 或 setup.py),二者缺一不可,否则会被编译系统忽略,且不报错 - 符号链接:如果目录包含其他目录的符号链接,编译系统会进入对应目录查找,这可能会引起路径导致的编译错误
- 重复包名:如果多个子目录定义了相同的包名,colcon 会报错
实用参数
--packages-select
仅编译指定的包,有多个包名时,使用空格隔开--packages-ignore
排除指定的包--base-paths
从指定的路径开始搜索。默认是从src
开始
# demo
colcon build --packages-select package1 package2 --packages-ignore package_to_skip --base-paths src/subdir
调试
调试的目的是获取运行时数据、查找出错位置,判断出错原因。
常用的调试方案就是打印日志
ros2 bag
用于记录和回放 ROS 2 话题数据的工具rqt_graph
可视化节点和话题的拓扑结构rqt_console
查看和管理ROS 2日志消息ros2doctor
检查ROS 2系统的健康状况- RViz(ROS Visualization Tool)强大的3D可视化工具,支持:机器人模型显示(URDF/SDF);传感器数据(激光雷达、点云、图像等);TF坐标系可视化4。适用场景:调试SLAM、导航、机械臂运动规划等
package 包
pakcage
不能嵌套 package
,workspace
也不能嵌套 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> |
包冲突管理 | 声明与当前包冲突的其他包(极少使用)。 |
依赖传递性规则
默认传递性:
build_depend
和exec_depend
不会自动传递给依赖当前包的其他包。- 若需传递,必须显式声明
build_export_depend
或exec_depend
。
例外:
3. depend
隐含传递性(等同于同时声明 build_export_depend
和 exec_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
批量操作
- 可以使用
ros2 launch
机制一次性启动多个节点,同时完成节点等配置 - 当需要配置的参数很多时,可以使用
ros2 param dump <node|service> > <name_of_storage_file>.yaml
然后使用ros2 run ... --params-files <name_of_storage_file>.yaml
导入参数