Skip to content

Latest commit

 

History

History
563 lines (459 loc) · 20.3 KB

File metadata and controls

563 lines (459 loc) · 20.3 KB

RCSSServerMJ 足球比赛启动与框架说明

一、如何启动一场完整的足球比赛

1.1 环境准备

安装服务器

pip install rcsssmj

安装示例队伍依赖(BahiaRT-MujOCo-base):

cd BahiaRT-MujOCo-base
# 使用 Poetry
poetry install
# 或使用 Hatch
hatch build

1.2 启动比赛的完整步骤

步骤1:启动仿真服务器

# 标准11v11比赛(FIFA场地)
rcssservermj -a 127.0.0.1 -p 60000 -m 60001

# 或者3v3比赛(HL Adult场地)
rcssservermj -a 127.0.0.1 -p 60000 -m 60001 -b hl_adult -f hl_adult_2020

服务器参数说明

  • -a <ip>: 服务器IP地址(默认:localhost)
  • -p <agent_port>: Agent连接端口(默认:60000)
  • -m <monitor_port>: 监视器端口(默认:60001)
  • -b <body>: 机器人模型(T1, K1, ant等)
  • -f <field>: 场地类型(fifa, hl_adult_2020等)

步骤2:启动左队(11名球员)

cd BahiaRT-MujOCo-base
./start.sh localhost 60000

或者手动启动每个球员:

for i in {1..11}; do
  python3 run_player.py --host localhost --port 60000 -n $i -t TeamLeft &
done

步骤3:启动右队(11名球员)

打开新终端:

cd BahiaRT-MujOCo-base
./start.sh localhost 60000

注意:第二支队伍会自动被分配到右侧(因为左侧已满)

步骤4:观看比赛

服务器会自动打开MuJoCo可视化窗口,显示实时比赛画面。

1.3 3v3比赛示例(Brazil Open Demo)

# 终端1:启动服务器
rcssservermj -a 127.0.0.1 -p 60000 -m 60001 -b T1 -f hl_adult_2020

# 终端2:启动左队(3名球员)
cd BahiaRT-MujOCo-base
./start3v3.sh localhost 60000

# 终端3:启动右队(3名球员)
cd BahiaRT-MujOCo-base
./start3v3.sh localhost 60000

二、通信框架详解

2.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                    RCSSServerMJ 服务器                       │
│  ┌───────────────────────────────────────────────────────┐  │
│  │         MuJoCo 物理仿真引擎                           │  │
│  │  - 机器人动力学模拟                                   │  │
│  │  - 碰撞检测                                           │  │
│  │  - 球的运动                                           │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↕                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │         Soccer Server 逻辑层                          │  │
│  │  - 比赛规则裁判                                       │  │
│  │  - Play Mode 管理                                     │  │
│  │  - 计分系统                                           │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↕                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │         通信层(TCP Socket)                          │  │
│  │  - Agent Port: 60000                                  │  │
│  │  - Monitor Port: 60001                                │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                          ↕ ↕ ↕
        ┌─────────────────┴─┴─┴─────────────────┐
        ↓                   ↓                    ↓
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│  Agent 1      │   │  Agent 2      │   │  Agent N      │
│  (球员1)      │   │  (球员2)      │   │  (球员N)      │
└───────────────┘   └───────────────┘   └───────────────┘

2.2 通信协议

消息格式

所有消息采用长度前缀 + S-expression内容

[4 bytes: 消息长度 (big-endian)] + [消息内容 (UTF-8)]

Python实现

# 发送消息
def send_message(sock, msg: str):
    msg_bytes = msg.encode('utf-8')
    length_prefix = len(msg_bytes).to_bytes(4, byteorder='big')
    sock.send(length_prefix + msg_bytes)

# 接收消息
def receive_message(sock) -> str:
    length_bytes = sock.recv(4)
    msg_length = int.from_bytes(length_bytes, byteorder='big')
    msg_bytes = sock.recv(msg_length)
    return msg_bytes.decode('utf-8')

连接流程

1. Agent创建TCP连接到服务器端口60000
   ↓
2. Agent发送初始化消息:(init <model> <team> <player_no>)
   例如:(init T1 TeamLeft 1)
   ↓
3. 服务器分配Agent ID,开始仿真循环
   ↓
4. 每个仿真周期(20ms):
   服务器 → Agent: 发送感知数据(Perception)
   Agent → 服务器: 发送动作命令(Action)
   ↓
5. 循环步骤4直到比赛结束或断开连接

2.3 感知数据(Perception)

服务器每个周期发送的S-expression格式数据:

(time (now 5.42))
(GS (t 5.0)(pm PlayOn)(tl TeamLeft)(tr TeamRight)(sl 0)(sr 0))
(HJ (n he1)(ax 0.5)(vx 0.1))
(HJ (n he2)(ax -2.3)(vx -0.5))
... (所有23个关节)
(GYR (n torso)(rt 1.2 -0.5 0.3))
(ACC (n torso)(a 0.1 0.2 9.8))
(quat (n torso)(q 0.998 0.01 -0.02 0.05))
(pos (n torso_pos)(p 5.2 -1.3 0.45))
(See
  (B (pol 3.5 -15.0 -2.0))
  (F1L (pol 10.2 45.0 0.0))
  (G1L (pol 8.5 30.0 -5.0))
  (P (team TeamLeft)(id 2)
     (head (pol 4.2 10.0 15.0))
     (lfoot-vismarker (pol 4.3 9.5 -20.0)))
)

关键感知类型

  • time: 仿真时间
  • GS: 比赛状态(play mode、队名、比分)
  • HJ: 关节状态(角度、角速度)
  • GYR: 陀螺仪(角速度)
  • ACC: 加速度计
  • quat: 姿态四元数
  • pos: 位置(ground-truth)
  • See: 视觉感知(球、地标、其他球员)

2.4 动作命令(Action)

Agent发送的动作消息格式:

(关节名 目标角度 目标角速度 kp kd 额外力矩)
(beam x y theta)

示例

(lle1 -20.0 0.0 25.0 0.6 0.0)(lle2 5.0 0.0 25.0 0.6 0.0)(beam 5.0 2.0 0.0)

PD控制公式

applied_torque = kp * (q_target - q_current) + kd * (dq_target - dq_current) + tau

三、Agent执行框架详解

3.1 BahiaRT-MujOCo-base 架构

┌─────────────────────────────────────────────────────────────┐
│                        Agent                                 │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  1. Server (通信层)                                   │  │
│  │     - TCP连接管理                                     │  │
│  │     - 消息收发缓冲                                    │  │
│  │     - send_immediate() / commit() / send()            │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  2. WorldParser (感知解析层)                         │  │
│  │     - S-expression解析                                │  │
│  │     - 数据结构化                                      │  │
│  │     - parse() → 更新World和Robot状态                 │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  3. World (世界模型层)                                │  │
│  │     - 比赛状态(play_mode, game_time, score)        │  │
│  │     - 球位置(ball_pos)                              │  │
│  │     - 自身位置(global_position)                     │  │
│  │     - 队友/对手位置(our_team_players, their_team_players)│
│  │     - 场地信息(field)                               │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  4. Robot (机器人状态层)                             │  │
│  │     - 关节状态(motor_positions, motor_speeds)      │  │
│  │     - 传感器数据(gyroscope, accelerometer)         │  │
│  │     - 姿态(global_orientation_quat/euler)          │  │
│  │     - 关节目标(motor_targets)                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  5. DecisionMaker (决策层)                           │  │
│  │     - 行为选择逻辑                                    │  │
│  │     - carry_ball(): 追球并带球向球门                 │  │
│  │     - 根据play_mode决定行为                          │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  6. SkillsManager (技能管理层)                       │  │
│  │     - 技能调度(execute, is_ready)                  │  │
│  │     - 可用技能:                                      │  │
│  │       * Walk: 行走控制(使用神经网络策略)           │  │
│  │       * GetUp: 起身动作(关键帧序列)                │  │
│  │       * Neutral: 中立姿态(关键帧)                  │  │
│  └───────────────────────────────────────────────────────┘  │
│                          ↓                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  7. Robot.commit_motor_targets_pd()                   │  │
│  │     - 将所有关节目标转换为动作消息                   │  │
│  │     - 提交到Server发送缓冲                            │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

3.2 主循环执行流程

代码位置BahiaRT-MujOCo-base/mujococodebase/agent.py:42-73

def run(self):
    # 1. 连接服务器
    self.server.connect()

    # 2. 发送初始化消息
    self.server.send_immediate(
        f"(init {self.robot.name} {self.world.team_name} {self.world.number})"
    )

    # 3. 主循环
    while True:
        try:
            # 3.1 接收感知数据
            self.server.receive()
            # ↓ 内部调用 world_parser.parse()
            # ↓ 更新 world 和 robot 状态

            # 3.2 更新世界模型
            self.world.update()
            # ↓ 更新 playmode_group

            # 3.3 决策
            self.decision_maker.update_current_behavior()
            # ↓ 根据 play_mode 选择行为
            # ↓ 调用 skills_manager.execute()
            # ↓ 技能更新 robot.motor_targets

            # 3.4 发送动作
            self.server.send()
            # ↓ 发送缓冲区中的所有动作消息

        except Exception:
            self.shutdown()
            raise

3.3 决策逻辑详解

代码位置BahiaRT-MujOCo-base/mujococodebase/decision_maker.py:55-85

def update_current_behavior(self):
    # 1. 比赛结束,不执行任何动作
    if self.agent.world.playmode is PlayModeEnum.GAME_OVER:
        return

    # 2. 开球前/重新开球:传送到初始位置
    if self.agent.world.playmode_group in (
        PlayModeGroupEnum.ACTIVE_BEAM,
        PlayModeGroupEnum.PASSIVE_BEAM,
    ):
        self.agent.server.commit_beam(
            pos2d=self.BEAM_POSES[...][self.agent.world.number][:2],
            rotation=self.BEAM_POSES[...][self.agent.world.number][2],
        )

    # 3. 检查是否需要起身
    if self.is_getting_up or self.agent.skills_manager.is_ready(skill_name="GetUp"):
        self.is_getting_up = not self.agent.skills_manager.execute(skill_name="GetUp")

    # 4. 正常比赛:追球并带球
    elif self.agent.world.playmode is PlayModeEnum.PLAY_ON:
        self.carry_ball()

    # 5. 其他模式:保持中立姿态
    elif self.agent.world.playmode in (PlayModeEnum.BEFORE_KICK_OFF, ...):
        self.agent.skills_manager.execute("Neutral")
    else:
        self.carry_ball()

    # 6. 提交所有关节目标到发送缓冲
    self.agent.robot.commit_motor_targets_pd()

3.4 carry_ball 行为详解

代码位置BahiaRT-MujOCo-base/mujococodebase/decision_maker.py:87-134

def carry_ball(self):
    # 1. 获取关键位置
    their_goal_pos = self.agent.world.field.get_their_goal_position()[:2]
    ball_pos = self.agent.world.ball_pos[:2]
    my_pos = self.agent.world.global_position[:2]

    # 2. 计算球到球门的方向
    ball_to_goal_dir = (their_goal_pos - ball_pos) / ||their_goal_pos - ball_pos||

    # 3. 计算理想带球位置(球后方0.3米)
    carry_ball_pos = ball_pos - ball_to_goal_dir * 0.30

    # 4. 判断是否对齐
    my_to_ball_dir = (ball_pos - my_pos) / ||ball_pos - my_pos||
    angle_diff = arccos(dot(my_to_ball_dir, ball_to_goal_dir))
    aligned = (angle_diff <= 7.5°)

    # 5. 判断是否在球后方
    behind_ball = dot(my_pos - ball_pos, ball_to_goal_dir) < 0

    # 6. 决策
    if not aligned or not behind_ball:
        # 移动到球后方的理想位置
        self.agent.skills_manager.execute(
            "Walk",
            target_2d=carry_ball_pos,
            is_target_absolute=True,
            orientation=None if distance > 2 else desired_orientation
        )
    else:
        # 已对齐,直接带球冲向球门
        self.agent.skills_manager.execute(
            "Walk",
            target_2d=their_goal_pos,
            is_target_absolute=True,
            orientation=desired_orientation
        )

四、关键数据流

4.1 感知数据流

服务器发送S-expression
    ↓
Server.receive() 接收原始字节流
    ↓
WorldParser.parse() 解析S-expression
    ↓
WorldParser.__sexpression_to_dict() 转换为字典
    ↓
更新 World 状态:
  - world.playmode
  - world.game_time
  - world.ball_pos
  - world.global_position
    ↓
更新 Robot 状态:
  - robot.motor_positions
  - robot.motor_speeds
  - robot.gyroscope
  - robot.accelerometer
  - robot.global_orientation_quat

4.2 动作数据流

DecisionMaker.update_current_behavior()
    ↓
SkillsManager.execute("Walk", target_2d=...)
    ↓
Walk.execute() 计算关节目标
    ↓
Robot.set_motor_target_position(motor_name, target, kp, kd)
    ↓
更新 robot.motor_targets[motor_name]
    ↓
Robot.commit_motor_targets_pd()
    ↓
生成动作消息:(motor_name target 0.0 kp kd 0.0)
    ↓
Server.commit(motor_msg) 添加到发送缓冲
    ↓
Server.send() 发送所有缓冲消息
    ↓
服务器接收并应用到MuJoCo仿真

五、技能系统详解

5.1 技能类型

1. Walk(行走技能)

  • 使用神经网络策略控制行走
  • 输入:目标位置、目标朝向
  • 输出:23个关节的目标角度

2. GetUp(起身技能)

  • 使用预定义的关键帧序列
  • 检测机器人是否倒地(z < 0.3m)
  • 执行起身动作序列

3. Neutral(中立姿态)

  • 单个关键帧
  • 用于开球前、进球后等静止状态

5.2 技能执行机制

# SkillsManager.execute()
def execute(self, skill_name: str, *args, **kwargs) -> bool:
    skill = self.get_skill_object(skill_name)

    # 检测技能切换,自动重置
    reset = (self.current_skill_name != skill_name)
    if reset:
        self.current_skill_name = skill_name

    # 执行技能
    finished = skill.execute(reset, *args, **kwargs)

    if finished:
        self.current_skill_name = None
        return True
    return False

六、Play Mode 状态机

BeforeKickOff (开球前)
    ↓ [裁判哨声]
KickOff_Left / KickOff_Right (开球)
    ↓ [球被踢动]
PlayOn (正常比赛)
    ↓ [球出界]
KickIn_Left / KickIn_Right (界外球)
    ↓ [球重新进场]
PlayOn
    ↓ [球进门]
Goal_Left / Goal_Right (进球)
    ↓ [重置]
BeforeKickOff
    ↓ [比赛时间结束]
GameOver (比赛结束)

七、总结

7.1 启动比赛的最简命令

# 终端1:服务器
rcssservermj

# 终端2:左队
cd BahiaRT-MujOCo-base && ./start.sh

# 终端3:右队
cd BahiaRT-MujOCo-base && ./start.sh

7.2 核心通信流程

Agent ←→ Server: TCP Socket (端口60000)
消息格式: [4字节长度] + [S-expression内容]
周期: 20ms (50Hz)

7.3 核心执行流程

接收感知 → 解析数据 → 更新世界模型 → 决策行为 →
选择技能 → 计算关节目标 → 发送动作 → 循环

7.4 扩展开发建议

  1. 自定义决策:修改 DecisionMaker.update_current_behavior()
  2. 新增技能:继承 Skill 类,在 SkillsManager.create_skills() 中注册
  3. 训练策略:使用 --sync --no-realtime 模式加速训练
  4. 多Agent协作:通过外部通信(Redis/ROS)实现队内协调

参考资源