☰
无人机基站轨迹优化:动态规划与深度强化学习结合实战
2026/10/1 13:33:35 网站建设 项目流程

简介:面向无人机基站轨迹优化的Python深度强化学习与动态规划源码,聚焦蜂窝网络下多无人机协作部署场景,适合通信与AI领域研究者、算法工程师及研究生用于算法验证与二次开发。包体共77个文件,压缩包仅157KB,其中38个py脚本构成核心算法实现,涵盖DQN、MADQN、MAQL等强化学习模型,24个txt与4个md文件提供说明和文档,ipynb笔记本便于交互式调试,mat文件则存放实验数据。目前已有140人浏览学习,体量精简但结构清晰,按算法模块组织目录。下载后可快速理解深度强化学习与动态规划结合求解轨迹优化的完整流程,包括多代理协同决策、阶段最优路径计算及蜂窝网络吞吐量提升策略,既可用于课程设计、毕业设计,也可作为论文实验的基准实现与改进起点。

1. 无人机基站轨迹优化:动态规划打底,深度强化学习上线,Python 源码怎么组织才不翻车

动态规划(DP)在静态环境里往往比深度强化学习(DRL)先出结果、后出问题——这不夸张。处理无人机基站轨迹优化时,我见过太多人一上来就堆 PPO,结果训练十几个小时,覆盖率还不如随便绕圈飞。其实合理的工程顺序是:先用动态规划算出离线参考轨迹,解决「理论上限在哪」,再用深度强化学习去做在线自适应决策,解决「环境变了怎么补」。本文把这条链路拆开,落在 Python 源码层面:状态怎么建模、DP 怎么递推、DRL 怎么训练、参数怎么调、坑在哪些,适合正在复现论文、做通信物理层预研、或者拿无人机基站做毕业设计的人。核心结论先放这:DP 是后悔药也是标尺,DRL 是上线方案,两者结合而不是二选一。

2. 把无人机基站轨迹问题拆成状态、动作与奖励:建模决定后面所有代码的复杂度

2.1 为什么不能直接对着覆盖率函数求导

无人机基站的轨迹优化,目标函数通常是用户覆盖率、信号质量和能耗的加权组合。很多第一次接触这个方向的人会想:直接把目标函数写成位置的函数,用梯度上升不就行了?问题是用户位置随时间变化,信道又随无人机高度、遮挡、天线倾角变化,目标函数不是光滑的——更麻烦的是无人机有速度、加速度约束,不能瞬移到任意位置。

所以常见做法是先把时间离散成时隙,把空间离散成网格。每个时隙内无人机位置近似静止,用户位置由移动模型给出,这样轨迹优化就变成了「在每个时隙选一个网格点」的序列决策问题。这个离散化水平直接决定了后续 DP 和 DRL 的复杂度。我一般在仿真里用 1 秒时隙、10 米网格,真实场景如果要求更高,再加密到 5 米。

离散化之后,问题的数学形态就很清楚了:无人机基站是一个移动节点,它既要靠近用户热点区域,又不能飞得太频繁导致电量掉得太快。这个权衡本身就是典型的动态规划结构——当前时刻的决策会影响后续所有时刻的收益,而且无人机的位置转移具有马尔可夫性。这也是为什么标题把动态规划和深度强化学习并列:两者建模的是同一个序列决策问题,只是求解范式不同。

2.2 状态空间、动作空间与奖励函数的具体设计

状态要回答三个问题:无人机在哪、用户在哪、还剩多少电。用户位置如果是静态假设,那状态就是用户位置矩阵加无人机坐标;如果用户移动,状态还要加一个时隙序号(或者用户的历史轨迹窗口)。动作空间在离散网格下是「上下左右移动一到两格或悬停」,在连续场景下是「水平速度向量加高度」。

奖励函数是这个方向最容易被低估的部分。覆盖率本身是稀疏的——如果无人机位置稍微偏一点,用户就掉线,奖励就是 0,学起来很难。我在训练 DRL 时用的奖励是这样拆的:

  • 基础奖励:当前时隙被覆盖用户数占总用户数的比例
  • 距离惩罚:无人机与热点中心的距离倒数乘上一个小系数
  • 能耗惩罚:每一格移动的能耗系数,悬停最小
  • 碰撞惩罚:撞到禁飞区或飞出边界,直接给负的大奖励并结束回合

这里有一个容易被忽略的细节:奖励系数如果设置不当,DRL 会学到「不动」的最优策略,因为动一下就有能耗惩罚,而覆盖率提升的奖励还没体现。动态规划也有同样的毛病,只是表现形态不同——DP 会算出停在中心不动的轨迹,因为网格粒度太粗,移动带来的增益被离散误差吞掉了。这两类问题后面避坑章节会展开。

2.3 动态规划与深度强化学习的分工:离线算上限,在线做决策

把 DP 和 DRL 放在同一个项目里,职责必须分开。动态规划在状态空间已知、转移概率明确时,可以算出严格的离线最优轨迹;它的劣势是状态空间稍微一大,就直接爆炸,而且环境一变就要全部重算。深度强化学习走的是数据驱动路线,不依赖显式模型,用户移动规律变了也能在线调整。

我一般会把整套源码组织成三层:第一层是环境模块,负责生成用户分布、计算覆盖率、更新位置;第二层是 DP 求解器,做离线轨迹计算,输出一条参考轨迹和收益上界;第三层是 DRL 训练器,把环境、奖励、模型串起来跑。这样 DP 的轨迹可以直接作为 DRL 的初始化参考,也可以放在训练曲线旁边做对照——如果 DRL 收敛后的收益还低于 DP,说明训练有问题,不是算法上限不行。

这三层的依赖关系很清晰:环境被两者共用,DP 不需要训练,DRL 不需要提前知道用户转移概率。源码的组织方式可以直接按这个分目录,避免后面想调一个参数结果不知道改哪里的窘境。选型上,如果用户分布是静态的、设备数量在几百以内,DP 就够用;如果是动态场景、用户群体随时变化,必须上 DRL,但 DP 依然值得保留作诊断工具。

3. 动态规划求离线最优轨迹:值迭代到底在算什么,代码怎么落地

3.1 把轨迹问题改写成多阶段决策,值迭代公式推导

动态规划处理无人机基站轨迹,本质是把轨迹切成一串阶段性决策。设时隙总数是 (T),无人机的可能位置集合是 (S),每个时隙选择一个位置 (s_t),收益是即时覆盖率 (r_t) 减去移动代价 (c(s_{t-1}, s_t))。目标是在 (T) 个时隙内最大化累计收益。这就是一个有限阶段、确定性的动态规划问题,价值函数满足:

[ V_t(s) = \max_{s'} \left[ r_t(s') - c(s, s') + V_{t+1}(s') \right] ]

从最后一个时隙往前递推,(V_T(s)) 就是最后时隙的收益。这是标准的贝尔曼方程离线版本。状态转移是确定性的——只要决定了下一个位置 (s'),下一时刻的状态就唯一确定,不需要对随机性求期望。用户位置如果看成静态快照,这个假设完全成立;用户移动时,把每个时隙的用户位置矩阵当输入,也可以继续用。

这个公式写出来很简单,但工程上有很多细节。第一是移动代价函数怎么定义,不能只用欧氏距离,因为无人机基站有最低飞行速度和悬停能耗,距离为零不代表代价为零。第二是边界:无人机不能飞出服务区域,这个约束必须在取最大值时直接排除掉。第三是时隙数量 T 不能太大,否则收益矩阵的存储开销线性增长,后面有具体的复杂度分析。

3.2 动态规划求解 Python 实现:网格化无人机位置与用户覆盖矩阵

这里我给出一个最小可跑的 DP 实现。假设用户位置是静态的,无人机高度固定,我们只优化水平轨迹。先把服务区域切成 (N \times N) 的网格,每个网格中心是一个候选悬停点,用户位置预先映射到网格上。

import numpy as np from scipy.spatial.distance import cdist def solve_dp_trajectory(user_positions, grid, T, move_cost=1.0, radius=2): """ 用户位置静态场景下的无人机基站轨迹动态规划求解。 user_positions: 数组,每一行是 [x, y] grid: 数组,每一行是候选悬停点的 [x, y] T: 时隙总数 move_cost: 每移动一格的能耗折算系数 radius: 基站覆盖半径,单位与坐标一致 """ n_grid = len(grid) # 覆盖矩阵:悬停点 i 对用户 j 是否覆盖 dist_mat = cdist(grid, user_positions) covered = dist_mat <= radius # 每时隙收益矩阵:停在 grid[i] 时覆盖的用户数 reward_per_pos = covered.sum(axis=1).astype(float) # 移动代价矩阵:从点 i 到点 j 的代价 travel_dist = cdist(grid, grid) cost_mat = move_cost * travel_dist # 收益矩阵铺成 T x n_grid,T 个时隙每个位置收益相同(静态用户) rewards = np.tile(reward_per_pos, (T, 1)) # 从最后一个时隙往前递推 V = np.zeros((T, n_grid)) V[T-1] = rewards[T-1] for t in range(T-2, -1, -1): # V[t][i] = rewards[t][i] + max_j( -cost_mat[i][j] + V[t+1][j] ) future = V[t+1] - cost_mat.T # 形状 n_grid x n_grid,第 i 行是"从 i 去各 j 的未来收益" best_future = future.max(axis=1) V[t] = rewards[t] + best_future # 回溯最优轨迹 trajectory = np.zeros(T, dtype=int) trajectory[0] = int(np.argmax(V[0])) for t in range(1, T): prev = trajectory[t-1] candidate = V[t] - cost_mat[prev] # 从 prev 出发去各点的即时+未来价值 trajectory[t] = int(np.argmax(candidate)) return V, trajectory # 演示:5x5网格,15个用户,10个时隙 grid = np.array([[x, y] for x in range(5) for y in range(5)], dtype=float) users = np.random.default_rng(42).uniform(0, 4, size=(15, 2)) V, traj = solve_dp_trajectory(users, grid, T=10) print("每时隙悬停的网格点编号:", traj) print("首时隙各候选点的价值:", np.round(V[0], 2))

这段代码的核心就两行:future = V[t+1] - cost_mat.T和V[t] = rewards[t] + best_future。前者把「下一时隙的价值」与「从当前点到下一个点的代价」合并成一个矩阵,后者完成贝尔曼方程的最大值操作。反向递推结束后,从第一个时隙选价值最大的点开始,跟着最优转移关系一步步回溯,就能还原整条轨迹。

参数说明:move_cost是能耗惩罚折算系数,设得太小会让无人机频繁去追单个用户,设得太大则干脆停着不动。radius是覆盖半径,决定了收益矩阵的稀疏度——半径越大,奖励越平滑,DP 越容易收敛到有意义的轨迹,但这会掩盖真实覆盖问题。这个实现里用户是静态的,rewards所有时隙都一样;如果用户按已知轨迹移动,只需把rewards换成每个时隙重新计算的覆盖矩阵。

3.3 动态规划版本的复杂度与适用边界:什么时候 DP 就该下场了

复杂度分析:假设候选点 (N_g) 个、时隙 (T) 个,DP 递推每时隙要算 (N_g \times N_g) 的转移矩阵,总复杂度 (O(T \cdot N_g^2))。上面的例子只有 25 个候选点、10 个时隙,瞬间出结果。但把网格加细到 50×50,候选点变成 2500,单时隙要算 625 万项,10 个时隙就是 6250 万次浮点运算,Python 循环直接慢到没法看。这时有两个出路:一是把矩阵运算向量化,上面代码已经是一半向量化了;二是接受粗粒度约束——动态规划的价值本来就不是线上实时解,而是离线参考、算上限。

DP 什么时候应该退场?第一,用户位置实时变化,需要秒级重算轨迹;第二,网格细到状态数超过 (10^4) 量级;第三,问题是连续的、动作空间不能离散。这三个条件任何一个成立,就该让深度强化学习接手。DP 退场不意味着彻底删掉,把 DP 输出的收益当成一个基准值,DRL 训练出来的期望收益如果低于这个值,说明训练配置有问题。

4. 深度强化学习接手动态场景:PPO 训练流程与 Python 实现

4.1 为什么选 PPO 而不是 DQN:连续动作空间与策略稳定性

动态规划只能在离散候选点上工作,DRL 的价值在于可以处理连续动作空间——无人机可以飞向任意方向任意距离,而不是锁死在网格上。连续动作这个要求,直接淘汰了 DQN 这类基于值函数的经典方法。DQN 输出的是离散动作的 Q 值,连续化需要额外做动作离散化或连续 Q 学习,工程复杂度高了不少。PPO(近端策略优化)是 actor-critic 结构,actor 直接输出动作分布的均值和方差,天然支持连续动作。

PPO 的另一个优势是训练稳定性。TRPO 虽然理论上更漂亮,但实现复杂;PPO 用 clipped surrogate objective 把策略更新限制在一个可控范围内,不追求每次更新的理论最优,而是保证不崩。对无人机基站这种奖励函数权重大、噪声明显的场景,PPO 的可调参数更少、更容易收敛到合理的次优解。我见过不少项目用 DQN 做无人机轨迹,最后都因为动作空间设计得太大而收敛缓慢,而同样的环境换成 PPO 之后十几个小时能看出明显提升。

4.2 PPO 训练环境与模型的 Python 实现

PPO 的完整训练需要环境类、策略网络、价值网络、经验缓冲和更新循环。这里用最小实现说明关键结构,重点看环境封装和回报处理,而不是逐行重复稳定扩散那种大模型。

import numpy as np import torch import torch.nn as nn class UAVEnv: """无人机基站环境:连续动作,离散时隙""" def __init__(self, users, area_size=1000.0, max_speed=30.0, coverage_radius=200.0): self.users = users # 用户位置,shape (N, 2) self.area_size = area_size self.max_speed = max_speed # 每时隙最大移动距离 self.coverage_radius = coverage_radius self.uav_pos = np.array([area_size/2, area_size/2]) # 初始位置 self.t = 0 def step(self, action): """动作是连续的方向与速度,裁剪到无人机机动范围""" speed = np.clip(action[0], 0.0, self.max_speed) angle = action[1] # 弧度 new_pos = self.uav_pos + speed * np.array([np.cos(angle), np.sin(angle)]) # 边界约束 new_pos = np.clip(new_pos, 0.0, self.area_size) self.uav_pos = new_pos # 覆盖率 dist = np.linalg.norm(self.users - self.uav_pos, axis=1) coverage = float(np.mean(dist <= self.coverage_radius)) # 奖励:覆盖率为主,轻微惩罚高速飞行 reward = coverage - 0.01 * speed self.t += 1 done = (self.t >= 20) # 回合长度 obs = np.concatenate([self.uav_pos / self.area_size, np.mean(self.users, axis=0) / self.area_size, [self.t / 20]]) return obs.astype(np.float32), reward, done class ActorCritic(nn.Module): """PPO 网络:连续动作输出高斯分布的均值与标准差""" def __init__(self, obs_dim=5, act_dim=2, hidden=128): super().__init__() self.shared = nn.Sequential( nn.Linear(obs_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), ) self.actor_mean = nn.Linear(hidden, act_dim) self.actor_logstd = nn.Parameter(torch.zeros(act_dim)) self.critic = nn.Linear(hidden, 1) def forward(self, obs): x = self.shared(obs) mean = self.actor_mean(x) std = torch.exp(self.actor_logstd) return mean, std, self.critic(x)

环境类的设计直接复现了第 2 章的建模约定:状态包含无人机归一化坐标、用户质心归一化坐标和当前时隙进度;奖励是覆盖率减去一个小的速度惩罚项。max_speed是关键参数,它必须和仿真步长匹配——如果时隙是 1 秒,max_speed=30表示无人机每步最多飞 30 米,这符合小型四旋翼的常见机动能力。动作是 (速度, 角度),比直接输出 (dx, dy) 更自然,因为它天然屏蔽了超出最大速度的动作。

PPO 的训练循环需要多轮采样更新,这里略去完整实现,但几个重要参数必须提。clip_ratio通常取 0.2,太大容易破坏旧策略,太小更新太慢;gamma取 0.99,因为回合长度只有 20 步,折扣因子接近 1 可以让智能体关注全局而不是短视;lr从 3e-4 起步,如果 loss 剧烈震荡再降到 1e-4。

4.3 训练参数怎么调:学习率、熵权、batch 大小与回合长度

PPO 的调参比网络结构更影响结果。我一般从这样的起点开始:n_steps=2048(每轮采样步数)、n_epochs=10(每轮数据利用率)、clip_ratio=0.2、entropy_coef=0.01。无人机轨迹任务里,熵权太小会让策略过早确定性化,飞进一个局部区域不肯出来;熵权太大则永远在乱飞。一个有用的经验是:如果覆盖率曲线在中途长时间不动,先把熵权调大一倍。

batch 大小影响的是梯度质量。小 batch 更新频繁但噪声大,大 batch 稳定但慢。2048 步采出来大约 100 个回合(20 步一个回合),用 mini-batch 大小为 256 比较平衡。回合长度 T 决定了优化视野——T 太短,智能体只看眼前几步,不会为了以后的高覆盖率提前移动;T 太长,探索空间膨胀,训练变慢。20 步是一个经过很多实验验证的起点,适合仿真环境。

训练时的日志要同时记录四样东西:episode_return、coverage、policy_loss、entropy。如果entropy掉到接近 0 而coverage还没上去,说明策略太早锁死,需要调大entropy_coef;如果policy_loss一直在负方向不停下降,说明 clip 没起作用,检查clip_ratio是否被误设成了很大的值。

5. 动态规划与深度强化学习在轨迹任务中的避坑指南

5.1 现象:DP 算出来的轨迹停在区域中心不动,覆盖率却不低

原因:网格粒度太粗 + 用户分布相对均匀。无人机稍微移动一格,覆盖的用户数量变化很小,而移动要付出代价,DP 的理性选择就是不动。这是离散化的天然偏差,不是算法写错了。

解决:先把用户位置做聚类,比如用 K-Means 找出 3-5 个热点中心,再把网格生成逻辑改成「围绕热点加密」而不是全区域均匀铺点。或者把移动代价调小一个数量级再看轨迹形态,如果依然不动,再检查覆盖半径是否远大于网格粒度。

提示:DP 输出的轨迹要先用可视化画出来检查,不要直接拿数字指标判断对错。轨迹分布在图上是否合理,一眼就能看出来。

5.2 现象:PPO 训练了上万步,loss 在下降,覆盖率还是 0

原因:奖励稀疏 + 初始位置离所有用户太远。无人机一开始可能踩在区域边角,覆盖率是 0,随机探索又很难碰到用户覆盖范围,所有回合奖励都是 0,网络梯度没有方向。

解决:给奖励加上成型项(reward shaping),比如无人机与最近用户距离的负值,或者与用户质心距离的负值,让智能体即使在没覆盖到用户时也能学到「靠近」这个行为。我之前踩过这个坑,加了一个简单的距离奖励后,覆盖率直接从 0 跳到 40% 以上。

5.3 现象:DP 在细网格数据上跑得越来越慢,几分钟出不来结果

原因:候选网格点数量 (N_g) 太大,复杂度是 (O(T \cdot N_g^2))。50×50 的网格已经要跑 625 万次矩阵运算,Python 纯循环完全扛不住。

解决:按 3.3 节的复杂度分析判断必要性。如果只是要一个参考轨迹,网格 20×20 完全够用;如果要精确解,改用 C++ 或者 Numba 加速转移矩阵的 for 循环部分,把瓶颈行用@jit装饰器优化。日常仿真我会首选 20×20。

5.4 现象:仿真里飞得好,拿到真实环境数据上效果不对

原因:仿真环境没建用户移动模型、没建模信号遮挡或者没设禁飞区。真实场景里建筑物遮挡会让覆盖率断崖式下降,用户移动会让热点漂移,这些仿真里如果没建模,DRL 学到的策略就是「对着仿真环境过拟合」。

解决:至少留出 20% 的测试用户分布不参与训练,专门用来验证泛化能力;同时给奖励函数加一个「被遮挡用户不可覆盖」的信道模型简化版。真实飞行没法直接验证时,用仿真里 k 个不同用户分布的平均覆盖率作为泛化指标。

5.5 现象:DP 和 DRL 结果一样好,怀疑 DRL 白训练了

原因:环境过于简单,比如用户分布完全静态、覆盖半径远大于网格粒度,这种环境下 DP 已经接近最优,DRL 没有发挥空间。

解决:这不算 bug,而是个重要的项目决策信号。如果场景确实静态,直接上 DP 就行,省电省时间;如果场景动态,把用户移动速度和热点漂移周期设得更有挑战性,让 DRL 的优势体现出来。判断标准很简单:DP 重算一次轨迹如果超过几秒钟,那它就不适合在线决策,DRL 才有价值。

6. 用 DP 轨迹做课程学习:让 DRL 沿着离线最优路径起步再放开

一个值得试的进阶做法:把 DP 解出的离线轨迹当成 DRL 的课程学习(curriculum learning)参考。具体操作分三段:第一阶段,把无人机的初始位置固定在 DP 轨迹的起点,并在奖励函数里加一个「与 DP 参考位置距离」的小惩罚项,让智能体前期几乎沿着 DP 轨迹飞;第二阶段,把距离惩罚逐渐衰减到原来的 1/10,智能体开始在 DP 参考附近探索偏离;第三阶段,完全去掉参考项,看它是否能在动态变化的用户分布中维持或超过 DP 基准收益。

这个做法比从头随机初始化训练要稳得多,因为智能体前期的探索空间被有效压缩,先学会「沿着一条能覆盖用户的路径飞」,再学会「偏离路径去覆盖漂移的用户」。距离惩罚系数我一般设在基础奖励的 0.2 倍,太高会让智能体不敢偏离,太低等于没加。

验证方法也很直接:训练日志里同时画三条曲线——DP 固定收益线、课程学习 DRL 收益线、从头训练 DRL 收益线。如果课程学习曲线在中后期追不上 DP 线,说明参考轨迹本身有误导;如果追上了但动态场景增益没体现,说明动态建模不够强。这套对比我每次都会做,它比任何指标都能说明问题。做离线参考再上线自适应,已经是我处理这类轨迹优化问题的固定习惯:先让 DP 把数理结构摸透,再让 DRL 在真实环境里自由发挥,希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询