🏆 ROS2 机器人 少年创客营:Day 7
主题:终极挑战 ------ 智能迷宫逃生 & 结营大典
🎉 恭喜!你已抵达终点线!
回顾旅程:
- Day 1-2: 你好,海龟!(节点、话题)
- Day 3: 听我指挥!(服务)
- Day 4: 复杂任务!(动作)
- Day 5: 感知世界!(传感器数据闭环)
- Day 6: 系统工程!(Launch 文件与参数)
今日使命 :
今天没有新的语法教学。今天是**"黑客马拉松"!你将综合运用过去 6 天学到的所有技能,解决一个真实的机器人难题:"智能迷宫逃生"**。
挑战目标 :
编写一个节点,控制海龟在充满未知障碍(或边界)的环境中,自主探索并找到出口(或存活最长时间),全程无需人工干预。
🗺️ 今日探险地图 (Checklist)
- 🧠 算法设计:设计"避障 + 寻路"的简单逻辑。
- 🤖 代码实现 :编写
maze_runner.py,融合订阅、发布、定时器逻辑。 - 🚀 系统集成 :编写
escape_launch.py,一键启动仿真环境和你的 AI。 - 🎬 演示时刻:运行程序,录制视频,见证奇迹。
- 🎓 结营仪式:总结回顾,颁发电子证书。
🧠 第一关:算法设计 (伪代码)
在写代码前,先理清思路。我们不需要复杂的 SLAM 或 A* 算法,一个简单的**"随机游走 + 遇墙转向"**策略就足以应对今天的挑战。
🤖 核心逻辑:
- 一直向前跑:默认发送直线速度指令。
- 感知危险 :订阅
/turtle1/pose,检查当前位置(x, y)。 - 判断边界 :Turtlesim 的地图大小是
11x11(0 到 11)。- 如果
x > 10或x < 1或y > 10或y < 1(接近墙壁):- 停止前进。
- 原地旋转 90 度 (或随机角度)。
- 继续前进。
- 如果
- (进阶) 寻找出口 :假设出口在
(10.5, 10.5)。- 如果距离出口 < 0.5 米 -> 任务成功,停止并庆祝!
💻 第二关:代码实现 (maze_runner.py)
在 src/my_turtle_bot/my_turtle_bot/ 下新建 maze_runner.py。
python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
import math
import random
class MazeRunnerNode(Node):
def __init__(self):
super().__init__('maze_runner_node')
# --- 参数配置 ---
self.declare_parameter('safe_distance', 1.0) # 离墙多远开始转向
self.declare_parameter('turn_angle', 1.57) # 转向角度 (弧度,1.57 ≈ 90度)
self.declare_parameter('linear_speed', 2.0)
self.declare_parameter('angular_speed', 1.5)
self.safe_dist = self.get_parameter('safe_distance').value
self.turn_angle = self.get_parameter('turn_angle').value
self.lin_speed = self.get_parameter('linear_speed').value
self.ang_speed = self.get_parameter('angular_speed').value
self.pose = None
self.state = 'MOVE' # 状态机:MOVE, TURNING
self.turn_start_theta = 0.0
self.target_theta = 0.0
# 定义地图边界 (Turtlesim 默认 11x11)
self.map_min = 0.5
self.map_max = 10.5
# 创建发布者和订阅者
self.cmd_pub = self.create_publisher(Twist, 'turtle1/cmd_vel', 10)
self.pose_sub = self.create_subscription(Pose, 'turtle1/pose', self.pose_callback, 10)
# 创建定时器 (10Hz)
self.timer = self.create_timer(0.1, self.control_loop)
self.get_logger().info('🤖 迷宫逃生系统已启动!正在寻找出路...')
def pose_callback(self, msg):
self.pose = msg
def control_loop(self):
if self.pose is None:
return
cmd = Twist()
# --- 状态机逻辑 ---
if self.state == 'MOVE':
# 1. 检查是否撞墙 (简化版:检查距离边界的距离)
dist_left = self.pose.x - self.map_min
dist_right = self.map_max - self.pose.x
dist_bottom = self.pose.y - self.map_min
dist_top = self.map_max - self.pose.y
min_dist = min(dist_left, dist_right, dist_bottom, dist_top)
if min_dist < self.safe_dist:
# 危险!准备转向
self.get_logger().warn(f'⚠️ 检测到墙壁 (距离: {min_dist:.2f}),正在转向...')
self.state = 'TURNING'
self.turn_start_theta = self.pose.theta
# 随机向左或向右转 90 度
direction = 1 if random.random() > 0.5 else -1
self.target_theta = self.turn_start_theta + (self.turn_angle * direction)
cmd.linear.x = 0.0
cmd.angular.z = self.ang_speed * direction
else:
# 安全,继续前进
cmd.linear.x = self.lin_speed
cmd.angular.z = 0.0
elif self.state == 'TURNING':
# 计算当前角度与目标角度的差
angle_diff = self.target_theta - self.pose.theta
# 标准化角度 (-pi 到 pi)
while angle_diff > math.pi:
angle_diff -= 2.0 * math.pi
while angle_diff < -math.pi:
angle_diff += 2.0 * math.pi
if abs(angle_diff) < 0.1: # 转向完成
self.get_logger().info('✅ 转向完成,继续探索。')
self.state = 'MOVE'
cmd.linear.x = 0.0
cmd.angular.z = 0.0
else:
# 继续旋转
cmd.linear.x = 0.0
cmd.angular.z = self.ang_speed * (1.0 if angle_diff > 0 else -1.0)
self.cmd_pub.publish(cmd)
def main(args=None):
rclpy.init(args=args)
node = MazeRunnerNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
🔧 第三关:集成与 Launch 文件
1. 更新 setup.py
别忘了注册新的可执行文件!
python
entry_points={
'console_scripts': [
'go_to_goal = my_turtle_bot.go_to_goal:main',
'maze_runner = my_turtle_bot.maze_runner:main', # 🆕 新增
],
},
# 确保 data_files 包含 launch 文件夹 (参考 Day 6)
2. 创建 escape.launch.py
在 launch/ 文件夹下新建 escape.launch.py。
python
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess
def generate_launch_description():
return LaunchDescription([
# 1. 启动仿真器 (背景设为黑色,增加神秘感)
Node(
package='turtlesim',
executable='turtlesim_node',
name='simulator',
parameters=[{'background_r': 20, 'background_g': 20, 'background_b': 20}]
),
# 2. 启动我们的 AI 导航员
Node(
package='my_turtle_bot',
executable='maze_runner',
name='ai_pilot',
output='screen',
parameters=[
{'safe_distance': 1.5}, # 离墙远点就开始转
{'linear_speed': 3.0} # 跑快点!
]
),
# 3. (可选) 自动打开键盘控制作为备用
# ExecuteProcess(cmd=['ros2', 'run', 'turtlesim', 'turtle_teleop_key']),
])
3. 编译与运行
bash
cd ~/ROS2
colcon build --packages-select my_turtle_bot
source install/setup.bash
# 一键启动逃生挑战!
ros2 launch my_turtle_bot escape.launch.py
👀 观察现象:
- 海龟会自动向前冲。
- 快到墙壁时,它会急刹车,然后帅气地转个弯,继续探索。
- 它永远不会撞墙!
🎨 创意升级挑战 (选做)
如果你想让项目更酷,尝试以下升级:
- 🌈 彩虹轨迹 :
- 修改代码,每当海龟成功转向时,调用
/clear服务,并改变背景颜色或海龟颜色。
- 修改代码,每当海龟成功转向时,调用
- 🎯 定点寻宝 :
- 结合 Day 5 的
go_to_goal逻辑。设定一个随机目标点,海龟必须先避障走到目标点,然后再找下一个。
- 结合 Day 5 的
- 👻 多龟大逃杀 :
- 使用
ros2 run turtlesim spawn生成 5 只海龟。 - 修改
maze_runner支持多只海龟(通过 remapping 话题名称turtleX/cmd_vel)。 - 看谁最后还在地图上活着!
- 使用
🎓 结营大典:你学到了什么?
恭喜你完成了 ROS2 机器人 少年创客营!让我们盘点一下你的技能树:
| 领域 | 技能点 | 应用场景 |
|---|---|---|
| 通信机制 | Topic (发布/订阅) | 传感器数据流、控制指令 |
| Service (请求/响应) | 开关灯、重置位置、清屏 | |
| Action (目标/反馈/结果) | 长时任务、导航、可中断操作 | |
| 节点开发 | Class 结构、Timer、Callback | 构建稳健的机器人逻辑 |
| 数学基础 | 坐标系转换、PID 思想、角度归一化 | 运动控制核心算法 |
| 工程工具 | Colcon 编译、Package.xml | 项目管理与依赖处理 |
| 系统集成 | Launch Files 、Parameters | 一键部署、动态配置、产品化 |
🎁 毕业作业
请将你的 src/my_turtle_bot 文件夹打包,或者上传到 GitHub。
在你的简历或作品集中,你可以自豪地写下:
项目名称 :基于 ROS 2 的自主移动机器人仿真系统
技术栈 :Python, ROS 2 (Humble/Iron/Jazzy), Launch System, State Machines
功能描述:实现了基于话题通信的运动控制、基于 Action 的任务管理、以及基于参数化 Launch 文件的自动化部署系统。具备自主避障与路径规划能力。
🚀 未来的路
今天的结束,是你机器人工程师生涯的开始。
- 下一步建议 :
- 尝试在 真实机器人 (如树莓派 + 小车) 上运行类似的代码。
- 学习 Nav2 (ROS 2 官方导航栈),那是工业级的避障与路径规划。
- 探索 SLAM (即时定位与地图构建),让机器人在未知环境中画地图。
- 学习 MoveIt 2,机械臂控制的王者。
保持好奇,保持创造。世界需要你的代码!
🎓 谨此颁发 "ROS2 机器人 少年创客营" 结业证书 (电子版)
授予:[你的名字]
鉴于其在 2026 年 3 月期间,成功掌握了 ROS 2 核心架构,完成了从单节点控制到系统集成自主导航的全部挑战,特发此证,以资鼓励。
签发人:AI 导师
日期:2026-03-24
© 2026 ROS2 机器人 少年创客营 | 愿你的代码永远 Compile Pass,愿你的机器人永不 Bug! 🤖✨