资讯详情 智能机器人路径规划实战:从课件习题到可运行代码
📅 2026/10/3 8:07:46
简介这份资源是清华大学精品人工智能课程第11章「智能机器人」的配套课件共47页PPT面向正在系统学习人工智能、希望补齐机器人方向知识的高校学生与自学者。内容从智能机器人的基本概念讲起梳理三代机器人的演进脉络并深入多传感器融合、自主导航与避障、路径规划、智能控制、人机接口等关键技术同时覆盖控制策略与实际应用场景章末附有习题便于自测巩固。资源包共1个pptx文件约3.39MB页面结构清晰、图文并茂可直接用于课堂讲授、复习梳理或自学笔记整理。目前已有236人学习下载。对于想理解机器人如何感知环境、规划路径并做出决策的读者这份课件能提供一条从概念到关键技术的完整学习线索也适合作为人工智能课程体系中的专题补充材料。1. 从一份 47 页课件说起智能机器人到底卡在哪很多人第一次接触智能机器人是从一份课件开始的。清华大学那套人工智能精品课程里第 11 章专门讲智能机器人47 页 PPT 配着习题看起来像是一份普通的课堂材料但真正翻进去会发现它把「感知—决策—执行」这条链路拆得相当细。问题在于课件给的是知识框架不是能跑的东西。你知道了机器人有传感器、有规划器、有控制器可一旦要自己搭一个能动的原型立刻就会卡在几个地方环境怎么表示、路径怎么搜、习题里的算法怎么落到代码上。这一章要解决的就是把这个落差补上。它适合两类人一类是正在学人工智能导论、人工智能基础这类课手里有课件但不知道怎么消化另一类是已经工作、想往具身智能机器人方向靠需要一个最小可复现的起点。我不打算复述 PPT而是把课件里最核心的几块——环境建模、路径规划、感知与执行——拆成能动手的步骤顺带把习题里那些容易翻车的点讲清楚。智能机器人这个词听起来大但落到工程上第一步往往就是一张栅格地图和一个能搜出路径的算法。2. 智能机器人的三层结构感知、规划、执行怎么对上号2.1 为什么课件先讲体系结构而不是直接上算法翻这套课件你会发现它没有一上来就甩公式而是先讲智能机器人的体系结构。这不是凑页数而是因为机器人系统和纯软件最大的区别在于它的每一层都受物理约束。感知层拿到的是带噪声的数据规划层算出来的路径要满足运动学约束执行层还有延迟和误差。如果一开始不把这三层的关系理清后面写代码时就会把该在规划层处理的问题丢给执行层结果就是机器人抖得像筛糠。常见的分层是「感知—建模—规划—控制」。感知负责把传感器原始数据变成可用的环境描述建模把描述组织成地图或图结构规划在地图上找一条可行路径控制把路径变成电机指令。课件里讲智能机器人时通常会强调感知和规划之间的耦合地图的精度直接决定规划的质量而规划的频率又反过来影响感知的更新节奏。这个耦合关系是后面所有实操的基础。我一般会建议先把这个结构画在纸上标清楚每一层的输入输出。比如感知层输入是激光雷达或深度相机的点云输出是占据栅格规划层输入是栅格地图和起终点输出是一串路径点控制层输入是路径点输出是速度指令。标完之后你会发现课件里那些看似独立的算法其实都挂在同一条数据流上。2.2 栅格地图把连续世界切成可计算的格子智能机器人栅格地图是绕不开的一步。连续空间没法直接搜路径必须离散化。栅格地图的做法是把环境切成固定大小的格子每个格子标记为占据、空闲或未知。课件里通常会给出分辨率的概念比如 0.05 米一格意思是每个格子代表现实中 5 厘米见方。这个参数看着小但它直接决定了地图的精度和内存占用。下面是一个把二维占据数据转成栅格并可视化的最小例子用 Python 写不依赖重型机器人框架方便你先理解结构。import numpy as np import matplotlib.pyplot as plt # 假设有一张 100x100 的占据栅格0 空闲1 占据-1 未知 # 实际项目中这份数据来自激光雷达建图这里先用模拟数据 grid np.zeros((100, 100), dtypeint) grid[30:70, 50] 1 # 一堵竖墙 grid[30, 30:70] 1 # 一堵横墙 grid[0:5, :] -1 # 顶部未知区域 resolution 0.05 # 每个格子 5 厘米 origin (0.0, 0.0) # 地图左下角对应的世界坐标 def world_to_grid(x, y): 世界坐标转栅格索引 gx int((x - origin[0]) / resolution) gy int((y - origin[1]) / resolution) return gx, gy def grid_to_world(gx, gy): 栅格索引转世界坐标取格子中心 x gx * resolution origin[0] resolution / 2 y gy * resolution origin[1] resolution / 2 return x, y # 可视化占据为黑空闲为白未知为灰 cmap plt.cm.colors.ListedColormap([gray, white, black]) plt.imshow(grid, cmapcmap, originlower) plt.title(占据栅格地图示意) plt.xlabel(栅格 X) plt.ylabel(栅格 Y) plt.show() # 验证坐标转换 print(world_to_grid(1.25, 2.50)) # 应输出 (25, 50) print(grid_to_world(25, 50)) # 应输出 (1.275, 2.525)这段代码的关键在resolution和origin两个参数。resolution越小地图越精细但同样面积下格子数量按平方增长搜索时间也会涨。origin决定了世界坐标和栅格索引的对应关系很多初学者在这里翻车建图时用的原点和规划时用的原点不一致导致路径整体偏移。代码里world_to_grid和grid_to_world是一对逆运算写完一定要拿几个点验证别等到机器人跑偏了才回头查。提示栅格地图的未知区域不要简单当成空闲。规划时把未知当空闲机器人可能一头撞进没探过的地方把未知当占据又可能把可行区域封死。常见做法是规划时对未知区域加惩罚而不是二值处理。2.3 从习题里的搜索算法到可运行代码课件第 11 章的习题里大概率会出现路径搜索相关的内容比如 A* 或者 Dijkstra。习题让你手算开放列表和关闭列表但真到工程里你得把它写成能在地图上跑的函数。下面是一个在栅格地图上跑 A* 的最小实现八邻域扩展支持对角移动。import heapq def a_star(grid, start, goal): grid: 二维数组0 可通行1 障碍 start/goal: (gx, gy) 栅格索引 返回路径列表找不到返回 None rows, cols grid.shape # 八邻域上下左右 四个对角 neighbors [(-1,0),(1,0),(0,-1),(0,1), (-1,-1),(-1,1),(1,-1),(1,1)] def heuristic(a, b): # 对角距离启发式比曼哈顿更贴合八邻域 dx abs(a[0] - b[0]) dy abs(a[1] - b[1]) return (dx dy) (1.414 - 2) * min(dx, dy) open_set [(0, start)] came_from {} g_score {start: 0} while open_set: _, current heapq.heappop(open_set) if current goal: path [current] while current in came_from: current came_from[current] path.append(current) return path[::-1] for dx, dy in neighbors: nx, ny current[0] dx, current[1] dy if not (0 nx rows and 0 ny cols): continue if grid[nx, ny] 1: continue # 对角移动时检查两个相邻格避免穿墙角 if dx ! 0 and dy ! 0: if grid[current[0] dx, current[1]] 1 or \ grid[current[0], current[1] dy] 1: continue step 1.414 if dx ! 0 and dy ! 0 else 1.0 tentative g_score[current] step neighbor (nx, ny) if tentative g_score.get(neighbor, float(inf)): came_from[neighbor] current g_score[neighbor] tentative f tentative heuristic(neighbor, goal) heapq.heappush(open_set, (f, neighbor)) return None这段代码有三个参数层面的点值得说。第一启发式函数用的是对角距离如果你用曼哈顿距离配八邻域搜索会偏向先走直线再拐弯路径不够自然。第二对角移动前检查两个相邻格这是防止机器人从两个障碍物的对角缝隙里穿过去实际机器人有体积穿墙角就是撞。第三g_score用字典存方便处理稀疏访问但如果地图很大换成数组会更快。把这段和课件习题对照你会发现习题考的是开放列表怎么更新、启发式怎么算而工程里多出来的是边界检查、穿墙检查和数据结构选择。这些多出来的部分才是真正让算法能跑在机器人上的东西。3. 把课件习题变成可复现实验环境、参数与验证3.1 搭一个最小仿真环境需要哪些模块课件给的是静态知识要复现就得有个能反复跑的环境。你不需要一上来就买机器人先用仿真把算法验证通。一个最小仿真环境包含四块地图加载、机器人位姿、运动模型、碰撞检测。地图加载负责把栅格读进来位姿记录机器人的位置和朝向运动模型根据速度指令更新位姿碰撞检测判断新位姿是否撞到障碍。常见做法是用 Python 加 Matplotlib 做可视化不引入 Gazebo 这类重型工具先把逻辑跑通。下面是一个简化的运动更新和碰撞检测示例。import math class Robot: def __init__(self, x, y, theta, radius0.2): self.x x self.y y self.theta theta # 朝向弧度 self.radius radius # 机器人半径米 def move(self, v, omega, dt): 按速度 v 和角速度 omega 更新位姿dt 为时间步长 self.x v * math.cos(self.theta) * dt self.y v * math.sin(self.theta) * dt self.theta omega * dt # 角度归一化到 [-pi, pi] self.theta (self.theta math.pi) % (2 * math.pi) - math.pi def check_collision(robot, grid, resolution, origin): 检查机器人圆形轮廓是否与占据格相交 gx int((robot.x - origin[0]) / resolution) gy int((robot.y - origin[1]) / resolution) r_cells int(math.ceil(robot.radius / resolution)) rows, cols grid.shape for i in range(gx - r_cells, gx r_cells 1): for j in range(gy - r_cells, gy r_cells 1): if 0 i rows and 0 j cols and grid[i, j] 1: # 格子中心到机器人中心的距离 cx i * resolution origin[0] resolution / 2 cy j * resolution origin[1] resolution / 2 if math.hypot(cx - robot.x, cy - robot.y) robot.radius: return True return Falseradius这个参数很关键。很多仿真里把机器人当成一个点规划出来的路径贴着墙走真机上因为体积直接撞。把半径设成实际尺寸碰撞检测才会把墙推开一段距离。dt是仿真步长设太大运动更新会跳过障碍设太小计算量上去一般取 0.05 到 0.1 秒之间。3.2 参数怎么设分辨率、步长、启发式权重参数不是拍脑袋定的每个都有对应的物理含义和取舍。下面这张表是我在复现课件实验时常用的起点你可以根据场景调。参数常用取值影响调整方向栅格分辨率0.05 m精度与内存的平衡室内小场景可到 0.02大场景放到 0.1仿真步长 dt0.05 s运动更新精度速度高时减小避免穿墙机器人半径0.2 m碰撞检测保守程度按实际尺寸加 5% 余量启发式权重1.0搜索速度与最优性大于 1 更快但路径可能次优规划频率1 到 5 Hz响应与计算负载动态环境取高值启发式权重这个参数值得单独说。标准 A* 里启发式权重是 1保证找到最短路径。但实际机器人上你往往不需要绝对最短只需要够快够安全。把权重调到 1.2 到 1.5搜索会明显变快代价是路径可能绕一点。课件习题通常默认权重为 1但工程里这个旋钮很有用。3.3 怎么验证你的实现是对的验证分两层。第一层是算法层拿课件习题里的手算结果对。比如习题给了一个小网格和起终点你手算出的路径长度和代码输出比差太多就说明实现有问题。第二层是行为层在仿真里让机器人跑一圈看它会不会撞墙、会不会卡在死角、路径是不是抖动。我一般会准备三个测试用例一个空旷地图测直线一个带凹形障碍的地图测绕行一个窄通道地图测贴边。三个都过了才认为规划器基本可用。窄通道那个最容易暴露问题因为对角移动的穿墙检查和机器人半径的碰撞检测会同时起作用参数没调好就会卡在通道口进不去。注意验证时不要只看最终路径要把开放列表的扩展过程可视化出来。很多时候路径看着对但扩展了几万个节点实际跑起来根本来不及。扩展节点数是比路径长度更早暴露性能问题的指标。4. 避坑与排查智能机器人复现里最容易翻车的五件事4.1 现象路径贴着障碍物边缘真机一跑就撞原因规划时把机器人当成质点没有膨胀障碍物或做半径碰撞检测。栅格地图里的占据格是传感器测到的障碍边界机器人有体积中心贴着边界走外壳已经进去了。解决两种做法选一种。一是规划前对障碍物做膨胀膨胀半径等于机器人半径把膨胀后的地图交给规划器二是在碰撞检测里用圆形轮廓判断像 3.1 里的check_collision。膨胀更简单但会损失一些可行空间圆形检测更精确但每次扩展都要算距离。我一般先用膨胀快速验证再换圆形检测做精细调优。4.2 现象A* 搜出来的路径拐来拐去不平滑原因八邻域搜索的路径本质上是格子序列方向只有八个天然带锯齿。启发式函数选得不对会加重这个问题比如用曼哈顿距离配八邻域搜索会偏好先横后竖。解决先换对角距离启发式能改善一部分。如果还不行在路径后处理加一步平滑常见的是梯度下降或样条拟合。但平滑后一定要重新做碰撞检测平滑可能把路径推到障碍物上。课件习题不涉及平滑但工程里这一步省不掉。4.3 现象机器人走到一半停住规划器返回 None原因目标点落在占据格里或者起点被障碍物包围。还有一种情况是地图更新后原来可行的通道被新观测的障碍堵死但规划器还在用旧地图。解决规划前检查起点和终点是否可通行不可通行就找最近的可通行格替代。动态环境里要保证地图更新和规划用同一份数据别一个线程在读一个线程在写。如果通道确实被堵死规划器返回 None 是正确行为上层要有重规划或等待逻辑而不是反复调用。4.4 现象仿真里跑得好好的换张地图就崩原因参数过拟合到某张地图。比如分辨率、机器人半径、启发式权重都是针对特定场景调的换地图后栅格尺度变了同样的半径对应的格子数变了碰撞检测的保守程度也跟着变。解决把参数和地图解耦。机器人半径用米表示碰撞检测时再按分辨率换算成格子数像 3.1 里那样。分辨率变化时只影响计算量不影响物理含义。每次换地图先跑三个标准测试用例确认基本行为没变再调场景相关参数。4.5 现象习题答案和代码输出对不上原因习题里的坐标系、栅格索引方式、邻域定义和你的代码不一致。比如习题用行列索引你的代码用 xy 索引习题四邻域你八邻域习题的启发式是曼哈顿你用了欧氏。解决先把习题的约定抄下来逐条和代码对照。最容易被忽略的是索引顺序grid[i, j]里 i 是行还是列不同教材不一样。对不上时不要改代码去凑答案先把约定统一再检查算法逻辑。如果约定统一后还对不上大概率是习题答案本身有笔误这种情况在课后题里不少见。5. 从 47 页课件到能跑的原型一个收尾技巧课件最后一页通常是习题但习题不是终点。我的习惯是每学完一章挑一道习题把它扩成一个能跑的最小原型。第 11 章智能机器人这道我会选路径规划那道题把网格从习题里的 5x5 扩到 100x100加上障碍物膨胀和路径平滑再套一个简单的运动模型让它真的动起来。这个过程里你会遇到习题里永远不会出现的边界情况起点在障碍里、目标不可达、路径抖动、参数不匹配。这些才是工程能力长出来的地方。具体技巧上我建议你给自己定一个「三图验证」的规矩每改一次规划器同时看三张图——原始栅格图、扩展节点热力图、最终路径图。原始图确认输入没被污染热力图确认搜索效率没退化路径图确认输出合理。三张图都对了再上仿真跑。这个习惯帮我省了很多次「路径看着对但机器人不动」的排查时间。还有一个参数层面的经验启发式权重不要一上来就调。先把权重设为 1确认路径最优性和碰撞检测都对再逐步加大权重换速度。反过来做你会分不清路径变差是因为权重还是因为碰撞检测有 bug。这个顺序是我踩过几次坑之后固定下来的。智能机器人这个方向课件给的是骨架真正让它站起来的是你亲手跑通的那几个最小实验。47 页 PPT 加习题认真消化完再配上能跑的代码你对感知、规划、执行这条链路的理解会完全不一样。希望帮到你。本文还有配套的精品资源点击获取