
简介面向Robocup3D仿真研究者的东南大学2014年校内赛源码包完整收录“seu2013”三维机器人足球仿真代码适用于人工智能、多智能体系统及机器人控制方向的学习者与参赛队伍可帮助深入理解比赛背后的算法落地。压缩包共包含332个文件体积约307KB以100个h头文件、87个cpp实现、55个rsg行为/配置脚本、22个hpp及txt说明文档为主兼顾函数声明、核心算法实现、机器人技能参数与使用说明结构清晰便于按模块检索。目前已有303人学习下载。代码覆盖物理仿真交互、球路预测、射门与防守策略、视觉特征识别、多智能体协作通信等关键环节并附带构建脚本与Doxygen配置可结合源码逐步梳理路径规划、动态决策及图像处理模块适合希望在机器人足球仿真领域动手实践、快速上手大型C项目的读者。1. 拿到 seu2013.tar.gzRoboCup 3D 代码包到底是什么如果你参加过机器人大赛的仿真组或者接手过实验室的老代码大概率见过这种命名风格的压缩包团队代号加年份里面塞着一个完整的 RoboCup 3D 仿真足球队。seu2013.tar.gz 就是这样一份代码包它来自 2013 赛季的 RoboCup 3D 仿真组是整个球队的智能体agent源码、配置和启动脚本的集合。RoboCup 3D 比赛里两支队伍各 11 个智能体在 SimSpark 仿真环境中踢球每个智能体要独立决策感知球场状态、做出跑位和踢球的动作还要和队友配合。代码包的价值在于它把「怎么让一个智能体在仿真环境里跑起来、怎么决策、怎么协作」这件事完整地做了一遍是研究多智能体决策和仿真机器人策略的极好起点。这份代码包适合谁两类人。一类是刚接手 RoboCup 3D 的新队伍与其从零搭框架不如基于现成代码改出自己的策略省掉大量环境对接的功夫另一类是研究强化学习或多智能体协作的开发者需要一个稳定、可控、能跑完整比赛的仿真环境来验证算法。当然拿到这份包只是第一步问题在于老代码包通常年代久远依赖的库版本和仿真器都有变化直接跑大概率会翻车。这篇笔记会带着你从理解底层机制开始把编译、启动、修改、排错整个链路走通。2. 理解 RoboCup 3D 的底层机制SimSpark、智能体与比赛闭环2.1 SimSpark 仿真器一个物理引擎之上的足球世界RoboCup 3D 的底层是 SimSpark一个基于开源物理引擎的 3D 仿真环境。SimSpark 做了两件事一是模拟物理世界包括球和智能体的质量、摩擦力、碰撞、关节运动二是维护比赛状态比如比分、时间、犯规、界外球。智能体不是一个图形界面里的角色而是一个独立的进程通过 TCP 或 UDP 与 SimSpark 服务器通信。服务器把「你看到了什么」发给智能体智能体把「我要做什么动作」发回给服务器服务器再更新仿真世界。这里有个关键点智能体没有全局视野。SimSpark 里的每个智能体都有自己的感知范围靠虚拟传感器获取信息。最常见的传感器有两种一种提供视觉信息比如看到球、看到球门、看到场地标志线另一种提供身体信息比如关节角度、加速度、朝向。你拿到一份代码包其实就是拿到了一套「怎么解析这些传感器消息、怎么做决策、怎么生成运动指令」的完整方案。2.2 比赛闭环一个智能体每秒钟要做多少次决策一场完整的 RoboCup 3D 比赛分上下半场每个半场 5 分钟仿真时间比真实时间略快。智能体的决策循环大致是感知消息 → 解析消息 → 状态估计 → 决策踢球、跑位、守门 → 发送动作指令。这个循环每秒钟要跑很多次具体次数取决于仿真器的步长设置常见是每秒 20 到 50 个仿真周期。意味着智能体要在几十毫秒内完成从感知到决策的全过程这也是 RoboCup 3D 代码里最核心的性能约束决策必须快且稳定。代码包里你怎么识别这个闭环找主循环。通常是一个 while 循环里面先阻塞等待服务器消息然后调用消息解析函数再调用决策模块最后发动作指令。这个循环的结构决定了后面你改策略时的切入点。seu2013 这种老代码包主循环的架构通常非常清晰因为它要承载后续大量策略迭代设计者不会把逻辑全堆在一个文件里。2.3 为什么选择 RoboCup 3D 而不是更简单的仿真平台很多人会问现在强化学习框架那么多比如各种机器人仿真套件为什么还要折腾 RoboCup 3D答案在于真实感和约束。SimSpark 不是把「踢球」抽象成高维动作空间里的一个决策而是要管 11 个关节、每个关节的角度和速度、身体姿态控制。智能体必须先学会走路、站稳、转身然后才谈得上踢球和战术。这种分层复杂度和真实机器人非常接近。更重要的RoboCup 3D 是有对抗的比赛。你的对手是另一支队伍对方也在动态调整策略。这种环境下你不能只在训练集上表现好要能在面对未知对手时保持竞争力。代码包的价值就在这——它不只是代码是一整套经过比赛检验的战术和工程实践。哪怕你最终的目标是跑强化学习算法拿这样一份完整基线代码起步也远比从空环境开始要快得多。3. 把代码包跑起来环境、编译、启动的最小链路3.1 环境准备与依赖安装一次搞定还是逐个排查RoboCup 3D 代码包常见的运行环境是 Ubuntu 或 Debian 系的 Linux 发行版。先确认系统版本老代码包通常要求特定版本的编译器和库。我一般建议先看看 README 或安装脚本没有的话就靠下一条命令摸清依赖。理论上一个 RoboCup 3D 智能体需要以下这些东西一个 C 编译器比如 gBoost 库SimSpark 服务器本体。不同代码包还会带各自的第三方库通常是打包在源码树里的。在新系统上装 SimSpark 服务器不一定要从源码编译很多发行版有现成的包但版本要留意。老代码包对接的是老版 SimSpark新版服务器可能改过消息格式导致智能体连不上。最稳妥的办法是和代码包发布时期的 SimSpark 版本保持一致。# 检查系统编译器版本 g --version # 检查 Boost 库是否安装RoboCup 3D agent 依赖它做网络通信和数值计算 dpkg -l | grep libboost逻辑说明第一行命令确认编译器版本是否满足老代码的编译需求第二行检查 Boost 库。如果缺库通常的解决方法是安装匹配版本。sudo apt-get update sudo apt-get install g make libboost-all-dev逻辑说明这里把编译需要的最小依赖装齐。libboost-all-dev 会把 Boost 全家桶装进来省去一个个找依赖的麻烦。参数说明如果你的系统是较新的版本默认编译器可能太新老代码会编译报错。这时候不要慌常见的做法是加装一个旧版本的 g比如 g-9 或 g-8然后用 update-alternatives 切换默认编译器。sudo apt-get install g-9 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-9 100逻辑说明把系统默认 g 指向旧版本避免代码包里用了新标准不支持的语法。RoboCup 3D 老代码大多基于 C11 或更早的标准太新的编译器反而会较真报错。3.2 编译代码包看懂 Makefile 与构建脚本解压后先浏览目录结构。RoboCup 3D 代码包的顶层通常有 Makefile、CMakeLists.txt 或者 build.sh 脚本。老代码包用 Makefile 的多因为当时 CMake 还没那么流行。编译命令很简单但坑都在细节里。tar -xzf seu2013.tar.gz cd seu2013 ls -la逻辑说明解压后先看目录里有什么。重点关注 Makefile、README、bin 或 src 目录。如果看到 configure 脚本说明是 autotools 体系要先跑 configure。常见的两种编译方式# 方式一直接 make make # 方式二先 configure 再 make ./configure make -j4逻辑说明方式一是最理想的情况makefile 直接可用方式二需要先根据当前系统生成适配的 makefile。加 -j4 是让编译用 4 个线程并行加快速度。参数说明make -j 后面的数字不要太大否则可能因为内存不足导致编译中断。编译 RoboCup 3D 智能体通常不会太久几分钟到十几分钟的量级。3.3 启动比赛连接服务器与拉起 11 个智能体编译通过后bin 目录下会出现智能体的可执行文件。启动比赛需要先有 SimSpark 服务器进程然后再启动智能体去连接。RoboCup 3D 的比赛组织方式服务器在某个端口监听每支队伍的智能体按编号连接上去队伍 ID 用来区分阵营。# 启动 SimSpark 服务器默认端口 3100 spark-server 3100 # 查看服务器是否在监听 netstat -tuln | grep 3100逻辑说明spark-server 是 SimSpark 的服务端程序参数 3100 是指定监听端口。加 让它在后台运行。netstat 检查端口是否被监听这是最直接的确认方式。# 启动一支队伍的 11 个智能体 cd bin ./agent --host localhost --port 3100 --team seu2013 --unum 1 ./agent --host localhost --port 3100 --team seu2013 --unum 2 # ... 依次启动到 11逻辑说明每个智能体是独立进程用不同的球员编号--unum区分同一个队伍名--team让服务器知道这些智能体属于一支球队从而渲染成相同颜色。参数说明--host 是服务器的地址本地就是 localhost--port 要和服务器一致--team 是队伍名不需要和压缩包名一致但建议用参赛时的队伍代号这样日志里好认--unum 的范围是 1-11。如果不知道 agent 还有哪些参数常见做法是加 --help 看看./agent --help逻辑说明大多数 RoboCup 3D agent 都会带命令行参数解析--help 会列出所有可选参数。不需要全部用上但至少要知道 team、unum、host 这几个关键的。启动后怎么判断智能体有没有连上服务器看两个信息一是启动日志里面应有「connect to server success」之类的话二是 SimSpark 服务器的终端它会打印有智能体加入的消息。如果两个信息都没有说明连接失败问题大概率出在端口不一致或者服务器没起来。4. 读懂代码结构从感知到决策的调用链4.1 目录划分一眼定位感知、决策、动作三大模块RoboCup 3D 代码包的目录结构通常有很强的规律性。虽然不同队伍的命名不同但共性很明显。拿到一份包先看三样东西一个入口主文件通常是 agent 或 main 开头的一个感知模块负责解析服务器消息一个决策模块负责根据当前状态做出动作选择。运动控制往往藏在决策模块里或者单独抽成一个模块。find . -type f -name *.cpp | head -30逻辑说明列出源码文件快速了解代码量级和模块分布。主循环文件一般不大几百行逻辑复杂的战术模块可能有几千行。常见的划分方式大致是入口文件里包含 main 函数和 socket 通信感知模块里包含消息解析和状态更新决策模块里包含角色分工、跑位策略、进攻防守切换动作模块里包含关节控制、走路和踢球动作的生成。seu2013 这种老包结构通常很直白不会像现代软件工程那样抽象很多层这对学习反而是好事——跟着调用链走一遍就通了。4.2 主循环里的黑匣子一帧一帧的感知-决策-动作主循环是整个智能体的心脏。它的结构一般是这样// 伪代码典型的 RoboCup 3D agent 主循环 while (true) { // 1. 等待并接收来自服务器的感知消息 std::string msg agent.receive(); // 2. 解析消息更新世界模型 world.parse(msg); // 3. 根据当前状态做出决策 Action action brain.selectAction(world); // 4. 发送动作指令 agent.send(action.toString()); }逻辑说明这个循环每收到一条服务器消息就跑一遍。parse 负责把服务器发来的字符串解析成结构化状态比如球在哪、队友在哪、自己的身体姿态selectAction 是策略核心返回一个动作对象send 把动作指令序列化后回传给服务器。参数说明这里有几个点要注意。一是 receive 是阻塞式的意味着智能体只在收到消息时才行动和 SimSpark 的周期严格同步二是动作不是瞬时完成的要持续发送多帧才能完成一个踢球动作所以决策模块内部通常会维护一个「动作状态机」——当前在做什么动作、动作做到哪一步了、下一步什么时候切。4.3 世界模型与坐标转换服务器消息里藏着什么服务器发给智能体的原始消息是一串带标记的文本类似「看到球在相对坐标x, y, z的位置」。解析这些消息后智能体建立自己的「世界模型」——一个对场上状态的内部表示。世界模型的核心是坐标系统智能体要知道自己在哪、球在哪、队友在哪全都转换到一个统一的全局坐标系里。// 伪代码视觉消息解析 if (msg.find(see) ! std::string::npos) { // 从 see 消息中提取球的位置相对于智能体自身 Vec3 relativePos extractBallPosition(msg); // 结合自身位置和朝向转换为全局坐标 Vec3 globalPos localToGlobal(relativePos, self.pos, self.angle); world.ball.pos globalPos; }逻辑说明localToGlobal 是一个坐标变换函数输入相对坐标、智能体的位置和朝向输出全局坐标。这个函数在 RoboCup 3D 代码里几乎一定会出现因为服务器给的是相对信息而战术决策需要全局视角。参数说明注意相对坐标的参考系是智能体的传感器不是场地中心。如果拿到代码后发现坐标方向反了检查一下角度转换常见的问题是忘记把角度从弧度换成度或者方向符号反了。4.4 动作库从站立到踢球每个动作为什么这么难RoboCup 3D 智能体的身体有约 20 个关节每个关节可以单独控制角度和速度但走路和踢球需要多关节协同。代码包里通常有一个动作库里面封装了「站立」「走直线」「转身」「踢球」「扑球」等基础动作。每个基础动作本质上是一串关节角度随时间变化的轨迹。// 伪代码踢球动作的定义 kick_action { step_duration: 0.04, // 每个控制周期 40ms joint_trajectory: [ // 时间 0.0s: 右腿抬起 {time: 0.0, joints: {leg_r1: 0.2, leg_r2: -1.0}}, // 时间 0.1s: 右腿前摆接触球 {time: 0.1, joints: {leg_r1: 0.8, leg_r2: -0.2}}, // 时间 0.2s: 复位 {time: 0.2, joints: {leg_r1: 0.0, leg_r2: 0.0}} ] };逻辑说明这是一份简化的踢球动作定义。每个时刻指定一组关节角度agent 按时间推进逐步执行这段轨迹。如果动作没接对常见的结果是智能体摔倒或者踢不到球这类问题的调试靠调整轨迹参数没有别的捷径。5. 排错与避坑老代码包在仿真环境里的玄学问题5.1 编译报错新编译器和老代码的兼容性战争现象用 g 12 编译老代码包报一堆「declared deprecated」或者「no matching function for call to」的错误。原因这些年 C 标准一直在演进老代码用的某些写法比如隐式类型转换、旧版标准库接口在新编译器下被收紧或移除了。这不是代码逻辑错了是语言规则变了。解决第一选择是把编译器降到和代码包年代接近的版本比如 g-9。第二选择是关掉某些警告很多编译错误其实是从 warning 升级来的比如加-Wno-deprecated-declarations但这个是治标不治本。我一般会先降编译器版本如果还不行再去改代码里报错的几行——通常就是加个强转或者包含一个头文件的事。5.2 连不上服务器端口、IP 与 SimSpark 版本的三重坑现象启动 agent 后终端一直打印「connect failed」或「connection refused」但服务器显然在跑检查端口也在监听。原因三个方向排查。一是 IP 写错了localhost 解析到 IPv6 的 ::1但服务器只监听 IPv4二是端口不一致agent 的默认端口可能和服务器启动参数对不上三是 SimSpark 版本消息协议不兼容服务器和 agent 握手阶段就崩了。解决IP 问题直接把 --host 改成 127.0.0.1端口问题显式地在两个命令里都写死同一个端口号版本问题要确认 agent 代码里的消息协议版本和服务器匹配。还有一个隐蔽的坑防火墙。不过本地仿真的话防火墙通常不会拦除非你手动开了。5.3 智能体原地打转或者站着不动现象智能体启动后log 显示连接正常、消息也一直在收但就是不移动或者像喝醉酒一样原地转圈。原因通常是三个地方出问题。一是传感器消息解析有误尤其是角速度或加速度的符号反了身体姿态估计错误导致决策模块以为自己在摔倒。二是动作库里的动作 ID 对应错误决策模块发了「向前走」的指令但动作模块执行的是「站立」。三是循环里决策频率不对动作指令发送间隔和服务器期望的不一致导致运动指令被丢弃。解决先看日志里智能体的自身状态估计值是不是和真实位置对得上。对不上的话重点查坐标变换和传感器解析对得上的话查决策模块和动作模块的接口部分。这个过程中最有效的工具是在日志里打点——每十帧打印一次当前状态和正在执行的动作指令很容易定位到是哪一层断了。5.4 球往反方向跑或者球员永远碰不到球现象智能体看起来在追球但越追越远或者明明踢到了球球却往自家球门方向飞。原因坐标系的朝向搞反了。RoboCup 3D 的标准约定是球场的长边沿一个方向球门在两端。如果智能体把自己朝向的初始值设错或者在坐标变换时少转了一个 180 度就会产生「你以为的球门和你实际面对的方向相反」的效果。解决对照 SimSpark 官方文档里的坐标系定义检查代码里朝向初始化和坐标变换函数。这里没有捷径但是有一个常见模式老代码包的自定义坐标系可能不遵循标准你要把代码里的全局坐标和 SimSpark 日志里的真实位置对齐一次。6. 进阶从复现到改出自己的球队策略6.1 改出一套简单实用的策略角色分工与站位模板拿到代码包后第一步不是写全新的战术而是在现有框架里改参数。RoboCup 3D 里最常见的策略是「角色分工 站位」把 11 个智能体划分为守门员、后卫、中场、前锋。每个角色对应一组站位坐标和一套行为逻辑。# 在配置文件里调整站位示例 # 前锋站位偏对方半场保持可接球距离 forward {x: 10.0, y: 0.0, z: 0.0} # 中场站位本方半场偏前负责拦截传球 midfielder {x: 5.0, y: -8.0, z: 0.0}修改后重新编译启动一场比赛观察智能体是否按新站位落位。这一步是验证「你改对地方了没有」的最快方式。6.2 验证手段从单机自检到对抗测试判断策略改得好不好至少要看三个层面。第一层是基础功能智能体能站稳、能走向目标点、能踢到球。第二层是配合能力球传出去队友能不能接住、防守时会不会全部涌向球。第三层是实战抗压和一个不同风格的对手队伍打完整场比赛看丢球是否减少、进攻是否更有威胁。一个非常推荐的验证习惯每次修改只改一个变量跑一场比赛记录结果再改下一个。RoboCup 3D 的随机性不小同一个策略打多场比赛结果也会不同所以单场结果不能说明问题。如果想系统验证可以写一个脚本循环启动多场比赛统计平均进球数和控球率。6.3 下一步方向把强化学习接进这个框架如果你志不在此想在 RoboCup 3D 上跑强化学习这份代码包仍然是很好的起点。突破口在决策模块把 selectAction 从规则逻辑替换成策略网络感知模块输出的世界模型当作观测比赛结果当作奖励。动作模块不变保持关节控制稳定就只需要关注上层决策的学习。这个替换的过程我习惯分三步走先让强化学习智能体在「站位」单一任务上收敛保证它学会走到目标点再扩展到「追球」最后放进完整比赛里测试。每一步都要能验证不然到了比赛场景里出了问题你根本分不清是底层控制的问题还是策略的问题。我在改代码包时踩过最大的坑就是心急一次改太多东西到最后不知道是哪段代码导致智能体集体罢工。后来养成了一个习惯每次只动一个模块跑一场比赛验证再动下一个。这个习惯帮我省了无数定位问题的时间。希望这些经验能帮你少走一些弯路更快地把这份老代码变成你自己的球队。本文还有配套的精品资源点击获取