1. 引言:从扫地机器人到自动驾驶技术
随着人工智能与机器人技术的迅猛发展,具备“移动”能力已成为智能体的基础功能之一。无论是家庭中常见的扫地机器人、物流仓库中的自动导引车(AGV),还是城市道路上行驶的自动驾驶汽车,其核心技术体系中都离不开两个关键模块:
- 路径规划(Path Planning):解决“如何从 A 点到达 B 点”的问题,主要关注几何路径的生成以及障碍物规避。
- 决策系统(Decision Making):处理“当前应采取何种行为”的判断,例如是否超车、跟车或停车等待,侧重于高层逻辑推理与行为选择。
得益于其简洁的语法和强大的科学计算生态(如 NumPy、SciPy 等库),Python 成为算法原型开发与教学演示的首选语言。本文将深入剖析上述两大领域的经典算法,并提供基于 Python 的实践代码实现。
2. 路径规划:探索最优路径的生成方法
路径规划通常划分为两个层次:全局规划 和 局部规划。前者在已知环境地图的基础上计算出一条从起点到终点的最优路径;后者则负责应对动态障碍物的实时避让及轨迹平滑处理。
2.1 基于图搜索的经典算法:A*(A-Star)
A* 是最为广泛应用的路径搜索算法之一,融合了 Dijkstra 算法的广度优先特性与贪婪最佳优先搜索的启发式优势,能够在效率与准确性之间取得良好平衡。
核心原理
A* 算法通过一个评估函数 f(n) 来决定节点 n 的扩展优先级:
f(n) = g(n) + h(n)
其中:
- g(n) 表示从起始点到当前节点 n 的实际行走代价;
- h(n) 是从当前节点 n 到目标点的预估代价,即启发式函数(Heuristic)。
常用启发式函数选择
- 曼哈顿距离(Manhattan Distance):适用于仅允许上下左右四方向移动的网格地图。
- 欧几里得距离(Euclidean Distance):适用于支持任意角度连续移动的空间场景。
Python 实现:A* 算法示例
以下是一个基于二维网格地图实现的 A* 路径规划代码:
import heapq
import math
class Node:
def __init__(self, x, y, cost, parent_index):
self.x = x
self.y = y
self.cost = cost # g(n)
self.parent_index = parent_index
def __lt__(self, other):
return self.cost < other.cost
class AStarPlanner:
def __init__(self, ox, oy, resolution, rr):
"""
ox: 障碍物x坐标列表
oy: 障碍物y坐标列表
resolution: 网格分辨率
rr: 机器人半径
"""
self.resolution = resolution
self.rr = rr
self.min_x, self.min_y = 0, 0
self.max_x, self.max_y = 0, 0
self.x_width, self.y_width = 0, 0
self.obstacle_map = None
self.calc_obstacle_map(ox, oy)
self.motion = self.get_motion_model()
class Node:
def __init__(self, x, y, cost, parent_index):
self.x = x # 网格索引
self.y = y # 网格索引
self.cost = cost
self.parent_index = parent_index
def __str__(self):
return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.parent_index)
def planning(self, sx, sy, gx, gy):
"""
sx, sy: 起点世界坐标
gx, gy: 终点世界坐标
"""
start_node = self.Node(self.calc_xy_index(sx, self.min_x),
self.calc_xy_index(sy, self.min_y), 0.0, -1)
goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
self.calc_xy_index(gy, self.min_y), 0.0, -1)
open_set, closed_set = dict(), dict()

open_set[self.calc_grid_index(start_node)] = start_node
# 初始化优先队列,存储 (启发式代价 + 实际代价, 网格索引)
pq = []
heapq.heappush(pq, (self.calc_heuristic(start_node, goal_node), self.calc_grid_index(start_node)))
while True:
if not open_set:
print("Open set is empty..")
break
_, c_id = heapq.heappop(pq)
current = open_set[c_id]
# 判断是否到达目标节点
if current.x == goal_node.x and current.y == goal_node.y:
goal_node.parent_index = current.parent_index
goal_node.cost = current.cost
print("Goal Found!")
return self.calc_final_path(goal_node, closed_set)
# 将当前节点从 open_set 移除,并加入 closed_set
del open_set[c_id]
closed_set[c_id] = current
# 遍历运动模型中的方向,扩展邻居节点
for i, _ in enumerate(self.motion):
node = self.Node(
current.x + self.motion[i][0],
current.y + self.motion[i][1],
current.cost + self.motion[i][2],
c_id
)
n_id = self.calc_grid_index(node)
# 若该节点已在 closed_set 中,则跳过
if n_id in closed_set:
continue
# 检查节点是否有效(如是否超出边界或碰撞障碍物)
if not self.verify_node(node):
continue
# 如果邻居节点不在 open_set 中,则加入并设置优先级
if n_id not in open_set:
open_set[n_id] = node
priority = node.cost + self.calc_heuristic(node, goal_node)
heapq.heappush(pq, (priority, n_id))
else:
# 若已存在但发现更小的代价,则更新节点信息
if open_set[n_id].cost > node.cost:
open_set[n_id] = node
# 注:此处未显式更新堆中优先级,工程实践中应优化处理
return None, None
def calc_heuristic(self, n1, n2):
"""
计算两点之间的启发式距离(欧几里得距离),可调节权重
"""
w = 1.0 # 启发函数权重
d = w * math.hypot(n1.x - n2.x, n1.y - n2.y)
return d
def calc_obstacle_map(self, ox, oy):
"""
根据障碍物坐标构建网格地图
"""
self.min_x = round(min(ox))
self.min_y = round(min(oy))
self.max_x = round(max(ox))
self.max_y = round(max(oy))
self.x_width = round((self.max_x - self.min_x) / self.resolution)
self.y_width = round((self.max_y - self.min_y) / self.resolution)
# 初始化障碍物地图为无障碍状态
self.obstacle_map = [[False for _ in range(self.y_width)] for _ in range(self.x_width)]
for ix in range(self.x_width):
x = self.calc_grid_position(ix, self.min_x)
for iy in range(self.y_width):
y = self.calc_grid_position(iy, self.min_y)
for iox, ioy in zip(ox, oy):
d = math.hypot(iox - x, ioy - y)
if d <= self.rr: # 若距离小于机器人半径,则标记为障碍
self.obstacle_map[ix][iy] = True
break
def get_motion_model(self):
"""
定义移动模型:包含8个方向的移动(四邻域+斜向),以及对应移动代价
"""
return [
[1, 0, 1], # 右
[0, 1, 1], # 上
[-1, 0, 1], # 左
[0, -1, 1], # 下
[-1, -1, math.sqrt(2)], # 左下(对角线)
[-1, 1, math.sqrt(2)], # 左上(对角线)
[1, -1, math.sqrt(2)], # 右下(对角线)
[1, 1, math.sqrt(2)] # 右上(对角线)
]
[1, -1, math.sqrt(2)], [1, 1, math.sqrt(2)]
def calc_grid_position(self, index, min_pos):
return index * self.resolution + min_pos
def calc_xy_index(self, position, min_pos):
return round((position - min_pos) / self.resolution)
def calc_grid_index(self, node):
return (node.y - self.min_y) * self.x_width + (node.x - self.min_x)
def verify_node(self, node):
px = self.calc_grid_position(node.x, self.min_x)
py = self.calc_grid_position(node.y, self.min_y)
if px < self.min_x or py < self.min_y or px >= self.max_x or py >= self.max_y:
return False
if self.obstacle_map[node.x][node.y]:
return False
return True
def calc_final_path(self, goal_node, closed_set):
rx, ry = [self.calc_grid_position(goal_node.x, self.min_x)], \
[self.calc_grid_position(goal_node.y, self.min_y)]
parent_index = goal_node.parent_index
while parent_index != -1:
n = closed_set[parent_index]
rx.append(self.calc_grid_position(n.x, self.min_x))
ry.append(self.calc_grid_position(n.y, self.min_y))
parent_index = n.parent_index
return rx, ry
基于采样的路径规划方法:RRT(快速探索随机树)
在面对高维空间(例如机械臂的关节空间)或存在非完整约束(如车辆无法原地旋转)的问题时,传统的网格搜索类算法如 A* 效率显著下降。此时,RRT(Rapidly-exploring Random Tree)及其优化版本 RRT* 成为更合适的选择。这类算法通过在配置空间中进行随机采样,逐步构建一棵能够快速覆盖自由空间的搜索树。
RRT 的核心流程包括以下几个步骤:
- 在可行空间内随机生成一个目标点 xrand;
- 在当前已构建的树结构中,查找距离 xrand 最近的节点 xnearest;
- 从 xnearest 沿着指向 xrand 的方向延伸一个固定步长,生成候选新节点 xnew;
- 检查从 xnearest 到 xnew 的路径是否发生碰撞;若无障碍,则将 xnew 添加至树中作为新分支。
机器人行为决策机制:赋予智能逻辑
路径规划主要解决“如何从起点到达终点”的几何问题,而决策算法则关注“在不同情境下应采取何种行为”,即实现高层的行为控制逻辑。通常,决策模块位于感知与规划之间,负责根据环境状态选择合适的动作策略。
有限状态机(Finite State Machine, FSM)
FSM 是一种经典且广泛应用的决策建模方式。系统在任意时刻仅处于一个明确的状态中,当外部触发事件或内部条件满足时,系统将按照预定义规则切换到下一个状态。
优势:
结构清晰、逻辑直观,适合实现简单任务场景下的行为切换。
局限性:
随着状态数量增加,状态之间的转移关系迅速膨胀,导致状态图复杂度剧增,维护困难,这种现象被称为“状态爆炸”(State Explosion)。
Python 实现示例:巡逻机器人状态机
以下代码演示了一个基本的 FSM 架构,用于模拟一个具有电量管理功能的巡逻机器人。
import time
import random
class State:
def run(self, robot):
pass
def next(self, robot):
pass
# 巡逻状态
class PatrolState(State):
def run(self, robot):
print(f"[{robot.name}] 正在巡逻中... 电量: {robot.battery}%")
robot.battery -= 10
time.sleep(0.5)
# 状态基类
class State:
def run(self, robot):
pass
def next(self, robot):
pass
# 1. 巡逻状态
class PatrolState(State):
def run(self, robot):
print(f"[{robot.name}] 正在巡逻... 电量: {robot.battery}%")
time.sleep(0.5)
def next(self, robot):
if robot.battery < 20:
return ChargeState()
if robot.detect_enemy():
return AttackState()
return self
# 2. 充电状态
class ChargeState(State):
def run(self, robot):
print(f"[{robot.name}] 正在充电... 电量: {robot.battery}%")
robot.battery += 20
time.sleep(0.5)
def next(self, robot):
if robot.battery >= 100:
robot.battery = 100
return PatrolState()
return self
# 3. 攻击状态
class AttackState(State):
def run(self, robot):
print(f"[{robot.name}] 发现敌人!开火!")
robot.battery -= 5
time.sleep(0.5)
def next(self, robot):
if not robot.detect_enemy():
print(f"[{robot.name}] 敌人已消除,继续巡逻。")
return PatrolState()
if robot.battery < 10:
return ChargeState() # 紧急进入充电模式
return self
# 机器人主体类
class Robot:
def __init__(self, name):
self.name = name
self.battery = 100
self.state = PatrolState()
def detect_enemy(self):
# 模拟随机探测到敌人的概率
return random.random() < 0.1
def update(self):
self.state.run(self)
self.state = self.state.next(self)
# 启动模拟运行
bot = Robot("Wall-E")
for _ in range(15):
bot.update()
行为树(Behavior Tree, BT)
为克服有限状态机(FSM)在复杂逻辑下扩展性差的问题,当前主流的游戏AI与机器人系统(例如ROS 2 Navigation Stack)普遍采用行为树架构。
行为树由两类核心节点构成:
- 控制节点:用于管理子节点的执行顺序和逻辑判断。
- 执行节点:执行具体动作或条件检测。
常见控制节点包括:
- Sequence(->):按顺序执行所有子节点,一旦某个节点失败则中断流程。
- Selector(?):尝试依次执行子节点,直到有一个成功即停止。
该结构具备高度模块化特性,支持将基础行为像积木一样灵活组合,从而构建复杂的决策逻辑。
规划与决策的协同机制
在实际工程实现中,决策系统与路径规划系统形成一个闭环反馈结构,二者紧密协作。
典型数据流流程
感知 (Perception) → 决策 (Decision) → 规划 (Planning) → 控制 (Control)
感知层:通过传感器(如激光雷达)识别环境信息,例如检测到前方5米处存在静态障碍物。
决策层:
当前运行模式为高速巡航状态。
基于 FSM 或 BT 的逻辑判断:发现障碍物后触发“变道避让”行为。
输出指令:目标位置更新至左侧相邻车道的安全区域。
规划层:
接收新的目标点坐标。
调用局部路径规划算法(如基于多项式插值的曲线生成),计算出一条平滑且安全的变道轨迹。
控制层:
利用控制器(如PID或MPC——模型预测控制)驱动车辆执行机构,精确跟踪所规划的轨迹,完成转向与速度调节。
进阶技术:轨迹优化(Trajectory Optimization)
传统A*等算法仅生成几何意义上的路径(Path),即一系列空间坐标点。而真实机器人需要的是包含时间维度的轨迹(Trajectory),其中应涵盖位置、速度、加速度乃至更高阶的动力学参数。
在Python生态中,常使用以下工具进行轨迹优化处理:
scipy.optimize
cvxpy
其中应用最广泛的方法是最小化加加速度(Minimum Jerk)的多项式轨迹设计。其优化目标函数如下:
J = ∫ || p(t) || dt
该方法通过降低运动过程中的“抖动”程度,使得机器人的动作更加平稳,有效保护执行器(如电机),同时提升整体控制精度与乘坐舒适性。
Python生态系统中的实用工具库
若无需从底层完全自研,以下Python开源库可极大加速路径规划与智能决策系统的开发进程:
- PythonRobotics:提供丰富的移动机器人算法示例,涵盖路径跟踪、避障、SLAM等功能模块,适合学习与原型开发。
收录了几乎所有主流机器人算法(如 A*、RRT、D*、SLAM、MPC 等)的 Python 实现,GitHub 上星标数极高,是学习相关技术的优质资源。
NetworkX:
这是一个功能强大的图论工具库。尽管其主要应用于网络结构分析,但在处理拓扑地图中的路径搜索问题时也表现出极高的实用性。
OMPL (Open Motion Planning Library):
基于 C++ 开发的开源运动规划库,同时提供 Python 接口绑定。作为工业级采样规划解决方案,支持多种经典算法,包括 RRT、EST 和 KPIECE,适用于复杂高维空间下的路径探索。
Gym (OpenAI):
强化学习领域的标准实验环境平台。若希望尝试使用 DQN 或 PPO 等方法实现端到端路径规划,Gym 是不可或缺的学习与开发工具。

总结
路径规划赋予机器“行走的智慧”,而决策算法则为其注入“判断的灵魂”。
- 在静态环境中,A* 算法通常是首选方案。
- 面对复杂高维环境,RRT 及其优化版本 RRT* 显得尤为关键。
- 处理简单逻辑控制任务时,有限状态机(FSM)已足够高效实用。
- 在涉及复杂交互行为的场景下,推荐采用行为树(Behavior Tree)架构。
Python 以其简洁易学的特性,大幅降低了接触和实现这些复杂算法的门槛。无论你是在准备算法岗位面试,还是致力于开发个人机器人项目,掌握上述核心算法都将成为你技术能力中极为重要的一部分。
好书推荐
《Python路径规划与决策算法》
内容简介:
本书系统讲解如何使用 Python 语言实现自动驾驶中的路径规划与智能决策算法,并结合具体案例演示各算法的实际应用。全书共分10章,内容涵盖:Dijkstra 算法、A* 算法、Bellman-Ford 算法、Floyd-Warshall 算法、D* 算法、D* Lite 算法、RRT 算法、其他典型路径规划方法、智能行为决策机制,以及基于 SLAM 的自主导航系统设计与实现。


雷达卡


京公网安备 11010802022788号







