Try RoboCup2D——开发文档

CSU_ThreeNoobs 开发文档

CSU_ThreeNoobs 是在 CSU_Yunlu2012 Robocup 2D仿真足球队原有代码的基础上,进行了部分代码上的优化并新增了一些简单函数的2D仿真足球队程序。

2020.07.20

本改进在2020年CSU校赛中取得了前50%的成绩,以一分之差错失三等奖,实在是太菜了

正文

经过一定数量的自我对局训练与比赛情况分析,我们努力观察了足球队在比赛过程中存在的缺点,并查阅了大量文献,钻研代码构造,最终对原有的代码进行了一下的改动:

  • 体力与冲刺速度平衡策略改动

    这一改动的灵感来源于2019年Robocup冠军队伍 Fractals2019 的队伍文献:

    Gliders2d-v1.1: stamina management with higher dash power rates (less conservative usage of the available stamina balance;strategy.cpp1

    其中采取了更加激进的体力平衡策略,使得球员在获取合适的冲刺速度时更可能获得较高的冲刺速度。在strategy.cpp中具体改动代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    /*in Strategy::get_normal_dash_power( const WorldModel & wm )*/
    /*--------------------------------------------------------*/
    double dash_power = ServerParam::i().maxDashPower();
    const double my_inc
    = wm.self().playerType().staminaIncMax()
    * wm.self().recovery();

    if ( wm.self().pos().x > wm.ourDefenseLineX() // 20140930 '>' -> '<'
    && wm.ball().pos().x < wm.ourDefenseLineX() + 25.0 ) //20.0 -> 25.0
    {
    dlog.addText( Logger::TEAM,
    __FILE__": (get_normal_dash_power) correct DF line. keep max power" );
    // keep max power
    dash_power = ServerParam::i().maxDashPower();
    }
    // exist kickable teammate
    else if ( wm.existKickableTeammate()
    && ( wm.ball().distFromSelf() < 20.0 ) )
    // || ( wm.self().pos().x - )
    {
    dash_power = std::min( my_inc * 1.1,
    ServerParam::i().maxDashPower() );
    if ( ! s_recover_mode
    && wm.self().pos().x < wm.ourDefenseLineX()
    && wm.ball().pos().x < 0)
    {
    dash_power = std::min( dash_power * 1.5 , ServerParam::i().maxDashPower() );
    }
    dlog.addText( Logger::TEAM,
    __FILE__": (get_normal_dash_power) exist kickable teammate. dash_power=%.1f",
    dash_power );
    }
    // in offside area
    else if ( wm.self().pos().x > wm.offsideLineX() )
    {
    dash_power = ServerParam::i().maxDashPower();
    if( s_recover_mode && wm.ball().pos().absX() < 15.0)
    {
    dash_power = dash_power * 0.8 ;
    }
    else if ( wm.getOpponentGoalie()->distFromSelf() > 20.0)
    {
    dash_power = dash_power * 0.9 ;
    }
    dlog.addText( Logger::TEAM,
    __FILE__": in offside area. dash_power=%.1f",
    dash_power );
    }
    else if ( wm.ball().pos().x > 25.0
    && wm.ball().pos().x > wm.self().pos().x + 10.0
    && self_min < opp_min - 6
    && mate_min < opp_min - 6 )
    {
    dash_power = bound( ServerParam::i().maxDashPower() * 0.1,
    my_inc * 0.5,
    ServerParam::i().maxDashPower() );
    dlog.addText( Logger::TEAM,
    __FILE__": (get_normal_dash_power) opponent ball dash_power=%.1f",
    dash_power );
    }
    else if ( s_recover_mode )
    {
    dash_power = my_inc - 25.0; // preffered recover value
    if ( dash_power < 0.0 ) dash_power = 0.0;

    dlog.addText( Logger::TEAM,
    __FILE__": (get_normal_dash_power) recovering" );
    }
    // normal
    else
    {
    dash_power = std::min( my_inc * 1.7,
    ServerParam::i().maxDashPower() );
    dlog.addText( Logger::TEAM,
    __FILE__": (get_normal_dash_power) normal mode dash_power=%.1f",
    dash_power );
    }
    return dash_power;
    }

    在原有代码中,if( s_recover_mode )这一判断语句位于决策层的较高位置,将体力的恢复放在了比较高的优先级。我们尝试将该决策放在了靠后的位置,让球员更加优先考虑场上情况来尽力使用体力,防止部分球员的体力被浪费。我们还在决策层中加入了中场情况与进攻区情况的体力决策,并在决策中具体再根据体力恢复模式进行差异化的冲刺速度决定。

  • 铲球动作的分情况决策

    我们经过观察和总结,决定在bhv_BasicMove.cpp中的铲球动作按照情况进行更加细化的决策处理,具体代码改变如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /* in Bhv_BasicMove::execute( PlayerAgent * agent )*/
    //-----------------------------------------------
    // tackle
    double min_tackle_prob = 0.8;
    double tackle_angle = 80.0;
    const WorldModel & wm = agent->world();

    if ( Bhv_SetPlay::is_delaying_tactics_situation(agent) )
    {
    min_tackle_prob = 0.6;
    }
    else if ( wm.ball().pos().x < -22.0 )
    {
    if ( FieldAnalyzer::is_ball_moving_to_our_goal( wm.ball().pos() , wm.ball().vel() , 0 ))
    {
    min_tackle_prob = 0.7;
    if ( wm.self().pos().x < wm.ball().pos().x )
    {
    tackle_angle = 20.0;
    }
    }
    else
    {
    min_tackle_prob = 0.9;
    if ( wm.self().pos().x < wm.ball().pos().x )
    {
    tackle_angle = 20.0;
    }
    }
    }
    else if ( wm.ball().pos().x > 22.0 )
    {
    min_tackle_prob = 0.6;
    }
    // tackle end

    if ( Bhv_BasicTackle( min_tackle_prob, tackle_angle ).execute( agent ) )
    {
    #ifdef DEBUG2014
    std::cerr << agent->world().time() << __FILE__ << agent->world().self().unum() << __FILE__ << ": basic_tackle...\n";
    #endif // DEBUG2014

    return true;
    }

    在原代码中,对于BasicMove.excute()中最上层的铲球动作实现比较死板,是固定的以最小成功率80%,角度80.0作为铲球参数。在此基础上,我们根据拖延战术情况、球的位置情况、对方进攻情况分别采取不同的铲球策略。在bhv_dangerAreaTackle.h中,我们也调整了危险区域内铲球的默认最小成功率。

  • 紧急射门函数

    在自我对局的过程中,我们发现出现了比赛时间即将结束时射门过于犹豫,最终时间用尽的情况。为此我们尝试新建了两个函数来解决时间不足时的射门问题。在chain_action/shoot_generator.cpp中新增具体代码如下:

    剩余时间判断函数Time_Remain_Check():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    bool
    Time_Remain_Check(const WorldModel &wm)
    {
    static GameTime s_update_time( 0, 0 );

    s_update_time = wm.time();

    const ServerParam & SP = ServerParam::i();

    if ( s_update_time.cycle() < ( SP.halfTime() * 10 - 100 ) )
    {
    return true;
    }
    else if ( s_update_time.cycle() < SP.halfTime()*10 )
    {
    return false;
    }
    else if ( s_update_time.cycle() < ( SP.nrNormalHalfs() * SP.halfTime() * 10 - 100 ) )
    {
    return true;
    }
    else if ( s_update_time.cycle() < SP.nrNormalHalfs() * SP.halfTime() * 10 )
    {
    return false;
    }

    return true;
    }

    该函数用于检测比赛时间是否处于最后10秒。

    紧急射门函数generateEmgerancyShoot():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    bool
    ShootGenerator::generateEmgerancyShoot(const WorldModel & wm)
    {
    {
    static GameTime s_update_time( 0, 0 );
    s_update_time = wm.time();

    clear();


    const ServerParam & SP = ServerParam::i();

    if( Time_Remain_Check( wm ) )
    {
    return false;
    }

    if ( wm.self().pos().dist2( SP.theirTeamGoalPos() ) > std::pow( 40.0, 2 ) ) //30.0 -> 40.0
    {
    #ifdef DEBUG_PRINT
    dlog.addText( Logger::SHOOT,
    __FILE__": over shootable distance" );
    #endif
    return false;
    }

    M_first_ball_pos = ( wm.self().isKickable()
    ? wm.ball().pos()
    : wm.ball().pos() + wm.ball().vel() );

    #ifdef DEBUG_PROFILE
    MSecTimer timer;
    #endif

    Vector2D goal_l( SP.pitchHalfLength(), -SP.goalHalfWidth() );
    Vector2D goal_r( SP.pitchHalfLength(), +SP.goalHalfWidth() );

    goal_l.y += std::min( 1.5,
    0.6 + goal_l.dist( M_first_ball_pos ) * 0.042 );
    goal_r.y -= std::min( 1.5,
    0.6 + goal_r.dist( M_first_ball_pos ) * 0.042 );

    if ( wm.self().pos().x > SP.pitchHalfLength() - 1.0
    && wm.self().pos().absY() < SP.goalHalfWidth() )
    {
    goal_l.x = wm.self().pos().x + 1.5;
    goal_r.x = wm.self().pos().x + 1.5;
    }

    const int DIST_DIVS = 13; // 25 -> 13
    const double dist_step = std::fabs( goal_l.y - goal_r.y ) / ( DIST_DIVS - 1 );

    #ifdef DEBUG_PRINT
    dlog.addText( Logger::SHOOT,
    __FILE__": ===== Shoot search range=(%.1f %.1f)-(%.1f %.1f) dist_step=%.1f =====",
    goal_l.x, goal_l.y, goal_r.x, goal_r.y, dist_step );
    #endif

    for ( int i = 0; i < DIST_DIVS; ++i )
    {
    ++M_total_count;

    Vector2D target_point = goal_l;
    target_point.y += dist_step * i;

    #ifdef DEBUG_PRINT
    dlog.addText( Logger::SHOOT,
    "%d: ===== shoot target(%.2f %.2f) ===== ",
    M_total_count,
    target_point.x, target_point.y );
    #endif
    createShoot( wm, target_point );
    }


    evaluateCourses( wm );


    #ifdef DEBUG_PROFILE
    dlog.addText( Logger::SHOOT,
    __FILE__": PROFILE %d/%d. elapsed=%.3f [ms]",
    (int)M_courses.size(),
    DIST_DIVS,
    timer.elapsedReal() );
    #endif

    }
    return true;
    }

    该函数的内容与原本的射门产生函数generate()基本相同,但是在其基础上,我们只对射门点进行了12点分,并在评价体系中对于时间不足情况下kick_step_==1的射门提高了评价分值,以期望得到更果断的最后射门尝试。

  • 守门员阵型文件改动

    在比赛观察中,我们发现被进球时的情况有很大一部分来源于守门员站位的问题。我们针对该问题,对守门员与其他球员在对方边角球的情况下的站位进行了微调。

参考

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2024 Jzjerry Jiang
  • Visitors: | Views:

请我喝杯咖啡吧~