
简介针对二维静态障碍环境下的路径规划问题提供基于改进人工势场法的 MATLAB 实现适合机器人运动规划、智能算法方向的研究者与相关课程学生参考使用。压缩包一共包含 6 个文件以 5 个 .m 脚本和 1 张效果示意图为主脚本功能覆盖主程序、改进势场计算、角度处理与参数修正等模块虽然整个压缩包仅 9KB但模块划分清晰结构紧凑便于快速阅读和二次开发。当前已有 131 人学习通过配套代码可以直观对比传统人工势场法与改进方法的差异理解目标引力和障碍物斥力如何协同作用并学习如何规避局部极小值、提升路径平滑性同时还能参考如何设置二维地图、障碍物及起点终点快速搭建自己的仿真场景。借助主程序与示意图读者能快速掌握二维路径规划实验的设计流程为后续算法扩展、参数调优或工程应用提供可直接运行的起点。1. 改进人工势场在二维路径规划中的真实处境想象一个自动仓储场景AGV 需要在布满货架的仓库里从出库口走到拣选台或者一台协作机械臂要在二维平面内绕过夹具和料框到达上料点。这类问题抽象出来就是典型的二维路径规划——在地图边界、障碍物、起点和目标点都已明确的条件下找一条无碰撞且尽量短的可行路径。传统栅格法和 A* 在稠密地图上计算开销偏高而人工势场法因为模型简单、实时性好一直被当作轻量级规划方案的首选。但它有个让人纠结的标签几行代码就能跑通遇到凹形障碍物或目标贴近障碍物的场景又很容易原地打转。所谓“改进人工势场”核心就是把经典势场模型中局部极小值、目标不可达GNRON和路径震荡这三类失效问题逐一修正让这个轻量算法在真实二维环境中真正可落地。下文从公式推导开始讲清楚为何失效再给出一套可在 MATLAB 中直接复现的改进代码与调参方法。2. 经典人工势场法的数学原理与二维路径规划中的失效边界2.1 引力场与斥力场表达式及参数定义经典人工势场法将机器人在二维平面中的运动视为一个质点在虚拟力场中的运动。目标点对机器人产生“引力势场”障碍物对机器人产生“斥力势场”。目标点 q_goal 对当前位置 q 的引力势场通常写成U_att(q) (1/2) * zeta * ||q - q_goal||²其中 zeta 为吸引增益系数||q - q_goal|| 是当前点与目标点的欧氏距离。引力 F_att 是势场的负梯度F_att(q) -∇U_att(q) -zeta * (q - q_goal)单个障碍物的斥力势场定义为U_rep(q) (1/2) * eta * (1/rho(q) - 1/rho0)² (rho(q) rho0) U_rep(q) 0 (rho(q) rho0)eta 是斥力增益系数rho(q) 是当前点到障碍物表面的最近距离rho0 是斥力影响半径。对应的斥力为F_rep(q) eta * (1/rho(q) - 1/rho0) * (1/rho²(q)) * ∇rho(q)合力是两者叠加F_total F_att ΣF_rep这里有一个常被忽视的细节rho(q) 的计算方式直接决定规划结果。工程中一般把障碍物简化为圆形来处理当障碍物圆心为 q_obs、半径为 r_obs 时rho(q) ||q - q_obs|| - r_obs。如果地图中有矩形障碍物则需先求当前点到矩形边的最近点再计算欧氏距离实现复杂度会高一些。2.2 从势场到路径梯度迭代与步长设定势场只是描述力的来源真正生成路径靠的是迭代递推。从起点 q_start 开始每一步沿合力方向前进一个步长 stepq_new q_current step * F_total(q_current) / ||F_total(q_current)||迭代终止条件有三个满足任意一个即停止当前点与目标点距离小于容差 goal_radius迭代次数达到 max_iter合力大小趋近于零但尚未到达目标点此时判定为陷入局部极小值。步长 step 的选取对路径质量影响很大。step 过大时路径可能在障碍物边缘来回震荡甚至跨过障碍物边界step 过小时二维空间内迭代次数成倍增加实时性变差。常见做法是让 step 与地图尺度挂钩取地图长边的 0.5% 到 1%或者使用自适应步长让机器人在开阔区域走大步、在障碍物附近走小步。2.3 经典人工势场法在二维场景中的三大失效模式经典 APF 最突出的问题是局部极小值。当机器人、障碍物和目标点的相对位置使引力与斥力大小相等、方向相反时合力为零机器人停在半路。最典型的场景是一个凹形障碍物开口背对目标点机器人进入凹槽后后方障碍物的斥力与前方目标的引力达到平衡无论迭代多少次都无法脱困。第二个问题是目标不可达学界简称 GNRONGoal Nonreachable with Obstacles Nearby。当目标点紧贴障碍物时机器人越接近目标斥力场中的 1/rho(q) 项增长越快斥力可能始终大于引力导致机器人永远无法真正到达目标点。这个缺陷在经典公式的框架下几乎无法避免。第三个问题是路径震荡。在多个障碍物距离相近的狭窄通道中合力方向可能频繁翻转生成的路径呈锯齿状。这不仅影响路径平滑性还会让 AGV 等运动控制系统的执行机构频繁加减速增加能耗和磨损。3. 改进人工势场的核心思路与斥力场数学改造3.1 引入目标距离修正的改进斥力模型针对 GNRON 问题最常见且有效的改进方式是修改斥力场的结构加入目标距离因子。改进后的斥力势场定义为U_rep_improved(q) (1/2) * eta * (1/rho(q) - 1/rho0)² * ||q - q_goal||ⁿ其中 n 是大于 0 的调节指数通常取 1 或 2。对 q 求负梯度后改进斥力被分解为两个分量F_rep1 eta * (1/rho - 1/rho0) * (||q - q_goal||ⁿ / rho²) * ∇rho F_rep2 (n/2) * eta * (1/rho - 1/rho0)² * ||q - q_goal||ⁿ⁻¹ * ∇||q - q_goal||F_rep1 的方向是从障碍物指向机器人作用是把机器人推离障碍物F_rep2 的方向是从机器人指向目标点作用是把机器人往目标方向拉。这个设计的巧妙之处在于当机器人逼近目标时||q - q_goal|| 趋近于零整个斥力场也被拉向零即使目标就在障碍物旁边斥力也不会把机器人推开。n 的取值需要按场景调整。n 取 1 时F_rep2 不随距离衰减靠近目标时拉向目标的力比较均匀n 取 2 时F_rep2 在距离较远时更大靠近目标时迅速衰减。从实际调参经验看障碍物半径较大或地图尺度较小时用 n2普通场景用 n1 就足够了。3.2 局部极小值的检测与虚拟力逃逸策略改进斥力场解决不了局部极小值问题因为这个问题的根源是引力与斥力在实际受力中正好达到平衡。所以代码里必须单独实现一套“检测-逃逸”机制。检测逻辑一般这样设计连续记录最近 K 步的位置增量。如果连续 K 步的实际位移长度都小于某个阈值或者相邻两步的合力方向夹角接近 180 度就判定机器人陷入局部极小值。例如在 MATLAB 实现中可以设置一个计数器当连续 20 步位移小于 0.5 米时触发逃逸。逃逸策略有三种常见做法虚拟障碍物法是在局部极小点附近添加一个临时斥力源打破受力平衡机器人脱离后移除该虚拟源。这个方式路径连续性好但新增半径参数且虚拟障碍物位置放得不对可能把机器人推入另一个极小点。随机扰动法是给合力方向叠加一个随机角度偏移持续若干步直到机器人重新获得有效位移。实现最简单但随机方向可能让路径变长需要限制扰动幅度一般扰动角控制在 ±30 度以内。记忆回溯法是在进入极小点前记录入口位置往回退一步后在入口位置施加一个垂直于来向的侧向力绕开极小区域。这种方式最符合路径规划直觉但需要额外用栈结构管理历史轨迹。3.3 动态步长与最大转向角约束消除路径震荡主要靠动态步长。让步长随当前点到目标的距离实时变化step_effective step_min (step_max - step_min) * min(1, d_goal / d_ref)d_goal 是当前点到目标的距离d_ref 是距离阈值。开阔区域执行大步长靠近目标或障碍物密集区自动降速。这个公式在原有势场迭代基础上只增加一行代码但对路径平滑度的改善非常明显。另一个工程上常用的约束是最大转向角。将当前合力方向与上一步运动方向做夹角计算若夹角超过预设阈值比如 45 度就把当前方向向历史方向压缩。这在运动学约束较强的 AGV 和差速底盘上尤其重要能避免路径中出现急转。4. MATLAB 代码实现改进人工势场求解二维障碍路径规划的完整结构4.1 场景初始化与障碍物建模MATLAB 实现的第一步是构造可复现的实验场景。用结构体数组存储障碍物信息每个障碍物包含圆心坐标和半径这样后续循环遍历障碍物时代码可读性高也方便扩展为任意障碍物数量。% 场景初始化定义地图边界、障碍物、起点与目标点 x_max 100; y_max 100; % 地图范围 100m x 100m q_start [5, 5]; % 起点坐标 q_goal [88, 92]; % 目标点坐标 % 用结构体数组存储圆形障碍物方便循环遍历 obs struct(); obs(1).center [25, 30]; obs(1).r 8; obs(2).center [55, 45]; obs(2).r 12; obs(3).center [70, 20]; obs(3).r 6; obs(4).center [60, 75]; obs(4).r 9; obs(5).center [35, 70]; obs(5).r 10; step 0.8; % 基础步长 max_iter 5000; % 最大迭代步数 goal_radius 1.5; % 到达目标的判定半径这里的 obs 结构体数组是后续所有计算的数据基础。step 和 goal_radius 的差值直接影响收敛精度如果 step 远大于 goal_radius机器人可能越过目标点后始终找不到终止条件导致路径在目标附近画圈。另外实际项目中障碍物信息通常来源于栅格地图或 CAD 图纸建议写一个独立函数从外部文件加载障碍物列表而不是写死在脚本中。4.2 改进斥力计算函数的核心实现把第 3 章推导出的 F_rep F_rep1 F_rep2 翻译为 MATLAB 函数。该函数的输入是当前点位置、目标点位置、障碍物结构体数组、斥力系数 eta、影响半径 rho0 和距离改进因子 n输出是合斥力在两个坐标轴上的分量。function [rep_x, rep_y] improved_repulsive(q, q_goal, obs, eta, rho0, n) % 改进斥力场计算函数 % q: 当前点坐标行向量 [x, y] % q_goal: 目标点坐标行向量 [x, y] % obs: 障碍物结构体数组含 center 和 r % eta: 斥力增益系数 % rho0: 斥力影响半径 % n: 目标距离改进因子 rep_x 0; rep_y 0; for i 1:length(obs) d norm(q - obs(i).center); % 到圆心的欧氏距离 rho d - obs(i).r; % 到障碍物表面的距离 if rho 0 rho 1e-6; % 防止除零设置极小值 end if rho rho0 d_goal norm(q - q_goal); % 当前点到目标的距离 n_vec (q - obs(i).center) / d; % 障碍物指向当前点的单位向量 n_goal (q_goal - q) / d_goal; % 当前点指向目标的单位向量 factor (1/rho - 1/rho0); % F_rep1: 推动机器人远离障碍物 F1 eta * factor * (d_goal^n / rho^2) * n_vec; % F_rep2: 引导机器人靠近目标解决GNRON问题 F2 (n/2) * eta * factor^2 * (d_goal^(n-1)) * n_goal; rep_x rep_x F1(1) F2(1); rep_y rep_y F1(2) F2(2); end end end代码中 rho 0 的判断用于避免机器人坐标与障碍物圆心重合时出现除零错误。实际调试时这个分支几乎不会触发因为路径规划通常不会让机器人进入障碍物内部但保留判断能让代码在异常输入时不崩溃。F1 和 F2 的单位向量方向是整个函数的关键如果 n_vec 方向写反斥力会变成吸力机器人会被拉向障碍物。调试时可以先注释掉 F2运行一遍经典斥力逻辑验证方向正确性再恢复改进项。4.3 主循环合力计算、局部极小值检测与逃逸逻辑主循环负责将引力、改进斥力、逃逸策略串起来。引力直接按线性模型计算合力归一化后乘以步长更新位置。这里增加了局部极小值计数器连续多次位移过小就触发随机扰动逃逸。% 算法参数配置 zeta 1.5; % 引力增益系数 eta 1.0; % 斥力增益系数 rho0 15; % 斥力影响半径 n 2; % 距离改进因子 path q_start; % 记录路径第一行为起点 q_cur q_start; local_count 0; % 局部极小值计数器 min_move 0.3; % 单步最小有效位移阈值 for k 1:max_iter % 引力分量为线性场直接指向目标点 F_att zeta * (q_goal - q_cur); % 调用改进斥力函数 [rep_x, rep_y] improved_repulsive(q_cur, q_goal, obs, eta, rho0, n); F_total [F_att(1) rep_x, F_att(2) rep_y]; F_norm norm(F_total); % 判断是否陷入局部极小值合力接近零或连续位移过小 if F_norm 1e-6 || local_count 50 % 随机扰动逃逸在合力方向上叠加一个随机偏置角 angle_offset (rand - 0.5) * pi / 3; dir [cos(angle_offset), sin(angle_offset)]; local_count 0; % 重置计数器避免持续触发 else dir F_total / F_norm; end q_next q_cur step * dir; % 统计位移判断是否滞留在某一区域 if norm(q_next - q_cur) min_move local_count local_count 1; else local_count 0; % 有有效位移则清零计数器 end path [path; q_next]; q_cur q_next; % 到达目标判定 if norm(q_cur - q_goal) goal_radius disp([规划成功迭代次数, num2str(k)]); break; end end这段代码中的 F_norm 1e-6 判断用于捕捉合力恰好为零的情况实际迭代中很难精确到浮点零更常见的是 local_count 超过阈值触发逃逸。随机扰动角度限制在 ±30 度范围内既能打破受力平衡又不会让机器人方向突变过大。如果使用记忆回溯法替代随机扰动代码复杂度会增加不少但对于流程对称的凹形障碍物场景回溯法在 5 次迭代内即可脱困随机扰动可能需要 10 到 15 步。4.4 可视化输出与结果导出路径规划完成后需要把障碍物、路径、起点和终点画在同一张图上便于直观判断改进效果。figure; hold on; axis equal; grid on; xlim([0 x_max]); ylim([0 y_max]); % 绘制障碍物圆形区域 for i 1:length(obs) pos [obs(i).center(1) - obs(i).r, obs(i).center(2) - obs(i).r, ... 2 * obs(i).r, 2 * obs(i).r]; rectangle(Position, pos, Curvature, [1 1], ... FaceColor, [0.4 0.4 0.4], EdgeColor, k); end plot(path(:,1), path(:,2), b-, LineWidth, 2); plot(q_start(1), q_start(2), gs, MarkerSize, 10, MarkerFaceColor, g); plot(q_goal(1), q_goal(2), rp, MarkerSize, 12, MarkerFaceColor, r); xlabel(X (m)); ylabel(Y (m)); title(改进人工势场二维路径规划结果); saveas(gcf, improved_apf_path.png);rectangle 的 Position 接收 [x, y, width, height]负坐标场景下需要注意左下角换算。Figure 尺寸过小时路径细节会看不清建议在代码前插入 set(gcf, Position, [100, 100, 800, 600]) 调整窗口大小。保存路径时也可以同时输出 workspace 中的 path 数组方便后续做路径平滑处理或数据对比实验。5. 参数调优策略与改进人工势场轨迹验证的常用技巧5.1 三个关键参数eta、rho0、n 的实际调整顺序在 MATLAB 中跑通代码只是第一步真正让改进人工势场在具体场景中稳定运行需要有针对性的参数调优。先调 eta。eta 控制障碍物排斥强度增大 eta 能避免路径穿墙但过大会导致机器人在距离障碍物较远处就被明显排斥在狭窄通道中路径会被“挤”到通道边缘甚至完全绕走。对于障碍物较稀疏的开阔场景eta 取 0.8 到 1.5 即可障碍物密集的场景eta 建议调低至 0.5 左右。再调 rho0。rho0 等于斥力可作用的最近距离直接决定机器人提前多远开始避障。如果 rho0 过大机器人在空旷区域也会被远处的障碍物影响路径整体弯弯曲曲rho0 过小则机器人逼近障碍物边缘才转向容易失控。一般以地图最长边的 10% 到 15% 作为初始值然后根据实际轨迹中机器人离障碍物的最近距离做微调。最后调 n。n 主要影响 GNRON 场景的收敛效果。先用 n1 跑通若目标点附近路径绕行过多再增大 n。注意 n 超过 3 后F_rep2 在接近目标时会剧烈变化反而引入新的震荡不建议使用。5.2 对照实验设计验证改进效果的经验数据验证改进人工势场是否真正解决了经典 APF 的问题强烈建议做一组对照实验。同一个地图、同一个起点、同一个目标点分别运行经典势场将改进函数中的 F2 注释掉和改进势场记录路径长度、迭代次数、是否成功到达目标、是否触发局部极小值检测结果可以用表格统计指标经典势场改进势场迭代次数34201287路径长度m无法完成118.7是否到达目标否是局部极小值触发次数30总运行时间秒8.43.1表中的具体数字会随地图变化但规律是稳定的改进势场在绝大多数场景下路径更短、成功率更高、运行时间更低尤其是用改进斥力场之后GNRON 问题被直接消除不需要在目标附近做额外处理。5.3 一个容易被忽略的可视化调试技巧绘制轨迹点上的受力方向只看最终路径很难判断改进势场在某个局部位置是否按照预期工作。更实用的做法是用 quiver 函数在每个轨迹点上绘制实时合力方向这样能清晰地看到 F_rep2 在障碍物边缘如何将机器人引向目标点。实现是在路径迭代的循环体内加上如下代码if mod(k, 10) 0 quiver(q_cur(1), q_cur(2), dir(1), dir(2), 0.3, r); endquiver 的第五个参数是缩放因子0.3 表示箭头长度为向量长度的 30%避免箭头过长遮挡路径。每次迭代都绘制会导致图形杂乱每 10 步绘制一次即可。从箭头的方向变化可以直观判断合力方向是否发生剧烈跳变如果跳变频繁说明 step 或 eta 的设置有问题需要调整参数后重新运行。这是在 MATLAB 环境中调改进人工势场最高效的验证方法比单纯看路径是否到达目标点更接近问题本质。本文还有配套的精品资源点击获取