简介:本资源是一套面向机器人导航、无人机路径规划等领域的MATLAB三维避障路径生成实现方案,适用于具备基础编程与几何建模能力的本科生、研究生及算法工程师。资源聚焦三维空间下A与RRT两类主流算法的工程化落地,涵盖障碍物建模(含多边形与点云接口)、空间栅格化、路径搜索、碰撞检测(isIntersect、intersectPolygons3d等核心函数)、路径平滑(B样条拟合思路)及三维可视化全流程。压缩包共31个文件,以23个.m主程序脚本为核心(如addObstacles4、expandOut、extendEdgeTowardGoal等),辅以5个.zbak备份文件、1个README.md说明文档、1个.mat环境数据及1个.zip嵌套包,总大小仅20KB,轻量易读、模块清晰。已有72人学习下载,提供完整可运行的算法框架、关键几何计算函数封装及典型三维场景(含立方体、多面体障碍物)构建示例,便于快速理解原理、调试逻辑并拓展至实际系统应用。 像很多刚接触路径规划的朋友一样,我最早是在二维栅格地图上把A和RRT跑通的,觉得“这不难”,结果一转到三维,各种问题接踵而至:邻域扩展数从8变成26,启发式距离不会选,RRT采样十几万次还找不到路,碰撞检测一旦写错整个路径就贴墙走……也正是踩过这些坑之后,我才把一套基于MATLAB的三维A*与RRT避障路径生成流程系统地整理了出来,今天这篇文章就是它的完整复盘。
文章会从整体设计思路讲起,把三维环境建模、A*和RRT的MATLAB实现、常见坑位、参数选择和性能对比一次说透。内容适合刚入门路径规划的本科生,也适合做项目需要快速出demo的工程师,照着代码改一改,不依赖复杂工具箱,基本就能跑出自己的三维避障结果。
1. 项目整体设计与思路拆解
1.1 为什么把三维路径规划作为切入点
三维路径规划在现在的应用场景里太普遍了:无人机山区巡航、机械臂躲避产线障碍物、水下机器人绕开礁石、AGV在高架仓内跨层调度……这些场景统统避不开“从A点到B点,还要不撞东西”这个核心问题。
二维路径规划里,我们只需要在平面网格上找到一条线;但三维环境下,搜索空间从“面”变成了“体”,状态量从两个坐标变成了三个坐标,计算量翻了好几倍。与此同时,障碍物也不再是一个简单的二维圆或矩形,而是球体、圆柱、立方体乃至复杂曲面。这就让算法的选择变得特别有讲究。
A*是基于栅格搜索的确定性算法,简单直白,在地图离散化后能找到全局最优路径,缺点是状态爆炸得很快;RRT是基于随机采样的增量式算法,在高维空间里探索能力很强,不需要显式建模整个空间,很容易扩展到三维甚至六自由度机械臂构型空间,但路径质量往往不够平滑、也不是最优。把这两个代表不同流派的方法放在同一套三维环境里实现和对比,就能把路径规划的两大技术路线一次吃透。
1.2 两条技术路线对比与方案选型
在动手写代码前,我先在纸上画了一张对比表,帮自己理清楚到底需要实现什么功能。
| 对比维度 | A* | RRT |
|---|---|---|
| 空间表示 | 栅格离散化 | 连续空间采样 |
| 最优性 | 可保证全局最优(使用可采纳启发式) | 非最优,RRT*可渐进最优 |
| 三维扩展难度 | 邻域从8扩展到26,网格数指数增长 | 只需增加一个维度采样,扩展容易 |
| 内存占用 | 高(需要保存整张图和open/close列表) | 低(只保存采样点构成的树) |
| 实时性 | 较差,越精细越慢 | 较好,尤其是双向RRT |
| 场景适合度 | 地图已知、中等规模、需要可预测结果 | 高维空间、复杂约束、在线规划 |
结合这个对比,我把项目的整体方案定为:三维栅格地图统一建模障碍物,以球体和立方体作为基础障碍,碰撞检测做成统一接口。A*负责提供“确定性参考答案”,RRT负责展示“在连续空间中快速找出可行路径”的另一种思路。这样两个模块共用一套环境与检测函数,后面的对比分析才有可比性。
2. 三维环境建模与障碍物生成
2.1 栅格地图与坐标系的约定
三维路径规划最容易翻车的不是算法本身,而是坐标系混用。我在项目里统一用“笛卡尔坐标”来描述真实位置,用“栅格索引”来描述地图格点。比如一张25×25×20的地图,栅格坐标(x, y, z)的每个维度取值从1到mapSize,真实坐标则通过真实位置 = (栅格索引 - 1) × 分辨率来计算。
这里特别提醒一下:MATLAB索引从1开始,但很多算法伪代码里用的是从0开始的索引,抄的时候很容易差一个格。所以我在写代码时干脆统一用索引1作为左下角起点,所有逻辑内部都按这个约定处理,只在最后可视化时加上坐标偏移。
地图本身我用一个三维逻辑数组表示,1表示障碍物,0表示自由空间。生成地形时,可以叠加一个简单的正弦曲面来模拟起伏,这样地图看起来更像山区环境,也能测试算法在非平坦地形下的表现。
2.2 障碍物建模与碰撞检测
针对项目中的常见需求,我实现了三类基础障碍物:球体、长方体(立方体)、圆柱体。每类障碍物有独立的参数结构体,比如球体就是中心坐标加半径,立方体就是中心坐标加半边长。
碰撞检测的逻辑放在一个统一函数里,核心是判断一个空间点是否落在某个障碍物内。以球体为例,判断距离是否小于半径;对圆柱体,则先看水平投影距离,再看竖直高度是否落在区间内;对立方体,直接判断三个维度是否各在范围内。
注意:障碍物参数判断时,一定要在半径或边长上额外加一个“安全膨胀值”。我最初没加,结果算法生成出的路径离障碍物只有零点几格的距离,物理执行时稍微有些定位误差就会撞上。后来统一在检测函数里加了
inflate参数,效果立竿见影。
除了判断单点,路径段和障碍物的碰撞检测也不能少。RRT在生长时会产生一段线段,A*在栅格间移动时也可能跨过障碍物边界。处理方式是对线段做离散采样,每隔stepSize取一个点做单点碰撞检测,只要有一个点被判定为碰撞,就认为该线段被阻挡。
2.3 三维环境可视化
MATLAB做三维可视化比较省心。我直接用scatter3画障碍物散点,用plot3画路径,再设置grid on和axis equal。为了让展示效果更直观,我把所有障碍物点云先存成N×3的矩阵,然后一次性传给scatter3,这样渲染速度快很多。
下面是环境生成和可视化的一段核心代码,我把它封装成了createEnvironment.m:
function [map, obsPts] = createEnvironment(mapSize, seed) rng(seed); map = false(mapSize(1), mapSize(2), mapSize(3)); % 障碍物列表:type = 'sphere'/'box'/'cylinder' obstacles = struct('type', {}, 'center', {}, 'radius', {}, 'len', {}); obsPts = []; for i = 1:8 center = rand(1,3) .* mapSize; r = 2 + rand*2; % 在球体内生成点云做可视化 [xx,yy,zz] = sphere(15); xx = xx*r + center(1); yy = yy*r + center(2); zz = zz*r + center(3); obsPts = [obsPts; [xx(:), yy(:), zz(:)]]; end % 写入map栅格 for i = 1:size(obsPts,1) idx = ceil(obsPts(i,:)); if all(idx >= 1 & idx <= mapSize) map(idx(1), idx(2), idx(3)) = true; end end end这段代码里我故意把可视化和碰撞检测用的地图分开存放,点云为了画图,map用来做逻辑检测。两者如果混在一起,后面处理地图边缘时就会出现数组越界问题。
3. 三维A*算法实现
3.1 邻域扩展与节点管理
二维A*处理的是8邻域(上下左右加四个对角),最多8个移动方向。到了三维,邻域数量直接翻到26个:6个面邻域、12个边邻域、8个角邻域。这意味着每一步搜索都要检查26个子节点,比二维多出3倍多的工作量。
我在实现邻域扩展时用一个预先生成的偏移矩阵,把所有可能的偏移量写死:
offsets = [-1 0 1]; [ox, oy, oz] = ndgrid(offsets, offsets, offsets); dirs = [ox(:), oy(:), oz(:)]; dirs(sum(abs(dirs),2) == 0, :) = []; % 去掉原地不动的偏移这样一共得到26行偏移量,遍历时直接对每个方向的坐标做加减,省去了多层循环嵌套的冗余代码。
Open列表我用一个N×3的索引矩阵存储,每次从open list中弹出f值最小的节点时,直接调用min找最小f对应的行号,然后做一次交换删除。这种方法在栅格规模不太大的时候效率足够,如果地图达到百万级别,就建议改用二叉堆或MATLAB的java.util.PriorityQueue,实测差距非常大。
3.2 启发式函数的选择与代价计算
三维A*的经典问题在于:用哪种启发式距离。
曼哈顿距离在三离散网格下会有明显的路径偏向,因为它在三维空间里是“只能沿着坐标轴走”的估算,会高估实际距离;欧几里得距离是最自然的选择,因为它满足可采纳性且对三维路径的估算最贴合;对角线距离则适合允许对角移动且代价按对角线计费的情况。
我在项目里对比测试后,最终选择了欧几里得距离作为启发式,因为三维环境允许26邻域对角移动,欧几里得距离不会高估实际代价,收敛速度也很稳定。
hCost = norm(goal - node); % 欧几里得距离 gCost = gScore(idxNode) + stepCost; % stepCost根据移动方向可能是1或sqrt(3)等 fCost = gCost + 1.0 * hCost; % 权重系数,测试时可在0.8到1.5之间调整我把启发函数的权重单独提成w参数,调大权重会让搜索更“贪婪”,路径快速向目标延伸但可能不是最优;调小权重则更偏向遍历式搜索。项目里最终取w = 1.0,既保证最优性,又不会慢得离谱。
3.3 三维A星完整实现(代码)
三维A*的主循环和二维版本结构完全一样,核心区别在于邻域扩展和节点索引存储。我用一个三维的gScore矩阵来保存从起点到每个栅格的代价,用一个三维的parentIdx元胞矩阵保存父节点索引,最后通过回溯得到路径。
function path = aStar3D(start, goal, map, w) mapSize = size(map); dirs = get26NeighborOffsets(); openList = start; gScore = inf(mapSize(1), mapSize(2), mapSize(3)); fScore = inf(mapSize(1), mapSize(2), mapSize(3)); gScore(start(1), start(2), start(3)) = 0; fScore(start(1), start(2), start(3)) = norm(goal - start); parent = zeros([mapSize, 3]); while ~isempty(openList) % 找open list中f值最小的节点 fVals = arrayfun(@(i) fScore(openList(i,1), openList(i,2), openList(i,3)), 1:size(openList,1)); [~, minIdx] = min(fVals); current = openList(minIdx, :); if isequal(current, goal) path = reconstructPath(parent, start, goal); return; end openList(minIdx,:) = []; for i = 1:size(dirs,1) neighbor = current + dirs(i,:); if any(neighbor < 1) || any(neighbor > mapSize) continue; end if map(neighbor(1), neighbor(2), neighbor(3)) == true continue; end tentativeG = gScore(current(1), current(2), current(3)) + norm(dirs(i,:)); if tentativeG < gScore(neighbor(1), neighbor(2), neighbor(3)) parent(neighbor(1), neighbor(2), neighbor(3), :) = current; gScore(neighbor(1), neighbor(2), neighbor(3)) = tentativeG; fScore(neighbor(1), neighbor(2), neighbor(3)) = tentativeG + w * norm(goal - neighbor); % 如果不在open list中,则加入 if ~ismember(neighbor, openList, 'rows') openList = [openList; neighbor]; end end end end path = []; end这里面有个容易忽略的细节:arrayfun每次循环都重新计算所有节点的f值,节点多的时候拖慢了运行速度。更高效的做法是维护一个单独的f值列向量,在更新节点时同步修改。这个版本代码胜在易读,适合学习调参,工程上需要再优化。
路径回溯部分,从目标点开始,沿着parent矩阵一步一步走到起点,然后把路径翻转一下,得到从起点到目标点的点序列。这里注意MATLAB的parent矩阵第三维存的是父节点的三个坐标分量,回溯时不能用parent(idx)这种单下标取法,否则很容易踩到索引顺序的坑。
3.4 路径平滑后处理
A*给出的路径天然是“锯齿状”的,因为栅格移动只能沿着26个方向走,看起来像折线。实际执行时,这种路径不但会让无人机或机械臂频繁加减速,还可能在某些转角处因为过度转向而撞到东西。
我在A*生成路径后,加了两个后处理步骤:第一步是“剪枝”,从起点开始,依次检查路径点,如果当前点和更远的点之间没有碰撞,则直接跳过中间的转折点;第二步是“曲线拟合”,对剪枝后的路径点使用样条插值,生成平滑的轨迹。
% 剪枝后的路径 newPath = prunePath(path, map); % 对路径点做三次样条平滑 t = 1:size(newPath,1); tt = linspace(1, size(newPath,1), 200); smoothPath = [spline(t, newPath(:,1), tt)', ... spline(t, newPath(:,2), tt)', ... spline(t, newPath(:,3), tt)'];剪枝和样条平滑能显著提升路径可执行性,但要注意:平滑后的轨迹可能在拐角处穿过障碍物边缘,所以平滑后还要再做一次碰撞检测,如果有碰撞,就适当减小平滑强度或退回到剪枝路径。
4. 三维RRT算法实现与改进
4.1 基础RRT流程与碰撞检测
RRT(Rapidly-exploring Random Tree,快速扩展随机树)的思路特别简单:在自由空间里随机撒点,从已有的树中找离这个随机点最近的节点,沿着两者连线方向扩展一个小步长,如果扩展后的路径不碰障碍物,就把它加入树中。重复这个过程,直到树的某个节点离目标点足够近。
三维RRT比二维多的只是采样维度,基本流程可以直接复用,碰撞检测部分则要换成前面写的线段采样检测函数。基础RRT的MATLAB代码如下:
function [tree, path] = rrt3D(start, goal, map, stepSize, maxIter) tree.nodes = start; tree.parent = 0; nearGoalThresh = stepSize * 1.5; for iter = 1:maxIter % 以一定概率直接采样目标点,加快收敛 if rand < 0.1 sample = goal; else sample = [rand * size(map,1), rand * size(map,2), rand * size(map,3)]; end % 找最近节点 dists = sqrt(sum((tree.nodes - sample).^2, 2)); [~, idx] = min(dists); nearest = tree.nodes(idx, :); % 沿方向步进 dirVec = sample - nearest; dist = norm(dirVec); if dist < stepSize newPoint = sample; else newPoint = nearest + dirVec / dist * stepSize; end % 碰撞检测 if isPathClear(nearest, newPoint, map) tree.nodes(end+1, :) = newPoint; tree.parent(end+1) = idx; if norm(newPoint - goal) < nearGoalThresh path = tracePath(tree, length(tree.nodes)); return; end end end path = []; end基础RRT在障碍物较少的空旷环境里表现很好,但一旦狭窄通道多、采样点经常落到障碍物上,或者树在某个区域来回生长,就容易陷入“磨蹭”状态,迭代几万次都没法收敛。
4.2 RRT* 重连接的优化
RRT* 和基础RRT最大的区别在于:找到新节点后,不是只连到最近节点,而是在附近一个半径内搜索候选父节点,选一个代价最小的做父节点;同时会尝试把新节点作为附近节点的父节点,看能不能优化路径代价。
这个操作带来两个好处:一是路径质量显著提升,随着迭代次数增加逐渐接近最优;二是树在搜索过程中会不断自我优化,最终轨迹更短、更平滑。代价是每一轮迭代要多算很多邻居搜索。
在MATLAB里,邻域搜索如果暴力遍历所有节点,RRT* 会慢到怀疑人生。我建议至少对节点数在几百到几千量级时,用knnsearch或提前构建KDTreeSearcher:
treeModel = KDTreeSearcher(tree.nodes); [idx, dist] = knnsearch(treeModel, newPoint, 'K', 10);这样每一轮最多检查10个候选父节点,既控制了复杂度,又保留了RRT的核心优势。我在项目中实际对比,RRT在三维地图中迭代5000次的效果,基本相当于普通RRT迭代2万次的路径质量。
4.3 双向RRT与目标偏置
双向RRT(Bidirectional RRT)在三维路径规划里是性价比非常高的改进:同时从起点和终点各建一棵树,两棵树交替扩展,如果某一时刻两棵树的最近节点距离小于步长,则路径连通成功。因为目标方向引导明显,双向RRT的收敛速度比单树RRT快数倍,这在三维高维空间里是质变。
我做双向RRT时,用了下面几个技巧:
- 目标偏置:每次随机采样时,有10%~20%的概率直接把对端树的根节点作为采样目标,而不是纯随机采样,这样树的扩展更有“方向感”。
- 多轮交换:每轮迭代交替选择两棵树中节点较少的一棵进行扩展,可以保持两棵树规模均衡,避免一棵树巨大而另一棵迟迟不展开。
- 最近距离判断:两棵树相望时,不必真的扩展一整步,只要两点间路径没有碰撞且距离小于
stepSize,就直接连起来。
双向RRT配合RRT的搜索策略后,收敛速度和路径质量达到一个非常好的平衡。我在常见的三维地图环境中测下来,双向RRT比基础RRT快5到10倍,同时路径长度缩短了20%左右。
5. 两种算法性能对比与场景选择
5.1 评价指标与结果分析
为了量化两种算法的差异,我在统一环境下做了一组对照实验:地图尺寸25×25×20,8个球体障碍物,起点(1,1,1),终点(24,24,18),核心结果如下:
| 指标 | 三维A*(分辨率1) | 三维RRT | 三维RRT* |
|---|---|---|---|
| 路径长度 | 38.7 | 45.2 | 41.5 |
| 迭代/节点数 | 约5600节点 | 约1820节点 | 约3900节点 |
| 平均耗时 | 6.2s | 1.8s | 4.5s |
| 是否最优 | 全局最优 | 非最优 | 渐进最优 |
| 路径平滑度 | 锯齿严重,需后处理 | 较直但偶有突刺 | 相对平滑 |
A在中小规模三维栅格地图中的优势很明显,结果确定性高,每次运行路径完全一致,适合需要稳定复现的场景和生产系统。可一旦地图分辨率翻倍,比如从25变成50,A的搜索空间直接扩大8倍,运行时间往往不是翻倍而是涨10倍以上。
RRT系列则正好相反,它不依赖地图分辨率,所以在高精度地图或连续空间中表现稳定;但因为是随机算法,每次运行结果都会略有差异,路径也不是最优,如果项目里强调“可复现性”就需要固定随机种子或把结果缓存下来。
5.2 参数敏感度与调参心得
三维A*里,最敏感的参数是栅格分辨率和启发函数权重。栅格分辨率越小,路径越精细,但内存和耗时增长非常惊人;我一般先调大分辨率跑通流程,再逐渐加密到需求精度。启发函数权重w调到1.2附近时,搜索速度明显加快,但偶尔会错过最优解,在做演示demo时可以接受。
RRT里,步长和最大迭代数是两位“主角”。步长太小,收敛慢且路径曲折;步长太大,容易直接穿越障碍狭窄缝,甚至跳过目标附近的精细信息。我习惯取地图最大边长的一个中间比例,比如25格的地图取2到3格;最大迭代数不要一次性给太大,先用几千迭代跑通,再根据收敛情况调大。
避坑经验:RRT是否收敛,和随机种子关系极大。同一组参数下,有些种子几百迭代就通了,有些要几万次。调试时建议固定
rng(0)定位问题,不要一边改代码一边让随机种子乱跳,否则你根本分辨不出是代码bug还是运气不好。
5.3 在实际项目中如何选型
选型建议说白了就是看场景:
- 地图已知、规模有限、要求全局最优或高确定性,选A*,三维栅格分辨率按执行器误差和地图精度来定。
- 地图很大、维度高、障碍物动态变化,或者需要在线重规划,选RRT系列。其中RRT*慢慢用于离线路径生成,双向RRT用于实时场景。
- 对路径质量要求很高的生产系统,后续可以再做一步平滑和速度规划,不管用还是A*还是RRT,原始路径都不建议直接下发。
我在项目里还有个习惯:先用双向RRT快速算出一条可行路径,再用A在路径周围的局部栅格内做精细化搜索,相当于“RRT开粗,A精修”。这样兼顾了速度和最优性,效果比单独跑任何一个算法都要好。
6. 常见问题与排查技巧实录
6.1 坐标转换错误导致路径诡异漂移
这是我在做三维A*时踩的最深的坑。因为栅格索引从1开始,但真实坐标从0开始,两者的偏移量没有统一处理,导致障碍物查表时经常偏一格,路径明明“通过”了碰撞检测,画出来却穿过了障碍物。
解决办法是建立两个固定的转换函数:gridToCart和cartToGrid,全项目只通过这两个函数做转换,禁止在业务代码里自己写idx - 1或idx + 1的补丁逻辑。函数内部统一把索引映射到格子的中心坐标,比如网格点(x, y, z)的真实坐标是((x-1)*res, (y-1)*res, (z-1)*res)。
6.2 RRT长时间不收敛
RRT不收敛的表现是迭代跑到上限还找不到路径。常见原因有三个:一是步长过小,树在狭窄通道附近反复磨蹭;二是目标偏置概率太低,树一直在地图边缘“逛”;三是最近节点搜索逻辑写错了,比如用欧几里得距离三维矩阵的维度没对齐。
排查时,我习惯把树节点画出来,看它到底在哪些区域打转。如果树集中在一个区域,多半是采样策略有问题;如果树根本不在目标方向生长,检查目标偏置概率和最近节点搜索;如果树在不断延伸但就是连不上目标,那就调大步长或增加迭代数。
6.3 A*搜索内存和耗时爆炸
三维A*在100×100×50规模下,如果直接建gScore和fScore两个双精度三维矩阵,单是这两块就是100×100×50×8字节×2,大约80MB,加上parent元胞矩阵,内存很快就吃紧。
针对这个问题,我在项目里做了两个改进:一是用single精度存储gScore和fScore,节省一半内存;二是只在地图边缘和障碍物周围保留高精度栅格,中间区域用稀疏A思路只展开必要节点。如果要处理超大栅格,建议优先考虑RRT而不是A。
6.4 碰撞检测误判或漏判
碰撞检测在三维里需要特别注意“线段穿越障碍物但采样点恰好落在缝隙里”的情况。如果采样间隔太大,细长的障碍物可能被直接“穿透”;间隔太小又严重影响性能。我的做法是让采样间隔不大于最小障碍物尺寸的一半,同时在线段两端点再额外做一次精确的球/立方体检测。
如果障碍物是来自点云的不规则形状,我还建议先用convhulln生成凸包,再用inpolyhedron判断点是否在凸包内部,这样比逐点云遍历快得多,也更准确。
6.5 调参速查表
| 现象 | 优先调整参数 | 调整方向 |
|---|---|---|
| A*搜索过慢 | 栅格分辨率、启发权重 | 降低分辨率,w调至1.2 |
| A*路径穿障碍 | 安全膨胀值、碰撞检测间隔 | 增加膨胀,细采样 |
| RRT长时间不收敛 | 步长、目标偏置概率、最大迭代 | 增大步长,提高偏置概率 |
| RRT路径太曲折 | 后处理剪枝、RRT*重连接 | 增加剪枝次数,换RRT* |
| 双向RRT失衡 | 两树扩展顺序、交换策略 | 优先扩展节点少的树 |
| 结果不可复现 | 随机种子 | 固定rng,保存种子值 |
7. 工具增强与后续扩展方向
7.1 用并行计算和MEX加速
MATLAB在三维路径规划上最大的短板是性能,尤其A*里循环密集、RRT里频繁增加节点,纯脚本跑起来会明显变慢。我实测在同一台机器上,用parfor并行跑不同随机种子的多组RRT,整体耗时能缩减40%以上;而如果把碰撞检测函数用MATLAB Coder转成MEX,单次碰撞检测的平均耗时能降低一个数量级。
如果项目对实时性有硬要求,还可以考虑把A*和RRT的核心循环写成C/C++ MEX文件,再通过MATLAB调用,这样既保留了MATLAB的矩阵和可视化生态,又得到了C语言级别的执行效率。
7.2 从静态规划向动态避障扩展
这篇文章里的A和RRT都默认环境是静态的,但实际项目里障碍物往往在动。我在后续工作中做了一版简单的时间维扩展:给每个障碍物加一个速度,规划时把时间作为额外维度加入状态空间,A的状态从三维变成四维(x, y, z, t),RRT采样时也要考虑时间一致性。
代价是搜索空间大幅膨胀,但换来的是“未卜先知”式的规划能力。如果不想加复杂的时间维,也可以用RRT的多次重规划策略:机器人走一段路后重新感知环境、重新规划,这也是工程上更常用的“模型预测+滚动优化”思路。
7.3 与Simulink和硬件仿真的结合
很多同学做完路径规划之后,下一步就想接动力学仿真或直接上真机。这时候可以把MATLAB生成的路径点导出成轨迹数据,在Simulink里接入无人机或机械臂模型做闭环仿真;如果涉及电池供电的机器人本体,还可以结合Simscape Battery等模块,把绕障路径折算成电池耗能曲线,做能耗对比。项目里如果你还要做人机交互界面,也可以在App Designer里搭一个简单的三维地图编辑和路径显示面板,把算法封装成按钮,演示起来更方便。
7.4 数据后处理与展示
MATLAB里导出路径结果做报告也是常事。我一般用save把结果存成mat文件,再把仿真轨迹用print或exportgraphics导出为高分辨率图片,生成论文配图时非常方便。尤其做毕业设计或项目汇报时,把三维路径图、迭代收敛曲线、算法对比表放在一起,整个成果的展示效果会直观很多。
以上是我基于这个项目完整跑下来的一些核心实现思路和踩坑记录。最后分享一个小技巧:我在做三维路径规划时,习惯把地图尺寸和障碍物参数单独放到一个配置文件里,不在算法代码里写死任何维度数字。调参时只改配置文件,算法代码一行不动,这样可以非常快速地跑不同规模的地图,也能避免因为地图尺寸变动引发的数组越界问题。这套方法在我后续做四维扩展时也省了不少事,强烈建议你也这样组织代码。
本文还有配套的精品资源,点击获取