一、ros2可执行节点

1.1 Node定义

节点(Node) 是 ROS2 中最基本的执行单元,可以理解为一个独立运行的程序。每个节点负责特定的功能,比如:

  • 读取传感器数据
  • 控制电机
  • 处理图像
  • 发布导航目标点(就像您的 qmini_nav2_waypoint.launch.py 文件那样)

1.2 Node代码结构

可执行节点就是可以直接运行的 Python 或 C++ 程序,必须满足:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Python 节点的基本结构
import rclpy
from rclpy.node import Node

class MyNode(Node):
def __init__(self):
super().__init__('my_node_name')
# 节点的功能代码
def func():
pass

def main(args=None):
rclpy.init(args=args) # 初始化 ROS2
node = MyNode() # 创建节点实例
rclpy.spin(node) # 保持节点运行
node.func()
node.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()

二、ros2 run vs ros2 launch 的区别

2.1 ros2 run - 运行单个节点

语法如下:

1
ros2 run <包名> <可执行文件名> [参数]

特点:
✅ 只启动一个节点
✅ 简单直接,适合测试和调试
✅ 支持交互式输入/输出
❌ 无法同时启动多个节点
❌ 无法配置复杂的启动参数

运行示例:

1
2
# 启动您的导航目标发布节点
ros2 run qmini_nav2 waypoint_publisher

2.2 ros2 launch - 启动多个节点和系统

语法如下:

1
ros2 launch <包名> <launch文件名.py> [参数:=值]

一个典型的 launch 文件是一个 Python 脚本,必须包含 generate_launch_description() 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
return LaunchDescription([
# 节点1
Node(
package='包名',
executable='可执行文件名',
name='节点名称',
parameters=[参数],
output='screen',
remappings=[
('/cmd_vel', '/robot1/cmd_vel')
]
),
# 节点2
Node(
package='另一个包',
executable='另一个可执行文件',
...
),
])

运行示例:

1
2
3
4
5
# 启动完整的导航系统(一次性启动多个节点)
ros2 launch qmini_nav2 qmini_nav2.launch.py

# 带参数启动(指定地图文件)
ros2 launch qmini_nav2 qmini_nav2.launch.py map:=/home/jetson/maps/my_map.yaml

简单总结:

  1. 可执行节点必须要有:
1
2
3
4
5
6
7
def main(args=None):
rclpy.init(args=args)
# 节点代码
rclpy.shutdown()

if __name__ == '__main__':
main()
  1. launch文件必须要有generate_launch_description() 函数。

三、可执行节点使用

  1. 在 src 中新建一个和包一样的目录,同时新建文件__init__.py,确保该文件为空:
1
2
3
4
cd ~/qmini_ws/src/qmini_nav2
mkdir -p qmini_nav2 # 创建 Python 包目录
touch qmini_nav2/waypoint_publisher.py
touch qmini_nav2/__init__.py
  1. 修改setup.py,在entry_points中注册如下:
1
2
3
4
5
entry_points={
'console_scripts': [
'waypoint_publisher = qmini_nav2.waypoint_publisher:main', # 注册可执行文件
],
},
  1. 重新编译
1
2
3
cd ~/qmini_ws
colcon build --symlink-install --packages-select qmini_nav2
source install/setup.bash

如果出现编译错误,特别是文件结构改变了,该问题可能是之前的编译结果缓存,可以执行如下命令:

1
2
3
cd ~/qmini_ws
# 清理 qmini_nav2 包的编译缓存
rm -rf build/qmini_nav2 install/qmini_nav2

整体的文件结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
jetson@ubuntu:~/qmini_ws/src/qmini_nav2$ tree
.
├── launch
│ └── qmini_nav2.launch.py
├── package.xml
├── params
│ └── nav2_params.yaml
├── qmini_nav2
│ ├── __init__.py
│ └── waypoint_publisher.py
├── resource
│ └── qmini_nav2
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py

5 directories, 11 files

四、entry_points 分析

entry_points 告诉 Python 包管理系统:将某个 Python 函数注册为命令行可执行程序

一般格式如下:

1
'可执行文件名 = 包名.模块名:函数名'

在本实现中如下:

1
'waypoint_publisher = qmini_nav2.waypoint_publisher:main'
部分 含义
可执行文件名 waypoint_publisher 在命令行输入的名字
包名 qmini_nav2 Python 包名(目录名)
模块名 waypoint_publisher Python 文件名(不含 .py
函数名 main 要执行的函数

即 entry_points 将 Python 函数包装成了命令行工具。