ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

无人机基站轨迹优化:动态规划与深度强化学习结合实战

无人机基站轨迹优化:动态规划与深度强化学习结合实战 简介面向无人机基站轨迹优化的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_{t1}(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_cost1.0, radius2): 用户位置静态场景下的无人机基站轨迹动态规划求解。 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(axis1).astype(float) # 移动代价矩阵从点 i 到点 j 的代价 travel_dist cdist(grid, grid) cost_mat move_cost * travel_dist # 收益矩阵铺成 T x n_gridT 个时隙每个位置收益相同静态用户 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[t1][j] ) future V[t1] - cost_mat.T # 形状 n_grid x n_grid第 i 行是从 i 去各 j 的未来收益 best_future future.max(axis1) V[t] rewards[t] best_future # 回溯最优轨迹 trajectory np.zeros(T, dtypeint) 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)], dtypefloat) users np.random.default_rng(42).uniform(0, 4, size(15, 2)) V, traj solve_dp_trajectory(users, grid, T10) print(每时隙悬停的网格点编号, traj) print(首时隙各候选点的价值, np.round(V[0], 2))这段代码的核心就两行future V[t1] - 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_size1000.0, max_speed30.0, coverage_radius200.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, axis1) 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, axis0) / self.area_size, [self.t / 20]]) return obs.astype(np.float32), reward, done class ActorCritic(nn.Module): PPO 网络连续动作输出高斯分布的均值与标准差 def __init__(self, obs_dim5, act_dim2, hidden128): 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_speed30表示无人机每步最多飞 30 米这符合小型四旋翼的常见机动能力。动作是 (速度, 角度)比直接输出 (dx, dy) 更自然因为它天然屏蔽了超出最大速度的动作。PPO 的训练循环需要多轮采样更新这里略去完整实现但几个重要参数必须提。clip_ratio通常取 0.2太大容易破坏旧策略太小更新太慢gamma取 0.99因为回合长度只有 20 步折扣因子接近 1 可以让智能体关注全局而不是短视lr从 3e-4 起步如果 loss 剧烈震荡再降到 1e-4。4.3 训练参数怎么调学习率、熵权、batch 大小与回合长度PPO 的调参比网络结构更影响结果。我一般从这样的起点开始n_steps2048每轮采样步数、n_epochs10每轮数据利用率、clip_ratio0.2、entropy_coef0.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 在真实环境里自由发挥希望帮到你。本文还有配套的精品资源点击获取
返回列表