消息循环

消息循环位于main.cc的run函数里面,此处根据参数创建不同的Behavior子类。如比赛时用的NaoBehavior,参数优化时用的OptimizationBehaviorFixedKickOptimizationBehaviorWalkForward

下面是run函数里面的一个片段。从服务器读取了一个消息,然后使用刚刚创建的Behavior子类的Think进行解析和运算,并返回要发送给服务器的字符串数据。

while (gLoop)
    {
        GetMessage(msg);
        string msgToServer = behavior->Think(msg);
        // To support agent sync mode
        msgToServer.append("(syn)");
        PutMessage(msgToServer);
        if (mPort != -1) {
            PutMonMessage(behavior->getMonMessage());
        }
    }

后面的if语句内容是优化的时候用的,mPort变量由启动agent时的--port参数赋值。用于优化时向服务器发送一些控制指令。如将球移动到一个位置,控制比赛开始停止等。

Think函数

Think函数原型为:

virtual std::string Think(const std::string& message) = 0;

它位于Behavior类,是一个纯虚函数,需要子类实现。

Think函数的实现在NaoBehavior类里面。NaoBehavior继承Behavior,然后其他的优化类等是继承的NaoBehavior

Think函数参数是string,为服务器发送来的消息。返回值也是string,是发送给服务器的消息。

服务器发送过来的数据先进行了解码,获取了服务器发送过来的数据,然后进行计算和决策,之后发送给服务器。这个过程理论上是20ms发送一次。

Parser类

Parser类是解析服务器发送过来的数据的类,位于parser/parser.h内。它的作用是将服务器发送过来的字符串解析为数据。字符串具体的语法可以参考实验室的文档。 解析过程在parse函数中, Parser::parse 函数在Think函数中被调用。

composeAction函数

string NaoBehavior::composeAction()

NaoBehavior::composeAction()函数功能和Parser::parse是对应的,它将要发送的数据转为字符串发送给服务器。它位于servercomm/primitives.cc内。返回值就是发送给服务器的数据。

selectSkill函数

 SkillType NaoBehavior::selectSkill()

NaoBehavior::selectSkill()应该是大家最熟悉的函数,这里写的是关于机器人高级决策的地方。位于behaviors/strategy.cc里面。比赛的时候都在写这个函数。这里决定机器人是要走路还是要踢球。它的返回值是SkillType,这是一个枚举变量。每一个都代表了一种Skill(如踢球,走路,摔倒后的起身)。

act函数

void NaoBehavior::act() 

NaoBehavior::act()函数决定现在要做什么。这里通常处理更低级的动作,它调用了刚刚的selectSkill函数,获取一个SkillType,然后执行这个Skill。这里有一个摔倒判断,如果摔倒了,这里会自动调用起身的Skill。所以摔倒的时候是不会执行selectSkill函数的。

Skill

简介

刚刚的act函数通过调用selectSkill函数决定自己该执行哪个Skill。这里就来解释一下Skill是什么。

Skill就是机器人的技能,可以是摔倒之后的起身动作,踢球的动作,走路的动作。技能可以通过技能描述语言来定义。注:起身的动作在behaviors/checkfall.cc中,这个动作没有使用.skl文件描述。

skills文件夹里面的.skl文件里面的就是定义skl的文件。

至于怎么添加一个Skill就可以看我以前写的文档。

Skill文件的加载

Skill文件加载代码在NaoBehavior的构造函数里面,如下:

使用了readSkillsFromFile函数来从.skl文件载入Skill。

try {
        readSkillsFromFile( "./skills/stand.skl" );
        readSkillsFromFile( "./skills/kick.skl" );

        // ik skills
        readSkillsFromFile( "./skills/kick_ik_0.skl" );
        // end ik skills

    }
    catch( std::string& what ) {
        cerr << "Exception caught: " << what << endl;
        exit(1);
    }
    catch (std::exception& e)
    {
        cerr << e.what() << endl;
        exit(1);
    }

Parameter Files

简介

参数文件在paramfiles文件夹里面,参数和前面的.skl文件可以配套使用,也可以直接在代码里面读取参数。比如我们在写踢球动作时,我们不知道要把每个关节转动多少度,以多快的速度转动。我们估计的基本不可能是最好的。所以可以把这些变量写在参数文件里面。程序运行时加载。

调整得好的参数会比原来的参数有更好的效果。比如提球的参数,原始参数可以踢6m,而经过优化的参数可以踢15m。

参数文件的加载

参数文件是在agent运行的时候通过--paramsfilec参数来加载。而agent可以加载多个参数文件,不同的文件里面可以出现同一个参数。由下面的代码可以看出,后面载入的文件的变量会覆盖之前加载的文件里面的同名变量。

void ReadOptions(int argc, char* argv[])
{

    teamName = "UTAustinVilla_Base";
    uNum = 0; // Value of 0 means choose next available number

    for( int i = 0; i < argc; i++)
    {
        if ( strcmp( argv[i], "--help" ) == 0 )
        {
            PrintHelp();
            exit(0);
        }
        else if ( strncmp( argv[i], "--host", 6 ) == 0 )
        {
            string tmp=argv[i];

            if ( tmp.length() <= 7 ) // minimal sanity check
            {
                PrintHelp();
                exit(0);
            }
            gHost = tmp.substr(7);
        }
        else if ( strncmp( argv[i], "--mhost", 7 ) == 0 )
        {
            string tmp=argv[i];

            if ( tmp.length() <= 8 ) // minimal sanity check
            {
                PrintHelp();
                exit(0);
            }
            mHost = tmp.substr(8);
        }
        else if ( strncmp( argv[i], "--port", 6) == 0 ) {
            if (i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            gPort = atoi(argv[i+1]);
        }
        else if ( strncmp( argv[i], "--mport", 7) == 0 ) {
            if (i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            mPort = atoi(argv[i+1]);
        }
        else if(strcmp(argv[i], "--team") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }

            teamName = argv[i + 1];
        }
        else if(strcmp(argv[i], "--unum") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            uNum = atoi(argv[i + 1]);
        }
        else if(strcmp(argv[i], "--paramsfile") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            string inputsFile = argv[i+1];
            LoadParams(inputsFile);
        }
        else if (strcmp(argv[i], "--experimentout") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            outputFile = argv[i+1];
        }
        else if (strcmp(argv[i], "--optimize") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            agentType = argv[i+1];
        }
        else if (strcmp(argv[i], "--type") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            rsg = "rsg/agent/nao/nao_hetero.rsg " + string(argv[i+1]);
            agentBodyType = atoi(argv[i+1]);
        }
        else if (strcmp(argv[i], "--rsg") == 0) {
            if(i == argc - 1) {
                PrintHelp();
                exit(0);
            }
            rsg = argv[i+1];
        }
        else if (strcmp(argv[i], "--pkgoalie") == 0) {
            agentType = "pkgoalie";
        }
        else if (strcmp(argv[i], "--pkshooter") == 0) {
            agentType = "pkshooter";
        }
        else if (strcmp(argv[i], "--gazebo") == 0) {
            agentType = "gazebo";
        }
        else if (strcmp(argv[i], "--recordstats") == 0) {
            agentType = "recordstats";
        }
    } // for-loop
}

hjwblog.com