TurtleBot3 激光雷达自主避障(ROS 2 Humble)
对应课程作业:使用传感器获取的数据进行感知,并对机器人进行运动控制。 仿真平台 Gazebo 11,机器人 TurtleBot3 Waffle,中间件 ROS 2 Humble。
1. 实验目的
- 熟悉 ROS 2 的节点、话题、发布者与订阅者通信机制;
- 掌握 TurtleBot3 机载激光雷达数据的读取与处理;
- 编写控制节点,使无人车检测到障碍物后自动避障;
- 掌握 ROS 2 功能包的创建、编译、节点注册与运行流程。
2. 实验环境
| 项目 | 版本 |
|---|---|
| 操作系统 | Ubuntu 22.04 |
| ROS | Humble |
| 仿真平台 | Gazebo 11 |
| 机器人 | TurtleBot3 Waffle |
| 开发语言 | Python 3.10 |
[!NOTE] ROS 2 与 Gazebo 发行版兼容性说明 - 基准环境:本模块原生基于 Ubuntu 22.04 + ROS 2 Humble + Gazebo 11 (Classic) 设计与验证,避障节点向
/cmd_vel发布标准geometry_msgs/msg/Twist控制指令。 - 高版本(ROS 2 Jazzy / Rolling + Gazebo Harmonic):在 Ubuntu 24.04(ROS 2 Jazzy)中,TurtleBot3 仿真已切换至新版 Gazebo(Gz Sim / Harmonic),底盘桥接节点(ros_gz_bridge)默认期望接收带时间戳的geometry_msgs/msg/TwistStamped。若在 Jazzy 环境下直接运行,请配置 bridge 桥接类型或适配为TwistStamped,否则小车无法响应纯Twist指令。

图 1:Gazebo 中的 TurtleBot3 迷宫场景(右下角为 Waffle 机器人)。
3. 实验原理
本实验基于 ROS 2 话题通信实现避障闭环。Gazebo 中的 TurtleBot3 搭载
激光雷达传感器,将周围环境的距离信息以 sensor_msgs/LaserScan 发布到
扫描话题;避障节点订阅该话题,按下述流程做出决策,再把
geometry_msgs/Twist 发布到 /cmd_vel 驱动底盘。
激光雷达 /scan ──▶ 扇区划分 ──▶ 有限状态机 ──▶ 势场法转向 ──▶ /cmd_vel
(感知) (最近距离) (巡航/避障/紧急) (方向+速度) (控制)
3.1 感知:扇区最小距离
把 360° 扫描按角度划分为三个扇区,各取最小距离:
| 扇区 | 角度范围 | 用途 |
|---|---|---|
| 前方 | −30° ~ +30° | 决定能否直行 |
| 左侧 | +30° ~ +90° | 转向方向选择 |
| 右侧 | −90° ~ −30° | 转向方向选择 |
取最小值而非均值:均值会被远处空旷区域稀释,导致对近处障碍不敏感。 例如前方 −30° 处有 0.3 m 障碍、其余区域为 3 m 时,均值约 2.9 m 会被误判为安全,而最小值为 0.3 m 能正确识别危险。
3.2 决策:三态有限状态机
| 状态 | 进入条件 | 行为 |
|---|---|---|
CRUISE |
前方 > clear_distance |
直行 0.15 m/s |
AVOID |
前方 < safe_distance |
减速并转向 |
EMERGENCY |
前方 < danger_distance |
后退并转向 0.8 s |
进入与退出避障使用不同阈值(safe_distance = 0.60 m /
clear_distance = 0.90 m),形成滞回,避免距离在阈值附近波动时
状态高频切换造成转向抖动。
3.3 控制:势场法转向
转向方向由左右两侧可通行度之差决定:
线速度随前方障碍接近程度线性衰减:
并施加最小转向力度约束:|ω| < min_turn 时强制取 ±min_turn。
该约束用于规避纯势场法的局部极小值问题——当两侧距离接近时合力趋零,
机器人可能贴着障碍面平移而不真正转开。
4. 代码实现
4.1 功能包结构
turtlebot3_lidar_avoid/
├── main.py 模块入口(课程 main.* 约定)
├── main.sh / main.bat Linux / Windows 启动脚本
├── package.xml / setup.py / setup.cfg ROS 2 包定义
├── launch/main.launch.py ROS 2 launch 文件
├── config/params.yaml 参数配置
└── turtlebot3_lidar_avoid/
├── __init__.py
└── avoid.py ★ 避障算法实现
4.2 避障节点核心代码
完整实现见 turtlebot3_lidar_avoid/avoid.py,核心流程如下:
def scan_callback(self, scan):
"""收到一帧激光数据后决策并发布速度指令。"""
front = self.sector_distance(scan, SECTOR_FRONT)
left = self.sector_distance(scan, SECTOR_LEFT)
right = self.sector_distance(scan, SECTOR_RIGHT)
state = self.update_state(front)
twist = Twist()
if state == State.CRUISE:
twist.linear.x = self.cruise_speed
twist.angular.z = 0.0
elif state == State.AVOID:
twist.linear.x = self.avoid_linear_speed(front)
twist.angular.z = self.avoid_angular_speed(left, right)
else: # EMERGENCY
twist.linear.x = self.back_speed
twist.angular.z = self.max_turn if left >= right else -self.max_turn
self.cmd_pub.publish(twist)
扇区距离计算(含 inf / nan 过滤与角度规范化):
def sector_distance(self, scan, sector):
low, high = sector
best = scan.range_max
for index, distance in enumerate(scan.ranges):
if not (distance == distance): # 剔除 nan
continue
if distance == float('inf') or distance < scan.range_min:
continue # 剔除超量程与噪声
angle = scan.angle_min + index * scan.angle_increment
while angle >= math.pi: # 规范化到 [-pi, pi)
angle -= 2.0 * math.pi
while angle < -math.pi:
angle += 2.0 * math.pi
if low <= angle < high and distance < best:
best = distance
return best
4.3 节点注册
setup.py 中注册可执行节点,使 ROS 2 能够发现并运行:
entry_points={
'console_scripts': [
'avoid_node = turtlebot3_lidar_avoid.avoid:main',
],
},
4.4 参数化
所有阈值与速度通过 ROS 2 参数暴露,可在运行时覆盖而无需改代码:
| 参数 | 默认值 | 说明 |
|---|---|---|
scan_topic |
/scan |
激光雷达话题 |
safe_distance |
0.60 m | 进入避障阈值 |
clear_distance |
0.90 m | 退出避障阈值(滞回) |
danger_distance |
0.28 m | 紧急后退阈值 |
cruise_speed |
0.15 m/s | 巡航线速度 |
avoid_speed |
0.08 m/s | 避障线速度 |
back_speed |
−0.05 m/s | 紧急后退速度 |
min_turn / max_turn |
0.50 / 1.20 rad/s | 角速度上下限 |
turn_gain |
1.50 | 转向增益 |
emergency_hold |
0.8 s | 紧急状态最短保持时间 |
5. 实验步骤
5.1 编译功能包
mkdir -p ~/ros2_ws/src
cp -r src/ground/turtlebot3_lidar_avoid ~/ros2_ws/src/
cd ~/ros2_ws
source /opt/ros/humble/setup.bash
colcon build --packages-select turtlebot3_lidar_avoid
source install/setup.bash
5.2 启动 Gazebo 仿真环境
新开终端并启动 Gazebo 仿真环境(必须先指定机器人型号环境变量,否则会报错 KeyError: 'TURTLEBOT3_MODEL'):
# 1. 设置机器人型号(可选 burger / waffle / waffle_pi)
export TURTLEBOT3_MODEL=burger
# 2. 启动标准 TurtleBot3 仿真世界场景
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
可选快捷方式:若使用预装特定课程环境的虚拟机镜像,该镜像可能自带了一键启动脚本(如
~/start-gazebo-turtlebot-ros-humble.sh),亦可使用该脚本启动仿真。标准独立 ROS 2 环境中推荐使用上述官方ros2 launch命令。
5.3 确认雷达话题
ros2 topic list | grep -i scan
ros2 topic hz /scan
TurtleBot3 标准场景为
/scan;部分课程镜像场景为/out/scan, 可通过scan_topic参数指定,无需修改代码。
5.4 启动避障节点
# 方式一:模块入口脚本
./main.sh
./main.sh /out/scan
# 方式二:launch 启动
ros2 launch turtlebot3_lidar_avoid main.launch.py
# 方式三:节点启动
ros2 run turtlebot3_lidar_avoid avoid_node
5.5 观察运行效果
ros2 node list # 出现 /turtlebot3_lidar_avoid
ros2 topic echo /cmd_vel # 观察速度指令变化
节点日志会在状态切换时输出提示:
[INFO] [turtlebot3_lidar_avoid]: 避障节点已启动:scan=/scan, cmd_vel=/cmd_vel, safe=0.60m, danger=0.28m
[INFO] [turtlebot3_lidar_avoid]: 状态切换 cruise -> avoid(前方 0.52 m)
[INFO] [turtlebot3_lidar_avoid]: 状态切换 avoid -> cruise(前方 1.03 m)
6. 实验结果
在 TurtleBot3 迷宫场景中,机器人能够自主完成避障巡航:
- 无障碍区域:以 0.15 m/s 直线巡航;
- 前方出现障碍:前方距离小于 0.60 m 时进入避障状态, 线速度按距离衰减并朝较空旷一侧转向;
- 贴墙 / 死角:前方距离小于 0.28 m 时进入紧急状态, 先后退约 0.8 s 再转向脱离;
- 障碍解除:前方距离恢复到 0.90 m 以上后自动回到巡航状态。
7. 相对初版单点避障的改进
课程实验初版只读取正前方单束雷达(ranges[180]),存在明显局限:
无法感知侧向障碍,在墙角或斜向障碍场景容易卡住。本模块的改进:
| 项 | 初版 | 本模块 |
|---|---|---|
| 感知范围 | 正前方单束 | 前 / 左 / 右三扇区 |
| 转向决策 | 固定右转 | 朝较空旷一侧转 |
| 速度控制 | 固定值 | 随障碍距离衰减 |
| 状态抖动 | 无滞回 | 进入 / 退出双阈值 |
| 墙角脱困 | 易卡住 | 紧急后退 + 原地转向 |
| 参数 | 硬编码 | ROS 2 参数,可运行时覆盖 |
8. 现象分析
实验基于 ROS 2 标准话题通信架构运行:激光雷达实时采集环境测距信息, 通过话题传输至避障节点,节点经状态机与势场法决策后输出运动指令, 构成完整的感知—决策—控制闭环。
本实现相比单点避障增加了侧向感知与滞回判断,因此:
- 在开阔区域不会因远处噪声误触发避障;
- 在斜向接近障碍时能提前转向,而非等到正前方贴近才反应;
- 在死角场景能通过紧急后退脱困,避免原地打转。
仍需注意的局限:本实现只使用二维扫描面信息,无法感知低于扫描面的 障碍(如台阶、凹陷),且没有全局路径规划,在长走廊尽头等 凹形结构中仍可能出现来回徘徊(反应式避障的固有局限)。
9. 实验总结
通过本实验完成了 ROS 2 下的 TurtleBot3 激光雷达自主避障功能开发, 掌握了节点创建、话题订阅与发布、功能包编译、节点注册运行的完整流程, 理解了基于传感器反馈的反应式控制算法原理。
后续改进方向:
- 多传感器融合:结合深度相机或 IMU,弥补单线激光的盲区;
- 全局规划:引入 A* / Dijkstra 生成全局路径,避免反应式避障的局部极小值;
- 局部规划:用 DWA 等考虑动力学约束的方法替代固定势场,使轨迹更平滑。
10. 思考题
10.1 如何修改代码实现左右侧障碍物检测?
原程序只检测正前方单束雷达,只能规避正前方障碍。改进方式是读取激光雷达 数组中左右侧角度对应的距离数据,分别与安全距离比较,通过对比三个方向的 距离判断障碍物方位,再分别执行左转、右转、直行或后退动作。
本模块即按此思路实现:sector_distance() 按角度区间取最小值,
avoid_angular_speed() 用左右距离差决定转向方向。更进一步的改进是把
三个扇区细分为更多方向(如 5~8 个),提升窄通道下的通过能力。
10.2 ROS 2 中 source 命令的作用是什么?
source 用于加载 ROS 2 系统与工作空间的环境配置文件,刷新当前终端的
环境变量(如 AMENT_PREFIX_PATH、PYTHONPATH、LD_LIBRARY_PATH)。
执行后终端才能识别 ROS 2 命令、自定义功能包与可执行节点,
从而正常完成编译、运行与话题查询等操作。
11. 声明
本模块的开发过程使用了 AI 大模型辅助(代码编写、文档撰写与问题排查), 提交者对全部内容负责。
12. 许可证
MIT License