如果只看标题,很容易误以为“运动规划”只是给机器人找一条路:起点 A,终点 B,中间绕开障碍物。但在真实的具身智能系统里,这件事远比“导航”两个字复杂得多——它要同时处理连续空间、机械结构约束、环境不确定性,还要把“规划结果”真正变成电机能执行的指令。很多时候,感知模型已经把目标识别得足够清楚,机器人依然撞墙、抖动、卡死,问题往往就出在运动规划这一层。
这篇文章的核心判断是:在具身智能的技术链条里,“看见”解决的是世界模型问题,“行动”解决的才是物理世界交互问题,而运动规划恰好是连接两者的桥梁。没有这条桥梁,再强的视觉模型也只是“纸上谈兵”。本文将先从运动规划要解决的底层问题讲起,再拆解主流算法家族,最后给出可运行的 A* 和 RRT 最小示例,并讨论从仿真走向真机的关键注意事项。
markdown复制具身智能真正难的,从来不是“看见”,而是“行动”。一个视觉大模型可以轻松识别桌上一只杯子,但要让机械臂在不碰倒旁边花瓶的前提下伸过去、握住它、平稳拿起来,每一步都涉及复杂的计算。运动规划(Motion Planning)要回答的问题,就是“如何从当前状态到达目标状态”,并且在这个过程中满足物理约束、避开障碍物、保持系统稳定。
如果你接触过导航系统,可能会觉得运动规划很像地图寻路。但从二维地图到高维机械臂空间,问题的复杂度是指数级上升的。本文会从运动规划的核心概念讲起,梳理图搜索、采样、优化三类主流方法,给出两个可以直接跑通的 Python 示例,然后重点讨论从仿真到真机部署时那些容易被忽略的坑。
1. 这篇文章真正要解决的问题
我在一些具身智能项目社区里看到,很多初学者把大量精力花在训练视觉模型上,却不理解为什么同一套感知结果放到不同机器人上,动作效果会天差地别。有人说模型不够强,有人怀疑硬件精度不够,但实际上,真正的问题往往出现在运动规划环节——规划器没有把“机械臂能不能到这个点”“关节转速够不够”“路径是否平滑可执行”这些约束考虑进去。
这篇文章不完全是一篇算法综述,更像是一份“运动规划从入门到跑通”的实战地图。读完你至少能解决以下几个问题:
- 运动规划在具身智能系统中的位置是什么?它和感知、控制如何配合?
- 路径规划和轨迹规划有什么区别?为什么不能混为一谈?
- 面对一个具体场景,应该选 A*、RRT 还是优化类算法?
- 如何用 Python 快速验证一个规划算法的效果?
- 从仿真部署到真实机械臂时,哪些坑最容易踩?
推荐的阅读人群是:准备入门具身智能、机器人学的研究生或工程师;已经在做感知或控制、想补齐规划知识的技术人;以及想要快速跑通一个运动规划 Demo 但不想被 ROS 复杂配置劝退的开发者。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 运动规划的核心概念:工作空间、配置空间与路径
2.1 工作空间与配置空间
很多教程一上来就抛公式,但我建议先从两个关键概念入手:工作空间(Workspace)和配置空间(Configuration Space)。
工作空间很好理解,就是机器人末端或机体在物理世界中能到达的三维空间的集合。例如一个六轴机械臂,它的工作空间是一个近似球形的区域,受限于各关节长度和角度范围。
配置空间则抽象得多。它描述的是“机器人所有关节角度(或自由度)数值构成的空间”。假设一个机械臂有 6 个关节,那么它的配置空间就是 6 维的,每一个点对应一组具体的关节角度值。动一个关节,就是在配置空间里移动了一个单位。
为什么要引入配置空间?因为障碍物约束、机械限位约束放在工作空间里非常难表达。把一个机械臂看成配置空间里的一个“点”,避障问题就变成了“在这个高维空间里,从一个点移动到另一个点且不进入障碍区域”的寻路问题。这就是为什么运动规划算法的复杂度会随自由度增加而急剧上升——空间维度上去了,搜索量完全不是一个量级。
2.2 路径规划与轨迹规划的边界
运动规划这个概念经常被拆成两个层次:路径规划(Path Planning)和轨迹规划(Trajectory Planning)。
路径规划只负责找出一条几何上可行的连接起点和终点的路径。它默认不关心“速度”“加速度”这些时间属性。比如二维栅格地图里 A* 搜出来的一条折线,就是一条路径。
轨迹规划则是在已确定路径的基础上,给路径上的每个点赋予时间信息,告诉机器人“什么时刻应该到达哪个位置”“速度曲线怎么变化”“加速度如何控制”。常见的梯形速度规划、S 型速度规划,都属于轨迹规划范畴。
在工程项目里,这两个层次通常分开做:先规划路径,再规划轨迹。但实时的运动规划往往需要将两者融合考虑,因为纯路径规划无法保证机械臂运行平稳,纯轨迹规划在复杂环境里又缺少全局方向。这也是业界常说“路径规划负责去哪里,轨迹规划负责怎么去”的原因。
2.3 运动规划要回答的三个问题
把概念拆开之后,我们可以把运动规划要解决的问题归纳为三个:
- 可行性问题:是否存在一条连接起点和终点的无碰撞路径?
- 最优性问题:在所有可行路径中,能否找到满足性能指标(路径最短、耗能最低、运行时间最短)的路径?
- 实时性问题:在环境动态变化时,规划器能否在允许时间内重新计算出新路径?
不同的算法家族在这三个问题上的侧重点差异很大。有的算法天生只会解决“可行性”,最优性需要额外扩展;有的算法能给出全局最优解,但计算量在复杂环境中根本无法实时运行。这也是为什么做具身智能系统集成时,很少只用一种规划算法,而是做分层规划或混合规划。
3. 主流算法家族与选型思路
3.1 图搜索类算法:从 Dijkstra 到 A*
图搜索是运动规划最基础的一类算法。核心思路是把配置空间离散化为网格或图结构,然后在图上搜索最短路径。
Dijkstra 算法是经典的最短路径算法,它从起点出发,不断扩展已知最短路径的节点,直到到达终点。优点是保证找到代价最低的路径,缺点是搜索没有方向性,在无障碍的空旷环境里它会把周围所有方向都扩展一遍,造成大量无效计算。
A* 在 Dijkstra 基础上加入了一个启发式函数,用“当前已走路程 + 到目标点的预估距离”作为排序依据,让搜索优先朝目标方向进行。A* 在栅格地图上非常高效,常用于二维导航、游戏寻路和移动机器人路径规划。
但 A* 有个明显短板:它需要把空间离散化。机械臂的配置空间一旦超过三维,网格数量会爆炸式增长,内存和计算时间都不可接受。所以图搜索主要适用于低维度规划,或者作为全局规划的上层模块。
3.2 采样类算法:RRT、RRT* 与 PRM
采样类算法是解决高维运动规划的利器。它不需要把整个配置空间离散化,而是通过随机采样来逐步探索可行区域。
RRT(Rapidly-exploring Random Tree)的基本原理是:从起点开始,维护一棵随机扩展树。每次在配置空间里随机采一个点,然后找到树上距离该点最近的节点,沿连线方向扩展一步。如果这一步没有撞到障碍,就把它加入树中。反复迭代,直到树到达目标点附近。
RRT 的最大优点是快,在高维空间里也能快速找到一条可行路径。但它不保证路径最优,甚至同样的起点终点,两次运行可能得到完全不同的路径。RRT* 在 RRT 基础上增加了重新选择父节点和重新布线(rewire)的操作,使路径逐步趋向最优,代价是计算量更高。
PRM(Probabilistic Roadmap)的思路则是先在工作空间或配置空间里建立一张无向图(路线图),再在图上搜索路径。它适合在静态环境中进行多次规划,因为一次建图后可以反复查询。当环境变化频繁时,PRM 的建图成本会成为明显瓶颈。
3.3 优化类算法:从轨迹到控制一体化
优化类算法把规划问题建模成一个带约束的最优化问题。目标函数可以是路径长度、平滑度、动力学代价,约束包括避障、关节限位、加速度限制等。
常见的优化方法包括 CHOMP(Covariant Hamiltonian Optimization for Motion Planning)、TrajOpt 以及基于梯度下降的轨迹优化算法。这类算法能生成非常平滑、适合直接执行的轨迹,但需要给定一个较好的初始解,否则容易陷入局部最优。
在实际工程中,优化类算法通常与采样类算法配合使用:先用 RRT 或 PRM 找到一条粗略路径,再用优化算法平滑并优化它。这种“采样生成初始解 + 优化精修”的范式,是当前工业级机械臂运动规划的主流做法。
3.4 算法选型参考表
| 算法 | 维度适用性 | 最优性 | 实时性 | 典型使用场景 |
|---|---|---|---|---|
| Dijkstra | 低维 | 全局最优 | 中低 | 二维静态地图最短路径 |
| A* | 低维 | 全局最优(用对启发函数时) | 中 | 移动机器人导航、栅格地图 |
| RRT | 高维 | 不保证最优 | 高 | 机械臂高维空间快速寻路 |
| RRT* | 高维 | 渐近最优 | 中低 | 对路径质量有一定要求的机械臂任务 |
| PRM | 中高维 | 受路线图影响 | 查询快、建图慢 | 静态环境多次规划 |
| CHOMP/TrajOpt | 高维 | 局部最优 | 中 | 对平滑轨迹有要求的精修阶段 |
选型没有绝对标准,核心原则是:低维空间优先选 A* 等确定性算法;高维空间用采样算法保证“能找到”;需要高质量可执行轨迹时,在采样基础上叠加优化精修。
4. 环境准备与仿真工具选择
在进入代码之前,先明确一下运行环境。本文示例以 Python 为主,只需要安装基础的科学计算库。
bash复制python -m venv motion_planning_env
source motion_planning_env/bin/activate # Windows 下使用 motion_planning_env\Scripts\activate
pip install numpy matplotlib
numpy 负责矩阵运算和采样逻辑,matplotlib 负责把路径可视化出来。这两个库足够跑通本文所有示例,不需要引入 ROS 或 MoveIt,降低入门门槛。
如果要做更接近工程级的验证,推荐按复杂度从低到高选择:
- PyBullet:轻量物理仿真,内置运动学、刚体动力学,适合机械臂仿真和碰撞检测。
- MuJoCo:物理仿真精度高,许多强化学习和具身智能项目用它作为 MuJoCo 环境。
- CoppeliaSim(V-REP):图形化界面友好,适合场景搭建和算法验证。
- Isaac Sim / Isaac Lab:基于 NVIDIA Omniverse,适合做大规模仿真和合成数据生成。
版本选择建议以官方文档为准,不必追新。仿真工具的作用是验证算法在接近真实物理条件下的表现,但它永远不能替代真机测试。关于这一点,第 8 节会再展开。
对于机械臂场景,还有一个容易被忽略的“安装路径”问题:机械臂的底座安装位置直接决定了工作空间覆盖范围。同样是“从 A 点抓取到 B 点放置”,基座装高了、偏了,规划器可用的连接空间都会改变。这提醒我们,运动规划不是孤立模块,它和机械结构设计有强耦合关系。
5. 最小可运行示例:A* 栅格路径规划
5.1 示例场景
假设我们有一张 30x30 的二维栅格地图,黑色区域是障碍物,起点在左上角附近,终点在右下角附近。先用 A* 找到一条绕过障碍物的最短路径,并把搜索过程和最终路径可视化。
这个例子虽然简单,但能帮助理解图搜索的核心流程:维护开放列表、闭合列表、评估函数 f(n) = g(n) + h(n),其中 g 是实际已走路程,h 是到目标的启发式估计。
5.2 完整代码
python复制# 文件路径:astar_grid_demo.py
import heapq
import numpy as np
import matplotlib.pyplot as plt
def heuristic(a, b):
# 欧几里得距离作为启发函数
return np.hypot(a[0] - b[0], a[1] - b[1])
def astar(grid, start, goal):
rows, cols = grid.shape
open_heap = []
heapq.heappush(open_heap, (0.0, start))
came_from = {}
g_score = {start: 0.0}
while open_heap:
_, current = heapq.heappop(open_heap)
if current == goal:
# 重建路径
path = []
while current in came_from:
path.append(current)
current = came_from[current]
path.append(start)
path.reverse()
return path
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
if dx == 0 and dy == 0:
continue
neighbor = (current[0] + dx, current[1] + dy)
if not (0 <= neighbor[0] < rows and 0 <= neighbor[1] < cols):
continue
if grid[neighbor] == 1: # 1 表示障碍物
continue
# 对角移动代价略高,避免斜穿墙角
step_cost = 1.0 if (dx == 0 or dy == 0) else np.sqrt(2.0)
tentative_g = g_score[current] + step_cost
if tentative_g < g_score.get(neighbor, float('inf')):
came_from[neighbor] = current
g_score[neighbor] = tentative_g
f_score = tentative_g + heuristic(neighbor, goal)
heapq.heappush(open_heap, (f_score, neighbor))
return None # 找不到路径
def main():
np.random.seed(42)
grid = np.zeros((30, 30))
# 生成一些模拟障碍物
for _ in range(150):
x, y = np.random.randint(0, 30, 2)
grid[x, y] = 1
# 清空起点和终点附近
grid[0, 0] = 0
grid[29, 29] = 0
start = (0, 0)
goal = (29, 29)
path = astar(grid, start, goal)
if path is None:
print("No path found")
return
path = np.array(path)
plt.figure(figsize=(6, 6))
plt.imshow(grid, cmap='Greys', origin='upper')
plt.plot(path[:, 1], path[:, 0], color='red', linewidth=2, label='A* path')
plt.scatter([start[1], goal[1]], [start[0], goal[0]], color=['green', 'blue'], zorder=5, label='start/goal')
plt.legend()
plt.title('A* Path Planning on 2D Grid')
plt.show()
print("Path length:", len(path))
if __name__ == '__main__':
main()
5.3 关键逻辑说明
A* 的核心在于 open_heap 里的排序键 f_score。它综合了当前实际代价和未来预估代价,使得算法在做选择时既有全局方向感,又不会完全无视已经探索过的可行区域。
代码里有两个容易被忽略的细节。第一个是移动代价:对角移动使用 sqrt(2),直连移动使用 1,这样斜穿障碍物墙角时会因为代价更高而被算法规避。第二个是闭合列表的处理:这里用 g_score 字典的无穷大初始值隐式完成了“如果邻居代价更小才更新”的判断,避免重复加入同一个节点。
运行后如果一切正常,会看到一张带红色路径的网格图。如果地图太密集导致路径不存在,程序会输出“No path found”,这也是 A* 的一种正常行为:它不会强行给出不可行路径,而是诚实地告诉系统此路不通。
6. 进阶示例:RRT 高维空间路径搜索
6.1 为什么从 A* 换到 RRT
A* 在栅格地图上表现很好,但一旦把问题扩展到机械臂,网格离散化就会失效。一个六轴机械臂的配置空间是 6 维的,如果每个维度只切 100 份,就是 100 的 6 次方个网格点,A* 根本无法运行。
RRT 不需要枚举这些网格。它通过随机采样把探索集中在可行区域,在高维空间中通常能在几毫秒到几百毫秒内找到可行路径。下面用一个二维连续空间的例子演示 RRT 的核心逻辑,代码稍加改造就能推广到更高维度。
6.2 完整代码
python复制# 文件路径:rrt_demo.py
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
class RRT:
def __init__(self, obstacle_list, start, goal, bounds,
step_size=1.0, max_iter=1000, goal_tolerance=0.5):
self.obstacles = obstacle_list # [(x_min, y_min, x_max, y_max), ...]
self.start = np.array(start, dtype=float)
self.goal = np.array(goal, dtype=float)
self.bounds = bounds # (x_min, x_max, y_min, y_max)
self.step_size = step_size
self.max_iter = max_iter
self.goal_tolerance = goal_tolerance
def collision_free(self, p1, p2):
# 分段采样检查连线是否碰障碍
dist = np.linalg.norm(p2 - p1)
n_check = max(int(dist / 0.1), 2)
for t in np.linspace(0, 1, n_check):
point = p1 + (p2 - p1) * t
for (x_min, y_min, x_max, y_max) in self.obstacles:
if x_min <= point[0] <= x_max and y_min <= point[1] <= y_max:
return False
return True
def plan(self):
nodes = [self.start]
parents = [-1]
for _ in range(self.max_iter):
# 以一定概率直接采目标点,加快收敛
if np.random.rand() < 0.1:
rand_point = self.goal
else:
rand_point = np.array([
np.random.uniform(self.bounds[0], self.bounds[1]),
np.random.uniform(self.bounds[2], self.bounds[3])
])
# 找最近节点
dists = [np.linalg.norm(n - rand_point) for n in nodes]
nearest_idx = int(np.argmin(dists))
nearest = nodes[nearest_idx]
# 从最近节点向随机点扩展一步
direction = (rand_point - nearest) / (np.linalg.norm(rand_point - nearest) + 1e-6)
new_point = nearest + direction * self.step_size
new_point[0] = np.clip(new_point[0], self.bounds[0], self.bounds[1])
new_point[1] = np.clip(new_point[1], self.bounds[2], self.bounds[3])
if not self.collision_free(nearest, new_point):
continue
nodes.append(new_point)
parents.append(nearest_idx)
if np.linalg.norm(new_point - self.goal) < self.goal_tolerance:
nodes.append(self.goal)
parents.append(len(nodes) - 2)
return self.extract_path(nodes, parents)
return None
def extract_path(self, nodes, parents):
path = []
idx = len(nodes) - 1
while idx != -1:
path.append(nodes[idx])
idx = parents[idx]
path.reverse()
return np.array(path)
def main():
np.random.seed(7)
obstacles = [(8, 5, 12, 15), (4, 12, 9, 18), (14, 3, 20, 8), (16, 12, 22, 20)]
start = (1, 1)
goal = (27, 25)
rrt = RRT(obstacle_list=obstacles, start=start, goal=goal,
bounds=(0, 30, 0, 30), step_size=1.2, max_iter=3000, goal_tolerance=0.8)
path = rrt.plan()
if path is None:
print("RRT failed to find a path")
return
fig, ax = plt.subplots(figsize=(6, 6))
for (x_min, y_min, x_max, y_max) in obstacles:
ax.add_patch(Rectangle((x_min, y_min), x_max - x_min, y_max - y_min,
color='black', alpha=0.6))
ax.plot(path[:, 0], path[:, 1], color='red', linewidth=2, label='RRT path')
ax.scatter([start[0], goal[0]], [start[1], goal[1]], s=80, color=['green', 'blue'])
ax.set_xlim(0, 30)
ax.set_ylim(0, 30)
ax.set_aspect('equal')
ax.legend()
ax.set_title('RRT Path Planning in Continuous Space')
plt.show()
print("Path points:", len(path))
if __name__ == '__main__':
main()
6.3 关键逻辑说明
RRT 的关键有三步:随机采样、找最近节点、沿方向扩展。为了让算法更快收敛到目标,代码里以 10% 概率直接把目标点作为采样点,其余 90% 保持随机采样以充分探索自由空间。
collision_free 函数把两个节点之间的连线按 0.1 间隔密集采样,检查所有采样点是否落入障碍物矩形。这个碰撞检测方式虽然朴素,但在二维示例里足够直观。在真实工程中,碰撞检测会使用更高效的数据结构(如 FCL、Bullet Collision),且会针对机械臂模型做精确计算。
运行多次你会发现,RRT 每次生成的路径都不一样,这正是采样类算法的典型特征:它不追求最优,只保证可行。如果暂时找不到路径,可以增大 max_iter,或者调整 step_size。注意 step_size 太大容易跨越狭窄通道,太小则探索缓慢。这个参数是 RRT 最重要的调节旋钮之一。
7. 常见问题与排查思路
在学习和实际部署运动规划时,以下问题出现频率最高。保留这张排查表,遇到问题时先按顺序检查。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| A* 找不到路径 | 起点或终点被障碍物占据;网格分辨率太低导致通道被抹平 | 打印 grid 检查起点终点值;可视化地图观察通道 | 清空起点终点周围区域;提高地图分辨率 |
| A* 路径明显绕路 | 启发函数设置不合理;对角移动代价未考虑 | 检查启发函数是否可采纳(不高估真实代价) | 改用曼哈顿或欧几里得距离,按移动方式匹配 |
| RRT 收敛极慢 | step_size 过小;目标偏置概率太低 | 打印每次迭代后树节点的数量 | 增大 step_size;适当提升目标偏置概率 |
| RRT 路径抖动大 | RRT 本身不保证光滑性 | 观察路径是否包含大量折线 | 叠加平滑算法或使用 RRT*;后续做轨迹优化 |
| 真机运行时抖动明显 | 路径规划结果直接发给底层控制器,中间缺少轨迹规划 | 检查是否只有 path 没有 trajectory | 增加速度、加速度规划,或用优化算法平滑 |
| 机械臂运行中撞到障碍物 | 仿真模型与真机尺寸或形状不一致 | 对比碰撞检测模型与实际装配 | 使用精确的 URDF/Mesh 模型;部署前做碰撞测试 |
| 规划耗时过长 | 高维空间暴力采样;碰撞检测函数低效 | 用 profile 统计主要开销 | 改用 RRT* 渐进优化;增加碰撞检测加速结构;必要时降维处理 |
这里特别想强调一个新手误区:很多人看到“RRT 找到了路径”就直接把它发到真机执行,结果机械臂动作极其生硬。这是因为 RRT 搜索出来的只是一系列离散路径点,没有考虑速度、加速度、关节力矩约束。真正可以执行的轨迹,需要经过轨迹规划或优化类算法精修,否则不仅动作难看,还可能损伤电机和减速器。
8. 从仿真到真机的最佳实践
8.1 路径规划结果不能直接执行
如果只记住一条工程经验,那就是上面这句话。仿真里看起来正常的路径,到了真机上至少还要过三关:运动学约束(关节速度极限、加速度极限)、动力学约束(力矩、惯性)、控制稳定性(跟踪误差)。规范的流程是“感知 → 规划路径 → 轨迹规划 → 控制跟踪”,每一步都要安全检查。
在轨迹规划阶段,比较常用的是梯形速度曲线或 S 型速度曲线。前者实现简单,但在加速度突变处会有冲击;后者引入 jerk(加加速度)限制,适合对平稳性要求高的搬运场景。选择依据是任务类型:码垛、焊接、装配对平滑性要求高,搬运大体一致对效率更敏感。
8.2 安全边界与最小权限原则
运动规划在真机部署时必须建立多层安全机制:
- 软件限位:关节角度范围在规划器里就硬编码约束,超出即拒绝方案。
- 硬件急停:机械臂控制系统保留独立急停链路,不依赖上层软件。
- 速度缩放:首次运行或环境不明确时,将规划输出速度乘以 0.1 甚至 0.05,先慢速验证。
- 碰撞检测兜底:即使规划器认为路径无碰撞,控制层仍然要保留实时碰撞检测接口,遇到异常力或异常位置偏差立即暂停。
- 回滚预案:记录每次规划的参数和结果,真机出现异常时能快速回退到上一版安全参数。
这些听起来不像算法内容,但实际项目里它们往往比算法选型更决定成败。
8.3 参数调优建议
运动规划器的参数没有通用魔法值,但有几个经验方向:
- RRT 的 step_size 先按工作空间最大尺寸的 1%~3% 起步。
- 目标偏置概率建议在 0.05~0.2 之间,太大容易早熟,太小收敛慢。
- A* 的启发函数要保证“不高估代价”,这是最优性成立的前提。
- 优化类算法的步长设置要配合障碍代价权重,权重过大路径会过度绕开障碍,过小则容易碰撞。
- 每次只调一个参数。同时调整多个参数,很难判断是哪个变量改善了效果。
8.4 与感知和控制的接口设计
运动规划器不是孤立模块。它从感知层接收目标状态,从环境模型接收障碍物信息,然后输出轨迹给控制层。这个接口设计有几个建议:
- 感知输出的目标位置,要经过位姿校验(例如物体是否在机械臂可达范围内)再传给规划器。
- 障碍物信息要统一建模到同一个坐标系下,坐标系转换错误是最隐蔽的 bug。
- 控制层跟踪误差超过阈值时,应该触发规划器重新规划,而不是盲目继续执行。
- 整个链路的日志要贯穿感知、规划、控制三个模块,否则排查问题时信息断层非常痛苦。
9. 总结与后续学习方向
回到开头那句话:具身智能真正难的,从来不是“看见”,而是“行动”。运动规划作为从感知到控制的关键桥梁,决定了机器人能不能在真实物理世界里可靠地完成“从 A 点到 B 点”的任务。
本文把运动规划拆成了三个层面:概念层(工作空间、配置空间、路径与轨迹)、算法层(图搜索、采样、优化三类主流方法)、工程层(从仿真到真机的安全与接口问题)。三个最小示例代码可以直接运行、可视化并修改,建议收藏备用。
下一步如果继续深入,建议按这条路走:
- 先学会在二维空间里把 A* 和 RRT 的代码改到三维,理解维度对算法的影响。
- 再用 PyBullet 加载一个真实的机械臂模型,把 RRT 改造成针对机械臂配置空间的规划器。
- 之后尝试 RRT* 和 CHOMP,对比不同算法在路径平滑度和规划耗时上的差异。
- 最后把规划结果接到速度规划模块,观察机械臂在真机上的动作质量。
运动规划是一个理论深度和工程复杂度都很高的方向,不可能一篇文章讲完。但先把 A* 和 RRT 这两个代表算法跑通,再理解配置空间和轨迹规划的差异,你就已经具备独立解决常见规划问题的能力了。真正的难点从来不在某个算法本身,而在于把算法放到真实系统中时,如何让每一层约束都恰好被满足。
