ARTICLE DETAIL

资讯详情

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

无蜂窝大规模MIMO与无人机通信:DQN资源调度实战

无蜂窝大规模MIMO与无人机通信:DQN资源调度实战 简介这份文档面向无线通信、6G与无人机网络方向的研究生及科研人员聚焦无蜂窝大规模MIMO场景下偏远地区覆盖不足的问题系统讲解如何用深度强化学习完成无人机辅助通信与资源调度。内容围绕两跳协作机制展开第1跳将AP功率分配与无人机服务区选择建模为双动作马尔可夫决策过程采用基于CNN的DQN求解第2跳将用户调度转化为0-1优化问题并分解求解。文中还涉及有限状态马尔可夫信道、DDPG三维轨迹与频带分配、多智能体强化学习动态资源分配等知识点并给出可达速率与总吞吐量的优化思路。资源包为1个docx文档约409KB结构完整、公式与系统模型清晰适合作为论文写作、算法复现与课题选题的参考材料。目前已有226人学习下载。1. 无蜂窝大规模MIMO遇上无人机为什么单靠传统优化解不动了地面基站被高楼遮挡、热点区域容量告急、应急场景下临时通信需求暴涨——这些场景下把无人机当成空中基站来辅助通信已经是业界比较认可的思路。但真正动手做的人很快会发现一个问题当无人机在空中移动、地面又是无蜂窝大规模MIMOCell-Free Massive MIMO架构时资源调度这件事变得极其难解。无蜂窝架构的特点是大量接入点AP通过回传链路连到中央处理单元共同服务少量用户用户不再绑定某个“小区”干扰结构跟传统蜂窝完全不同。再叠加无人机的三维移动信道随时间快速变化优化变量包括无人机轨迹、功率分配、AP选择、波束成形等维度高、耦合强、非凸传统凸优化或启发式方法要么假设太强要么求解时间根本跟不上信道变化。深度强化学习DRL在这里的价值是把序贯决策问题建模成马尔可夫决策过程让智能体在与环境交互中学习调度策略而不依赖精确的凸模型。DQN作为其中最早成熟的一类算法适合动作空间离散的场景比如“选哪个AP”“用哪个功率档位”“往哪个方向飞”。这篇笔记就围绕这个标题把无蜂窝大规模MIMO下无人机辅助通信与资源调度的完整落地路径拆开先讲清楚问题建模和选型理由再给可复现的仿真环境和DQN实现最后把踩过的坑和调参经验摊开。适合已经了解强化学习基础、想把这个方向做成论文或原型系统的读者。2. 把通信调度写成MDP状态、动作、奖励怎么定才不翻车2.1 无蜂窝大规模MIMO的信道模型与速率计算动手写代码前必须先把速率公式落到可计算的表达式。无蜂窝架构下所有AP通过回传连接到CPU用户k的下行可达速率通常写成R_k B * log2(1 SINR_k)其中SINR_k由有用信号功率除以干扰加噪声得到。在无蜂窝大规模MIMO中常用做法是采用最大比合并MRC或最小均方误差MMSE接收AP与用户之间的信道包含大尺度衰落和小尺度衰落。大尺度衰落用三段式路径损耗模型小尺度用瑞利或莱斯分布。无人机在空中时还要区分视距LoS和非视距NLoS概率通常用仰角相关的概率模型P_LoS 1 / (1 a * exp(-b * (theta - a)))theta是无人机到用户的仰角。这个概率直接影响路径损耗进而影响SINR。很多论文在这里偷懒直接假设全LoS结果仿真出来的调度策略在实际场景中根本不能用。我一般会在仿真里显式实现LoS/NLoS概率加权虽然计算量增加但策略的鲁棒性会好很多。下面是一个简化的信道增益计算代码用于后续DQN环境import numpy as np def channel_gain(ap_pos, uav_pos, user_pos, fc2e9, bw20e6): 计算AP-用户和UAV-用户链路的信道增益 ap_pos: (num_ap, 3) uav_pos: (3,) user_pos: (num_user, 3) fc: 载波频率 bw: 带宽 返回: 信道增益矩阵 (num_ap1, num_user) c 3e8 lambda_ c / fc num_ap ap_pos.shape[0] num_user user_pos.shape[0] # AP到用户的距离和仰角 d_au np.linalg.norm(ap_pos[:, None, :] - user_pos[None, :, :], axis2) # 无人机到用户的距离和仰角 d_uu np.linalg.norm(uav_pos[None, :] - user_pos, axis1) h_uav uav_pos[2] theta np.degrees(np.arcsin(h_uav / np.maximum(d_uu, 1e-3))) # LoS概率 a, b 9.61, 0.16 p_los 1 / (1 a * np.exp(-b * (theta - a))) # 路径损耗 (简化三段式) def pathloss(d, p_los_val): pl_los 20 * np.log10(4 * np.pi * fc / c) 20 * np.log10(np.maximum(d, 1)) pl_nlos pl_los 20 # NLoS额外损耗 return p_los_val * pl_los (1 - p_los_val) * pl_nlos pl_au pathloss(d_au, 1.0) # AP通常有固定LoS条件简化处理 pl_uu pathloss(d_uu, p_los) # 小尺度衰落 (瑞利) small_scale_au (np.random.randn(num_ap, num_user) 1j*np.random.randn(num_ap, num_user)) / np.sqrt(2) small_scale_uu (np.random.randn(num_user) 1j*np.random.randn(num_user)) / np.sqrt(2) # 信道增益 (含天线增益简化) gain_au np.abs(small_scale_au)**2 * 10**(-pl_au/10) gain_uu np.abs(small_scale_uu)**2 * 10**(-pl_uu/10) return np.vstack([gain_au, gain_uu[None, :]])这段代码里p_los用仰角计算pathloss函数把LoS和NLoS按概率加权。参数a9.61, b0.16来自常见的城市环境实测拟合不同场景要调整。fc2e9是载波频率bw20e6是带宽。实际写环境时这个函数每个时隙调用一次返回的信道增益用于计算SINR和速率。2.2 状态空间设计别把原始信道直接塞进DQNDQN的输入维度直接决定训练难度。最朴素的做法是把所有AP到所有用户的信道增益拉平成一个向量作为状态但无蜂窝大规模MIMO的AP数量通常几十甚至上百用户数几十状态维度轻松上千DQN根本训不动。我一般会做特征压缩状态里只保留每个用户当前的平均SINRnum_user维无人机当前位置和速度6维每个AP的剩余回传容量num_ap维可归一化上一时隙各用户的速率num_user维这样状态维度控制在几十到一百左右。如果AP数量特别大还可以用AP分簇后的簇头信息代替全部AP。注意状态里不要放原始复数信道DQN处理复数需要拆成实部虚部维度翻倍且信息冗余。用速率、SINR这类标量特征更稳。2.3 动作空间与奖励函数离散化是DQN的前提DQN只能处理离散动作所以必须把连续变量离散化。常见的动作设计有两种第一种是联合动作无人机移动方向比如8个方向 功率档位比如3档 AP选择比如从K个簇里选1个。动作总数8×3×KK4时是96个动作DQN还能接受。第二种是分解动作用多个DQN分别输出移动、功率、AP选择但这样会破坏动作之间的关联性收敛更慢。我建议新手先用联合动作动作数控制在200以内。奖励函数直接决定策略学出来有没有用。最直接的是用系统和速率reward sum(R_k) - penaltypenalty包括无人机飞出边界、碰撞、功率超限、用户速率低于门限的惩罚。注意奖励要归一化否则DQN的Q值会爆炸。我一般把速率除以一个参考速率比如100Mbps让奖励在0到1之间。def compute_reward(rate, uav_pos, power, user_rates, min_rate1e6): rate: 各用户速率 (num_user,) uav_pos: 无人机位置 power: 发射功率 user_rates: 历史速率用于公平性 sum_rate np.sum(rate) / 1e8 # 归一化 # 公平性惩罚 fairness_penalty np.std(rate) / (np.mean(rate) 1e-6) * 0.1 # 边界惩罚 boundary_penalty 0 if uav_pos[0] 0 or uav_pos[0] 1000 or uav_pos[1] 0 or uav_pos[1] 1000: boundary_penalty 1.0 # 低速率惩罚 low_rate_penalty np.sum(rate min_rate) * 0.5 # 功率惩罚 power_penalty 0.01 * power / 10 # 假设最大10W reward sum_rate - fairness_penalty - boundary_penalty - low_rate_penalty - power_penalty return reward奖励里的fairness_penalty用速率标准差除以均值鼓励调度器不要只服务信道好的用户。boundary_penalty防止无人机飞出区域。low_rate_penalty对低于门限的用户施加惩罚。这些系数需要根据仿真结果微调没有万能值。3. 用DQN跑通训练从环境封装到收敛判断3.1 仿真环境搭建Gym接口与无蜂窝速率计算为了让DQN代码能直接复用建议把环境封装成Gym风格实现reset()和step(action)。下面是一个最小可用的环境骨架import gym from gym import spaces import numpy as np class CellFreeUAVEnv(gym.Env): def __init__(self, num_ap16, num_user8, area1000): super().__init__() self.num_ap num_ap self.num_user num_user self.area area # 动作: 8方向 3功率档 4个AP簇 96 self.action_space spaces.Discrete(8 * 3 * 4) # 状态: 用户SINR(8) 无人机位置速度(6) AP容量(16) 上一时隙速率(8) self.obs_dim num_user 6 num_ap num_user self.observation_space spaces.Box(low-np.inf, highnp.inf, shape(self.obs_dim,), dtypenp.float32) self.ap_pos np.random.rand(num_ap, 3) * area self.ap_pos[:, 2] 30 # AP高度30m self.user_pos np.random.rand(num_user, 3) * area self.user_pos[:, 2] 1.7 self.uav_pos np.array([area/2, area/2, 100.0]) self.uav_vel np.zeros(3) self.prev_rate np.zeros(num_user) self.step_count 0 def reset(self): self.uav_pos np.array([self.area/2, self.area/2, 100.0]) self.uav_vel np.zeros(3) self.prev_rate np.zeros(self.num_user) self.step_count 0 return self._get_obs() def _get_obs(self): # 简化: 用距离反比近似SINR d_uu np.linalg.norm(self.uav_pos[None, :] - self.user_pos, axis1) sinr_approx 1e6 / (d_uu**2 1e-6) sinr_approx sinr_approx / (np.max(sinr_approx) 1e-6) ap_cap np.ones(self.num_ap) * 0.8 # 简化固定值 obs np.concatenate([sinr_approx, self.uav_pos, self.uav_vel, ap_cap, self.prev_rate]) return obs.astype(np.float32) def step(self, action): # 解析动作 move_idx action // 12 power_idx (action % 12) // 4 ap_cluster action % 4 # 移动 angle move_idx * np.pi / 4 speed 20.0 # m/s self.uav_vel np.array([speed * np.cos(angle), speed * np.sin(angle), 0]) self.uav_pos self.uav_vel * 1.0 # 1秒时隙 # 功率档位 power [0.1, 1.0, 5.0][power_idx] # 计算速率 (简化) d_uu np.linalg.norm(self.uav_pos[None, :] - self.user_pos, axis1) rate 20e6 * np.log2(1 power * 1e6 / (d_uu**2 1e-6)) rate np.clip(rate, 0, 1e8) reward compute_reward(rate, self.uav_pos, power, self.prev_rate) self.prev_rate rate / 1e8 self.step_count 1 done self.step_count 200 return self._get_obs(), reward, done, {}这个环境里_get_obs用距离反比近似SINR实际项目中应该替换成2.1节的完整信道计算。step里动作解析成移动方向、功率、AP簇速率计算也是简化版。重点是接口结构状态是固定长度向量动作是离散整数奖励是标量。DQN代码可以不改动直接对接。3.2 DQN网络结构与关键超参数DQN的核心是用神经网络近似Q值函数。对于这个场景输入维度约38输出维度96两层隐藏层各256个神经元就够了。下面是用PyTorch实现的DQNimport torch import torch.nn as nn import torch.optim as optim import random from collections import deque class QNetwork(nn.Module): def __init__(self, obs_dim, action_dim, hidden256): super().__init__() self.net nn.Sequential( nn.Linear(obs_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, action_dim) ) def forward(self, x): return self.net(x) class DQNAgent: def __init__(self, obs_dim, action_dim, lr1e-3, gamma0.95, epsilon_start1.0, epsilon_end0.05, epsilon_decay5000, buffer_size100000, batch_size64, target_update200): self.action_dim action_dim self.gamma gamma self.epsilon epsilon_start self.epsilon_end epsilon_end self.epsilon_decay epsilon_decay self.batch_size batch_size self.target_update target_update self.step_count 0 self.q_net QNetwork(obs_dim, action_dim) self.target_net QNetwork(obs_dim, action_dim) self.target_net.load_state_dict(self.q_net.state_dict()) self.optimizer optim.Adam(self.q_net.parameters(), lrlr) self.buffer deque(maxlenbuffer_size) def select_action(self, obs): self.epsilon self.epsilon_end (1.0 - self.epsilon_end) * \ np.exp(-self.step_count / self.epsilon_decay) self.step_count 1 if random.random() self.epsilon: return random.randrange(self.action_dim) with torch.no_grad(): obs_t torch.FloatTensor(obs).unsqueeze(0) q_values self.q_net(obs_t) return q_values.argmax().item() def store(self, obs, action, reward, next_obs, done): self.buffer.append((obs, action, reward, next_obs, done)) def train(self): if len(self.buffer) self.batch_size: return batch random.sample(self.buffer, self.batch_size) obs, actions, rewards, next_obs, dones zip(*batch) obs torch.FloatTensor(np.array(obs)) actions torch.LongTensor(actions).unsqueeze(1) rewards torch.FloatTensor(rewards).unsqueeze(1) next_obs torch.FloatTensor(np.array(next_obs)) dones torch.FloatTensor(dones).unsqueeze(1) q_values self.q_net(obs).gather(1, actions) with torch.no_grad(): next_q self.target_net(next_obs).max(1, keepdimTrue)[0] target rewards self.gamma * next_q * (1 - dones) loss nn.MSELoss()(q_values, target) self.optimizer.zero_grad() loss.backward() # 梯度裁剪防止Q值爆炸 nn.utils.clip_grad_norm_(self.q_net.parameters(), 10.0) self.optimizer.step() if self.step_count % self.target_update 0: self.target_net.load_state_dict(self.q_net.state_dict())关键参数说明gamma0.95是折扣因子通信调度场景下不宜太高否则智能体会过度关注远期速率而忽略当前约束。epsilon_decay5000控制探索衰减速度太小会导致探索不足太大会收敛慢。target_update200是目标网络更新频率太小训练不稳定太大收敛慢。buffer_size100000经验回放池太小会遗忘早期经验太大则训练慢。梯度裁剪clip_grad_norm_是必须的DQN在奖励尺度没调好时Q值容易爆炸。3.3 训练循环与收敛判断看什么曲线训练循环把环境和智能体串起来env CellFreeUAVEnv() agent DQNAgent(env.obs_dim, env.action_space.n) episode_rewards [] for episode in range(2000): obs env.reset() total_reward 0 done False while not done: action agent.select_action(obs) next_obs, reward, done, _ env.step(action) agent.store(obs, action, reward, next_obs, done) agent.train() obs next_obs total_reward reward episode_rewards.append(total_reward) if episode % 50 0: avg np.mean(episode_rewards[-50:]) print(fEpisode {episode}, Avg Reward: {avg:.4f}, Epsilon: {agent.epsilon:.3f})判断收敛不要只看奖励曲线。我一般同时看三个指标每50回合平均奖励是否稳定上升并趋于平台epsilon是否降到0.1以下Q值均值是否在合理范围比如0到10之间。如果奖励震荡剧烈先检查奖励归一化如果Q值持续增大检查gamma和奖励尺度如果策略始终不移动无人机检查动作空间里移动动作的奖励差异是否被其他惩罚淹没。4. 避坑与排查训练不收敛、策略学废的5个血泪教训4.1 现象奖励曲线剧烈震荡Q值爆炸原因奖励没有归一化速率量级在1e8DQN的Q值直接冲到1e10以上梯度爆炸。解决奖励必须除以参考值让单步奖励在0到1之间。同时加梯度裁剪clip_grad_norm_设为10。如果还震荡把学习率从1e-3降到1e-4。4.2 现象无人机原地不动或反复撞边界原因移动动作的奖励差异太小被边界惩罚或功率惩罚淹没。解决检查奖励函数里各项系数的量级。边界惩罚设为1.0而速率归一化后可能只有0.5智能体宁愿不动也不冒险。我一般把边界惩罚降到0.3同时给移动动作加一个小的探索奖励鼓励智能体尝试不同方向。4.3 现象训练初期奖励上升后期突然崩掉原因经验回放池里早期低质量经验太多或者目标网络更新频率太低导致Q值过估计。解决把target_update从200降到100或者用Double DQN。Double DQN的改动很小把目标Q值计算改成用当前网络选动作、目标网络算值with torch.no_grad(): next_actions self.q_net(next_obs).argmax(1, keepdimTrue) next_q self.target_net(next_obs).gather(1, next_actions) target rewards self.gamma * next_q * (1 - dones)这个改动几乎不增加计算量但能明显缓解Q值过估计。4.4 现象策略只服务信道最好的用户其他用户速率极低原因奖励函数只用了和速率没有公平性约束。解决在奖励里加公平性惩罚用速率标准差除以均值系数从0.1开始调。如果公平性还是差改用比例公平奖励sum(log(rate))这个函数天然鼓励公平。4.5 现象仿真里表现很好换一组用户位置就废了原因状态里没有包含用户位置信息或者训练时用户位置固定。解决每次reset()时随机化用户位置状态里加入用户相对无人机的方位角或距离。如果状态维度不允许至少加入用户距离的统计量均值、方差。另外训练时要用多个随机种子确保策略泛化。5. 进阶技巧用优先经验回放和动作掩码把收敛速度提上去5.1 优先经验回放让DQN多学“难样本”普通经验回放均匀采样但通信调度里有些状态比如用户速率骤降、无人机接近边界的样本更有学习价值。优先经验回放PER按TD误差给样本加权TD误差大的样本被采样概率高。实现上把deque换成带优先级的缓冲区采样时按概率抽。核心代码class PrioritizedBuffer: def __init__(self, capacity, alpha0.6): self.capacity capacity self.alpha alpha self.buffer [] self.priorities np.zeros(capacity, dtypenp.float32) self.pos 0 def store(self, transition, td_error): max_priority self.priorities.max() if self.buffer else 1.0 if len(self.buffer) self.capacity: self.buffer.append(transition) else: self.buffer[self.pos] transition self.priorities[self.pos] max_priority self.pos (self.pos 1) % self.capacity def sample(self, batch_size, beta0.4): if len(self.buffer) self.capacity: priorities self.priorities else: priorities self.priorities[:len(self.buffer)] probs priorities ** self.alpha probs / probs.sum() indices np.random.choice(len(self.buffer), batch_size, pprobs) samples [self.buffer[i] for i in indices] # 重要性采样权重 total len(self.buffer) weights (total * probs[indices]) ** (-beta) weights / weights.max() return samples, indices, weightsalpha0.6控制优先级程度0是均匀采样1是完全按优先级。beta0.4到1.0线性增长用于修正偏差。PER能让收敛速度提升30%到50%但实现复杂度增加建议先跑通普通DQN再加。5.2 动作掩码把非法动作直接屏蔽通信调度里有些动作是物理上不可行的比如无人机已经到边界还往外飞、功率超过硬件上限。与其用惩罚让智能体慢慢学不如在动作选择时直接屏蔽非法动作。实现上在select_action里加一个掩码def select_action(self, obs, action_maskNone): if random.random() self.epsilon: if action_mask is not None: valid_actions np.where(action_mask 1)[0] return np.random.choice(valid_actions) return random.randrange(self.action_dim) with torch.no_grad(): q_values self.q_net(torch.FloatTensor(obs).unsqueeze(0)) if action_mask is not None: q_values[0, action_mask 0] -1e9 return q_values.argmax().item()action_mask是一个长度等于动作数的0/1向量1表示合法。这个技巧在无人机边界控制和功率约束上特别有效能减少大量无效探索。5.3 验证方法用固定测试集对比基线训练完之后不要只看训练奖励。我一般会固定10组用户位置和信道实现分别跑DQN策略、随机策略、贪心策略选瞬时速率最大的AP和功率对比平均和速率和公平性。如果DQN比贪心策略只高5%以内说明策略没学到东西大概率是状态设计有问题。如果DQN在测试集上波动很大检查训练时用户位置是否随机化。最后把训练好的模型在不同用户数比如8、12、16下测试看泛化能力。这些验证做完才能判断这个方案值不值得继续投入。我自己的习惯是每次改奖励函数或状态设计先跑500回合看趋势不要一上来就训几千回合。DQN调参是个耐心活状态和奖励设计对了收敛是自然而然的事。希望帮到你。本文还有配套的精品资源点击获取
返回列表