☰
异构固定翼无人机集群协同搜索:自适应决策与避障Matlab复现
2026/10/19 5:03:51 网站建设 项目流程

收到这个复现需求时,我的第一反应不是“又是无人机集群”,而是先琢磨明白“异构”这两个字到底意味着什么。很多人看到“异构固定翼无人机集群协同搜索”这串名词,容易直接往编队飞行、覆盖路径规划那套去联想。但实际把代码跑起来之后,你会发现这个课题的难点根本不在“飞”,而在“怎么在飞行的过程中不断做决策”,以及“不同性能的飞机怎么被捏合成一个整体”。

这篇博文我就用一次完整的复现经历来拆解这个课题:任务边界怎么定、自适应决策和避障是怎么嵌进搜索框架的、Matlab代码的模块怎么划分最顺手,以及我在调参和Debug过程中踩过的那些坑。内容不追求复刻某一篇论文的公式,而是把这类方法背后的工程逻辑捋清楚,让拿到相似题目的人少走弯路。

1. 问题拆解:异构固定翼集群协同搜索到底在做什么

1.1 先搞清楚任务的边界条件

这类课题的场景通常是这样:一片事先不完全已知的区域内,可能分布着若干需要被发现的目标,比如救灾物资点、地面移动车辆、遇险信标。我们手里有几架固定翼无人机,它们的气动参数、速度范围、传感器性能各不相同,需要在尽量短的时间内把区域搜索覆盖完,同时报告目标位置,并避免撞上地形障碍、禁飞区或者彼此之间发生碰撞。

这看起来像“区域全覆盖路径规划”,但实际有本质区别。全覆盖路径规划的前提是“静态、已知、不存在强实时决策”,而这里强调的是“复杂环境”和“自适应决策”,意味着环境信息在飞行过程中是逐步获取的,目标出现概率是动态变化的。你需要根据当前观测到的信息,实时调整搜索重点,而不是机械地按着蛇形航线飞。

从工程上看,这个任务可以拆成三个相互耦合的子问题:

  1. 区域搜索问题:怎么在概率意义上让“找到目标”的期望收益最大化,也就是搜索图的构建与更新。
  2. 多机协同问题:多架性能不同的飞机,怎么分工才不重复、不冲突,也就是任务分配和协调机制。
  3. 安全避障问题:固定翼不能悬停、转弯半径受限,遇到障碍物怎么在不违背运动学约束的前提下安全绕开。

1.2 为什么固定翼比多旋翼难搞得多

我在复现之前一直做多旋翼的路径规划,转过来做固定翼集群那几天非常不适应。多旋翼最大的优势是可以原地悬停、原地转向,路径规划时基本可以把它当成一个质点,或者加上简单的曲率约束就行。固定翼则完全不同,它有最小速度限制,速度太低会失速;有最小转弯半径约束,航线不能出现锐角急转;甚至在实际飞行中还要考虑爬升率和下滑率。

这些运动学约束叠加到搜索任务上,会产生一个很直接的影响:搜索路径不能随意规划,转折必须是平滑的曲线。这意味着避障模块的输出不能只是一串离散航路点,而得是一段满足曲率连续性的轨迹。论文里的“异构”二字,恰恰就是在这个地方显出分量——快飞机和慢飞机的最小转弯半径不同,同样的障碍物场景下,它们能走的绕行弧线完全不同。

我在复现时用了很朴素的处理方式:把每架飞机当成一个带转弯半径约束的Dubins模型,不同的飞机有不同的最小转弯半径、巡航速度和感知半径。这样做的好处是,仿真模型既不复杂到难以调试,又能把“异构”的核心特征保留下来。如果一上来就上六自由度气动模型,反而会被姿态控制细节淹没,研究重点就跑偏了。

2. 系统架构:把论文的抽象概念映射成可落地的工程模块

2.1 分层式结构是最稳妥的选择

这个课题的难点之一在于,搜索、决策、避障、协同这些功能之间互相影响。我开始时想写一个“大而全”的主循环,把所有逻辑塞在一起,结果代码耦合度极高,改一个阈值就要全局排查,调试效率非常低。

后来我参考了常见的无人机任务管理系统,把整个架构拆成了四层:

  • 任务规划层:负责维护搜索区域的目标概率图,计算每架飞机当前位置下的搜索收益,给出“下一步飞向哪里”的宏观指令。
  • 协同决策层:负责多机之间的任务分配,解决“这片区域归谁管”“这个高价值目标由谁去确认”这类问题。
  • 航迹规划层:接收决策层的目标点,在满足固定翼运动学约束的前提下生成可飞航迹,并叠加避障逻辑。
  • 底层控制层:在仿真中就是一个简单的制导律模型,跟踪航迹规划层给出的参考航向和速度。

这样分层之后,每一个层次都可以独立测试。比如我先单独验证概率图更新逻辑在单机场景下是否收敛,再加入协同分配,最后才叠加避障。整个过程逻辑清晰得多。

2.2 异构性在代码里如何具体建模

“异构”这个词在论文里很抽象,但在代码里必须落到具体参数上。我采用了这样一个数据结构来定义每架无人机:

uav = struct(... 'id', 1, ... 'type', 'fast', ... % 类型:fast / slow / scout 'pos', [x0, y0, z0], ... % 初始位置 'speed', 25, ... % 巡航速度 m/s 'minTurnRadius', 120, ... % 最小转弯半径 m 'sensorRange', 300, ... % 传感器探测半径 m 'sensorFov', deg2rad(60), ...% 传感器视场角 'searchMap', [], ... % 局部搜索图(可选) 'taskQueue', []); % 待执行任务队列

类型不同的飞机,主要体现在speed、minTurnRadius和sensorRange三个参数上。比如“fast”型飞机速度快、转弯半径大、传感器探测距离远,适合大范围快速侦察;“slow”型飞机速度低、转弯半径小,适合在重点区域精细搜索。

这么建模有一个很直接的好处:任务分配算法不需要知道飞机内部具体是什么型号,只需要读取几个公开参数就能做决策。这其实就是面向接口编程的思路,后续把模型换成更精细的动力学模型时,上层逻辑不用大改。

3. 自适应决策的核心:搜索图、收益函数和实时更新

3.1 搜索图与贝叶斯更新

这个课题里所谓的“自适应决策”,最底层的支撑是“目标概率图”。可以把整片搜索区域划分成网格,每个格子维护一个数值,代表“目标在这个格子里的存在概率”。初始时我们可以根据先验信息设置不同的初始概率,比如某些区域有情报提示、概率高一些,其余区域均匀分布。

飞机每飞过一个地方,传感器就会产生一次“探测结果”:可能发现目标,也可能没有发现。这时候要用贝叶斯公式更新对应格子的概率。用Matlab写这个更新过程其实很简单,核心就是:

% p_prior: 先验概率 % p_detect: 目标存在时传感器检测到目标的概率 % p_false: 目标不存在时传感器误报的概率 % z: 本次观测结果,1表示检测到目标,0表示未检测到 if z == 1 p_post = (p_detect * p_prior) / ... (p_detect * p_prior + p_false * (1 - p_prior)); else p_post = ((1 - p_detect) * p_prior) / ... ((1 - p_detect) * p_prior + (1 - p_false) * (1 - p_prior)); end

这个更新看起来简单,但实际使用中有一个非常关键的细节:p_false不能设成0。如果认为传感器的虚警概率是0,那么一次“未检测到”就会把格子概率直接打到极低,导致后续即使有目标也不会被检索到。我一开始把虚警概率设成0.001,搜索后期几乎所有区域概率都趋近于0,整个地图就“死”掉了,后来改成0.1才正常。

除了存在概率,我建议还要维护一张“截止时间图”。每个格子记录最后一次被探测到的时刻,如果一个地方很久没被飞过,它的不确定度应该随时间上升。这个机制能有效防止无人机被“旧信息”误导,是自适应决策里很重要的一环。

3.2 收益函数:把“去哪搜索”变成一个优化问题

自适应决策的本质,就是每一时刻回答一个问题:“这架飞机现在应该往哪里飞,才能让整体搜索收益最大化?”

这个问题可以用收益函数来量化。我当时用的收益函数包含三个项:

  1. 信息增益项:飞机移动到某个候选点后,预计能降低多少区域不确定度。通常用熵减或者概率图的方差变化来度量。
  2. 搜索价值项:候选点附近的累计目标概率越高,越值得去。
  3. 代价惩罚项:包括转弯代价、飞行距离代价、以及与其它飞机任务重叠的代价。

于是决策变成了一个带约束的优化问题:在所有满足运动学约束的候选航向中,选一个综合收益最大的方向。

这里我想补充一个“自适应”特别重要的体现:这几项的权重不是固定的,而是随着搜索进程动态变化。比如搜索初期,整个地图都还没覆盖,信息增益项的权重应该大一些,让飞机尽量扩展覆盖区域;到了搜索后期,大多数区域概率已经很低了,只剩下几个高概率热点,这时候搜索价值项和确认代价的权重就要提高。

具体到代码实现,我是这样处理的:每架飞机每隔一段时间生成一批候选航向(比如每隔15度一个候选方向),对每个候选方向模拟生成一截圆弧轨迹,然后计算这条轨迹覆盖的网格对应的总收益,选收益最大的方向作为目标方向。这样做虽然有点暴力枚举的味道,但在仿真规模不大的情况下,计算效率完全够用,而且逻辑直观,方便调整权重。

3.3 多机协同中的任务分配策略

多机协同在异构场景下比同构要复杂得多。同构集群可以简单地把区域平均分区,但异构集群不能这么做,因为不同飞机的搜索效率、转弯性能和传感器覆盖范围不同,硬性分区必然导致“快飞机闲置、慢飞机累死”或者“大转弯半径飞机进不了狭窄区域”的问题。

我在复现时采用的是“市场拍卖机制”的思路。简单来说,每当有新的搜索热点出现,或者某架飞机完成了当前任务,就会触发一次任务分配:

  1. 所有飞机评估自己对每个待分配任务的“执行代价”,代价可以包括距离、转弯难度、预计完成时间等。
  2. 每架飞机对任务进行“投标”,投标值越低代表越适合执行这个任务。
  3. 由一个协调节点(或分布式共识机制)决定哪个任务分配给哪架飞机。

在Matlab中,我用一个共享的任务列表来实现协调节点,每个任务包含位置、优先级、登记时间。每架飞机在完成当前任务后,查询任务列表,选择投标值最小的任务接下。这样做的好处是架构简单,不需要复杂的通信协议模拟,就能体现出异构集群的分工逻辑。

实际调的时候有一个体会:绝对不能只按距离分配任务。由于异构飞机的转弯半径不同,路程近的飞机可能因为转弯约束,实际到达时间反而更晚。所以在投标值里,我把“基于Dubins路径的预估飞行时间”作为主要指标,而不是欧氏距离。这一点对复现效果影响很大,建议一开始就把这层考虑进去。

4. 避障实现:固定翼约束下的路径生成与重规划

4.1 障碍物建模与安全约束

避障模块要回答的问题很具体:已知前方有障碍物,飞机怎么绕过去,同时不掉高度、不超速、不违反转弯约束。

复现论文时,我把障碍物分成两类:

  • 静态障碍物:位置固定的山体、高楼、禁飞区,可预先建模成圆形或多边形。
  • 动态障碍物:其它无人机、移动的天气系统、突然出现的未知威胁,位置和速度不固定。

静态障碍物的处理相对简单,可以在任务开始前预计算一张“可行区域图”。动态障碍物则必须实时探测、实时绕行,这是“复杂环境”下避障的核心难点。

我采用的避障策略是“全局规划+局部重规划”的组合。全局层面,任务规划层给出的目标点通常是可达的,但路径未必安全。局部层面,飞行过程中如果探测到新障碍物,就在当前状态和障碍物边界约束下,重新生成一段局部Dubins航迹,绕过障碍后再回到原目标方向。

4.2 绕行路径生成的实现细节

固定翼避障最忌讳的就是“先规划一串航路点,再用直线连接”。直线连接的航迹在转弯点是不可飞的,因为固定翼必须有连续的曲率。所以我用Dubins路径算法来生成最小转弯半径约束下的最短可行路径。

Dubins路径的核心结论是:在二维平面上,给定起点和终点的位置及朝向,满足最小转弯半径约束的路径,可以由“直线段”和“圆弧段”组合而成,最优解一定属于六种组合之一:右转-直线-右转、右转-直线-左转、左转-直线-左转、左转-直线-右转,以及两种纯圆弧组合。

在Matlab里自己实现Dubins路径并不困难,核心步骤是:

  1. 输入起点位置和朝向角,终点位置和朝向角。
  2. 根据飞机的转弯半径,构造两组“转弯圆”圆心。
  3. 枚举六种路径组合,计算每种路径的总长度。
  4. 选择总长度最短且在安全区域内的一条路径。
% Dubins路径长度计算伪代码示意 function [pathLen, waypoints] = dubinsPath(start, startYaw, goal, goalYaw, turnRadius) [startCircles] = makeCircles(start, startYaw, turnRadius); [goalCircles] = makeCircles(goal, goalYaw, turnRadius); bestLen = Inf; for i = 1:2 for j = 1:2 % 计算切线和弧线组合 [len, waypoints] = computeTangent(startCircles(i,:), goalCircles(j,:), startYaw, goalYaw, turnRadius); if len < bestLen && isSafe(waypoints) bestLen = len; bestPath = waypoints; end end end end

这个算法在无人机集群规模不大时性能足够。但有一点要特别提醒:Dubins路径在遇到密集障碍区时可能没有可行解。比如起点和目标点之间有一大片障碍,绕过去的总长度超过了飞机剩余航程。这时候我选择的做法是:降低该任务的目标优先级,重新分配给其它更合适的飞机。这也算是任务分配与避障联动的一个体现。

4.3 动态障碍与多机互撞问题

多机互撞是集群搜索里避不开的问题。我一开始天真地认为,只要各机之间有最小安全距离约束,就不会出事。结果仿真跑了几个回合就发现,两架飞机同时飞向同一个高概率热点,很容易在目标点附近汇聚碰撞。

这个问题本质上是“协同避碰”,和单纯“环境避障”不同。环境避障只要绕开固定障碍就行,协同避碰则需要考虑对方未来的轨迹意图。我当时用的是一种偏保守但有效的策略:

  1. 预判冲突:基于当前速度和航向,估算未来若干秒内两架飞机的最短距离。
  2. 优先级排序:如果预测距离小于安全阈值,则按照“优先级”决定谁先通过。
  3. 局部调整:低优先级飞机在其可用航向集合中,选择一个能拉开距离的方向作为临时绕行方向。

这个策略在Matlab仿真中非常有效,唯一的问题是会增加一些航程开销。实际测试下来,在目标点密集的场景中,航程开销大约增加10%~15%,但换来的是零碰撞事故,完全值得。

5. 复现过程的工程细节与代码结构记录

5.1 总控主循环的设计

代码的整体结构,我建议按“主循环+模块化函数”的方式组织。我最终的项目目录大致是这样:

simulation/ ├── main.m % 主入口:初始化参数并启动循环 ├── config/ │ ├── loadScenario.m % 加载场景参数:地图、障碍物、目标 │ └── loadFleet.m % 加载无人机集群配置 ├── core/ │ ├── searchMapUpdate.m % 搜索概率图更新 │ ├── decisionMaking.m % 自适应决策:候选航向收益计算 │ ├── taskAllocation.m % 协同任务分配 │ └── dubinsPath.m % Dubins路径生成 ├── avoidance/ │ ├── obstacleDetect.m % 障碍物检测与参数提取 │ ├── localReplan.m % 局部避障重规划 │ └── collisionAvoid.m % 多机协同避碰 ├── sim/ │ ├── dynamicsUpdate.m % 固定翼运动学模型更新 │ ├── sensorSim.m % 传感器模拟 │ └── plotResults.m % 结果可视化 └── tools/ ├── gridMapTools.m % 栅格地图工具 └── metrics.m % 搜索覆盖率、发现时间等指标

主循环的逻辑非常单纯,每帧执行以下操作:

  1. 检查所有飞机是否到达目标航路点,若到达则触发任务分配;
  2. 对每架飞机调用决策模块,计算下一个目标方向;
  3. 用避障模块检测并修正航向,避开障碍和它机;
  4. 用运动学模型更新飞机位置;
  5. 调用传感器模拟,读取附近网格的“真实目标”,更新搜索图并统计发现记录。

这个循环看起来像教材里的“感知-规划-执行”闭环,但真正写起来,难点全在模块之间的数据传递上。我建议从一开始就定义好数据结构,用结构体或者类把飞机、地图、任务都封装清楚,避免用一堆全局变量串联。全局变量在小型demo里很方便,一旦加避障、加协同分配,代码很快就变得没法看。

5.2 搜索图与真实环境的双轨数据结构

复现这类项目,最忌讳的就是把“真实环境”和“无人机的认知环境”混为一谈。

真实环境是仿真器里的“真相”,目标位置是预先埋好的,障碍物坐标是固定的。而每架无人机内部维护的搜索图是它的“认知”,只包含它探测过的信息。我一开始为了省事,直接让决策模块读真实环境的目标位置,导致无人机跟开了挂一样直奔目标,整个搜索算法的意义全部消失了。

正确的做法是建立双轨数据结构:

% 真实环境(仿真器持有) trueMap.targetPos = [x1, y1; x2, y2; ...]; trueMap.obstacles = [cx, cy, radius; ...]; % 无人机认知(每架飞机各自持有) beliefMap.probGrid = zeros(gridRows, gridCols); beliefMap.lastSeenTime = zeros(gridRows, gridCols); beliefMap.visitedCount = zeros(gridRows, gridCols);

所有决策模块只能读取beliefMap中的数据,只有传感器模拟模块可以同时访问trueMap和beliefMap,负责把真实目标的探测结果转化为概率图更新。这样做的逻辑就干净了,算法是否有效、信息融合是否充分,也都可以通过对比“认知”和“真相”来评估。

5.3 结果统计与可视化的实操经验

论文复现最后总得有性能指标来佐证“这个方法有效”。我在实验中主要统计了三个指标:

  1. 区域覆盖率:被至少探测过一次的网格比例,随时间的变化曲线。固定翼集群的覆盖率曲线通常会呈现“快速上升-平台期-二次上升”的形态,平台期往往对应着多机从广域搜索转向热点确认的过程。
  2. 目标发现时间:从仿真开始到每个目标被首次发现的时间。这个指标最能体现自适应决策的价值——高效的方法应该优先发现高概率区域里的目标。
  3. 平均航程与碰撞次数:总航程反映搜索效率,碰撞次数反映安全性。

可视化方面,我最常用的是一张动态图:底图是概率图的热力图,上面叠加每架飞机的航迹、传感器覆盖范围、当前目标方向箭头,以及障碍物边界。在Matlab中用imagesc显示概率图,用line和quiver绘制航迹和航向,动画效果已经足够好。这张图在debug阶段帮了大忙——很多逻辑错误一眼就能从热力图分布看出来。

6. 调参、踩坑与复盘总结

6.1 参数敏感性分析:哪些参数最值得先调

这个课题的参数非常多,盲目调参等于大海捞针。我把参数按“影响程度”分成三档:

第一档:直接影响算法成败。包括目标存在概率的初始值、检测概率和虚警概率、收益函数三项的权重、安全距离阈值。这几个参数决定了无人机是在“认真搜索”还是在“到处乱逛”。

第二档:影响搜索效率。包括航向候选角度的间隔、搜索图网格分辨率、任务分配的投标权重。这些参数调好了能显著缩短搜索完成时间,调差了可能让飞机绕远路。

第三档:影响仿真真实性。包括固定翼的转弯半径、巡航速度、传感器视场角和探测距离。这些参数应该从实际飞行数据中获取,不要为了“仿真好看”而随意调整。

我在调参时最常犯的错误是把收益函数里的代价惩罚项权重设得太大,导致飞机宁愿在原地转圈,也不愿意飞向尚未覆盖的区域。后来我发现,最好的调参手段是观察覆盖率曲线:如果曲线前一段斜率太低,说明信息增益项的权重太小,飞机没有积极探索新区域;如果曲线后段长时间停滞,说明搜索价值项的权重不足,飞机没有集中精力确认高概率区域。

6.2 复现中几个容易踩的坑

有些坑我是花了整整几天才爬出来的,写在这里希望大家能避开。

第一个坑:坐标统一。搜索图、无人机位置、障碍物坐标如果用了不同的坐标系(比如有的用经纬度,有的用米制平面坐标,有的用栅格索引),非常容易出问题。我在一开始把所有坐标统一到“米制平面坐标+栅格索引间的映射函数”,才彻底消灭了这类Bug。建议在项目初始化时就用一个gridToWorld和worldToGrid函数做转换,不要到处写转换公式。

第二个坑:时间步长与运动学更新不一致。固定翼模型的速度通常很高,如果仿真步长太大,飞机每一步会跨过好几个网格,导致搜索图大量漏更新;步长太小,计算量又暴增。我的经验是,步长选择要保证飞机单步移动距离不超过一个网格尺寸的1/3,这样才能保证概率图更新的连续性。

第三个坑:任务分配与物理可行性脱节。任务分配算法给出的目标点,如果只考虑欧氏距离而不考虑转弯半径,经常会出现“明明该往东飞,最后却为了转弯先往西飞一大圈”的滑稽局面。加入Dubins路径长度作为投标指标后,这个问题立刻缓解。

第四个坑:随机数种子不固定。仿真里涉及目标概率图的随机初始化、传感器噪声、初始位置微扰,如果不在main.m开头固定随机数种子,每次实验的结果都不一样,调参时根本无法判断是“参数改好了”还是“运气变好了”。rng(2025)这样一行代码,能省掉非常多无意义的重复实验。

6.3 从复现到改进:一些可扩展的方向

如果你跑通了这套基础框架,还想继续深入,我有几个亲测有价值的方向推荐:

第一,把任务分配的“集中式协调节点”替换成“分布式一致性协议”。这样更贴近真实集群场景,也能让系统在部分通信断链时仍能继续运行。Matlab里可以用消息传递的简化模拟来做,难度不算高,但效果立马不一样。

第二,给传感器模型加噪声和延迟。真实传感器不是“探测到就绝对准确”,而是有一定概率检测、一定概率漏检,甚至通信延迟会导致信息更新滞后。加入这些噪声后,算法的鲁棒性才能真正体现出来。

第三,把Dubins路径替换成带高度维度的三维航线,加入地形跟随和爬升率约束。固定翼在真实环境中几乎不会停留在同一高度,三维航迹规划更贴合实际。

我个人在实际操作中的体会是,这类“复现论文”的项目,最大的价值不是把别人的代码跑通,而是在跑通的过程中理解每一个设计决策背后的权衡。比如为什么要用概率图而不是确定性覆盖?因为现实世界里传感器的观测天然带不确定性;为什么要用Dubins路径而不是普通直线路径?因为固定翼的物理特性决定了转弯必须平滑;为什么要做异构,而不是把所有飞机设成一样的参数?因为真实任务里不同型号的飞机各有专长,协同配合才能发挥最大效率。

如果你正好也在复现类似课题,建议先从单机搜索开始,确认概率图更新、决策逻辑都正确了,再扩展到多机协同,最后再加避障。每一步都做扎实,整体效果自然会出来。遇到仿真结果不理想的时候,不要急着改公式,先画图、看轨迹、看热力图,大部分问题在视觉上都是藏不住的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询