机器人足球仿真实验报告

合肥工业大学 计算机与信息学院 机器人足球仿真 实 验 报 告 

班级: 计算机科学与技术 11-1 班 姓名: 魏慷 学号: 20112425 20## 年 12 月 5 日

实验一 机器人足球比赛编程预备知识

1. 实验目的

掌握 RoboCup 仿真机器人足球比赛相关知识点,具体内容如下: (1) Linux 操作系统的熟悉及了解其基本操作。 (2) 掌握 Linux 下如何进行 C++编程,了解 gcc 编译器以及一些简单编辑工 具,如:vi、emacs、gedit、Anjuta、Kdevelope 等。 (3) 启动 RoboCup 仿真(2D)足球队的比赛。 2. 实验设备

硬件环境:PC 机 软件环境:操作系统 linux 3. 实验内容

(1) 掌握 Linux 一些常用的命令 ? 文件或目录处理 格式:ls [-atFlgR][name]        第一项是一些语法加量。第二项是文件名。 常用的方法有: ls      列出当前目录下的所有文件。 ls –a    列出包括以 .开始的隐藏文件的所有文件名。  ls –t    依照文件最后修改时间的顺序列出文件名。 ls –F   列出当前目录下的文件名及其类型。以/结尾表示为目录名、以* 结尾表示未可执行文件、以@结尾表示为符号连接。 ls –l    列出目录下所有文件的权限、所有者、文件大小、修改时间及名 称。 ls –lg   同上,并显示出文件的所有者工作组名。 ls –R   显示出目录下以及其所有子目录的文件名。 ? 改变工作目录 格式:: cd [name] name :目录名、路径或目录缩写。 常用的方法有: cd     改变目录位置至用户登录时的工作目录。 cd dirl     改变目录位置至 dirl 目录下。 cd ~user    改变目录位置至用户的工作目录。 cd ..       改变目录位置至。 cd ../user   改变目录位置至相对路径 user 的目录下。 cd /../..     改变目录位置至绝对路径的目录位置下。 ? 复制文件 格式:cp [-r] 源地址  目的地址

常用的方法有: cp  file1  file2  将文件 file1 复制成 file2 cp  file1  dir1  将文件 file1 复制到目录 dir1 下,文件名仍为 file1。 cp  /tmp/file1  将目录/tmp 下的文件 file1 复制到当前目录下,文件名仍为 file1。 cp  /tmp/file1 file2 将目录/tmp 下的文件 file1 复制到当前目录下,文件名 仍为 file2。 cp -r dir1 dir2   复制整个目录。 ? 移动或更改文件、目录名称 格式:mv 源地址 目的地址 常用的方法有: mv  file1 file2 将文件 file1 更名为 file2。 mv  file1 dir1 将文件 file1 转移到目录 dir1 下,文件名仍为 file1。 mv  dir1 dir2  将目录 dir1 更改为目录 dir2。 ? 建立新目录 格式:mkdir 目录名  删除目录 格式:rmdir  [目录名|文件名]     常用的方法有: rm –r dir1 删除目录 dir1 及其子目录下的所有文件。 ? 列出当前所在的目录位置 格式:pwd ? 查看文件内容 格式: cat 文件名 ? 文件权限的设定 格式:chmod  [-R] mode name  name:文件名或目录名。 mode:3 个或 8 个数字或 rwx 的组合。r-read(读权限)、w-write(写权限)、 x-execute(执行)  常用的方法有: chmod 777 file1  给所有用户 file1 全部的权限。 ? 文件的打包和解压缩 格式:tar [option] [file]  gzip[option] [file] tar 主要来进行打包而不压缩,而 gzip 则进行压缩。 option 主要有: -c — 创建一个新归档。 -f — 当与 -c 选项一起使用时,创建的 tar 文件使用该选项指定的文件名; 当与-x 选项一起使用时,则解除该选项指定的归档。 -t — 显示包括在 tar 文件中的文件列表。 -v — 显示文件的归档进度。 -x — 从归档中抽取文件。 -z — 使用 gzip 来压缩 tar 文件。  如使用:tar -cvf filename.tar /home/mine/work /home/mine/school 上面的命令把 /home/mine 目录下的 work 和 school 子目录内的所有文件

都放入当前目录中一个叫做 filename.tar 的新文件里。 (2) 完成以下操作: 1. 如何找到用户主目录的绝对路径名?在自己的系统上,用户主目录的绝对 路径名是什么? 执行 cd;执行 pwd;即显示用户主目录的绝对路径名。 用户主目录的绝对路径名是”/home/username” 2. 将当前工作目录从/home/UVA 转到/home/Tsinghua 需要使用什么命令? 如何显示当前目录? cd /home/Tsinghua 或 cd ../Tsinghua 3. 如何在当前目录下建立子目录 RoboCup? mkdir  Robcup 4. 如何删除子目录 RoboCup? rmdir Robcup 5. 如何查看当前目录下的内容? ls 6. 如何将文件 start.sh 的权限设定为:start.sh 属于可读、可写、可执行? 可读:chmod g+rr start.sh 可写:chmod g+rw start.sh 可执行:chmod g+rx start.sh 7. 如何将当前目录包括所有子目录全部做备份文件,备份文件名为 first.tar? tar czvf first.tar dir1 8. 如何将目录/home 下每一个文件压缩成.gz 文件? tar -cvf store.tar  dir1 9. 如何把上例中每个压缩的文件解压,并列出详细的信息? tar xvf store.tar ls -lg 4. 启动球队上场比赛

完成以下操作: rcssserver rcsslogplayer 在球队路径下: ./start.sh 在本地启动第二个球队: ./start.sh localhost aaa 

实验二 Demeer5 基本动作 1

1. 实验目的

(1) 了解 Demeer5 的工作原理

(2) 学会对 Demeer5 进行简单的修改 2. 实验设备

硬件环境:PC 软件环境:Linux 3. 实验内容

(1) Demeer5 的工作原理: Demeer5 函数是整个球队的核心,它最终返回一个可以执行的动作,底层的 模块负责将此动作发送给 server,然后由 server 执行。可以说,Demeer5 就是我 们想法的体现,是一支球队的大脑。在 Demeer5 中有一系列的判断来决定每个 周期的动作。下面对 Demeer5 进行简要的分析. Demeer5()是一个决策函数,在策略上使用的是下面这个简单的策略: 如果球可踢,则用最大力量踢球; 如果球不可踢且我是队友中最快到球的队员,则去截球; 其他情况按战略点跑位。 我们现在只要看一下球可踢时的代码: else if( WM->isBallKickable())                    // 如果球可踢

{

//确定踢向的点

VecPosition posGoal( PITCH_LENGTH/2.0, -1+2*(WM->getCurrentCycle()%2))* 0.4 * SS->getGoalWidth() );

 //调用踢球的动作

soc = kickTo( posGoal, SS->getBallSpeedMax() ); // kick maximal

//将动作放入命令队列中

ACT->putCommandInQueue( soc );

//将脖子转向球

ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

//记录调试信息

Log.log( 100, "kick ball" );

}

这一小段函数决定了当球在球员 Agent 的可踢范围之内时应当做的动作,这 里是一个简单的把球向前踢而不考虑任何其他情况的方法。该程序段的条理是很 清晰的。

(2) 对 Demeer5 进行简单的修改: 现在我们对 Demeer5 进行简单的修改,让它在球可踢的时候进行带球的动作。 带球就是 kick 和 dash 动作序列的结合。带球的函数在 BasicPlayer 中,函数为 dribble().它接收两个参数,第一个参数为带球的方向,第二个参数为带球的 类型。 带球类型解释如下: DRIBBLE_FAST:快速带球; DRIBBLE_SLOW:慢速带球; DRIBBLE_WITHBALL:安全带球; 所以,对 dribble 的一种调用形式为:dribble(ang,DRIBBLE_FAST ) 其中 ang 为 AngDeg(是一个 double)类型 该函数的返回值是一个 SoccerCommand 类型。 知道了如何调用 dribble,我们来对 Demeer5 进行替换: else if( WM->isBallKickable()) {       AngDeg ang = 0.0;

      soc = dribble(ang,DRIBBLE_FAST ); // 进行带球

    //将动作放入命令队列中

      ACT->putCommandInQueue( soc );

      //将脖子转向球

      ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

      //记录调试信息

      Log.log( 100, "kick ball" ); } 这样,在球可踢的时候,球员智能体将把球带向前方,这里取的是 0 度角, 即沿 x 轴一直向前快速带球。 我们再次对 Demeer5 函数进行修改,这次是让球员 Agent 将球踢向各个不同 的地方。 这将调用 kickTo()函数来完成。下面简要说明一下 kickTo()函数 的使用方法:这个函数有两个参数,第一个参数是目标点的坐标,第二个参数是 球到达目标点是的速度,返回一个踢球的动作。可以使用下面的形式来调用: VecPosition pos( x, y ); double speed = 1.0; kickTo( pos , speed ); kickTo()函数在其内部将会决定踢球时所用力量的大小,并且会判断是否能 够将球踢到该点,并作出相应的调整。比如,将球一直踢向( 0, 0)点, 1.0 的末速度: else if( WM->isBallKickable()) { soc = kickTo( VecPosition( 0, 0 ),1.0);

//将动作放入命令队列中

ACT->putCommandInQueue( soc );

//将脖子转向球

ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

//记录调试信息

Log.log( 100, "kick ball" );

}

(3) 根据上述操作,完成以下踢球操作: 将球踢向对方的球门。 VecPosition pos=WM->getPosOpponentGoal; soc=kickTo( pos,1.0 ); ACT->putCommandInQueue ( soc );   ACT->putCommandInQueue ( turnNeckToObject ( OBJECT_BALL, soc ) ); 将球踢向距离自己最近的队友。 ObjectT o=WM->getClosestInSetTo( OBJECT_SET_TEAMMATES,posAgent); soc=kickTo( WM->getGlobalPosition( o ) ,1.0 ); ACT->putCommandInQueue ( soc );   ACT->putCommandInQueue ( turnNeckToObject ( OBJECT_BALL, soc ) ); 尝试不同的踢球点。 (略) (4) 根据上述内容,完成以下带球的操作: 用不同的带球模式进行带球,并观察效果,比较异同。 AngDeg ang = 0.0;

soc = dribble(ang,DRIBBLE_SLOW); 

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) ); 将球向对方的球门方向带。 double ang=( VecPosition(52.5,0)-posAgent).getDirection();//求与球门间角度

soc=dribble( ang,DRIBBLE_SLOW ); ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) ); 尝试不同的带球组合。 (略) (5) 根据上述内容,完成以下综合练习: 带球与踢球的结合: 让 agent 一直向对方球门的方向带球,在进入对方禁区后以最大力量踢向球

门( 末速度最大为 2.7 )。涉及到的具体函数请查看教材。 VecPosition posGoal ( 52.5 , 0 );

if ( WM->isInTheirPenaltyArea ( WM->getBallPos() ) ) //判断是否在对方禁区

{

soc=kickTo ( posGoal , 2.7 );

} else {

 double ang=( posGoal - posAgent ).getDirection();//求与球门间角度

 soc=dribble( ang,DRIBBLE_FAST ); } ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

实验三 Demeer5 基本动作 2

1. 实验目的

熟悉 demeer5 并学会 demeer5 的基本使用方法,具体内容如下: (1) 能理解 UVA 程序中原来的 demeer5 中的内容 (2) 能通过修改 demeer5 中的具体函数内容实现对场上球员的控制 (3) 能通过底层动作的简单组合控制场上队员做出一些复杂动作 2. 实验设备:

硬件设备:pc 机 软件设备:操作系统 linux 3. 实验内容

(1) 在球队程序中找到 playerTeams.cpp;

(2) 在 player.cpp 中找到 demmer5 函数;

(3) 阅读此段程序,并结合 Monitor 观察球员的具体行为(你将发

现可以踢到球的球员会将球朝球门的方向踢去,而不能踢到球的

队员中如果是离球最近的队员就去截球,否者则按阵型跑位);

(4) 修改 demmer5 函数改变队员的行为具体步骤如下:       ①在 demeer5 函数中找到

if( WM->isBallKickable())   

// isBallKickable()函数用来判断球是否可踢

{

VecPosition posGoal( PITCH_LENGTH/2.0, (-1 + 2*(WM->getCurrentCycle()%2)) * 0.4 * SS->getGoalWidth() );

//设定射门位置坐标

soc = kickTo( posGoal, SS->getBallSpeedMax() ); // 朝球门方向将球以最大

力度踢出

ACT->putCommandInQueue( soc ); //只有把命令放入命令队列动作才会执行

ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

//做动作的同时改变脖子的方向   

    ②『控球』将此函数修改为 if( WM->isBallKickable())  {

soc = kickBallCloseToBody(45);//45 是踢球的方向

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

    然后编译运行程序,观察球员的行为我们会发现当球可踢时,球员不再朝着 球门的方向踢了,而是将球绕自己身体转动(uva 的这个底层动作经常把球转丢!)          ③『带球』将此函数修改为 if( WM->isBallKickable())                     {

soc = dribble(0.0,DRIBBLE_SLOW);//其中 dribble 函数中第一个参数表示带球的

方向-180~180 之间,不一定是 0.0

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

后编译运行程序,观察球员的行为我们会发现当球可踢时,球员不再朝着球 门的方向踢了,而是朝我们指定的方向执行带球

9  

④『传球』将此函数修改为 if( WM->isBallKickable())                     {

soc = leadingPass(OBJECT_TEAMMATE_9,1);//9 是接球的人,1 是指球与接球人

之间的距离

//其中 leadingPass 中第一个参数表示传球的对象,本实验中我们将球直接传给指定

号码(1~11)的球员,不一定是 OBJECT_TEAMMATE_9

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );

}

然后编译运行程序,观察球员的行为我们会发现当球可踢时,球员不再朝着 球门的方向踢了,而是将球传给我们指定号码的队员。 

    ⑤『配合』将此函数修改为 if( WM->isBallKickable())                     { if(WM->getAgentObjectType()==OBJECT_TEAMMATE_9)

soc = dribble(0.0,DRIBBLE_SLOW);     //带球

} else

soc = leadingPass(OBJECT_TEAMMATE_9,1); //传球

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) ); } 编译程序,观察球员行为,我们会发现,当 9 号队员得到球后会朝前方带球, 其他队员得到球后会将球传给 9 号(不管 9 号是不是越位). (5) 根据以上描述完成以下实验内容: 如果在对方禁区内就射门,否则,如果是 7,8,9 号队员就朝前带球, 其他队员将球传给 9 号(用 WM->isInTheirPenaltyArea(WM->getBallPos()) 来判断球是否在对方禁区) 如果队员的位置在自己半场就将球朝对方球门踢去,否者就朝前方带 球(用 WM->getBallPos().getX()来得到球的 x 坐标) 当有人来抢球时(离自己很近),就将球传给离自己最近的队员,否则 就自己带球(调用 WM->getClosestRelativeInSet 函数来得到离自己最近的 己方或对方球员,通过pos1.getDistanceTo(pos2)来得到两位置之间的距离)。 Circle cir ( posAgent,2.0 );  //定义这样一个圆形区域,以我为圆心,2.0 为半径

10 

int num=WM->getNrInSetInCircle ( OBJECT_SET_OPPONENTS,cir );  //判断 2.0

范围内有多少人

if ( num>0 ) {  soc=leadingPass( WM->getClosestRelativeInSet( OBJECT_SET_TEAMMATES ) ,1); } else if ( WM->getBallPos().getX() >0 )  {  if ( WM->isInTheirPenaltyArea ( WM->getBallPos() ) )  {   VecPosition posGoal ( PITCH_LENGTH/2.0, ( -1+2* ( WM->getCurrentCycle() %2 ) ) *0.4*SS->getGoalWidth() );   soc=kickTo ( posGoal,SS->getBallSpeedMax() );  }  else if ( WM->getAgentObjectType() ==OBJECT_TEAMMATE_7 || WM->getAgentObjectType() ==OBJECT_TEAMMATE_8 || WM->getAgentObjectType() ==OBJECT_TEAMMATE_9 )   {     soc=dribble ( 0.0,DRIBBLE_SLOW );   }   else   {    soc=leadingPass ( OBJECT_TEAMMATE_9 ,1 );   }  }   else  {   VecPosition pos= ( PITCH_LENGTH/2.0, ( -1 + 2* ( WM->getCurrentCycle() %2 ) ) * 0.4 * SS->getGoalWidth() );   soc=kickTo ( pos,SS->getBallSpeedMax() );  }

ACT->putCommandInQueue ( soc ); // 放入命令队列

ACT->putCommandInQueue ( turnNeckToObject ( OBJECT_BALL, soc ) );

11 

实验四 复杂的动作决策

1. 实验目的

进一步了解demeer5并能熟悉的修改demeer5的内容以达到对场上的球员的控 制 (1) 能理解 UVA 程序中原来的 demeer5 中的全部内容 (2) 能通过修改 demeer5 中的具体函数内容实现对场上球员的控制 (3) 能通过底层动作的简单组合控制场上队员做出一些复杂动作决策 (4) 对 WorldModel 有初步的认识,学会在 WorldModel,basicplayer 里添加新函 数 2. 实验设备

硬件环境:PC 软件环境:操作系统 linux 3. 实验内容

(1) 在球队程序中找到 player.c 并打开;

(2) 在 player.c 中找到 demmer5 函数;

(3) 阅读此段程序,并结合 monitor 观察球员的具体行为(你将发

现可以踢到球的球员会将球朝球门的方向踢去,而不能踢到球的

队员中如果是离球最近的队员就去截球,否者则按阵型跑位);

(4) 修改 demmer5 函数改变队员的行为具体步骤如下:       ①在 demeer5 函数中找到 else if( WM->getFastestInSetTo( OBJECT_SET_TEAMMATES, OBJECT_BALL, &iTmp )`               == WM->getAgentObjectType()  && !WM->isDeadBallThem() )

{                                                // 如果是最快到达球的队员

Log.log( 100, "I am fastest to ball; can get there in %d cycles", iTmp );

soc = intercept( false );                      // 截球 

if( soc.commandType == CMD_DASH &&             // 当体力低时

WM->getAgentStamina().getStamina() < SS->getRecoverDecThr()*SS->getStaminaMax()+200 )

12 

{

soc.dPower = 30.0 * WM->getAgentStamina().getRecovery(); // 慢速移

ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) ); //

脖子转向球

}

else                                           // 当体力高时

{

ACT->putCommandInQueue( soc );                //正常移动速度

ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) );//

脖子转向球

}

}

此函数的内容是,当球不可踢时,如果是离球最近的队员就执行截球命令(截 球函数 intercept()在 BasicPlayer.c 中定义)观察在队员体力值小于多少时,带球 速度会变慢。     ②现在我们来通过修改函数,来改变非持球队员的决策,在以上函数之前加 上此段代码: else if(WM->getAgentObjectType()==OBJECT_TEAMMATE_9) //如果是 9 号队员

{

soc = SoccerCommand(CMD_TURN,60);     //转身体

ACT->putCommandInQueue( soc );                   

ACT->putCommandInQueue( alignNeckWithBody( ) );  //脖子随着身体一起转动

}

然后编译运行程序,观察球员的行为我们会发现当 9 号不持球时,身体在一 直的转动(此动作可用来找球) 

(5) 在 WorldMoled 里填加状态函数 ① .打开 WorldModel.h,在里面预定义函数,即写入 bool    isOpponentAtAngleEx( AngDeg angA , AngDeg angB ,double   dDist );

该函数用来判断当前球员角度在 angA~angB 之间距离小于 dDist 的范围内是

13 

否有对方队员。 

②.找到并打开 WorldModel.c 在里面填加一个新函数 bool WorldModel::isOpponentAtAngleEx( AngDeg angA , AngDeg angB ,double     dDist ) { VecPosition posAgent   = getAgentGlobalPosition(); VecPosition posOpp; AngDeg      angOpp; int         iIndex; 

for( ObjectT o = iterateObjectStart( iIndex, OBJECT_SET_OPPONENTS );        o != OBJECT_ILLEGAL;        o = iterateObjectNext ( iIndex, OBJECT_SET_OPPONENTS ) ) { posOpp    = getGlobalPosition( o ); angOpp    = ( posOpp - posAgent ).getDirection() ; if( angA<=angOpp && angOpp <=angB && posAgent.getDistanceTo( posOpp ) < dDist ) return true; } iterateObjectDone( iIndex ); return false; }       ③.将 if( WM->isBallKickable())内的内容改为: if( WM->isBallKickable())   {     double ang = (VecPosition(52.5,0)-posAgent).getDirection();               if ( WM->isOpponentAtAngleEx(ang-45, ang, 6) )                 ang+=45;         else if ( WM->isOpponentAtAngleEx(ang,ang+45,6) )                 ang-=45;     SoccerCommand soc = dribble ( ang , DRIBBLE_SLOW );       ACT->putCommandInQueue( soc );     ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc ) ); 

} 然后编译运行程序,观察球员的行为,试分析球员的行为 

14 

(6) 根据以上描述完成练习:通过基本动作的组合实现球员的以下

行为 判断守门员的位置,朝球门空隙较大的一方射门,(通过在 WorldModel 里 建立新状态来判断,球门哪一方空隙较大,守门员的位置为 VecPosition posGoalie = WM->getGlobalPosition(WM->getOppGoalieType());球门位置坐标 为(52.5,0),可尝试朝(52.5,6.5)( 52.5,-6.5)两点射门) VecPosition vec=WM->getGlobalPosition(WM->getOppGoalieType()); if(vec.getY()>0) {  VecPosition pos=(52.2,-6.5);  soc=kickTo(pos,SS->getBallSpeedMax()); } else {  VecPosition pos1=(52.5,6.5);  soc=kickTo(pos1,SS->getBallSpeedMax()); } ACT->putCommandInQueue( soc ); ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc )); 

在 BasicPlay 里填加一个带球函数,要求如果无人阻挡(带球将要经过的路 线附近没有对方球员)就朝球门方向带球,否则想办法避开对方球员带球前进 (要求只要作出闪避的动作即可,不要求效果)。 打开 WorldModel.h,在里面预定义函数,即写入 bool    isOpponentAtAngleEx( AngDeg angA , AngDeg angB ,double   dDist );

该函数用来判断当前球员角度在 angA~angB 之间距离小于 dDist 的范围内是 否有对方队员。 找到并打开 WorldModel.c 在里面添加一个新函数: bool WorldModel::isOpponentAtAngleEx( AngDeg angA , AngDeg angB ,double     dDist ) { VecPosition posAgent   = getAgentGlobalPosition(); VecPosition posOpp; AngDeg    angOpp; int     iIndex; for( ObjectT o = iterateObjectStart( iIndex, OBJECT_SET_OPPONENTS ); o != OBJECT_ILLEGAL; o = iterateObjectNext ( iIndex, OBJECT_SET_OPPONENTS ) ) { posOpp    = getGlobalPosition( o ); angOpp    = ( posOpp - posAgent ).getDirection() ;

15 

if( angA<=angOpp && angOpp <=angB && posAgent.getDistanceTo( posOpp ) < dDist ) return true; } iterateObjectDone( iIndex ); return false;

在 playerteams.cpp 中添加 else if( WM->isBallKickable())                    {     Circle cir(posAgent,2.5); int num=WM->getNrInSetInCircle(OBJECT_SET_OPPONENTS,cir); if(num<2) { double ang = (VecPosition(52.5,0)-posAgent).getDirection();      if ( WM->isOpponentAtAngleEx(ang-45, ang, 6) ) ang+=45; else if ( WM->isOpponentAtAngleEx(ang,ang+45,6) ) ang-=45; SoccerCommand soc = dribble ( ang , DRIBBLE_SLOW );   } else { soc=dribble(0.0,DRIBBLE_FAST); } ACT->putCommandInQueue( soc );  ACT->putCommandInQueue( turnNeckToObject( OBJECT_BALL, soc )); } 尝试修改视觉函数使得球员能更多的获得场上信息(要求不影响球员的动 作)。 先在 Basicplayer.h 中函数声明  SoccerCommand   view1122333 (SoccerCommand soc  ); 在 Basicplayer.cpp 中 view1122333 (SoccerCommand soc  )函数定义如下: /*将视觉分成 1122333 的视觉模式(“1”代表 60 度,“2”代表 120,“ 3”代表 180)*/

SoccerCommand BasicPlayer::view1122333(SoccerCommand soc) {

   //7 --> 共分成 7 份

   VecPosition posAgent = WM->getAgentGlobalPosition();    AngDeg angBody = WM->getAgentGlobalBodyAngle();    AngDeg angTurn=30;    switch( WM->getCurrentCycle()%7 )    {

16 

      case 0:          angTurn -= 60;       case 1:          ACT->putCommandInQueue(SoccerCommand(CMD_CHANGEVIEW, VA_NARROW, VQ_HIGH));          ACT->putCommandInQueue(turnNeckToPoint(posAgent+VecPosition(                            5, VecPosition::normalizeAngle(angBody+angTurn), POLAR), soc));          break;       case 2:       case 3:          ACT->putCommandInQueue(SoccerCommand(CMD_CHANGEVIEW, VA_NORMAL, VQ_HIGH));          ACT->putCommandInQueue(turnNeckToPoint(posAgent+VecPosition(5, angBody, POLAR), soc));          break;       case 4:       case 5:        case 6:          ACT->putCommandInQueue(SoccerCommand(CMD_CHANGEVIEW, VA_WIDE, VQ_HIGH));          break;       default:;    } } 再在 playerTeams.cpp 球可踢的条件下增加 ACT->putCommandInQueue(view1122333(soc)); 就可看到明显的视觉变化。

实验五 特殊比赛模式的设计

1. 实验目的

掌握 Robocup 仿真机器人足球比赛中特殊比赛模式发生的条件; 掌握 Robocup 仿真机器人足球比赛特殊比赛模式的规则要求; 了解 Robocup 仿真机器人足球比赛特殊比赛模式的战术设计思想; 进一步熟悉 WorldModel 类。 2. 实验设备

硬件环境:PC 机 软件环境:操作系统 Linux

17 

3. 实验内容

(1) 概述 Robocup 仿真机器人足球比赛特殊比赛模式包括角球(corner_kick)、界外球 (kick_in)、定位球/任意球(free_kick)以及球门球(goal_kick)。 (2) 角球(corner_kick) 当防守方球员将球踢出底线时,由进攻方开角球。Server 一旦接收到发球队 员发出的 kick 命令后,就将比赛模式设为正常的 play_on 模式。注意,和国际足 联的规则类似,发球队员在其他球员接触球之前不能再触球,否则判犯规。此时, 另一方球员须在一定时间(根据 Manual 的规定为 300 周期)内将球开出,否则 算发球失误,由对方发定位球。 在 UVA_trilearn 的原代码中,球员是不管这些特殊比赛模式的,统统是最近 的球员跑向球,一脚踢向球门。这就需要我们手工编码完成这些细节地方。一种 简单的设计思路是:(1)如果比赛进入角球模式,则离球最近的球员 A 跑向球, 而次近的球员 B 跑到某个接应点,等待 A 将球传过来;(2)A 跑到球跟前(即 进入可踢范围)时,不必立即将球开出,可以先看看场上环境,等 B 跑到预定 位置并且体力恢复得差不多时再开球。 以上设计思路同学们在实验过程中只须完成(1)即可,对于学有余力的同 学可以考虑(2)的实现。下面列出几个可能用到的函数: PlayModeT WorldModel::getPlayMode() const 返回比赛模式(或者直接用 bool WorldModel::isCornerKickUs() 判断是不是我方开角球); ObjectT WorldModel::getClosestInSetTo(ObjectSetT objectSet, ObjectT o, double *dDist=NULL, double dConfThr=-1.0) 返回在对象集合 objectSet 中距离对 象 o 最近的对象,只有当对象的可信度高于给定的阈值才被考虑,如果没有给出 阈值则使用 PlayerSettings 中定义的阈值,同时 dDist 返回距离; ObjectT WorldModel::getSecondClosestInSetTo(ObjextSetT objectSet, ObjectT o, double *dDist=NULL, double dConfThr=-1.0) 返回在对象集合 objectSet 中距离对 象 o 次近的对象,只有当对象的可信度高于给定的阈值才被考虑,如果没有给出 阈值则使用 PlayerSettings 中定义的阈值,同时 dDist 返回距离。 下面具体描述一下实现过程: 首先,我们知道 demeer5()主要是围绕三句话展开的,即(1)如果球可踢, 则用最大力量踢球;(2)如果球不可踢且我是队友中最快到球的队员,则去截球; (3)其他情况按战略点跑位。我们可以围绕这三句话来实现角球策略。即在角 球模式下(1)如果球可踢,则传球给接应球员;(2)如果球不可踢且我是队友 中最快到球的队员,则去发球;(3)其他情况下,如果我是离球次近的队员,那 么我去接应。 接着,有了这个基本思想后,我们开始编写代码。在 demeer5() 中找到 Else if ( WM->isBallKickable() ){           // if kickable 在里面加入我们的角球代码: If ( WM-> isCornerKickUs() ){  ACT->putCommandInQueue(kickTo(pointToKickTo(),SS->getBallSpeedMax()* 0.8 ));

18 

} 这一部分代码是完成开球动作。函数 pointToKickTo()是 Player 类的成员函数,返 回一个 VecPosition 类型的参数,代表要将球踢向的坐标。下面给出一段参考代 码: VecPosition Player::pointToKickTo(){     VecPosition pos,temp;     double x,y;     pos=WM->getBallPos();     x=pos.getX();     y=pos.getY();     temp.setX(-x/fabs(x)*5+x);     temp.setY(-y/fabs(y)*12+y); return temp; }  再在 demeer5()中找到 else if( posAgent.getDistanceTo(WM->getStrategicPosition()) >1.5 + fabs( posAgent.getX() - posBall.getX() ) / 10.0)  // if not near strategic pos 在里面加入如下代码: if  ( WM-> isCornerKickUs() ) {  ObjectT o;  SoccerCommand sctemp;  o = WM->getSecondClosestInSetTo(OBJECT_SET_TEAMMATES, OBJECT_BALL);  if ( o = WM->getAgentObjectType() ){   sctemp = ACT->putCommandInQueue( moveToPos( pointToKickTo(), PS->getPlayWhenToTurnAngle() );  }  ACT = putCommandInQueue( turnNeckToObject( OBJECT_BALL, sctemp )); } 这段代码是实现接应球员的跑位。 这样,一个简单的角球策略就实现了。同学们可以编译运行看看效果。 (3) 界外球(kick_in) 当一方球员将球踢出边线时,比赛即进入界外球模式。 比赛的规则要求基本同角球。 简单的设计思路和实验内容与角球也差不多,在此不再赘述。 要求同学们编写一段程序实现界外球策略,要求能够根据发球点的位置给出 合适的接应球员的接应位置。 可能用到的函数: bool WorldModel::isKickInUs() 返回是否是我方开界外球。 if(WM->isKickInUs())//我方界外球

{  if(WM->getAgentObjectType()==OBJECT_TEAMMATE_11)

19 

{

soc =leadingPass(OBJECT_TEAMMATE_9,2);     //传球给 9 号

}  if(WM->getAgentObjectType()==OBJECT_TEAMMATE_10) {

soc=leadingPass(OBJECT_OPPONENT_9,2);     //传球给 9 号

 }  if(WM->getAgentObjectType()==OBJECT_TEAMMATE_5)  {}  if(WM->getAgentObjectType()==OBJECT_TEAMMATE_4)  {}  if(WM->getAgentObjectType()==OBJECT_TEAMMATE_7)  {} } (4) 定位球/任意球(free_kick)

当一方球员犯规或违例时,由对方开任意球。跟国际足联的规定略有不同的 是,Robocup 的任意球没有直接任意球跟间接任意球之分。 比赛的规则要求基本同界外球。 (5) 球门球(goal_kick)

当对方进攻球员将球踢出底线或我方守门员截住对方射门的球时,由我方守 门员开门球。注意,如果守门员截住的是己方后卫的回传球,那么会由对方球员 在离截球点最近的禁区角点发任意球,因为这是违例行为。 如果守门员在 300 周期内不能将球开出,那么由对方球员在离开球点最近的 禁区角点发任意球。 在 UVA_trilearn 的源代码中,守门员开球也是直接将球踢向对方球门。而开 门球是一个比较复杂且非常重要的细节,许多强队,诸如清华、科大,都有不错 的开门球策略。我们这次实验只完成一个简单的策略,即守门员沿某一直线开球, 该直线是最近对方球员、守门员、次近对方球员三点所构成角的角平分线。

相关推荐