NS3Gym中的模拟脚本代码讲解
示例的模拟脚本sim.cc
与Agent运行脚本
位于同一个目录下,该脚本主要定义了在NS3
中如何定义环境以及如何实现其与Gym
通信的接口,以下是注释形式的代码讲解:
// 包含头文件,这些头文件包含在build目录下ns3的目录,在执行ns3脚本时,系统会自动进入这个目录,包含以下两个头文件
#include "ns3/core-module.h"
#include "ns3/opengym-module.h"
// 使用这个命名空间
using namespace ns3;
// 定义一个记录为Opengym
NS_LOG_COMPONENT_DEFINE ("OpenGym");
// 以下几步为定义自己的接口函数,在主函数会将这些函数的回调进行绑定
// 定义状态空间
Ptr<OpenGymSpace> MyGetObservationSpace(void)
{
uint32_t nodeNum = 5;
float low = 0.0;
float high = 10.0;
std::vector<uint32_t> shape = {nodeNum,};
std::string dtype = TypeNameGet<uint32_t> ();
Ptr<OpenGymBoxSpace> space = CreateObject<OpenGymBoxSpace> (low, high, shape, dtype);
NS_LOG_UNCOND ("MyGetObservationSpace: " << space);
return space;
}
// 定义动作空间
Ptr<OpenGymSpace> MyGetActionSpace(void)
{
uint32_t nodeNum = 5;
Ptr<OpenGymDiscreteSpace> space = CreateObject<OpenGymDiscreteSpace> (nodeNum);
NS_LOG_UNCOND ("MyGetActionSpace: " << space);
return space;
}
// 定义游戏结束条件
bool MyGetGameOver(void)
{
bool isGameOver = false;
bool test = false;
static float stepCounter = 0.0;
stepCounter += 1;
// 试验次数超过十次后游戏结束
if (stepCounter == 10 && test) {
isGameOver = true;
}
NS_LOG_UNCOND ("MyGetGameOver: " << isGameOver);
return isGameOver;
}
// 得到观测
Ptr<OpenGymDataContainer> MyGetObservation(void)
{
uint32_t nodeNum = 5;
uint32_t low = 0.0;
uint32_t high = 10.0;
// 随机变量
Ptr<UniformRandomVariable> rngInt = CreateObject<UniformRandomVariable> ();
std::vector<uint32_t> shape = {nodeNum,};
Ptr<OpenGymBoxContainer<uint32_t> > box = CreateObject<OpenGymBoxContainer<uint32_t> >(shape);
// 生成随机的观测数据
for (uint32_t i = 0; i<nodeNum; i++){
uint32_t value = rngInt->GetInteger(low, high);
box->AddValue(value);
}
NS_LOG_UNCOND ("MyGetObservation: " << box);
return box;
}
// 得到回报函数
float MyGetReward(void)
{
static float reward = 0.0;
reward += 1;
return reward;
}
// 得到额外的信息
std::string MyGetExtraInfo(void)
{
std::string myInfo = "testInfo";
myInfo += "|123";
NS_LOG_UNCOND("MyGetExtraInfo: " << myInfo);
return myInfo;
}
// 执行动作
bool MyExecuteActions(Ptr<OpenGymDataContainer> action)
{
// 其实就是做了个变量类型转化,并没有真正地执行动作
Ptr<OpenGymDiscreteContainer> discrete = DynamicCast<OpenGymDiscreteContainer>(action);
NS_LOG_UNCOND ("MyExecuteActions: " << action);
return true;
}
// 递归对自己进行调用
void ScheduleNextStateRead(double envStepTime, Ptr<OpenGymInterface> openGym)
{
Simulator::Schedule (Seconds(envStepTime), &ScheduleNextStateRead, envStepTime, openGym);
// 这个函数将当前的状态返回
openGym->NotifyCurrentState();
}
int
main (int argc, char *argv[])
{
// 随机函数种子
uint32_t simSeed = 1;
// 这里定义模拟的总时间,也就是10秒
double simulationTime = 0.5;
// 这里定义每一步执行的时间,为0.1秒
double envStepTime = 0.1;
uint32_t openGymPort = 5555;
uint32_t testArg = 0;
CommandLine cmd;
// 必选参数
cmd.AddValue ("openGymPort", "Port number for OpenGym env. Default: 5555", openGymPort);
cmd.AddValue ("simSeed", "Seed for random generator. Default: 1", simSeed);
// 可选参数
cmd.AddValue ("simTime", "Simulation time in seconds. Default: 10s", simulationTime);
cmd.AddValue ("testArg", "Extra simulation argument. Default: 0", testArg);
cmd.Parse (argc, argv);
NS_LOG_UNCOND("Ns3Env parameters:");
NS_LOG_UNCOND("--simulationTime: " << simulationTime);
NS_LOG_UNCOND("--openGymPort: " << openGymPort);
NS_LOG_UNCOND("--envStepTime: " << envStepTime);
NS_LOG_UNCOND("--seed: " << simSeed);
NS_LOG_UNCOND("--testArg: " << testArg);
// 随机数种子使用
RngSeedManager::SetSeed (1);
RngSeedManager::SetRun (simSeed);
// 定义接口类,并通过接口类挂载7个必须实现的函数
// 在创建对象时,需指明接口的端口号,默认为5555
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) );
// 在模拟时需指明接口类的对象地址
// 这是一个递归调用的过程,该函数每过envStepTime秒的时间被调用一次,从而不停地向gym端发送当前的数据
Simulator::Schedule (Seconds(0.0), &ScheduleNextStateRead, envStepTime, openGym);
NS_LOG_UNCOND ("Simulation start");
// 在执行simulationTime秒后停止
Simulator::Stop (Seconds (simulationTime));
Simulator::Run ();
NS_LOG_UNCOND ("Simulation stop");
// 同步执行,而非异步执行,执行结束后通知客户端该模拟已经结束
openGym->NotifySimulationEnd();
// 释放相应的空间
Simulator::Destroy ();
}