ROS2 基础知识学习笔记

该文章已生成可运行项目,

ROS 2 核心概念教程


声明:
本文内容整理自【古月居】古月·ROS2入门21讲和自己的学习笔记

1. 引言

机器人操作系统(ROS,Robot Operating System)是一个用于机器人软件开发的开源框架。ROS 2 是 ROS 的升级版本,提供了更好的性能和灵活性,适用于多种机器人应用场景。

2. 系统架构

2.1 ROS 和 ROS 2 是什么

Robot Operating System,简称为ROS,由通信机制、开发工具、应用功能、生态系统四大部分组成。

ROS 是一个用于机器人软件开发的开源框架,旨在提高机器人软件的复用性。ROS 2 是其升级版本,提供了更好的性能和灵活性,适用于多种机器人应用场景。

ROS也可以跨平台使用,Linux、Windows、嵌入式系统都可以跑。

ROS全球社区有几个重要网站:

网站描述
answers.ros.org一个ROS问答网站,大家可以在上边提出任何关于ROS的问题,全球很多开发者都很乐意回答我们的问题。
wiki.ros.orgROS的维基百科,记录了ROS教程和各种功能包的使用。
discourse.ros.orgROS论坛,关于ROS开发的新鲜事都可以在这里发表和查看,比如ROS的活动、新功能包的发布等等。
index.ros.orgROS各种资源的一个索引网站。
packages.ros.orgROS功能包存储的数据库。

2.2 ROS 2 对比 ROS 1

1. 发展历程
在这里插入图片描述

2. ROS 1 的特点和问题

ROS设计之初 是为了开发一款PR2家庭服务机器人,这款机器人绝大部分时间都是独立工作。具备了以下特点:

  • 工作站级别的计算平台,支持各种复杂的实时运算和处理
  • 良好的网络连接,没有丢数据或者黑客入侵的风险
  • 高昂的成本和售价

当前问题:

  • 要在资源有限的嵌入式系统中运行;
  • 要在有干扰的地方保证通信的可靠性;
  • 要做成产品走向市场,甚至用在自动驾驶汽车和航天机器人上。

当前需求:

  1. 多机器人系统:机器人和机器人之间需要通信和协作
  2. 跨平台:机器人应用场景不同,控制平台也会有差异
  3. 实时性:机器人运动控制和很多行为策略要求机器人具备实时性
  4. 网络连接:无论在怎样的网络环境下,ROS 2都可以尽量保障机器人大量数据的完整性和安全性
  5. 产品化:不仅可以用于机器人研发阶段,还可以直接搭载在产品中,走向消费市场,这对ROS 2的稳定性、强壮性也提除了巨大挑战。
  6. 项目管理:机器人开发是一个复杂的系统工程,设计、开发、调试、测试、部署等全流程的项目管理工具和机制,也会在ROS 2中体现,更方便我们去开发一款机器人。

ROS 2的变化:

架构的颠覆ROS 1架构下,所有节点需要Master进行管理,若Master出现问题,系统就面临宕机的风险
ROS 2使用基于DDS的Discovery机制
API的重新设计ROS 2重新设计了API,且使用方法类似于ROS 1
编译系统升级ROS 1使用rosbuild、catlin管理项目
ROS 2使用ament、colcon

在这里插入图片描述

2.3 ROS 2 安装方法

1. Linux系统简介

Linux是一套免费并且开放源代码的操作系统,是ROS2依赖的重要底层系统,对Linux系统的支持最好
Linux应该叫做操作系统内核,并没有可视化界面,发行版就是给这个内核加上华丽的外衣,把操作界面和各种应用软件放到一起,打包成我们安装系统的镜像。每一个发行版都有其适用的场景,比如RedHat适合商业应用、CentOS适合服务器、Ubuntu、Fedora适合个人使用等
在这里插入图片描述

2. 不同安装方法对比
在这里插入图片描述

3. 基于虚拟机的Ubuntu安装方法

  1. 虚拟机安装

    https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html
    
  2. 下载系统镜像

    https://ubuntu.com/download/desktop
    

    选择带有LTS的版本(长期支持版:5年持续维护更新),其余只有18个月。

  3. 创建系统

    (1)虚拟机安装方法:
    参考教程:

    https://zhuanlan.zhihu.com/p/684460783
    https://blog.csdn.net/qq_42417071/article/details/136327674
    

    相关设置:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

(2)双系统的安装和一些配置可以参考博主的另一篇文章:
https://blog.csdn.net/Master_Zhang_/article/details/147670342?spm=1001.2014.3001.5501

  1. 安装ROS2

    wget http://fishros.com/install -O fishros && . fishros
    

    参考链接:动手安装ROS2

    查看安装位置:

    cd /opt/ros/humble/
    ls
    

    卸载ROS2

    sudo apt remove ros-humble-*
    sudo apt autoremove
    
  2. Ubuntu系统常用设置

    分辨率

    本显示器配置:
    1920*1080   16:9  75Hz 125%缩放
    
    虚拟机分辨率设置:
    -半屏:1280*1024(5:4) 60Hz 125%缩放
    -全屏:1920*1080(16:9) 60Hz 125%缩放
    

2.4 命令行操作

启动终端

在 Ubuntu 系统中,终端是通过字符输入进行命令操作的重要工具。启动终端的方式如下:

  • 方式 1: 在应用列表中打开。

  • 方式 2: 使用快捷键

    Ctrl+Alt+T
    
  • 方式 3: 鼠标右键选择“打开终端”。


Linux 常用命令总结
命令语法功能说明
cdcd <目录路径>改变工作目录,未指定路径时回到用户主目录。
pwdpwd显示当前工作目录的绝对路径。
mkdirmkdir [选项] <目录名称>创建一个目录/文件夹。
touchtouch <文件名称>创建一个空文件。
nanonano <文件名称>使用 nano 编辑器编辑文件。
lsls [选项] [目录名称...]列出目录/文件夹中的文件列表。
geditgedit <文件名称>打开 gedit 编辑器编辑文件,若文件不存在则新建。
mvmv [选项] <源文件或目录> <目标>重命名文件或将文件移动到目标目录。
cpcp [选项] <源文件> <目标文件>复制文件或目录到指定目标。
rmrm [选项] <文件或目录>删除文件或目录,可递归删除子目录内容。
sudosudo [选项] [指令]以管理员权限执行命令。
sourcesource <文件路径>在当前 shell 环境中执行文件中的命令。
echoecho [选项] [字符串]输出字符串到终端或文件。
exportexport <变量名>=<值>设置环境变量。
grepgrep [选项] <模式> <文件>在文件中搜索匹配的模式。
findfind <路径> [选项] [表达式]在指定目录下查找文件。
chmodchmod [选项] <模式> <文件>更改文件或目录的权限。
chownchown [选项] <所有者> <文件>更改文件或目录的所有者。

示例:

  1. 创建文件夹
mkdir -p ~/try_ws/src
cd ~/try_ws/src
ros2 pkg create robot_description --build-type ament_cmake --license Apache-2.0
  1. 创建文件
gedit ~/try_ws/src/robot_description/CMakeLists.txt

ROS2 命令行操作指南

ROS2 的命令行机制与 Linux 类似,但所有操作集成在 ros2 命令下。以下是 ROS2 常用命令及功能。通过 -h--help 的方式查看命令帮助文档。通过 Tab 键可以查看命令的自动补全提示。

ros2 pkg:功能包管理工具
ros2 run:运行功能包节点程序
ros2 node:节点相关命令行工具
ros2 topic:话题通信相关的命令行工具
ros2 interface:接口(msg、srv、action)消息相关的命令行工具ros2 service:服务通信相关的命令行工具
ros2 action:动作通信相关的命令行工具ros2 param:参数服务相关的命令行工具

2.5 核心概念

功能包
ros2 pkg create --build-type <编译类型> <pkg_name> <依赖1> <依赖2>  # 创建功能包
# 编译类型:ament_cmake、ament_python
ros2 pkg list                          # 列出所有功能包
ros2 pkg describe <package_name>       # 查看功能包的详细信息
ros2 pkg executables <package_name>    # 查看功能包的可执行文件

示例:

ros2 pkg create --build-type ament_python pkg_name rclpy std_msgs sensor_msgs
节点
ros2 run <pkg_name> <node_name>         # 运行指定功能包的节点
ros2 node list                          # 列出所有节点
ros2 node info /turtlesim               # 查看指定节点的详细信息

示例:

ros2 run turtlesim turtlesim_node       # 运行海龟仿真节点
ros2 run turtlesim turtle_teleop_key    # 运行键盘控制节点
话题

查看当前系统中的话题:

ros2 topic bw <topic_name>                    # 查看话题的带宽
ros2 topic delay <topic_name>                 # 查看话题的延迟
ros2 topic echo <topic_name>            # 查看话题的消息数据
ros2 topic find <type_name>                   # 查找特定类型的话题
ros2 topic hz <topic_name>                    # 查看话题的发布频率
ros2 topic info <topic_name>                  # 查看话题的详细信息
ros2 topic list                         # 列出所有话题
ros2 topic type <topic_name>                  # 查看话题的接口类型
ros2 topic pub <topic_name> <msg_type> <msg_data>  # 发布话题消息

示例:

ros2 topic list -t                      # 查看所有话题的类型
ros2 topic info /turtle1/cmd_vel        # 查看指定话题的详细信息
ros2 topic echo /turtle1/pose           # 查看指定话题的消息数据
ros2 topic pub --rate 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}" #通过话题指令直接控制海龟移动:
服务
ros2 service list                                                # 查看服务列表
ros2 service type <service_name>                                 # 查看服务数据类型
ros2 service find <type_name>                                    # 查找特定类型的服务
ros2 service call <service_name> <service_type> <service_data>   # 发送服务请求
动作
ros2 action list                  # 查看服务列表
ros2 action info <action_name>    # 查看服务数据类型
ros2 action send_goal <action_name> <action_type> <action_data>  # 发送服务请求,yaml格式

示例:

ros2 interface show turtlesim/action/RotateAbsolute  # 接口展示
接口
ros2 interface list                    # 查看系统接口列表
ros2 interface show <interface_name>   # 查看某个接口的详细定义
ros2 interface package <package_name>  # 查看某个功能包中的接口定义
ros2 interface packages                 # 查看包含接口消息的功能包
ros2 interface proto <interface_name>   # 查看某个接口消息原型
参数
  1. 查看参数列表

    ros2 param list
    
  2. 参数查询与修改

    ros2 param describe <node_name> <parameter_name>   # 查看某个参数的描述信息 
    ros2 param get <node_name> <parameter_name> <value>       # 查询某个参数的值 
    ros2 param set <node_name> <parameter_name> <value>     # 修改某个参数的值
    ros2 param delete <node_name> <parameter_name>     # 删除某个参数
    
  3. 参数文件保存与加载

    #查看节点当前的所有参数值
    ros2 param dump <node_name>
    #查看节点当前的所有参数值
    ros2 param dump <node_name> > <parameter_file>
    #从文件加载参数:
    ros2 param load <node_name> <parameter_file> #通常为yaml文件
    #使用保存的参数值启动同一节点
    ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>
    #例如:
    ros2 run turtlesim turtlesim_node --ros-args --params-file turtlesim.yaml
    
编译
colcon build  # 编译工作空间中的所有功能包
colcon build --packages-select <package_1> <package_2>  # 编译指定功能包
其它

录制与播放控制命令

使用 rosbag 录制与回放 ROS2 数据:

ros2 bag record /turtle1/cmd_vel        # 录制话题数据
ros2 bag play <rosbag文件路径>           # 播放录制的数据

2.6 ROS 2 开发环境配置

1. 安装git
sudo apt install git #安装git
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git #下载教程
2. VSCode 插件配置指南

推荐插件列表

插件名称功能说明
中文语言包为 VSCode 提供中文支持,方便中文用户使用。
Python 插件提供 Python 语言支持,包括代码补全、调试、Linting 等功能,适用于 Python 开发。
C++ 插件提供 C++ 语言支持,包括语法高亮、调试、代码补全等功能。
CMake 插件提供 CMake 语法支持,用于管理和构建 C++ 项目。
vscode-icons添加美观的文件图标,便于快速识别不同类型的文件。
ROS 插件专为 ROS/ROS2 开发设计的插件,支持启动文件、话题、服务的可视化与管理。
Msg Language Support为 ROS 消息文件(.msg)和服务文件(.srv)提供语法高亮与支持。
Visual Studio IntelliCode使用 AI 提供智能代码补全建议,提高编程效率。
URDF 插件为 ROS 中的 URDF 文件提供语法支持,便于编辑机器人描述文件。
Markdown All in One提供 Markdown 文件支持,包括语法高亮、预览、表格生成等功能,适用于文档书写与管理。

vscode常用命令

功能快捷键
新建终端Ctrl+Shift+
打开终端`Ctrl+``
关闭终端Ctrl+Shift+W
切换终端Ctrl+PageUp/PageDown
查找Ctrl+F
替换Ctrl+H
全局查找Ctrl+Shift+F
全局替换Ctrl+Shift+H
打开命令面板Ctrl+Shift+P
打开设置Ctrl+,
打开扩展Ctrl+Shift+X
格式化代码Shift+Alt+F
代码折叠Ctrl+Shift+[
代码展开Ctrl+Shift+]
注释代码Ctrl+/
多行注释Shift+Alt+A
跳转到定义F12
查找所有引用Shift+F12
重命名符号F2
显示大纲Ctrl+Shift+O
显示问题Ctrl+Shift+M
显示调试控制台Ctrl+Shift+Y
启动调试F5
停止调试Shift+F5
单步调试F10
进入函数F11
跳出函数Shift+F11
切换全屏F11
切换 Zen 模式Ctrl+K Z
切换侧边栏Ctrl+B
切换活动栏Ctrl+Shift+B
切换面板Ctrl+J
切换编辑器布局Ctrl+Shift+P,输入 View: Toggle Editor Group Layout
切换文件图标主题Ctrl+Shift+P,输入 Preferences: File Icon Theme
切换颜色主题Ctrl+Shift+P,输入 Preferences: Color Theme
切换键盘快捷键Ctrl+Shift+P,输入 Preferences: Open Keyboard Shortcuts
切换设置同步Ctrl+Shift+P,输入 Preferences: Turn On Settings Sync
切换工作区Ctrl+Shift+P,输入 Workspaces: Open Workspace
切换窗口Ctrl+Shift+P,输入 View: Switch Window
切换活动栏Ctrl+Shift+B
切换面板Ctrl+J
3. 常用python包的安装
# pip源设置
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# pip源查看
pip config list

#安装opencv
sudo apt install python3-opencv

#安装三维几何变换的包
sudo pip3 install transforms3d

# 语音合成
pip install espeakng

# 资源监视器
pip install nvitop
4. 常用ROS包,软件,依赖的安装
export ROS_DISTRO=humble
echo $ROS_DISTRO
#安装ros相机驱动
sudo apt install ros-humble-usb-cam

#安装turtle的tf工具
sudo apt install ros-humble-turtle-tf2-py ros-humble-tf2-tools
sudo apt install ros-humble-tf-transformations

#安装gazebo相关的所有包
sudo apt install ros-humble-gazebo-*

#安装rqt(humble版本)
sudo apt install ros-humble-rqt


#显示文件夹结构
sudo apt install tree

# 
sudo apt install net-tools
sudo apt install ros-$ROS_DISTRO-joint-state-publisher-gui ros-$ROS_DISTRO-robot-state-publisher
sudo apt install ros-$ROS_DISTRO-xacro
sudo apt install ros-$ROS_DISTRO-gazebo-ros-pkgs

#控制器插件
sudo apt install ros-$ROS_DISTRO-ros2-control ros-$ROS_DISTRO-ros2-controllers 
sudo apt install ros-$ROS_DISTRO-gazebo-ros2-control

#安装
sudo apt-get install ros-$ROS_DISTRO-navigation2  #安装导航包
sudo apt-get install ros-$ROS_DISTRO-nav2-bringup  #安装启动包

#安装slam_toolbox
sudo apt-get install ros-$ROS_DISTRO-slam-toolbox
sudo apt-get install ros-$ROS_DISTRO-nav2-map-server

#语言包
sudo apt install espeak-ng

#pluginlib
sudo apt-get install ros-$ROS_DISTRO-pluginlib -y

# 更新软件列表
sudo apt-get update
# 安装g++
sudo apt-get install g++
# 安装gcc
sudo apt-get install gcc
# 安装make
sudo apt-get install make

3. 核心概念

3.1 工作空间

什么是工作空间
工作空间是 ROS 系统中用于存放项目开发相关文件的文件夹,是整个开发过程中所有代码、参数、脚本等资料的“大本营”。每个工作空间包含开发中产生的所有必要文件。

  • 本质: 工作空间就是一个管理项目文件的文件夹。
  • 功能: 用于组织机器人开发的代码、参数配置、日志等。
  • 自定义: 工作空间的名称可以自定义,且数量不唯一。

示例:

  • 工作空间1:dev_ws_a - 用于 A 机器人的开发。
  • 工作空间2:dev_ws_b - 用于 B 机器人的某些功能开发。
  • 工作空间3:dev_ws_b2 - 用于 B 机器人的其他功能开发。

工作空间的典型结构

在这里插入图片描述

一个典型的工作空间结构如下:

dev_ws/
├── src     # 代码空间,存放功能包源码
├── build   # 编译空间,保存编译过程中的缓存信息和中间文件
├── install # 安装空间,存放编译后生成的可执行文件与脚本
├── log     # 日志空间,存放编译和运行时的日志信息

使用说明

  • 开发和操作主要集中在 src 目录。
  • 编译成功后,运行的是 install 目录中的可执行文件。
  • buildlog 用于编译过程和日志记录,日常使用较少。

创建工作空间

mkdir -p ~/dev_ws/src
cd ~/dev_ws/src
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git

自动安装依赖
我们从社区中下载的各种代码,多少都会有一些依赖,我们可以手动一个一个安装,也可以使用rosdep工具自动安装:

sudo apt install -y python3-pip
sudo pip3 install rosdepc
sudo rosdepc init
rosdepc update
cd ..
rosdepc install -i --from-path src --rosdistro humble -y

编译工作空间
依赖安装完成后,就可以使用如下命令编译工作空间啦,如果有缺少的依赖,或者代码有错误,编译过程中会有报错,否则编译过程应该不会出现任何错误:

sudo apt install python3-colcon-ros
cd ~/dev_ws/
colcon build

编译成功后,就可以在工作空间中看到自动生产的build、log、install文件夹了。

设置环境变量
编译成功后,为了让系统能够找到我们的功能包和可执行文件,还需要设置环境变量:

source install/local_setup.sh # 仅在当前终端生效
echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效

3.2 功能包

功能包是什么

功能包是 ROS 中的核心机制,用于组织机器人不同功能的代码,方便管理与复用。

功能包的作用

  • 模块化管理: 将机器人不同功能(如移动控制、视觉感知、自主导航)的源码分开存放,降低耦合性。
  • 提高复用性: 功能包可以单独分享,只需说明如何使用,其他开发者即可快速集成。
  • 便于维护: 不同功能的代码分散存储,便于维护和调试。

功能包的意义

举个例子:

  • 如果把所有代码混合存放(如将红豆、绿豆、黄豆混在一起),很难在需要时快速提取某一功能代码。
  • 而将代码划分为不同的功能包(如分别存放红豆、绿豆、黄豆),在需要时可以快速找到并提取对应功能代码。

创建功能包

在 ROS2 中,可以通过以下命令创建功能包:

ros2 pkg create --build-type <build-type> <package_name>

参数说明

  • pkg: 表示功能包相关的功能。

  • create: 表示创建功能包。

  • build-type : 指定功能包的构建类型:

    • ament_cmake: 用于 C++ 或 C。
    • ament_python: 用于 Python。
  • package_name: 新建功能包的名称。

示例
在终端中分别创建 C++ 和 Python 版本的功能包:

cd ~/dev_ws/src
ros2 pkg create --build-type ament_cmake learning_pkg_c               # C++
ros2 pkg create --build-type ament_python learning_pkg_python         # Python

#编译
cd ~/dev_ws
colcon build   # 编译工作空间中的所有功能包
source install/local_setup.bash  # 配置环境变量

功能包的结构
功能包不是普通的文件夹,判断某文件夹是否为功能包的关键在于其包含的文件结构。以下是功能包的基本结构及特点:

C++ 功能包
C++ 功能包中,必然包含以下两个文件:

  1. package.xml: 功能包的描述文件,主要内容包括:
    • 功能包的名称、版本号、描述信息。
    • 版权声明。
    • 各种依赖的声明。
  2. CMakeLists.txt: 功能包的编译规则文件,使用 CMake 语法,指定如何编译 C++ 源代码。

Python 功能包

Python 功能包无需编译,其主要文件为:

  1. package.xml: 功能包的描述文件,内容与 C++ 功能包类似。

  2. setup.py
    功能包的安装和入口配置文件,主要内容包括:
    • 版权声明。
    • entry_points 配置,指定程序入口。

  1. scripts:存储脚本文件,例如python源码或.sh脚本
  2. src: 存储C++源文件
  3. include:存储.h头文件
  4. launch:存储启动文件,可一次性运行多个节点
  5. config:存储配置信息

3.3 节点

节点是什么

机器人是各种功能的综合体,每一项功能就像机器人的一个工作细胞,众多细胞通过一些机制连接到一起,成为了一个机器人整体。在 ROS 中,这些“细胞”被称为 节点

  • 职责: 节点的职责是执行某些具体的任务。从计算机操作系统的角度来看,节点可以看作进程。
  • 语言支持: 每个节点是一个独立的可执行文件,可以由 C++、Python、Java、Ruby 等多种语言编写。
  • 分布式系统: 节点可以分布在不同的硬件设备上,例如计算机 A、计算机 B,甚至云端。
  • 命名: 每个节点都有唯一的名称,用于查询或访问其状态。

类比

节点可以比喻为一个个工人,各自完成不同的任务。有的工人在一线厂房工作,有的在后勤保障,虽然互相不认识,但通过合作推动整个“机器人工厂”的运行。

实现流程

  • 编程接口初始化。
  • 创建节点并初始化。
  • 实现节点功能。
  • 销毁节点并关闭接口。

案例一:Hello World 节点(面向过程)

循环打印“Hello World”字符串:

$ ros2 run learning_node node_helloworld

代码解析

~/dev_ws/src/ros2_21_tutorials/learning_node/node_helloworld.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy                                   # ROS2 Python接口库
from rclpy.node import Node                    # ROS2 节点类
import time

def main(args=None):                           # ROS2节点主入口main函数
    rclpy.init(args=args)                      # ROS2 Python接口初始化
    node = Node("node_helloworld")             # 创建ROS2节点对象并进行初始化

    while rclpy.ok():                          # ROS2系统是否正常运行
        node.get_logger().info("Hello World")  # ROS2日志输出
        time.sleep(0.5)                        # 休眠控制循环时间

    node.destroy_node()                        # 销毁节点对象    
    rclpy.shutdown()                           # 关闭ROS2 Python接口

入口配置
打开功能包的 ~/dev_ws/src/ros2_21_tutorials/learning_node/setup.py 文件,添加入口点:

entry_points={
    'console_scripts': [
        'node_helloworld = learning_node.node_helloworld:main',
    ],
}

案例二:Hello World 节点(面向对象)

循环打印“Hello World”字符串:

ros2 run learning_node node_helloworld_class

代码解析

~/dev_ws/src/ros2_21_tutorials/learning_node/node_helloworld_class.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
import time

#创建一个HelloWorld节点, 初始化时输出“hello world”日志
class HelloWorldNode(Node):
    def __init__(self, name):
        super().__init__(name)                     # ROS2节点父类初始化
        while rclpy.ok():                          # ROS2系统是否正常运行
            self.get_logger().info("Hello World")  # ROS2日志输出
            time.sleep(0.5)                        # 休眠控制循环时间

def main(args=None):                               # ROS2节点主入口main函数
    rclpy.init(args=args)                          # ROS2 Python接口初始化
    node = HelloWorldNode("node_helloworld_class") # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                               # 循环等待ROS2退出
    node.destroy_node()                            # 销毁节点对象
    rclpy.shutdown()                               # 关闭ROS2 Python接口

入口配置
打开功能包的 ~/dev_ws/src/ros2_21_tutorials/learning_node/setup.py 文件,添加入口点:

entry_points={
    'console_scripts': [
     'node_helloworld       = learning_node.node_helloworld:main',
     'node_helloworld_class = learning_node.node_helloworld_class:main',
    ],
}

节点并不是孤立的,他们之间会有很多种机制保持联系


话题、服务、动作对比


机制描述适用场景
话题节点通过发布和订阅话题进行异步通信,适用于数据流较大的场景。传感器数据、机器人状态
服务节点通过请求和响应进行同步通信,适用于需要即时反馈的场景。查询节点状态、快速计算、参数查询
动作节点通过发送目标和接收反馈进行长时间任务的通信,适用于复杂任务控制。机器人导航,机械臂操作

在这里插入图片描述

在这里插入图片描述

3.4 话题

话题是什么?

节点实现了机器人的各种功能,但这些功能之间并不是独立的,往往需要数据交换。在 ROS 中,节点之间传递数据的桥梁被称为 话题

  • 单向传输: 话题的通信模型是单向的,从一个节点到另一个节点。
  • 异步通信: 发布者和订阅者无需同时在线,数据发布和接收可以是异步的。

在这里插入图片描述

以两个节点为例:

  • 节点 A: 驱动相机,获取图像数据。
  • 节点 B: 视频监控,显示节点 A 发布的图像数据。

这种从节点 A 到节点 B 的图像数据传输方式,称为 话题通信。话题是节点间实现单向数据传输的桥梁。

话题使用**.msg文件**定义


发布/订阅模型

话题基于 DDS(Data Distribution Service) 实现了发布/订阅模型:

  • 发布者: 发送数据的节点。
  • 订阅者: 接收数据的节点。
  • 话题名称: 每个话题都有唯一的名称,用于区分不同数据流。
  • 数据类型: 传输的数据有固定的数据格式,如 std_msgs/Stringsensor_msgs/Image

类比

  • 一个微信公众号的名字相当于话题名称。
  • 发布者是公众号小编,编辑和发布文章(消息)。
  • 订阅者订阅公众号,接收文章。

话题通信特点

  1. 多对多通信:
    • 一个话题可以有多个发布者和多个订阅者。
    • 例如,多个摇杆节点可以控制一个机器人,也可以同时控制多个机器人。
  2. 异步通信:
    • 发布者不知道订阅者何时接收消息,消息发送后发布者可以继续运行。
    • 更适合周期性数据传输,如传感器数据或运动控制指令。
  3. 消息接口:
    • 数据需要有统一的格式描述,称为 消息(Message),类似编程语言中的数据结构。
    • 消息可以使用 .msg 文件自定义,也可以使用 ROS 标准消息类型。

如何实现话题通信?

发布者订阅者
1. 编程接口初始化1. 编程接口初始化
2. 创建节点并初始化2. 创建节点并初始化
3. 创建发布者对象3. 创建订阅者对象
4. 创建并填充话题消息4. 回调函数处理话题数据
5. 发布话题消息5. 销毁节点并关闭接口
6. 销毁节点并关闭接口

案例一:Hello World 话题通信

目标

创建一个发布者节点和一个订阅者节点,通过话题 “chatter” 实现字符串 “Hello World” 的通信。消息类型为 std_msgs/String
在这里插入图片描述


运行

  1. 启动发布者节点:
    $ ros2 run learning_topic topic_helloworld_pub
    
  2. 启动订阅者节点
    $ ros2 run learning_topic topic_helloworld_sub
    

代码解析

  1. 发布者

learning_topic/topic_helloworld_pub.py

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
from std_msgs.msg import String                  # 字符串消息类型

#创建一个发布者节点
class PublisherNode(Node):

    def __init__(self, name):
        super().__init__(name)                                    # ROS2节点父类初始化
        self.pub = self.create_publisher(String, "chatter", 10)   # 创建发布者对象(消息类型、话题名、队列长度)
        self.timer = self.create_timer(0.5, self.timer_callback)  # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
    
    def timer_callback(self):                                     # 创建定时器周期执行的回调函数
        msg = String()                                            # 创建一个String类型的消息对象
        msg.data = 'Hello World'                                  # 填充消息对象中的消息数据
        self.pub.publish(msg)                                     # 发布话题消息
        self.get_logger().info('Publishing: "%s"' % msg.data)     # 输出日志信息,提示已经完成话题发布

def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = PublisherNode("topic_helloworld_pub")     # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                 # 循环等待ROS2退出
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

setup.py:

entry_points={
    'console_scripts': [
     'topic_helloworld_pub  = learning_topic.topic_helloworld_pub:main',
    ],
},
  1. 订阅者

learning_topic/topic_helloworld_sub.py

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import rclpy                      # ROS2 Python接口库
from rclpy.node   import Node     # ROS2 节点类
from std_msgs.msg import String   # ROS2标准定义的String消息

#创建一个订阅者节点
class SubscriberNode(Node):

    def __init__(self, name):
        super().__init__(name)                             # ROS2节点父类初始化
        self.sub = self.create_subscription(\
            String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
    
    def listener_callback(self, msg):                      # 创建回调函数,执行收到话题消息后对数据的处理
        self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息

def main(args=None):                               # ROS2节点主入口main函数
    rclpy.init(args=args)                          # ROS2 Python接口初始化
    node = SubscriberNode("topic_helloworld_sub")  # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                               # 循环等待ROS2退出
    node.destroy_node()                            # 销毁节点对象
    rclpy.shutdown()                               # 关闭ROS2 Python接口

setup.py:

entry_points={
    'console_scripts': [
     'topic_helloworld_pub  = learning_topic.topic_helloworld_pub:main',
     'topic_helloworld_sub  = learning_topic.topic_helloworld_sub:main',
    ],
},

3.5 服务

服务是什么

服务是一种实现 节点之间同步通信 的机制,通过“请求 - 应答”的方式进行数据交换。
与话题的单向异步通信不同,服务基于 客户端/服务器(CS)模型

服务使用的是**.srv文件**定义

在这里插入图片描述

特点

  1. 同步通信: 客户端等待服务器返回结果。
  2. 一对多通信: 一个服务器可以服务多个客户端。
  3. 服务接口:
    • 数据分为 请求数据反馈数据
    • 定义文件使用 .srv,类似话题的 .msg 文件。

服务的实现方法

客户端服务端
编程接口初始化编程接口初始化
创建节点并初始化创建节点并初始化
创建客户端对象创建服务器端对象
创建并发送请求数据通过回调函数处理服务
等待服务器端应答数据向客户端反馈应答结果
销毁节点并关闭接口销毁节点并关闭接口

案例一:加法求解器

目标

实现一个服务,用于加法计算:

  • 客户端: 发送两个加数的请求。
  • 服务器: 接收请求并返回加法结果。

运行效果

  1. 启动服务端节点:

    ros2 run learning_service service_adder_server
    
  2. 启动客户端节点:

    $ ros2 run learning_service service_adder_client 2 3
    

在这里插入图片描述

客户端代码

learning_service/service_adder_client.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#说明: ROS2服务示例-发送两个加数,请求加法器计算
import sys
import rclpy                                  # ROS2 Python接口库
from rclpy.node   import Node                 # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口

class adderClient(Node):
    def __init__(self, name):
        super().__init__(name)                                       # ROS2节点父类初始化
        self.client = self.create_client(AddTwoInts, 'add_two_ints') # 创建服务客户端对象(服务接口类型,服务名)
        while not self.client.wait_for_service(timeout_sec=1.0):     # 循环等待服务器端成功启动
            self.get_logger().info('service not available, waiting again...') 
        self.request = AddTwoInts.Request()                          # 创建服务请求的数据对象

    def send_request(self):                                          # 创建一个发送服务请求的函数
        self.request.a = int(sys.argv[1])
        self.request.b = int(sys.argv[2])
        self.future = self.client.call_async(self.request)           # 异步方式发送服务请求

def main(args=None):
    rclpy.init(args=args)                        # ROS2 Python接口初始化
    node = adderClient("service_adder_client")   # 创建ROS2节点对象并进行初始化
    node.send_request()                          # 发送服务请求

    while rclpy.ok():                            # ROS2系统正常运行
        rclpy.spin_once(node)                    # 循环执行一次节点

        if node.future.done():                   # 数据是否处理完成
            try:
                response = node.future.result()  # 接收服务器端的反馈数据
            except Exception as e:
                node.get_logger().info(
                    'Service call failed %r' % (e,))
            else:
                node.get_logger().info(          # 将收到的反馈信息打印输出
                    'Result of add_two_ints: for %d + %d = %d' % 
                    (node.request.a, node.request.b, response.sum))
            break

    node.destroy_node()                          # 销毁节点对象
    rclpy.shutdown()                             # 关闭ROS2 Python接口

setup.py:

entry_points={
    'console_scripts': [
     'service_adder_client  = learning_servicservice_adder_client:main',
    ],
}

服务端代码

learning_service/service_adder_server.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-提供加法器的服务器处理功能
"""

import rclpy                                     # ROS2 Python接口库
from rclpy.node   import Node                    # ROS2 节点类
from learning_interface.srv import AddTwoInts    # 自定义的服务接口

class adderServer(Node):
    def __init__(self, name):
        super().__init__(name)                                                           # ROS2节点父类初始化
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback)  # 创建服务器对象(接口类型、服务名、服务器回调函数)

    def adder_callback(self, request, response):   # 创建回调函数,执行收到请求后对数据的处理
        response.sum = request.a + request.b       # 完成加法求和计算,将结果放到反馈的数据中
        self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))   # 输出日志信息,提示已经完成加法求和计算
        return response                          # 反馈应答信息

def main(args=None):                             # ROS2节点主入口main函数
    rclpy.init(args=args)                        # ROS2 Python接口初始化
    node = adderServer("service_adder_server")   # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                             # 循环等待ROS2退出
    node.destroy_node()                          # 销毁节点对象
    rclpy.shutdown()                             # 关闭ROS2 Python接口

setup.py:

entry_points={
    'console_scripts': [
     'service_adder_client  = learning_servicservice_adder_client:main',
     'service_adder_server  = learning_servicservice_adder_server:main',
    ],
},

3.6 通信接口

3.7 动作

动作是什么

动作是一种 应用层通信机制,适合对机器人 完整行为流程 进行管理,比如旋转到某个角度、移动到某个目标点等。动作通信支持 进度反馈终止控制,让用户可以更好地管理行为执行。

动作使用的也是客户端和服务器模型。客户端发送动作的目标,服务器端执行动作过程,控制机器人达到运动的目标,同时周期反馈动作执行过程中的状态。

动作的底层是基于话题和服务来实现的。
在这里插入图片描述

特点

  1. 同步通信: 支持实时反馈进度和动作完成状态。
  2. 一对多通信: 一个服务器可以服务多个客户端。
  3. 底层实现: 动作是基于 服务和话题 的组合通信机制:
    • 服务: 用于发送目标和接收结果。
    • 话题: 用于发布动作的周期反馈。
案例一:小海龟动作

通过动作接口控制小海龟的旋转行为:

运行效果

  1. 启动小海龟仿真器:

    ros2 run turtlesim turtlesim_node
    
  2. 发送动作目标

    ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: -1.57}" --feedback
    
案例二:机器人画圆

动作虽然是基于话题和服务实现的,但在实际使用中,并不会直接使用话题和服务的编程方法,而是有一套针对动作特性封装好的编程接口

  1. 启动

    ros2 run learning_action action_move_server 
    ros2 run learning_action action_move_client
    
  2. 实现原理

    动作接口定义

    learning_interface/action/MoveCircle.action:

    bool enable     # 动作目标,true 表示开始执行动作
    
    bool finish     # 动作结果,true 表示完成动作
    
    int32 state     # 动作反馈,当前旋转角度
    

    定义完成后,在 CMakeLists.txt 中添加以下配置,让编译器生成接口相关代码:

    find_package(rosidl_default_generators REQUIRED)
    
    rosidl_generate_interfaces(${PROJECT_NAME}
      "action/MoveCircle.action"
    )
    

    服务端代码解析

    learning_action/action_move_server.py:

    #!/usr/bin/env python3
    
    # -*- coding: utf-8 -*-
    #说明: ROS2动作示例-负责执行圆周运动动作的服务端
    
    import time
    import rclpy                                      # ROS2 Python接口库
    from rclpy.node   import Node                     # ROS2 节点类
    from rclpy.action import ActionServer             # ROS2 动作服务器类
    from learning_interface.action import MoveCircle  # 自定义的圆周运动接口
    
    class MoveCircleActionServer(Node):
        def __init__(self, name):
            super().__init__(name)                   # ROS2节点父类初始化
            self._action_server = ActionServer(      # 创建动作服务器(接口类型、动作名、回调函数)
                self,
                MoveCircle,
                'move_circle',
                self.execute_callback)
    
        def execute_callback(self, goal_handle):            # 执行收到动作目标之后的处理函数
            self.get_logger().info('Moving circle...')
            feedback_msg = MoveCircle.Feedback()            # 创建一个动作反馈信息的消息
        
            for i in range(0, 360, 30):                     # 从0到360度,执行圆周运动,并周期反馈信息
                feedback_msg.state = i                      # 创建反馈信息,表示当前执行到的角度
                self.get_logger().info('Publishing feedback: %d' % feedback_msg.state)
                goal_handle.publish_feedback(feedback_msg)  # 发布反馈信息
                time.sleep(0.5)
        
            goal_handle.succeed()                           # 动作执行成功
            result = MoveCircle.Result()                    # 创建结果消息
            result.finish = True                            
            return result                                   # 反馈最终动作执行的结果
    
    def main(args=None):                                    # ROS2节点主入口main函数
        rclpy.init(args=args)                               # ROS2 Python接口初始化
        node = MoveCircleActionServer("action_move_server") # 创建ROS2节点对象并进行初始化
        rclpy.spin(node)                                    # 循环等待ROS2退出
        node.destroy_node()                                 # 销毁节点对象
        rclpy.shutdown()                                    # 关闭ROS2 Python接口
    

    setup.py:

     entry_points={
       'console_scripts': [
       'action_move_server = learning_action.action_move_server:main',
       ],
     },
    

    客户端代码解析
    learning_action/action_move_client.py:

    #!/usr/bin/env python3
    
    # -*- coding: utf-8 -*-
    #说明: ROS2动作示例-请求执行圆周运动动作的客户端
    
    import rclpy                                      # ROS2 Python接口库
    from rclpy.node   import Node                     # ROS2 节点类
    from rclpy.action import ActionClient             # ROS2 动作客户端类
    
    from learning_interface.action import MoveCircle  # 自定义的圆周运动接口
    
    class MoveCircleActionClient(Node):
        def __init__(self, name):
            super().__init__(name)                   # ROS2节点父类初始化
            self._action_client = ActionClient(      # 创建动作客户端(接口类型、动作名)
                self, MoveCircle, 'move_circle') 
    
        def send_goal(self, enable):                 # 创建一个发送动作目标的函数
            goal_msg = MoveCircle.Goal()             # 创建一个动作目标的消息
            goal_msg.enable = enable                 # 设置动作目标为使能,希望机器人开始运动
        
            self._action_client.wait_for_server()    # 等待动作的服务器端启动
            self._send_goal_future = self._action_client.send_goal_async(   # 异步方式发送动作的目标
                goal_msg,                                                   # 动作目标
                feedback_callback=self.feedback_callback)                   # 处理周期反馈消息的回调函数
        
            self._send_goal_future.add_done_callback(self.goal_response_callback) # 设置一个服务器收到目标之后反馈时的回调函数
        
        def goal_response_callback(self, future):           # 创建一个服务器收到目标之后反馈时的回调函数
            goal_handle = future.result()                   # 接收动作的结果
            if not goal_handle.accepted:                    # 如果动作被拒绝执行
                self.get_logger().info('Goal rejected :(')
                return
        
            self.get_logger().info('Goal accepted :)')                            # 动作被顺利执行
        
            self._get_result_future = goal_handle.get_result_async()              # 异步获取动作最终执行的结果反馈
            self._get_result_future.add_done_callback(self.get_result_callback)   # 设置一个收到最终结果的回调函数 
        
        def get_result_callback(self, future):                                    # 创建一个收到最终结果的回调函数
            result = future.result().result                                       # 读取动作执行的结果
            self.get_logger().info('Result: {%d}' % result.finish)                # 日志输出执行结果
        
        def feedback_callback(self, feedback_msg):                                # 创建处理周期反馈消息的回调函数
            feedback = feedback_msg.feedback                                      # 读取反馈的数据
            self.get_logger().info('Received feedback: {%d}' % feedback.state) 
    
    def main(args=None):                                       # ROS2节点主入口main函数
        rclpy.init(args=args)                                  # ROS2 Python接口初始化
        node = MoveCircleActionClient("action_move_client")    # 创建ROS2节点对象并进行初始化
        node.send_goal(True)                                   # 发送动作目标
        rclpy.spin(node)                                       # 循环等待ROS2退出
        node.destroy_node()                                    # 销毁节点对象
        rclpy.shutdown()                                       # 关闭ROS2 Python接口
     
    

    setup.py:

     entry_points={
         'console_scripts': [
          'action_move_client = learning_action.action_move_client:main',
          'action_move_server = learning_action.action_move_server:main',
         ],
     },  
    

3.8 参数

什么是参数?

参数是一种常用的数据传输方式,类似C++编程中的全局变量,可以便于在多个程序中共享某些数据。

参数是ROS机器人系统中的全局字典,可以运行多个节点中共享数据。

参数的特性

  1. 全局字典:参数由名称(键)和数值(值)组成,节点间可以通过访问参数名称获取对应的值。
  2. 动态监控**:当参数的值被修改,其他节点可以实时检测到并使用新值。
  3. 参数文件:参数可以保存到 YAML 文件中,也可以从 YAML 文件加载。
案例一:小海龟的参数
  1. 启动仿真器和键盘控制节点

    ros2 run turtlesim turtlesim_node 
    ros2 run turtlesim turtle_teleop_key
    
  2. 查看参数列表

    ros2 param list
    
  3. 参数查询与修改

    ros2 param describe turtlesim background_b   # 查看某个参数的描述信息 
    ros2 param get turtlesim background_b        # 查询某个参数的值 
    ros2 param set turtlesim background_b 10     # 修改某个参数的值
    
  4. 参数文件保存与加载

    #保存当前节点的参数到文件:
    ros2 param dump turtlesim >> turtlesim.yaml
     
    #从文件加载参数:
    ros2 param load turtlesim turtlesim.yaml
    

3.9 分布式通信

什么是分布式通信?

在 ROS 系统中,机器人功能由多个节点组成,这些节点可以分布在不同的计算平台上,形成分布式系统。通过这种方式,可以将资源密集型任务分配到性能更强的计算机中,从而提高系统的整体效率。

示例场景

以一个双平台的机器人系统为例:

  • 树莓派:用于传感器驱动和电机控制。
  • 笔记本电脑:用于视觉处理和高级应用功能。

分布式网络分组

为了限制通信范围,可以使用 ROS_DOMAIN_ID 机制,将不同计算机分配到不同的分组中。

配置方法
在电脑和树莓派端的.bashrc 中加入这样一句配置:

export ROS_DOMAIN_ID=<your_domain_id>

只有相同的 DOMAIN_ID才可以通信。

3.10 DDS

什么是DDS?

DDS(Data Distribution Service,数据分发服务)核心功能是实现以数据为中心的通信机制。ROS2 系统中的话题、服务、动作等通信都基于 DDS 实现。

  • 数据为中心:以数据分发为核心设计。
  • 服务质量(QoS)策略:支持实时、高效、灵活的通信需求。
  • 分布式实时通信:满足复杂系统中高频通信的需求。

DDS 提供了一种高效的通信方式,与传统的通信模型相比具有显著优势:

模型描述
点对点模型通信双方必须建立连接,节点增多时连接数激增,且客户端需知道服务器地址。
Broker 模型中央 Broker 处理所有请求,但性能受限,单点故障可能导致整个系统瘫痪(ROS1 使用该模型)。
广播模型所有节点均可接收广播消息,但消息冗余多,节点需处理所有消息。
DDS 模型提供 DataBus 数据总线,仅订阅感兴趣的数据,支持高效并行,通信双方无需建立直接连接。

DDS 的核心功能

  1. Domain 分组通信
    通过 DOMAIN_ID 将节点分组,仅同组节点能互相通信,避免无关数据干扰。

  2. 服务质量(QoS)策略
    QoS 是 DDS 的核心功能,是一种网络传输策略,用于指定通信的传输质量要求。常见策略包括:

  • DEADLINE:通信数据需在截止时间内完成传输。
  • HISTORY:定义历史数据的缓存大小。
  • RELIABILITY:传输模式,包括 BEST_EFFORT(尽力传输,可能丢失数据) RELIABLE(保证数据完整性)。
  • DURABILITY:确保晚加入节点可接收到历史数据。
  1. 应用场景
  • 无人机遥控:命令采用 RELIABLE 模式,保证每个指令送达;视频流采用 BEST_EFFORT 模式,确保流畅。
  • 数据加密:DDS 支持通信数据加密,提升安全性。
案例一:命令行配置 DDS
  1. 启动发布者节点,使用 BEST_EFFORT 模式:

    ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42" --qos-reliability best_effort
    
  2. 启动订阅者节点,使用 RELIABLE 模式(无法接收数据):

    ros2 topic echo /chatter --qos-reliability reliable
    
  3. 修改订阅者为 BEST_EFFORT 模式(成功接收数据):

    ros2 topic echo /chatter --qos-reliability best_effort
    

4. 常用工具

4.1 Launch

Launch 简介

ROS系统中多节点启动与配置的一种脚本

  1. 启动

    ros2 launch learning_launch simple.launch.py

  2. 原理分析

    示例1
    learning_launch/simple.launch.py:

    
    from launch import LaunchDescription           # launch文件的描述类
    from launch_ros.actions import Node            # 节点启动的描述类
    
    def generate_launch_description():             # 自动生成launch文件的函数
        return LaunchDescription([                 # 返回launch文件的描述信息
            Node(                                  # 配置一个节点的启动
                package='learning_topic',          # 节点所在的功能包
                executable='topic_helloworld_pub', # 节点的可执行文件
            ),
            Node(                                  # 配置一个节点的启动
                package='learning_topic',          # 节点所在的功能包
                executable='topic_helloworld_sub', # 节点的可执行文件名
            ),
        ])   
    

    示例2
    learning_launch/rviz.launch.py:

    import os
    
    from ament_index_python.packages import get_package_share_directory   # 查询功能包路径的方法
    from launch import LaunchDescription                                  # launch文件的描述类
    from launch_ros.actions import Node                                   # 节点启动的描述类
    
    def generate_launch_description():                                    # 自动生成launch文件的函   数
      rviz_config = os.path.join(                                         # 找到配置文件的完整路径
        get_package_share_directory('learning_launch'),
        'rviz',
        'turtle_rviz.rviz'
      )
    
      return LaunchDescription([                                    # 返回launch文件的描述信息
        Node(                                                       # 配置一个节点的启动
          package='rviz2',                                          # 节点所在的功能包
          executable='rviz2',                                       # 节点的可执行文件名
          name='rviz2',                                             # 对节点重新命名
          arguments=['-d', rviz_config]                             # 加载命令行参数
        )
      ])
    

资源重映射

  1. 启动

    ros2 launch learning_launch remapping.launch.py
    ros2 topic pub --rate 1 /turtlesim1/turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"            #转圈
    
    

    通过话题重映射实现同步运动
    在这里插入图片描述

  2. 原理分析

learning_launch/remapping.launch.py:

from launch import LaunchDescription      # launch文件的描述类
from launch_ros.actions import Node       # 节点启动的描述类

def generate_launch_description():        # 自动生成launch文件的函数
    return LaunchDescription([            # 返回launch文件的描述信息
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim1',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim2',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            executable='mimic',           # 节点的可执行文件名
            name='mimic',                 # 对节点重新命名
            remappings=[                  # 资源重映射列表
                ('/input/pose', '/turtlesim1/turtle1/pose'), # 将/input/pose话题名为/turtlesim1/turtle1/pose
                ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),  # 将/output/cmd_vel话题名修改为/turtlesim2/turtle1/cmd_vel
            ]
        )
    ])

在 setup.py 中添加如下配置以支持 Launch 文件:

data_files=[
    ('share/ament_index/resource_index/packages',
        ['resource/' + package_name]),
    ('share/' + package_name, ['package.xml']),
    (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
    (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
    (os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.*'))),
],

4.2 TF

TF 是 ROS 中的重要工具,用于管理机器人系统中的各种坐标系关系,支持静态和动态坐标变换以及坐标系查询。

1. 机器人中的常见坐标系
  • 机械臂机器人

    • 基坐标系:Base Frame
    • 世界坐标系:World Frame
    • 工具坐标系:Tool Frame
    • 工件坐标系:Object Frame

    在这里插入图片描述

  • 移动机器人

    • 里程计坐标系:Odom Frame
    • 地图坐标系:Map Frame
    • 激光雷达坐标系:Laser Frame
    • 基坐标系:Base Link

    在这里插入图片描述

这些坐标系之间的关系可用 4x4 的齐次变换矩阵描述,包含平移和旋转。

例程一 小海龟跟随例程
ros2 launch turtle_tf2_py turtle_tf2_demo.launch.py
ros2 run turtlesim turtle_teleop_key

启动了四个节点,分别是:

  • 小海龟仿真器
  • 海龟1的坐标系广播
  • 海龟2的坐标系广播
  • 海龟跟随控制

查看TF树

默认在当前终端路径下生成了一个frames.pdf文件,打开之后,就可以看到系统中各个坐标系的关系了

ros2 run tf2_tools view_frames 

在这里插入图片描述

查询坐标变换信息

ros2 run tf2_ros tf2_echo turtle2 turtle1

循环打印坐标系的变换数值了,由平移和旋转两个部分组成,还有旋转矩阵

At time 1736842433.4032845
- Translation: [0.000, 0.000, 0.000]
- Rotation: in Quaternion [0.000, 0.000, 0.985, -0.173]
- Rotation: in RPY (radian) [0.000, -0.000, -2.793]
- Rotation: in RPY (degree) [0.000, -0.000, -160.030]
- Matrix:
 -0.940  0.342  0.000  0.000
 -0.342 -0.940  0.000  0.000
  0.000  0.000  1.000  0.000
  0.000  0.000  0.000  1.000

坐标系可视化

ros2 run rviz2 rviz2 -d $(ros2 pkg prefix --share turtle_tf2_py)/rviz/turtle_rviz.rviz

在这里插入图片描述

2. 静态TF变换

运行

ros2 run learning_tf static_tf_broadcaster
ros2 run tf2_tools view_frames 

在这里插入图片描述

代码解析
learning_tf/static_tf_broadcaster.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#说明: ROS2 TF示例-广播静态的坐标变换

import rclpy                                                                 # ROS2 Python接口库
from rclpy.node import Node                                                  # ROS2 节点类
from geometry_msgs.msg import TransformStamped                               # 坐标变换消息
import tf_transformations                                                    # TF坐标变换库
from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster  # TF静态坐标系广播器类

class StaticTFBroadcaster(Node):
    def __init__(self, name):
        super().__init__(name)                                                  # ROS2节点父类初始化
        self.tf_broadcaster = StaticTransformBroadcaster(self)                  # 创建一个TF广播器对象

        static_transformStamped = TransformStamped()                            # 创建一个坐标变换的消息对象
        static_transformStamped.header.stamp = self.get_clock().now().to_msg()  # 设置坐标变换消息的时间戳
        static_transformStamped.header.frame_id = 'world'                       # 设置一个坐标变换的源坐标系
        static_transformStamped.child_frame_id  = 'house'                       # 设置一个坐标变换的目标坐标系
        static_transformStamped.transform.translation.x = 10.0                  # 设置坐标变换中的X、Y、Z向的平移
        static_transformStamped.transform.translation.y = 5.0                    
        static_transformStamped.transform.translation.z = 0.0
        quat = tf_transformations.quaternion_from_euler(0.0, 0.0, 0.0)          # 将欧拉角转换为四元数(roll, pitch, yaw)
        static_transformStamped.transform.rotation.x = quat[0]                  # 设置坐标变换中的X、Y、Z向的旋转(四元数)
        static_transformStamped.transform.rotation.y = quat[1]
        static_transformStamped.transform.rotation.z = quat[2]
        static_transformStamped.transform.rotation.w = quat[3]

        self.tf_broadcaster.sendTransform(static_transformStamped)              # 广播静态坐标变换,广播后两个坐标系的位置关系保持不变

def main(args=None):
    rclpy.init(args=args)                                # ROS2 Python接口初始化
    node = StaticTFBroadcaster("static_tf_broadcaster")  # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                     # 循环等待ROS2退出
    node.destroy_node()                                  # 销毁节点对象
    rclpy.shutdown()

setup.py:

entry_points={
    'console_scripts': [
        'static_tf_broadcaster = learning_tf.static_tf_broadcaster:main',
    ],
},
3. TF监听

查询两个坐标系之间的位置关系

运行

ros2 run learning_tf tf_listener

代码解析

learning_tf/tf_listener.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#说明: ROS2 TF示例-监听某两个坐标系之间的变换


import rclpy                                              # ROS2 Python接口库
from rclpy.node import Node                               # ROS2 节点类
import tf_transformations                                 # TF坐标变换库
from tf2_ros import TransformException                    # TF左边变换的异常类
from tf2_ros.buffer import Buffer                         # 存储坐标变换信息的缓冲类
from tf2_ros.transform_listener import TransformListener  # 监听坐标变换的监听器类

class TFListener(Node):

    def __init__(self, name):
        super().__init__(name)                                      # ROS2节点父类初始化

        self.declare_parameter('source_frame', 'world')             # 创建一个源坐标系名的参数
        self.source_frame = self.get_parameter(                     # 优先使用外部设置的参数值,否则用默认值
            'source_frame').get_parameter_value().string_value

        self.declare_parameter('target_frame', 'house')             # 创建一个目标坐标系名的参数
        self.target_frame = self.get_parameter(                     # 优先使用外部设置的参数值,否则用默认值
            'target_frame').get_parameter_value().string_value

        self.tf_buffer = Buffer()                                   # 创建保存坐标变换信息的缓冲区
        self.tf_listener = TransformListener(self.tf_buffer, self)  # 创建坐标变换的监听器

        self.timer = self.create_timer(1.0, self.on_timer)          # 创建一个固定周期的定时器,处理坐标信息

    def on_timer(self):
        try:
            now = rclpy.time.Time()                                 # 获取ROS系统的当前时间
            trans = self.tf_buffer.lookup_transform(                # 监听当前时刻源坐标系到目标坐标系的坐标变换
                self.target_frame,
                self.source_frame,
                now)
        except TransformException as ex:                            # 如果坐标变换获取失败,进入异常报告
            self.get_logger().info(
                f'Could not transform {self.target_frame} to {self.source_frame}: {ex}')
            return

        pos  = trans.transform.translation                          # 获取位置信息
        quat = trans.transform.rotation                             # 获取姿态信息(四元数)
        euler = tf_transformations.euler_from_quaternion([quat.x, quat.y, quat.z, quat.w])
        self.get_logger().info('Get %s --> %s transform: [%f, %f, %f] [%f, %f, %f]' 
          % (self.source_frame, self.target_frame, pos.x, pos.y, pos.z, euler[0], euler[1], euler[2]))

def main(args=None):
    rclpy.init(args=args)                       # ROS2 Python接口初始化
    node = TFListener("tf_listener")            # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                            # 循环等待ROS2退出
    node.destroy_node()                         # 销毁节点对象
    rclpy.shutdown()                            # 关闭ROS2 Python接口

setup.py:

entry_points={
    'console_scripts': [
        'static_tf_broadcaster = learning_tf.static_tf_broadcaster:main',
        'tf_listener = learning_tf.tf_listener:main',
    ],
},

4.3 URDF

URDF(统一机器人描述格式)是 ROS 中用于描述机器人模型的标准方法。通过 URDF,我们可以定义机器人的外观、结构、传感器位置以及运动学等属性。

1. 机器人的基本组成

机器人通常由以下四个部分组成:

  1. 硬件结构:底盘、外壳、电机等。
  2. 驱动系统:电机驱动器、电源管理等。
  3. 传感系统:编码器、IMU、摄像头、激光雷达等。
  4. 控制系统:计算平台(如树莓派、PC)及其操作系统和软件。
2. URDF 的核心概念
2.1 URDF 文件结构

URDF 使用 XML 格式定义机器人模型。主要标签包括:

  • <robot>:表示整个机器人模型。
  • <link>:描述机器人模型的刚体部分(如底盘、连杆)。
  • <joint>:描述机器人连杆之间的连接及运动类型。

2.2 连杆(Link)

连杆是机器人模型的基本构件,用 <link> 标签定义。每个连杆包含以下属性:

  1. 名称<link name="link_name">
  2. 视觉属性(外观):<visual>
    • 几何形状:<geometry>(如 <cylinder><sphere>
    • 材质:<material><color>
  3. 碰撞属性(计算时的简化模型):<collision>

示例

以下是一个描述机械臂连杆的示例:

<link name="base_link">
    <visual>
        <origin xyz="0 0 0" rpy="0 0 0"/>
        <geometry>
            <cylinder length="0.16" radius="0.20"/>
        </geometry>
        <material name="yellow">
            <color rgba="1 0.4 0 1"/>
        </material>
    </visual>
    <collision>
        <origin xyz="0 0 0" rpy="0 0 0"/>
        <geometry>
            <cylinder length="0.16" radius="0.20"/>
        </geometry>
    </collision>
</link>
2.3 关节(Joint)

关节用于连接连杆,并定义连杆之间的运动。URDF 支持以下六种关节类型:

  1. 连续关节continuous):可无限旋转(如车轮)。
  2. 旋转关节revolute):旋转范围有限。
  3. 滑动关节prismatic):沿某轴滑动。
  4. 固定关节fixed):无相对运动。
  5. 浮动关节floating):自由运动。
  6. 平面关节planar):平面内的运动。

示例

以下是一个描述旋转关节的示例:

<joint name="left_wheel_joint" type="continuous">
    <origin xyz="0 0.19 -0.05" rpy="0 0 0"/>
    <parent link="base_link"/>
    <child link="left_wheel_link"/>
    <axis xyz="0 1 0"/>
</joint>
3. 创建机器人模型

功能包结构

learning_urdf/
├── urdf/        # 机器人模型文件
├── meshes/      # 三维模型文件
├── launch/      # 启动文件
├── rviz/        # RViz 配置文件

#可视化机器人模型
ros2 launch learning_urdf display.launch.py

#查看URDF模型结构
urdf_to_graphviz mbot_base.urdf  # 在模型文件夹下运行

在这里插入图片描述

4.4 Gazebo

Gazebo是ROS系统中最为常用的三维物理仿真平台,支持动力学引擎,可以实现高质量的图形渲染,不仅可以模拟机器人及周边环境,还可以加入摩擦力、弹性系数等物理属性。

启动

ros2 launch gazebo_ros gazebo.launch.py

设计好的URDF模型此时还不能直接放到Gazebo中,需要我们做一些优化

1. XACRO机器人模型优化

同样也是对机器人URDF模型的创建,XACRO文件加入了更多编程化的实现方法,可以让模型创建更友好。

  • 宏定义,一个小车有4个轮子,每个轮子都一样,我们就没必要创建4个一样的link,像函数定义一样,做一个可重复使用的模块就可以了。
  • 文件包含,复杂机器人的模型文件可能会很长,为了切分不同的模块,比如底盘、传感器,我们还可以把不同模块的模型放置在不同的文件中,然后再用一个总体文件做包含调用。
  • 可编程接口,比如在XACRO模型文件中,定义一些常量,描述机器人的尺寸,定义一些变量,在调用宏定义的时候传递数据,还可以在模型中做数据计算,甚至加入条件语句,比如你的机器人叫A,就有摄像头,如果叫B,就没有摄像头。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 机器人仿真模型配置
  1. 完善物理参数

    确保每一个link都有惯性参数和碰撞属性。

  2. 添加Gazebo标签

    可以在gazebo中渲染每一个link的颜色,因为URDF中的颜色系统和gazebo中的不同。

  3. 配置传动装置

    给运动的joint配置传动装置,可以理解为仿真了一个电机。

  4. 添加控制器插件

    添加一个gazebo的控制器插件,小车是差速控制的,那就添加差速控制器插件,这样在不同角度下两个电机的速度分配,就可以交给控制器插件来完成了。

3. 构建仿真环境

把模型加载到Gazebo中了,需要用到一个gazebo提供的功能节点spwan_entity。

learning_gazebo/launch/load_urdf_into_gazebo.launch.py:

import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node

def generate_launch_description():

    # Include the robot_state_publisher launch file, provided by our own package. Force sim time to be enabled
    # !!! MAKE SURE YOU SET THE PACKAGE NAME CORRECTLY !!!

    package_name='learning_gazebo' #<--- CHANGE ME
    world_file_path = 'worlds/neighborhood.world'

    pkg_path = os.path.join(get_package_share_directory(package_name))
    world_path = os.path.join(pkg_path, world_file_path)  

    # Pose where we want to spawn the robot
    spawn_x_val = '0.0'
    spawn_y_val = '0.0'
    spawn_z_val = '0.0'
    spawn_yaw_val = '0.0'

    mbot = IncludeLaunchDescription(
                PythonLaunchDescriptionSource([os.path.join(
                    get_package_share_directory(package_name),'launch','mbot.launch.py'
                )]), launch_arguments={'use_sim_time': 'true', 'world':world_path}.items()
    )

    # Include the Gazebo launch file, provided by the gazebo_ros package
    gazebo = IncludeLaunchDescription(
                PythonLaunchDescriptionSource([os.path.join(
                    get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]),
             )

    # Run the spawner node from the gazebo_ros package. The entity name doesn't really matter if you only have a single robot.
    spawn_entity = Node(package='gazebo_ros', executable='spawn_entity.py',
                        arguments=['-topic', 'robot_description',
                                   '-entity', 'mbot',
                                   '-x', spawn_x_val,
                                   '-y', spawn_y_val,
                                   '-z', spawn_z_val,
                                   '-Y', spawn_yaw_val],
                        output='screen')



    # Launch them all!
    return LaunchDescription([
        mbot,
        gazebo,
        spawn_entity,
    ])
5. 机器人运动仿真

一个简单的模型仿真

#启动仿真环境
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
#启动键盘控制
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/burger/cmd_vel  

一个复杂的模型仿真

ros2 launch learning_gazebo load_urdf_into_gazebo.launch.py  #启动仿真环境
ros2 run teleop_twist_keyboard teleop_twist_keyboard         #键盘控制
6. Ignition 仿真
ros2 launch ros_ign_gazebo_demos rgbd_camera_bridge.launch.py

官网:www.ignitionrobotics.org/

7. 机器人模型

位置

learning_gazebo/urdf/mbot_gazebo.xacro

learning_gazebo/urdf/mbot_base_gazebo.xacro

4.5 Rviz

Rviz 是 ROS 中常用的三维可视化工具,用于显示机器人模型、传感器数据、运动轨迹等信息。

1. 启动 Rviz
ros2 run rviz2 rviz2
2. 彩色相机仿真与可视化

插件名称:libgazebo_ros_camera.so

learning_gazebo/urdf/sensers/camera_gazebo.xacro:

<gazebo reference="${prefix}_link">                <!-- 设置插件依附的链接 -->
    <sensor type="camera" name="camera_node">      <!-- 定义传感器类型为相机 -->
        <update_rate>30.0</update_rate>            <!-- 设置相机更新频率为 30Hz -->
        <camera>                                   <!-- 相机配置参数 -->
            <horizontal_fov>1.3962634</horizontal_fov> <!-- 相机水平视场角 -->
            <image>                                <!-- 图像输出设置 -->
                <width>1280</width>                <!-- 图像宽度 -->
                <height>720</height>               <!-- 图像高度 -->
                <format>R8G8B8</format>            <!-- 图像颜色格式 -->
            </image>
            <clip>                                 <!-- 图像可见范围 -->
                <near>0.02</near>                  <!-- 最近可见距离 -->
                <far>300</far>                     <!-- 最远可见距离 -->
            </clip>
        </camera>
        <plugin name="gazebo_camera" filename="libgazebo_ros_camera.so"> <!-- 加载 Gazebo 相机插件 -->
            <remapping>~/image_raw:=image_raw</remapping>  <!-- 重映射图像输出话题 -->
        </plugin>
    </sensor>
</gazebo>

主要配置项如下:

  • 标签:描述传感器

type:传感器类型,camera

name:摄像头命名,自由设置

  • 标签:描述摄像头参数

分辨率,编码格式,图像范围,噪音参数等

  • 标签:加载摄像头仿真插件

运行

ros2 launch learning_gazebo load_mbot_camera_into_gazebo.launch.py
ros2 run teleop_twist_keyboard teleop_twist_keyboard         #键盘控制
ros2 run rviz2 rviz2

在左侧Displays窗口中点击“Add”,找到Image显示项,设置topic为/camera/image_raw,即可看到相机图像。

在这里插入图片描述
在这里插入图片描述

3. 三维相机仿真与可视化

插件名称:libgazebo_ros_ray_sensor.so

learning_gazebo/urdf/sensers/kinect_gazebo.xacro:

```xml
<gazebo reference="${prefix}_link">                <!-- 设置插件依附的链接 -->
    <sensor type="depth" name="${prefix}">         <!-- 定义传感器类型为深度相机 -->
        <always_on>true</always_on>                <!-- 设置传感器始终开启 -->
        <update_rate>15.0</update_rate>            <!-- 设置更新频率为 15Hz -->
        <pose>0 0 0 0 0 0</pose>                   <!-- 设置传感器的位姿 -->
        <camera name="kinect">                     <!-- 相机配置参数 -->
            <horizontal_fov>${60.0*M_PI/180.0}</horizontal_fov> <!-- 相机水平视场角 -->
            <image>                                <!-- 图像输出设置 -->
                <format>R8G8B8</format>            <!-- 图像颜色格式 -->
                <width>640</width>                 <!-- 图像宽度 -->
                <height>480</height>               <!-- 图像高度 -->
            </image>
            <clip>                                 <!-- 图像可见范围 -->
                <near>0.05</near>                  <!-- 最近可见距离 -->
                <far>8.0</far>                     <!-- 最远可见距离 -->
            </clip>
        </camera>
        <plugin name="${prefix}_controller" filename="libgazebo_ros_camera.so"> <!-- 加载 Gazebo 相机插件 -->
            <ros>                                                               <!-- ROS 配置 -->
                <!-- <namespace>${prefix}</namespace> -->
                <remapping>${prefix}/image_raw:=rgb/image_raw</remapping>             <!-- 重映射图像输出话题 -->
                <remapping>${prefix}/image_depth:=depth/image_raw</remapping>         <!-- 重映射深度图像输出话题 -->
                <remapping>${prefix}/camera_info:=rgb/camera_info</remapping>         <!-- 重映射相机信息话题 -->
                <remapping>${prefix}/camera_info_depth:=depth/camera_info</remapping> <!-- 重映射深度相机信息话题 -->
                <remapping>${prefix}/points:=depth/points</remapping>                 <!-- 重映射点云话题 -->
            </ros>
            <camera_name>${prefix}</camera_name>             <!-- 设置相机名称 -->
            <frame_name>${prefix}_frame_optical</frame_name> <!-- 设置相机坐标系名称 -->
            <hack_baseline>0.07</hack_baseline>              <!-- 设置相机基线距离 -->
            <min_depth>0.001</min_depth>                     <!-- 设置最小深度值 -->
            <max_depth>300.0</max_depth>                     <!-- 设置最大深度值 -->
        </plugin>
    </sensor>
</gazebo>

运行

ros2 launch learning_gazebo load_mbot_rgbd_into_gazebo.launch.py

ros2 run teleop_twist_keyboard teleop_twist_keyboard         #键盘控制

ros2 run rviz2 rviz2

点击Add,添加PointCloud2,设置订阅的点云话题,还要配置Rviz的参考系是odom,就可以看到点云数据

在这里插入图片描述

4. 激光雷达仿真与可视化

插件名称:libgazebo_ros_ray_sensor.so

learning_gazebo/urdf/sensers/lidar_gazebo.xacro:

<gazebo reference="${prefix}_link">                <!-- 设置插件依附的链接 -->
    <sensor type="ray" name="rplidar">             <!-- 定义传感器类型为激光雷达 -->
        <update_rate>20</update_rate>              <!-- 设置更新频率为 20Hz -->
        <ray>                                      <!-- 激光雷达配置参数 -->
            <scan>
                <horizontal>                       <!-- 水平扫描设置 -->
                    <samples>360</samples>         <!-- 水平扫描的采样点数 -->
                    <resolution>1</resolution>     <!-- 水平分辨率 -->
                    <min_angle>-3</min_angle>      <!-- 最小扫描角度 -->
                    <max_angle>3</max_angle>       <!-- 最大扫描角度 -->
                </horizontal>
            </scan>
            <range>                                <!-- 距离范围设置 -->
                <min>0.10</min>                    <!-- 最小测距 -->
                <max>30.0</max>                    <!-- 最大测距 -->
                <resolution>0.01</resolution>      <!-- 距离分辨率 -->
            </range>
            <noise>
              <type>gaussian</type>                <!-- 噪声类型 -->
              <mean>0.0</mean>                     <!-- 均值 -->
              <stddev>0.01</stddev>                <!-- 标准差 -->
            </noise>
        </ray>
        <plugin name="gazebo_rplidar" filename="libgazebo_ros_ray_sensor.so"> <!-- 加载雷达仿真插件 -->
            <ros>
                <namespace>/</namespace>                        <!-- 设置ROS话题的命名空间 -->
                <remapping>~/out:=scan</remapping>              <!-- 重映射激光话题 -->
            </ros>  
            <output_type>sensor_msgs/LaserScan</output_type>    <!-- 输出消息类型 -->
        </plugin>
    </sensor>
</gazebo>

运行

ros2 launch learning_gazebo load_mbot_laser_into_gazebo.launch.py  # 启动加载激光雷达的仿真环境
ros2 run teleop_twist_keyboard teleop_twist_keyboard         #键盘控制
ros2 run rviz2 rviz2  # 启动 Rviz 可视化工具

点击Add,选择Laserscan,配置Topic,rviz的固定坐标系为odom

在这里插入图片描述

4.6 Rqt

1. rqt 简介
  • rqt 是一个基于 Qt 开发的模块化可视化工具,适用于 ROS 系统中的各种场景。
  • 与 Rviz 不同,rqt 更注重提供小巧、模块化的功能插件,满足特定需求。
2. 常用功能
  1. 日志显示

    1.1 运行

    ros2 run rqt_console rqt_console    # 启动日志显示工具
    ros2 run turtlesim turtlesim_node   # 启动小海龟仿真器
    

    1.2 记录器级别

    |- Fatal:系统将终止以尝试保护自身免受损害

    • Error:存在重大问题,这些问题不一定会损坏系统,但会妨碍系统正常运行
    • Warn:意外的活动或不理想的结果,可能代表更深层次的问题,但不会彻底损害功能
    • Info:指示事件和状态更新,作为系统是否按预期运行的视觉验证
    • Debug:详细说明了系统执行的整个逐步过程
  2. 图像显示

  3. 发布话题数据/服务调用

  4. 绘制数据曲线

  5. 数据包管理

  6. 节点可视化

5. 资源汇总

5.1 常用框架

  1. 自主导航

    Navigation2 是 ROS 2 中常用的自主导航框架,提供了从地图构建、定位、路径规划到避障的完整解决方案。

    开始使用 — Navigation 2 1.0.0 文档

  2. 自动驾驶

    Autoware 是一个基于 ROS 的开源自动驾驶软件,提供了从感知、定位、规划到控制的完整解决方案。

  3. 路径规划

    机械臂路径规划 moveit 是 ROS 中常用的机械臂路径规划框架,支持多种机械臂模型和运动学求解器。

5.2 机器人学

在这里插入图片描述

  1. 《Principal of Robot Motion》——Howie Choset
  2. 《introduction to Robotics》——John J.Craig
  3. 《Robotics: Modelling, Planning and Control》——Bruno Siciliano
  4. 《Rigid Body Dynamics Algorithms》——Roy Featherstone
  5. 《Probabilistic Robotics》——Sebastian Thrun
  6. 《Handbook of Robotics》——Bruno Siciliano
  7. 《Robotic Manipulation》——Matthew T. Mason
  8. 《Robotics, Vision and Control》——Peter Corke

5.3 视频教程

  1. 斯坦福大学公开课 —— 机器人学
  2. 台大机器人学之运动学
  3. Self-Driving Cars with ROS2 & Autoware
  4. 古月学院

5.4 常用链接

  1. ROS:https://www.ros.org
  2. ROS 2 Documentation:https://docs.ros.org/en/humble/index.html
  3. 古月居:https://www.guyuehome.com/
  4. ROS : https://www.ros.org
  5. ROS Wiki : http://wiki.ros.org/
  6. ROSCon : https://roscon.ros.org
  7. ROS Robots : https://robots.ros.org/
  8. Ubuntu Wiki : https://wiki.ubuntu.org.cn
  9. ROS2 Github : https://github.com/ros2
  10. Gazebo : https://classic.gazebosim.org

6. Markdown 语法

Markdown Reference

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值