本文介绍SRC系统中的策略框架。

整体结构

structure

整个结构是分层的,策略层大概可以这样分层:

  1. 顶层是PlayBook,负责根据场上情况来选择合适的战术。其中使用了贝叶斯方法(recursive Bayesian estimation) 来评估场上情况;
  2. 第二层是Play,存储着制定好的各种战术,包括各种定位球战术,运动战战术。每一个战术都是一个状态机,会根据不同的状态做出不同的动作, 满足条件时会跳出。每一个状态内部都定义了每一台机器人的行为,不过也可以选择让机器人维持之前的状态,每个状态内部还定义了机器人的角色匹配规则,在 执行这个战术(即PlayBook选择这个战术)时会根据状态内部的规则进行角色匹配。这一层主要用Lua实现;
  3. 第三层是Agent,在程序中代表执行动作的个体,也就是一台机器人,Play层的战术会分配给每一个机器人去分别执行。 执行战术的人选是在Play层就定好了的,在这一层将动作分配给每一个个体,个体执行分配到的任务,也就是执行SKill层的一个个动作;
  4. 第四层是SKill,一个SKill只定义一个机器人的动作,比如射门、传球。这一层提供各种战术动作给Play层,完成战术布置。 在这一层内部也有从简单到复杂的层级结构,最基本的SKill(动作)只有跑到一个固定点固定朝向和踢球,复杂的动作比如追踢是由调用简单的底层动作实现的。 这一层主要用C++实现;

除了策略部分之外还有一个World Model,维护着环境信息,类似于openAI gym中的environment。这个部分将摄像头和机器人身上的传感器收集到的数据暴露给 策略部分中的各个层级,为它们提供信息。这一部分有Lua和C++的接口。

有限状态机

  1. 进入一个状态,
  2. 从World Model中获取信息,判断下一步进入哪个状态
  3. 动作分配,
  4. 匹配规则

一个例子:


-- 在实现中,每个状态都是一个Lua的table类型变量,
-- 包括switch,match,Kicker、Goalie(各个角色)这些键
["run1"] = {
    -- 状态的跳转函数
    switch = function ()
        -- 从World Model中获取信息,判断下一步进入哪个状态
        if player.toTargetDist("Kicker") < 20 then
            return "run2"
        end
    end,
    -- 分配任务,调用封装的SKill
    Kicker = task.goCmuRush(TargetPos1, 0),
    -- 匹配规则,这样写就是不匹配
    match = ""
},