🤖 ROS2 机器人 少年创客营:Day 6
主题:一键启动的魔法 ------ Launch 文件与动态参数
👋 欢迎回来,系统架构师!
昨天回顾 :我们掌握了 Action,让机器人能执行长任务并随时中断。现在的你,已经拥有了控制机器人的"三把利剑":Topic(广播)、Service(电话)、Action(遥控导弹)。
今天的痛点 :
看看你现在的桌面,为了运行一个项目,你需要打开多少个终端?
ros2 run turtlesim turtlesim_noderos2 run my_turtle_bot go_to_goalros2 run rqt_graph rqt_graph(看拓扑图)ros2 run rviz2 rviz2(看可视化)太麻烦了! 如果每次都要手动敲这么多命令,不仅手酸,还容易敲错。而且,如果想把海龟的背景色改成蓝色,难道要修改代码重新编译吗?
今日目标:
- ⚡ 一键启动 :学习 Launch Files,用一行命令启动整个机器人系统。
- 🎛️ 动态配置 :掌握 Parameters (参数),不改代码就能调整机器人的行为(颜色、速度、目标点)。
- 🏗️ 工程化:将前几天的散乱脚本整合成一个专业的、可交付的 ROS 2 项目。
🗺️ 今日探险地图 (Checklist)
- 📜 概念解锁:理解 Launch 文件的作用(导演 vs 演员)。
- 🐍 语法入门 :编写第一个 Python 格式的 Launch 文件 (
my_first_launch.py)。 - 🎛️ 参数魔法:在代码中读取参数,并在 Launch 文件中动态赋值。
- 🚀 实战演练:创建一个"超级启动器",同时启动仿真器、导航节点和可视化工具。
- 🎨 创意挑战:设计一个"变色龙模式",通过命令行参数一键切换海龟颜色和背景。
📜 第一关:Launch 文件 ------ 机器人的"总导演"
如果把每个 ROS 节点(Node)比作一个演员 ,那么 Launch 文件 就是总导演。
- 演员 (Node):只负责演好自己的戏(发速度、收数据)。
- 导演 (Launch):负责喊"Action"(启动所有节点)、安排座位(设置参数)、协调灯光(配置环境变量)。
为什么需要它?
- 自动化:一行命令启动几十个节点。
- 灵活性:可以在启动时动态修改参数(如:今天让海龟跑快点,明天跑慢点),无需重新编译代码。
- 复用性:把复杂的启动逻辑写死在文件里,别人只需运行一次即可。
💡 知识点 :ROS 2 推荐使用 Python 编写 Launch 文件(后缀
.launch.py),因为它比 XML 更灵活、更强大!
🎛️ 第二关:参数 (Parameters) ------ 让代码"活"起来
在写 Launch 文件之前,我们先改造一下之前的代码,让它支持外部配置。
1. 改造 go_to_goal.py
打开 src/my_turtle_bot/my_turtle_bot/go_to_goal.py,我们将硬编码的目标坐标改为参数。
python
# ... 前面的 import 不变 ...
class GoToGoalNode(Node):
def __init__(self):
super().__init__('go_to_goal_node')
# 🆕 声明参数 (带默认值)
# 格式:self.declare_parameter(参数名, 默认值)
self.declare_parameter('goal_x', 5.5)
self.declare_parameter('goal_y', 5.5)
self.declare_parameter('max_speed', 2.0)
# 🆕 获取参数值
# 如果 Launch 文件传了新值,就用新值;否则用默认值
self.goal_x = self.get_parameter('goal_x').value
self.goal_y = self.get_parameter('goal_y').value
self.max_speed = self.get_parameter('max_speed').value
self.reached_goal = False
self.current_pose = None
# ... 发布者和订阅者创建不变 ...
# 修改日志,显示当前配置
self.get_logger().info(f'🚀 导航启动!目标:({self.goal_x}, {self.goal_y}), 限速:{self.max_speed}')
# ... control_loop 中的速度限制改为使用 self.max_speed ...
# 原代码:speed = min(2.0, distance * 2.0)
# 新代码:speed = min(self.max_speed, distance * 2.0)
# ... 其余代码不变 ...
✅ 效果:现在,你可以不修改代码,直接通过命令行改变海龟的目标和速度!
bash
# 默认运行 (去 5.5, 5.5)
ros2 run my_turtle_bot go_to_goal
# 自定义运行 (去 9.0, 1.0,且最高速度 1.0)
ros2 run my_turtle_bot go_to_goal --ros-args -p goal_x:=9.0 -p goal_y:=1.0 -p max_speed:=1.0
💻 第三关:编写 Launch 文件 (核心时刻!✨)
现在,我们要编写一个"总导演"脚本,自动启动一切。
1. 创建文件
在功能包根目录下创建一个新文件夹 launch,并在其中新建 turtle_party.launch.py。
- 路径:
src/my_turtle_bot/launch/turtle_party.launch.py
2. 编写代码
python
#!/usr/bin/env python3
# 🆕 引入 Launch 相关的库
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
"""
这是 Launch 文件的入口函数。
它必须返回一个 LaunchDescription 对象,里面包含了所有要启动的动作。
"""
# --- 1. 定义可配置的参数 (让用户能在命令行修改) ---
# 格式:DeclareLaunchArgument('参数名', default_value='默认值', description='说明')
goal_x_arg = DeclareLaunchArgument(
'goal_x',
default_value='5.5',
description='Target X coordinate for the turtle'
)
goal_y_arg = DeclareLaunchArgument(
'goal_y',
default_value='5.5',
description='Target Y coordinate for the turtle'
)
bg_color_arg = DeclareLaunchArgument(
'bg_color',
default_value='0x4556FF', # 默认蓝色
description='Background color hex code (e.g., 0x000000 for black)'
)
# --- 2. 读取配置 ---
# 将刚才声明的参数转换为可以在 Node 中使用的变量
goal_x = LaunchConfiguration('goal_x')
goal_y = LaunchConfiguration('goal_y')
bg_color = LaunchConfiguration('bg_color')
# --- 3. 定义要启动的节点 (Nodes) ---
# 节点 A: 海龟仿真器
turtlesim_node = Node(
package='turtlesim',
executable='turtlesim_node',
name='simulator',
parameters=[{'background_r': 69, 'background_g': 86, 'background_b': 255}] # 这里可以硬编码,也可以用参数
# 🆕 进阶:可以通过 LaunchConfiguration 动态设置背景色,此处为简化先写死或略过复杂转换
)
# 节点 B: 我们的导航脚本
navigator_node = Node(
package='my_turtle_bot',
executable='go_to_goal', # 对应 setup.py 里的 entry_points 名字
name='navigator',
output='screen', # 🌟 重要:将节点的日志输出到当前终端,方便调试
parameters=[
{'goal_x': goal_x}, # 🌟 关键:将 Launch 的参数传给节点
{'goal_y': goal_y},
{'max_speed': 1.5}
]
)
# 节点 C: (可选) 自动打开 rqt_graph 查看拓扑
# rqt_node = Node(package='rqt_graph', executable='rqt_graph')
# --- 4. 返回总导演计划 ---
return LaunchDescription([
goal_x_arg,
goal_y_arg,
bg_color_arg,
turtlesim_node,
navigator_node
# rqt_node
])
🔧 第四关:注册与运行 (避坑指南)
⚠️ 关键步骤:修改 setup.py
为了让 ROS 2 知道我们多了一个 launch 文件夹,必须 更新 setup.py 中的 data_files。
常见错误 :忘记导入 glob 模块会导致编译失败 (NameError: name 'glob' is not defined)。
✅ 正确的 setup.py 写法:
python
from setuptools import find_packages, setup
import os
import glob # <--- 🌟 必须添加这一行!否则编译会报错
package_name = 'my_turtle_bot'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/' + package_name, ['package.xml']),
# 🌟 这行代码会自动查找 launch 文件夹下所有的 .launch.py 文件并安装
('share/' + package_name + '/launch', glob.glob(os.path.join('launch', '*.launch.py'))),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='mayf',
maintainer_email='your_email@example.com',
description='ROS 2 Turtle Bot Package',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'go_to_goal = my_turtle_bot.go_to_goal:main',
# 如果有其他脚本,继续在这里添加
# 'start_action = my_turtle_bot.action_client_turtle:main',
],
},
)
2. 编译
bash
cd ~/ROS2
# 推荐清理旧缓存,防止旧错误干扰
rm -rf build/ install/ log/
colcon build --packages-select my_turtle_bot
source install/setup.bash
如果看到 Summary: 1 package finished,恭喜你,成功避开了 glob 陷阱!
3. 见证奇迹:一键启动!
现在,你只需要在一个终端中输入一行命令:
场景 A:默认模式
bash
ros2 launch my_turtle_bot turtle_party.launch.py
- 现象 :
- 海龟仿真器窗口自动弹出。
- 导航节点自动启动。
- 终端里同时显示两个节点的日志(因为加了
output='screen')。 - 海龟自动游向 (5.5, 5.5)。
场景 B:自定义模式 (参数魔法)
bash
ros2 launch my_turtle_bot turtle_party.launch.py goal_x:=9.0 goal_y:=1.0
- 现象 :
- 系统启动。
- 导航节点日志显示:"🚀 导航启动!目标:(9.0, 1.0)..."
- 海龟乖乖地游向了右下角!
- 全程没有修改一行代码,也没有重新编译!
🎨 第五关:创意挑战赛
现在的系统已经很像样了。来挑战更高难度的集成吧!
🌟 挑战等级
-
🥉 青铜级:背景变色龙 🦎
- 任务 :修改 Launch 文件,增加
red,green,blue三个参数。 - 逻辑 :将这三个参数传递给
turtlesim_node的background_r/g/b参数。 - 运行 :
ros2 launch ... red:=255 green:=0 blue:=0(让背景变成红色!)
- 任务 :修改 Launch 文件,增加
-
🥈 白银级:多机派对 🎉
- 任务 :在 Launch 文件中启动两个
go_to_goal节点。 - 技巧 :给它们起不同的名字 (
name='nav1',name='nav2'),并传入不同的目标参数。 - 现象:一只海龟去左上,一只去右下,同时出发!
- 任务 :在 Launch 文件中启动两个
-
🥇 黄金级:条件启动 ⚖️
- 任务 :增加一个布尔参数
use_rviz(默认 false)。 - 逻辑 :只有当用户输入
use_rviz:=true时,才启动rviz2节点。 - 提示 :使用
LaunchConfiguration和 Python 的IfCondition(需导入from launch.conditions import IfCondition)。
- 任务 :增加一个布尔参数
🆘 常见问题急救包
| 问题 | 原因 | 解决方案 |
|---|---|---|
NameError: name 'glob' is not defined |
setup.py 中使用了 glob 但未导入 |
在 setup.py 顶部添加 import glob。 |
Package not found |
没编译或没 source | 运行 colcon build 和 source install/setup.bash。 |
Executable not found |
setup.py 里的 executable 名字和 entry_points 不一致 |
检查 Launch 文件里的 executable='go_to_goal' 是否匹配 setup.py 中的 'go_to_goal = ...'。 |
| 参数没生效 | 参数名拼写错误或类型不匹配 | 检查 declare_parameter 的名字是否和 Launch 文件里传递的名字完全一致。 |
| 日志不显示 | 忘记加 output='screen' |
在 Node() 定义中添加 output='screen'。 |
📝 Day 6 总结清单
| 概念 | 关键词 | 作用 |
|---|---|---|
| Launch File | .launch.py, LaunchDescription |
一键启动多个节点,系统集成的核心 |
| Parameters | declare_parameter, get_parameter |
代码与配置分离,实现动态调整 |
| LaunchConfiguration | LaunchConfiguration('name') |
在 Launch 文件中引用命令行参数 |
| Data Files | glob, data_files |
将 launch 文件打包安装,使其可被 ros2 launch 发现 |
| Output Screen | output='screen' |
在同一个终端查看所有节点的日志,调试神器 |
🔮 明日预告 (Day 7 - 结营大典)
恭喜你!你已经掌握了 ROS 2 的核心技能树:
- 节点、话题、服务、动作 (通信)
- 定时器、回调、闭环控制 (逻辑)
- 工作空间、编译、Launch、参数 (工程)
最后一天,我们将举办"黑客马拉松"! 🏆
- 任务 :综合运用所有知识,设计一个**"智能迷宫逃生"或"海龟足球赛"**。
- 展示:你将拥有自己的专属仓库,向全世界展示你的代码。
- 颁奖:颁发"ROS2 机器人 少年创客营"结业证书。
准备好迎接最终的挑战了吗?明天,让我们创造奇迹!
© 2026 ROS2 机器人 少年创客营 | 从单点突破到系统集成,你已是一名真正的机器人工程师!