惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
Security Latest
Security Latest
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cybersecurity and Infrastructure Security Agency CISA
Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
大猫的无限游戏
大猫的无限游戏
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
C
Cisco Blogs
V
Vulnerabilities – Threatpost
S
Security Archives - TechRepublic
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
J
Java Code Geeks
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Know Your Adversary
Know Your Adversary
博客园 - 叶小钗
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy International News Feed
P
Palo Alto Networks Blog
博客园_首页
V
V2EX
WordPress大学
WordPress大学
Schneier on Security
Schneier on Security
月光博客
月光博客
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
TaoSecurity Blog
TaoSecurity Blog
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
The Cloudflare Blog
罗磊的独立博客
美团技术团队
N
News | PayPal Newsroom
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Last Week in AI
Last Week in AI
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
S
SegmentFault 最新的问题
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog

又见苍岚

COLMAP PatchMatch Stereo 算法详解 事件驱动的状态机框架:从理论到工程实践 Git 在国内网络环境下无法 Push 的排查与修复 —— 配置 Clash 代理 分段五次多项式插值原理详解 路径插值方法深度对比研究 Claude Code 使用指南 OpenClaw 记忆管理与技能创建指南 CBS(Conflict-Based Search)算法详解 A* 算法及其变种详解 OpenClaw 配置多 Agents Windows Powershell 无法加载文件,因为在此系统上禁止运行脚本问题的解决方案 MaxClaw 安装流程 大模型 AI 名词介绍 AList 网盘聚合工具简介 Protobuf 简介与测试 Claude Code 简介以及 GLM 4.7 模型接入 Github 歌词下载工具 163MusicLyrics Python __getattr__ 懒加载 Python TypedDict 机器人仿真平台 Gazebo 安装记录 机器人仿真平台 Gazebo 简介 多机器人路径规划问题(Multi-Agent Path Finding, MAPF)简介 Python exifread 读取修改过的 jpeg 信息错误问题修复 3D 坐标系变换的理解 3D 旋转矩阵基本概念 MongoDB Compass 介绍 Python 环境管理工具 uv Flutter 开发指南 Snipaste 安装下载与黑屏问题解决方案 全局路径规划算法记录 2025 Python 版本性能测试 Flutter Hello World Flutter 安装环境配置 Ubuntu VMware 硬盘扩容后 SMBus Host controller not enabled 报错问题解决 Python NetworkX 教程 Docker GPU 报错 - Failed to initialize NVML Unknown Error 解决方案 Python matplotlib 图表绘制 cuda-toolkit 安装替代 Cuda 与 Cudnn Jinja2 Python 利用 docxtpl 和 Jinja2 生成基于模板的 Word 文档 Docker 实现 CPU 核心隔离 LoFTR 基于 Transformer 的特征提取匹配算法 OmniGlue 特征匹配 SuperGlue 使用图神经网络学习特征匹配 Ubuntu 下将 xlsx 文件按照 sheet 转换为 图片 Python 使用 SQLAlchemy Python FastAPI 教程 openwrt 软路由配置安装 Nav2 地图文件(PGM/YAML)规范标准 3D OBJ 模型转换为 glb 瓦片格式 Python 源码 Redis 数据库介绍 Ubuntu 22.04 内核自动升级导致 MongoDB 7.0.12 错误记录 ubuntu 20.04 安装 ROS Noetic ubuntu 18.04 安装 ROS Melodic VMware Workstation Pro 个人免费版下载、安装、使用指南 Hybrid A-star 路径规划 Reeds-Shepp 曲线 Dubins 曲线 Linux kvm 虚拟机网络不通的问题解决方法 Ubuntu 自动内存清理 BiliBili 缓存视频转 mp4 Python 求解线性规划 3D Gaussian Splatting 官方源码实践记录 ImageMagick 教程 Ubuntu 22.04 安装 Colmap 对数几率 odds Ubuntu nmcli 网络管理工具使用指南 SuperPoint 自监督深度学习特征点提取 SyncTV Music Tag Web 在线音乐信息整理工具 ncm 格式转 mp3 MusicBrainz 音乐元数据百科数据库 Ubuntu 网络流量监控工具 私人云音乐平台 Navidrome 入门 手眼标定 四元数(Quaternions) OHTTPS 实现免费自动 https 证书申请、更新、部署 ubuntu 22.04 安装 CloudCompare 单机 KVM 虚拟机冷迁移 Ubuntu 22.04 使用 mdadm 实现软 raid 小鱼 一键安装 ROS-humble Fluid -46- 基于 Simpletex API 构建公式识别页面 公式识别 API 简介 -- Simpletex 使用 Python web 部署库 waitress 3D Gaussian Splatting for Real-Time Radiance Field Rendering Ubuntu Swap 简介与空间扩展 Ubuntu 24.04 安装 forticlient Clash Verge 使用 MongoDB 7.0.17 集群 Docker 构建源码 Error code - 2013. Lost connection to MySQL server during query 问题解决 Python 日志记录库 loguru 使用指北 Python 实现 Web 日志查看服务 MySQL LOAD DATA LOCAL INFILE 极速数据加载 Image size exceeds limit of 89478485 pixels 解决方案 Docker 使用 NVIDIA GPU 驱动错误解决 阿里云 docker 镜像仓库 Ubuntu中没有wired connected的解决方案 MinIO 简介 subconverter 代理订阅格式转换 修复 node –openssl-legacy-provider is not allowed in NODE_OPTIONS 错误
ROS2 框架下运行 Python
Yiwei Zhang · 2024-07-11 · via 又见苍岚

ROS2 支持 Python 语言,本文记录 Python 创建 ROS2 节点的流程以及运行方式。

环境配置

  1. 安装好 ROS2 环境

  2. 安装好 Python 环境 (python 3.10)

    我安装的 ros2 humbel 自带了 python 3.10, 使用其他版本的 python 会报错

  3. 安装 colcon

    1
    pip install -U colcon-common-extensions

ROS2 创建单个节点

  • 建立文件夹 ros2-python-test,后续将在这里创建一系列子文件夹以用作不同的ROS2案例的工作空间。

  • 然后创建我们的第一个文件夹(也即我们的第一个工作空间):

1
mkdir ros2_ws_demo001/
  • 接下来,切换到该工作空间下,本次ROS2的所有操作都将在该目录下操作。
1
cd ros2_ws_demo001

确定了工作空间,就像盖房子已经选好了地址,打好了地基。

然后在这个地基上,就要开始搭建房子了,并且对房子进行各种定义。

ROS2是怎么搭建这个房子的呢?它是要先创建一个统一的固定名字的文件夹“src”,然后在这个文件夹下面再去具体定义各个房间。

  • 因此,要先创建一个src文件夹,并切换到这个目录下。
1
2
mkdir src
cd src

在src文件夹下面定义各个房间早已经工程化了,直接在命令行属于ros2相关命令就能快速搭建好一个基础框架出来。

  • 使用如下命令就可以创建包,这个包就是功能包。
1
ros2 pkg create package_001  --build-type ament_python --dependencies rclpy

ros2 会创建一系列 py 框架文件,当前文件结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
└── src
└── package_001
├── package_001
│   └── __init__.py
├── package.xml
├── resource
│   └── package_001
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py

5 directories, 8 files

虽然一下多了很多文件夹和文件,但是这些都是一个功能包的标配。

我们只需要在 package_001 中定义好一个节点,然后再在 setup.py 文件中配置好我们要调用这个节点就可以了。

  • package_001 中创建 node_001.py 文件,写入如下内容:
1
2
3
4
5
6
7
8
9
import rclpy
from rclpy.node import Node

def main(args=None):
rclpy.init(args=args) # init rclpy
my_node = Node("node_001") # to create a Node object
my_node.get_logger().info("hello, I am node_001") # to print a message.
rclpy.spin(my_node) # to keep Node running
rclpy.shutdown() # to close Node

  • 保存文件后,再更改下配置文件 setup.py

console_scripts的值原本为空,这里需要将console_scripts的值添加上我们新建的节点,如下所示:

1
2
3
'console_scripts': [
"node_001 = package_001.node_001:main"
],

保存之后退出 setup.py 文件。

到这里终于把源码部分定义好了(相当与定义好了房间的框架),接下来,只需要编译一下这个工作空间(相当于快速填充墙体),一个最简单的房子就搭建好了。

  • 因此我们回到工作空间目录 ros2_ws_demo001 下,执行如下命令进行编译就行了。
1
colcon build

终端输出:

1
2
3
4
5
> colcon build
Starting >>> package_001
Finished <<< package_001 [0.45s]

Summary: 1 package finished [0.54s]

编译完成后,我们发现工作空间下又多出了几个文件夹。分别是build、install、和log。而在这几个文件夹下面又很多其他文件夹和文件生成。

  • 执行 python 节点程序:
1
2
source install/setup.zsh
ros2 run package_001 node_001

可以看到屏幕上打印了“hello,I am node_001”,这个正是我们在自定义节点里做的事。

1
2
> ros2 run package_001 node_001
[INFO] [1720682420.964395679] [node_001]: hello, I am node_001
  • 新开一个终端,输入如下指令,可以检查下我们正在运行的节点列表(虽然目前只有一个节点在运行):
1
2
> ros2 node list
/node_001

小结

我们来稍微总结下使用python创建一个ros2节点并运行的整个过程:

1、创建一个独立的工作空间(其实就是创建了一个文件夹);

2、在工作空间下创建src文件夹(源码文件夹),并在src下创建功能包(非常简单,使用ros2命令行工具直接就生成了);

3、在功能包里面编辑一个节点python程序,唯一需要你动动脑子写的东西,但我们这个例子也才区区8行代码,已经简单到了极致;

4、在功能包里面配置setup.py文件,将我们上一步创建的节点程序调用起来;

5、回到工作空间,使用colcon工具编译(build)整个代码;

6、运行编译后的代码;

非官方方式

事实上 Python 使用 ROS2 总线相对灵活,不一定需要上述 ros2 run 的方法,直接 Python 运行某个 py 文件也是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import rclpy
from rclpy.node import Node

def main(args=None):
rclpy.init(args=args) # init rclpy
my_node = Node("node_001") # to create a Node object
my_node.get_logger().info("hello, I am node_001") # to print a message.
rclpy.spin(my_node) # to keep Node running
rclpy.shutdown() # to close Node

if __name__ == "__main__":
main()

你可以自己试一下。

ROS2 创建两个节点

稍微扩展下上面的代码,做两个ROS2节点,然后运行起来。

  • 将上一节的工作空间复制出一个新的工作空间,接下来我将在这个新的工作空间里完成我新的测试。
1
2
cp -rf ros2_ws_demo001/ ros2_ws_demo002_2_nodes/
cd ros2_ws_demo002_2_nodes
  • 现在我们在新的工作空间里面了。接下来要做的事情也是非常简单,将第一个节点文件复制生成第二个节点文件,并稍作编辑以作区分。
1
2
cp src/package_001/package_001/node_001.py  src/package_001/package_001/node_002.py
gedit src/package_001/package_001/node_002.py
  • 修改 node_002.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import rclpy
    from rclpy.node import Node

    def main(args=None):
    rclpy.init(args=args) # init rclpy
    my_node = Node("node_002") # to create a Node object
    my_node.get_logger().info("hello,I am node_002") # to print a message.
    rclpy.spin(my_node) # to keep Node running
    rclpy.shutdown() # to close Node

  • 把它配置到setup.py文件中:

    1
    2
    3
    4
    'console_scripts': [
    "node_001 = package_001.node_001:main",
    "node_002 = package_001.node_002:main"
    ],
  • 然后,退回到新的工作空间下,删除之前的编译结果,重新进行编译。

    1
    2
    3
    4
    5
    cd ~/ROS2_study/ros2_ws_demo002_2_nodes
    rm -rf build
    rm -rf install
    rm -rf log
    colcon build
  • 编译完成后,在当前终端直接启动第一个节点:

    1
    2
    source install/setup.zsh
    ros2 run package_001 node_001
  • 然后新打开一个终端,运行第二个节点:

    1
    2
    source install/setup.zsh
    ros2 run package_001 node_002
  • 两个终端页面显示了我们输出的字符串:

    1
    2
    3
    4
    5
    6
    7
    # terminal 1
    > ros2 run package_001 node_001
    [INFO] [1720690420.025982182] [node_001]: hello, I am node_001

    # terminal 2
    > ros2 run package_001 node_002
    [INFO] [1720690454.321045447] [node_002]: hello, I am node_002
  • 然后再来打开一个终端确认下目前正在运行的节点有哪些:

    1
    ros2 node list

    可以看到两个节点在线

    1
    2
    3
    > ros2 node list
    /node_001
    /node_002

    至此,我们实现了两个ROS2节点的创建并完成了调用。

ROS2 使用 launch 文件启动多个节点

规范

launch 文件默认 调用的 py 文件中存在 main() 函数,他会自动调用 main 函数,并将其中生成的节点命名为 launch 文件中配置的名称

1
2
3
4
5
6
Node(
package='package_001',
executable='node_001',
name='node_001_launch',
output='screen',
),

该配置会在 package_001 文件夹中,调用 node_001.py 文件, 调用其中的 main() 函数,将生成的节点命名为 node_001_launch,调用的 Python 为当前终端环境中的默认 python 程序路径,也就是说, 通过 conda activate 的环境可以作用于 ros2

通过 launch 启动的节点在launch 程序结束时会调用 shutdown 关闭这些节点,直接运行 py 文件创建的节点如果没有及时运行 shutdown 可能会导致节点残留,即程序已经退出但是仍然可以被 ros2 发现的节点。

实操

ROS2 可以通过配置 launch 文件,一次性将所有节点都设置好,然后统一启动。

  • 备份上一节的工作空间,复制生成新的工作空间并进入。
1
2
cp -rf ros2_ws_demo002_2_nodes/ ros2_ws_demo003_2_nodes_launch/
cd ros2_ws_demo003_2_nodes_launch/
  • 然后在功能包下面手动创建一个launch文件夹:
1
mkdir src/package_001/launch
  • 在launch文件夹下新建一个python文件用作启动文件,这里我随意命令为了 my_multi_nodes_launch.py, 内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# my_multi_nodes_launch.py

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
return LaunchDescription([
Node(
package='package_001',
executable='node_001',
name='node_001',
output='screen',
),
Node(
package='package_001',
executable='node_002',
name='node_002',
output='screen',
),
])

  • 然后,依然需要在setup.py文件中引入该launch文件以保证编译的时候能找到它。setup.py更改后内容如下:
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
from setuptools import setup

package_name = 'package_001'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/' + package_name, ['launch/my_multi_nodes_launch.py']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='goodman',
maintainer_email='goodman@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"node_001 = package_001.node_001:main",
"node_002 = package_001.node_002:main"
],
},
)

实这里只添加了一句话:

1
('share/' + package_name, ['launch/my_multi_nodes_launch.py']),

最后,回到工作空间编译一下:

1
2
3
4
5
cd ros2_ws_demo003_2_nodes_launch/
rm -rf build
rm -rf install
rm -rf log
colcon build
  • 运行这个launch文件(注意这次的运行方式有些不同了):
1
2
source install/setup.zsh
ros2 launch package_001 my_multi_nodes_launch.py

终端输出:

1
2
3
4
5
6
7
> ros2 launch package_001 my_multi_nodes_launch.py
[INFO] [launch]: All log files can be found below /home/vvd/.ros/log/2024-07-11-17-44-48-vvd
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [node_001-1]: process started with pid [25889]
[INFO] [node_002-2]: process started with pid [25891]
[node_001-1] [INFO] [1720691088.132584782] [node_001]: hello, I am node_001
[node_002-2] [INFO] [1720691088.135841995] [node_002]: hello, I am node_002
  • 用ros2 node list确认下正在运行的节点:
1
2
3
> ros2 node list
/node_001
/node_002

至此我们通过launch方法实现了一次性启动多个节点。

直接启动多个节点

可以在 一个 py 文件中直接启动多个 ros2 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import rclpy
from rclpy.node import Node

class MyNode(Node):
def __init__(self, name):
super().__init__(name)
self.get_logger().info(f'Hello from {name}')

def main(args=None):
rclpy.init(args=args)
node1 = MyNode('Test_node1')
node2 = MyNode('Test_node2')
rclpy.spin(node1)
rclpy.spin(node2)
rclpy.shutdown()

if __name__ == '__main__':
main()

这样在运行这个 py 程序后可以看到 Test_node1 Test_node2 两个节点在线.

但是如果将该文件替换上文的 node_001.py 文件,则会报错:

1
[node_001-1] [WARN] [1720692076.228787954] [rcl.logging_rosout]: Publisher already registered for provided node name. If this is due to multiple nodes with the same name then all logs for that logger name will go out over the existing publisher. As soon as any node with that name is destructed it will unregister the publisher, preventing any further logs for that name from being published on the rosout topic.

这时查看 ros2 的节点列表看到的是:

1
2
3
4
5
> ros2 node list
WARNING: Be aware that are nodes in the graph that share an exact name, this can have unintended side effects.
/node_001
/node_001
/node_002

也就是说通过 ros2 launch 启动方式启动和 运行 py 文件启动的ros 节点结果可能是不同的, 关键在于launch 配置, 将过程中创建的节点都命名为配置的名称.

CTL 工具

这里记录官方文档的一些工具细节

Remapping

重新映射允许您将节点名称、主题名称、服务名称等默认节点属性重新指定为自定义值。

终端中运行以下命令可以重新指定 turtlesim 节点的名称。

1
ros2 run turtlesim turtlesim_node --ros-args --remap __node:=my_turtle

ros2 node info

如果已经知道了节点的名称,可以使用以下命令获取更多节点信息:

1
ros2 node info <node_name>

ros2 node info 返回订阅者、发布者、服务和操作的列表,即与该节点交互的 ROS 图连接。输出结果应该是这样的:

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
/my_turtle
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/turtle1/cmd_vel: geometry_msgs/msg/Twist
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
/turtle1/color_sensor: turtlesim/msg/Color
/turtle1/pose: turtlesim/msg/Pose
Service Servers:
/clear: std_srvs/srv/Empty
/kill: turtlesim/srv/Kill
/my_turtle/describe_parameters: rcl_interfaces/srv/DescribeParameters
/my_turtle/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/my_turtle/get_parameters: rcl_interfaces/srv/GetParameters
/my_turtle/list_parameters: rcl_interfaces/srv/ListParameters
/my_turtle/set_parameters: rcl_interfaces/srv/SetParameters
/my_turtle/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
/reset: std_srvs/srv/Empty
/spawn: turtlesim/srv/Spawn
/turtle1/set_pen: turtlesim/srv/SetPen
/turtle1/teleport_absolute: turtlesim/srv/TeleportAbsolute
/turtle1/teleport_relative: turtlesim/srv/TeleportRelative
Service Clients:

Action Servers:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
Action Clients:

参考资料

文章链接:
https://www.zywvvd.com/notes/tools/ros2/ros2-python/ros2-python/