ns3-gym入门(一):代码结构和简单例子

ns3-gym真的好难学,网上可以参考的例子也太少了,如果有用这个做路由的麻烦联系我交流一下吧,太痛苦了

一、安装

之前的文章已经提到过了,这里不赘述了

二、运行简单的例子

用两个终端的方式运行感觉更加直观,但是仍然有些坑:

  1. Terminal 1:最好用sudo模式,可能是哪里的权限不够,反正我只用waf指令时好时坏,也是挺神奇的,这里实际上是在运行/contrib/opengym/examples/目录下,"opengym"文件夹里的"sim.cc",在后续更复杂的例子里,其实ns3环境是配置在"mygym.cc"里面的,最终通过头文件"mygym.h"被"sim.cc"调用
  2. Terminal 2:Ubuntu 20.04自带的protobuf版本相对低了一点,直接运行会报错,所以在运行时先设置环境变量PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python。这里是执行/contrib/opengym/examples/目录下,"opengym"文件夹对应的test.py
bash 复制代码
# Terminal 1
cd ~/ns-allinone-3.35/ns-3.35
sudo ./waf --run "opengym"

# Terminal 2
cd ~/ns-allinone-3.35/ns-3.35/contrib/opengym/examples/opengym/
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python ./test.py --start=0

三、例子代码详解

整体的代码结构图如下,摘自对应的论文。这里的接口主要用的是ZMQ Socket通信,另一个ns3-ai用的是共享内存的方法,据说会比ns3-gym快100倍,但是资料也不多,有时间再试一下

1.基本的接口

python端:

python 复制代码
import gym
import ns3gym
import MyAgent
from ns3gym import ns3env

#env = gym.make('ns3-v0')  <--- causes some errors with the new OpenAI Gym framework, please use ns3env.Ns3Env()
env = ns3env.Ns3Env()
obs = env.reset()
agent = MyAgent.Agent()

while True:
  action = agent.get_action(obs)
  obs, reward, done, info = env.step(action)

  if done:
    break
env.close()

ns3端:

cpp 复制代码
Ptr<OpenGymSpace> GetObservationSpace();
Ptr<OpenGymSpace> GetActionSpace();
Ptr<OpenGymDataContainer> GetObservation();
float GetReward();
bool GetGameOver();
std::string GetExtraInfo();
bool ExecuteActions(Ptr<OpenGymDataContainer> action);

2.Example1:"opengym"详解

这个需要sim.cc和test.py对照着来看,懒得打字了,我直接把关键的函数、属性、两个端口的交互画在纸上了:

首先初始化环境会有一个observation(step0),然后每一轮按照action-obs+reward+done+info的方式迭代(step++),迭代是否结束是sim.cc里面的done布尔值决定的。

obs收集到的状态是在(low, high)范围内生成的5个随机数,reward是每次迭代+1(我后来改成了reward=action),action是在离散空间下的[0,1,2,3,4],agent通过sample随机选择动作。

这里还需要对相应的数据类型有一些了解:

我稍微改动了一下代码,让每一轮迭代更清晰了一些。

python端:

ns3端:

PS. 这里和普通的gym环境不太一样:在sim.cc里定义的各个空间函数属性不能改,这个应该是定义在如下的接口回调函数里了,要实现什么功能在对应的函数内部改动就行

cpp 复制代码
  // OpenGym Env
  Ptr<OpenGymInterface> openGym = CreateObject<OpenGymInterface> (openGymPort);
  openGym->SetGetActionSpaceCb( MakeCallback (&MyGetActionSpace) );
  openGym->SetGetObservationSpaceCb( MakeCallback (&MyGetObservationSpace) );
  openGym->SetGetGameOverCb( MakeCallback (&MyGetGameOver) );
  openGym->SetGetObservationCb( MakeCallback (&MyGetObservation) );
  openGym->SetGetRewardCb( MakeCallback (&MyGetReward) );
  openGym->SetGetExtraInfoCb( MakeCallback (&MyGetExtraInfo) );
  openGym->SetExecuteActionsCb( MakeCallback (&MyExecuteActions) );
  Simulator::Schedule (Seconds(0.0), &ScheduleNextStateRead, envStepTime, openGym);

3.Example2:"opengym-2"详解

运行指令和之前一毛一样,只需要需修改代码路径,还是最好开两个终端(有时候ns3会突然发疯报错,我什么代码都没改它也报错。。。这个时候多半是终端卡住了,重开一个就行)

bash 复制代码
# Terminal 1
cd ~/ns-allinone-3.35/ns-3.35
sudo ./waf --run "opengym-2"

# Terminal 2
cd ~/ns-allinone-3.35/ns-3.35/contrib/opengym/examples/opengym-2/
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python ./test.py --start=0

这个比较特别的是把ns3仿真环境单独写成了mygym.cc,再在sim.cc里面通过头文件mygym.h进行引用,其实具体的obs\reward\action定义都和"opengym"差不多,只是用到了更丰富的数据类型。后面如果要用到比较复杂的ns3环境的话,估计也会这样写更方便调试。

sim.cc(不再具体创建环境了,只是定义一些宏观的参数):

cpp 复制代码
  // OpenGym Env
  Ptr<OpenGymInterface> openGymInterface = CreateObject<OpenGymInterface> (openGymPort);
  Ptr<MyGymEnv> myGymEnv = CreateObject<MyGymEnv> (Seconds(envStepTime));
  myGymEnv->SetOpenGymInterface(openGymInterface);

mygym.h(创建一个自定义的 Gym 环境类,该类继承自OprnGymEnv):

cpp 复制代码
class MyGymEnv : public OpenGymEnv
{
public:
  MyGymEnv ();
  MyGymEnv (Time stepTime);
  virtual ~MyGymEnv ();
  static TypeId GetTypeId (void);
  virtual void DoDispose ();

  Ptr<OpenGymSpace> GetActionSpace();
  Ptr<OpenGymSpace> GetObservationSpace();
  bool GetGameOver();
  Ptr<OpenGymDataContainer> GetObservation();
  float GetReward();
  std::string GetExtraInfo();
  bool ExecuteActions(Ptr<OpenGymDataContainer> action);

private:
  void ScheduleNextStateRead();

  Time m_interval;
};

mygym.cc(对应mygym.h分别进行定义,这里只把和"opengym/sim.cc"不一样的地方贴出来了,后面依然是对各个要素空间的定义,用到了tuplr\dict两种数据类型):

cpp 复制代码
MyGymEnv::MyGymEnv ()
{
  NS_LOG_FUNCTION (this);
  m_interval = Seconds(0.1);

  Simulator::Schedule (Seconds(0.0), &MyGymEnv::ScheduleNextStateRead, this);
}

MyGymEnv::MyGymEnv (Time stepTime)
{
  NS_LOG_FUNCTION (this);
  m_interval = stepTime;

  Simulator::Schedule (Seconds(0.0), &MyGymEnv::ScheduleNextStateRead, this);
}

void
MyGymEnv::ScheduleNextStateRead ()
{
  NS_LOG_FUNCTION (this);
  Simulator::Schedule (m_interval, &MyGymEnv::ScheduleNextStateRead, this);
  Notify();
}

MyGymEnv::~MyGymEnv ()
{
  NS_LOG_FUNCTION (this);
}

TypeId
MyGymEnv::GetTypeId (void)
{
  static TypeId tid = TypeId ("MyGymEnv")
    .SetParent<OpenGymEnv> ()
    .SetGroupName ("OpenGym")
    .AddConstructor<MyGymEnv> ()
  ;
  return tid;
}

void
MyGymEnv::DoDispose ()
{
  NS_LOG_FUNCTION (this);
}

这两个简单的例子其实重在展示基础的交互过程和丰富的数据类型,并没有涉及到强化学习的内核,比如说reward如何基于state(obs)和action定义,action又是怎么作用于state的,要与具体的网络环境耦合还需要更深层次的探索

相关推荐
小狮子安度因13 分钟前
边缘智能-大模型架构初探
linux·网络
晨春计16 分钟前
【git】
android·linux·git
weixin_4866811425 分钟前
C++系列-STL容器中统计算法count, count_if
开发语言·c++·算法
基德爆肝c语言26 分钟前
C++入门
开发语言·c++
Flying_Fish_roe29 分钟前
linux-软件包管理-包管理工具(RedHat/CentOS 系)
linux·运维·centos
Splashtop高性能远程控制软件31 分钟前
centos远程桌面连接windows
linux·windows·centos·远程控制·远程桌面
怀九日32 分钟前
C++(学习)2024.9.18
开发语言·c++·学习·面向对象·引用·
一道秘制的小菜33 分钟前
C++第七节课 运算符重载
服务器·开发语言·c++·学习·算法
tang&1 小时前
【Linux】进程概念
linux
苏少朋1 小时前
Docker安装 ▎Docker详细讲解 ▎数据卷挂载 ▎Nginx安装理解
linux·nginx·docker·容器