ns3-gym入门(二):linear-mesh例子详解

一、问题背景:Random Access

Controlling the random access in an IEEE 802.11 mesh network is challenging as the network nodes compete for the shared radio resources. It is known that assigning the same channel access probability to each node is not optimal [17] and therefore the literature proposed solutions where e.g. the channel access probability depends on the network load (queue size) of a node.

在IEEE 802.11网状网络中控制随机接入具有挑战性,因为网络节点会争夺共享的无线电资源。众所周知,为每个节点分配相同的信道访问概率并不是最优的,因此文献提出了一些解决方案,例如,信道访问概率取决于节点的网络负载(队列大小)
争用窗口(Contention Window,CW)是一个关键概念,它与介质访问控制(MAC)协议中的冲突避免机制密切相关。具体来说,CW用于控制设备在发送数据之前等待的随机退避时间,CWmin和CWmax分别表示竞争窗口的最小值和最大值,较小的CW值意味着节点可以更频繁地尝试发送数据包,但也可能导致更多的冲突;较大的CW值则相反。以下是对CW的详细解释:

  • 冲突避免:在WiFi网络中,多个设备可能同时尝试访问共享的无线信道。如果两个或多个设备同时发送数据,会导致数据包冲突。为了减少这种冲突,WiFi使用了一个称为载波侦听多路访问/冲突避免(CSMA/CA)的机制
  • 退避算法:CSMA/CA中的退避算法规定,当一个设备检测到信道正在被占用时,它不会立即重试发送数据,而是会等待一个随机的时间段。这个随机时间段由争用窗口(CW)决定
  • CW的范围:争用窗口的大小是一个动态变化的范围,通常表示为CW_min, CW_maxCWm​in,CWm​ax。当设备第一次尝试发送数据时,它会从0, CW_min0,CWm​in范围内随机选择一个时间槽(slot time)进行等待。如果再次发生冲突,CW的范围会加倍,直到达到CW_max。这种机制称为指数退避(Exponential Backoff)。

作为网络负载函数的控制信道访问概率值。我们在 ns-3 中创建了一个由五个节点组成的线性拓扑,并设置了从最左侧到最右侧节点的饱和 UDP 数据包流。

  • observation - queue lengths of each node 节点队列长度
  • actions - set channel access probability for each node; here we set both CWmin and CWMax to the same value, i.e. uniform backoff (window stays constant even when in case of packet collisions) 频道接入概率
  • reward - the number of packets received at the flow’s ultimate destination during last step interval 目的节点接收到的数据包数量
  • gameover - end of simulation time 仿真时间结束

二、关键步骤

①收集状态GetObservation:collect每个节点的队列长度存入box类型的状态空间

Ptr<OpenGymDataContainer>
MyGymEnv::GetObservation()
{NS_LOG_FUNCTION (this);uint32_t nodeNum = NodeList::GetNNodes ();std::vector<uint32_t> shape = {nodeNum,};Ptr<OpenGymBoxContainer<uint32_t> > box = CreateObject<OpenGymBoxContainer<uint32_t> >(shape);for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); ++i) {Ptr<Node> node = *i;Ptr<WifiMacQueue> queue = GetQueue (node);uint32_t value = queue->GetNPackets();box->AddValue(value);}NS_LOG_UNCOND ("MyGetObservation: " << box);return box;
}

收集节点队列长度的函数GetQueue:

Ptr<WifiMacQueue>
MyGymEnv::GetQueue(Ptr<Node> node)
{Ptr<NetDevice> dev = node->GetDevice (0);Ptr<WifiNetDevice> wifi_dev = DynamicCast<WifiNetDevice> (dev);Ptr<WifiMac> wifi_mac = wifi_dev->GetMac ();Ptr<RegularWifiMac> rmac = DynamicCast<RegularWifiMac> (wifi_mac);PointerValue ptr;rmac->GetAttribute ("Txop", ptr);Ptr<Txop> txop = ptr.Get<Txop> ();Ptr<WifiMacQueue> queue = txop->GetWifiMacQueue ();return queue;
}

②奖励:根据收包数确定

float
MyGymEnv::GetReward()
{NS_LOG_FUNCTION (this);static float lastValue = 0.0;float reward = m_rxPktNum - lastValue;lastValue = m_rxPktNum;NS_LOG_UNCOND ("MyGetReward: " << reward);return reward;
}

收包数的统计

void
MyGymEnv::NotifyPktRxEvent(Ptr<MyGymEnv> entity, Ptr<Node> node, Ptr<const Packet> packet)
{NS_LOG_DEBUG ("Client received a packet of " << packet->GetSize () << " bytes");entity->m_currentNode = node;entity->m_rxPktNum++;NS_LOG_UNCOND ("Node with ID " << entity->m_currentNode->GetId() << " received " << entity->m_rxPktNum << " packets");entity->Notify();
}void
MyGymEnv::CountRxPkts(Ptr<MyGymEnv> entity, Ptr<Node> node, Ptr<const Packet> packet)
{NS_LOG_DEBUG ("Client received a packet of " << packet->GetSize () << " bytes");entity->m_currentNode = node;entity->m_rxPktNum++;
}

③动作:调整CWmin和CWmax,改变随机接入概率

bool
MyGymEnv::ExecuteActions(Ptr<OpenGymDataContainer> action)
{NS_LOG_FUNCTION (this);NS_LOG_UNCOND ("MyExecuteActions: " << action);Ptr<OpenGymBoxContainer<uint32_t> > box = DynamicCast<OpenGymBoxContainer<uint32_t> >(action);std::vector<uint32_t> actionVector = box->GetData();uint32_t nodeNum = NodeList::GetNNodes ();for (uint32_t i=0; i<nodeNum; i++){Ptr<Node> node = NodeList::GetNode(i);uint32_t cwSize = actionVector.at(i);SetCw(node, cwSize, cwSize);}return true;
}

调整Cw的函数SetCw:

bool
MyGymEnv::SetCw(Ptr<Node> node, uint32_t cwMinValue, uint32_t cwMaxValue)
{Ptr<NetDevice> dev = node->GetDevice (0);Ptr<WifiNetDevice> wifi_dev = DynamicCast<WifiNetDevice> (dev);Ptr<WifiMac> wifi_mac = wifi_dev->GetMac ();Ptr<RegularWifiMac> rmac = DynamicCast<RegularWifiMac> (wifi_mac);PointerValue ptr;rmac->GetAttribute ("Txop", ptr);Ptr<Txop> txop = ptr.Get<Txop> ();// if both set to the same value then we have uniform backoff?if (cwMinValue != 0) {NS_LOG_DEBUG ("Set CW min: " << cwMinValue);txop->SetMinCw(cwMinValue);}if (cwMaxValue != 0) {NS_LOG_DEBUG ("Set CW max: " << cwMaxValue);txop->SetMaxCw(cwMaxValue);}return true;
}

④路由环境与ns3-gym接口的耦合
以上过程均来源于mygym.cc,而在sim.cc里面,先单独配置好正常的网络环境,然后写入接口,主要是Env的初始化、统计收包数目的函数回调

  // OpenGym EnvPtr<OpenGymInterface> openGymInterface = CreateObject<OpenGymInterface> (openGymPort);Ptr<MyGymEnv> myGymEnv;if (eventBasedEnv){myGymEnv = CreateObject<MyGymEnv> ();} else {myGymEnv = CreateObject<MyGymEnv> (Seconds(envStepTime));}myGymEnv->SetOpenGymInterface(openGymInterface);// connect OpenGym entity to event sourcePtr<UdpServer> udpServer = DynamicCast<UdpServer>(sinkApps.Get(0));if (eventBasedEnv){udpServer->TraceConnectWithoutContext ("Rx", MakeBoundCallback (&MyGymEnv::NotifyPktRxEvent, myGymEnv, dstNode));} else {udpServer->TraceConnectWithoutContext ("Rx", MakeBoundCallback (&MyGymEnv::CountRxPkts, myGymEnv, dstNode));}

⑤Agent的学习(dqn_agent_v1.py)

Our RL agent was able to learn to assign lower CWmin/CWMax values to nodes closer to the flow destination. Hence it was able to outperform the baseline where all nodes were assigned the same CWmin/CWMax.
我们的 RL 代理能够学习将较低的 CWmin/CWMax 值分配给更接近流目标的节点。因此,它的表现能够优于所有节点都被分配到相同的 CWmin/CWMax 的基线。

也就是说智能体可以逐渐学会如何根据节点队列长度来优化CWmin/CWmax值,通过分配较低的CW值,离目的地较近的节点能够更频繁地尝试发送数据包,从而减少数据传输的延迟和可能的中断,从而提高网络的整体性能。

首先定义DQN结构,这里用的是tensorflow框架

class DqnAgent(object):"""docstring for DqnAgent"""def __init__(self, inNum, outNum):super(DqnAgent, self).__init__()self.model = keras.Sequential()self.model.add(keras.layers.Dense(inNum, input_shape=(inNum,), activation='relu'))self.model.add(keras.layers.Dense(outNum, activation='softmax'))self.model.compile(optimizer=tf.train.AdamOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])def get_action(self, state):return np.argmax(self.model.predict(state)[0])def predict(self, next_state):return self.model.predict(next_state)[0]def fit(self, state, target, action):target_f = self.model.predict(state)target_f[0][action] = targetself.model.fit(state, target_f, epochs=1, verbose=0)

其次定义agent

agent0 = DqnAgent(inputQueues, cwSize)
agent1 = DqnAgent(inputQueues, cwSize)
agent2 = DqnAgent(inputQueues, cwSize)
agent3 = DqnAgent(inputQueues, cwSize)

定义agent如何选择动作,这里使用的是贪心策略

        # Choose actionif np.random.rand(1) < epsilon:action0 = np.random.randint(cwSize)action1 = np.random.randint(cwSize)action2 = np.random.randint(cwSize)action3 = np.random.randint(cwSize)else:action0 = agent0.get_action(state[:,0:2])action1 = agent1.get_action(state[:,1:3])action2 = agent2.get_action(state[:,2:4])action3 = agent3.get_action(state[:,3:5])

执行动作并得到反馈

        # StepactionVec = [action0, action1, action2, action3, 100]next_state, reward, done, _ = env.step(actionVec)

根据reward训练、更新,Q值根据贝尔曼方程计算

        # Traintarget0 = rewardtarget1 = rewardtarget2 = rewardtarget3 = rewardif not done:target0 = reward + 0.95 * np.amax(agent0.predict(next_state[:,0:2]))target1 = reward + 0.95 * np.amax(agent1.predict(next_state[:,1:3]))target2 = reward + 0.95 * np.amax(agent2.predict(next_state[:,2:4]))target3 = reward + 0.95 * np.amax(agent3.predict(next_state[:,3:5]))agent0.fit(state[:,0:2], target0, action0)agent1.fit(state[:,1:3], target1, action1)agent2.fit(state[:,2:4], target2, action2)agent3.fit(state[:,3:5], target3, action3)

更新状态、奖励、贪心因子

        state = next_staterewardsum += rewardif epsilon > epsilon_min: epsilon *= epsilon_decay

记录数据

    time_history.append(time)rew_history.append(rewardsum)

训练过程绘图

plt.plot(range(len(time_history)), time_history)
plt.plot(range(len(rew_history)), rew_history)
plt.xlabel('Episode')
plt.ylabel('Time')
plt.show()

三、关于接口的思考
①ns3->ns3gym
初始化环境就相当于把所有的回调函数调用了
执行动作、收集状态最好单独写个函数,注意传参要统一

②ns3gym->python
其实和最原始的那两个例子差不多,主要是添加了强化学习的核心代码
像这里使用的是简单的DQN,没有涉及到经验回放,包含两个全连接层(Dense layers),第一个隐藏层使用 ReLU 激活函数,输出层使用 Softmax 激活函数
输入节点队列长度,输出竞争窗口大小

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/46557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无人机之机架类型篇

碳纤维机架 具有低密度、高强度和高刚度的特点&#xff0c;非常适合商业或工业级无人机的设计。碳纤维机架在飞行过程中具有良好的减振效果&#xff0c;使飞行更加稳定&#xff0c;但制作工艺复杂&#xff0c;成本较高。 工程塑料机架 以其轻便、耐冲击和易加工等特点受到一…

Vue 3 组件通信与状态管理:从基础到Pinia的全面解析

引言 Vue 3引入了Composition API&#xff0c;这为组件通信提供了更多的灵活性和强大的功能。例如&#xff0c;使用provide和inject可以轻松实现跨层级组件的数据传递&#xff0c;而setup函数则为组件提供了更早的生命周期钩子&#xff0c;使得在组件初始化阶段就能进行通信设…

Chrome调试工具

一、Chrome调试工具 1.Ctrl滚轮 可以放大开发者工具代码大小。 2.左边是 HTML 元素结构&#xff0c;右边是 CSS 样式。 3.右边 CSS 样式可以改动数值&#xff08;左右箭头或者直接输入&#xff09;和查看颜色。 4.Ctrl 0 复原浏览器大小。 5.如果点击元素&#xff0c;发现右侧…

Netfilter之连接跟踪(Connection Tracking)和反向 SNAT(Reverse SNAT)

连接跟踪&#xff08;Connection Tracking&#xff09; 连接跟踪是 Netfilter 框架中的一个功能&#xff0c;用于跟踪网络连接的状态和元数据。它使防火墙能够识别和处理数据包属于哪个连接&#xff0c;并在双向通信中正确匹配请求和响应数据包。 工作原理 建立连接&#xf…

微服务节流控制:Eureka中服务速率限制的精妙配置

微服务节流控制&#xff1a;Eureka中服务速率限制的精妙配置 在微服务架构中&#xff0c;服务的可用性和稳定性对于整个系统的性能至关重要。Eureka作为Netflix开源的服务发现框架&#xff0c;虽然主要用于服务注册与发现&#xff0c;但合理地配置服务的速率限制也是确保服务健…

【C语言】深入解析选择排序

文章目录 什么是选择排序&#xff1f;选择排序的基本实现代码解释选择排序的优化选择排序的性能分析选择排序的实际应用结论 在C语言编程中&#xff0c;选择排序是一种简单且直观的排序算法。尽管它在处理大型数据集时效率不高&#xff0c;但由于其实现简单&#xff0c;常常用于…

基于协同过滤混合算法的餐饮推荐系统设计与实现

基于协同过滤混合算法的餐饮推荐系统设计与实现 Design and Implementation of Restaurant Recommendation System Based on Hybrid Collaborative Filtering Algorithm 完整下载链接:基于协同过滤混合算法的餐饮推荐系统设计与实现 文章目录 基于协同过滤混合算法的餐饮推荐…

牛客TOP101:反转链表

文章目录 1. 题目描述2. 解题思路3. 代码实现 1. 题目描述 2. 解题思路 简单粗暴的写法&#xff0c;就是从头到尾挨个将所有结点的指向翻转即可。需要注意的是&#xff0c;翻转之后会失去原有指向的结点&#xff0c;所以需要提前保存。   具体做法就是&#xff0c;使用cur标记…

面试必备!Redis面试题合集

目录 1.Redis简介 2. 分布式缓存常见的技术选型方案有哪些? 3. 说一下 Redis 和 Memcached 的区别和共同点 4. 缓存数据的处理流程是怎样的? 5. 为什么要用 Redis/为什么要用缓存? 6. Redis 常见数据结构以及使用场景分析 6.1. string 6.2. list 6.3. hash 6.4. s…

Spring源码注解篇二:手写@Component注解

Component注解的功能 在Spring框架中&#xff0c;Component 注解是一个核心特性&#xff0c;用于自动检测类并将其注册为Spring应用上下文中的Bean。这大大简化了Bean的配置过程&#xff0c;使得开发者能够通过注解的方式快速地将类标记为组件&#xff0c;并由Spring容器进行管…

硬件检测工具 | CPU-Z v2.10.0 官方中文绿色版

软件简介 CPU-Z是一款广受欢迎的硬件检测工具&#xff0c;主要用于收集电脑处理器的详细信息。这款软件能够提供关于CPU的详细数据&#xff0c;包括处理器名称、编号、代号、进程和缓存等信息。此外&#xff0c;CPU-Z还能实时监测每个内核的内部频率和内存频率&#xff0c;以及…

【洛谷B3644】【模板】拓扑排序 / 家谱树 解题报告

洛谷B3644 - 【模板】拓扑排序 / 家谱树 题目描述 有个人的家族很大&#xff0c;辈分关系很混乱&#xff0c;请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列&#xff0c;使得每个人的后辈都比那个人后列出。 输入格式 第 1 1 1 行一个整数 N N N&#xf…

Java字节流的输入输出

Java字节流的输入输出指的是使用Java中的字节流来进行数据的读取和写入操作。字节流是以字节为单位进行读写操作的&#xff0c;常用于处理二进制数据或者字符数据。 Java中常用的字节流类有InputStream和OutputStream。InputStream用于读取字节数据&#xff0c;OutputStream用…

【运维项目经历|037】MFS-Scale分布式对象存储系统部署与优化项目

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 ​ 🏅阿里云ACE认证高级工程师 ​ 🏅阿里云开发者社区专家博主 💊交流社区:CSDN云计算交流社区欢迎您的加入! 目录 项目名称 项目背景 项目目标 …

如何在项目中打印sql和执行的时间

目标&#xff1a;打印DAO方法中sql和执行的时间 一种方式是去实现Mybatis的拦截器Interceptor &#xff0c;比较麻烦&#xff1b; 这里介绍一种比较简单的实现方式&#xff1b; 1、如何打印sql&#xff1f; 配置文件加这个可以打印出com.zhenhui.ids.busi.watch包下执行的sq…

c++包管理器

conan conan search&#xff0c;查看网络库 conan profile detect&#xff0c;生成缓存信息conan new cmake_exe/cmake_lib&#xff0c;创建cmakelists.txtconan install .&#xff0c;执行Conanfile.txt中的配置&#xff0c;生成相关的bat文件 项目中配置Conanfile.txt(或者…

ImportError: DLL load failed while importing _swigfaiss: 找不到指定的模块。

ImportError: DLL load failed while importing _swigfaiss: 找不到指定的模块 这个错误通常是由于系统中缺少某些必要的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;或者由于与当前环境中的库版本不兼容导致的。以下是一些解决这个问题的步骤&#xff1a; 方法一&…

linux高级编程(sqlite数据库调用)

数据库 1、分类&#xff1a; 大型 中型 小型 ORACLE MYSQL/MSSQL SQLITE DBII powdb 关系型数据库 2、名词&#xff1a; DB 数据库 select update database DBMS 数据库管理系统 MIS 管理…

tessy 集成测试:小白入门指导手册

目录 1,创建集成测试模块且分析源文件 2,设置测试环境 3,TIE界面设置相关函数 4,SCE界面增加用例 5,编辑数据 6,用例所对应的测试函数序列 7,添加 work task 函数 8,为测试场景添加函数 9,为函数赋值 10,编辑时间序列的数值 11,执行用例 12,其他注意事项…

《昇思25天学习打卡营第2天|初学教程/快速入门》

文章目录 快速入门处理数据集网络构建模型训练保存模型加载模型 快速入门 通过MindSpore的API来快速实现一个简单的深度学习模型。 MindSpore 是华为推出的一个全场景深度学习框架&#xff0c;它旨在实现易开发、高效执行和全场景覆盖的目标。这个框架支持云、边缘以及端侧场…