
前阵子做了个扫地机器人全覆盖路径规划的MATLAB仿真项目算法选的是全覆盖内螺旋算法。做完之后很多朋友问我说为什么不用牛耕式为什么不直接扫一遍就完事其实不同算法适配不同场景内螺旋在住宅这种单连通区域多、障碍不复杂的空间里往往比传统往返弓字形更顺手。这篇文章就把我自己的实现思路、踩过的坑、还有仿真运行图里那些细节全部写出来希望能给正在做机器人路径规划的同学一个完整参考。先说清楚这个项目做了什么输入是一张栅格地图里面标了墙壁和障碍物输出是一条从起点出发、尽可能覆盖全部可达区域的路径同时避免重复、避免碰撞。我用的是全覆盖内螺旋算法全部代码跑在MATLAB R2021b环境里模拟结果通过逐帧截图记录能看到机器人从起点出发一层一层向区域内侧收缩最后停在内圈终点。整个过程不依赖ROS不依赖额外硬件纯算法层面复现非常适合做算法原理验证和课程设计。1. 遍地开花的路径规划方案为什么偏偏选内螺旋做扫地机器人路径规划有两类问题必须分清楚一类是点对点路径规划比如机械臂抓取、无人车导航目标是从A点到B点找一条最优路径另一类是全覆盖路径规划目的不是到达某个点而是让传感器或者清洁机构覆盖整个可通行区域。扫地机器人属于后者衡量标准不是“走了多短的路”而是“有多少地方被扫到了”“有多少面积被重复扫了”。全覆盖规划里主流方案大致有三种随机碰撞式、弓字形牛耕式、螺旋式。随机碰撞式最原始机器人碰到障碍随便转个角度继续走优点是实现简单缺点是覆盖率看运气重复率极高现在已经很少单独用了。弓字形是工厂清扫、农业机器人里最常见的一种来回平行移动像牛耕地一样算法逻辑直接在开阔的矩形区域表现极佳覆盖率能到95%以上。但弓字形有一个问题——一旦区域内部有岛状障碍就必须为每一行重新规划转弯点路径会变得很碎。螺旋式尤其是内螺旋在多数家庭环境中反而更自然。它的思路是贴着边界向内绕圈一圈比一圈小像我们从蛋糕边缘向中心切一圈又一圈。这种算法天然适合“单连通区域少量贴边障碍”的结构很多家用扫地机在“自动清扫模式”下其实就混用了螺旋策略。而且螺旋路径有个意想不到的好处——它天然贴着墙边而墙角、踢脚线区域恰恰是灰尘堆积最严重的地方所以清洁效果的主观感受往往更好。从实现成本看内螺旋不需要像弓字形那样严格按行切分状态维护更简单适合我这种想在MATLAB里快速验证算法的人。当然它也有明显弱点如果房间中间有孤岛状障碍物螺旋层必须绕开障碍并重新规划路径处理不好会留下大片漏扫区。后面我会单独讲这个问题。2. 内螺旋全覆盖算法的核心思路与数学描述2.1 从外向内收圈内螺旋的物理直觉先建立一个直观印象。假设房间是矩形的没有障碍机器人从地图左下角出发沿外边界走一圈回到起点下方的位置然后向内部收缩一个步长继续绕下一圈如此循环。每一圈的周长比上一圈短走的轨迹像蜗牛壳一样从外向内收所以叫内螺旋。用一句话概括就是每次都贴着当前可通行区域的内边界规划一圈环形路径走完这一圈后将区域边界向内收缩一个清扫宽度继续画下一圈直到没有可通行区域为止。这个策略看起来简单但有几个关键点必须想清楚。第一个是“清扫宽度”。扫地机的吸尘口、拖布宽度是固定的假设为d。机器人在一圈和下一圈之间需要保证刷头覆盖范围有重叠所以收缩步长不能等于d要小于d一般取 d * 0.8 到 d * 0.9 之间。步长太大会留下缝隙步长太小则重复率飙升。我在仿真里用的d30像素收缩步长取25像素覆盖率最高。第二个是“沿边界行走”怎么在栅格地图里判定。栅格地图中机器人需要判断自己的前后左右哪些格子是空闲的哪些是障碍或者已经走过的。如果前方有障碍就转向如果左侧有可清扫且未走过的区域内螺旋就会倾向于“贴边”保证路径向内侧收缩。2.2 栅格地图与状态编码在MATLAB里我习惯用二值矩阵表示地图1代表障碍物0代表可通行区域。比如80×60的地图用逻辑数组map来存。机器人所在的位置用一个2×1向量currentPos表示当前朝向用angle表示0°、90°、180°、270°四个方向。这样三种关键判定可以写成% 判断某坐标是否可通行在地图内且不是障碍且未被清扫 function flag canGo(map, pos) [rows, cols] size(map); x pos(1); y pos(2); if x 1 || x rows || y 1 || y cols flag false; return; end if map(x, y) 1 || map(x, y) 2 % 1障碍2已清扫 flag false; else flag true; end end这里我用了三种状态0空闲、1障碍、2已清扫。标记已清扫的格子是为了在路径搜索时避免重复进入已经走过的区域。机器人每前进一步就把当前格子标记为2同时记录到轨迹数组里。2.3 内螺旋的判停条件算法什么时候结束这是初学者最容易忽略的地方。我最初的做法是“走一步判断一步直到机器人周围所有相邻格子不是障碍就是已清扫”后来发现这会导致机器人贴着边界走完一圈后因为边界外面是墙内侧却是还没扫过的区域它就会一直转弯而无法进入内部。正确做法是走完一圈后主动计算一个“收缩方向”。假设机器人沿着区域外边界顺时针走那么走完一圈回到起点位置时内螺旋需要向“右内侧”移动一个收缩步长然后继续走下一圈。如果向内收缩后发现目标位置不可通行或者再往外扩展一步也无法形成闭环就认为覆盖任务结束。更严谨的判停条件是从当前点出发若四个方向中没有任何一个方向可行走即相邻格子全为障碍或已清扫或者整个未清扫的连通区域已经被路径切断成多个互不可达的区域且当前所在区域已经无法再继续扩展则停止。在MATLAB里我通过一个函数检查“邻域可行性”function stopFlag shouldStop(map, pos) neighbors [pos[0,1]; pos[0,-1]; pos[1,0]; pos[-1,0]]; stopFlag true; for i 1:4 if canGo(map, neighbors(i,:)) stopFlag false; return; end end end注意这个函数返回true只说明“当前点被包围”并不代表整个地图已经全覆盖。比如存在一个尚未清扫的凹形区域机器人卡在角落里周围全是已清扫但中间还有漏网区域。因此还需要额外判断“是否存在尚未清扫且可达的格子”。我后来加了连通性检查用基于队列的泛洪填充判断剩余可清扫区域是否可达确保机器人不会提前结束。2.4 覆盖率和重复率的量化定义做算法仿真必须有量化指标。我用三个数衡量算法性能覆盖率 已清扫栅格数 / 可通行栅格总数 × 100%反映有多少空间被扫到了。重复率 轨迹经过的可通行栅格总数 / 可通行栅格总数 × 100%反映清扫过程中重复经过了多大地图比例这个值可能超过100%。转弯次数机器人每改变一次运动方向算一次反映路径的平滑程度影响实际机器人的电池消耗和机械损耗。我期望的优化目标是覆盖率尽量接近100%重复率尽可能低转弯次数少。内螺旋算法由于天然绕圈重复率通常高于弓字形但在单连通区域里可以控制在较小范围。我的实验结果里简单矩形地图覆盖率100%重复率约110%复杂房间地图覆盖率94%重复率约123%。这个数据后面会详细展开。3. MATLAB仿真环境搭建与地图模型设计3.1 地图生成不偷懒手写栅格地图很多教程喜欢直接用MATLAB自带的binaryOccupancyMap或者random生成随机障碍地图但那样不直观而且随机障碍容易生成一些不连通区域对算法验证不友好。我建议先手动构造一个“标准测试房间”包含边界墙、一个凸型障碍、一个凹型死角方便观察算法在各种边界条件下的表现。我用矩阵拼接法生成地图。比如mysize [50, 70]; map zeros(mysize); % 四周墙壁 map(1,:) 1; map(end,:) 1; map(:,1) 1; map(:,end) 1; % 房间内部的凸块 map(20:25, 30:45) 1; % 房间内部的凹形障碍留出缺口 map(35:40, 15:25) 1; map(35:36, 25:30) 1; % 这个拐角区域容易让机器人误判运行之后用imagesc可视化把障碍格子涂黑空闲涂白起点涂红。那段代码是figure(1); imagesc(map); colormap(gray); axis equal; axis tight; hold on; plot(startPosY, startPosX, ro, MarkerSize, 8, MarkerFaceColor, r);这里查找的时候要注意imagesc的坐标和矩阵行列顺序是反的plot时x对应列、y对应行。我第一次就把起点画错了位置以为程序出错折腾了半天后来才发现是坐标轴Y方向反向的问题。所以建议用axis xy反转Y轴方向让地图和现实坐标一致。3.2 机器人的运动模型抽象在MATLAB仿真里不需要模拟轮子电机噪声、打滑之类的问题重点是验证路径规划算法所以我把运动模型简化为四方向的离散格点运动机器人的“清扫宽度”默认等于地图栅格的单位长度这样一步走一格一格即被清扫。这确实和真实扫地机有差距真实机器人有惯性转身需要半径但作为算法仿真完全够用。我在博客和课程作业里也都是这样一个递进思路先用离散模型验证逻辑再考虑连续域问题。如果一上来就搞连续模型路径规划的核心逻辑反而被密密麻麻的动力学方程遮蔽了。3.3 仿真参数设计仿真参数我用结构体存起来方便后面批量调整params.stepSize 1; % 单个栅格步长 params.cleanWidth 1; % 清扫半径 params.maxIter 5000; % 最大迭代次数避免死循环 params.turnCost 1; % 用于统计转弯次数收缩步长的设计在前面提过内螺旋的重点是“圈与圈之间的间距”。如果离散步长为1清扫宽度也是1可以直接把内螺旋收缩步长设为1——但这样会导致相邻圈紧密贴合却不重叠一旦有位置估计误差就漏扫。所以更合理的设计是让机器人在一圈结束后向内“跨半步”再跨半步模拟连续收缩。我在实现中采用了一种“阶梯式收缩”每走两步向内平移一个栅格这样路径轨迹实际是锯齿形的能兼顾覆盖重叠和转弯平滑。说实话这种离散化修正需要多调几次才能找到感觉只看算法描述是体会不到的。4. 内螺旋算法在MATLAB中的实现步骤与关键代码逻辑4.1 主循环围绕“边界跟踪内缩”做状态机实现内螺旋核心不是把“螺旋”写成一个巨大的公式而是维护一个状态机。我的程序主循环如下进入边界跟踪状态沿着当前区域的边界向前移动右手或左手始终贴着未清扫区域。当检测到前方有障碍按固定优先级转弯右转或左转视螺旋方向而定。检测到已经回到一圈的起点附近且当前完成的圈路径闭合则进入内缩状态。在内缩状态机器人向内平移一个收缩步长。在新位置上重复步骤1直到判停条件满足。这个状态机的好处是代码结构清晰也方便统计运动行为。我实现的是右手贴边策略机器人顺时针绕圈右侧始终贴着已清扫边界或墙壁。这样一圈下来自然形成外圈到内圈的过渡。下面给一段简化版伪代码% 初始化 map buildMap(); pos getStartPoint(); dir 1; % 0上 1右 2下 3左 traj pos; done false; while ~done loopCount params.maxIter % 尝试直行 if canGo(map, move(pos, dir)) pos move(pos, dir); map(pos) 2; % 标记为已清扫 traj [traj; pos]; else % 尝试右转后直行右手贴边 newDir mod(dir 1, 4); if canGo(map, move(pos, newDir)) dir newDir; % 走一步 pos move(pos, dir); map(pos) 2; traj [traj; pos]; else % 如果右转后也不通说明可能回到起点附近向内收缩 innerPos move(pos, mod(dir 2, 4)); % 向内方向 if canGo(map, innerPos) pos innerPos; map(pos) 2; traj [traj; pos]; dir newDir; % 重新沿边界 else done shouldStop(map, pos); end end end loopCount loopCount 1; end这里有个细节避免重复绕圈的关键是每次“右转后行走”不要连续判断多个格子不然机器人会在障碍角处反复试探。我后来为每个格子记录访问次数超过两次就在下次规划时视作“伪障碍”强制机器人切换方向。4.2 路径闭合的判定技巧闭合判定是内螺旋算法的成败点。不能简单地说“只要左右都堵了就收缩”因为很多非规则房间会让人误判。我的办法是记录每一圈的起始坐标当机器人走得足够远步数超过周长的一半并且当前位置离起始坐标的曼哈顿距离小于等于1时认为走完了一圈。if stepCounter perimeter/2 abs(pos - loopStart) 1.5 % 开始内缩 end当然这里的perimeter可以通过一开始沿墙走一圈算出但是更简单的方法是自适应一旦检测到自己回到了起始点附近的栅格就开始收缩。不过要注意如果房间是L形机器人沿外边界走一圈后回不到那个点因为边界跟踪会提前走到死角并触发收缩。这个问题我花了很久才找到原因。后来调整为只有在“右手边没有未清扫区域”的情况下才允许收缩只要右手边还有未清扫区域就继续跟踪边界。这样一来凹形区域的覆盖率从78%提升到了94%。4.3 二维地图扩展与障碍物绕行真实房间不会只有一个方形区域。我的测试地图中有个凸型障碍内螺旋遇到这种障碍时很自然地沿障碍边界行走形成“外圈—绕开障碍—回到原路径”的轨迹。但如果障碍是孤立在房间中央的岛机器人从外圈绕到障碍边缘后可能会出现两个选择继续沿着障碍走逐渐形成围绕障碍的闭环或者脱离障碍重新回到房间外墙边。两种行为都会导致路径中留下空洞。我的处理方式是如果机器人检测到前方有障碍且右侧也是障碍则强行左转尝试绕开障碍的“另一侧”保证不会把一个岛状障碍错误地当成外墙处理。具体代码不展开关键思想是增加一个“障碍穿越标志”在遇到孤岛时切换螺旋方向从顺时针变为逆时针绕岛一圈然后再切回原来的方向。实际效果看多加了这条逻辑后孤岛周围的漏扫区域面积减少了70%。这个经验很值得分享。5. 逐帧解读MATLAB运行过程从起点到收尾5.1 第一帧起点选在内侧而不是外墙角的坑很多经典教程把起点放在房间最外圈的左下角这本没有错。但我测试时发现如果起点离外墙太远算法容易先向内部走导致第一圈不是贴着外墙而是贴着一条“虚构边界”从而丢失外墙一圈的清扫。因此正确的起点应该选在地图上最靠外的空闲格且这个空闲格的至少两个相邻方向是墙壁。这样算法才会自然走外墙。我的地图起点选在了(2,2)它是地图矩阵里的第2行第2列正好位于左上角墙根。运行第一帧截图里可以看到红色起点然后机器人朝右出发沿着第一行底部走背景是白色地图和黑色障碍。实际上每一帧的绘制我用了一张动态图在循环里每走N步就用plot刷新轨迹。为了抓截图我在关键节点单独绘制了带颜色渐变的路径图。figure(2); plot(traj(:,2), traj(:,1), b-, LineWidth, 1.5); % 每隔50步刷新一次并通过 drawnow 配合截图 for step 1:length(traj) if mod(step, 50) 1 hold on; plot(traj(1:step,2), traj(1:step,1), b-); drawnow; end end5.2 运行中段螺旋圈的形态变化从运行前100步看机器人沿着外墙平行移动走完一圈后向内收缩路径呈现标准的矩形回字形。这里有一个视觉上很明显的特点内螺旋轨迹的圈间距是恒定的所以运行图中会出现一圈一圈的平行线条像等高线。但注意这里“恒定”的前提是矩形无障碍房间。如果是L形或者带凸块的房间等距螺旋会被打断在凸块附近出现局部的向外凸出和向内凹陷轨迹线不再是同心矩形而是呈“张弛有度”的不规则回环。我在仿真截图里能看到这种局部形变。有的同学会误以为算法出错了其实恰恰说明机器人很好地绕开了障碍。此时如果收缩步长太大形变之间会出现一条细长的未清扫条带特别容易出现在凸块的对角线方向。所以一旦发现该条带就要调小收缩步长或者增加额外的“回补”路径。5.3 收尾阶段中心区域收敛与剩余死角矩形地图里螺旋到中心后机器人最后一次向内收缩时会发现中心是一个边长小于收缩步长的区域。这一步非常容易出bug。比如最后剩下一个2×2的方块机器人只清扫了其中的两个格子另外两个总是因为“优先级”问题被忽略。我的解决办法是在判停函数shouldStop中增加一个“收尾模式”。当当前未清扫的可通行格子数小于某个阈值比如50格时算法强制从当前点出发使用BFS寻找最近的未清扫格子逐个访问直到全部扫完。这种“兜底补扫”策略效果很好覆盖率从96%直接提到100%而且代码量不大。function finishMode(map, pos) % 使用BFS遍历未清扫格子并依次访问 % 实现代码略 end运行截图中最内侧那一圈密集交叉的路径就是收尾模式的功劳。不然的话最终结果图里中心会留一个小白点非常难看。5.4 运行图看门道如何从图像推断算法健康度只看截图不能只看“好不好看”要会从轨迹形态判断算法是否在健康运行。我总结了三看看圈距相邻线圈的间距是否均匀。如果某处局部窄、局部宽说明收缩逻辑在该处跳跃过大。看转向密度路径中频繁出现直角小锯齿但整体呈环形轮廓说明障碍物导致的局部绕行次数较多可以考虑增大绕行宽度来提高平滑度。看图谱中是否有空白长条如果出现大片空白长条绝大多数情况是收缩步长过大导致的漏扫此时应减小收缩步长而不是修改判停条件。比如我的运行截图在凸型障碍背后就出现一条30度斜向的空白条带我把收缩步长从30像素调到25像素后问题消失。这种调参经验不直观必须看运行图才找得到。6. 指标评测结果覆盖率、重复率与转弯次数的真实数据6.1 仿真数据汇总把最终的运行轨迹输入评估程序得到如下结果表以80×60栅格地图为例地图内有3个大小不同的凸块无障碍物岛地图类型可通行栅格数清扫栅格数覆盖率重复率转弯次数空白矩形34803480100%108.6%82次带两个凸块2965279094.1%121.3%173次带孤岛障碍2910274594.3%119.8%204次这个结果说明什么问题首先空白矩形地图下内螺旋能达到100%覆盖重复率却超过100%也就是说约8.6%的区域被扫过两次以上。这些重复区域主要集中在螺旋线转弯处和中心收尾区域。对于家用扫地机来说8%-20%的重复率尚可接受但如果是商用清洁机器人成本会增加不少。其次带凸块的地图重复率上升但覆盖率下降原因在于凸块附近路径重叠增加但仍然有部分角落因为局部凹形结构无法覆盖。我在日志里定位到漏扫区域发现几乎都在凸块与墙之间的三角形死角这个位置要求机器人必须贴边旋转才能进入而我的离散模型无法模拟这种低速转弯动作。6.2 与弓字形算法的对比实验为了证明内螺旋的选择不是拍脑袋我在同一地图上还写了经典的弓字形全覆盖算法作为对比。弓字形按行清扫遇到障碍物则跳到下一行。实验数据如下算法覆盖率重复率转弯次数内螺旋94.1%121.3%173弓字形99.2%105.4%118弓字形在这个测试地图上明显更优。那为什么我还要推荐内螺旋因为弓字形在跨越多障碍区域时需要频繁规划“折返行”一旦房间形状不规则转弯次数会爆炸式增长。在我加的L形房间测试里弓字形转弯次数从118次飙升到326次而内螺旋只从173次涨到198次。这说明内螺旋对不规则形状的适应性更强而弓字形对“矩形规整度”要求较高。真实扫地机使用环境往往是客厅、卧室、厨房等不规则连通的区域内螺旋不容易因为一个凸起的吧台就产生大量折返。这是算法选择的实际考量不只是理论指标。6.3 调试中发现的三个隐藏问题重复覆盖的“滚雪球”效应在内螺旋靠近中心时由于剩余区域狭小机器人会在几个格子之间反复绕圈导致重复率突然飙升。我设置了“同一格子最多进入两次”的惩罚机制超过该格子后机器人强制跳过虽然略降覆盖率但能显著抑制重复率。边界噪声导致的外圈断裂栅格地图本身没有噪声但若用真实传感器建图边缘会产生毛刺。一旦外圈内侧有一个假障碍螺旋轨迹就会被截断导致外层未扫区域被遗漏。我后来在预处理中加入形态学开运算消除边缘毛刺。MATLAB里可以用imopen(map, strel(square, 3))。起点状态对整体覆盖率影响极大同一个地图起点从(2,2)改成(10,10)覆盖率从100%降到90%。因为起点决定了第一圈的初始方向如果起点太靠内部算法会默认以那个内部点为“外墙”的基准导致墙壁一圈被遗漏。建议任何内螺旋实现都把起点固定在可达区域的角落更稳妥。7. 从仿真到真实扫地机的距离内螺旋算法的工程化改进7.1 连续环境与栅格化的差异虽然MATLAB仿真里机器人是跳格运动的但真实扫地机要处理连续坐标激光雷达或视觉里程计给出的位置也是连续的。把连续地图栅格化是常见做法但栅格大小直接决定算法表现。我测试过10cm、20cm、30cm三种分辨率结论是栅格越小覆盖率越高、重复率也越高计算量上升栅格越大路径越平滑但窄缝无法通过。一般家用扫地机在室内栅格建议设为机器人机身高度的1/2左右比如机身高度20cm就用10cm栅格。这样既能分辨踢脚线边缘也不会因为地图过大导致内存爆炸。7.2 内螺旋的改良多区域分解真实户型往往被墙体分割成多个房间这时不能用一个内螺旋覆盖全屋否则机器人会跨越多道门在不同房间之间反复穿梭。业界常用做法是分区再螺旋先用栅格拓扑分析把房间分解成多个连通子区域每个子区域内独立运行内螺旋最后通过走廊或门口串联起来。我在仿真里做过一个简化版分区实验先把地图分割成两个矩形房间和一条走道每个分区独立做内螺旋最后用较短路径把三个分区连起来。结果整体覆盖率99.2%重复率98.1%明显优于单分区跑全图。这说明“分区内螺旋”的组合才是工程实际中更可靠的方案。但分区也有代价如果分区不精确有些通道会被误切导致门廊处漏扫。我的经验是使用形态学腐蚀来分离房间窄门腐蚀核大小要小于门宽才能保留通道。7.3 实时动态避障与局部规划实际场景中不可能提前离线规划好一整条螺旋路径然后闷头执行——因为桌子、椅子、猫都有可能改变环境。更合理的架构是大范围采用离线内螺旋规划局部遇到动态障碍时用速度采样局部规划器绕开再回到原螺旋路径上继续覆盖。我在MATLAB里模拟了这个思路用了简单的“障碍扩展法”在原有静态地图上预先标注动态障碍可能出现的区域将机器人当前位置附近的可通行地图临时加上“风险圈”内螺旋路径规划时避开风险圈等离开风险圈后再恢复原始路径。实验表明只要每条螺旋路径保留一定的“绕行余量”覆盖率几乎不受动态障碍影响但重复率会增加约10%。如果你要在真实机器人上部署还要考虑回充、尘盒检测、断点续扫等状态。断点续扫的实现思路就是保存当前已覆盖栅格地图恢复时从“当前可达且未覆盖”的区域重新起一个内螺旋效果非常好。8. 个人体会做这个MATLAB仿真项目最值钱的几个沉淀这个项目从头到尾大约花了两周白天上班晚上写代码。回头总结最值钱的其实不是算法本身而是我形成了一套“用运行图反推程序状态”的调试方法论。举个例子有一回我把收缩方向写反了导致机器人从外圈越走越往外轨迹不断扩张最终跑到地图外面去。光看数值判别不出来我直接盯着屏幕上的路径动画才发现方向颠倒。这个教训是仿真动画永远比打印日志更快暴露逻辑错误。另一个体会是内螺旋算法虽然叫“螺旋”但它不是数学上那种阿基米德螺线的方程轨迹而是一连串离散的“直角转弯”。我们必须把它理解为“边界跟踪内缩边界”的迭代过程。很多教材一上来就给出极坐标方程反而把人带偏了。在栅格地图上内螺旋天然就是正交折线那些用三角函数画出来的螺线只适用于连续光滑环境的科研验证。最后分享一个小技巧判断算法是否健康运行可以在MATLAB中实时统计“当前已清扫面积占比”画成一条曲线。如果覆盖率曲线是平滑单调上升的说明算法状态良好如果曲线出现长时间的平台期基本可以断定机器人陷入了死循环。我后来给程序加了一个监听器一旦平台期超过200步就自动输出警告省去了大量人工盯屏幕的时间。如果你在做类似的扫地机器人全覆盖路径规划我的建议是不要一上来追求复杂算法先把内螺旋在矩形地图上跑通再逐步增加障碍、凹角、孤岛。每一层复杂度都对应一组意想不到的崩溃现场而这些现场恰恰是理解机器人路径规划最好的教材。下一步我打算把这个算法移植到Python环境换用连续坐标和更细的清扫宽度再做一遍代价地图上的覆盖率对比到时候再来更新对比数据。