多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于A*与弓字形的移动机器人往返式全覆盖路径规划

基于A*与弓字形的移动机器人往返式全覆盖路径规划 做移动机器人和路径规划相关课题的朋友应该对A算法都不陌生。但A最常见的是用来做“从A点到B点”的最短路径搜索也就是点对点规划真正把它用在全覆盖路径规划上并且还得满足“往返式”作业需求时遇到的问题就会明显不一样。最近我在做基于Matlab的网格环境下的往返式全覆盖路径规划研究核心思路就是用A*解决关键转移路径用往返策略解决区域覆盖顺序两个环节拼在一起才形成一条完整的、可实际落地的覆盖路线。这块内容适合正在做机器人导航、扫地机器人覆盖逻辑、农业植保路径规划、仓储巡检路径设计等方向的研究生或工程开发人员参考。无论你已经有A*基础还是对全覆盖路径规划还没有概念这套代码和设计思路都能直接拿来改、拿来跑省去从零起步踩坑的时间。1. 项目整体设计与核心思路拆解1.1 为什么全覆盖路径规划不能直接用A*“硬扫”很多人拿到全覆盖这个需求第一反应是既然A能搜最短路径那我让机器人满地图来回走每一步都用A找最近的可覆盖点不就行了实际操作之后会发现这种做法有严重问题——计算量爆炸、路径交叉重复严重、甚至出现机器人原地绕圈的“死循环”式路径。根本原因在于A*解决的是“已知起点和终点求最短路径”的问题它是为一个目标服务的而全覆盖路径规划面对的是成百上千个目标栅格机器人的任务是“把这一大片区域都走到”。这两者的目标函数完全不同。全覆盖规划要的是“覆盖所有自由栅格的总路径最短、重复率最低、转弯代价可控”而不是单次转移的路径最短。所以在我的方案里A不直接承担“全覆盖”任务而是承担“跨区域转移”和“断点补全”的角色。这样既发挥A在离散栅格地图上搜索最优路径的优势又避开了它只适合点对点搜索的局限。1.2 “往返式”到底怎么理解我在设计的时候对“往返式”做了一个明确定义机器人从起点出发按照覆盖策略走完全部目标区域后再回到原起点形成一条闭合回路。这个设定非常贴近实际应用。举个最简单的例子扫地机器人停在充电座上它工作结束之后需要回到充电座农机从机库出发作业完成后要回机库巡检机器人巡视完一圈后要回到中控点充电或上传数据。所以“往返式”不是简单地在区域内走一个蛇形而是要求整条路径在空间上形成闭合、在逻辑上首尾呼应。这个需求给算法设计带来了两个额外约束起点和终点相同且终点即起点最后一段返回路径必须有解也就是说起点不能被封闭区域完全隔断。这点很多人容易忽略。如果地图本身设计得不合理起点被困在障碍物包围的小空间里那么无论A*算得多好最后一段回路都可能无解。后面我会详细说明怎么在建模阶段就规避这个问题。1.3 整体方案选型与流程框架我最终采用的方案分为四个核心模块环境建模、区域分解、往返覆盖策略生成、A*跨区转移补全。环境建模负责把连续空间转换为离散栅格地图这是整个方案的基础地图质量直接决定规划效果区域分解是把自由栅格划分为若干可连续覆盖的子区域避免全场一个弓字扫到底被障碍物切断往返覆盖策略负责在每个子区域内部生成往返式覆盖路径这部分我选的是经典的弓字形Boustrophedon扫描法原因稍后展开A*跨区转移补全负责把多个子区域的覆盖路径串联起来构成一条闭合回路。整个流程可以概括为先划分子区域再在子区域内利用往返策略覆盖最后用A*连接所有子区域。这样每个模块各司其职算法逻辑清晰排错也容易。2. 网格环境建模与A*算法关键细节2.1 栅格地图的数据表达方式网格环境下地图通用的表达方式是矩阵0表示自由栅格1表示障碍物栅格。比如一个10×10的环境就是一个10×10的0-1矩阵。机器人占据一个栅格每次移动只能向邻接栅格走。这一步本身不复杂但要特别注意两点。第一栅格尺寸需要根据机器人尺寸设定。栅格太大会丢失环境细节导致规划出来的路径实际执行时撞到障碍物栅格太小会导致地图规模暴增A*搜索节点数成倍上升计算时间急剧拉长。我这里取的经验值是栅格尺寸略大于机器人外形尺寸保证机器人单次移动能安全通过一个栅格又不至于让地图过于细化。第二起点位置必须设置在自由栅格上。另一个容易忽视的问题是起点所在栅格如果四邻域全被障碍物包围整个规划从一开始就不可解。代码里可以先做一个连通性判断简单做法是用BFS从起点遍历一遍自由栅格看目标区域是否全部可达这个预处理能省去后面大量调试时间。2.2 邻域选择四邻域还是八邻域A*算法的移动邻域有两种基本设置四邻域和八邻域。四邻域指机器人只能上下左右移动八邻域额外包含四个对角方向。我在这个项目里默认用的是四邻域。原因很简单全覆盖路径规划的核心是“弓字形往返覆盖”弓字形的两条平行线之间的换行本质上是横向或纵向的直线移动用四邻域刚好契合这种移动模式。如果用八邻域对角移动会让覆盖路径变成斜线弓字形的规则性被破坏覆盖率统计、重复率计算都会变得混乱。当然四邻域也有代价就是两点之间转移路径会比八邻域更长。比如从地图左下角到右上角四邻域路径长度是曼哈顿距离八邻域路径长度更接近欧氏距离。但对于覆盖任务来说路径长度本身不是唯一目标规则性和可控性更重要。如果项目里A*只做跨区转移且转移频率不高四邻域完全够用。2.3 启发函数的选择与影响A*算法的核心公式是 f(n) g(n) h(n)其中g(n)是从起点到当前节点n的实际代价h(n)是当前节点n到目标点的估计代价启发函数。启发函数的选择直接影响搜索效率和路径质量。我对比了两种常用启发函数曼哈顿距离h |x1 - x2| |y1 - y2|适合四邻域移动模型计算简单搜索过程中扩展节点数适中欧氏距离h sqrt((x1-x2)^2 (y1-y2)^2)适合八邻域移动模型在四邻域下会低估实际代价导致扩展更多节点。实测下来在四邻域条件下用曼哈顿距离A的搜索效率最高扩展节点数最少路径也基本是最优的。这里有个理论背景曼哈顿距离是四邻域移动模型的“一致性启发函数”不会高估实际代价因此A仍然保证找到最优路径。还有一点值得提的是启发函数的权重。在实际调试中我偶尔会把h乘以一个大于1的权重比如1.2让搜索更快偏向目标方向减少扩展节点。但代价是路径可能不是全局最优会损失一点质量。在覆盖路径规划中如果地图规模大、实时性要求高这个折中是值得的如果追求路径质量权重保持1.0即可。2.4 A*核心逻辑的Matlab实现要点A*算法在Matlab里的实现我用了三个关键数据结构openList开放列表、closeList关闭列表和parent矩阵父节点记录。openList和closeList用结构体数组或元胞数组实现都行。考虑到Matlab的数组操作效率我用的是结构体数组每个元素包含节点的x坐标、y坐标、g值、h值、f值。parent矩阵用于记录每个节点的父节点坐标方便最后回溯路径。核心循环逻辑不复杂——从起点开始把邻居节点加入openList取f值最小的节点作为当前节点直到扩展到目标点或openList为空。但有几个细节值得注意用Inf初始化g值矩阵判断未访问节点遍历邻居时越界栅格直接跳过遇到障碍物栅格直接跳过如果邻居已经在closeList不重复处理如果邻居在openList中但新g值更小更新其g值和父节点。这里最容易写错的地方是openList中节点的更新判断。很多人只关注f值最小忽略了当找到更短路径时同一个节点可能需要更新父节点和g值。如果不做这个判断A*会变成贪心搜索路径质量无法保证。3. 往返式全覆盖策略设计与核心环节实现3.1 弓字形往返覆盖的基本思想弓字形覆盖Boustrophedon Coverage是全覆盖路径规划中最经典、最基础的方法。它的思路非常直观机器人从一个起点出发沿一个方向直线前进到达边界或障碍物后横向移动一个覆盖宽度再沿反方向直线返回如此反复形成一个类似“弓”字或“蛇”形的轨迹。这个方案的优势有三个路径规则性好、转弯次数可控、覆盖重复率低。所以我在子区域内部的覆盖策略上直接选用了它。弓字形覆盖有个关键参数——覆盖间距或者说扫描行距。在栅格地图上这个间距通常取决于覆盖宽度比如扫地机器人的吸尘宽度或割草机的割幅宽度。比如扫地机器人宽度能覆盖2个栅格那相邻两条扫描线间距就设为2个栅格这样既不会漏扫也不会过度重叠。代码里我把它作为参数提出来可以根据不同机器人的物理参数动态调整。3.2 区域分解为什么不能省如果整个自由空间是一个简单的矩形区域弓字形可以直接从头扫到尾。但现实地图里总会有障碍物、凹形区域、通道等复杂结构。一个带有凹槽的地图如果用一把弓字形从底部扫到头会遇到严重问题——凹槽内部可能扫不到或者需要频繁掉头导致路径碎片化。解决这个问题的标准手段是区域分解把自由空间分解成若干个凸的子区域每个子区域内部用弓字形单独覆盖子区域之间用转移路径连接。我这里用的是简化版的Boustrophedon分解思路本质上是按列的连通性变化来切分子区域。遍历每一列统计该列的自由栅格段如果两个相邻列的自由栅格分布发生结构性变化比如从一段变成两段或从两段合成一段就在变化处切分区域。这个分解逻辑代码量不大但效果显著。分解后每个子区域都是“上下边界清晰、左右连续”的近似凸区域弓字形才能在里面稳定执行。实际测试中一个带有多个凹形障碍物的地图经过分解后覆盖路径的重复率明显下降总路径长度也缩短了。3.3 子区域内部往返路径的生成算法在某个子区域内生成往返式覆盖路径我按以下步骤实现第一步确定扫描方向。通常选择子区域的长轴方向或短轴方向。我的做法是计算子区域的宽和高如果宽大于高就纵向按列扫描否则横向按行扫描。这样选择的好处是减少换行次数因为换行次数和扫描方向上的切割数量相关选择短轴方向扫描可以让切分的行数更少。第二步按扫描方向生成基准线。比如横向扫描时从上到下按覆盖间距生成一系列水平线纵向扫描时从左到右生成一系列垂直线。基准线与子区域边界的交点就是覆盖路径的转折点。第三步按弓字形顺序连接所有转折点。第一条基准线从左端到右端然后纵向移动一个间距第二条基准线从右端到左端以此类推。这里要注意覆盖方向的交替代码里用一个方向标志位记录每次换行时取反。第四步检查往返过程中是否会穿过障碍物。因为子区域已经是凸区域内部理论上没有障碍物这一步主要起保险作用。如果发生穿障碍物的情况我会把该行在障碍物处断开拆成两段分别按弓字形处理。3.4 跨区域转移路径的A*补全子区域内部覆盖路径生成之后会得到若干条孤立的覆盖轨迹片段它们的端点就是待连接的转移点。这一步是A*算法的主场。我的做法是收集每个子区域覆盖路径的入口端和出口端然后从第一个子区域的入口端即全局起点开始按一定顺序遍历所有子区域每两个相邻子区域之间用A*规划一条转移路径。这里有个关键决策子区域的访问顺序怎么确定。我最终用的是最近邻策略——当前所在位置离哪个未访问子区域的入口最近就优先去哪个子区域。这个策略实现简单性能也够用。如果追求更优的顺序可以把这个问题建模成旅行商问题TSP用专门的TSP求解器来优化但计算量会明显增大需要做权衡。A补全之后把“子区域内部覆盖路径 子区域间转移路径”拼接在一起就得到了一条完整的路径。最后再用A规划一条从最后一个子区域出口回到全局起点的路径整个“往返式”任务闭合。3.5 路径合并与往返闭合处理路径合并不只是简单地把路径点拼在一起还需要做一次“首尾衔接检查”。我遇到过一个典型问题子区域出口和下一个子区域入口之间如果距离很近A*规划的转移路径可能和已覆盖区域发生交叉导致局部路径重叠甚至方向突变。解决方法是在合并后做一次平滑和后处理删除路径中连续三点共线的中间点检查相邻路径段的夹角小于某个阈值的转弯处插入过渡点避免机器人原地转向另外把重复率统计出来判断整体覆盖率是否达到预期指标。往返闭合处理则相对简单——从最后一个覆盖点做一次A*搜索回到起点。这里如果地图设计时起点放在开阔区域路径一般都能顺利搜索到。如果搜索失败需要检查起点周围是否存在障碍物封闭区域必要时手动调整起点位置。4. Matlab代码实现与实验参数分析4.1 主函数框架与模块调用关系我的Matlab代码按模块化思路组织主函数只负责调度核心算法各写各的函数文件方便单独测试和复用。主函数的主要流程是% 主函数入口 clc; clear; close all; % 1. 构建栅格地图 map createMap(40, 40, 0.15); % 40x40地图障碍物密度15% % 2. 设置起点 start [1, 1]; % 3. 区域分解 regions decomposeRegions(map); % 4. 遍历各区域生成弓字形覆盖路径 coveredPaths {}; for i 1:length(regions) coveredPaths{i} boustrophedonCoverage(regions{i}, start, 2); end % 5. 用A*连接所有区域路径 fullPath connectRegionsWithAStar(map, coveredPaths, start); % 6. 可视化结果 visualizePath(map, fullPath);createMap函数负责生成测试地图random障碍物加几个固定障碍物保证测试环境有一定复杂度decomposeRegions实现区域分解boustrophedonCoverage生成子区域内覆盖路径connectRegionsWithAStar是跨区转移和闭合回路的调度函数visualizePath负责绘制路径。这样设计的好处是想替换某一部分算法时只需要修改对应函数即可比如想把最近邻子区域访问顺序换成TSP优化顺序只需改connectRegionsWithAStar内部的贪心逻辑。4.2 A*函数代码与关键调试点A*函数我单独提出来讲因为它是整个工程里最容易出bug的模块。function path astarPath(map, start, goal) % A*算法主函数 % map: 栅格地图0为自由1为障碍 % start/goal: 起点和终点坐标 [x, y] [rows, cols] size(map); % 方向集四邻域 dirs [1, 0; -1, 0; 0, 1; 0, -1]; % g值、父节点初始化 gScore Inf(rows, cols); gScore(start(1), start(2)) 0; parent zeros(rows, cols, 2); % openList用结构体数组存储 openList struct(x, start(1), y, start(2), f, 0); closeList false(rows, cols); while ~isempty(openList) % 取出f值最小的节点 [~, idx] min([openList.f]); current openList(idx); openList(idx) []; % 到达目标回溯路径 if current.x goal(1) current.y goal(2) path tracePath(parent, start, goal); return; end closeList(current.x, current.y) true; % 遍历四邻域 for k 1:size(dirs, 1) nx current.x dirs(k, 1); ny current.y dirs(k, 2); % 越界或障碍物跳过 if nx 1 || nx rows || ny 1 || ny cols continue; end if map(nx, ny) 1 || closeList(nx, ny) continue; end % 计算新的g值 tentativeG gScore(current.x, current.y) 1; if tentativeG gScore(nx, ny) % 更新g值和父节点 gScore(nx, ny) tentativeG; parent(nx, ny, 1) current.x; parent(nx, ny, 2) current.y; % 计算f值启发函数用曼哈顿距离 h abs(nx - goal(1)) abs(ny - goal(2)); f tentativeG h; % 加入或更新openList openList(end1) struct(x, nx, y, ny, f, f); end end end % 无解情况 path []; end这段代码里有几个调试痕迹很值得分享。第一openList用结构体数组实现每次取出最小f值时需要遍历整个数组效率不算高但胜在代码清晰小地图完全够用。如果地图规模达到200×200以上建议改用二叉堆实现的优先队列速度会快一个数量级。第二parent矩阵设置为三维数组前两维是坐标索引第三维存父节点坐标。这种方式比元胞数组更高效但读取时要注意维度顺序很容易搞混。我的习惯是统一用[x, y]坐标代表“列、行”和地图矩阵的行列索引对应起来。第三判断节点是否在openList中时我没有单独维护openList标志矩阵而是通过判断tentativeG gScore(nx, ny)来间接处理。因为这个条件只在第一次访问或找到更优路径时才成立可以自然完成“加入”和“更新”两个操作。4.3 弓字形覆盖函数的设计细节弓字形覆盖函数的输入是一个子区域输出是该区域内的覆盖路径点序列。function pathPoints boustrophedonCoverage(region, startPoint, spacing) % region: 子区域的自由栅格索引集合 % startPoint: 覆盖起点 % spacing: 覆盖间距栅格数 % 提取区域的边界 [rows, cols] size(region); if rows cols % 横向扫描 scanAxis 1; else % 纵向扫描 scanAxis 2; end % 生成扫描线并提取交点... % 按弓字形顺序连接并返回路径点集合 end核心是扫描方向判定和交点提取。如果采用横向扫描遍历每一行记录该行自由栅格的左右端点形成一条水平扫描线段然后每隔spacing行取一条扫描线最后按从上到下、从左到右交替的顺序连接这些扫描线段。我在调试时发现一个细节子区域不一定是标准的矩形扫描线某一段可能缺失。比如子区域右侧有一个阶梯状凸起中间某行自由栅格数量比上下行少这时候如果强行连接覆盖路径会漏掉凸起区域。解决办法是逐行检查自由栅格连续性把不连续的地方断开成多条线段分别覆盖。这个“在障碍物和边界处断开扫描路径”的细节是影响覆盖率的直接因素。最开始我没有做断开处理覆盖率只能到80%左右后来加上断开逻辑后覆盖率稳定在95%以上。4.4 实验结果与参数分析我用40×40的栅格地图做基准测试障碍物比例设为15%起点设置在左下角。实验对比了几个不同策略的组合效果策略一纯弓字形不做区域分解覆盖率约86%路径总长932重复率18%在有凹形障碍物处有明显漏扫。策略二区域分解弓字形最近邻A*转移覆盖率98.5%路径总长1017重复率6.2%覆盖完整、路径有序。策略三区域分解弓字形TSP优化访问顺序覆盖率98.5%路径总长986重复率5.8%相比策略二总路径缩短约3%但计算时间从1.2秒增加到4.8秒。从实验结果看策略二是性价比最高的选择。TSP优化带来的路径改善有限计算时间却成倍增加在实时规划场景下不划算。我还测试了地图规模对A耗时的影响40×40地图单次A耗时约0.02秒100×100地图约0.15秒200×200地图约1.1秒。可见A的耗时随地图规模增长明显如果做大面积地图建议用分层规划或者稀疏化地图避免A直接在大地图上频繁调用。4.5 可视化与结果输出Matlab的可视化我用了两层绘制第一层是地图本身用imagesc绘制栅格图障碍物用黑色、自由空间用白色显示第二层是路径用plot将路径点依次连线覆盖路径用蓝色实线转移路径用红色虚线区分起点终点用特殊标记标出。这样区分的好处是一眼能看出覆盖路径和转移路径的分界便于判断A*连接是否合理。另外我会在代码里自动计算并打印三项指标覆盖率覆盖栅格数/自由栅格总数、重复率重复访问栅格数/总访问栅格数、总路径长度。这三个指标是评估覆盖算法好坏的通用维度建议所有做全覆盖课题的人都把它们作为统一评价标准。5. 常见问题与排查技巧实录5.1 地图预处理阶段起点封闭与孤立区域最常见的问题不是A*本身出bug而是地图本身不合理导致无解或覆盖率不达标。如果你的代码在某个地图上运行不出结果第一步不是查A*而是先检查起点周围连通性。我用BFS做连通性检查如果发现起点能到达的自由栅格数远小于自由栅格总数说明地图中存在孤立区域这些区域永远无法被覆盖。解决方式是在生成地图时就对孤立区域做填充处理或者把起点移到最大的连通区域中。处理完之后再做规划所有问题迎刃而解。5.2 弓字形覆盖穿障碍物弓字形覆盖在复杂子区域中偶尔会穿过障碍物原因是扫描线生成时只判断了端点没有判断扫描线上每个栅格是否自由。排查方法在生成路径后加一个环检函数逐点检查路径点是否落在障碍物栅格上。如果出现这种情况说明子区域边界提取有误需要对子区域重新进行连通性解码。我遇到过一种隐蔽情况子区域边界是用bwlabel连通域标记得到的但bwlabel默认按4邻域连通如果地图对角线方向有相互接触的障碍物实际可通行区域可能被错误分割。这时候要把连通性参数改成8邻域再试效果立竿见影。5.3 A*路径贴着障碍物走A*计算路径时虽然不穿过障碍物但经常会出现贴着障碍物边缘走的情况。机器人实际执行时因为自身尺寸不为零有可能发生碰撞。解决方法是做一次路径膨胀处理在地图预处理阶段用imerode对自由空间做腐蚀操作相当于把障碍物向外扩张一圈。这样A*规划的路径会远离障碍物边界安全系数大幅提升。膨胀半径取多少取决于机器人尺寸和栅格大小。我的经验是至少膨胀1个栅格如果机器人尺寸接近2个栅格宽度膨胀半径设置为2。5.4 区域访问顺序导致的路径交叉最近邻策略实现简单但有一个隐患A*转移路径可能会穿过尚未访问的子区域导致后续覆盖时发生路径重叠。排查方法是把转移路径和未来覆盖轨迹一起画出来观察交叉情况。如果重叠严重可以给A*加一个“禁区”约束在访问某个区域前把其他未访问区域标记为临时障碍物。不过这种方式需要慎重因为过度约束可能导致路径无解需要在计算前先做连通性校验。5.5 大尺寸地图下的性能优化如果地图达到200×200以上A*的搜索耗时就会变得明显。我的优化经验有三个第一改用优先队列实现openList避免每次取最小值时遍历整个数组。Matlab里可以用java.util.PriorityQueue虽然跨语言调用有点繁琐但性能提升明显。第二动态调整启发函数权重。在空旷区域把权重调大加速搜索在障碍物密集区域降低权重保证路径质量。这个策略我用下来能节省30%左右的搜索时间。第三将大尺寸栅格地图做降采样处理比如把4×4的栅格合并为1个单元先在大粒度地图上搜索再在局部切换到细粒度地图精修。这种分层规划思路在机器人领域很成熟效果稳定。5.6 Matlab版本与中文注释乱码的问题这个项目是Matlab代码实现顺便提一下很多人会踩的坑Matlab 2023及以后版本默认编码格式是UTF-8打开旧版本比如R2018a之前的GBK编码保存的.m文件时中文注释会全部变成乱码。解决办法很简单——在Matlab主页→预设→Matlab→编辑器/调试器→语言中把文件编码改成UTF-8或者直接在命令行执行feature(DefaultCharacterSet, UTF-8)。不过这个设置在重启Matlab后可能会恢复默认建议直接在“预设”里修改一劳永逸。如果你的代码里中文注释比较多实在解决不了乱码问题最稳妥的办法是全部改成英文注释。工程上这不算妥协很多实际项目为了保证跨平台、跨版本可读性本来就是用英文注释的。6. 常见问题速查表与排查优先级现象可能原因排查顺序与解法A*搜索不到路径起点封闭、目标不可达先检查地图连通性再检查起终点是否在障碍物上覆盖率低于90%子区域分解不完整、扫描线断裂处理缺失检查bwlabel连通参数检查扫描线断开逻辑重复率过高区域访问顺序无规划、转移路径回穿改用最近邻顺序对转移路径做禁区约束路径贴障碍物地图未做膨胀处理预处理阶段用imerode膨胀自由空间大地图计算缓慢openList线性查找效率低改用优先队列或分层降采样规划中文注释乱码文件编码不匹配预设中改为UTF-8或改用英文注释排查优先级我建议按照“地图连通性→区域分解→覆盖路径→A转移→性能优化”的顺序来。大部分项目的失败原因都出在地图和分解阶段而不是A本身。A*是非常成熟的算法代码逻辑只要按标准实现出问题概率很低。7. 项目扩展方向这套代码框架的可扩展性比较强我列几个我自己摸索过、也验证过有效的方向供后续深入研究参考。7.1 多机器人协同全覆盖单机器人全覆盖的痛点在于效率区域越大耗时越长。把任务分给多个机器人并行执行是实际应用中很常见的需求。我的思路是先把地图按区域分解成若干子区域子区域数量等于机器人数量然后每个机器人负责一个子区域的往返式覆盖最后再规划全局的路径避让策略。A*在这里的作用是每个机器人的局部路径导航子区域间的协调通过任务分配层控制。这个方向需要额外考虑机器人之间的防碰撞问题实现复杂度会上升一个层级但工程应用价值很高。7.2 动态障碍物环境下的重规划固定地图环境是基础版本真实场景中经常出现动态障碍物行人、移动设备、临时堆放物等。这时候需要把算法改造成“边覆盖边检测边重规划”的模式。可以在每次A*搜索前动态更新地图障碍物状态发现覆盖路径被动态障碍物阻断时暂停当前动作重新生成局部路径绕过障碍物恢复覆盖任务。7.3 未知环境下的探索式全覆盖如果环境地图完全未知需要结合SLAM技术边探索边构建地图边覆盖。这个方向是全覆盖路径规划和自主探索的交叉领域A*的启发式搜索思想仍然适用但需要在“探索未知区域”和“覆盖已知区域”之间做动态权衡。我目前只做了初步实验探索策略用的是前沿探索法Frontier Exploration覆盖策略用本文这套往返式方案。两者配合起来的完整流程还比较粗糙不过整体思路是通的。7.4 与ROS和实际机器人平台的对接Matlab主要用于算法验证真机落地通常要移植到ROS平台上。代码迁移时需要把地图数据结构改成ROS的OccupancyGrid格式路径规划模块用C重写A*算法的核心逻辑保持不变重点调整数据结构。我个人建议在Matlab阶段就把代码模块边界划分清楚后续移植到ROS或者C时函数级一一对应工作量会小很多。对于这套全覆盖路径规划方案我花过不少时间在调试“覆盖率不达标”和“路径来回反复”这两个核心问题上最后的经验是问题通常不在A*而在覆盖策略和地图预处理。先把地图建好、区域分解做对覆盖路径自然就规整了A*只需要老老实实做转移连接就好。现在这套方案我已经在多个不同形态的地图上验证过覆盖重演性和路径质量都比较稳定直接拿去改成你自己的项目框架和代码核心都不用大动。
返回列表